From 7b30aec93d1a7ec450334d8870aa53419a7497ea Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 15 Feb 2011 16:34:09 -0500 Subject: [PATCH 001/154] freetdm: ftmod_r2 - check fclose return value when dumping IO --- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 4265c9b8fa..540d7cb6e9 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -714,6 +714,7 @@ static void dump_mf(openr2_chan_t *r2chan) { char dfile[512]; FILE *f = NULL; + int rc = 0; ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); ftdm_r2_data_t *r2data = ftdmchan->span->signal_data; if (r2data->mf_dump_size) { @@ -727,7 +728,10 @@ static void dump_mf(openr2_chan_t *r2chan) if (f) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dumping IO input in file %s\n", dfile); ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, f); - fclose(f); + rc = fclose(f); + if (rc) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failure closing IO input file %s: %s\n", dfile, strerror(errno)); + } } else { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Could not dump IO input in file %s, error: %s", dfile, strerror(errno)); } @@ -738,7 +742,10 @@ static void dump_mf(openr2_chan_t *r2chan) if (f) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dumping IO output in file %s\n", dfile); ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_OUTPUT, f); - fclose(f); + rc = fclose(f); + if (rc) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failure closing IO output file %s: %s\n", dfile, strerror(errno)); + } } else { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Could not dump IO output in file %s, error: %s", dfile, strerror(errno)); } From 925623c19e8f376d050eab3dc4dcbd08589d2d7c Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Wed, 16 Feb 2011 11:06:26 -0500 Subject: [PATCH 002/154] freetdm: Removed ftmod_sangoma_boost --- libs/freetdm/Makefile.am | 21 - libs/freetdm/configure.ac | 3 +- .../ftmod_sangoma_boost/BOOST.limitations | 20 - .../ftmod/ftmod_sangoma_boost/boost-tasks.txt | 146 - .../ftmod_sangoma_boost/ftdm_sangoma_boost.h | 75 - .../ftmod_sangoma_boost.2008.vcproj | 373 --- .../ftmod_sangoma_boost.2010.vcxproj | 206 -- .../ftmod_sangoma_boost.2010.vcxproj.filters | 35 - .../ftmod_sangoma_boost/ftmod_sangoma_boost.c | 2730 ----------------- .../sangoma_boost_client.c | 589 ---- .../sangoma_boost_client.h | 164 - .../sangoma_boost_interface.h | 254 -- .../src/ftmod/ftmod_sangoma_boost/sigboost.h | 221 -- 13 files changed, 1 insertion(+), 4836 deletions(-) delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj.filters delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h delete mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index b3353f32de..a26035b2ef 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -39,7 +39,6 @@ libdir = @libdir@ library_includedir = $(prefix)/include INCS = -I$(FT_SRCDIR)/$(SRC)/include -I$(FT_SRCDIR)/$(SRC)/include/private -INCS += -I$(FT_SRCDIR)/$(SRC)/ftmod/ftmod_sangoma_boost if HAVE_SNG_SS7 INCS += -I/usr/include/sng_ss7 @@ -109,7 +108,6 @@ core-install: install-libLTLIBRARIES # tools & test programs # noinst_PROGRAMS = testtones detect_tones detect_dtmf testpri testr2 testanalog testapp testcid -noinst_PROGRAMS += testsangomaboost testapp_SOURCES = $(SRC)/testapp.c testapp_LDADD = libfreetdm.la @@ -143,17 +141,6 @@ testr2_SOURCES = $(SRC)/testr2.c testr2_LDADD = libfreetdm.la testr2_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) -if HAVE_SCTP -noinst_PROGRAMS += testboost -testboost_SOURCES = $(SRC)/testboost.c -testboost_LDADD = libfreetdm.la -testboost_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) -endif - -testsangomaboost_SOURCES = $(SRC)/testsangomaboost.c -testsangomaboost_LDADD = libfreetdm.la -testsangomaboost_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) - testanalog_SOURCES = $(SRC)/testanalog.c testanalog_LDADD = libfreetdm.la testanalog_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) @@ -201,14 +188,6 @@ ftmod_isdn_la_LDFLAGS = -shared -module -avoid-version $(LIBISDN_LDFLAGS) $(PCA ftmod_isdn_la_LIBADD = libfreetdm.la $(LIBISDN_LIBS) $(PCAP_LIBS) endif -if HAVE_SCTP -mod_LTLIBRARIES += ftmod_sangoma_boost.la -ftmod_sangoma_boost_la_SOURCES = $(SRC)/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c $(SRC)/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c -ftmod_sangoma_boost_la_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) -ftmod_sangoma_boost_la_LDFLAGS = -shared -module -avoid-version -ftmod_sangoma_boost_la_LIBADD = libfreetdm.la -endif - if HAVE_LIBPRI mod_LTLIBRARIES += ftmod_libpri.la ftmod_libpri_la_SOURCES = $(SRC)/ftmod/ftmod_libpri/ftmod_libpri.c $(SRC)/ftmod/ftmod_libpri/lpwrap_pri.c diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index 1f4fc9c6ac..38f849b9e6 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -121,8 +121,7 @@ AC_CHECK_LIB([dl], [dlopen]) AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([m], [cos]) -AC_CHECK_HEADERS([netinet/sctp.h netdb.h sys/select.h]) -AM_CONDITIONAL([HAVE_SCTP],[test "${ac_cv_header_netinet_sctp_h}" = "yes"]) +AC_CHECK_HEADERS([netdb.h sys/select.h]) AC_CHECK_FUNC([gethostbyname_r], [], [AC_CHECK_LIB([nsl], [gethostbyname_r])] diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations deleted file mode 100644 index 663b7d376a..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations +++ /dev/null @@ -1,20 +0,0 @@ -== Boost sigmod current limitations == -- we don't support having openzap spans with physical channels - belonging to other physical spans. this is due to netborder sangoma abstraction, therefore - any openzap span using sigboost must have only channels belonging to the corresponding - physical span. - - This is the reason we added group functionality in openzap core, furthermore, previous groups in openzap - were only possible through adding of b-channels to a single span, but this forces the user to create groups - of channels only whithin the same type of trunk among other things. - -- all spans must be configured and then started, cannot configure, start, configure start etc - this is due to netborder telesoft abstraction. that requires configuring everything and - then starting everything at once. - -- sangoma_prid and sangoma_brid on Windows had to be compiled hacking make/Makefile.platform to comment all VC runtime checks, - otherwise when running in debug mode exceptions are thrown due to loss of data ie short to char conversions. - -== TODO == -- proper upper layer management of HW alarms (this must be done in mod_openzap.c) - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt deleted file mode 100644 index f0eab3a277..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt +++ /dev/null @@ -1,146 +0,0 @@ -== General Design == - -NBE will do its current loading of spans and configuration process through Sangoma Board Manager (SBM). -After doing SangomaBoardManager::getInstance().configure -> start. It will proceed to initalize -the openzap stack (just as the TelesoftStack is loaded after starting SMB. The procedure will be: - -- create a static or malloced zap_io_interface_t -- call zap_global_set_logger with the logging hooks. -- call zap_global_set_memhandler() with the memory hooks. -- call zap_global_init() to initialize the stack -- call zap_add_io_iface() to add the I/O iface. -- iterate over all SBM spans configured for BRI or any boost-managed signaling and: - * call zap_span_create(NBE I/O mod, in_ptrSpan, SMB span name) - * Fill in some members like: - span->trunk_type = E1/T1/J1/FXO/FXS etc ... - * iterate over all channels in SMB span and: - * zap_span_add_channel(zap_span, sock, type:CAS|BCHAN|DCHAN|ETC) - * call zap_configure_span("sangoma_boost", span, sigmsg_callback, "param1", value1, "param2", value1 ...) - * zap_span_start(span); - - -At this point, NBE would receive signaling msgs via sigmsg_callback registered when configuring -and NBE would request hangup or making calls throug openzap API, like zap_set_state_* and zap_channel_outgoing_call() to place calls. - -When NBE wants to check for link status. - - zap_get_siglink_state() which would return - ZAP_SIG_STATE_UP (D-chan UP, R2 bits in IDLE, ss7?) - ZAP_SIG_STATE_SUSPENDED (D-chan in power saving mode?) - ZAP_SIG_STATE_DOWN (D-chan down, R2 bits in blocked, ss7?) - - Whenever a state in sig link changes, the sigmsg_callback will be used to notify NBE or any other user. - -NOTE: right now hardware alarms notification in openzap is seriously broken, -see ozmod_libpri.c process_event ... reads an event from hardware (zap_event_t *), -then checks the event type, if its ZAP_OOB_ALARM_TRAP prepares a zap_sigmsg_t -(signaling event) setting its event_id to ZAP_OOB_ALARM_TRAP, which is *WRONG* -because event_id is of type zap_signal_event_t and not zap_oob_event_t! -this means on alarm the user will get ZAP_SIGEVENT_PROGRESS_MEDIA!! which is -value 7 that is in conflict with ZAP_OOB_ALARM_TRAP, I think a separate -callback should be used if the outside user wants to be notified about -hardware events like HW DTMF or so. Currently there is alreadya generic DTMF -listener. - -== Tasks Stage 1 / OpenZAP and Boost changes (To be tested with FreeSWITCH) == - -- Change malloc and other mem functions in openzap - to use internal hooks provided via zap_global_set_memhandler() - which would be called before zap_global_init(), this is - already done for the logger via zap_global_set_logger() - - question: should the mem routines allow for memory pool ptr? - this could be useful to provide a memory pool to - the whole module. - - question: should we allow hooks for threads and locking? - I think we can skip this one unless needed. They already - use their own threading abstraction which is working for - Linux and Windows. If we ever need to profile threading - we can add profiling hooks. - - question: I had to add openzap calls to the hash table and libteletone implementations, is that acceptable? - -- Modify zap_global_init() API - - This API must just initialize vars, mutexes etc. - and NOT DO ANY CONFIGURATION LOADING, PARSING, SPAN CREATION and I/O - configuration, which is what is currently doing. - We don't want zap_global_init() to create the spans based on that configuration - since NBE will have its own configuration and will take care of creating - the needed data structures on its own. - -- Add new zap_std_io_config() API - - This API will parse the standard openzap.conf module and create the spans. - This will be used by FS but not by NBE, which will create the openzap spans by itself. - The NBE flow to initialize openzap will be: - -- Add new API zap_global_add_io_iface(), - - This API will add a new I/O interface structure to the internal openzap hash of I/O structs. - This is needed because NBE I/O structure will NOT be loaded from an openzap module (.so/.dll) - but rather just registered on runtime (probably from a static structure in NBE code). - This openzap hash is used by zap_api_execute() for example, to find the module that can - handle a given API, ie (oz libpri status). This is an example of how an openzap I/O interface - can decide to implement just the ->api() member to handle commands and NOTHING else, - so I/O interfaces not necessary are hardware-related. - -- Add new zap_channel_get_siglink_state(zap_channel, zap_siglink_status_t &status) - -- Modify mod_openzap.c to read proto= setting in boost spans, this will determine wich boost sig - module will handle the configuration and those channels. - - - - Then as first config arg to zap_config_span() the boost proto module name would be included as "sigmod" which will be used - by ozmod_sangoma_boost to decide which sig module must handle that span configuration - -- Create minimal boost mod interface. - - ozmod_boost_ss7 should load sig boost mods and get interface via dlsym(boost_get_interface) boost_get_interface(boost_iface); - The boost interface will have - * const char *name // boost sigmod name (brid,ss7d) - * set_write_boost_msg_cb(callback) // tell the stack how to send us boost messages - * set_sig_status_cb(callback); // tell the stack how to notify us about link status changes - * write_boost_msg(struct boost_msg) // send a boost msg to the stack - * configure_span(zap_span_t span, "configuration", value, "configuration", value) // configure a given span - * get_sig_status(openzap_sigstatus_t status) - * start(span) // to start a given openzap span - * stop(span) // to stop the stack on a given openzap span - -- Migrate current sangoma_brid sig module to openzap - * Make sangoma_brid a library - * Move from using malloc, threading, locking, logging and I/O to openzap functions. Export the boost sigmod interface and its supporting code. - -== State 2 Tasks == - -- Create the I/O NBE interface and supporting functions. It must be possible to poll over the span - given that ozmod_sangoma_boost BRI module and others may need to *wait* for data. The poll() - function in I/O NBE interface would wait on a pthread condition or Windows event, which would - be triggered by some external NBE component registered with Sangoma Board Manager (SMB) for d-chan - data, whenever d-chan data arrives, saves the data in a buffer and triggers the condition to wakeup - any waiter, then the waiter (sangoma_brid or any other boost client) calls zap_channel_read which calls - our own I/O NBE interface read method and retrieves the data from the buffer. - - Dropped alternative design: - Another option is to add a new API zap_span_push_incoming_data(span/chan, data); However this changes - the model openzap has followed and I don't think fits that well, since now we have 2 different models - to support in openzap. - -== TODO == - -- how about logging specific modules, like, just ozmod_boost, or just the BRI stack? - more work to be done so the BRI module uses zap_log instead of current syslog - then work to be done to be able to filter logs from specific openzap code? is it worth it? - -- remove FORCE_SEGFAULT from sprid - - -=== Shortcomings == - -- we had to drop smg support in the branch where we work on sangoma prid. - After all, most people using sangoma_prid is using freeswitch/openzap and not Sangoma Media Gateway - The problem is in freeswitch/openzap mode, sangoma_boost ozmod takes care of span events (POLLPRI) - where in SMG and Netborder POLLPRI is done typically by sangoma board manager. - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h deleted file mode 100644 index 6fbf272d07..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2007, Anthony Minessale II - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FTDM_SANGOMA_BOOST_H -#define FTDM_SANGOMA_BOOST_H -#include "sangoma_boost_client.h" -#include "freetdm.h" - -#define MAX_CHANS_PER_TRUNKGROUP 1024 - -typedef enum { - FTDM_SANGOMA_BOOST_RUNNING = (1 << 0), - FTDM_SANGOMA_BOOST_RESTARTING = (1 << 1), - FTDM_SANGOMA_BOOST_EVENTS_RUNNING = (1 << 2), -} ftdm_sangoma_boost_flag_t; - -typedef struct ftdm_sangoma_boost_data { - sangomabc_connection_t mcon; - sangomabc_connection_t pcon; - int iteration; - uint32_t flags; - boost_sigmod_interface_t *sigmod; - ftdm_queue_t *boost_queue; -} ftdm_sangoma_boost_data_t; - -typedef struct ftdm_sangoma_boost_trunkgroup { - ftdm_mutex_t *mutex; - ftdm_size_t size; /* Number of b-channels in group */ - unsigned int last_used_index; /* index of last b-channel used */ - ftdm_channel_t* ftdmchans[MAX_CHANS_PER_TRUNKGROUP]; - //TODO need to merge congestion timeouts to this struct -} ftdm_sangoma_boost_trunkgroup_t; -#endif - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj deleted file mode 100644 index 73e421818f..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj deleted file mode 100644 index 684e4326cb..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj +++ /dev/null @@ -1,206 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - ftmod_sangoma_boost - {D021EF2A-460D-4827-A0F7-41FDECF46F1B} - ftmod_sangoma_boost - Win32Proj - - - - DynamicLibrary - Unicode - true - - - DynamicLibrary - Unicode - - - DynamicLibrary - Unicode - true - - - DynamicLibrary - Unicode - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - true - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - false - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - true - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - false - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - - - - - Disabled - ..\..\include;..\..\isdn\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;FTMOD_SANGOMA_BOOST_EXPORTS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;%(DisableSpecificWarnings) - - - %(AdditionalDependencies) - $(OutDir);%(AdditionalLibraryDirectories) - true - Windows - MachineX86 - - - - - MaxSpeed - true - ..\..\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;FTMOD_SANGOMA_BOOST_EXPORTS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level4 - true - ProgramDatabase - 4100;%(DisableSpecificWarnings) - - - true - Windows - true - true - MachineX86 - - - - - X64 - - - Disabled - ..\..\include;..\..\isdn\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;FTMOD_SANGOMA_BOOST_EXPORTS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;%(DisableSpecificWarnings) - - - %(AdditionalDependencies) - $(OutDir);%(AdditionalLibraryDirectories) - true - Windows - MachineX64 - - - - - X64 - - - MaxSpeed - true - ..\..\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;FTMOD_SANGOMA_BOOST_EXPORTS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level4 - false - ProgramDatabase - 4100;%(DisableSpecificWarnings) - - - true - Windows - true - true - MachineX64 - - - - - - - - - - - - - - - {93b8812c-3ec4-4f78-8970-ffbfc99e167d} - false - - - - - - \ No newline at end of file diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj.filters b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj.filters deleted file mode 100644 index 2aeed155cd..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2010.vcxproj.filters +++ /dev/null @@ -1,35 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c deleted file mode 100644 index d0bc14c8d8..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c +++ /dev/null @@ -1,2730 +0,0 @@ -/* - * Copyright (c) 2007, Anthony Minessale II - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Contributors: - * - * Moises Silva - * David Yat Sin - * Nenad Corbic - * - */ - -/* NOTE: -On __WINDOWS__ platform this code works with sigmod ONLY, don't try to make sense of any socket code for win -I basically ifdef out everything that the compiler complained about -*/ - -#include "private/ftdm_core.h" -#include "sangoma_boost_client.h" -#include "ftdm_sangoma_boost.h" -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -/* Boost signaling modules global hash and its mutex */ -ftdm_mutex_t *g_boost_modules_mutex = NULL; -ftdm_hash_t *g_boost_modules_hash = NULL; - -#define MAX_TRUNK_GROUPS 64 -//TODO need to merge congestion_timeouts with ftdm_sangoma_boost_trunkgroups -static time_t congestion_timeouts[MAX_TRUNK_GROUPS]; - -static ftdm_sangoma_boost_trunkgroup_t *g_trunkgroups[MAX_TRUNK_GROUPS]; - -static ftdm_io_interface_t ftdm_sangoma_boost_interface; -static ftdm_status_t ftdm_sangoma_boost_list_sigmods(ftdm_stream_handle_t *stream); - -#define BOOST_QUEUE_SIZE 500 - -/* get freetdm span and chan depending on the span mode */ -#define BOOST_SPAN(ftdmchan) ((ftdm_sangoma_boost_data_t*)(ftdmchan)->span->signal_data)->sigmod ? ftdmchan->physical_span_id : ftdmchan->physical_span_id-1 -#define BOOST_CHAN(ftdmchan) ((ftdm_sangoma_boost_data_t*)(ftdmchan)->span->signal_data)->sigmod ? ftdmchan->physical_chan_id : ftdmchan->physical_chan_id-1 - -/** - * \brief SANGOMA boost notification flag - */ -typedef enum { - SFLAG_SENT_FINAL_MSG = (1 << 0), - SFLAG_SENT_ACK = (1 << 1), - SFLAG_RECVD_ACK = (1 << 2), - SFLAG_HANGUP = (1 << 3), - SFLAG_TERMINATING = (1 << 4) -} sflag_t; - -typedef uint16_t sangoma_boost_request_id_t; - -/** - * \brief SANGOMA boost request status - */ -typedef enum { - BST_FREE, - BST_WAITING, - BST_READY, - BST_FAIL -} sangoma_boost_request_status_t; - -/** - * \brief SANGOMA boost request structure - */ -typedef struct { - sangoma_boost_request_status_t status; - sangomabc_short_event_t event; - ftdm_span_t *span; - ftdm_channel_t *ftdmchan; - int hangup_cause; - int flags; -} sangoma_boost_request_t; - -typedef struct { - int call_setup_id; - int last_event_id; -} sangoma_boost_call_t; - -#define CALL_DATA(ftdmchan) ((sangoma_boost_call_t*)((ftdmchan)->call_data)) - -//#define MAX_REQ_ID FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN * FTDM_MAX_CHANNELS_PHYSICAL_SPAN -#define MAX_REQ_ID 6000 - -static uint16_t SETUP_GRID[FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN+1][FTDM_MAX_CHANNELS_PHYSICAL_SPAN+1] = {{ 0 }}; - -static sangoma_boost_request_t OUTBOUND_REQUESTS[MAX_REQ_ID+1] = {{ 0 }}; - -static ftdm_mutex_t *request_mutex = NULL; - -static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; -static uint8_t nack_map[MAX_REQ_ID+1] = { 0 }; - -/** - * \brief Releases span and channel from setup grid - * - * \note This is ALWAYS based on freetdm span/chan numbers! not boost event numbers - * is totally brain damaged to use event->span or event->chan to release the request - * use BOOST_SPAN_EVENT and BOOST_SPAN_CHAN to get the right index!! - * - * \param span Span number - * \param chan Channel number - * \param func Calling function - * \param line Line number on request - * \return NULL if not found, channel otherwise - */ -static void __release_request_id_span_chan(int span, int chan, const char *func, int line) -{ - int id; - - ftdm_mutex_lock(request_mutex); - if ((id = SETUP_GRID[span][chan])) { - ftdm_assert(id <= MAX_REQ_ID, "Invalid request id\n"); - req_map[id] = 0; - SETUP_GRID[span][chan] = 0; - } - ftdm_mutex_unlock(request_mutex); -} -#define release_request_id_span_chan(s, c) __release_request_id_span_chan(s, c, __FUNCTION__, __LINE__) - -/** - * \brief Releases request ID - * \param func Calling function - * \param line Line number on request - * \return NULL if not found, channel otherwise - */ -static void __release_request_id(sangoma_boost_request_id_t r, const char *func, int line) -{ - ftdm_assert(r <= MAX_REQ_ID, "Invalid request id\n"); - ftdm_mutex_lock(request_mutex); - req_map[r] = 0; - ftdm_mutex_unlock(request_mutex); -} -#define release_request_id(r) __release_request_id(r, __FUNCTION__, __LINE__) - -static sangoma_boost_request_id_t last_req = 0; - -/** - * \brief Gets the first available tank request ID - * \param func Calling function - * \param line Line number on request - * \return 0 on failure, request ID on success - */ -static sangoma_boost_request_id_t __next_request_id(const char *func, int line) -{ - sangoma_boost_request_id_t r = 0, i = 0; - int found=0; - - ftdm_mutex_lock(request_mutex); - //r = ++last_req; - //while(!r || req_map[r]) { - - for (i=1; i<= MAX_REQ_ID; i++){ - r = ++last_req; - - if (r >= MAX_REQ_ID) { - r = last_req = 1; - } - - if (req_map[r]) { - /* Busy find another */ - continue; - - } - - req_map[r] = 1; - found=1; - break; - - } - - ftdm_mutex_unlock(request_mutex); - - if (!found) { - return 0; - } - - return r; -} -#define next_request_id() __next_request_id(__FUNCTION__, __LINE__) - - -static void print_request_ids(void) -{ - sangoma_boost_request_id_t i = 0; - int cnt=0; - - ftdm_mutex_lock(request_mutex); - - for (i=1; i<= MAX_REQ_ID; i++){ - if (req_map[i]) { - ftdm_log(FTDM_LOG_CRIT, "Used Request ID=%i\n",i); - cnt++; - } - } - - ftdm_mutex_unlock(request_mutex); - ftdm_log(FTDM_LOG_CRIT, "Total Request IDs=%i\n",cnt); - - return; -} - - -/** - * \brief Finds the channel that triggered an event - * \param span Span where to search the channel - * \param event SANGOMA event - * \param force Do not wait for the channel to be available if in use - * \return NULL if not found, channel otherwise - */ -static ftdm_channel_t *find_ftdmchan(ftdm_span_t *span, sangomabc_short_event_t *event, int force) -{ - uint32_t i; - ftdm_channel_t *ftdmchan = NULL; - - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - uint32_t targetspan = BOOST_EVENT_SPAN(sangoma_boost_data->sigmod, event); - uint32_t targetchan = BOOST_EVENT_CHAN(sangoma_boost_data->sigmod, event); - - /* NC: Sanity check in case the call setup id does not relate - to span. This can happen if RESTART is received on a - full load. Where stray ACK messages can arrive after - a RESTART has taken place. - */ - if (!span) { - ftdm_log(FTDM_LOG_CRIT, "No Span for Event=%s s%dc%d cid=%d\n", - BOOST_DECODE_EVENT_ID(event->event_id), - targetspan, - targetchan, - event->call_setup_id); - return NULL; - } - - - for(i = 1; i <= span->chan_count; i++) { - if (span->channels[i]->physical_span_id == targetspan && span->channels[i]->physical_chan_id == targetchan) { - ftdmchan = span->channels[i]; - if (force || (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE))) { - break; - } else { - ftdmchan = NULL; - ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d ~ %d:%d is already in use in state %s\n", - span->channels[i]->span_id, - span->channels[i]->chan_id, - span->channels[i]->physical_span_id, - span->channels[i]->physical_chan_id, - ftdm_channel_state2str(span->channels[i]->state)); - break; - } - } - } - - return ftdmchan; -} - -static int check_congestion(int trunk_group) -{ - if (congestion_timeouts[trunk_group]) { - time_t now = time(NULL); - - if (now >= congestion_timeouts[trunk_group]) { - congestion_timeouts[trunk_group] = 0; - } else { - return 1; - } - } - - return 0; -} - - -/** - * \brief Requests an sangoma boost channel on a span (outgoing call) - * \param span Span where to get a channel - * \param chan_id Specific channel to get (0 for any) - * \param direction Call direction - * \param caller_data Caller information - * \param ftdmchan Channel to initialise - * \return Success or failure - */ -static FIO_CHANNEL_REQUEST_FUNCTION(sangoma_boost_channel_request) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - ftdm_status_t status = FTDM_FAIL; - sangoma_boost_request_id_t r; - sangomabc_event_t event = {0}; - /* sanity has to be more than 8 seconds. - * In PRI specs, timeout is 4 seconds for remote switch to respond to a SETUP, - * and PRI stack will retransmit a second SETUP after the first timeout, so - * we should allow for at least 8 seconds. - */ - - int boost_request_timeout = 10000; - sangoma_boost_request_status_t st; - char dnis[128] = ""; - char *gr = NULL; - uint32_t count = 0; - int tg=0; - - /* NC: On large number of calls 10 seconds is not enough. - Resetting to 30 seconds. Especially on ss7 when - links are reset during large call volume */ - if (!sangoma_boost_data->sigmod) { - boost_request_timeout = 30000; - } - - if (sangoma_boost_data->sigmod) { - ftdm_log(FTDM_LOG_CRIT, "This function should not be called when sigmod was configured in boost\n"); - *ftdmchan = NULL; - return FTDM_FAIL; - } - - if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED)) { - ftdm_log(FTDM_LOG_CRIT, "SPAN is Suspended.\n"); - *ftdmchan = NULL; - return FTDM_FAIL; - } - - if (check_congestion(tg)) { - ftdm_log(FTDM_LOG_CRIT, "All circuits are busy. Trunk Group=%i (CONGESTION)\n",tg+1); - *ftdmchan = NULL; - return FTDM_FAIL; - } - - if (count >= span->chan_count) { - ftdm_log(FTDM_LOG_CRIT, "All circuits are busy.\n"); - *ftdmchan = NULL; - return FTDM_FAIL; - } - - r = next_request_id(); - if (r == 0) { - ftdm_log(FTDM_LOG_CRIT, "All tanks ids are busy.\n"); - *ftdmchan = NULL; - return FTDM_FAIL; - } - - /* After this point we must release request id before we leave the function - in case of an error. */ - - ftdm_set_string(dnis, caller_data->dnis.digits); - - if ((gr = strchr(dnis, '@'))) { - *gr++ = '\0'; - } - - if (gr && *(gr+1)) { - tg = atoi(gr+1); - if (tg > 0) { - tg--; - } - } - - sangomabc_call_init(&event, caller_data->cid_num.digits, dnis, r); - - event.trunk_group = tg; - - - ftdm_span_channel_use_count(span, &count); - - if (gr && *(gr+1)) { - switch(*gr) { - case 'g': - event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; - break; - case 'G': - event.hunt_group = SIGBOOST_HUNTGRP_SEQ_DESC; - break; - case 'r': - event.hunt_group = SIGBOOST_HUNTGRP_RR_ASC; - break; - case 'R': - event.hunt_group = SIGBOOST_HUNTGRP_RR_DESC; - break; - default: - ftdm_log(FTDM_LOG_WARNING, "Failed to determine huntgroup (%s)\n", gr); - event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; - } - } - - ftdm_set_string(event.calling_name, caller_data->cid_name); - ftdm_set_string(event.rdnis.digits, caller_data->rdnis.digits); - if (strlen(caller_data->rdnis.digits)) { - event.rdnis.digits_count = (uint8_t)strlen(caller_data->rdnis.digits)+1; - event.rdnis.ton = caller_data->rdnis.type; - event.rdnis.npi = caller_data->rdnis.plan; - } - - event.calling.screening_ind = caller_data->screen; - event.calling.presentation_ind = caller_data->pres; - - event.calling.ton = caller_data->cid_num.type; - event.calling.npi = caller_data->cid_num.plan; - - event.called.ton = caller_data->dnis.type; - event.called.npi = caller_data->dnis.plan; - - /* we're making a contract now that FreeTDM values for capability, layer 1 and such will be the same as for boost */ - event.bearer.capability = caller_data->bearer_capability; - event.bearer.uil1p = caller_data->bearer_layer1; - - if (caller_data->raw_data_len) { - ftdm_set_string(event.custom_data, caller_data->raw_data); - event.custom_data_size = (uint16_t)caller_data->raw_data_len; - } - - OUTBOUND_REQUESTS[r].status = BST_WAITING; - OUTBOUND_REQUESTS[r].span = span; - - if (sangomabc_connection_write(&sangoma_boost_data->mcon, &event) <= 0) { - ftdm_log(FTDM_LOG_CRIT, "Failed to tx boost event [%s]\n", strerror(errno)); - status = OUTBOUND_REQUESTS[r].status = FTDM_FAIL; - if (!sangoma_boost_data->sigmod) { - *ftdmchan = NULL; - } - goto done; - } - - while(ftdm_running() && OUTBOUND_REQUESTS[r].status == BST_WAITING) { - ftdm_sleep(1); - if (--boost_request_timeout <= 0) { - status = FTDM_FAIL; - if (!sangoma_boost_data->sigmod) { - *ftdmchan = NULL; - } - ftdm_log(FTDM_LOG_CRIT, "Csid:%d Timed out waiting for boost channel request response, current status: BST_WAITING\n", r); - goto done; - } - } - - if (OUTBOUND_REQUESTS[r].status == BST_READY && OUTBOUND_REQUESTS[r].ftdmchan) { - *ftdmchan = OUTBOUND_REQUESTS[r].ftdmchan; - status = FTDM_SUCCESS; - } else { - status = FTDM_FAIL; - if (!sangoma_boost_data->sigmod) { - *ftdmchan = NULL; - } - } - - done: - - st = OUTBOUND_REQUESTS[r].status; - OUTBOUND_REQUESTS[r].status = BST_FREE; - - if (status == FTDM_FAIL) { - if (st == BST_FAIL) { - caller_data->hangup_cause = OUTBOUND_REQUESTS[r].hangup_cause; - } else { - caller_data->hangup_cause = FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE; - } - } - - if (st == BST_FAIL) { - release_request_id(r); - } else if (st != BST_READY) { - ftdm_assert_return(r <= MAX_REQ_ID, FTDM_FAIL, "Invalid index\n"); - nack_map[r] = 1; - if (sangoma_boost_data->sigmod) { - sangomabc_exec_command(&sangoma_boost_data->mcon, - BOOST_SPAN((*ftdmchan)), - BOOST_CHAN((*ftdmchan)), - r, - SIGBOOST_EVENT_CALL_START_NACK, - 0, 0); - } else { - sangomabc_exec_command(&sangoma_boost_data->mcon, - 0, - 0, - r, - SIGBOOST_EVENT_CALL_START_NACK, - 0, 0); - } - } - - return status; -} - -/** - * \brief Starts an sangoma boost channel (outgoing call) - * \param ftdmchan Channel to initiate call on - * \return Success - */ -static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(sangoma_boost_outgoing_call) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - - if (!sangoma_boost_data->sigmod) { - return FTDM_SUCCESS; - } - - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); - - return FTDM_SUCCESS; -} - -/** - * \brief Handler for call start ack no media event - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_progress(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - - - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - ftdm_mutex_lock(ftdmchan->mutex); - if (!sangoma_boost_data->sigmod && ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { - if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { - ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); - } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { - ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to PROGRESS [Csid:%d]\n", event->call_setup_id); - } else { - ftdmchan->init_state = FTDM_CHANNEL_STATE_IDLE; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to IDLE [Csid:%d]\n", event->call_setup_id); - } - } else { - if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); - } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); - } else { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE); - } - } - ftdm_mutex_unlock(ftdmchan->mutex); - } -} - -/** - * \brief Handler for call start ack event - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_start_ack(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - - ftdm_channel_t *ftdmchan = NULL; - uint32_t event_span = BOOST_EVENT_SPAN(mcon->sigmod, event); - uint32_t event_chan = BOOST_EVENT_CHAN(mcon->sigmod, event); - - - if (nack_map[event->call_setup_id]) { - /* In this scenario outgoing call was alrady stopped - via NACK and now we are expecting an NACK_ACK. - If we receive an ACK its a race condition thus - ignor it */ - return; - } - - if (mcon->sigmod) { - ftdmchan = OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan; - } else { - ftdmchan = find_ftdmchan(OUTBOUND_REQUESTS[event->call_setup_id].span, event, 0); - } - - - if (ftdmchan) { - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - if (!mcon->sigmod && ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to open FTDM channel [%s]\n", ftdmchan->last_error); - } else { - - /* Only bind the setup id to GRID when we are sure that channel is ready - otherwise we could overwite the original call */ - OUTBOUND_REQUESTS[event->call_setup_id].event = *event; - SETUP_GRID[event_span][event_chan] = event->call_setup_id; - - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE); - ftdmchan->sflags = SFLAG_RECVD_ACK; - - if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { - if (sangoma_boost_data->sigmod) { - ftdm_log(FTDM_LOG_DEBUG, "Channel state changing to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); - } else { - ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); - } - } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { - if (sangoma_boost_data->sigmod) { - ftdm_log(FTDM_LOG_DEBUG, "Channel state changing to PROGRESS [Csid:%d]\n", event->call_setup_id); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); - } else { - ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to PROGRESS [Csid:%d]\n", event->call_setup_id); - } - } else { - if (sangoma_boost_data->sigmod) { - /* should we set a state here? */ - } else { - ftdmchan->init_state = FTDM_CHANNEL_STATE_IDLE; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to IDLE [Csid:%d]\n", event->call_setup_id); - } - } - if (!sangoma_boost_data->sigmod) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HOLD); - ftdm_log(FTDM_LOG_DEBUG, "Assigned chan %d:%d (%d:%d) to CSid=%d\n", - ftdmchan->span_id, ftdmchan->chan_id, event_span, event_chan, event->call_setup_id); - OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan = ftdmchan; - } - OUTBOUND_REQUESTS[event->call_setup_id].flags = event->flags; - OUTBOUND_REQUESTS[event->call_setup_id].status = BST_READY; - return; - } - - } else { - - ftdm_assert(!mcon->sigmod, "CALL STOP ACK: Invalid Sigmod Path"); - - if ((ftdmchan = find_ftdmchan(OUTBOUND_REQUESTS[event->call_setup_id].span, (sangomabc_short_event_t*)event, 1))) { - int r; - /* NC: If we get CALL START ACK and channel is in active state - then we are completely out of sync with the other end. - Treat CALL START ACK as CALL STOP and hangup the current call. - */ - - if (ftdmchan->state == FTDM_CHANNEL_STATE_UP || - ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA || - ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS) { - ftdm_log(FTDM_LOG_CRIT, "FTDMCHAN CALL ACK STATE UP -> Changed to TERMINATING %d:%d\n", event_span, event_chan); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, r); - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP || ftdm_test_sflag(ftdmchan, SFLAG_HANGUP)) { - ftdm_log(FTDM_LOG_CRIT, "FTDMCHAN CALL ACK STATE HANGUP -> Changed to HANGUP COMPLETE %d:%d\n", event_span, event_chan); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, r); - } else { - ftdm_log(FTDM_LOG_CRIT, "FTDMCHAN STATE INVALID State %s on IN CALL ACK %d:%d\n", - ftdm_channel_state2str(ftdmchan->state), event_span, event_chan); - } - ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG); - ftdmchan=NULL; - } - } - - - if (!ftdmchan) { - ftdm_log(FTDM_LOG_CRIT, "START ACK CANT FIND A CHAN %d:%d\n", event_span, event_chan); - } else { - /* only reason to be here is failed to open channel when we we're in sigmod */ - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); - ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG); - } - - sangomabc_exec_command(mcon, - event->span, - event->chan, - event->call_setup_id, - SIGBOOST_EVENT_CALL_STOPPED, - FTDM_CAUSE_DESTINATION_OUT_OF_ORDER, 0); - OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; - OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; -} - -/** - * \brief Handler for call done event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_done(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - int r = 0; - - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - ftdm_mutex_lock(ftdmchan->mutex); - - if (sangoma_boost_data->sigmod) { - /* not really completely done, but if we ever get an incoming call before moving to HANGUP_COMPLETE - * handle_incoming_call() will take care of moving the state machine to release the channel */ - sangomabc_exec_command(&sangoma_boost_data->mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_RELEASED, - 0, 0); - } - - if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP_COMPLETE || ftdm_test_sflag(ftdmchan, SFLAG_TERMINATING)) { - goto done; - } - - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, r); - if (r) { - ftdm_mutex_unlock(ftdmchan->mutex); - return; - } - } - - done: - - if (ftdmchan) { - ftdm_mutex_unlock(ftdmchan->mutex); - } - - if (event->call_setup_id) { - release_request_id(event->call_setup_id); - } else { - release_request_id_span_chan(BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - } -} - - -/** - * \brief Handler for call start nack event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_start_nack(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { - uint32_t count = 0; - int delay = 0; - int tg=event->trunk_group; - - ftdm_span_channel_use_count(span, &count); - - delay = (int) (count / 100) * 2; - - if (delay > 10) { - delay = 10; - } else if (delay < 1) { - delay = 1; - } - - if (tg < 0 || tg >= MAX_TRUNK_GROUPS) { - ftdm_log(FTDM_LOG_CRIT, "Invalid All Ckt Busy trunk group number %i\n", tg); - tg=0; - } - - congestion_timeouts[tg] = time(NULL) + delay; - event->release_cause = 17; - - } else if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { - event->release_cause = 17; - } - - if (event->call_setup_id) { - if (sangoma_boost_data->sigmod) { - ftdmchan = OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan; - CALL_DATA(ftdmchan)->last_event_id = event->event_id; - CALL_DATA(ftdmchan)->call_setup_id = event->call_setup_id; - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); - ftdm_clear_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); - } else { - sangomabc_exec_command(mcon, - 0, - 0, - event->call_setup_id, - SIGBOOST_EVENT_CALL_START_NACK_ACK, - 0, 0); - OUTBOUND_REQUESTS[event->call_setup_id].event = *event; - OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; - OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; - ftdm_log(FTDM_LOG_DEBUG, "setting outbound request status %d to BST_FAIL\n", event->call_setup_id); - } - return; - } else { - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - int r = 0; - - /* if there is no call setup id this should not be an outbound channel for sure */ - ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND), "Yay, outbound flag should not be set here!\n"); - - CALL_DATA(ftdmchan)->last_event_id = event->event_id; - ftdm_mutex_lock(ftdmchan->mutex); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, r); - if (r == FTDM_SUCCESS) { - ftdmchan->caller_data.hangup_cause = event->release_cause; - } - ftdm_mutex_unlock(ftdmchan->mutex); - if (r) { - return; - } - } - } - - if (ftdmchan) { - ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); - } - - /* nobody else will do it so we have to do it ourselves */ - sangomabc_exec_command(mcon, - event->span, - event->chan, - event->call_setup_id, - SIGBOOST_EVENT_CALL_START_NACK_ACK, - 0, 0); -} - -static void handle_call_released(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - ftdm_log(FTDM_LOG_DEBUG, "Releasing completely chan s%dc%d\n", BOOST_EVENT_SPAN(mcon->sigmod, event), - BOOST_EVENT_CHAN(mcon->sigmod, event)); - ftdm_channel_close(&ftdmchan); - } else { - ftdm_log(FTDM_LOG_CRIT, "Odd, We could not find chan: s%dc%d to release the call completely!!\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - } -} - -/** - * \brief Handler for call stop event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_stop(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - int r = 0; - - ftdm_mutex_lock(ftdmchan->mutex); - - if (ftdm_test_sflag(ftdmchan, SFLAG_HANGUP) || - ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || - ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - - /* NC: Checking for state DOWN because ss7box can - send CALL_STOP twice in a row. If we do not check for - STATE_DOWN we will set the state back to termnating - and block the channel forever - */ - - /* racing condition where both sides initiated a hangup - * Do not change current state as channel is already clearing - * itself through local initiated hangup */ - - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_STOPPED_ACK, - 0, 0); - ftdm_mutex_unlock(ftdmchan->mutex); - return; - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { - ftdmchan->init_state = FTDM_CHANNEL_STATE_TERMINATING; - ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to TERMINATING [Csid:%d]\n", event->call_setup_id); - OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; - ftdmchan->caller_data.hangup_cause = event->release_cause; - ftdm_mutex_unlock(ftdmchan->mutex); - return; - } else { - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, r); - } - - if (r == FTDM_SUCCESS) { - ftdmchan->caller_data.hangup_cause = event->release_cause; - } - - ftdm_mutex_unlock(ftdmchan->mutex); - - if (r) { - return; - } - } else { /* we have to do it ourselves.... */ - ftdm_log(FTDM_LOG_CRIT, "Odd, We could not find chan: s%dc%d\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - release_request_id_span_chan(BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - } -} - -/** - * \brief Handler for call answer event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_answer(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - - if ((ftdmchan = find_ftdmchan(span, event, 1))) { - ftdm_mutex_lock(ftdmchan->mutex); - - if (ftdm_test_sflag(ftdmchan, SFLAG_HANGUP) || - ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || - ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - - /* NC: Do nothing here because we are in process - of stopping the call. So ignore the ANSWER. */ - ftdm_log(FTDM_LOG_DEBUG, "Got answer but call is already hangup %d:%d\n", BOOST_EVENT_SPAN(mcon->sigmod, event), - BOOST_EVENT_CHAN(mcon->sigmod, event)); - - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { - ftdmchan->init_state = FTDM_CHANNEL_STATE_UP; - - } else { - int r = 0; - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_UP, r); - } - ftdm_mutex_unlock(ftdmchan->mutex); - } else { - ftdm_log(FTDM_LOG_CRIT, "Could not find channel %d:%d on answer message!\n", BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - sangomabc_exec_command(mcon, - event->span, - event->chan, - event->call_setup_id, - SIGBOOST_EVENT_CALL_STOPPED, - FTDM_CAUSE_DESTINATION_OUT_OF_ORDER, 0); - } -} - -static __inline__ void stop_loop(ftdm_channel_t *ftdmchan); - -/** - * \brief Handler for call start event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_call_start(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_event_t *event) -{ - ftdm_channel_t *ftdmchan = NULL; - int hangup_cause = FTDM_CAUSE_CALL_REJECTED; - int retry = 1; - -tryagain: - - if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 0))) { - if ((ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { - int r; - - /* NC: If we get CALL START and channel is in active state - then we are completely out of sync with the other end. - Treat CALL START as CALL STOP and hangup the current call. - The incoming call will also be NACKed. - */ - - if (ftdmchan->state == FTDM_CHANNEL_STATE_UP || - ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA || - ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS) { - ftdm_log(FTDM_LOG_CRIT, "s%dc%d: FTDMCHAN STATE UP -> Changed to TERMINATING\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, r); - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP || ftdm_test_sflag(ftdmchan, SFLAG_HANGUP)) { - ftdm_log(FTDM_LOG_CRIT, "s%dc%d: FTDMCHAN STATE HANGUP -> Changed to HANGUP COMPLETE\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, r); - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) { - ftdm_log(FTDM_LOG_WARNING, "s%dc%d: Collision, hanging up incoming call\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - /* dont hangup the outgoing call, the other side will send a call start nack too - * and there we will move to terminating. If we move to terminating here. We used to move - * to terminating here, but that introduces a problem in handle_call_start_nack where - * when receiving call start nack we move the channel from DOWN to TERMINATING ( cuz we already - * hangup here ) and the channel gets stuck in terminating forever. So at this point we're trusting - * the other side to send the call start nack ( or proceed with the call ) - * ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, r); - */ - } else if (ftdmchan->state == FTDM_CHANNEL_STATE_IN_LOOP && retry) { - retry = 0; - stop_loop(ftdmchan); - ftdm_channel_advance_states(ftdmchan); - goto tryagain; - } else { - ftdm_log(FTDM_LOG_ERROR, "s%dc%d: rejecting incoming call in channel state %s\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event), - ftdm_channel_state2str(ftdmchan->state)); - } - ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG); - ftdmchan = NULL; - } else { - ftdm_log(FTDM_LOG_CRIT, "s%dc%d: incoming call in invalid channel (channel not found)!\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - } - goto error; - } - - if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "s%dc%d: failed to open channel on incoming call, rejecting!\n", - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - goto error; - } - - ftdm_log(FTDM_LOG_DEBUG, "Got call start from s%dc%d mapped to freetdm logical s%dc%d, physical s%dc%d\n", - event->span, event->chan, - ftdmchan->span_id, ftdmchan->chan_id, - ftdmchan->physical_span_id, ftdmchan->physical_chan_id); - - ftdmchan->sflags = 0; - ftdm_set_string(ftdmchan->caller_data.cid_num.digits, (char *)event->calling.digits); - ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)event->calling.digits); - ftdm_set_string(ftdmchan->caller_data.ani.digits, (char *)event->calling.digits); - ftdm_set_string(ftdmchan->caller_data.dnis.digits, (char *)event->called.digits); - ftdm_set_string(ftdmchan->caller_data.rdnis.digits, (char *)event->rdnis.digits); - if (event->custom_data_size) { - ftdm_set_string(ftdmchan->caller_data.raw_data, event->custom_data); - ftdmchan->caller_data.raw_data_len = event->custom_data_size; - } - - if (strlen(event->calling_name)) { - ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)event->calling_name); - } - - ftdmchan->caller_data.cid_num.plan = event->calling.npi; - ftdmchan->caller_data.cid_num.type = event->calling.ton; - - ftdmchan->caller_data.ani.plan = event->calling.npi; - ftdmchan->caller_data.ani.type = event->calling.ton; - - ftdmchan->caller_data.dnis.plan = event->called.npi; - ftdmchan->caller_data.dnis.type = event->called.ton; - - ftdmchan->caller_data.rdnis.plan = event->rdnis.npi; - ftdmchan->caller_data.rdnis.type = event->rdnis.ton; - - ftdmchan->caller_data.screen = event->calling.screening_ind; - ftdmchan->caller_data.pres = event->calling.presentation_ind; - - ftdmchan->caller_data.bearer_capability = event->bearer.capability; - ftdmchan->caller_data.bearer_layer1 = event->bearer.uil1p; - - /* more info about custom data: http://www.ss7box.com/smg_manual.html#ISUP-IN-RDNIS-NEW */ - if (event->custom_data_size) { - char* p = NULL; - - p = strstr((char*)event->custom_data,"PRI001-ANI2-"); - if (p!=NULL) { - int ani2 = 0; - sscanf(p, "PRI001-ANI2-%d", &ani2); - snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", ani2); - } - } - - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); - return; - - error: - hangup_cause = ftdmchan ? ftdmchan->caller_data.hangup_cause : FTDM_CAUSE_REQUESTED_CHAN_UNAVAIL; - sangomabc_exec_command(mcon, - event->span, - event->chan, - event->call_setup_id, - SIGBOOST_EVENT_CALL_START_NACK, - hangup_cause, 0); - -} - -static void handle_call_loop_start(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_status_t res = FTDM_FAIL; - ftdm_channel_t *ftdmchan; - - if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 0))) { - ftdm_log(FTDM_LOG_CRIT, "CANNOT START LOOP, CHAN NOT AVAILABLE %d:%d\n", BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - return; - } - - if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_CRIT, "CANNOT START LOOP, CANT OPEN CHAN %d:%d\n", BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - return; - } - - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_IN_LOOP, res); - if (res != FTDM_SUCCESS) { - ftdm_channel_t *toclose = ftdmchan; - ftdm_log(FTDM_LOG_CRIT, "yay, could not set the state of the channel to IN_LOOP, loop will fail\n"); - ftdm_channel_close(&toclose); - return; - } - ftdm_log(FTDM_LOG_DEBUG, "%d:%d starting loop\n", ftdmchan->span_id, ftdmchan->chan_id); - ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_LOOP, NULL); -} - -static __inline__ void stop_loop(ftdm_channel_t *ftdmchan) -{ - ftdm_status_t res = FTDM_FAIL; - ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_LOOP, NULL); - /* even when we did not sent a msg we set this flag to avoid sending call stop in the DOWN state handler */ - ftdm_set_flag(ftdmchan, SFLAG_SENT_FINAL_MSG); - ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_DOWN, res); - if (res != FTDM_SUCCESS) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "yay, could not set the state of the channel from IN_LOOP to DOWN\n"); - } -} - -static void handle_call_loop_stop(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan; - if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { - ftdm_log(FTDM_LOG_CRIT, "CANNOT STOP LOOP, INVALID CHAN REQUESTED %d:%d\n", BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - return; - } - if (ftdmchan->state != FTDM_CHANNEL_STATE_IN_LOOP) { - ftdm_log(FTDM_LOG_WARNING, "Got stop loop request in a channel that is not in loop, ignoring ...\n"); - return; - } - stop_loop(ftdmchan); -} - -/** - * \brief Handler for heartbeat event - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static void handle_heartbeat(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - int err; - - err = sangomabc_connection_writep(mcon, (sangomabc_event_t*)event); - - if (err <= 0) { - ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost connection [%s]: %s\n", strerror(errno)); - } - return; -} - -/** - * \brief Handler for restart ack event - * \param mcon sangoma boost connection - * \param span Span where event was fired - * \param event Event to handle - */ -static void handle_restart_ack(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_short_event_t *event) -{ - ftdm_log(FTDM_LOG_DEBUG, "RECV RESTART ACK\n"); -} - -/** - * \brief Handler for restart event - * \param mcon sangoma boost connection - * \param span Span where event was fired - * \param event Event to handle - */ -static void handle_restart(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_short_event_t *event) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - mcon->rxseq_reset = 0; - ftdm_set_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); - ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); - ftdm_set_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); - -} - -/** - * \brief Handler for incoming digit event - * \param mcon sangoma boost connection - * \param span Span where event was fired - * \param event Event to handle - */ -static void handle_incoming_digit(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_event_t *event) -{ - ftdm_channel_t *ftdmchan = NULL; - char digits[MAX_DIALED_DIGITS + 2] = ""; - - if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t *)event, 1))) { - ftdm_log(FTDM_LOG_ERROR, "Invalid channel\n"); - return; - } - - if (event->called_number_digits_count == 0) { - ftdm_log(FTDM_LOG_WARNING, "Error Incoming digit with len %s %d [w%dg%d]\n", - event->called_number_digits, - event->called_number_digits_count, - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - return; - } - - ftdm_log(FTDM_LOG_WARNING, "Incoming digit with len %s %d [w%dg%d]\n", - event->called_number_digits, - event->called_number_digits_count, - BOOST_EVENT_SPAN(mcon->sigmod, event), BOOST_EVENT_CHAN(mcon->sigmod, event)); - - memcpy(digits, event->called_number_digits, event->called_number_digits_count); - ftdm_channel_queue_dtmf(ftdmchan, digits); - - return; -} - - -/** - * \brief Checks if span has state changes pending and processes - * \param span Span where event was fired - * \param event Event to handle - * \return The locked FTDM channel associated to the event if any, NULL otherwise - */ -static ftdm_channel_t* event_process_states(ftdm_span_t *span, sangomabc_short_event_t *event) -{ - ftdm_channel_t *ftdmchan = NULL; - ftdm_sangoma_boost_data_t *signal_data = span->signal_data; - - switch (event->event_id) { - case SIGBOOST_EVENT_CALL_START_NACK: - case SIGBOOST_EVENT_CALL_START_NACK_ACK: - if (event->call_setup_id && !signal_data->sigmod) { - return NULL; - } - //if event->span and event->chan is valid, fall-through - case SIGBOOST_EVENT_CALL_START: - case SIGBOOST_EVENT_CALL_START_ACK: - case SIGBOOST_EVENT_CALL_STOPPED: - case SIGBOOST_EVENT_CALL_PROGRESS: - case SIGBOOST_EVENT_CALL_ANSWERED: - case SIGBOOST_EVENT_CALL_STOPPED_ACK: - case SIGBOOST_EVENT_DIGIT_IN: - case SIGBOOST_EVENT_INSERT_CHECK_LOOP: - case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: - case SIGBOOST_EVENT_CALL_RELEASED: - if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { - ftdm_log(FTDM_LOG_CRIT, "could not find channel %d:%d to process pending state changes!\n", - BOOST_EVENT_SPAN(signal_data->sigmod, event), - BOOST_EVENT_CHAN(signal_data->sigmod, event)); - return NULL; - } - break; - case SIGBOOST_EVENT_HEARTBEAT: - case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: - case SIGBOOST_EVENT_SYSTEM_RESTART: - case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: - return NULL; - default: - ftdm_log(FTDM_LOG_CRIT, "Unhandled event id: %d\n", event->event_id); - return NULL; - } - - ftdm_mutex_lock(ftdmchan->mutex); - ftdm_channel_advance_states(ftdmchan); - return ftdmchan; -} - -/** - * \brief Handler for sangoma boost event - * \param span Span where event was fired - * \param mcon sangoma boost connection - * \param event Event to handle - */ -static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) -{ - ftdm_channel_t* ftdmchan = NULL; - - if (!ftdm_running()) { - ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n"); - return -1; - } - - ftdm_assert_return(event->call_setup_id <= MAX_REQ_ID, -1, "Unexpected call setup id\n"); - - /* process all pending state changes for that channel before - * processing the new boost event */ - ftdmchan = event_process_states(span, event); - - switch(event->event_id) { - case SIGBOOST_EVENT_CALL_START: - handle_call_start(span, mcon, (sangomabc_event_t*)event); - break; - case SIGBOOST_EVENT_CALL_STOPPED: - handle_call_stop(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_RELEASED: - handle_call_released(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_START_ACK: - handle_call_start_ack(mcon, event); - break; - case SIGBOOST_EVENT_CALL_PROGRESS: - handle_call_progress(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_START_NACK: - handle_call_start_nack(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_ANSWERED: - handle_call_answer(span, mcon, event); - break; - case SIGBOOST_EVENT_HEARTBEAT: - handle_heartbeat(mcon, event); - break; - case SIGBOOST_EVENT_CALL_STOPPED_ACK: - handle_call_done(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_START_NACK_ACK: - /* On NACK ack span chan are always invalid - All there is to do is to clear the id */ - if (event->call_setup_id) { - nack_map[event->call_setup_id] = 0; - release_request_id(event->call_setup_id); - } else { - handle_call_done(span, mcon, event); - } - break; - case SIGBOOST_EVENT_INSERT_CHECK_LOOP: - handle_call_loop_start(span, mcon, event); - break; - case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: - handle_call_loop_stop(span, mcon, event); - break; - case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: - handle_restart_ack(mcon, span, event); - break; - case SIGBOOST_EVENT_SYSTEM_RESTART: - handle_restart(mcon, span, event); - break; - case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: - //handle_gap_abate(event); - break; - case SIGBOOST_EVENT_DIGIT_IN: - handle_incoming_digit(mcon, span, (sangomabc_event_t*)event); - break; - default: - ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", sangomabc_event_id_name(event->event_id)); - break; - } - - if(ftdmchan != NULL) { - ftdm_channel_advance_states(ftdmchan); - ftdm_mutex_unlock(ftdmchan->mutex); - } - - return 0; - -} - -/** - * \brief Handler for channel state change - * \param ftdmchan Channel to handle - */ -static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - sangomabc_connection_t *mcon = &sangoma_boost_data->mcon; - ftdm_sigmsg_t sig; - ftdm_status_t status; - - - ftdm_assert_return(ftdmchan->last_state != ftdmchan->state, FTDM_FAIL, "Channel state already processed\n"); - - ftdm_log(FTDM_LOG_DEBUG, "%d:%d PROCESSING STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); - - memset(&sig, 0, sizeof(sig)); - sig.chan_id = ftdmchan->chan_id; - sig.span_id = ftdmchan->span_id; - sig.channel = ftdmchan; - - ftdm_channel_complete_state(ftdmchan); - - switch (ftdmchan->state) { - case FTDM_CHANNEL_STATE_DOWN: - { - int call_stopped_ack_sent = 0; - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - - if (ftdmchan->last_state == FTDM_CHANNEL_STATE_IN_LOOP) { - ftdm_log(FTDM_LOG_DEBUG, "%d:%d terminating loop\n", ftdmchan->span_id, ftdmchan->chan_id); - } else { - release_request_id_span_chan(ftdmchan->physical_span_id, ftdmchan->physical_chan_id); - - if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG)) { - ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); - - if (ftdmchan->call_data && CALL_DATA(ftdmchan)->last_event_id == SIGBOOST_EVENT_CALL_START_NACK) { - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - CALL_DATA(ftdmchan)->call_setup_id, - SIGBOOST_EVENT_CALL_START_NACK_ACK, - 0, 0); - - } else { - /* we got a call stop msg, time to reply with call stopped ack */ - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_STOPPED_ACK, - 0, 0); - call_stopped_ack_sent = 1; - } - } - } - - ftdmchan->sflags = 0; - memset(ftdmchan->call_data, 0, sizeof(sangoma_boost_call_t)); - if (sangoma_boost_data->sigmod && call_stopped_ack_sent) { - /* we dont want to call ftdm_channel_close just yet until call released is received */ - ftdm_log(FTDM_LOG_DEBUG, "Waiting for call release confirmation before declaring chan %d:%d as available \n", - ftdmchan->span_id, ftdmchan->chan_id); - } else { - ftdm_channel_t *toclose = ftdmchan; - ftdm_channel_close(&toclose); - } - } - break; - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; - if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } else { - if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_ACK)) { - ftdm_set_sflag(ftdmchan, SFLAG_SENT_ACK); - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_START_ACK, - 0, 0); - } - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_PROGRESS, - 0, SIGBOOST_PROGRESS_MEDIA); - } - } - break; - case FTDM_CHANNEL_STATE_PROGRESS: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_PROGRESS; - if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } else { - if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_ACK)) { - ftdm_set_sflag(ftdmchan, SFLAG_SENT_ACK); - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_START_ACK, - 0, SIGBOOST_PROGRESS_RING); - } - } - } - break; - case FTDM_CHANNEL_STATE_IDLE: - case FTDM_CHANNEL_STATE_HOLD: - { - /* twiddle */ - } - break; - case FTDM_CHANNEL_STATE_RING: - { - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_START; - if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } - } - break; - case FTDM_CHANNEL_STATE_RESTART: - { - sig.event_id = FTDM_SIGEVENT_RESTART; - status = ftdm_span_send_signal(ftdmchan->span, &sig); - ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - break; - case FTDM_CHANNEL_STATE_UP: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_UP; - if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } else { - if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) { - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_START_ACK, - 0, 0); - } - - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_ANSWERED, - 0, 0); - } - } - break; - case FTDM_CHANNEL_STATE_DIALING: - { - char dnis[128] = ""; - sangoma_boost_request_id_t r; - sangomabc_event_t event = {0}; - - ftdm_assert(sangoma_boost_data->sigmod != NULL, "We should be in sigmod here!\n"); - - ftdm_set_string(dnis, ftdmchan->caller_data.dnis.digits); - - r = next_request_id(); - if (r == 0) { - ftdm_log(FTDM_LOG_CRIT, "All boost request ids are busy.\n"); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); - break; - } - - sangomabc_call_init(&event, ftdmchan->caller_data.cid_num.digits, dnis, r); - - event.span = (uint8_t)ftdmchan->physical_span_id; - event.chan = (uint8_t)ftdmchan->physical_chan_id; - /* because we already have a span/chan here we bind to the SETUP_GRID now and not on call start ack */ - SETUP_GRID[event.span][event.chan] = event.call_setup_id; - - ftdm_set_string(event.calling_name, ftdmchan->caller_data.cid_name); - ftdm_set_string(event.rdnis.digits, ftdmchan->caller_data.rdnis.digits); - if (strlen(ftdmchan->caller_data.rdnis.digits)) { - event.rdnis.digits_count = (uint8_t)strlen(ftdmchan->caller_data.rdnis.digits)+1; - event.rdnis.ton = ftdmchan->caller_data.rdnis.type; - event.rdnis.npi = ftdmchan->caller_data.rdnis.plan; - } - - event.calling.screening_ind = ftdmchan->caller_data.screen; - event.calling.presentation_ind = ftdmchan->caller_data.pres; - - event.calling.ton = ftdmchan->caller_data.cid_num.type; - event.calling.npi = ftdmchan->caller_data.cid_num.plan; - - event.called.ton = ftdmchan->caller_data.dnis.type; - event.called.npi = ftdmchan->caller_data.dnis.plan; - - if (ftdmchan->caller_data.raw_data_len) { - ftdm_set_string(event.custom_data, ftdmchan->caller_data.raw_data); - event.custom_data_size = (uint16_t)ftdmchan->caller_data.raw_data_len; - } - - OUTBOUND_REQUESTS[r].status = BST_WAITING; - OUTBOUND_REQUESTS[r].span = ftdmchan->span; - OUTBOUND_REQUESTS[r].ftdmchan = ftdmchan; - - ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s over boost channel with request id %d\n", event.called_number_digits, r); - if (sangomabc_connection_write(&sangoma_boost_data->mcon, &event) <= 0) { - release_request_id(r); - ftdm_log(FTDM_LOG_CRIT, "Failed to tx boost event [%s]\n", strerror(errno)); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); - } - - } - break; - case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: - { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - break; - case FTDM_CHANNEL_STATE_HANGUP: - { - ftdm_set_sflag_locked(ftdmchan, SFLAG_HANGUP); - - if (ftdm_test_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG) || ftdm_test_sflag(ftdmchan, SFLAG_TERMINATING)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); - } else { - ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || - ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || - ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA) || - ftdm_test_sflag(ftdmchan, SFLAG_RECVD_ACK)) { - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_STOPPED, - ftdmchan->caller_data.hangup_cause, 0); - } else { - sangomabc_exec_command(mcon, - BOOST_SPAN(ftdmchan), - BOOST_CHAN(ftdmchan), - 0, - SIGBOOST_EVENT_CALL_START_NACK, - ftdmchan->caller_data.hangup_cause, 0); - } - } - } - break; - case FTDM_CHANNEL_STATE_TERMINATING: - { - ftdm_set_sflag_locked(ftdmchan, SFLAG_TERMINATING); - sig.event_id = FTDM_SIGEVENT_STOP; - status = ftdm_span_send_signal(ftdmchan->span, &sig); - } - break; - case FTDM_CHANNEL_STATE_IN_LOOP: - { - /* nothing to do, we sent the FTDM_COMMAND_ENABLE_LOOP command in handle_call_loop_start() right away */ - } - break; - default: - break; - } - return FTDM_SUCCESS; -} - -/** - * \brief Initialises outgoing requests array - */ -static __inline__ void init_outgoing_array(void) -{ - memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS)); -} - -/** - * \brief Checks current state on a span - * \param span Span to check status on - */ -static __inline__ void check_state(ftdm_span_t *span) -{ - ftdm_channel_t *ftdmchan = NULL; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - int susp = ftdm_test_flag(span, FTDM_SPAN_SUSPENDED); - - if (susp && ftdm_check_state_all(span, FTDM_CHANNEL_STATE_DOWN)) { - susp = 0; - } - - if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE) || susp) { - uint32_t j; - ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); - if (susp) { - for(j = 1; j <= span->chan_count; j++) { - if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE) || susp) { - ftdm_mutex_lock(span->channels[j]->mutex); - ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); - if (susp && span->channels[j]->state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_set_state(span->channels[j], FTDM_CHANNEL_STATE_RESTART); - } - ftdm_channel_advance_states(span->channels[j]); - ftdm_mutex_unlock(span->channels[j]->mutex); - } - } - } else { - while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) { - /* it can happen that someone else processed the chan states - * but without taking the chan out of the queue, so check th - * flag before advancing the state */ - ftdm_mutex_lock(ftdmchan->mutex); - ftdm_channel_advance_states(ftdmchan); - ftdm_mutex_unlock(ftdmchan->mutex); - } - } - } - - if (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING)) { - if (ftdm_check_state_all(span, FTDM_CHANNEL_STATE_DOWN)) { - sangomabc_exec_command(&sangoma_boost_data->mcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART_ACK, - 0, 0); - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); - ftdm_clear_flag_locked(span, FTDM_SPAN_SUSPENDED); - ftdm_clear_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); - init_outgoing_array(); - } - } -} - - -/** - * \brief Checks for events on a span - * \param span Span to check for events - */ -static __inline__ ftdm_status_t check_events(ftdm_span_t *span, int ms_timeout) -{ - ftdm_status_t status; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - status = ftdm_span_poll_event(span, ms_timeout, NULL); - - switch(status) { - case FTDM_SUCCESS: - { - ftdm_event_t *event; - while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { - switch (event->enum_id) { - case FTDM_OOB_ALARM_TRAP: - if (sangoma_boost_data->sigmod) { - sangoma_boost_data->sigmod->on_hw_link_status_change(event->channel, FTDM_HW_LINK_DISCONNECTED); - } - break; - case FTDM_OOB_ALARM_CLEAR: - if (sangoma_boost_data->sigmod) { - sangoma_boost_data->sigmod->on_hw_link_status_change(event->channel, FTDM_HW_LINK_CONNECTED); - } - break; - } - } - } - break; - case FTDM_FAIL: - { - if (!ftdm_running()) { - break; - } - ftdm_log(FTDM_LOG_ERROR, "Boost Check Event Failure Failure: %s\n", span->last_error); - return FTDM_FAIL; - } - break; - default: - break; - } - - return FTDM_SUCCESS; -} - -/** - * \brief Main thread function for sangoma boost span (monitor) - * \param me Current thread - * \param obj Span to run in this thread - */ -static void *ftdm_sangoma_events_run(ftdm_thread_t *me, void *obj) -{ - ftdm_span_t *span = (ftdm_span_t *) obj; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - unsigned errs = 0; - - while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_EVENTS_RUNNING) && ftdm_running()) { - if (check_events(span, 100) != FTDM_SUCCESS) { - if (errs++ > 50) { - ftdm_log(FTDM_LOG_ERROR, "Too many event errors, quitting sangoma events thread\n"); - return NULL; - } - } - } - - ftdm_log(FTDM_LOG_DEBUG, "Sangoma Boost Events thread ended.\n"); - - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_EVENTS_RUNNING); - - return NULL; -} - -static ftdm_status_t ftdm_boost_connection_open(ftdm_span_t *span) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - if (sangoma_boost_data->sigmod) { - if (sangoma_boost_data->sigmod->start_span(span) != FTDM_SUCCESS) { - return FTDM_FAIL; - } - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); - ftdm_clear_flag_locked(span, FTDM_SPAN_SUSPENDED); - ftdm_clear_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); - } - - sangoma_boost_data->pcon = sangoma_boost_data->mcon; - - /* when sigmod is present, all arguments: local_ip etc, are ignored by sangomabc_connection_open */ - if (sangomabc_connection_open(&sangoma_boost_data->mcon, - sangoma_boost_data->mcon.cfg.local_ip, - sangoma_boost_data->mcon.cfg.local_port, - sangoma_boost_data->mcon.cfg.remote_ip, - sangoma_boost_data->mcon.cfg.remote_port) < 0) { - ftdm_log(FTDM_LOG_ERROR, "Error: Opening MCON Socket [%d] %s\n", sangoma_boost_data->mcon.socket, strerror(errno)); - return FTDM_FAIL; - } - - if (sangomabc_connection_open(&sangoma_boost_data->pcon, - sangoma_boost_data->pcon.cfg.local_ip, - ++sangoma_boost_data->pcon.cfg.local_port, - sangoma_boost_data->pcon.cfg.remote_ip, - ++sangoma_boost_data->pcon.cfg.remote_port) < 0) { - ftdm_log(FTDM_LOG_ERROR, "Error: Opening PCON Socket [%d] %s\n", sangoma_boost_data->pcon.socket, strerror(errno)); - return FTDM_FAIL; - } - - /* try to create the boost sockets interrupt objects */ - if (ftdm_interrupt_create(&sangoma_boost_data->pcon.sock_interrupt, sangoma_boost_data->pcon.socket) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost msock interrupt!\n", span->name); - return FTDM_FAIL; - } - - if (ftdm_interrupt_create(&sangoma_boost_data->mcon.sock_interrupt, sangoma_boost_data->mcon.socket) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost psock interrupt!\n", span->name); - return FTDM_FAIL; - } - - return FTDM_SUCCESS; -} - -/*! - \brief wait for a boost event - \return -1 on error, 0 on timeout, 1 when there are events - */ -static int ftdm_boost_wait_event(ftdm_span_t *span) -{ - ftdm_status_t res; - ftdm_interrupt_t *ints[3]; - int numints; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - ftdm_queue_get_interrupt(span->pendingchans, &ints[0]); - numints = 1; - /* if in queue mode wait for both the pendingchans queue and the boost msg queue */ - if (sangoma_boost_data->sigmod) { - ftdm_queue_get_interrupt(sangoma_boost_data->boost_queue, &ints[1]); - numints = 2; - } -#ifndef __WINDOWS__ - else { - /* socket mode ... */ - ints[1] = sangoma_boost_data->mcon.sock_interrupt; - ints[2] = sangoma_boost_data->pcon.sock_interrupt; - numints = 3; - sangoma_boost_data->iteration = 0; - } -#endif - res = ftdm_interrupt_multiple_wait(ints, numints, 100); - if (FTDM_SUCCESS != res && FTDM_TIMEOUT != res) { - ftdm_log(FTDM_LOG_CRIT, "Unexpected return value from interrupt waiting: %d\n", res); - return -1; - } - return 0; -} - - -static sangomabc_event_t *ftdm_boost_read_event(ftdm_span_t *span) -{ - sangomabc_event_t *event = NULL; - sangomabc_connection_t *mcon, *pcon; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - mcon = &sangoma_boost_data->mcon; - pcon = &sangoma_boost_data->pcon; - - event = sangomabc_connection_readp(pcon, sangoma_boost_data->iteration); - - /* if there is no event and this is not a sigmod-driven span it's time to try the other connection for events */ - if (!event && !sangoma_boost_data->sigmod) { - event = sangomabc_connection_read(mcon, sangoma_boost_data->iteration); - } - - return event; -} - -/** - * \brief Main thread function for sangoma boost span (monitor) - * \param me Current thread - * \param obj Span to run in this thread - */ -static void *ftdm_sangoma_boost_run(ftdm_thread_t *me, void *obj) -{ - ftdm_span_t *span = (ftdm_span_t *) obj; - sangomabc_connection_t *mcon, *pcon; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - mcon = &sangoma_boost_data->mcon; - pcon = &sangoma_boost_data->pcon; - - /* sigmod overrides socket functionality if not null */ - if (sangoma_boost_data->sigmod) { - mcon->span = span; - pcon->span = span; - /* everything could be retrieved through span, but let's use shortcuts */ - mcon->sigmod = sangoma_boost_data->sigmod; - pcon->sigmod = sangoma_boost_data->sigmod; - mcon->boost_queue = sangoma_boost_data->boost_queue; - pcon->boost_queue = sangoma_boost_data->boost_queue; - } - - if (ftdm_boost_connection_open(span) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_CRIT, "ftdm_boost_connection_open failed\n"); - goto end; - } - - init_outgoing_array(); - if (!sangoma_boost_data->sigmod) { - sangomabc_exec_commandp(pcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART, - 0); - ftdm_set_flag(mcon, MSU_FLAG_DOWN); - } - - while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING)) { - sangomabc_event_t *event = NULL; - - if (!ftdm_running()) { - if (!sangoma_boost_data->sigmod) { - sangomabc_exec_commandp(pcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART, - 0); - ftdm_set_flag(mcon, MSU_FLAG_DOWN); - } - ftdm_log(FTDM_LOG_DEBUG, "ftdm is no longer running\n"); - break; - } - - if (ftdm_boost_wait_event(span) < 0) { - ftdm_log(FTDM_LOG_ERROR, "ftdm_boost_wait_event failed\n"); - } - - while ((event = ftdm_boost_read_event(span))) { - parse_sangoma_event(span, pcon, (sangomabc_short_event_t*)event); - sangoma_boost_data->iteration++; - } - - check_state(span); - } - -end: - if (!sangoma_boost_data->sigmod) { - sangomabc_connection_close(&sangoma_boost_data->mcon); - sangomabc_connection_close(&sangoma_boost_data->pcon); - } - - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); - - ftdm_log(FTDM_LOG_DEBUG, "Sangoma Boost thread ended.\n"); - return NULL; -} - -#if 0 -static int sigmod_ss7box_isup_exec_cmd(ftdm_stream_handle_t *stream, char *cmd) -{ - FILE *fp; - int status=0; - char path[1024]; - - fp = popen(cmd, "r"); - if (fp == NULL) { - stream->write_function(stream, "%s: -ERR failed to execute cmd: %s\n", - __FILE__,cmd); - return -1; - } - - while (fgets(path, sizeof(path)-1, fp) != NULL) { - path[sizeof(path)-1]='\0'; - stream->write_function(stream,"%s", path); - } - - - status = pclose(fp); - if (status == -1) { - /* Error reported by pclose() */ - } else { - /* Use macros described under wait() to inspect `status' in order - to determine success/failure of command executed by popen() */ - } - - return status; -} -#endif - -static void ftdm_cli_span_state_cmd(ftdm_span_t *span, char *state) -{ - unsigned int j; - int cnt=0; - ftdm_channel_state_t state_e = ftdm_str2ftdm_channel_state(state); - if (state_e == FTDM_CHANNEL_STATE_INVALID) { - ftdm_log(FTDM_LOG_CRIT, "Checking for channels not in the INVALID state is probably not what you want\n"); - } - for(j = 1; j <= span->chan_count; j++) { - if (span->channels[j]->state != state_e) { - ftdm_channel_t *ftdmchan = span->channels[j]; - ftdm_log(FTDM_LOG_CRIT, "Channel %i s%dc%d State=%s\n", - j, ftdmchan->physical_span_id-1, ftdmchan->physical_chan_id-1, ftdm_channel_state2str(ftdmchan->state)); - cnt++; - } - } - ftdm_log(FTDM_LOG_CRIT, "Total Channel Cnt %i\n",cnt); -} - -#define FTDM_BOOST_SYNTAX "list sigmods | | tracelevel " -/** - * \brief API function to kill or debug a sangoma_boost span - * \param stream API stream handler - * \param data String containing argurments - * \return Flags - */ -static FIO_API_FUNCTION(ftdm_sangoma_boost_api) -{ - char *mycmd = NULL, *argv[10] = { 0 }; - int argc = 0; - - if (data) { - mycmd = ftdm_strdup(data); - argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); - } - - if (argc > 1) { - if (!strcasecmp(argv[0], "list")) { - if (!strcasecmp(argv[1], "sigmods")) { - if (ftdm_sangoma_boost_list_sigmods(stream) != FTDM_SUCCESS) { - stream->write_function(stream, "-ERR failed to list sigmods\n"); - goto done; - } - goto done; - } - - if (!strcasecmp(argv[1], "ids")) { - print_request_ids(); - goto done; - } - } else if (!strcasecmp(argv[0], "tracelevel")) { - ftdm_status_t status; - const char *levelname = NULL; - int dbglevel; - ftdm_sangoma_boost_data_t *sangoma_boost_data; - ftdm_span_t *span; - - if (argc <= 2) { - stream->write_function(stream, "-ERR usage: tracelevel \n"); - goto done; - } - - status = ftdm_span_find_by_name(argv[1], &span); - if (FTDM_SUCCESS != status) { - stream->write_function(stream, "-ERR failed to find span by name %s\n", argv[1]); - goto done; - } - - if (span->signal_type != FTDM_SIGTYPE_SANGOMABOOST) { - stream->write_function(stream, "-ERR span %s is not of boost type\n", argv[1]); - goto done; - } - - for (dbglevel = 0; (levelname = FTDM_LEVEL_NAMES[dbglevel]); dbglevel++) { - if (!strcasecmp(levelname, argv[2])) { - break; - } - } - - if (!levelname) { - stream->write_function(stream, "-ERR invalid log level %s\n", argv[2]); - goto done; - } - - sangoma_boost_data = span->signal_data; - sangoma_boost_data->pcon.debuglevel = dbglevel; - sangoma_boost_data->mcon.debuglevel = dbglevel; - stream->write_function(stream, "+OK span %s has now trace level %s\n", argv[1], FTDM_LEVEL_NAMES[dbglevel]); - goto done; -#ifndef __WINDOWS__ -#if 0 -/* NC: This code crashes the kernel due to fork on heavy fs load */ - } else if (!strcasecmp(argv[0], "ss7box_isupd_ckt")) { - - if (!strcasecmp(argv[1], "used")) { - stream->write_function(stream, "ss7box_isupd: in use\n", FTDM_BOOST_SYNTAX); - sigmod_ss7box_isup_exec_cmd(stream, (char*) "ckt_report.sh inuse"); - } else if (!strcasecmp(argv[1], "reset")) { - stream->write_function(stream, "ss7box_isupd: in reset\n", FTDM_BOOST_SYNTAX); - sigmod_ss7box_isup_exec_cmd(stream, (char*) "ckt_report.sh reset"); - } else if (!strcasecmp(argv[1], "ready")) { - stream->write_function(stream, "ss7box_isupd: ready \n", FTDM_BOOST_SYNTAX); - sigmod_ss7box_isup_exec_cmd(stream, (char*) "ckt_report.sh free"); - } else { - stream->write_function(stream, "ss7box_isupd: list\n", FTDM_BOOST_SYNTAX); - sigmod_ss7box_isup_exec_cmd(stream, (char*) "ckt_report.sh"); - } - - goto done; -#endif -#endif - - } else if (!strcasecmp(argv[0], "span")) { - int err; - sangomabc_connection_t *pcon; - ftdm_sangoma_boost_data_t *sangoma_boost_data; - ftdm_span_t *span; - - if (argc <= 2) { - stream->write_function(stream, "-ERR invalid span usage: span \n"); - goto done; - } - - err = ftdm_span_find_by_name(argv[1], &span); - if (FTDM_SUCCESS != err) { - stream->write_function(stream, "-ERR failed to find span by name %s\n",argv[1]); - goto done; - } - - if (!strcasecmp(argv[2], "restart")) { - sangoma_boost_data = span->signal_data; - pcon = &sangoma_boost_data->pcon; - - /* No need to set any span flags because - our RESTART will generate a RESTART from the sig daemon */ - sangomabc_exec_commandp(pcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART, - 0); - } else if (!strcasecmp(argv[2], "state")) { - if (argc <= 3) { - stream->write_function(stream, "-ERR invalid span state: span state \n"); - goto done; - } - ftdm_cli_span_state_cmd(span,argv[3]); - } - - goto done; - - } else { - boost_sigmod_interface_t *sigmod_iface = NULL; - sigmod_iface = hashtable_search(g_boost_modules_hash, argv[0]); - if (sigmod_iface) { - char *p = strchr(data, ' '); - if (++p) { - char* mydup = strdup(p); - if(sigmod_iface->exec_api == NULL) { - stream->write_function(stream, "%s does not support api functions\n", sigmod_iface->name); - goto done; - } - //stream->write_function(stream, "sigmod:%s command:%s\n", sigmod_iface->name, mydup); - if (sigmod_iface->exec_api(stream, mydup) != FTDM_SUCCESS) { - stream->write_function(stream, "-ERR:failed to execute command:%s\n", mydup); - } - free(mydup); - } - - goto done; - } else { - stream->write_function(stream, "-ERR: Could not find sigmod %s\n", argv[0]); - } - } - } - stream->write_function(stream, "-ERR: Usage: %s\n", FTDM_BOOST_SYNTAX); -done: - ftdm_safe_free(mycmd); - return FTDM_SUCCESS; -} - -/** - * \brief Loads sangoma_boost IO module - * \param fio FreeTDM IO interface - * \return Success - */ -static FIO_IO_LOAD_FUNCTION(ftdm_sangoma_boost_io_init) -{ - ftdm_assert(fio != NULL, "fio is NULL"); - memset(&ftdm_sangoma_boost_interface, 0, sizeof(ftdm_sangoma_boost_interface)); - - ftdm_sangoma_boost_interface.name = "boost"; - ftdm_sangoma_boost_interface.api = ftdm_sangoma_boost_api; - - *fio = &ftdm_sangoma_boost_interface; - - return FTDM_SUCCESS; -} - -/** - * \brief Loads sangoma boost signaling module - * \param fio FreeTDM IO interface - * \return Success - */ -static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_boost_init) -{ - g_boost_modules_hash = create_hashtable(10, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); - if (!g_boost_modules_hash) { - return FTDM_FAIL; - } - ftdm_mutex_create(&request_mutex); - ftdm_mutex_create(&g_boost_modules_mutex); - memset(&g_trunkgroups[0], 0, sizeof(g_trunkgroups)); - return FTDM_SUCCESS; -} - -static FIO_SIG_UNLOAD_FUNCTION(ftdm_sangoma_boost_destroy) -{ - ftdm_hash_iterator_t *i = NULL; - boost_sigmod_interface_t *sigmod = NULL; - const void *key = NULL; - void *val = NULL; - ftdm_dso_lib_t lib; - ftdm_log(FTDM_LOG_DEBUG, "Destroying sangoma boost module\n"); - for (i = hashtable_first(g_boost_modules_hash); i; i = hashtable_next(i)) { - hashtable_this(i, &key, NULL, &val); - if (key && val) { - sigmod = val; - lib = sigmod->pvt; - ftdm_log(FTDM_LOG_DEBUG, "destroying sigmod %s\n", sigmod->name); - sigmod->on_unload(); - ftdm_dso_destroy(&lib); - } - } - - hashtable_destroy(g_boost_modules_hash); - ftdm_mutex_destroy(&request_mutex); - ftdm_mutex_destroy(&g_boost_modules_mutex); - return FTDM_SUCCESS; -} - -static ftdm_status_t ftdm_sangoma_boost_start(ftdm_span_t *span) -{ - int err; - - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - ftdm_set_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); - err = ftdm_thread_create_detached(ftdm_sangoma_boost_run, span); - if (err) { - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); - return err; - } - - // launch the events thread to handle HW DTMF and possibly - // other events in the future - ftdm_set_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_EVENTS_RUNNING); - err = ftdm_thread_create_detached(ftdm_sangoma_events_run, span); - if (err) { - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_EVENTS_RUNNING); - ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); - } - - return err; -} - -static ftdm_status_t ftdm_sangoma_boost_stop(ftdm_span_t *span) -{ - int cnt = 50; - ftdm_status_t status = FTDM_SUCCESS; - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - - if (sangoma_boost_data->sigmod) { - /* I think stopping the span before destroying the queue makes sense - otherwise may be boost events would still arrive when the queue is already destroyed! */ - status = sangoma_boost_data->sigmod->stop_span(span); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_CRIT, "Failed to stop span %s boost signaling\n", span->name); - return FTDM_FAIL; - } - ftdm_queue_enqueue(sangoma_boost_data->boost_queue, NULL); - } - - while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING) && cnt-- > 0) { - ftdm_log(FTDM_LOG_DEBUG, "Waiting for boost thread\n"); - ftdm_sleep(100); - } - - if (!cnt) { - ftdm_log(FTDM_LOG_CRIT, "it seems boost thread in span %s may be stuck, we may segfault :-(\n", span->name); - return FTDM_FAIL; - } - - cnt = 50; - while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_EVENTS_RUNNING) && cnt-- > 0) { - ftdm_log(FTDM_LOG_DEBUG, "Waiting for boost events thread\n"); - ftdm_sleep(100); - } - - if (!cnt) { - ftdm_log(FTDM_LOG_CRIT, "it seems boost events thread in span %s may be stuck, we may segfault :-(\n", span->name); - return FTDM_FAIL; - } - - if (sangoma_boost_data->sigmod) { - ftdm_queue_destroy(&sangoma_boost_data->boost_queue); - } - - return status; -} - -static ftdm_state_map_t boost_state_map = { - { - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_ANY_STATE}, - {FTDM_CHANNEL_STATE_RESTART, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, - {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_DIALING, FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_HOLD, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_HOLD, FTDM_END}, - {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, - FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_HANGUP, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_DIALING, FTDM_END}, - {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_DIALING, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_HANGUP, FTDM_END} - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, - }, - { - ZSD_OUTBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_UP, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} - }, - - /****************************************/ - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_ANY_STATE}, - {FTDM_CHANNEL_STATE_RESTART, FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_DOWN}, - {FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_IN_LOOP}, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, - {FTDM_CHANNEL_STATE_RING, FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_RING, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA,FTDM_END} - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_HANGUP, FTDM_END}, - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, - {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, - }, - { - ZSD_INBOUND, - ZSM_UNACCEPTABLE, - {FTDM_CHANNEL_STATE_UP, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, - }, - - - } -}; - -static BOOST_WRITE_MSG_FUNCTION(ftdm_boost_write_msg) -{ - sangomabc_short_event_t *shortmsg = NULL; - ftdm_sangoma_boost_data_t *sangoma_boost_data = NULL; - sangomabc_queue_element_t *element = NULL; - - ftdm_assert_return(msg != NULL, FTDM_FAIL, "Boost message to write was null"); - - if (!span) { - shortmsg = msg; - ftdm_log(FTDM_LOG_ERROR, "Unexpected boost message %d\n", shortmsg->event_id); - return FTDM_FAIL; - } - /* duplicate the event and enqueue it */ - element = ftdm_calloc(1, sizeof(*element)); - if (!element) { - return FTDM_FAIL; - } - memcpy(element->boostmsg, msg, msglen); - element->size = msglen; - - sangoma_boost_data = span->signal_data; - return ftdm_queue_enqueue(sangoma_boost_data->boost_queue, element); -} - -static BOOST_SIG_STATUS_CB_FUNCTION(ftdm_boost_sig_status_change) -{ - ftdm_sigmsg_t sig; - ftdm_log(FTDM_LOG_NOTICE, "%d:%d Signaling link status changed to %s\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_signaling_status2str(status)); - - memset(&sig, 0, sizeof(sig)); - sig.chan_id = ftdmchan->chan_id; - sig.span_id = ftdmchan->span_id; - sig.channel = ftdmchan; - sig.event_id = FTDM_SIGEVENT_SIGSTATUS_CHANGED; - sig.ev_data.sigstatus.status = status; - ftdm_span_send_signal(ftdmchan->span, &sig); - return; -} - -static FIO_CHANNEL_SET_SIG_STATUS_FUNCTION(sangoma_boost_set_channel_sig_status) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - if (!sangoma_boost_data->sigmod) { - ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost channel with no signaling module configured\n"); - return FTDM_FAIL; - } - if (!sangoma_boost_data->sigmod->set_channel_sig_status) { - ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost channel: method not implemented\n"); - return FTDM_NOTIMPL; - } - return sangoma_boost_data->sigmod->set_channel_sig_status(ftdmchan, status); -} - -static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(sangoma_boost_get_channel_sig_status) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; - if (!sangoma_boost_data->sigmod) { - return FTDM_FAIL; - } - if (!sangoma_boost_data->sigmod->get_channel_sig_status) { - return FTDM_NOTIMPL; - } - return sangoma_boost_data->sigmod->get_channel_sig_status(ftdmchan, status); -} - -static FIO_SPAN_SET_SIG_STATUS_FUNCTION(sangoma_boost_set_span_sig_status) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - if (!sangoma_boost_data->sigmod) { - ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost span with no signaling module configured\n"); - return FTDM_FAIL; - } - if (!sangoma_boost_data->sigmod->set_span_sig_status) { - ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost span: method not implemented\n"); - return FTDM_NOTIMPL; - } - return sangoma_boost_data->sigmod->set_span_sig_status(span, status); -} - -static FIO_SPAN_GET_SIG_STATUS_FUNCTION(sangoma_boost_get_span_sig_status) -{ - ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; - if (!sangoma_boost_data->sigmod) { - return FTDM_FAIL; - } - if (!sangoma_boost_data->sigmod->get_span_sig_status) { - ftdm_log(FTDM_LOG_ERROR, "Cannot get signaling status in boost span: method not implemented\n"); - return FTDM_NOTIMPL; - } - return sangoma_boost_data->sigmod->get_span_sig_status(span, status); -} - -/** - * \brief Initialises an sangoma boost span from configuration variables - * \param span Span to configure - * \param sig_cb Callback function for event signals - * \param ap List of configuration variables - * \return Success or failure - */ -static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_boost_configure_span) -{ -#define FAIL_CONFIG_RETURN(retstatus) \ - if (sangoma_boost_data) \ - ftdm_safe_free(sangoma_boost_data); \ - if (err) \ - ftdm_safe_free(err) \ - if (hash_locked) \ - ftdm_mutex_unlock(g_boost_modules_mutex); \ - if (lib) \ - ftdm_dso_destroy(&lib); \ - return retstatus; - - boost_sigmod_interface_t *sigmod_iface = NULL; - ftdm_sangoma_boost_data_t *sangoma_boost_data = NULL; - const char *local_ip = "127.0.0.65", *remote_ip = "127.0.0.66"; - const char *sigmod = NULL; - int local_port = 53000, remote_port = 53000; - const char *var = NULL, *val = NULL; - int hash_locked = 0; - ftdm_dso_lib_t lib = NULL; - char path[255] = ""; - char *err = NULL; - unsigned int j = 0; - unsigned paramindex = 0; - ftdm_status_t rc = FTDM_SUCCESS; - - for (; ftdm_parameters[paramindex].var; paramindex++) { - var = ftdm_parameters[paramindex].var; - val = ftdm_parameters[paramindex].val; - if (!strcasecmp(var, "sigmod")) { - sigmod = val; - } else if (!strcasecmp(var, "local_ip")) { - local_ip = val; - } else if (!strcasecmp(var, "remote_ip")) { - remote_ip = val; - } else if (!strcasecmp(var, "local_port")) { - local_port = atoi(val); - } else if (!strcasecmp(var, "remote_port")) { - remote_port = atoi(val); - } else if (!strcasecmp(var, "outbound-called-ton")) { - ftdm_set_ton(val, &span->default_caller_data.dnis.type); - } else if (!strcasecmp(var, "outbound-called-npi")) { - ftdm_set_npi(val, &span->default_caller_data.dnis.plan); - } else if (!strcasecmp(var, "outbound-calling-ton")) { - ftdm_set_ton(val, &span->default_caller_data.cid_num.type); - } else if (!strcasecmp(var, "outbound-calling-npi")) { - ftdm_set_npi(val, &span->default_caller_data.cid_num.plan); - } else if (!strcasecmp(var, "outbound-rdnis-ton")) { - ftdm_set_ton(val, &span->default_caller_data.rdnis.type); - } else if (!strcasecmp(var, "outbound-rdnis-npi")) { - ftdm_set_npi(val, &span->default_caller_data.rdnis.plan); - } else if (!sigmod) { - snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - } - - if (!sigmod) { -#ifndef HAVE_NETINET_SCTP_H - ftdm_log(FTDM_LOG_CRIT, "No sigmod attribute in span %s, you must either specify a sigmod or re-compile with SCTP available to use socket mode boost!\n", span->name); - ftdm_set_string(span->last_error, "No sigmod configuration was set and there is no SCTP available!"); - FAIL_CONFIG_RETURN(FTDM_FAIL); -#else - if (!local_ip && local_port && remote_ip && remote_port && sig_cb) { - ftdm_set_string(span->last_error, "missing Sangoma boost IP parameters"); - FAIL_CONFIG_RETURN(FTDM_FAIL); - } -#endif - } - - sangoma_boost_data = ftdm_calloc(1, sizeof(*sangoma_boost_data)); - if (!sangoma_boost_data) { - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - - /* WARNING: be sure to release this mutex on errors inside this if() */ - ftdm_mutex_lock(g_boost_modules_mutex); - hash_locked = 1; - if (sigmod && !(sigmod_iface = hashtable_search(g_boost_modules_hash, (void *)sigmod))) { - ftdm_build_dso_path(sigmod, path, sizeof(path)); - lib = ftdm_dso_open(path, &err); - if (!lib) { - ftdm_log(FTDM_LOG_ERROR, "Error loading Sangoma boost signaling module '%s': %s\n", path, err); - snprintf(span->last_error, sizeof(span->last_error), "Failed to load sangoma boost signaling module %s", path); - - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - if (!(sigmod_iface = (boost_sigmod_interface_t *)ftdm_dso_func_sym(lib, BOOST_INTERFACE_NAME_STR, &err))) { - ftdm_log(FTDM_LOG_ERROR, "Failed to read Sangoma boost signaling module interface '%s': %s\n", path, err); - snprintf(span->last_error, sizeof(span->last_error), "Failed to read Sangoma boost signaling module interface '%s': %s", path, err); - - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - rc = sigmod_iface->on_load(); - if (rc != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to load Sangoma boost signaling module interface '%s': on_load method failed (%d)\n", path, rc); - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - sigmod_iface->pvt = lib; - sigmod_iface->set_write_msg_cb(ftdm_boost_write_msg); - sigmod_iface->set_sig_status_cb(ftdm_boost_sig_status_change); - hashtable_insert(g_boost_modules_hash, (void *)sigmod_iface->name, sigmod_iface, HASHTABLE_FLAG_NONE); - lib = NULL; /* destroying the lib will be done when going down and NOT on FAIL_CONFIG_RETURN */ - } - ftdm_mutex_unlock(g_boost_modules_mutex); - hash_locked = 0; - - if (sigmod_iface) { - /* try to create the boost queue */ - if (ftdm_queue_create(&sangoma_boost_data->boost_queue, BOOST_QUEUE_SIZE) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost queue!\n", span->name); - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - ftdm_log(FTDM_LOG_NOTICE, "Span %s will use Sangoma Boost Signaling Module %s\n", span->name, sigmod_iface->name); - sangoma_boost_data->sigmod = sigmod_iface; - sigmod_iface->configure_span(span, ftdm_parameters); - } else { - ftdm_log(FTDM_LOG_NOTICE, "Span %s will use boost socket mode\n", span->name); - ftdm_set_string(sangoma_boost_data->mcon.cfg.local_ip, local_ip); - sangoma_boost_data->mcon.cfg.local_port = local_port; - ftdm_set_string(sangoma_boost_data->mcon.cfg.remote_ip, remote_ip); - sangoma_boost_data->mcon.cfg.remote_port = remote_port; - } - - for (j = 1; j <= span->chan_count; j++) { - span->channels[j]->call_data = ftdm_calloc(1, sizeof(sangoma_boost_call_t)); - if (!span->channels[j]->call_data) { - FAIL_CONFIG_RETURN(FTDM_FAIL); - } - } - - span->signal_cb = sig_cb; - span->start = ftdm_sangoma_boost_start; - span->stop = ftdm_sangoma_boost_stop; - span->signal_data = sangoma_boost_data; - span->signal_type = FTDM_SIGTYPE_SANGOMABOOST; - span->outgoing_call = sangoma_boost_outgoing_call; - span->channel_request = sangoma_boost_channel_request; - span->get_channel_sig_status = sangoma_boost_get_channel_sig_status; - span->set_channel_sig_status = sangoma_boost_set_channel_sig_status; - span->get_span_sig_status = sangoma_boost_get_span_sig_status; - span->set_span_sig_status = sangoma_boost_set_span_sig_status; - span->state_map = &boost_state_map; - span->state_processor = state_advance; - sangoma_boost_data->mcon.debuglevel = FTDM_LOG_LEVEL_DEBUG; - sangoma_boost_data->pcon.debuglevel = FTDM_LOG_LEVEL_DEBUG; - ftdm_clear_flag(span, FTDM_SPAN_SUGGEST_CHAN_ID); - ftdm_set_flag(span, FTDM_SPAN_USE_CHAN_QUEUE); - if (sigmod_iface) { - /* the core will do the hunting */ - span->channel_request = NULL; - } - ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); - return FTDM_SUCCESS; -} - -static ftdm_status_t ftdm_sangoma_boost_list_sigmods(ftdm_stream_handle_t *stream) -{ - ftdm_hash_iterator_t *i = NULL; - boost_sigmod_interface_t *sigmod_iface = NULL; - const void *key = NULL; - void *val = NULL; - - stream->write_function(stream, "List of loaded sigmod modules:\n"); - for (i = hashtable_first(g_boost_modules_hash); i; i = hashtable_next(i)) { - hashtable_this(i, &key, NULL, &val); - if (key && val) { - sigmod_iface = val; - stream->write_function(stream, " %s\n", sigmod_iface->name); - } - } - stream->write_function(stream, "\n"); - return FTDM_SUCCESS; -} - -/** - * \brief FreeTDM sangoma boost signaling module definition - */ -EX_DECLARE_DATA ftdm_module_t ftdm_module = { - /*.name =*/ "sangoma_boost", - /*.io_load =*/ ftdm_sangoma_boost_io_init, - /*.io_unload =*/ NULL, - /*.sig_load = */ ftdm_sangoma_boost_init, - /*.sig_configure =*/ NULL, - /*.sig_unload = */ftdm_sangoma_boost_destroy, - /*.configure_span_signaling = */ ftdm_sangoma_boost_configure_span -}; - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c deleted file mode 100644 index 6ddb74d253..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define _GNU_SOURCE - -#if HAVE_NETDB_H -#include -#endif - -#include "freetdm.h" -#include "sangoma_boost_client.h" - -#ifndef HAVE_GETHOSTBYNAME_R -extern int gethostbyname_r (const char *__name, - struct hostent *__result_buf, - char *__buf, size_t __buflen, - struct hostent **__result, - int *__h_errnop); -#endif - -struct sangomabc_map { - uint32_t event_id; - const char *name; -}; - -static struct sangomabc_map sangomabc_table[] = { - {SIGBOOST_EVENT_CALL_START, "CALL_START"}, - {SIGBOOST_EVENT_CALL_START_ACK, "CALL_START_ACK"}, - {SIGBOOST_EVENT_CALL_START_NACK, "CALL_START_NACK"}, - {SIGBOOST_EVENT_CALL_PROGRESS, "CALL PROGRESS"}, - {SIGBOOST_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, - {SIGBOOST_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, - {SIGBOOST_EVENT_CALL_STOPPED, "CALL_STOPPED"}, - {SIGBOOST_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, - {SIGBOOST_EVENT_CALL_RELEASED, "CALL_RELEASED"}, - {SIGBOOST_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, - {SIGBOOST_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, - {SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, - {SIGBOOST_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, - {SIGBOOST_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}, - {SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE, "AUTO_CALL_GAP_ABATE"}, - {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"} -}; - - - -static void sangomabc_print_event_call(sangomabc_connection_t *mcon, sangomabc_event_t *event, int priority, int dir, const char *file, const char *func, int line) -{ - if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) - return; - - ftdm_log(file, func, line, mcon->debuglevel, "%s EVENT (%s): %s:(%X) [w%dg%d] CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s] Rdnis=[%s]\n", - dir ? "TX":"RX", - priority ? "P":"N", - sangomabc_event_id_name(event->event_id), - event->event_id, - BOOST_EVENT_SPAN(mcon->sigmod, event), - BOOST_EVENT_CHAN(mcon->sigmod, event), - event->call_setup_id, - event->fseqno, - strlen(event->calling_name)?event->calling_name:"N/A", - (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), - (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A"), - event->isup_in_rdnis); - -} -static void sangomabc_print_event_short(sangomabc_connection_t *mcon, sangomabc_short_event_t *event, int priority, int dir, const char *file, const char *func, int line) -{ - if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) - return; - ftdm_log(file, func, line, mcon->debuglevel, "%s EVENT (%s): %s:(%X) [s%dc%d] Rc=%i CSid=%i Seq=%i \n", - dir ? "TX":"RX", - priority ? "P":"N", - sangomabc_event_id_name(event->event_id), - event->event_id, - BOOST_EVENT_SPAN(mcon->sigmod, event), - BOOST_EVENT_CHAN(mcon->sigmod, event), - event->release_cause, - event->call_setup_id, - event->fseqno); -} - - -static int create_conn_socket(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) -{ -#ifndef WIN32 - int rc; - struct hostent *result, *local_result; - char buf[512], local_buf[512]; - int err = 0, local_err = 0; - - if (mcon->sigmod) { - ftdm_log(FTDM_LOG_WARNING, "I should not be called on a sigmod-managed connection!\n"); - return 0; - } - - memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); - memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); -#ifdef HAVE_NETINET_SCTP_H - ftdm_log(FTDM_LOG_DEBUG, "Creating SCTP socket L=%s:%d R=%s:%d\n", - local_ip, local_port, ip, port); - mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); -#else - ftdm_log(FTDM_LOG_DEBUG, "Creating UDP socket L=%s:%d R=%s:%d\n", - local_ip, local_port, ip, port); - mcon->socket = socket(AF_INET, SOCK_DGRAM, 0); -#endif - - if (mcon->socket >= 0) { - int flag; - - flag = 1; -#ifdef HAVE_GETHOSTBYNAME_R_FIVE - gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &err); - gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_err); - if (!err && !local_err) { -#else - gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); - gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &local_err); - if (result && local_result) { -#endif - mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; - memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); - mcon->remote_addr.sin_port = htons(port); - - mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; - memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); - mcon->local_addr.sin_port = htons(local_port); - -#ifdef HAVE_NETINET_SCTP_H - setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, - (char *)&flag, sizeof(int)); -#endif - - if ((rc = bind(mcon->socket, - (struct sockaddr *) &mcon->local_addr, - sizeof(mcon->local_addr))) < 0) { - close(mcon->socket); - mcon->socket = -1; - } else { -#ifdef HAVE_NETINET_SCTP_H - rc=listen(mcon->socket, 100); - if (rc) { - close(mcon->socket); - mcon->socket = -1; - } -#endif - } - } - } - - return mcon->socket; -#else - return 0; -#endif // ifndef WIN32 -} - -int sangomabc_connection_close(sangomabc_connection_t *mcon) -{ -#ifndef WIN32 - if (mcon->sigmod) { - ftdm_log(FTDM_LOG_WARNING, "I should not be called on a sigmod-managed connection!\n"); - return 0; - } - if (mcon->socket > -1) { - close(mcon->socket); - } - - if (mcon->mutex) { - ftdm_mutex_lock(mcon->mutex); - ftdm_mutex_unlock(mcon->mutex); - ftdm_mutex_destroy(&mcon->mutex); - } - memset(mcon, 0, sizeof(*mcon)); - mcon->socket = -1; -#endif - return 0; -} - -int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) -{ - ftdm_mutex_create(&mcon->mutex); - if (mcon->sigmod) { - /*value of mcon->socket will be ignored in sigmod mode */ - return 0; - } -#ifndef WIN32 - create_conn_socket(mcon, local_ip, local_port, ip, port); - return mcon->socket; -#else - return 0; -#endif -} - - -int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags) -{ - sangomabc_event_t *oevent; - sangomabc_short_event_t sevent; - sangomabc_event_t fevent; - int retry = 5; - - if (boost_full_event(cmd)) { - sangomabc_event_init((void *)&fevent, cmd, chan, span); - oevent = &fevent; - } else { - sangomabc_event_init(&sevent, cmd, chan, span); - sevent.release_cause = (uint8_t)cause; - oevent = (sangomabc_event_t *)&sevent; - } - oevent->flags = flags; - - if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART || cmd == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { - mcon->rxseq_reset = 1; - mcon->txseq = 0; - mcon->rxseq = 0; - mcon->txwindow = 0; - } - - if (id >= 0) { - oevent->call_setup_id = (uint16_t)id; - } - - while (sangomabc_connection_write(mcon, (sangomabc_event_t*)oevent) <= 0) { - if (--retry <= 0) { - ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost socket: %s\n", strerror(errno)); - return -1; - } else { - ftdm_log(FTDM_LOG_WARNING, "Failed to tx on boost socket: %s :retry %i\n", strerror(errno), retry); - ftdm_sleep(1); - } - } - - return 0; -} - - -int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause) -{ - sangomabc_short_event_t oevent; - int retry = 5; - - sangomabc_event_init(&oevent, cmd, chan, span); - oevent.release_cause = (uint8_t)cause; - - if (id >= 0) { - oevent.call_setup_id = (uint16_t)id; - } - - while (sangomabc_connection_writep(pcon, (sangomabc_event_t*)&oevent) <= 0) { - if (--retry <= 0) { - ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost socket: %s\n", strerror(errno)); - return -1; - } else { - ftdm_log(FTDM_LOG_WARNING, "Failed to tx on boost socket: %s :retry %i\n", strerror(errno), retry); - ftdm_sleep(1); - } - } - - return 0; -} - -sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) -{ -#ifndef WIN32 - unsigned int fromlen = sizeof(struct sockaddr_in); -#endif - int bytes = 0; - int msg_ok = 0; - sangomabc_queue_element_t *e = NULL; - - if (mcon->sigmod) { - e = ftdm_queue_dequeue(mcon->boost_queue); - if (e) { - bytes = (int)e->size; - memcpy(&mcon->event, e->boostmsg, bytes); - ftdm_safe_free(e); - } - } -#ifndef WIN32 - else { - bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, - (struct sockaddr *) &mcon->local_addr, &fromlen); - } -#endif - if (bytes <= 0) { - return NULL; - } - - if (mcon->event.version != SIGBOOST_VERSION) { - ftdm_log(FTDM_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); - } - - if ((bytes >= MIN_SIZE_CALLSTART_MSG) && boost_full_event(mcon->event.event_id)) { - msg_ok=1; - - } else if (bytes == sizeof(sangomabc_short_event_t)) { - msg_ok=1; - - } else { - msg_ok=0; - } - - if (msg_ok) { - if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { - if (mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART && - mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && - mcon->event.event_id != SIGBOOST_EVENT_HEARTBEAT) { - ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Not reading packets when connection is down. [%s]\n", - sangomabc_event_id_name(mcon->event.event_id)); - return NULL; - } - } - - if (boost_full_event(mcon->event.event_id)) { - sangomabc_print_event_call(mcon, &mcon->event, 0, 0, file, func, line); - } else { - sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 0, 0, file, func, line); - } - -#if 0 -/* NC: NOT USED ANY MORE */ - if (mcon->rxseq_reset) { - //if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { - ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n"); - mcon->rxseq = mcon->event.fseqno; - return &mcon->event; - //} - errno=EAGAIN; - ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n"); - return NULL; - } -#endif - - mcon->txwindow = mcon->txseq - mcon->event.bseqno; - mcon->rxseq++; - -#if 0 - if (mcon->rxseq != mcon->event.fseqno) { - ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); - return NULL; - } -#endif - - return &mcon->event; - } else { - if (iteration == 0) { - ftdm_log(FTDM_LOG_CRIT, "NC - Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); - return NULL; - } - } - - return NULL; -} - -sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) -{ -#ifndef WIN32 - unsigned int fromlen = sizeof(struct sockaddr_in); -#endif - int bytes = 0; - - if (mcon->sigmod) { - /* priority stuff is handled just the same when there is a sigmod */ - return sangomabc_connection_read(mcon, iteration); - } -#ifndef WIN32 - else { - bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); - } -#endif - if (bytes <= 0) { - return NULL; - } - - if (mcon->event.version != SIGBOOST_VERSION) { - ftdm_log(FTDM_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); - } - - if (bytes == sizeof(sangomabc_short_event_t)) { - - if (boost_full_event(mcon->event.event_id)) { - sangomabc_print_event_call(mcon, &mcon->event, 1, 0, file, func, line); - } else { - sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 1, 0, file, func, line); - } - - return &mcon->event; - } else { - if (iteration == 0) { - ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); - return NULL; - } - } - - return NULL; -} - - -int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) -{ - int err = 0; - int event_size=MIN_SIZE_CALLSTART_MSG+event->isup_in_rdnis_size; - - ftdm_assert_return(event != NULL, -1, "No event!"); - ftdm_assert_return(mcon->socket >= 0, -1, "No mcon->socket!"); - ftdm_assert_return(mcon->mutex != NULL, -1, "No mcon->mutex!"); - - ftdm_assert_return(event->span <= FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN, -1, "Invalid span when writing boost event\n"); - ftdm_assert_return(event->chan <= FTDM_MAX_CHANNELS_PHYSICAL_SPAN, -1, "Invalid chan when writing boost event\n"); - - if (!boost_full_event(event->event_id)) { - event_size=sizeof(sangomabc_short_event_t); - } - - if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { - if (event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART && - event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && - event->event_id != SIGBOOST_EVENT_HEARTBEAT) { - ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Not writing packets when connection is down. [%s]\n", - sangomabc_event_id_name(event->event_id)); - return 0; - } - } - - ftdm_mutex_lock(mcon->mutex); - if (event->event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { - mcon->txseq=0; - mcon->rxseq=0; - event->fseqno=0; - } else { - event->fseqno = mcon->txseq++; - } - event->bseqno = mcon->rxseq; - event->version = SIGBOOST_VERSION; - - if (boost_full_event(event->event_id)) { - sangomabc_print_event_call(mcon, event, 0, 1, file, func, line); - } else { - sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 0, 1, file, func, line); - } - - if (mcon->sigmod) { - mcon->sigmod->write_msg(mcon->span, event, event_size); - err = event_size; - } -#ifndef WIN32 - else { - err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); - } -#endif - - ftdm_mutex_unlock(mcon->mutex); - - ftdm_assert_return(err == event_size, -1, "Failed to send the boost message completely!"); - - return err; -} - - -int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) -{ - int err = 0; - int event_size=sizeof(sangomabc_event_t); - - if (!mcon->sigmod) { - ftdm_assert_return(event != NULL, -1, "No event!"); - ftdm_assert_return(mcon->socket >= 0, -1, "No mcon->socket!"); - ftdm_assert_return(mcon->mutex != NULL, -1, "No mcon->mutex!"); - } - - if (!boost_full_event(event->event_id)) { - event_size=sizeof(sangomabc_short_event_t); - } - - ftdm_mutex_lock(mcon->mutex); - event->version = SIGBOOST_VERSION; - if (mcon->sigmod) { - mcon->sigmod->write_msg(mcon->span, event, event_size); - err = event_size; - - } -#ifndef WIN32 - else { - err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); - } -#endif - ftdm_mutex_unlock(mcon->mutex); - - ftdm_assert_return(err == event_size, -1, "Failed to send boost message completely!"); - - if (boost_full_event(event->event_id)) { - sangomabc_print_event_call(mcon, event, 1, 1, file, func, line); - } else { - sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 1, 1, file, func, line); - } - - return err; -} - - -void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id) -{ - memset(event, 0, sizeof(sangomabc_event_t)); - event->event_id = SIGBOOST_EVENT_CALL_START; - - if (calling) { - strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); - event->calling_number_digits_count = (uint8_t)strlen(calling); - } - - if (called) { - strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); - event->called_number_digits_count = (uint8_t)strlen(called); - } - - event->call_setup_id = (uint16_t)setup_id; - -} - -void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span) -{ - if (boost_full_event(event_id)) { - memset(event, 0, sizeof(sangomabc_event_t)); - } else { - memset(event, 0, sizeof(sangomabc_short_event_t)); - } - event->event_id = event_id; - event->chan = (uint8_t)chan; - event->span = (uint8_t)span; -} - -const char *sangomabc_event_id_name(uint32_t event_id) -{ - unsigned int x; - const char *ret = NULL; - - for (x = 0 ; x < sizeof(sangomabc_table)/sizeof(struct sangomabc_map); x++) { - if (sangomabc_table[x].event_id == event_id) { - ret = sangomabc_table[x].name; - break; - } - } - - return ret; -} - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h deleted file mode 100644 index e333c4aba3..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _SANGOMABC_H -#define _SANGOMABC_H - -#include "sangoma_boost_interface.h" - -#include -#include -#ifndef WIN32 -#include -#include -#include -#ifdef HAVE_NETINET_SCTP_H -#include -#endif -#include -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include "sigboost.h" - -#define sangomabc_test_flag(p,flag) ((p)->flags & (flag)) - -#define sangomabc_set_flag(p,flag) do { \ - ((p)->flags |= (flag)); \ - } while (0) - -#define sangomabc_clear_flag(p,flag) do { \ - ((p)->flags &= ~(flag)); \ - } while (0) - -#define sangomabc_copy_flags(dest,src,flagz) do { \ - (dest)->flags &= ~(flagz); \ - (dest)->flags |= ((src)->flags & (flagz)); \ - } while (0) - -typedef t_sigboost_callstart sangomabc_event_t; -typedef t_sigboost_short sangomabc_short_event_t; -typedef uint32_t sangomabc_event_id_t; - -typedef struct sangomabc_ip_cfg -{ - char local_ip[25]; - int local_port; - char remote_ip[25]; - int remote_port; -}sangomabc_ip_cfg_t; - -typedef enum { - MSU_FLAG_EVENT = (1 << 0), - MSU_FLAG_DOWN = (1 << 1) -} sangomabc_flag_t; - - -struct sangomabc_connection { - ftdm_socket_t socket; - struct sockaddr_in local_addr; - struct sockaddr_in remote_addr; - sangomabc_event_t event; - struct hostent remote_hp; - struct hostent local_hp; - unsigned int flags; - ftdm_mutex_t *mutex; - FILE *log; - unsigned int txseq; - unsigned int rxseq; - unsigned int txwindow; - unsigned int rxseq_reset; - sangomabc_ip_cfg_t cfg; - /* boost signaling mod interface pointer (if not working in TCP mode) */ - boost_sigmod_interface_t *sigmod; - ftdm_queue_t *boost_queue; - ftdm_interrupt_t *sock_interrupt; - ftdm_span_t *span; - int debuglevel; -}; - -typedef struct sangomabc_connection sangomabc_connection_t; - -typedef struct sangomabc_queue_element { - unsigned char boostmsg[sizeof(sangomabc_event_t)]; - ftdm_size_t size; -} sangomabc_queue_element_t; - -/* disable nagle's algorythm */ -static __inline__ void sctp_no_nagle(int socket) -{ -#ifdef HAVE_NETINET_SCTP_H - int flag = 1; - setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); -#endif -} - -int sangomabc_connection_close(sangomabc_connection_t *mcon); -int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); -sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); -sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); -int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); -int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); -#define sangomabc_connection_write(_m,_e) __sangomabc_connection_write(_m, _e, __FILE__, __FUNCTION__, __LINE__) -#define sangomabc_connection_writep(_m,_e) __sangomabc_connection_writep(_m, _e, __FILE__, __FUNCTION__, __LINE__) -#define sangomabc_connection_read(_m,_e) __sangomabc_connection_read(_m, _e, __FILE__, __FUNCTION__, __LINE__) -#define sangomabc_connection_readp(_m,_e) __sangomabc_connection_readp(_m, _e, __FILE__, __FUNCTION__, __LINE__) -void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span); -void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id); -const char *sangomabc_event_id_name(uint32_t event_id); -int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags); -int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause); - -#define BOOST_EVENT_SPAN(sigmod, event) ((sigmod) ? event->span : event->span + 1) -#define BOOST_EVENT_CHAN(sigmod, event) ((sigmod) ? event->chan : event->chan + 1) - - -#endif - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h deleted file mode 100644 index f6130db444..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2009, Sangoma Technologies - * Moises Silva - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SANGOMA_BOOST_INTERFACE_H -#define SANGOMA_BOOST_INTERFACE_H - -#include "private/ftdm_core.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*! - \brief Callback used to notify signaling status changes on a channel - \param ftdmchan The freetdm channel where the signaling status just changed - \param status The new signaling status - */ -#define BOOST_SIG_STATUS_CB_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) -typedef void (*boost_sig_status_cb_func_t) BOOST_SIG_STATUS_CB_ARGS; -#define BOOST_SIG_STATUS_CB_FUNCTION(name) void name BOOST_SIG_STATUS_CB_ARGS - -/*! - \brief Write a boost msg to a boost endpoint - \param span The freetdm span where this msg was generated - \param msg The generic message pointer, owned by the caller - \param msglen The length of the provided structure pointed by msg - \return FTDM_SUCCESS or FTDM_FAIL - - The msg buffer is owned by the caller and it should - be either t_sigboost_callstart or t_sigboost_short - the endpoint receiving the msg will first cast to - t_sigboost_short, check the event type, and if needed. - */ -#define BOOST_WRITE_MSG_ARGS (ftdm_span_t *span, void *msg, ftdm_size_t msglen) -typedef ftdm_status_t (*boost_write_msg_func_t) BOOST_WRITE_MSG_ARGS; -#define BOOST_WRITE_MSG_FUNCTION(name) ftdm_status_t name BOOST_WRITE_MSG_ARGS - -/*! - \brief Set the callback to be used by a signaling module to write boost messages - \param callback The callback to be used by the signaling module - - The provided callback will be used for the signaling boost module to notify the - user with boost messages. - */ -#define BOOST_SET_WRITE_MSG_CB_ARGS (boost_write_msg_func_t callback) -typedef void (*boost_set_write_msg_cb_func_t) BOOST_SET_WRITE_MSG_CB_ARGS; -#define BOOST_SET_WRITE_MSG_CB_FUNCTION(name) void name BOOST_SET_WRITE_MSG_CB_ARGS - -/*! - \brief Notify hardware status change - \param ftdmchan The freetdm channel - \param status The hw status - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS (ftdm_channel_t *ftdmchan, ftdm_channel_hw_link_status_t status) -typedef void (*boost_on_hw_link_status_change_func_t) BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS; -#define BOOST_ON_HW_LINK_STATUS_CHANGE_FUNCTION(name) void name BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS - -/*! - \brief Set signaling status callback used by the signaling module to report signaling status changes - \param callback The callback to be used by the signaling module - - The provided callback will be used for the signaling boost module to notify the - user with signaling link status changes. - */ -#define BOOST_SET_SIG_STATUS_CB_ARGS (boost_sig_status_cb_func_t callback) -typedef void (*boost_set_sig_status_cb_func_t) BOOST_SET_SIG_STATUS_CB_ARGS; -#define BOOST_SET_SIG_STATUS_CB_FUNCTION(name) void name BOOST_SET_SIG_STATUS_CB_ARGS - -/*! - \brief Get the signaling status on the given channel. - \param ftdmchan The freetdm channel - \param status The status pointer where the current signaling status will be set - */ -#define BOOST_GET_CHANNEL_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *status) -typedef ftdm_status_t (*boost_get_channel_sig_status_func_t) BOOST_GET_CHANNEL_SIG_STATUS_ARGS; -#define BOOST_GET_CHANNEL_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_GET_CHANNEL_SIG_STATUS_ARGS - -/*! - \brief Set the signaling status on the given channel. - \param ftdmchan The freetdm channel - \param status The new status for the channel - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_SET_CHANNEL_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) -typedef ftdm_status_t (*boost_set_channel_sig_status_func_t) BOOST_SET_CHANNEL_SIG_STATUS_ARGS; -#define BOOST_SET_CHANNEL_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_SET_CHANNEL_SIG_STATUS_ARGS - -/*! - \brief Get the signaling status on the given span. - \param span The freetdm span - \param status The status pointer where the current signaling status will be set - */ -#define BOOST_GET_SPAN_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t *status) -typedef ftdm_status_t (*boost_get_span_sig_status_func_t) BOOST_GET_SPAN_SIG_STATUS_ARGS; -#define BOOST_GET_SPAN_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_GET_SPAN_SIG_STATUS_ARGS - -/*! - \brief Set the signaling status on the given span. - \param ftdmchan The freetdm span - \param status The new status for the span - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_SET_SPAN_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t status) -typedef ftdm_status_t (*boost_set_span_sig_status_func_t) BOOST_SET_SPAN_SIG_STATUS_ARGS; -#define BOOST_SET_SPAN_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_SET_SPAN_SIG_STATUS_ARGS - -/*! - \brief Configure the given span signaling - \param span The freetdm span - \param parameters The array of configuration key,value pairs (must be null terminated) - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_CONFIGURE_SPAN_ARGS (ftdm_span_t *span, ftdm_conf_parameter_t *parameters) -typedef ftdm_status_t (*boost_configure_span_func_t) BOOST_CONFIGURE_SPAN_ARGS; -#define BOOST_CONFIGURE_SPAN_FUNCTION(name) ftdm_status_t name BOOST_CONFIGURE_SPAN_ARGS - -/*! - \brief Start the given span - \param span The freetdm span - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_START_SPAN_ARGS (ftdm_span_t *span) -typedef ftdm_status_t (*boost_start_span_func_t) BOOST_START_SPAN_ARGS; -#define BOOST_START_SPAN_FUNCTION(name) ftdm_status_t name BOOST_START_SPAN_ARGS - -/*! - \brief Stop the given span - \param span The freetdm span - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_STOP_SPAN_ARGS (ftdm_span_t *span) -typedef ftdm_status_t (*boost_stop_span_func_t) BOOST_START_SPAN_ARGS; -#define BOOST_STOP_SPAN_FUNCTION(name) ftdm_status_t name BOOST_STOP_SPAN_ARGS - -/*! - \brief Called when the module is being loaded BEFORE calling anything else - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_ON_LOAD_ARGS (void) -typedef ftdm_status_t (*boost_on_load_func_t) BOOST_ON_LOAD_ARGS; -#define BOOST_ON_LOAD_FUNCTION(name) ftdm_status_t name BOOST_ON_LOAD_ARGS - -/*! - \brief Called when the module is being unloaded, last chance to stop everything! - */ -#define BOOST_ON_UNLOAD_ARGS (void) -typedef ftdm_status_t (*boost_on_unload_func_t) BOOST_ON_UNLOAD_ARGS; -#define BOOST_ON_UNLOAD_FUNCTION(name) ftdm_status_t name BOOST_ON_UNLOAD_ARGS - -/*! - \brief Called when user wants to execute sigmod api function - \return FTDM_SUCCESS or FTDM_FAIL - */ -#define BOOST_API_ARGS (ftdm_stream_handle_t *stream, char *cmd) -typedef ftdm_status_t (*boost_api_func_t) BOOST_API_ARGS; -#define BOOST_API_FUNCTION(name) ftdm_status_t name BOOST_API_ARGS - - -/*! - \brief The boost signaling module interface - */ -typedef struct boost_sigmod_interface_s { - /*! \brief Module name */ - const char *name; - /*! \brief write boost message function */ - boost_write_msg_func_t write_msg; - /*! \brief set the user write boost message function */ - boost_set_write_msg_cb_func_t set_write_msg_cb; - /*! \brief set the user signaling status function */ - boost_set_sig_status_cb_func_t set_sig_status_cb; - /*! \brief get channel signaling status */ - boost_get_channel_sig_status_func_t get_channel_sig_status; - /*! \brief set channel signaling status */ - boost_set_channel_sig_status_func_t set_channel_sig_status; - /*! \brief get span signaling status */ - boost_get_span_sig_status_func_t get_span_sig_status; - /*! \brief set span signaling status */ - boost_set_span_sig_status_func_t set_span_sig_status; - /*! \brief set notify hardware link status change */ - boost_on_hw_link_status_change_func_t on_hw_link_status_change; - /*! \brief configure span signaling */ - boost_configure_span_func_t configure_span; - /*! \brief start freetdm span */ - boost_start_span_func_t start_span; - /*! \brief stop freetdm span */ - boost_stop_span_func_t stop_span; - /*! \brief the module was just loaded */ - boost_on_load_func_t on_load; - /*! \brief the module is about to be unloaded */ - boost_on_unload_func_t on_unload; - /*! \brief module api function */ - boost_api_func_t exec_api; - /*! \brief private pointer for the interface user */ - void *pvt; -} boost_sigmod_interface_t; - -#ifdef __cplusplus -} // extern C -#endif - -#define BOOST_INTERFACE_NAME boost_sigmod_interface -#define BOOST_INTERFACE_NAME_STR "boost_sigmod_interface" -/* use this in your sig boost module to declare your interface */ -#ifndef WIN32 -#define BOOST_INTERFACE boost_sigmod_interface_t BOOST_INTERFACE_NAME -#else -#define BOOST_INTERFACE __declspec(dllexport) boost_sigmod_interface_t BOOST_INTERFACE_NAME -#endif -#endif - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ - diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h deleted file mode 100644 index 7975dc5b93..0000000000 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h +++ /dev/null @@ -1,221 +0,0 @@ -/**************************************************************************** - * sigboost.h $Revision: 1.13 $ - * - * Definitions for the sigboost interface. - * - * WARNING WARNING WARNING - * - * This file is used by sangoma_mgd and perhaps other programs. Any changes - * to this file must be coordinated with other user programs, - * - * Copyright (C) 2005 Xygnada Technology, Inc. - * -****************************************************************************/ -#ifndef _SIGBOOST_H_ -#define _SIGBOOST_H_ - -#define SIGBOOST_VERSION 103 - -// handy to define integer types that actually work on both Lin and Win -#include - -enum e_sigboost_event_id_values -{ - SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ - SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ - SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ - SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ - SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ - SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ - SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ - SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ - SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ - /* CALL_RELEASED is aimed to fix a race condition that became obvious - * when the boost socket was replaced by direct function calls - * and the channel hunting was moved to freetdm, the problem is - * we can get CALL_STOPPED msg and reply with CALL_STOPPED_ACK - * but the signaling module will still (in PRI) send RELEASE and - * wait for RELEASE_COMPLETE from the isdn network before - * marking the channel as available, therefore freetdm should - * also not mark the channel as available until CALL_RELEASED - * is received, for socket mode we can continue working as usual - * with CALL_STOPPED being the last step because the hunting is - * done in the signaling module. - * */ - SIGBOOST_EVENT_CALL_RELEASED = 0x51, /* 81 */ - SIGBOOST_EVENT_CALL_PROGRESS = 0x50, /*decimal 80*/ - /* Following IDs are ss7boost to sangoma_mgd only. */ - SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ - SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ - SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ - SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ - SIGBOOST_EVENT_DIGIT_IN = 0x8d, /*141*/ -}; - -#define BOOST_DECODE_EVENT_ID(id) \ - (id==SIGBOOST_EVENT_CALL_START)?"SIGBOOST_EVENT_CALL_START": \ - (id==SIGBOOST_EVENT_CALL_START_ACK)?"SIGBOOST_EVENT_CALL_START_ACK": \ - (id==SIGBOOST_EVENT_CALL_START_NACK)?"SIGBOOST_EVENT_CALL_START_NACK": \ - (id==SIGBOOST_EVENT_CALL_ANSWERED)?"SIGBOOST_EVENT_CALL_ANSWERED": \ - (id==SIGBOOST_EVENT_CALL_STOPPED)?"SIGBOOST_EVENT_CALL_STOPPED": \ - (id==SIGBOOST_EVENT_CALL_STOPPED_ACK)?"SIGBOOST_EVENT_CALL_STOPPED_ACK": \ - (id==SIGBOOST_EVENT_SYSTEM_RESTART)?"SIGBOOST_EVENT_SYSTEM_RESTART": \ - (id==SIGBOOST_EVENT_SYSTEM_RESTART_ACK)?"SIGBOOST_EVENT_SYSTEM_RESTART_ACK": \ - (id==SIGBOOST_EVENT_CALL_RELEASED)?"SIGBOOST_EVENT_CALL_RELEASED": \ - (id==SIGBOOST_EVENT_CALL_PROGRESS)?"SIGBOOST_EVENT_CALL_PROGRESS": \ - (id==SIGBOOST_EVENT_HEARTBEAT)?"SIGBOOST_EVENT_HEARTBEAT": \ - (id==SIGBOOST_EVENT_INSERT_CHECK_LOOP)?"SIGBOOST_EVENT_INSERT_CHECK_LOOP": \ - (id==SIGBOOST_EVENT_REMOVE_CHECK_LOOP)?"SIGBOOST_EVENT_REMOVE_CHECK_LOOP": \ - (id==SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE)?"SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE": \ - (id==SIGBOOST_EVENT_DIGIT_IN)?"SIGBOOST_EVENT_DIGIT_IN": "Unknown" - -enum e_sigboost_release_cause_values -{ - SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, - SIGBOOST_RELEASE_CAUSE_NORMAL = 16, - /* probable elimination */ - //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ - //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ - //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ - //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ -}; - -enum e_sigboost_call_setup_ack_nack_cause_values -{ - //SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 34, /* Q.850 value - don't use */ - SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* non Q.850 value indicates local all ckt busy - causing sangoma_mgd to perform automatic call - gapping*/ - SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 17, /* Q.850 value */ - SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, /* Q.850 value */ - SIGBOOST_CALL_SETUP_CSUPID_DBL_USE = 200, /* unused Q.850 value */ -}; - - -enum e_sigboost_huntgroup_values -{ - SIGBOOST_HUNTGRP_SEQ_ASC = 0x00, /* sequential with lowest available first */ - SIGBOOST_HUNTGRP_SEQ_DESC = 0x01, /* sequential with highest available first */ - SIGBOOST_HUNTGRP_RR_ASC = 0x02, /* round-robin with lowest available first */ - SIGBOOST_HUNTGRP_RR_DESC = 0x03, /* round-robin with highest available first */ -}; - -enum e_sigboost_event_info_par_values -{ - SIGBOOST_EVI_SPARE = 0x00, - SIGBOOST_EVI_ALERTING = 0x01, - SIGBOOST_EVI_PROGRESS = 0x02, -}; - -enum e_sigboost_progress_flags -{ - SIGBOOST_PROGRESS_RING = (1 << 0), - SIGBOOST_PROGRESS_MEDIA = (1 << 1) -}; - -#define MAX_DIALED_DIGITS 31 - -/* Next two defines are used to create the range of values for call_setup_id - * in the t_sigboost structure. - * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ -#define CORE_MAX_SPANS 200 -#define CORE_MAX_CHAN_PER_SPAN 32 -#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN -/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ - -/* Should only be used by server */ -#define MAX_CALL_SETUP_ID 0xFFFF - -#define SIZE_CUSTOM 900 -#define SIZE_RDNIS SIZE_CUSTOM - - -#pragma pack(1) - -typedef struct -{ - uint8_t capability; - uint8_t uil1p; -} t_sigboost_bearer; - -typedef struct -{ - uint8_t digits_count; - char digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ - uint8_t npi; - uint8_t ton; - uint8_t screening_ind; - uint8_t presentation_ind; -}t_sigboost_digits; - -typedef struct -{ - uint16_t version; - uint32_t event_id; - /* delete sequence numbers - SCTP does not need them */ - uint32_t fseqno; - uint32_t bseqno; - uint16_t call_setup_id; - uint32_t trunk_group; - uint8_t span; - uint8_t chan; - uint32_t flags; - /* struct timeval tv; */ - t_sigboost_digits called; - t_sigboost_digits calling; - t_sigboost_digits rdnis; - /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ - char calling_name[MAX_DIALED_DIGITS + 1]; - t_sigboost_bearer bearer; - uint8_t hunt_group; - uint16_t custom_data_size; - char custom_data[SIZE_CUSTOM]; /* it's a null terminated string */ - -} t_sigboost_callstart; - -#define called_number_digits_count called.digits_count -#define called_number_digits called.digits -#define calling_number_digits_count calling.digits_count -#define calling_number_digits calling.digits -#define calling_number_screening_ind calling.screening_ind -#define calling_number_presentation calling.presentation_ind - -#define isup_in_rdnis_size custom_data_size -#define isup_in_rdnis custom_data - - -#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_CUSTOM - -typedef struct -{ - uint16_t version; - uint32_t event_id; - /* delete sequence numbers - SCTP does not need them */ - uint32_t fseqno; - uint32_t bseqno; - uint16_t call_setup_id; - uint32_t trunk_group; - uint8_t span; - uint8_t chan; - uint32_t flags; - /* struct timeval tv; */ - uint8_t release_cause; -} t_sigboost_short; -#pragma pack() - - -static __inline__ int boost_full_event(int event_id) -{ - switch (event_id) { - case SIGBOOST_EVENT_CALL_START: - case SIGBOOST_EVENT_DIGIT_IN: - case SIGBOOST_EVENT_CALL_PROGRESS: - return 1; - default: - break; - } - - return 0; -} - -#endif From c166f5f02cac2dcb98f908e3e5d31b7b8b84c389 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 17 Feb 2011 09:17:41 -0500 Subject: [PATCH 003/154] freetdm: increase size of static buffer for DSO path --- libs/freetdm/src/ftdm_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 01125e2f61..92559a43a5 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -4979,7 +4979,7 @@ FT_DECLARE(int) ftdm_load_module(const char *name) { ftdm_dso_lib_t lib; int count = 0, x = 0; - char path[128] = ""; + char path[512] = ""; char *err; ftdm_module_t *mod; From cabd05d116b8e71fbc5163365f55e7153fcd28fa Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 18 Feb 2011 13:01:57 -0500 Subject: [PATCH 004/154] chlog: freetdm: channel/call variables moved to sigmsg --- libs/freetdm/Makefile.am | 1 + libs/freetdm/docs/variables.txt | 138 +++++++++ libs/freetdm/mod_freetdm/mod_freetdm.c | 35 +-- libs/freetdm/msvc/freetdm.2008.vcproj | 4 + libs/freetdm/msvc/freetdm.2010.vcxproj | 3 +- .../freetdm/msvc/freetdm.2010.vcxproj.filters | 5 +- libs/freetdm/src/ftdm_io.c | 285 +++++------------- libs/freetdm/src/ftdm_state.c | 18 +- .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.c | 80 ++--- .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.h | 15 + .../ftmod_sangoma_isdn_stack_out.c | 16 +- .../ftmod_sangoma_isdn_support.c | 141 ++++++--- libs/freetdm/src/include/freetdm.h | 103 +++---- libs/freetdm/src/include/private/ftdm_core.h | 18 +- 14 files changed, 480 insertions(+), 382 deletions(-) create mode 100644 libs/freetdm/docs/variables.txt diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index a26035b2ef..782eaf680e 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -76,6 +76,7 @@ libfreetdm_la_SOURCES = \ $(SRC)/ftdm_queue.c \ $(SRC)/ftdm_sched.c \ $(SRC)/ftdm_call_utils.c \ + $(SRC)/ftdm_variables.c \ $(SRC)/ftdm_config.c \ $(SRC)/ftdm_callerid.c \ $(SRC)/fsk.c \ diff --git a/libs/freetdm/docs/variables.txt b/libs/freetdm/docs/variables.txt new file mode 100644 index 0000000000..f667b48e53 --- /dev/null +++ b/libs/freetdm/docs/variables.txt @@ -0,0 +1,138 @@ +1. User application sending variables or raw buffer to FreeTDM +============================================================== + +The User can attach a ftdm_sigmsg_t to ftdm_caller_data_t before sending an event to freetdm. + +example #1 - Adding a variable: +------------------------------- + +When using ftmod_sangoma_isdn, user want to specify progress indicator inside PROCEED message. + + + ftdm_caller_data_t *caller_data; + ftdm_sigmsg_t sigmsg; + + /* Attach variable to sigmsg */ + ftdm_event_add_var(&sigmsg, "isdn.prog_ind.descr", "inband-info-available"); + + + /* Attach sigmsg to channel's caller_data */ + caller_data = ftdm_channel_get_caller_data(ftdmchan); + caller_data->sigmsg = &sigmsg; + + /* Request FreeTDM to send a PROCEED msg */ + ftdm_channel_call_indicate(ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED); + +Note: +1.When ftdm_channel_call_indicate returns, caller_data->sigmsg will be NULL. + + + +example #2a - Setting raw data without autofree feature: +-------------------------------------------------------- + +When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, inside a FACILITY message. + + ftdm_caller_data_t *caller_data; + ftdm_sigmsg_t sigmsg; + uint8_t my_facility_ie [200]; + unsigned my_facility_ie_len = 0; + + /* Fill my_facility_ie with custom data here */ + my_facility_ie[my_facility_ie_len++] = 0x1C; /* Q.931 Facility IE ID */ + my_facility_ie[my_facility_ie_len++] = 0x03; /* Length of facility IE */ + my_facility_ie[my_facility_ie_len++] = 0x01; + my_facility_ie[my_facility_ie_len++] = 0x02; + my_facility_ie[my_facility_ie_len++] = 0x03; + + + sigmsg.raw.data = my_facility_ie; + sigmsg.raw.len = my_facility_ie_len; + sigmsg.raw.autofree = 0; + + /* Attach sigmsg to channel's caller_data */ + caller_data = ftdm_channel_get_caller_data(ftdmchan); + caller_data->sigmsg = &sigmsg; + + sigmsg.event_id = FTDM_SIGEVENT_FACILITY; + + ftdm_channel_call_send_msg(ftdmchan, sigmsg); + + + +example #2b - Setting raw data with autofree feature: +----------------------------------------------------- + + ftdm_caller_data_t *caller_data; + ftdm_sigmsg_t sigmsg; + uint8_t *my_facility_ie = ftdm_calloc(1, 200); + unsigned my_facility_ie_len = 0; + + /* Fill my_facility_ie with custom data here */ + my_facility_ie[my_facility_ie_len++] = 0x1C; /* Q.931 Facility IE ID */ + my_facility_ie[my_facility_ie_len++] = 0x03; /* Length of facility IE */ + my_facility_ie[my_facility_ie_len++] = 0x01; + my_facility_ie[my_facility_ie_len++] = 0x02; + my_facility_ie[my_facility_ie_len++] = 0x03; + + + sigmsg.raw.data = my_facility_ie; + sigmsg.raw.len = my_facility_ie_len; + sigmsg.raw.autofree = 1; + + /* Attach sigmsg to channel's caller_data */ + caller_data = ftdm_channel_get_caller_data(ftdmchan); + caller_data->sigmsg = &sigmsg; + + sigmsg.event_id = FTDM_SIGEVENT_FACILITY; + + ftdm_channel_call_send_msg(ftdmchan, sigmsg); + + /* FreeTDM will automatically free my_facility_ie */ + + +2. User application receiving variables and raw buffer from FreeTDM +================================================================== + +example #1 - print all variables received from FreeTDM +------------------------------------------------------ + + /* Inside event call-back function */ + ftdm_iterator_t *iter = NULL; + ftdm_iterator_t *curr = NULL; + const char *var_name = NULL; + const char *var_value = NULL; + + ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(sigmsg->channel); + + /* Read all variables attached to this event */ + iter = ftdm_event_get_var_iterator(sigmsg, iter); + for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { + ftdm_get_current_var(curr, &var_name, &var_value); + fprintf("Call Variable: %s=%s\n", var_name, var_value); + } + ftdm_iterator_free(iter); + + +example #2 - accessing a specific variable +------------------------------------------ + + /* Inside event call-back function */ + char *string = NULL; + string = ftdm_event_get_var(sigmsg, "isdn.prog_ind.descr"); + if (string && *string) { + fprintf("Progress indicator:%s\n", string); + } + + +example #3 - accessing raw data +------------------------------- + + /* Inside event call-back function */ + ftdm_size_t len; + uint8_t *mydata; + if (ftdm_event_get_raw_data(ftdmchan->sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + /* raw data is available, do something with mydata here */ + } + + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index ef945cf629..4a995bc8de 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -874,7 +874,7 @@ static switch_status_t channel_receive_message_b(switch_core_session_t *session, default: break; } - + return SWITCH_STATUS_SUCCESS; } @@ -1152,7 +1152,10 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi int argc = 0; const char *var; const char *dest_num = NULL, *callerid_num = NULL; - ftdm_hunting_scheme_t hunting; + ftdm_hunting_scheme_t hunting; + ftdm_sigmsg_t sigmsg; + + memset(&sigmsg, 0, sizeof(ftdm_sigmsg_t)); if (!outbound_profile) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); @@ -1343,11 +1346,6 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi if ((var = channel_get_variable(session, var_event, "freetdm_calling_party_category"))) { ftdm_set_calling_party_category(var, (uint8_t *)&caller_data.cpc); } - - if ((var = channel_get_variable(session, var_event, "freetdm_custom_call_data"))) { - ftdm_set_string(caller_data.raw_data, var); - caller_data.raw_data_len = (uint32_t)strlen(var); - } if (!zstr(dest)) { ftdm_set_string(caller_data.dnis.digits, dest); @@ -1386,7 +1384,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi char *v = h->name + FREETDM_VAR_PREFIX_LEN; if (!zstr(v)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding outbound freetdm variable %s=%s to channel %d:%d\n", v, h->value, span_id, chan_id); - ftdm_call_add_var(&caller_data, v, h->value); + ftdm_event_add_var(&sigmsg, v, h->value); } } } @@ -1413,6 +1411,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi hunt_data.caller_profile = caller_profile; hunt_data.tech_pvt = tech_pvt; caller_data.priv = &hunt_data; + caller_data.sigmsg = &sigmsg; if ((status = ftdm_call_place(&caller_data, &hunting)) != FTDM_SUCCESS) { if (tech_pvt->read_codec.implementation) { @@ -1430,7 +1429,6 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi } goto fail; } - ftdm_channel_init(caller_data.fchan); return SWITCH_CAUSE_SUCCESS; @@ -1552,6 +1550,8 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", chanid); switch_channel_set_variable_printf(channel, "freetdm_bearer_capability", "%d", channel_caller_data->bearer_capability); switch_channel_set_variable_printf(channel, "freetdm_bearer_layer1", "%d", channel_caller_data->bearer_layer1); + switch_channel_set_variable_printf(channel, "screening_ind", ftdm_screening2str(channel_caller_data->screen)); + switch_channel_set_variable_printf(channel, "presentation_ind", ftdm_presentation2str(channel_caller_data->pres)); if (globals.sip_headers) { switch_channel_set_variable(channel, "sip_h_X-FreeTDM-SpanName", ftdm_channel_get_span_name(sigmsg->channel)); @@ -1577,28 +1577,17 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session switch_channel_set_variable_printf(channel, "sip_h_X-FreeTDM-Screen", "%d", channel_caller_data->screen); switch_channel_set_variable_printf(channel, "sip_h_X-FreeTDM-Presentation", "%d", channel_caller_data->pres); } - if (channel_caller_data->raw_data_len) { - switch_channel_set_variable_printf(channel, "freetdm_custom_call_data", "%s", channel_caller_data->raw_data); - } - /* Add any channel variable to the dial plan */ - iter = ftdm_channel_get_var_iterator(sigmsg->channel, NULL); - for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { - ftdm_channel_get_current_var(curr, &var_name, &var_value); - snprintf(name, sizeof(name), FREETDM_VAR_PREFIX "%s", var_name); - switch_channel_set_variable_printf(channel, name, "%s", var_value); - } /* Add any call variable to the dial plan */ - iter = ftdm_call_get_var_iterator(channel_caller_data, iter); + iter = ftdm_event_get_var_iterator(sigmsg, iter); for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { - ftdm_call_get_current_var(curr, &var_name, &var_value); + ftdm_event_get_current_var(curr, &var_name, &var_value); snprintf(name, sizeof(name), FREETDM_VAR_PREFIX "%s", var_name); switch_channel_set_variable_printf(channel, name, "%s", var_value); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Call Variable: %s=%s\n", name, var_value); } ftdm_iterator_free(iter); - switch_channel_set_state(channel, CS_INIT); if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n"); @@ -2149,8 +2138,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) switch(sigmsg->event_id) { case FTDM_SIGEVENT_START: { - ftdm_call_add_var(caller_data, "screening_ind", ftdm_screening2str(caller_data->screen)); - ftdm_call_add_var(caller_data, "presentation_ind", ftdm_presentation2str(caller_data->pres)); return ftdm_channel_from_event(sigmsg, &session); } break; diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj index 0539ff3f42..348a66f50b 100644 --- a/libs/freetdm/msvc/freetdm.2008.vcproj +++ b/libs/freetdm/msvc/freetdm.2008.vcproj @@ -428,6 +428,10 @@ RelativePath="..\src\ftdm_call_utils.c" > + + diff --git a/libs/freetdm/msvc/freetdm.2010.vcxproj b/libs/freetdm/msvc/freetdm.2010.vcxproj index aecb9ef79c..62a36ff225 100644 --- a/libs/freetdm/msvc/freetdm.2010.vcxproj +++ b/libs/freetdm/msvc/freetdm.2010.vcxproj @@ -203,6 +203,7 @@ + @@ -222,4 +223,4 @@ - \ No newline at end of file + diff --git a/libs/freetdm/msvc/freetdm.2010.vcxproj.filters b/libs/freetdm/msvc/freetdm.2010.vcxproj.filters index e6dc40d372..9f15c9c737 100644 --- a/libs/freetdm/msvc/freetdm.2010.vcxproj.filters +++ b/libs/freetdm/msvc/freetdm.2010.vcxproj.filters @@ -85,6 +85,9 @@ Source Files + + Source Files + Source Files @@ -131,4 +134,4 @@ Source Files - \ No newline at end of file + diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 01125e2f61..bd7b043a34 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -62,10 +62,8 @@ struct tm *localtime_r(const time_t *clock, struct tm *result); ftdm_time_t time_last_throttle_log = 0; ftdm_time_t time_current_throttle_log = 0; -static ftdm_iterator_t *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data); static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data); -static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan); static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan); static int time_is_init = 0; @@ -2390,12 +2388,14 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const ch #endif ftdm_channel_lock(ftdmchan); + ftdm_channel_save_event_data(ftdmchan); if (ftdmchan->span->send_msg) { status = ftdmchan->span->send_msg(ftdmchan, sigmsg); } else { status = FTDM_NOTIMPL; ftdm_log(FTDM_LOG_ERROR, "send_msg method not implemented in this span!\n"); } + ftdm_channel_clear_event_data(ftdmchan); ftdm_channel_unlock(ftdmchan); return status; } @@ -2450,13 +2450,12 @@ static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *f goto done; } - /* in case of success, *before* unlocking the channel, we must set the call started flag and the call id - * that is a guarantee that signaling modules expect from us */ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED); ftdm_call_set_call_id(ftdmchan, &ftdmchan->caller_data); if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { - /* be aware this waiting unlocks the channel and locks it back when done */ - ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_STATE_CHANGE, 100); + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 1); + } else { + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0); } done: @@ -2633,7 +2632,7 @@ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan) ftdm_buffer_destroy(&ftdmchan->pre_buffer); ftdmchan->pre_buffer_size = 0; ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); - ftdm_channel_clear_vars(ftdmchan); + if (ftdmchan->hangup_timer) { ftdm_sched_cancel_timer(globals.timingsched, ftdmchan->hangup_timer); } @@ -4032,166 +4031,7 @@ done: return status; } -FT_DECLARE(void) ftdm_call_clear_data(ftdm_caller_data_t *caller_data) -{ - ftdm_call_clear_vars(caller_data); - memset(&caller_data->raw_data, 0, sizeof(caller_data->raw_data)); - caller_data->raw_data_len = 0; - return; -} - -FT_DECLARE(ftdm_status_t) ftdm_call_clear_vars(ftdm_caller_data_t *caller_data) -{ - if (caller_data->variables) { - hashtable_destroy(caller_data->variables); - } - caller_data->variables = NULL; - return FTDM_SUCCESS; -} - -FT_DECLARE(ftdm_status_t) ftdm_call_remove_var(ftdm_caller_data_t *caller_data, const char *var_name) -{ - if (caller_data->variables) { - hashtable_remove(caller_data->variables, (void *)var_name); - } - - return FTDM_SUCCESS; -} - - -FT_DECLARE(ftdm_status_t) ftdm_call_add_var(ftdm_caller_data_t *caller_data, const char *var_name, const char *value) -{ - char *t_name = 0, *t_val = 0; - - if (!var_name || !value) { - return FTDM_FAIL; - } - - if (!caller_data->variables) { - /* initialize on first use */ - caller_data->variables = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); - ftdm_assert_return(caller_data->variables, FTDM_FAIL, "Failed to create hash table\n"); - } - - t_name = ftdm_strdup(var_name); - t_val = ftdm_strdup(value); - hashtable_insert(caller_data->variables, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE); - return FTDM_SUCCESS; -} - -FT_DECLARE(const char *) ftdm_call_get_var(ftdm_caller_data_t *caller_data, const char *var_name) -{ - const char *var = NULL; - - if (!caller_data->variables || !var_name) { - return NULL; - } - - var = (const char *)hashtable_search(((struct hashtable*)caller_data->variables), (void *)var_name); - return var; -} - -FT_DECLARE(ftdm_iterator_t *) ftdm_call_get_var_iterator(const ftdm_caller_data_t *caller_data, ftdm_iterator_t *iter) -{ - ftdm_hash_iterator_t *hashiter = NULL; - hashiter = caller_data->variables == NULL ? NULL : hashtable_first(caller_data->variables); - - if (hashiter == NULL) { - return NULL; - } - - if (!(iter = get_iterator(FTDM_ITERATOR_VARS, iter))) { - return NULL; - } - iter->pvt.hashiter = hashiter; - return iter; -} - -FT_DECLARE(ftdm_status_t) ftdm_call_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val) -{ - const void *key = NULL; - void *val = NULL; - - *var_name = NULL; - *var_val = NULL; - - ftdm_assert_return(iter && (iter->type == FTDM_ITERATOR_VARS) && iter->pvt.hashiter, FTDM_FAIL, "Cannot get variable from invalid iterator!\n"); - - hashtable_this(iter->pvt.hashiter, &key, NULL, &val); - - *var_name = key; - *var_val = val; - - return FTDM_SUCCESS; -} - - -static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan) -{ - ftdm_channel_lock(ftdmchan); - - if (ftdmchan->variable_hash) { - hashtable_destroy(ftdmchan->variable_hash); - } - ftdmchan->variable_hash = NULL; - - ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; -} - -FT_DECLARE(ftdm_status_t) ftdm_channel_add_var(ftdm_channel_t *ftdmchan, const char *var_name, const char *value) -{ - char *t_name = 0, *t_val = 0; - - ftdm_status_t status = FTDM_FAIL; - - if (!var_name || !value) { - return FTDM_FAIL; - } - - ftdm_channel_lock(ftdmchan); - - if (!ftdmchan->variable_hash) { - /* initialize on first use */ - ftdmchan->variable_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); - if (!ftdmchan->variable_hash) { - goto done; - } - } - - t_name = ftdm_strdup(var_name); - t_val = ftdm_strdup(value); - - hashtable_insert(ftdmchan->variable_hash, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE); - - status = FTDM_SUCCESS; - -done: - ftdm_channel_unlock(ftdmchan); - - return status; -} - - -FT_DECLARE(const char *) ftdm_channel_get_var(ftdm_channel_t *ftdmchan, const char *var_name) -{ - const char *var = NULL; - - ftdm_channel_lock(ftdmchan); - - if (!ftdmchan->variable_hash || !var_name) { - goto done; - } - - var = (const char *)hashtable_search(ftdmchan->variable_hash, (void *)var_name); - -done: - ftdm_channel_unlock(ftdmchan); - - return var; -} - -static ftdm_iterator_t *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter) +FT_DECLARE(ftdm_iterator_t) *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter) { int allocated = 0; if (iter) { @@ -4215,25 +4055,6 @@ static ftdm_iterator_t *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t return iter; } -FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan, ftdm_iterator_t *iter) -{ - ftdm_hash_iterator_t *hashiter = NULL; - ftdm_channel_lock(ftdmchan); - hashiter = ftdmchan->variable_hash == NULL ? NULL : hashtable_first(ftdmchan->variable_hash); - ftdm_channel_unlock(ftdmchan); - - - if (hashiter == NULL) { - return NULL; - } - - if (!(iter = get_iterator(FTDM_ITERATOR_VARS, iter))) { - return NULL; - } - iter->pvt.hashiter = hashiter; - return iter; -} - FT_DECLARE(ftdm_iterator_t *) ftdm_span_get_chan_iterator(const ftdm_span_t *span, ftdm_iterator_t *iter) { if (!(iter = get_iterator(FTDM_ITERATOR_CHANS, iter))) { @@ -4244,24 +4065,6 @@ FT_DECLARE(ftdm_iterator_t *) ftdm_span_get_chan_iterator(const ftdm_span_t *spa return iter; } -FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val) -{ - const void *key = NULL; - void *val = NULL; - - *var_name = NULL; - *var_val = NULL; - - ftdm_assert_return(iter && (iter->type == FTDM_ITERATOR_VARS) && iter->pvt.hashiter, FTDM_FAIL, "Cannot get variable from invalid iterator!\n"); - - hashtable_this(iter->pvt.hashiter, &key, NULL, &val); - - *var_name = key; - *var_val = val; - - return FTDM_SUCCESS; -} - FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter) { ftdm_assert_return(iter && iter->type, NULL, "Invalid iterator\n"); @@ -5476,13 +5279,8 @@ static ftdm_status_t ftdm_span_trigger_signal(const ftdm_span_t *span, ftdm_sigm { ftdm_status_t status = span->signal_cb(sigmsg); if (sigmsg->channel) { - ftdm_call_clear_data(&(sigmsg->channel->caller_data)); - } - if (sigmsg->raw.autofree) { - ftdm_safe_free(sigmsg->raw.data); - sigmsg->raw.data = NULL; - sigmsg->raw.len = 0; - } + ftdm_channel_clear_event_data(sigmsg->channel); + } return status; } @@ -6221,6 +6019,71 @@ static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data) return FTDM_SUCCESS; } +FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen) +{ + if (!sigmsg || !sigmsg->raw.len) { + return FTDM_FAIL; + } + + *data = sigmsg->raw.data; + *datalen = sigmsg->raw.len; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_event_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen, uint8_t autofree) +{ + ftdm_assert_return(sigmsg, FTDM_FAIL, "Trying to set raw data on a NULL event\n"); + ftdm_assert_return(sigmsg->raw.len, FTDM_FAIL, "Overwriting existing raw data\n"); + + sigmsg->raw.data = ftdm_calloc(1, datalen); + memcpy(sigmsg->raw.data, data, datalen); + sigmsg->raw.len = datalen; + sigmsg->raw.autofree = autofree; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_save_event_data(ftdm_channel_t *ftdmchan) +{ + ftdm_assert_return(!ftdmchan->sigmsg, FTDM_FAIL, "Info from previous event was not cleared\n"); + if (ftdmchan->caller_data.sigmsg) { + /* Copy sigmsg from user to internal copy so user can set new variables without race condition */ + ftdmchan->sigmsg = ftdm_calloc(1, sizeof(ftdm_sigmsg_t)); + memcpy(ftdmchan->sigmsg, ftdmchan->caller_data.sigmsg, sizeof(ftdm_sigmsg_t)); + + if (ftdmchan->caller_data.sigmsg->raw.data) { + ftdm_event_set_raw_data(ftdmchan->sigmsg, ftdmchan->caller_data.sigmsg->raw.data, ftdmchan->caller_data.sigmsg->raw.len, 1); + + if (ftdmchan->caller_data.sigmsg->raw.autofree) { + ftdm_safe_free(ftdmchan->caller_data.sigmsg->raw.data); + } + } + ftdmchan->caller_data.sigmsg = NULL; + } + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_event_data(ftdm_channel_t *ftdmchan) +{ + if (!ftdmchan->sigmsg) { + return FTDM_SUCCESS; + } + + if (ftdmchan->sigmsg->variables) { + ftdm_event_clear_vars(ftdmchan->sigmsg); + } + + if (ftdmchan->sigmsg->raw.data && ftdmchan->sigmsg->raw.autofree) { + ftdm_safe_free(ftdmchan->sigmsg->raw.data); + ftdmchan->sigmsg->raw.data = NULL; + ftdmchan->sigmsg->raw.len = 0; + } + + ftdm_safe_free(ftdmchan->sigmsg); + ftdmchan->sigmsg = NULL; + return FTDM_SUCCESS; +} diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index de62c0f0e7..08902421cd 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -40,13 +40,15 @@ FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_ FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID) +static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq); + /* This function is only needed for boost and we should get rid of it at the next refactoring */ FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan) { ftdm_channel_lock(fchan); if (fchan->init_state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1); + ftdm_core_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1); fchan->init_state = FTDM_CHANNEL_STATE_DOWN; } @@ -121,7 +123,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, in the current state */ _ftdm_channel_complete_state(file, func, line, fchan); } - return ftdm_channel_set_state(file, func, line, fchan, state, 0); + return ftdm_core_set_state(file, func, line, fchan, state, 0); } static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map) @@ -223,9 +225,18 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char return FTDM_SUCCESS; } + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) +{ + if (ftdm_channel_save_event_data(ftdmchan) == FTDM_SUCCESS) { + return ftdm_core_set_state(file, func, line, ftdmchan, state, waitrq); + } + return FTDM_FAIL; +} + /* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */ #define DEFAULT_WAIT_TIME 1000 -FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) +static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) { ftdm_status_t status; int ok = 1; @@ -514,6 +525,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan) * call to ftdm_set_state() */ fchan->state_status = FTDM_STATE_STATUS_PROCESSED; } + ftdm_channel_clear_event_data(fchan); } return FTDM_SUCCESS; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c index 608b0b6ac0..9947d40d76 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c @@ -649,8 +649,7 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending incoming call from %s to %s to FTDM core\n", ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.dnis.digits); /* we have enough information to inform FTDM of the call*/ - sigev.event_id = FTDM_SIGEVENT_START; - ftdm_span_send_signal(ftdmchan->span, &sigev); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_START); } break; case FTDM_CHANNEL_STATE_DIALING: /* outgoing call request */ @@ -674,8 +673,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { /*OUTBOUND...so we were told by the line of this so noifiy the user*/ - sigev.event_id = FTDM_SIGEVENT_PROCEED; - ftdm_span_send_signal(ftdmchan->span, &sigev); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROCEED); + if (sngisdn_test_flag(sngisdn_info, FLAG_MEDIA_READY)) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); } @@ -694,8 +693,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { /* OUTBOUND...so we were told by the line of this so notify the user */ - sigev.event_id = FTDM_SIGEVENT_RINGING; - ftdm_span_send_signal(ftdmchan->span, &sigev); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_RINGING); + if (sngisdn_test_flag(sngisdn_info, FLAG_MEDIA_READY)) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); } @@ -710,8 +709,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm /*check if the channel is inbound or outbound*/ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { /*OUTBOUND...so we were told by the line of this so noifiy the user*/ - sigev.event_id = FTDM_SIGEVENT_PROGRESS; - ftdm_span_send_signal(ftdmchan->span, &sigev); + + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROGRESS); } else { /* Send a progress message, indicating: Call is not end-to-end ISDN, further call progress may be available */ ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN}; @@ -722,8 +721,7 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; - ftdm_span_send_signal(ftdmchan->span, &sigev); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROGRESS_MEDIA); } else { /* Send a progress message, indicating: In-band information/pattern available */ ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_IB_AVAIL}; @@ -736,8 +734,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm /* check if the channel is inbound or outbound */ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { /* OUTBOUND ... so we were told by the line that the other side answered */ - sigev.event_id = FTDM_SIGEVENT_UP; - ftdm_span_send_signal(ftdmchan->span, &sigev); + + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_UP); if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP && ((sngisdn_span_data_t*)ftdmchan->span->signal_data)->signalling == SNGISDN_SIGNALING_NET) { @@ -763,8 +761,7 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm case FTDM_CHANNEL_STATE_TERMINATING: /* call is hung up by the remote end */ { /* this state is set when the line is hanging up */ - sigev.event_id = FTDM_SIGEVENT_STOP; - ftdm_span_send_signal(ftdmchan->span, &sigev); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_STOP); } break; case FTDM_CHANNEL_STATE_HANGUP: /* call is hung up locally */ @@ -878,7 +875,22 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm } break; } - + + /* If sngisdn_info->variables is not NULL, it means did not send any + * sigevent to the user, therefore we have to free that sigmsg */ + if (sngisdn_info->variables) { + hashtable_destroy(sngisdn_info->variables); + sngisdn_info->variables = NULL; + } + + /* If sngisdn_info->raw_data is not NULL, it means did not send any + * sigevent to the user, therefore we have to free that sigmsg */ + if (sngisdn_info->raw_data) { + ftdm_safe_free(sngisdn_info->raw_data); + sngisdn_info->raw_data = NULL; + sngisdn_info->raw_data_len = 0; + } + if (ftdmchan->state == initial_state) { ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "state change flag is still set, but we did not change state\n"); } @@ -902,8 +914,7 @@ static FIO_CHANNEL_SEND_MSG_FUNCTION(ftdm_sangoma_isdn_send_msg) default: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Unsupported signalling msg requested\n"); status = FTDM_BREAK; - } - ftdm_call_clear_data(&ftdmchan->caller_data); + } return status; } @@ -911,34 +922,25 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(ftdm_sangoma_isdn_outgoing_call) { sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; ftdm_status_t status = FTDM_FAIL; - - switch (ftdmchan->state) { - case FTDM_CHANNEL_STATE_DOWN: - { - if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE)) { - /* A call came in after we called ftdm_channel_open_chan for this call, but before we got here */ - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Glare detected - aborting outgoing call\n"); + if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) { + if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE)) { + /* A call came in after we called ftdm_channel_open_chan for this call, but before we got here */ + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Glare detected - aborting outgoing call\n"); - sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); - status = FTDM_BREAK; - } else { - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING); - status = FTDM_SUCCESS; - } - } - break; - default: - { - /* the channel is already used...this can't be, end the request */ - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Outgoing call requested channel in already in use\n"); status = FTDM_BREAK; + } else { + status = FTDM_SUCCESS; } - break; + } else { + /* the channel is already used...this can't be, end the request */ + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Outgoing call requested channel in already in use (%s)\n", ftdm_channel_state2str(ftdmchan->state)); + status = FTDM_BREAK; } - + return status; } static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_get_chan_sig_status) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h index 80dc73f0fa..79ca889d25 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h @@ -179,6 +179,15 @@ typedef struct sngisdn_chan_data { uint8_t globalFlg; sngisdn_glare_data_t glare; ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS]; + + /* variables saved here will be sent to the user application + on next SIGEVENT_XXX */ + ftdm_hash_t* variables; + + /* raw_data saved here will be sent to the user application + on next SIGEVENT_XXX */ + void *raw_data; + ftdm_size_t raw_data_len; } sngisdn_chan_data_t; /* Span specific data */ @@ -414,6 +423,12 @@ ftdm_status_t set_restart_ind_ie(ftdm_channel_t *ftdmchan, RstInd *rstInd); ftdm_status_t set_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr); ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t *data_len); + +ftdm_status_t sngisdn_add_var(sngisdn_chan_data_t *sngisdn_info, const char* var, const char* val); +ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* data, ftdm_size_t data_len); +ftdm_status_t sngisdn_clear_data(sngisdn_chan_data_t *sngisdn_info); +void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t event_id); + uint8_t sngisdn_get_infoTranCap_from_user(ftdm_bearer_cap_t bearer_capability); uint8_t sngisdn_get_usrInfoLyr1Prot_from_user(ftdm_user_layer1_prot_t layer1_prot); ftdm_bearer_cap_t sngisdn_get_infoTranCap_from_stack(uint8_t bearer_capability); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c index 4b27f21e1c..be204e5eb3 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c @@ -156,8 +156,6 @@ void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_i set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind); set_facility_ie(ftdmchan, &cnStEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); - ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROCEED (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, MI_CALLPROC, signal_data->dchan_id, sngisdn_info->ces)) { @@ -189,7 +187,6 @@ void sngisdn_snd_progress(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ memset(&cnStEvnt, 0, sizeof(cnStEvnt)); set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind); set_facility_ie(ftdmchan, &cnStEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROGRESS (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId,&cnStEvnt, MI_PROGRESS, signal_data->dchan_id, sngisdn_info->ces)) { @@ -216,7 +213,6 @@ void sngisdn_snd_alert(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ind set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind); set_facility_ie(ftdmchan, &cnStEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending ALERT (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); @@ -241,11 +237,10 @@ void sngisdn_snd_connect(ftdm_channel_t *ftdmchan) } memset(&cnStEvnt, 0, sizeof(cnStEvnt)); - + set_chan_id_ie(ftdmchan, &cnStEvnt.chanId); set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind); set_facility_ie(ftdmchan, &cnStEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending CONNECT (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if (sng_isdn_con_response(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, signal_data->dchan_id, sngisdn_info->ces)) { @@ -272,7 +267,6 @@ void sngisdn_snd_fac_req(ftdm_channel_t *ftdmchan) /* No point in sending a FACILITY message if there is no Facility IE to transmit */ return; } - ftdm_call_clear_data(&ftdmchan->caller_data); facEvnt.facElmt.eh.pres = PRSNT_NODEF; facEvnt.facElmt.facStr.pres = PRSNT_NODEF; @@ -306,7 +300,7 @@ void sngisdn_snd_info_req(ftdm_channel_t *ftdmchan) //ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Sending INFO REQ\n"); - ftdm_call_clear_data(&ftdmchan->caller_data); + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending INFO REQ (suId:%d dchan:%d ces:%d)\n", signal_data->cc_id, signal_data->dchan_id, sngisdn_info->ces); @@ -327,8 +321,6 @@ void sngisdn_snd_status_enq(ftdm_channel_t *ftdmchan) //ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Sending STATUS ENQ\n"); memset(&staEvnt, 0, sizeof(StaEvnt)); - - ftdm_call_clear_data(&ftdmchan->caller_data); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending Status ENQ on suId:%d suInstId:%u spInstId:%d dchan:%d ces:%d\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if (sng_isdn_status_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &staEvnt, MI_STATENQ)) { @@ -367,8 +359,7 @@ void sngisdn_snd_disconnect(ftdm_channel_t *ftdmchan) discEvnt.causeDgn[0].recommend.pres = NOTPRSNT; discEvnt.causeDgn[0].dgnVal.pres = NOTPRSNT; - set_facility_ie(ftdmchan, &discEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); + set_facility_ie(ftdmchan, &discEvnt.facilityStr); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId); if (sng_isdn_disc_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &discEvnt)) { @@ -416,7 +407,6 @@ void sngisdn_snd_release(ftdm_channel_t *ftdmchan, uint8_t glare) } set_facility_ie(ftdmchan, &relEvnt.facilityStr); - ftdm_call_clear_data(&ftdmchan->caller_data); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending RELEASE/RELEASE COMPLETE (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, suInstId, spInstId); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c index 23fe08b983..55e72a84f5 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c @@ -225,26 +225,26 @@ ftdm_status_t get_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) } if (cgPtyNmb->screenInd.pres == PRSNT_NODEF) { - ftdm_call_add_var(caller_data, "isdn.cg_pty2.screen_ind", ftdm_screening2str(cgPtyNmb->screenInd.val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.cg_pty2.screen_ind", ftdm_screening2str(cgPtyNmb->screenInd.val)); } if (cgPtyNmb->presInd0.pres == PRSNT_NODEF) { - ftdm_call_add_var(caller_data, "isdn.cg_pty2.presentation_ind", ftdm_presentation2str(cgPtyNmb->presInd0.val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.cg_pty2.presentation_ind", ftdm_presentation2str(cgPtyNmb->presInd0.val)); } if (cgPtyNmb->nmbPlanId.pres == PRSNT_NODEF) { - ftdm_call_add_var(caller_data, "isdn.cg_pty2.npi", ftdm_npi2str(cgPtyNmb->nmbPlanId.val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.cg_pty2.npi", ftdm_npi2str(cgPtyNmb->nmbPlanId.val)); } if (cgPtyNmb->typeNmb1.pres == PRSNT_NODEF) { - ftdm_call_add_var(caller_data, "isdn.cg_pty2.ton", ftdm_ton2str(cgPtyNmb->typeNmb1.val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.cg_pty2.ton", ftdm_ton2str(cgPtyNmb->typeNmb1.val)); } if (cgPtyNmb->nmbDigits.pres == PRSNT_NODEF) { char digits_string [32]; memcpy(digits_string, (const char*)cgPtyNmb->nmbDigits.val, cgPtyNmb->nmbDigits.len); digits_string[cgPtyNmb->nmbDigits.len] = '\0'; - ftdm_call_add_var(caller_data, "isdn.cg_pty2.digits", digits_string); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.cg_pty2.digits", digits_string); } memcpy(&caller_data->ani, &caller_data->cid_num, sizeof(caller_data->ani)); return FTDM_SUCCESS; @@ -343,7 +343,7 @@ ftdm_status_t get_calling_subaddr(ftdm_channel_t *ftdmchan, CgPtySad *cgPtySad) memcpy(subaddress, (char*)cgPtySad->sadInfo.val, cgPtySad->sadInfo.len); subaddress[cgPtySad->sadInfo.len] = '\0'; - ftdm_call_add_var(&ftdmchan->caller_data, "isdn.calling_subaddr", subaddress); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.calling_subaddr", subaddress); return FTDM_SUCCESS; } @@ -358,21 +358,20 @@ ftdm_status_t get_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr ftdm_status_t get_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t data_len) { - ftdm_caller_data_t *caller_data = &ftdmchan->caller_data; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; if (signal_data->facility_ie_decode == SNGISDN_OPT_FALSE) { - /* size of facilityStr->facilityStr.len is a uint8_t so no need to check - for overflow here as facilityStr->facilityStr.len will always be smaller - than sizeof(caller_data->raw_data) */ + /* Max size of Facility IE is 255 */ + uint8_t my_data [255]; - memset(caller_data->raw_data, 0, sizeof(caller_data->raw_data)); /* Always include Facility IE identifier + len so this can be used as a sanity check by the user */ - caller_data->raw_data[0] = SNGISDN_Q931_FACILITY_IE_ID; - caller_data->raw_data[1] = data_len; - - memcpy(&caller_data->raw_data[2], data, data_len); - caller_data->raw_data_len = data_len+2; + my_data[0] = SNGISDN_Q931_FACILITY_IE_ID; + my_data[1] = data_len; + memcpy(&my_data[2], data, data_len); + + + sngisdn_add_raw_data((sngisdn_chan_data_t*)ftdmchan->call_data, data, data_len+2); + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Raw Facility IE copied available\n"); } else { /* Call libsng_isdn to process facility IE's here */ @@ -412,7 +411,7 @@ ftdm_status_t get_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd) val = SNGISDN_PROGIND_DESCR_INVALID; break; } - ftdm_call_add_var(&ftdmchan->caller_data, "isdn.prog_ind.descr", ftdm_sngisdn_progind_descr2str(val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.prog_ind.descr", ftdm_sngisdn_progind_descr2str(val)); } if (progInd->location.pres) { @@ -443,7 +442,7 @@ ftdm_status_t get_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd) val = SNGISDN_PROGIND_LOC_INVALID; break; } - ftdm_call_add_var(&ftdmchan->caller_data, "isdn.prog_ind.loc", ftdm_sngisdn_progind_loc2str(val)); + sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.prog_ind.loc", ftdm_sngisdn_progind_loc2str(val)); } return FTDM_SUCCESS; } @@ -493,7 +492,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) uint8_t len,val; ftdm_caller_data_t *caller_data = &ftdmchan->caller_data; - string = ftdm_call_get_var(caller_data, "isdn.cg_pty2.digits"); + string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.digits"); if ((string == NULL) || !(*string)) { return FTDM_FAIL; } @@ -510,7 +509,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->screenInd.pres = PRSNT_NODEF; val = FTDM_SCREENING_INVALID; - string = ftdm_call_get_var(caller_data, "isdn.cg_pty2.screening_ind"); + string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.screening_ind"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_screening(string); } @@ -527,7 +526,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->presInd0.pres = PRSNT_NODEF; val = FTDM_PRES_INVALID; - string = ftdm_call_get_var(caller_data, "isdn.cg_pty2.presentation_ind"); + string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.presentation_ind"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_presentation(string); } @@ -542,7 +541,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->nmbPlanId.pres = PRSNT_NODEF; val = FTDM_NPI_INVALID; - string = ftdm_call_get_var(caller_data, "isdn.cg_pty2.npi"); + string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.npi"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_npi(string); } @@ -557,7 +556,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) /* Type of Number */ val = FTDM_TON_INVALID; - string = ftdm_call_get_var(caller_data, "isdn.cg_pty2.ton"); + string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.ton"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_ton(string); } @@ -691,7 +690,7 @@ ftdm_status_t set_calling_name(ftdm_channel_t *ftdmchan, ConEvnt *conEvnt) ftdm_status_t set_calling_subaddr(ftdm_channel_t *ftdmchan, CgPtySad *cgPtySad) { const char* clg_subaddr = NULL; - clg_subaddr = ftdm_call_get_var(&ftdmchan->caller_data, "isdn.calling_subaddr"); + clg_subaddr = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.calling_subaddr"); if ((clg_subaddr != NULL) && (*clg_subaddr)) { unsigned len = strlen (clg_subaddr); cgPtySad->eh.pres = PRSNT_NODEF; @@ -721,14 +720,17 @@ ftdm_status_t set_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t *data_len) { - int len; - ftdm_caller_data_t *caller_data = &ftdmchan->caller_data; - if (caller_data->raw_data_len > 0 && caller_data->raw_data[0] == SNGISDN_Q931_FACILITY_IE_ID) { - len = caller_data->raw_data[1]; - memcpy(data, &caller_data->raw_data[2], len); - *data_len = len; - return FTDM_SUCCESS; - } + ftdm_size_t len; + uint8_t *mydata; + + if (ftdm_event_get_raw_data(ftdmchan->sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (len > 2 && mydata[0] == SNGISDN_Q931_FACILITY_IE_ID) { + len = mydata[1]; + memcpy(data, &mydata[2], len); + *data_len = len; + return FTDM_SUCCESS; + } + } return FTDM_FAIL; } @@ -738,7 +740,7 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s int descr = prog_ind.descr; int loc = prog_ind.loc; - str = ftdm_call_get_var(&ftdmchan->caller_data, "isdn.prog_ind.descr"); + str = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.prog_ind.descr"); if (str && *str) { /* User wants to override progress indicator */ descr = ftdm_str2ftdm_sngisdn_progind_descr(str); @@ -749,7 +751,7 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s return FTDM_SUCCESS; } - str = ftdm_call_get_var(&ftdmchan->caller_data, "isdn.prog_ind.loc"); + str = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.prog_ind.loc"); if (str && *str) { loc = ftdm_str2ftdm_sngisdn_progind_loc(str); } @@ -861,7 +863,7 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId) ftdm_status_t set_bear_cap_ie(ftdm_channel_t *ftdmchan, BearCap *bearCap) { sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; - + bearCap->eh.pres = PRSNT_NODEF; bearCap->infoTranCap.pres = PRSNT_NODEF; bearCap->infoTranCap.val = sngisdn_get_infoTranCap_from_user(ftdmchan->caller_data.bearer_capability); @@ -1194,6 +1196,75 @@ void sngisdn_print_spans(ftdm_stream_handle_t *stream) return; } +ftdm_status_t sngisdn_add_var(sngisdn_chan_data_t *sngisdn_info, const char* var, const char* val) +{ + char *t_name = 0, *t_val = 0; + if (!var || !val) { + return FTDM_FAIL; + } + if (!sngisdn_info->variables) { + /* initialize on first use */ + sngisdn_info->variables = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + ftdm_assert_return(sngisdn_info->variables, FTDM_FAIL, "Failed to create hash table\n"); + } + t_name = ftdm_strdup(var); + t_val = ftdm_strdup(val); + hashtable_insert(sngisdn_info->variables, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE); + return FTDM_SUCCESS; +} + +ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* data, ftdm_size_t data_len) +{ + ftdm_assert_return(!sngisdn_info->raw_data, FTDM_FAIL, "Overwriting existing raw data\n"); + + sngisdn_info->raw_data = ftdm_calloc(1, data_len); + ftdm_assert_return(sngisdn_info->raw_data, FTDM_FAIL, "Failed to allocate raw data\n"); + + memcpy(sngisdn_info->raw_data, data, data_len); + sngisdn_info->raw_data_len = data_len; + return FTDM_SUCCESS; +} + +void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t event_id) +{ + ftdm_sigmsg_t sigev; + ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; + + memset(&sigev, 0, sizeof(sigev)); + + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + sigev.event_id = event_id; + + if (sngisdn_info->variables) { + /* + * variables now belongs to the ftdm core, and + * will be cleared after sigev is processed by user. Set + * local pointer to NULL so we do not attempt to + * destroy it */ + sigev.variables = sngisdn_info->variables; + sngisdn_info->variables = NULL; + } + + if (sngisdn_info->raw_data) { + /* + * raw_data now belongs to the ftdm core, and + * will be cleared after sigev is processed by user. Set + * local pointer to NULL so we do not attempt to + * destroy it */ + + sigev.raw.data = sngisdn_info->raw_data; + sigev.raw.len = sngisdn_info->raw_data_len; + sigev.raw.autofree = 1; + + sngisdn_info->raw_data = NULL; + sngisdn_info->raw_data_len = 0; + } + ftdm_span_send_signal(ftdmchan->span, &sigev); +} + + /* For Emacs: * Local Variables: * mode:c diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 77abf6ab3e..84adf327dd 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -172,7 +172,7 @@ typedef enum { /*! \brief I/O channel type */ typedef enum { FTDM_CHAN_TYPE_B, /*!< Bearer channel */ - FTDM_CHAN_TYPE_DQ921, /*< DQ921 channel (D-channel) */ + FTDM_CHAN_TYPE_DQ921, /*!< DQ921 channel (D-channel) */ FTDM_CHAN_TYPE_DQ931, /*!< DQ931 channel */ FTDM_CHAN_TYPE_FXS, /*!< FXS analog channel */ FTDM_CHAN_TYPE_FXO, /*!< FXO analog channel */ @@ -214,6 +214,7 @@ typedef void *(*ftdm_queue_dequeue_func_t)(ftdm_queue_t *queue); typedef ftdm_status_t (*ftdm_queue_wait_func_t)(ftdm_queue_t *queue, int ms); typedef ftdm_status_t (*ftdm_queue_get_interrupt_func_t)(ftdm_queue_t *queue, ftdm_interrupt_t **interrupt); typedef ftdm_status_t (*ftdm_queue_destroy_func_t)(ftdm_queue_t **queue); + typedef struct ftdm_queue_handler { ftdm_queue_create_func_t create; ftdm_queue_enqueue_func_t enqueue; @@ -223,7 +224,6 @@ typedef struct ftdm_queue_handler { ftdm_queue_destroy_func_t destroy; } ftdm_queue_handler_t; - /*! \brief Type Of Number (TON) */ typedef enum { FTDM_TON_UNKNOWN = 0, @@ -318,8 +318,6 @@ typedef struct { uint8_t plan; } ftdm_number_t; -typedef void * ftdm_variable_container_t; - /*! \brief Caller information */ typedef struct ftdm_caller_data { char cid_date[8]; /*!< Caller ID date */ @@ -333,21 +331,24 @@ typedef struct ftdm_caller_data { uint8_t pres; /*!< Presentation*/ char collected[FTDM_DIGITS_LIMIT]; /*!< Collected digits so far */ int hangup_cause; /*!< Hangup cause */ - char raw_data[1024]; /*!< Protocol specific raw caller data */ - uint32_t raw_data_len; /*!< Raw data length */ /* these 2 are undocumented right now, only used by boost: */ /* bearer capability */ ftdm_bearer_cap_t bearer_capability; /* user information layer 1 protocol */ ftdm_user_layer1_prot_t bearer_layer1; ftdm_calling_party_category_t cpc; /*!< Calling party category */ - ftdm_variable_container_t variables; /*!< Variables attached to this call */ - /* We need call_id inside caller_data for the user to be able to retrieve + + ftdm_channel_t *fchan; /*!< FreeTDM channel associated (can be NULL) */ + ftdm_sigmsg_t *sigmsg; /*!< Set by user if additional parameters need to sent */ + + /* + * We need call_id inside caller_data for the user to be able to retrieve * the call_id when ftdm_channel_call_place is called. This is the only time * that the user can use caller_data.call_id to obtain the call_id. The user - * should use the call_id from sigmsg otherwise */ + * should use the call_id from sigmsg otherwise + */ uint32_t call_id; /*!< Unique call ID for this call */ - ftdm_channel_t *fchan; /*!< FreeTDM channel associated (can be NULL) */ + void *priv; /*!< Private data for the FreeTDM user */ } ftdm_caller_data_t; @@ -547,6 +548,8 @@ typedef struct { ftdm_status_t status; } ftdm_event_indication_completed_t; +typedef void * ftdm_variable_container_t; + /*! \brief Generic signaling message */ struct ftdm_sigmsg { ftdm_signal_event_t event_id; /*!< The type of message */ @@ -555,6 +558,7 @@ struct ftdm_sigmsg { uint32_t span_id; /*!< easy access to span_id */ uint32_t call_id; /*!< unique call id for this call */ void *call_priv; /*!< Private data for the FreeTDM user from ftdm_caller_data->priv */ + ftdm_variable_container_t variables; union { ftdm_event_sigstatus_t sigstatus; /*!< valid if event_id is FTDM_SIGEVENT_SIGSTATUS_CHANGED */ ftdm_event_trace_t trace; /*!< valid if event_id is FTDM_SIGEVENT_TRACE or FTDM_SIGEVENT_TRACE_RAW */ @@ -563,7 +567,7 @@ struct ftdm_sigmsg { } ev_data; struct { uint8_t autofree; /*!< Whether the freetdm core will free it after message delivery */ - uint32_t len; /*!< Data len */ + ftdm_size_t len; /*!< Data len */ void *data; /*!< Signaling module specific data */ } raw; }; @@ -1369,24 +1373,49 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data */ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t datasize, ftdm_size_t *datalen); -/*! \brief Add a custom variable to the channel +/*! \brief Add a custom variable to the event * \note This variables may be used by signaling modules to override signaling parameters * \todo Document which signaling variables are available * */ -FT_DECLARE(ftdm_status_t) ftdm_channel_add_var(ftdm_channel_t *ftdmchan, const char *var_name, const char *value); +FT_DECLARE(ftdm_status_t) ftdm_event_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value); -/*! \brief Get a custom variable from the channel. - * \note The variable pointer returned is only valid while the channel is open and it'll be destroyed when the channel is closed. */ -FT_DECLARE(const char *) ftdm_channel_get_var(ftdm_channel_t *ftdmchan, const char *var_name); +/*! \brief Remove a custom variable from the event + * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ +FT_DECLARE(ftdm_status_t) ftdm_event_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name); -/*! \brief Get an iterator to iterate over the channel variables - * \param ftdmchan The channel structure containing the variables +/*! \brief Get a custom variable from the event + * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ +FT_DECLARE(const char *) ftdm_event_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name); + +/*! \brief Get an iterator to iterate over the event variables + * \param sigmsg The message structure containing the variables * \param iter Optional iterator. You can reuse an old iterator (not previously freed) to avoid the extra allocation of a new iterator. * \note The iterator pointer returned is only valid while the channel is open and it'll be destroyed when the channel is closed. * This iterator is completely non-thread safe, if you are adding variables or removing variables while iterating * results are unpredictable */ -FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan, ftdm_iterator_t *iter); +FT_DECLARE(ftdm_iterator_t *) ftdm_event_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter); + + +/*! \brief Attach raw data to event + * \param sigmsg The message structure containing the variables + * \param data pointer to data + * \param datalen datalen length of data + * \param autofree when set to non-zero, freetdm will automatically free data once event is processed + * \retval FTDM_SUCCESS success, data was successfully saved + * \retval FTDM_FAIL failed, event already had data attached to it. + * \note When using autofree, data must have been allocated using ftdm_calloc. + */ +FT_DECLARE(ftdm_status_t) ftdm_event_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen, uint8_t autofree); + +/*! \brief Get raw data from event + * \param sigmsg The message structure containing the variables + * \param data data will point to available data pointer if available + * \param datalen datalen will be set to length of data available + * \retval FTDM_SUCCESS data is available + * \retval FTDM_FAIL no data available + */ +FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen); /*! \brief Get iterator current value (depends on the iterator type) * \note Channel iterators return a pointer to ftdm_channel_t @@ -1395,7 +1424,7 @@ FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t FT_DECLARE(void *) ftdm_iterator_current(ftdm_iterator_t *iter); /*! \brief Get variable name and value for the current iterator position */ -FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val); +FT_DECLARE(ftdm_status_t) ftdm_event_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val); /*! \brief Advance iterator */ FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter); @@ -1405,40 +1434,6 @@ FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter); */ FT_DECLARE(ftdm_status_t) ftdm_iterator_free(ftdm_iterator_t *iter); -/*! \brief Add a custom variable to the call - * \note This variables may be used by signaling modules to override signaling parameters - * \todo Document which signaling variables are available - * */ -FT_DECLARE(ftdm_status_t) ftdm_call_add_var(ftdm_caller_data_t *caller_data, const char *var_name, const char *value); - -/*! \brief Get a custom variable from the call. - * \note The variable pointer returned is only valid during the callback receiving SIGEVENT. */ -FT_DECLARE(const char *) ftdm_call_get_var(ftdm_caller_data_t *caller_data, const char *var_name); - -/*! \brief Get an iterator to iterate over the channel variables - * \param caller_data The signal msg structure containing the variables - * \param iter Optional iterator. You can reuse an old iterator (not previously freed) to avoid the extra allocation of a new iterator. - * \note The iterator pointer returned is only valid while the signal message and it'll be destroyed when the signal message is processed. - * This iterator is completely non-thread safe, if you are adding variables or removing variables while iterating - * results are unpredictable - */ -FT_DECLARE(ftdm_iterator_t *) ftdm_call_get_var_iterator(const ftdm_caller_data_t *caller_data, ftdm_iterator_t *iter); - -/*! \brief Get variable name and value for the current iterator position */ -FT_DECLARE(ftdm_status_t) ftdm_call_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val); - -/*! \brief Clear all variables attached to the call - * \note Variables are cleared at the end of each call back, so it is not necessary for the user to call this function. - * \todo Document which signaling variables are available - * */ -FT_DECLARE(ftdm_status_t) ftdm_call_clear_vars(ftdm_caller_data_t *caller_data); - -/*! \brief Remove a variable attached to the call - * \note Removes a variable that was attached to the call. - * \todo Document which call variables are available - * */ -FT_DECLARE(ftdm_status_t) ftdm_call_remove_var(ftdm_caller_data_t *caller_data, const char *var_name); - /*! \brief Clears all the temporary data attached to this call * \note Clears caller_data->variables and caller_data->raw_data. * */ diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 7b26d655c5..fa519be9f5 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -468,6 +468,7 @@ struct ftdm_channel { ftdm_interrupt_t *state_completed_interrupt; /*!< Notify when a state change is completed */ int32_t txdrops; int32_t rxdrops; + ftdm_sigmsg_t *sigmsg; }; struct ftdm_span { @@ -588,6 +589,10 @@ FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void); FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan); FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status); + +FT_DECLARE(ftdm_iterator_t) *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); + + /*! * \brief Retrieves an event from the span * @@ -625,10 +630,21 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span); /*! \brief clear the tone detector state */ FT_DECLARE(void) ftdm_channel_clear_detected_tones(ftdm_channel_t *ftdmchan); -/* start/stop echo cancelling at the beginning/end of a call */ +/*! \brief adjust echocanceller for beginning of call */ FT_DECLARE(void) ftdm_set_echocancel_call_begin(ftdm_channel_t *chan); + +/*! \brief adjust echocanceller for end of call */ FT_DECLARE(void) ftdm_set_echocancel_call_end(ftdm_channel_t *chan); +/*! \brief clear variables hashtable inside sigmsg */ +FT_DECLARE(ftdm_status_t) ftdm_event_clear_vars(ftdm_sigmsg_t *sigmsg); + +/*! \brief save data from user(ftdmchan->caller_data.sigmsg) into internal copy (ftdmchan->sigmsg) */ +FT_DECLARE(ftdm_status_t) ftdm_channel_save_event_data(ftdm_channel_t *ftdmchan); + +/*! \brief free data from internal copy (ftdmchan->sigmsg) */ +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_event_data(ftdm_channel_t *ftdmchan); + /*! \brief Assert condition */ From 7b1da2316bc933d807a19812fe1e3b4354343f39 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 18 Feb 2011 13:21:05 -0500 Subject: [PATCH 005/154] freetdm: Updated signalling modules to not set channel state to DIALING on OUTGOING_CALL_FUNCTION --- .../freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c | 24 +++++++++++++++++-- .../src/ftmod/ftmod_libpri/ftmod_libpri.c | 3 +-- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 3 --- .../ftmod_sangoma_ss7_main.c | 5 ++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c index 5b4ce7196a..f9674a5046 100644 --- a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c @@ -287,7 +287,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) { ftdm_status_t status = FTDM_SUCCESS; ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); return status; } @@ -300,7 +299,14 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) #ifdef __TODO__ static FIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request) { +#if 1 /* FIXME caller_data.raw_data does not exist anymore, see docs/variables.txt for more info */ + Q931mes_Generic empty_gen; + Q931mes_Generic *gen = &empty_gen; + + memset(&empty_gen, 0, sizeof(empty_gen)) ; +#else Q931mes_Generic *gen = (Q931mes_Generic *) caller_data->raw_data; +#endif Q931ie_BearerCap BearerCap; Q931ie_ChanID ChanID = { 0 }; Q931ie_CallingNum CallingNum; @@ -1095,12 +1101,16 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic #ifdef __TODO_OR_REMOVE__ ftdmchan->caller_data.CRV = gen->CRV; #endif +#if 0 /* FIXME */ if (cplen > sizeof(caller_data->raw_data)) { cplen = sizeof(caller_data->raw_data); } +#endif gen->CRVFlag = !(gen->CRVFlag); +#if 0 /* FIXME */ memcpy(caller_data->raw_data, msg, cplen); caller_data->raw_data_len = cplen; +#endif fail = 0; } } @@ -1298,12 +1308,20 @@ static int ftdm_isdn_921_21(void *pvt, L2UCHAR *msg, L2INT mlen) static __inline__ void state_advance(ftdm_channel_t *ftdmchan) { - Q931mes_Generic *gen = (Q931mes_Generic *) ftdmchan->caller_data.raw_data; ftdm_isdn_data_t *isdn_data = ftdmchan->span->signal_data; ftdm_span_t *span = ftdm_channel_get_span(ftdmchan); ftdm_sigmsg_t sig; ftdm_status_t status; +#if 1 /* FIXME caller_data.raw_data does not exist anymore, see docs/variables.txt for more info */ + Q931mes_Generic empty_gen; + Q931mes_Generic *gen = &empty_gen; + + memset(&empty_gen, 0, sizeof(empty_gen)) ; +#else + Q931mes_Generic *gen = (Q931mes_Generic *) ftdmchan->caller_data.raw_data; +#endif + ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), @@ -1431,7 +1449,9 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan) gen->MesType = Q931mes_CONNECT; gen->BearerCap = 0; gen->CRVFlag = 1; /* inbound call */ +#if 0 /* FIXME */ Q931Rx43(&isdn_data->q931, gen, ftdmchan->caller_data.raw_data_len); +#endif } } break; diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index 2f9c70a48d..5f0ae716e1 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -94,8 +94,7 @@ static FIO_SPAN_GET_SIG_STATUS_FUNCTION(isdn_get_span_sig_status) static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) { ftdm_status_t status = FTDM_SUCCESS; - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); return status; } diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 540d7cb6e9..eb7df35ba0 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -471,12 +471,9 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call) return FTDM_FAIL; } - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING); - ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS); ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL); ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_RX_BUFFERS, NULL); - return FTDM_SUCCESS; } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index 48579ada85..976d43f3d6 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -1272,9 +1272,8 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(ftdm_sangoma_ss7_outgoing_call) switch (ftdmchan->state){ /**************************************************************************/ case FTDM_CHANNEL_STATE_DOWN: - /* inform the monitor thread that we want to make a call */ - ftdm_set_state_locked (ftdmchan, FTDM_CHANNEL_STATE_DIALING); - + /* inform the monitor thread that we want to make a call by returning FTDM_SUCCESS */ + /* unlock the channel */ ftdm_mutex_unlock (ftdmchan->mutex); From a3b18e5b7cf33bf9de36cb12c953a5b527549a9f Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 19 Feb 2011 16:47:19 +0100 Subject: [PATCH 006/154] [SDK] Import autotools version of fs-mod-sdk. This is a autotools (autoconf/automake/libtool) based skeleton framework for developing external (= out-of-tree) modules for FreeSWITCH. Variants of this framework have been used to develop modules like mod_openais, mod_ssh and others. NOTE: This version of the SDK relies on pkg-config to detect FreeSWITCH installations. Other SDKs for external modules should be added in: "src/mod/sdk//" e.g. a scons-based SDK should be put in: "src/mod/sdk/scons/" Signed-off-by: Stefan Knoblich Requested-by: Michal Bielicki - cypromis --- src/mod/sdk/autotools/.gitignore | 35 +++ src/mod/sdk/autotools/Makefile.am | 4 + src/mod/sdk/autotools/autogen.sh | 2 + src/mod/sdk/autotools/configure.ac | 252 ++++++++++++++++++ .../sdk/autotools/m4/ax_compiler_vendor.m4 | 15 ++ src/mod/sdk/autotools/src/Makefile.am | 66 +++++ src/mod/sdk/autotools/src/mod_example.c | 84 ++++++ 7 files changed, 458 insertions(+) create mode 100644 src/mod/sdk/autotools/.gitignore create mode 100644 src/mod/sdk/autotools/Makefile.am create mode 100755 src/mod/sdk/autotools/autogen.sh create mode 100644 src/mod/sdk/autotools/configure.ac create mode 100644 src/mod/sdk/autotools/m4/ax_compiler_vendor.m4 create mode 100644 src/mod/sdk/autotools/src/Makefile.am create mode 100644 src/mod/sdk/autotools/src/mod_example.c diff --git a/src/mod/sdk/autotools/.gitignore b/src/mod/sdk/autotools/.gitignore new file mode 100644 index 0000000000..57b51acef5 --- /dev/null +++ b/src/mod/sdk/autotools/.gitignore @@ -0,0 +1,35 @@ +*.o +*.lo +*.so +*.a +*.orig +*.rej +*.log +*.la + +.deps +.libs + +stamp-h1 +samples* +Makefile +Makefile.in +config.log +config.sub +config.guess +config.status +configure +libtool +aclocal.m4 +autom4te.cache +depcomp +install-sh +compile +missing +ltmain.sh +doxygen +m4/libtool.m4 +m4/ltsugar.m4 +m4/ltoptions.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 diff --git a/src/mod/sdk/autotools/Makefile.am b/src/mod/sdk/autotools/Makefile.am new file mode 100644 index 0000000000..8f8a25a048 --- /dev/null +++ b/src/mod/sdk/autotools/Makefile.am @@ -0,0 +1,4 @@ +ACLOCAL_AMFLAGS = -I m4 +AUTOMAKE_OPTIONS = foreign no-dist subdir-objects + +SUBDIRS = src diff --git a/src/mod/sdk/autotools/autogen.sh b/src/mod/sdk/autotools/autogen.sh new file mode 100755 index 0000000000..08240fdfcd --- /dev/null +++ b/src/mod/sdk/autotools/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf -i -f diff --git a/src/mod/sdk/autotools/configure.ac b/src/mod/sdk/autotools/configure.ac new file mode 100644 index 0000000000..d4f29f492b --- /dev/null +++ b/src/mod/sdk/autotools/configure.ac @@ -0,0 +1,252 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# website url +m4_define([AC_PACKAGE_URL], [http://www.example.com/]) + +AC_PREREQ([2.61]) +AC_INIT([mod_example], [0.0.0], [contact@example.com]) +AC_CONFIG_SRCDIR([src/mod_example.c]) +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([foreign no-dist subdir-objects]) +AC_DISABLE_STATIC + +# disable libtool fortran and c++ checks +m4_defun([_LT_AC_LANG_F77_CONFIG], [:]) +m4_defun([_LT_AC_LANG_CXX_CONFIG], [:]) + +# >=automake-1.11 +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_LIBTOOL + +# pkgconfig +AC_PATH_PROG([PKG_CONFIG], [pkg-config], ["no"]) +if test "x${PKG_CONFIG}" = "xno" +then + AC_MSG_ERROR([Cannot find pkg-config, make sure it is installed in your PATH]) +fi +PKG_PROG_PKG_CONFIG + +# Checks for cflags +AC_MSG_RESULT([${as_nl}<<>> Compiler vendor and features]) + +## +## Compiler vendor and flag checks +## +HAVE_VISIBILITY="no" +AC_ARG_ENABLE([visibility], + [AS_HELP_STRING([--disable-visibility], [Disable symbol visibility support (default: enabled, if available)])], + [case "${enableval}" in + yes) enable_visibility="yes" ;; + no) enable_visibility="no" ;; + *) AC_MSG_ERROR([Invalid value ${enableval} for parameter --disable-visibility]) ;; + esac], + [enable_visibility="yes"] +) + +AX_COMPILER_VENDOR + +case "${ax_cv_c_compiler_vendor}" in +gnu) + AC_MSG_CHECKING([whether the compiler supports -fvisibility=hidden]) + AS_IF([test "x${enable_visibility}" != "xno"], + [save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} -fvisibility=hidden" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [int foo __attribute__ ((visibility("default")));], + [;] + )], + + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_VISIBILITY],[1],[GCC visibility support]) + HAVE_VISIBILITY="yes"], + + [AC_MSG_RESULT([no]) + HAVE_VISIBILITY="no"] + ) + CFLAGS="${save_CFLAGS}"], + [AC_MSG_RESULT([disabled by user])] + ) + + AS_IF([test "x${HAVE_VISIBILITY}" != "xno"], + [save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} -fvisibility-inlines-hidden" + AC_MSG_CHECKING([whether the compiler supports -fvisibility-inlines-hidden]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [;], [;] + )], + + [AC_MSG_RESULT([yes]) + HAVE_VISIBILITY_INLINES_HIDDEN="yes"], + + [AC_MSG_RESULT([no]) + HAVE_VISIBILITY_INLINES_HIDDEN="no"] + ) + CFLAGS="${save_CFLAGS}"], + [:] + ) + AC_DEFINE([COMPILER_GCC], [1], [Compiler is GCC]) + ;; +sun) + AC_MSG_CHECKING([whether the compiler supports -xldscope=hidden]) + AS_IF([test "x${enable_visibility}" != "xno"], + [save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} -xldscope=hidden" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [int foo __attribute__ ((visibility("default")));], + [;] + )], + + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_VISIBILITY],[1],[SUNCC visibility support]) + HAVE_VISIBILITY="yes"], + + [AC_MSG_RESULT([no]) + HAVE_VISIBILITY="no"] + ) + CFLAGS="${save_CFLAGS}"], + [AC_MSG_RESULT([disabled by user])] + ) + AC_DEFINE([COMPILER_SUNCC], [1], [Compiler is SunCC]) + ;; +*) + AC_MSG_WARN([No visibility checks for this compiler defined]) + ;; +esac + +AM_CONDITIONAL([COMPILER_GCC], [test "x${ax_cv_c_compiler_vendor}" = "xgnu"]) +AM_CONDITIONAL([COMPILER_SUNCC], [test "x${ax_cv_c_compiler_vendor}" = "xsun"]) + +AM_CONDITIONAL([HAVE_VISIBILITY], [test "x${HAVE_VISIBILITY}" = "xyes"]) +AM_CONDITIONAL([HAVE_VISIBILITY_INLINES_HIDDEN], [test "x${HAVE_VISIBILITY_INLINES_HIDDEN}" = "xyes"]) + +## +## pkgconfig based freeswitch detection code +## +AC_MSG_RESULT([${as_nl}<<>> FreeSWITCH environment]) + +PKG_CHECK_MODULES([freeswitch], [freeswitch], + [save_LIBS="${LIBS}" + save_CFLAGS="${CFLAGS}" + save_CPPFLAGS="${CPPFLAGS}" + + AC_MSG_CHECKING([FreeSWITCH version]) + + FREESWITCH_VERSION="`${PKG_CONFIG} --modversion freeswitch 2>/dev/null`" + AS_IF([test "x${FREESWITCH_VERSION}" = "x"], + [AC_MSG_ERROR([failed to get FreeSWITCH version])], + [AC_MSG_RESULT([$FREESWITCH_VERSION])] + ) + + AC_MSG_CHECKING([whether FreeSWITCH ${FREESWITCH_VERSION} is usable]) + CFLAGS="${freeswitch_CFLAGS}" + CPPFLAGS="${freeswitch_CPPFLAGS}" + LIBS="${freeswitch_LIBS}" + AC_TRY_LINK([#include ], + [switch_core_init(0, 0, NULL);], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([libfreeswitch is unusable, please check config.log for details])] + ) + LIBS="${save_LIBS}" + CFLAGS="${save_CFLAGS}" + CPPFLAGS="${save_CPPFLAGS}" + + # get locations, critical first + AC_MSG_CHECKING([for installation prefix]) + FREESWITCH_PREFIX_DIR="`${PKG_CONFIG} --variable=prefix freeswitch 2>/dev/null`" + AS_IF( + [test "x${FREESWITCH_PREFIX_DIR}" = "x"], [AC_MSG_ERROR([unable to get FreeSWITCH prefix directory])], + [test ! -e "${FREESWITCH_PREFIX_DIR}"], [AC_MSG_ERROR([FreeSWITCH prefix directory ${FREESWITCH_PREFIX_DIR} does not exist])] + ) + AC_MSG_RESULT([${FREESWITCH_PREFIX_DIR}]) + + AC_MSG_CHECKING([for modules directory]) + FREESWITCH_MODULES_DIR="`${PKG_CONFIG} --variable=modulesdir freeswitch 2>/dev/null`" + AS_IF( + [test "x${FREESWITCH_MODULES_DIR}" = "x"], [AC_MSG_ERROR([unable to get FreeSWITCH modules directory])], + [test ! -e "${FREESWITCH_MODULES_DIR}"], [AC_MSG_ERROR([FreeSWITCH modules directory ${FREESWITCH_MODULES_DIR} does not exist])] + ) + AC_MSG_RESULT([${FREESWITCH_MODULES_DIR}]) + + AC_MSG_CHECKING([for configuration directory]) + FREESWITCH_CONFIG_DIR="`${PKG_CONFIG} --variable=sysconfdir freeswitch 2>/dev/null`" + AS_IF( + [test "x${FREESWITCH_CONFIG_DIR}" = "x"], [AC_MSG_ERROR([unable to get FreeSWITCH configuration directory])], + [test ! -e "${FREESWITCH_CONFIG_DIR}"], [AC_MSG_ERROR([FreeSWITCH configuration directory ${FREESWITCH_CONFIG_DIR} does not exist])] + ) + AC_MSG_RESULT([${FREESWITCH_CONFIG_DIR}]) + + # non-critical paths + FREESWITCH_HTDOCS_DIR="`${PKG_CONFIG} --variable=htdocsdir freeswitch 2>/dev/null`" + + FREESWITCH_RUNTIME_DIR="`${PKG_CONFIG} --variable=runtimedir freeswitch 2>/dev/null`" + FREESWITCH_SCRIPTS_DIR="`${PKG_CONFIG} --variable=scriptsdir freeswitch 2>/dev/null`" + + # cflags, libs + AC_SUBST([FREESWITCH_CFLAGS], [${freeswitch_CFLAGS}]) + AC_SUBST([FREESWITCH_CPPFLAGS], [${freeswitch_CPPFLAGS}]) + AC_SUBST([FREESWITCH_LIBS], [${freeswitch_LIBS}]) + + # version + AC_SUBST([FREESWITCH_VERSION]) + + # locations + AC_SUBST([FREESWITCH_PREFIX_DIR]) + AC_SUBST([FREESWITCH_HTDOCS_DIR]) + AC_SUBST([FREESWITCH_CONFIG_DIR]) + AC_SUBST([FREESWITCH_MODULES_DIR]) + AC_SUBST([FREESWITCH_SCRIPTS_DIR]) + AC_SUBST([FREESWITCH_RUNTIME_DIR]) + ], + [AC_MSG_ERROR([FreeSWITCH not found])] +) + +## +## Add your other dependency checks here +## +AC_MSG_RESULT([${as_nl}<<>> Other dependencies]) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + +AC_MSG_RESULT([${as_nl}<<>> Create output files]) +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT + +AC_MSG_RESULT([ +====================== Configuration Summary ===================== ++ Package + Name:...................... ${PACKAGE_NAME} + Version:................... ${PACKAGE_VERSION} + Bugreports:................ ${PACKAGE_BUGREPORT} + Website:................... ${PACKAGE_URL} + ++ Compiler + Vendor:.................... ${ax_cv_c_compiler_vendor} + Symbol visibility:......... ${HAVE_VISIBILITY} + ++ FreeSWITCH + Version:................... ${FREESWITCH_VERSION} + Prefix:.................... ${FREESWITCH_PREFIX_DIR} + Modules directory:......... ${FREESWITCH_MODULES_DIR} + Configuration directory:... ${FREESWITCH_CONFIG_DIR} + + Cflags/CPPflags/CXXflags:.. ${FREESWITCH_CFLAGS} ${FREESWITCH_CPPFLAGS} + LDflags/Libs:.............. ${FREESWITCH_LIBS} ${FREESWITCH_LDFLAGS} + ++ Other + N/A +================================================================== +]) diff --git a/src/mod/sdk/autotools/m4/ax_compiler_vendor.m4 b/src/mod/sdk/autotools/m4/ax_compiler_vendor.m4 new file mode 100644 index 0000000000..a24a58da0f --- /dev/null +++ b/src/mod/sdk/autotools/m4/ax_compiler_vendor.m4 @@ -0,0 +1,15 @@ +AC_DEFUN([AX_COMPILER_VENDOR], +[ +AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown + # note: don't check for gcc first since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ +#if !($vencpp) + thisisanerror; +#endif +])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) + done + ]) +]) diff --git a/src/mod/sdk/autotools/src/Makefile.am b/src/mod/sdk/autotools/src/Makefile.am new file mode 100644 index 0000000000..d2bad5bc80 --- /dev/null +++ b/src/mod/sdk/autotools/src/Makefile.am @@ -0,0 +1,66 @@ +moddir = @FREESWITCH_MODULES_DIR@ +sysconfdir = @FREESWITCH_CONFIG_DIR@ + +### +# Flags +# +AM_CFLAGS = +AM_CXXFLAGS= +AM_CPPFLAGS= -I. -I$(includedir) +AM_LDFLAGS = -L. -L$(libdir) -avoid-version -module -no-undefined -shared + +### +# GCC specific flags +# +if COMPILER_GCC +AM_CFLAGS += -Wall +# symbol visibility support +if HAVE_VISIBILITY +AM_CFLAGS += -fvisibility=hidden +AM_CXXFLAGS+= -fvisibility=hidden +AM_CPPFLAGS+= -DSWITCH_API_VISIBILITY=1 +endif +if HAVE_VISIBILITY_INLINES_HIDDEN +AM_CXXFLAGS += -fvisibility-inlines-hidden +endif +endif + +### +# SunCC specific flags +# +if COMPILER_SUNCC +AM_CFLAGS += +# symbol visibility support +if HAVE_VISIBILITY +AM_CFLAGS += -xldscope=hidden +AM_CXXFLAGS += -xldscope=hidden +AM_CPPFLAGS += -DSWITCH_API_VISIBILITY=1 +endif +endif + + +### +# add module(s) here, with suffix '.la' +# +mod_LTLIBRARIES = mod_example.la + +### +# mod_example +# +mod_example_la_SOURCES = mod_example.c +mod_example_la_CFLAGS = $(AM_CFLAGS) $(FREESWITCH_CFLAGS) +mod_example_la_CPPFLAGS= $(AM_CPPFLAGS) $(FREESWITCH_CPPFLAGS) +mod_example_la_LDFLAGS = $(AM_LDFLAGS) +mod_example_la_LIBADD = $(FREESWITCH_LIBS) + +# configuration file +#sysconf_DATA = example.conf.xml + +# +# install configuration +# +#install-sysconfDATA: +# $(INSTALL) -d $(DESTDIR)/$(sysconfdir) +# for x in $(sysconf_DATA); do \ +# test -e $(DESTDIR)$(sysconfdir)/$$x || $(INSTALL) -m644 $$x $(DESTDIR)$(sysconfdir)/$$x ; \ +# done diff --git a/src/mod/sdk/autotools/src/mod_example.c b/src/mod/sdk/autotools/src/mod_example.c new file mode 100644 index 0000000000..5e7755c752 --- /dev/null +++ b/src/mod/sdk/autotools/src/mod_example.c @@ -0,0 +1,84 @@ +/* + * 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. + * + * Contributor(s): + * + * Anthony Minessale II + * Neal Horman + * + * + * mod_example.c -- Framework Demo Module + * + */ +#include + +/* +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_example_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_example_runtime); +*/ + +SWITCH_MODULE_LOAD_FUNCTION(mod_example_load); +SWITCH_MODULE_DEFINITION(mod_example, mod_example_load, NULL, NULL); + +SWITCH_MODULE_LOAD_FUNCTION(mod_example_load) +{ + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Hello World!\n"); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* + Called when the system shuts down +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_example_shutdown); +{ + return SWITCH_STATUS_SUCCESS; +} +*/ + +/* + If it exists, this is called in it's own thread when the module-load completes + If it returns anything but SWITCH_STATUS_TERM it will be called again automaticly +SWITCH_MODULE_RUNTIME_FUNCTION(mod_example_runtime); +{ + while(looping) + { + switch_yield(1000); + } + return SWITCH_STATUS_TERM; +} +*/ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ From f20ba4d1f6f145118362d3c5246f27d647fbb6d2 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 19 Feb 2011 23:07:22 +0100 Subject: [PATCH 007/154] Add --with-pkglibdir option to set the installation directory of freeswitch.pc. Default behaviour is unchanged. Packagers should use this option to install freeswitch.pc into the system's main pkg-config directory (e.g. /usr/lib/pkgconfig). Signed-off-by: Stefan Knoblich Tested-by: Stefan Knoblich --- Makefile.am | 2 +- configure.in | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 37a01d27fb..5b6b9896ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -356,7 +356,7 @@ scripts/fsxs: scripts/fsxs.in ## misc ## -pkgconfigdir = $(libdir)/pkgconfig +pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = build/freeswitch.pc $(switch_builddir)/modules.conf: diff --git a/configure.in b/configure.in index a0c9418751..0e45e064f1 100644 --- a/configure.in +++ b/configure.in @@ -93,6 +93,17 @@ AC_SUBST(libdir) AC_SUBST(bindir) AC_SUBST(includedir) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir=DIR], [Installation directory for pkgconfig file (default: \${libdir}/pkgconfig)])], + [case "${withval}" in + yes|no) AC_MSG_ERROR([Invalid value ${withval} for option --with-pkgconfigdir]) ;; + *) pkgconfigdir="${withval}" ;; + esac + ], + [pkgconfigdir="${libdir}/pkgconfig"] +) +AC_SUBST([pkgconfigdir]) + #Set default language AC_LANG_C # Checks for programs. From c8065499adc3bfdd97d3c51e44465865f9417efb Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 19 Feb 2011 23:35:41 +0100 Subject: [PATCH 008/154] [FreeTDM] Add --with-pkgconfigdir option to FreeTDM. Default behaviour is unchanged. Packagers should use this option to install freetdm.pc into the system's main pkg-config directory (e.g. /usr/lib/pkgconfig). Signed-off-by: Stefan Knoblich Tested-by: Stefan Knoblich --- libs/freetdm/Makefile.am | 2 +- libs/freetdm/configure.ac | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index b3353f32de..63e04b6d0b 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -62,7 +62,7 @@ LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(FTDM_CFLAGS) $(LDFLAGS) -o $ # EXTRA_DIST = freetdm.pc.in -pkgconfigdir = $(libdir)/pkgconfig +pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = freetdm.pc diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index 1f4fc9c6ac..29661c1cdf 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -65,6 +65,18 @@ AC_ARG_WITH([modinstdir], ) AC_SUBST([modinstdir]) +# freetdm.pc pkgconfig file +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir=DIR], [Installation directory for pkgconfig file (default: ${libdir}/pkgconfig)])], + [case "${withval}" in + yes|no) AC_MSG_ERROR([Invalid value ${withval} for option --with-pkgconfigdir]) ;; + *) pkgconfigdir="${withval}" ;; + esac + ], + [pkgconfigdir="${libdir}/pkgconfig"] +) +AC_SUBST([pkgconfigdir]) + AC_ARG_ENABLE([enable_64], [AS_HELP_STRING([--enable-64], [Enable 64bit compilation])], [enable_64="${enableval}"], From 78472897cdf1432fd3ceb9814ebee6b8376fd6e7 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 19 Feb 2011 23:38:04 +0100 Subject: [PATCH 009/154] Do not escape ${libdir} in --with-pkgconfigdir option's AS_HELP_STRING description parameter (not needed and ends up in output). Signed-off-by: Stefan Knoblich --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 0e45e064f1..7978d04754 100644 --- a/configure.in +++ b/configure.in @@ -94,7 +94,7 @@ AC_SUBST(bindir) AC_SUBST(includedir) AC_ARG_WITH([pkgconfigdir], - [AS_HELP_STRING([--with-pkgconfigdir=DIR], [Installation directory for pkgconfig file (default: \${libdir}/pkgconfig)])], + [AS_HELP_STRING([--with-pkgconfigdir=DIR], [Installation directory for pkgconfig file (default: ${libdir}/pkgconfig)])], [case "${withval}" in yes|no) AC_MSG_ERROR([Invalid value ${withval} for option --with-pkgconfigdir]) ;; *) pkgconfigdir="${withval}" ;; From 180e3d4ab1943361610ca241d150565aad8d16fd Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 20 Feb 2011 14:37:23 -0600 Subject: [PATCH 010/154] agc tweaks --- src/include/switch_resample.h | 9 ++ .../mod_conference/mod_conference.c | 84 +++++++++++++------ src/switch_resample.c | 37 ++++++++ 3 files changed, 104 insertions(+), 26 deletions(-) diff --git a/src/include/switch_resample.h b/src/include/switch_resample.h index b38e094dd6..3dff620c4b 100644 --- a/src/include/switch_resample.h +++ b/src/include/switch_resample.h @@ -39,6 +39,7 @@ */ #define switch_normalize_volume(x) if (x > 4) x = 4; if (x < -4) x = -4; +#define switch_normalize_volume_granular(x) if (x > 12) x = 12; if (x < -12) x = -12; #ifndef SWITCH_RESAMPLE_H #define SWITCH_RESAMPLE_H @@ -158,6 +159,14 @@ SWITCH_DECLARE(void) switch_generate_sln_silence(int16_t *data, uint32_t samples \param vol the volume factor -4 -> 4 */ SWITCH_DECLARE(void) switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vol); + +/*! + \brief Change the volume of a signed linear audio frame with more granularity + \param data the audio data + \param samples the number of 2 byte samples + \param vol the volume factor -12 -> 12 + */ +SWITCH_DECLARE(void) switch_change_sln_volume_granular(int16_t *data, uint32_t samples, int32_t vol); ///\} SWITCH_DECLARE(uint32_t) switch_merge_sln(int16_t *data, uint32_t samples, int16_t *other_data, uint32_t other_samples); diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 5eeae5c781..8a3f53e17a 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -318,6 +318,7 @@ struct conference_member { switch_mutex_t *audio_in_mutex; switch_mutex_t *audio_out_mutex; switch_mutex_t *read_mutex; + switch_codec_implementation_t read_impl; switch_codec_implementation_t orig_read_impl; switch_codec_t read_codec; switch_codec_t write_codec; @@ -1942,14 +1943,37 @@ static void conference_loop_fn_hangup(conference_member_t *member, caller_contro switch_clear_flag_locked(member, MFLAG_RUNNING); } +static void check_agc_levels(conference_member_t *member) +{ + if (!member->avg_score) return; + + if (member->avg_score < member->conference->agc_level - 200) { + member->agc_volume_in_level++; + switch_normalize_volume_granular(member->agc_volume_in_level); + } else if (member->avg_score > member->conference->agc_level + 200) { + member->agc_volume_in_level--; + switch_normalize_volume_granular(member->agc_volume_in_level); + } + //} else { + //member->vol_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 5; + //} +} + static void clear_avg(conference_member_t *member) { - member->agc_volume_in_level = 0; + if (member->agc_volume_in_level < -5) { + member->agc_volume_in_level = 0; + } + + if (member->conference->agc_level) { + check_agc_levels(member); + } + member->avg_score = 0; member->avg_itt = 0; member->avg_tally = 0; - member->nt_tally = 0; + member->agc_concur = 0; } @@ -1962,7 +1986,6 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_status_t status; switch_frame_t *read_frame = NULL; uint32_t hangover = 40, hangunder = 5, hangover_hits = 0, hangunder_hits = 0, energy_level = 0, diff_level = 400; - switch_codec_implementation_t read_impl = { 0 }; switch_core_session_t *session = member->session; int check_floor_change; @@ -1972,7 +1995,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v channel = switch_core_session_get_channel(session); - switch_core_session_get_read_impl(session, &read_impl); + switch_core_session_get_read_impl(session, &member->read_impl); /* As long as we have a valid read, feed that data into an input buffer where the conference thread will take it and mux it with any audio from other channels. */ @@ -1997,6 +2020,10 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v } if (switch_test_flag(read_frame, SFF_CNG)) { + if (member->conference->agc_level) { + member->nt_tally++; + } + if (hangunder_hits) { hangunder_hits--; } @@ -2017,6 +2044,11 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v goto do_continue; } + + if (member->nt_tally > (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 3) { + member->agc_volume_in_level = 0; + clear_avg(member); + } /* Check for input volume adjustments */ if (!member->conference->agc_level) { @@ -2031,30 +2063,29 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v uint32_t energy = 0, i = 0, samples = 0, j = 0; int16_t *data; int divisor = 0; - int agc_period = (read_impl.actual_samples_per_second / read_impl.samples_per_packet) / 2; - int combined_vol = 0; + int agc_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) / 4; + data = read_frame->data; - if (!(divisor = read_impl.actual_samples_per_second / 8000)) { + if (!(divisor = member->read_impl.actual_samples_per_second / 8000)) { divisor = 1; } member->score = 0; - combined_vol = member->agc_volume_in_level; - if (member->conference->agc_level) { - combined_vol += member->agc_volume_in_level; + if (member->volume_in_level) { + switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level); } - if (combined_vol) { - switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, combined_vol); + if (member->agc_volume_in_level) { + switch_change_sln_volume_granular(read_frame->data, read_frame->datalen / 2, member->agc_volume_in_level); } if ((samples = read_frame->datalen / sizeof(*data))) { for (i = 0; i < samples; i++) { energy += abs(data[j]); - j += read_impl.number_of_channels; + j += member->read_impl.number_of_channels; } member->score = energy / (samples / divisor); } @@ -2082,22 +2113,14 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v - if (++member->nt_tally >= agc_period) { + if (++member->agc_concur >= agc_period) { if (!member->vol_period) { - if (member->avg_score < member->conference->agc_level) { - member->agc_volume_in_level++; - switch_normalize_volume(member->agc_volume_in_level); - member->vol_period = (read_impl.actual_samples_per_second / read_impl.samples_per_packet) * 2; - } - - if (member->avg_score > member->conference->agc_level) { - member->agc_volume_in_level--; - switch_normalize_volume(member->agc_volume_in_level); - member->vol_period = (read_impl.actual_samples_per_second / read_impl.samples_per_packet) * 2; - } + check_agc_levels(member); } - member->nt_tally = 0; + member->agc_concur = 0; } + } else { + member->nt_tally++; } member->score_iir = (int) (((1.0 - SCORE_DECAY) * (float) member->score) + (SCORE_DECAY * (float) member->score_iir)); @@ -2112,6 +2135,10 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v hangover_hits--; } + if (member->conference->agc_level) { + member->nt_tally = 0; + } + if (diff >= diff_level || ++hangunder_hits >= hangunder) { check_floor_change = 1; @@ -2147,6 +2174,11 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v if (hangunder_hits) { hangunder_hits--; } + + if (member->conference->agc_level) { + member->nt_tally++; + } + if (switch_test_flag(member, MFLAG_TALKING) && switch_test_flag(member, MFLAG_CAN_SPEAK)) { switch_event_t *event; if (++hangover_hits >= hangover) { diff --git a/src/switch_resample.c b/src/switch_resample.c index 10c633c01a..64656b6c61 100644 --- a/src/switch_resample.c +++ b/src/switch_resample.c @@ -269,6 +269,43 @@ SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, u } +SWITCH_DECLARE(void) switch_change_sln_volume_granular(int16_t *data, uint32_t samples, int32_t vol) +{ + double newrate = 0; + double pos[12] = {1.25, 1.50, 1.75, 2.0, 2.25, 2.50, 2.75, 3.0, 3.25, 3.50, 3.75, 4.0}; + double neg[12] = {.917, .834, .751, .668, .585, .502, .419, .336, .253, .017, .087, .004}; + double *chart; + uint32_t i; + + if (vol == 0) return; + + switch_normalize_volume_granular(vol); + + if (vol > 0) { + chart = pos; + } else { + chart = neg; + } + + i = abs(vol) - 1; + + switch_assert(i < 12); + + newrate = chart[i]; + + if (newrate) { + int32_t tmp; + uint32_t x; + int16_t *fp = data; + + for (x = 0; x < samples; x++) { + tmp = (int32_t) fp[x] * newrate; + switch_normalize_to_16bit(tmp); + fp[x] = (int16_t) tmp; + } + } +} + SWITCH_DECLARE(void) switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vol) { double newrate = 0; From 3cd120a02e28225cc89f6898a317fd4f93b0c138 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Sun, 20 Feb 2011 14:50:20 -0600 Subject: [PATCH 011/154] fix warning --- src/switch_resample.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_resample.c b/src/switch_resample.c index 64656b6c61..949b351785 100644 --- a/src/switch_resample.c +++ b/src/switch_resample.c @@ -299,7 +299,7 @@ SWITCH_DECLARE(void) switch_change_sln_volume_granular(int16_t *data, uint32_t s int16_t *fp = data; for (x = 0; x < samples; x++) { - tmp = (int32_t) fp[x] * newrate; + tmp = (int32_t) (fp[x] * newrate); switch_normalize_to_16bit(tmp); fp[x] = (int16_t) tmp; } From 06988e1a36d0f6f8b35651d36fdab79ac8c63c5c Mon Sep 17 00:00:00 2001 From: Brian West Date: Sun, 20 Feb 2011 15:10:02 -0600 Subject: [PATCH 012/154] FS-3084 --- src/mod/endpoints/mod_sofia/sofia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 8a3d1b10f6..90193ac63a 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -2135,7 +2135,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) register_transport = (char *) sofia_glue_transport2str(gateway->register_transport); - if (contact_params) { + if (! zstr(contact_params)) { if (*contact_params == ';') { params = switch_core_sprintf(gateway->pool, "%s;transport=%s;gw=%s", contact_params, register_transport, gateway->name); } else { From 4f5ca9e88dc1a94efa78878ca576d62345203ca2 Mon Sep 17 00:00:00 2001 From: Christopher Rienzo Date: Mon, 21 Feb 2011 15:00:35 +0000 Subject: [PATCH 013/154] FS-3077 prevent crash on double call to asr_close --- src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c index d5bac5f7f4..8698e4a02c 100644 --- a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c +++ b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c @@ -3155,15 +3155,19 @@ static switch_status_t recog_asr_disable_all_grammars(switch_asr_handle_t *ah) static switch_status_t recog_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags) { speech_channel_t *schannel = (speech_channel_t *) ah->private_info; - recognizer_data_t *r = (recognizer_data_t *) schannel->data; - speech_channel_stop(schannel); - speech_channel_destroy(schannel); - switch_core_hash_destroy(&r->grammars); - switch_core_hash_destroy(&r->enabled_grammars); - if (r->dtmf_generator) { - mpf_dtmf_generator_destroy(r->dtmf_generator); - } + recognizer_data_t *r = NULL; + /* close if not already closed */ + if (schannel != NULL && !switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + r = (recognizer_data_t *) schannel->data; + speech_channel_stop(schannel); + speech_channel_destroy(schannel); + switch_core_hash_destroy(&r->grammars); + switch_core_hash_destroy(&r->enabled_grammars); + if (r->dtmf_generator) { + mpf_dtmf_generator_destroy(r->dtmf_generator); + } + } /* this lets FreeSWITCH's speech_thread know the handle is closed */ switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED); From 38b430f43f52efda748ceabb47341fca284103b0 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 10:51:26 -0600 Subject: [PATCH 014/154] FS-3057 --- src/switch_rtp.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index d0c22ce585..bbb0468775 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -2396,7 +2396,7 @@ static void do_flush(switch_rtp_t *rtp_session) #define return_cng_frame() do_cng = 1; goto timer_check -static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes, switch_frame_flag_t *flags) +static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes, switch_frame_flag_t *flags, switch_bool_t return_jb_packet) { switch_status_t status = SWITCH_STATUS_FALSE; stfu_frame_t *jb_frame; @@ -2493,8 +2493,11 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t stfu_n_eat(rtp_session->jb, rtp_session->last_read_ts, rtp_session->recv_msg.header.pt, rtp_session->recv_msg.body, *bytes - rtp_header_len, rtp_session->timer.samplecount); - *bytes = 0; status = SWITCH_STATUS_FALSE; + if (!return_jb_packet) { + return status; + } + *bytes = 0; } if (rtp_session->jb && !rtp_session->pause_jb && !rtp_session->checked_jb) { @@ -2668,17 +2671,19 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if ((switch_test_flag(rtp_session, SWITCH_RTP_FLAG_AUTOFLUSH) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_STICKY_FLUSH)) && rtp_session->read_pollfd) { if (switch_poll(rtp_session->read_pollfd, 1, &fdr, 0) == SWITCH_STATUS_SUCCESS) { - status = read_rtp_packet(rtp_session, &bytes, flags); - /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initial %d\n", bytes); */ - read_pretriggered = 1; + status = read_rtp_packet(rtp_session, &bytes, flags, SWITCH_FALSE); + /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initial (%i) %d\n", status, bytes); */ + if (status != SWITCH_STATUS_FALSE) { + read_pretriggered = 1; + } - if (switch_poll(rtp_session->read_pollfd, 1, &fdr, 0) == SWITCH_STATUS_SUCCESS) { - /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Trigger %d\n", rtp_session->hot_hits); */ - rtp_session->hot_hits += rtp_session->samples_per_interval; - } else { - rtp_session->hot_hits = 0; - switch_core_timer_sync(&rtp_session->timer); - goto recvfrom; + if (bytes) { + if (switch_poll(rtp_session->read_pollfd, 1, &fdr, 0) == SWITCH_STATUS_SUCCESS) { + /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Trigger %d\n", rtp_session->hot_hits); */ + rtp_session->hot_hits += rtp_session->samples_per_interval; + } else { + rtp_session->hot_hits = 0; + } } if (rtp_session->hot_hits >= rtp_session->samples_per_second * 5) { @@ -2737,9 +2742,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ } if (poll_status == SWITCH_STATUS_SUCCESS) { - if (!read_pretriggered) { - status = read_rtp_packet(rtp_session, &bytes, flags); - /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Read bytes %d\n", bytes); */ + if (read_pretriggered) { + read_pretriggered = 0; + } else { + status = read_rtp_packet(rtp_session, &bytes, flags, SWITCH_TRUE); + /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Read bytes (%i) %d\n", status, bytes); */ } } else { if (!SWITCH_STATUS_IS_BREAK(poll_status) && poll_status != SWITCH_STATUS_TIMEOUT) { From 4e60f14a4d0e942aef7973546dbe040ef330640f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 11:02:42 -0600 Subject: [PATCH 015/154] FS-3072 --- src/mod/endpoints/mod_sofia/mod_sofia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 4853dff3bf..7125bceb7c 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -3513,7 +3513,7 @@ SWITCH_STANDARD_API(sofia_contact_function) profile = sofia_glue_find_profile(profile_name); } - if (!profile) { + if (!profile && !zstr(domain)) { profile = sofia_glue_find_profile(domain); } } From 4a4ac354268cd7699bc2f39e8e728acbf3855703 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 13:12:51 -0600 Subject: [PATCH 016/154] another round of agc changes --- .../mod_conference/mod_conference.c | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 8a3f53e17a..92ca1042a0 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -266,7 +266,6 @@ typedef struct conference_obj { switch_thread_rwlock_t *rwlock; uint32_t count; int32_t energy_level; - int32_t agc_energy_level; uint8_t min; switch_speech_handle_t lsh; switch_speech_handle_t *sh; @@ -1943,6 +1942,28 @@ static void conference_loop_fn_hangup(conference_member_t *member, caller_contro switch_clear_flag_locked(member, MFLAG_RUNNING); } + +static int noise_gate_check(conference_member_t *member) +{ + int r = 0; + + + if (member->conference->agc_level && member->agc_volume_in_level != 0) { + int target_score = 0; + + target_score = (member->energy_level + (25 * member->agc_volume_in_level)); + + if (target_score < 0) target_score = 0; + + r = member->score > target_score; + + } else { + r = member->score > member->energy_level; + } + + return r; +} + static void check_agc_levels(conference_member_t *member) { if (!member->avg_score) return; @@ -1954,6 +1975,9 @@ static void check_agc_levels(conference_member_t *member) member->agc_volume_in_level--; switch_normalize_volume_granular(member->agc_volume_in_level); } + + + //} else { //member->vol_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 5; //} @@ -1962,9 +1986,9 @@ static void check_agc_levels(conference_member_t *member) static void clear_avg(conference_member_t *member) { - if (member->agc_volume_in_level < -5) { - member->agc_volume_in_level = 0; - } + //if (member->agc_volume_in_level < -5) { + //member->agc_volume_in_level = 0; + //} if (member->conference->agc_level) { check_agc_levels(member); @@ -1985,7 +2009,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_channel_t *channel; switch_status_t status; switch_frame_t *read_frame = NULL; - uint32_t hangover = 40, hangunder = 5, hangover_hits = 0, hangunder_hits = 0, energy_level = 0, diff_level = 400; + uint32_t hangover = 40, hangunder = 5, hangover_hits = 0, hangunder_hits = 0, diff_level = 400; switch_core_session_t *session = member->session; int check_floor_change; @@ -2049,13 +2073,17 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v member->agc_volume_in_level = 0; clear_avg(member); } + + if (member->avg_itt > (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 3) { + clear_avg(member); + } + /* Check for input volume adjustments */ if (!member->conference->agc_level) { clear_avg(member); } - energy_level = member->energy_level; /* if the member can speak, compute the audio energy level and */ /* generate events when the level crosses the threshold */ @@ -2096,9 +2124,9 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v if (member->conference->agc_level && member->score && switch_test_flag(member, MFLAG_CAN_SPEAK) && - member->score > member->conference->agc_energy_level + noise_gate_check(member) ) { - + member->avg_tally += member->score; member->avg_itt++; if (!member->avg_itt) member->avg_itt++; @@ -2129,8 +2157,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v member->score_iir = SCORE_MAX_IIR; } - if (member->score > energy_level) { - uint32_t diff = member->score - energy_level; + if (noise_gate_check(member)) { + uint32_t diff = member->score - member->energy_level; if (hangover_hits) { hangover_hits--; } @@ -2198,7 +2226,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v } /* skip frames that are not actual media or when we are muted or silent */ - if ((switch_test_flag(member, MFLAG_TALKING) || energy_level == 0) && switch_test_flag(member, MFLAG_CAN_SPEAK) && + if ((switch_test_flag(member, MFLAG_TALKING) || member->energy_level == 0) && switch_test_flag(member, MFLAG_CAN_SPEAK) && !switch_test_flag(member->conference, CFLAG_WAIT_MOD)) { switch_audio_resampler_t *read_resampler = member->read_resampler; void *data; @@ -3444,7 +3472,7 @@ static switch_status_t conf_api_sub_mute(conference_member_t *member, switch_str static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) { - int level, energy_level; + int level; int on = 0; if (argc == 2) { @@ -3462,13 +3490,7 @@ static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_str if (argc > 3) { level = atoi(argv[3]); } else { - level = 650; - } - - if (argc > 4) { - energy_level = atoi(argv[4]); - } else { - energy_level = 100; + level = 1200; } if (level > conference->energy_level) { @@ -3476,10 +3498,9 @@ static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_str conference->avg_itt = 0; conference->avg_tally = 0; conference->agc_level = level; - conference->agc_energy_level = energy_level; if (stream) { - stream->write_function(stream, "OK AGC ENABLED %d %d\n", conference->agc_level, conference->agc_energy_level); + stream->write_function(stream, "OK AGC ENABLED %d\n", conference->agc_level); } } else { @@ -3853,7 +3874,7 @@ static void conference_xlist(conference_obj_t *conference, switch_xml_t x_confer if (conference->agc_level) { char tmp[30] = ""; - switch_snprintf(tmp, sizeof(tmp), "%d:%d", conference->agc_level, conference->agc_energy_level); + switch_snprintf(tmp, sizeof(tmp), "%d", conference->agc_level); switch_xml_set_attr_d_buf(x_conference, "agc", tmp); } @@ -6421,30 +6442,16 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c if (!zstr(auto_gain_level)) { int level = 0; - int energy_level = 100; if (switch_true(auto_gain_level)) { - level = 650; + level = 1200; } else { - char *p; - int tmp = 0; - level = atoi(auto_gain_level); - if ((p = strchr(auto_gain_level, ':'))) { - p++; - if (p) tmp = atoi(p); - if (tmp > 0) { - energy_level = tmp; - } - } } if (level > 0 && level > conference->energy_level) { conference->agc_level = level; } - - conference->agc_energy_level = energy_level; - } if (!zstr(maxmember_sound)) { From 1ba1c57eff854f13e34e8f03a012f5442cb69b36 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 13:28:41 -0600 Subject: [PATCH 017/154] move jb debug to level 8 --- src/switch_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index bbb0468775..f06771880c 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -1830,7 +1830,7 @@ static void jb_callback(stfu_instance_t *i, void *udata) stfu_n_report(i, &r); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG8, "%s JB REPORT:\nlen: %u\nin: %u\nclean: %u\ngood: %u\nbad: %u\n", switch_core_session_get_name(session), r.qlen, From a79b64ee2016ba90af72821990d8ae678c96623d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 14:35:13 -0600 Subject: [PATCH 018/154] more agc --- .../mod_conference/mod_conference.c | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 92ca1042a0..1fc4ed58ba 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -311,6 +311,7 @@ struct conference_member { switch_buffer_t *resample_buffer; uint32_t flags; uint32_t score; + uint32_t last_score; uint32_t score_iir; switch_mutex_t *flag_mutex; switch_mutex_t *write_mutex; @@ -2126,20 +2127,26 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_test_flag(member, MFLAG_CAN_SPEAK) && noise_gate_check(member) ) { + int last_shift = abs(member->last_score - member->score); - member->avg_tally += member->score; - member->avg_itt++; - if (!member->avg_itt) member->avg_itt++; - member->avg_score = member->avg_tally / member->avg_itt; - + if (member->score && member->last_score && last_shift > 900) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, + "AGC %s:%d drop anomalous shift of %d\n", + member->conference->name, + member->id, last_shift); + + } else { + member->avg_tally += member->score; + member->avg_itt++; + if (!member->avg_itt) member->avg_itt++; + member->avg_score = member->avg_tally / member->avg_itt; + } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, - "conf %s AGC %d %d %d %d %d %d\n", + "AGC %s:%d diff:%d level:%d cur:%d avg:%d vol:%d\n", member->conference->name, member->id, member->conference->agc_level - member->avg_score, member->conference->agc_level, member->score, member->avg_score, member->agc_volume_in_level); - - if (++member->agc_concur >= agc_period) { if (!member->vol_period) { @@ -2223,6 +2230,9 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v } } } + + + member->last_score = member->score; } /* skip frames that are not actual media or when we are muted or silent */ @@ -3490,7 +3500,7 @@ static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_str if (argc > 3) { level = atoi(argv[3]); } else { - level = 1200; + level = 1400; } if (level > conference->energy_level) { @@ -6444,7 +6454,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c int level = 0; if (switch_true(auto_gain_level)) { - level = 1200; + level = 1400; } else { level = atoi(auto_gain_level); } From e431481d4d45ca13c4d2f929f4ef0f23521fe922 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 17:16:34 -0600 Subject: [PATCH 019/154] agc --- .../mod_conference/mod_conference.c | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 1fc4ed58ba..53b3bfcaca 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -36,7 +36,8 @@ * */ #include -//#define INTENSE_DEBUG +#define DEFAULT_AGC_LEVEL 1000 + SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown); SWITCH_MODULE_DEFINITION(mod_conference, mod_conference_load, mod_conference_shutdown, NULL); @@ -1965,42 +1966,44 @@ static int noise_gate_check(conference_member_t *member) return r; } -static void check_agc_levels(conference_member_t *member) -{ - if (!member->avg_score) return; - - if (member->avg_score < member->conference->agc_level - 200) { - member->agc_volume_in_level++; - switch_normalize_volume_granular(member->agc_volume_in_level); - } else if (member->avg_score > member->conference->agc_level + 200) { - member->agc_volume_in_level--; - switch_normalize_volume_granular(member->agc_volume_in_level); - } - - - - //} else { - //member->vol_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 5; - //} -} - static void clear_avg(conference_member_t *member) { - //if (member->agc_volume_in_level < -5) { - //member->agc_volume_in_level = 0; - //} - - if (member->conference->agc_level) { - check_agc_levels(member); - } - member->avg_score = 0; member->avg_itt = 0; member->avg_tally = 0; member->agc_concur = 0; } +static void check_agc_levels(conference_member_t *member) +{ + int x = 0; + + if (!member->avg_score) return; + + if (member->avg_score < member->conference->agc_level - 100) { + member->agc_volume_in_level++; + switch_normalize_volume_granular(member->agc_volume_in_level); + x = 1; + } else if (member->avg_score > member->conference->agc_level + 100) { + member->agc_volume_in_level--; + switch_normalize_volume_granular(member->agc_volume_in_level); + x = -1; + } + + if (x) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, + "AGC %s:%d diff:%d level:%d cur:%d avg:%d vol:%d %s\n", + member->conference->name, + member->id, member->conference->agc_level - member->avg_score, member->conference->agc_level, + member->score, member->avg_score, member->agc_volume_in_level, x > 0 ? "+++" : "---"); + + clear_avg(member); + } +} + + + /* marshall frames from the call leg to the conference thread for muxing to other call legs */ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, void *obj) @@ -2056,6 +2059,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v if (++hangover_hits >= hangover) { hangover_hits = hangunder_hits = 0; switch_clear_flag_locked(member, MFLAG_TALKING); + check_agc_levels(member); clear_avg(member); if (test_eflag(member->conference, EFLAG_STOP_TALKING) && @@ -2075,13 +2079,9 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v clear_avg(member); } - if (member->avg_itt > (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 3) { - clear_avg(member); - } - - /* Check for input volume adjustments */ if (!member->conference->agc_level) { + member->conference->agc_level = 0; clear_avg(member); } @@ -2091,16 +2091,10 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v if ((switch_test_flag(member, MFLAG_CAN_SPEAK) || switch_test_flag(member, MFLAG_MUTE_DETECT))) { uint32_t energy = 0, i = 0, samples = 0, j = 0; int16_t *data; - int divisor = 0; int agc_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) / 4; data = read_frame->data; - - if (!(divisor = member->read_impl.actual_samples_per_second / 8000)) { - divisor = 1; - } - member->score = 0; if (member->volume_in_level) { @@ -2116,7 +2110,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v energy += abs(data[j]); j += member->read_impl.number_of_channels; } - member->score = energy / (samples / divisor); + + member->score = energy / samples; } if (member->vol_period) { @@ -2219,8 +2214,9 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v if (++hangover_hits >= hangover) { hangover_hits = hangunder_hits = 0; switch_clear_flag_locked(member, MFLAG_TALKING); + check_agc_levels(member); clear_avg(member); - + if (test_eflag(member->conference, EFLAG_STOP_TALKING) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_add_event_member_data(member, event); @@ -3500,7 +3496,7 @@ static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_str if (argc > 3) { level = atoi(argv[3]); } else { - level = 1400; + level = DEFAULT_AGC_LEVEL; } if (level > conference->energy_level) { @@ -6454,7 +6450,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c int level = 0; if (switch_true(auto_gain_level)) { - level = 1400; + level = DEFAULT_AGC_LEVEL; } else { level = atoi(auto_gain_level); } From f6dd557e5db9dacede48d26c98aee639378612f9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 17:24:35 -0600 Subject: [PATCH 020/154] agc --- src/mod/applications/mod_conference/mod_conference.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 53b3bfcaca..71ed1ed4b7 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -36,7 +36,7 @@ * */ #include -#define DEFAULT_AGC_LEVEL 1000 +#define DEFAULT_AGC_LEVEL 1100 SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown); From b61fc39622ce76067852f142dcc5ec53a56c1c30 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 20:01:01 -0600 Subject: [PATCH 021/154] add support for bz2 to getlibs --- build/getlib.sh.in | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/build/getlib.sh.in b/build/getlib.sh.in index ebad7b5084..a42fe3a58f 100755 --- a/build/getlib.sh.in +++ b/build/getlib.sh.in @@ -1,5 +1,8 @@ #!/bin/sh +bz="false" + +BUNZUP=/usr/bin/bunzip2 TAR=@TAR@ ZCAT=@ZCAT@ WGET=@WGET@ @@ -17,14 +20,26 @@ base=http://files.freeswitch.org/downloads/libs/ tarfile=$1 url=`echo $tarfile | grep "://"` +if [ `echo $tarfile | grep bz2` ] ; then + bz="true" + UNZIPPER=$BUNZIP +else + UNZIPPER=$ZCAT +fi + if [ ! -z $url ] ; then base=$tarfile/ tarfile=$2 fi if [ ! -d $tarfile ] ; then - uncompressed=`echo $tarfile | sed "s/\.tar\.gz//g"` - uncompressed=`echo $uncompressed | sed "s/\.tgz//g"` + + if [ $bz = "true" ] ; then + uncompressed=`echo $tarfile | sed "s/\.tar\.bz2//g"` + else + uncompressed=`echo $tarfile | sed "s/\.tar\.gz//g"` + uncompressed=`echo $uncompressed | sed "s/\.tgz//g"` + fi if [ ! -f $tarfile ] ; then rm -fr $uncompressed @@ -35,7 +50,7 @@ if [ ! -d $tarfile ] ; then fi fi if [ ! -d $uncompressed ] ; then - $ZCAT -c -d $tarfile | $TAR xf - + $UNZIPPER -c -d $tarfile | $TAR xf - fi fi From 079f3f73ed63733b5642ae3b49ec5cf66db2d6dc Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 20:01:46 -0600 Subject: [PATCH 022/154] bump mod_shout to use mpg123-1.13.2 to hopefully address unwanted calls to exit() and inherit other upstream fixes --- src/mod/formats/mod_shout/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/formats/mod_shout/Makefile b/src/mod/formats/mod_shout/Makefile index 0ff6fc6525..7388bc0c67 100644 --- a/src/mod/formats/mod_shout/Makefile +++ b/src/mod/formats/mod_shout/Makefile @@ -1,6 +1,6 @@ LAME=lame-3.97 SHOUT=libshout-2.2.2 -MPG123=mpg123 +MPG123=mpg123-1.13.2 BASE=../../../.. WANT_CURL=yes From e7acd4d138e0faa6b4a3cec1667015c430e8f3f6 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 21 Feb 2011 20:17:58 -0600 Subject: [PATCH 023/154] FS-3054 re-open if this does not fix it. --- .../endpoints/mod_dingaling/mod_dingaling.c | 6 ++-- src/mod/endpoints/mod_sofia/mod_sofia.c | 35 ++++++++++++++++--- src/switch_nat.c | 6 ++-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 9fb1f39e11..f9f0788c93 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -596,9 +596,9 @@ static void ipchanged_event_handler(switch_event_t *event) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "EVENT_TRAP: IP change detected\n"); - if (cond && !strcmp(cond, "network-address-change")) { - const char *old_ip4 = switch_event_get_header_nil(event, "network-address-previous-v4"); - const char *new_ip4 = switch_event_get_header_nil(event, "network-address-change-v4"); + if (cond && !strcmp(cond, "network-external-address-change")) { + const char *old_ip4 = switch_event_get_header_nil(event, "network-external-address-previous-v4"); + const char *new_ip4 = switch_event_get_header_nil(event, "network-external-address-change-v4"); switch_hash_index_t *hi; void *val; char *tmp; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 7125bceb7c..dff33ee6f5 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -4587,17 +4587,42 @@ static void general_event_handler(switch_event_t *event) case SWITCH_EVENT_TRAP: { const char *cond = switch_event_get_header(event, "condition"); + switch_hash_index_t *hi; + const void *var; + void *val; + sofia_profile_t *profile; + if (zstr(cond)) { + cond = ""; + } - if (cond && !strcmp(cond, "network-address-change") && mod_sofia_globals.auto_restart) { + if (!strcmp(cond, "network-external-address-change") && mod_sofia_globals.auto_restart) { + const char *old_ip4 = switch_event_get_header_nil(event, "network-external-address-previous-v4"); + const char *new_ip4 = switch_event_get_header_nil(event, "network-external-address-change-v4"); + + switch_mutex_lock(mod_sofia_globals.hash_mutex); + if (mod_sofia_globals.profile_hash) { + for (hi = switch_hash_first(NULL, mod_sofia_globals.profile_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &var, NULL, &val); + + if ((profile = (sofia_profile_t *) val)) { + if (!strcmp(profile->extsipip, old_ip4)) { + profile->extsipip = switch_core_strdup(profile->pool, new_ip4); + } + + if (!strcmp(profile->extrtpip, old_ip4)) { + profile->extrtpip = switch_core_strdup(profile->pool, new_ip4); + } + } + } + } + switch_mutex_unlock(mod_sofia_globals.hash_mutex); + sofia_glue_restart_all_profiles(); + } else if (!strcmp(cond, "network-address-change") && mod_sofia_globals.auto_restart) { const char *old_ip4 = switch_event_get_header_nil(event, "network-address-previous-v4"); const char *new_ip4 = switch_event_get_header_nil(event, "network-address-change-v4"); const char *old_ip6 = switch_event_get_header_nil(event, "network-address-previous-v6"); const char *new_ip6 = switch_event_get_header_nil(event, "network-address-change-v6"); - switch_hash_index_t *hi; - const void *var; - void *val; - sofia_profile_t *profile; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "EVENT_TRAP: IP change detected\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IP change detected [%s]->[%s] [%s]->[%s]\n", old_ip4, new_ip4, old_ip6, new_ip6); diff --git a/src/switch_nat.c b/src/switch_nat.c index 85b0247d6d..d9110efe18 100644 --- a/src/switch_nat.c +++ b/src/switch_nat.c @@ -325,9 +325,9 @@ static void *SWITCH_THREAD_FUNC switch_nat_multicast_runtime(switch_thread_t * t do_repub = SWITCH_TRUE; switch_event_create(&event, SWITCH_EVENT_TRAP); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "network-address-change"); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-previous-v4", nat_globals.pub_addr); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-change-v4", newip); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "network-external-address-change"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-external-address-previous-v4", nat_globals.pub_addr); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-external-address-change-v4", newip); switch_event_fire(&event); switch_set_string(nat_globals.pub_addr, newip); From f28c211c0c69c8f34a8c483cbb5de379a2b96b84 Mon Sep 17 00:00:00 2001 From: Brian West Date: Tue, 22 Feb 2011 09:56:07 -0600 Subject: [PATCH 024/154] FS-3077 --- src/switch_ivr_async.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index be360138c2..3afbfb6397 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -3216,8 +3216,10 @@ static switch_status_t speech_on_dtmf(switch_core_session_t *session, const swit switch_status_t status = SWITCH_STATUS_SUCCESS; switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; - if (switch_core_asr_feed_dtmf(sth->ah, dtmf, &flags) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Feeding DTMF\n"); + if (sth) { + if (switch_core_asr_feed_dtmf(sth->ah, dtmf, &flags) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Feeding DTMF\n"); + } } return status; @@ -3231,6 +3233,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_stop_detect_speech(switch_core_sessio switch_assert(channel != NULL); if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { switch_channel_set_private(channel, SWITCH_SPEECH_KEY, NULL); + switch_core_event_hook_remove_recv_dtmf(session, speech_on_dtmf); switch_core_media_bug_remove(session, &sth->bug); return SWITCH_STATUS_SUCCESS; } @@ -3265,14 +3268,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_sess SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); switch_status_t status; if (sth) { if ((status = switch_core_asr_load_grammar(sth->ah, grammar, name)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error loading Grammar\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); } return status; } @@ -3307,14 +3309,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_start_input_timers(swit SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, const char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); switch_status_t status; if (sth) { if ((status = switch_core_asr_unload_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error unloading Grammar\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); } return status; } @@ -3324,14 +3325,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_c SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_enable_grammar(switch_core_session_t *session, const char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); switch_status_t status; if (sth) { if ((status = switch_core_asr_enable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error enabling Grammar\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); } return status; } @@ -3341,14 +3341,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_enable_grammar(switch_c SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_grammar(switch_core_session_t *session, const char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); switch_status_t status; if (sth) { if ((status = switch_core_asr_disable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error disabling Grammar\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); } return status; } @@ -3358,14 +3357,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_grammar(switch_ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_all_grammars(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); switch_status_t status; if (sth) { if ((status = switch_core_asr_disable_all_grammars(sth->ah)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error disabling all Grammars\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); } return status; } @@ -3397,7 +3395,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t * if (sth) { if (switch_core_asr_load_grammar(sth->ah, grammar, name) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error loading Grammar\n"); - switch_core_asr_close(sth->ah, &flags); + switch_ivr_stop_detect_speech(session); return SWITCH_STATUS_FALSE; } From 4f8d4b95cd14085fced4e2d31d06d1c37342e39a Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Tue, 22 Feb 2011 11:13:44 -0500 Subject: [PATCH 025/154] added ftdm_variables.c --- libs/freetdm/src/ftdm_variables.c | 127 ++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 libs/freetdm/src/ftdm_variables.c diff --git a/libs/freetdm/src/ftdm_variables.c b/libs/freetdm/src/ftdm_variables.c new file mode 100644 index 0000000000..706970247b --- /dev/null +++ b/libs/freetdm/src/ftdm_variables.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010, Sangoma Technologies + * David Yat Sin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * + * Moises Silva + * + */ + +#include "private/ftdm_core.h" + +FT_DECLARE(ftdm_status_t) ftdm_event_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value) +{ + char *t_name = 0, *t_val = 0; + + if (!sigmsg || !var_name || !value) { + return FTDM_FAIL; + } + + if (!sigmsg->variables) { + /* initialize on first use */ + sigmsg->variables = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + ftdm_assert_return(sigmsg->variables, FTDM_FAIL, "Failed to create hash table\n"); + } + + t_name = ftdm_strdup(var_name); + t_val = ftdm_strdup(value); + hashtable_insert(sigmsg->variables, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_event_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name) +{ + if (sigmsg && sigmsg->variables) { + hashtable_remove(sigmsg->variables, (void *)var_name); + } + return FTDM_SUCCESS; +} + +FT_DECLARE(const char *) ftdm_event_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name) +{ + const char *var = NULL; + + if (!sigmsg || !sigmsg->variables || !var_name) { + return NULL; + } + + var = (const char *)hashtable_search(((struct hashtable*)sigmsg->variables), (void *)var_name); + return var; +} + +FT_DECLARE(ftdm_iterator_t *) ftdm_event_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter) +{ + ftdm_hash_iterator_t *hashiter = NULL; + if (!sigmsg) { + return NULL; + } + + hashiter = sigmsg->variables == NULL ? NULL : hashtable_first(sigmsg->variables); + + if (hashiter == NULL) { + return NULL; + } + + if (!(iter = get_iterator(FTDM_ITERATOR_VARS, iter))) { + return NULL; + } + iter->pvt.hashiter = hashiter; + return iter; +} + +FT_DECLARE(ftdm_status_t) ftdm_event_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val) +{ + const void *key = NULL; + void *val = NULL; + + *var_name = NULL; + *var_val = NULL; + + ftdm_assert_return(iter && (iter->type == FTDM_ITERATOR_VARS) && iter->pvt.hashiter, FTDM_FAIL, "Cannot get variable from invalid iterator!\n"); + + hashtable_this(iter->pvt.hashiter, &key, NULL, &val); + + *var_name = key; + *var_val = val; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_event_clear_vars(ftdm_sigmsg_t *sigmsg) +{ + if (sigmsg->variables) { + hashtable_destroy(sigmsg->variables); + sigmsg->variables = NULL; + } + return FTDM_SUCCESS; +} From 7860db011007ccbab95e89fff9b17b74addad6bc Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Tue, 22 Feb 2011 11:22:58 -0500 Subject: [PATCH 026/154] freetdm: updated documentation for ftdm_variables --- libs/freetdm/docs/variables.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libs/freetdm/docs/variables.txt b/libs/freetdm/docs/variables.txt index f667b48e53..b849677277 100644 --- a/libs/freetdm/docs/variables.txt +++ b/libs/freetdm/docs/variables.txt @@ -103,8 +103,6 @@ example #1 - print all variables received from FreeTDM const char *var_name = NULL; const char *var_value = NULL; - ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(sigmsg->channel); - /* Read all variables attached to this event */ iter = ftdm_event_get_var_iterator(sigmsg, iter); for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { @@ -131,7 +129,7 @@ example #3 - accessing raw data /* Inside event call-back function */ ftdm_size_t len; uint8_t *mydata; - if (ftdm_event_get_raw_data(ftdmchan->sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (ftdm_event_get_raw_data(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { /* raw data is available, do something with mydata here */ } From 3116334d277b0f69305434388bd56f32ae1cb2e6 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 22 Feb 2011 13:16:16 -0600 Subject: [PATCH 027/154] FS-3086 --comment-only This should fix the 'hupall' based lockups --- src/switch_core_session.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/switch_core_session.c b/src/switch_core_session.c index d2e28de523..dfe419ed15 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -203,16 +203,12 @@ SWITCH_DECLARE(void) switch_core_session_hupall_matching_var(const char *var_nam for (hi = switch_hash_first(NULL, session_manager.session_table); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, NULL, NULL, &val); if (val) { - const char *this_val; session = (switch_core_session_t *) val; if (switch_core_session_read_lock(session) == SWITCH_STATUS_SUCCESS) { - if (switch_channel_up(session->channel) && - (this_val = switch_channel_get_variable(session->channel, var_name)) && (!strcmp(this_val, var_val))) { - np = switch_core_alloc(pool, sizeof(*np)); - np->str = switch_core_strdup(pool, session->uuid_str); - np->next = head; - head = np; - } + np = switch_core_alloc(pool, sizeof(*np)); + np->str = switch_core_strdup(pool, session->uuid_str); + np->next = head; + head = np; switch_core_session_rwunlock(session); } } @@ -221,7 +217,11 @@ SWITCH_DECLARE(void) switch_core_session_hupall_matching_var(const char *var_nam for(np = head; np; np = np->next) { if ((session = switch_core_session_locate(np->str))) { - switch_channel_hangup(session->channel, cause); + const char *this_val; + if (switch_channel_up(session->channel) && + (this_val = switch_channel_get_variable(session->channel, var_name)) && (!strcmp(this_val, var_val))) { + switch_channel_hangup(session->channel, cause); + } switch_core_session_rwunlock(session); } } From e26b5727de21b50023670e0da4612f91bef8eda4 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Tue, 22 Feb 2011 14:59:57 -0500 Subject: [PATCH 028/154] freetdm: fix for calling print_hex_dump with invalid string length --- .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 9feb5c8c43..50c7cf74ab 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -833,8 +833,9 @@ static ftdm_status_t sngisdn_get_frame_info(uint8_t *data, uint32_t data_len, ft //ftdm_log(FTDM_LOG_DEBUG, "Decoded IE:%s\n", get_code_2_str(ie_id, dcodQ931IEIDTable)); } if (!bchan_no) { + uint32_t tmp_len = 0; char tmp[1000]; - print_hex_dump(tmp, 0, data, 0, data_len); + print_hex_dump(tmp, &tmp_len, data, 0, data_len); ftdm_log(FTDM_LOG_WARNING, "Failed to determine b-channel on SETUP message\n%s\n", tmp); } } From 9f8c428ebc1ea407ba0364337e276ace23bb6e16 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Tue, 22 Feb 2011 14:59:57 -0500 Subject: [PATCH 029/154] freetdm: fix for calling print_hex_dump with invalid string length --- .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 9feb5c8c43..50c7cf74ab 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -833,8 +833,9 @@ static ftdm_status_t sngisdn_get_frame_info(uint8_t *data, uint32_t data_len, ft //ftdm_log(FTDM_LOG_DEBUG, "Decoded IE:%s\n", get_code_2_str(ie_id, dcodQ931IEIDTable)); } if (!bchan_no) { + uint32_t tmp_len = 0; char tmp[1000]; - print_hex_dump(tmp, 0, data, 0, data_len); + print_hex_dump(tmp, &tmp_len, data, 0, data_len); ftdm_log(FTDM_LOG_WARNING, "Failed to determine b-channel on SETUP message\n%s\n", tmp); } } From e8a1055854bb4cc7037f3be434e261280714dfdf Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 22 Feb 2011 17:07:18 -0600 Subject: [PATCH 030/154] ESL-56 null terminate buffer after reading from the socket to prevent cross-over to old data that confuses the parser and throws off framing. (regression from 2081bf97b9836f5299c22edbb1ead077842ea2bc) --- libs/esl/src/esl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c index 078c5de973..cd103ecd17 100644 --- a/libs/esl/src/esl.c +++ b/libs/esl/src/esl.c @@ -949,15 +949,18 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_ while(!revent && handle->connected) { esl_size_t len1; - if ((len1 = esl_buffer_read_packet(handle->packet_buf, handle->socket_buf, sizeof(handle->socket_buf)))) { + if ((len1 = esl_buffer_read_packet(handle->packet_buf, handle->socket_buf, sizeof(handle->socket_buf) - 1))) { char *data = (char *) handle->socket_buf; char *p, *e; + + *(data + len1) = '\0'; esl_event_create(&revent, ESL_EVENT_CLONE); revent->event_id = ESL_EVENT_SOCKET_DATA; esl_event_add_header_string(revent, ESL_STACK_BOTTOM, "Event-Name", "SOCKET_DATA"); hname = p = data; + while(p) { hname = p; p = NULL; @@ -984,7 +987,8 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_ break; } - rrval = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf)); + rrval = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf) - 1); + *((char *)handle->socket_buf + rrval) = '\0'; if (rrval == 0) { if (++zc >= 100) { @@ -1020,7 +1024,8 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_ if (s >= len) { sofar = esl_buffer_read(handle->packet_buf, body, len); } else { - r = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf)); + r = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf) - 1); + *((char *)handle->socket_buf + r) = '\0'; if (r < 0) { strerror_r(handle->errnum, handle->err, sizeof(handle->err)); From 0dcdd78cb5ba84f940fdcf0dfefcfb7ee17cc0c6 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 22 Feb 2011 17:22:01 -0600 Subject: [PATCH 031/154] FS-3054 --comment-only try latest commit, I can guess what probably causes the seg based on my last patch --- src/mod/endpoints/mod_sofia/mod_sofia.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index dff33ee6f5..9e8d8200f4 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -4601,16 +4601,16 @@ static void general_event_handler(switch_event_t *event) const char *new_ip4 = switch_event_get_header_nil(event, "network-external-address-change-v4"); switch_mutex_lock(mod_sofia_globals.hash_mutex); - if (mod_sofia_globals.profile_hash) { + if (mod_sofia_globals.profile_hash && !zstr(old_ip4) && !zstr(new_ip4)) { for (hi = switch_hash_first(NULL, mod_sofia_globals.profile_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); if ((profile = (sofia_profile_t *) val)) { - if (!strcmp(profile->extsipip, old_ip4)) { + if (!zstr(profile->extsipip) && !strcmp(profile->extsipip, old_ip4)) { profile->extsipip = switch_core_strdup(profile->pool, new_ip4); } - if (!strcmp(profile->extrtpip, old_ip4)) { + if (!zstr(profile->extrtpip) && !strcmp(profile->extrtpip, old_ip4)) { profile->extrtpip = switch_core_strdup(profile->pool, new_ip4); } } From f202328effa33588b3766a63ca0a7f6ab835ef76 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Wed, 23 Feb 2011 00:31:26 +0100 Subject: [PATCH 032/154] Doc: somes details on user registration functions --- src/include/switch_core.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 984b54fac7..9505fe4c40 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -2227,9 +2227,33 @@ SWITCH_DECLARE(const char *) switch_core_banner(void); SWITCH_DECLARE(switch_bool_t) switch_core_session_in_thread(switch_core_session_t *session); SWITCH_DECLARE(uint32_t) switch_default_ptime(const char *name, uint32_t number); +/*! + \brief Add user registration + \param [in] user + \param [in] realm + \param [in] token + \param [in] url - a freeswitch dial string + \param [in] expires + \param [in] network_ip + \param [in] network_port + \param [in] network_proto - one of tls, tcp, udp + \param [out] err - Error if it exists +*/ SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, const char *realm, const char *token, const char *url, uint32_t expires, const char *network_ip, const char *network_port, const char *network_proto); +/*! + \brief Delete user registration + \param [in] user + \param [in] realm + \param [in] token + \param [out] err - Error if it exists +*/ SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, const char *realm, const char *token); +/*! + \brief Expire user registrations + \param [in] force delete all registrations + \param [out] err - Error if it exists +*/ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force); SWITCH_END_EXTERN_C From ea29a1dfcc53729af1e7798050a62566700d99fa Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Wed, 23 Feb 2011 00:56:00 +0100 Subject: [PATCH 033/154] centralized registration: - allow infinite registrations (expire=0) - when removing a registration, keep the other with same user if multiple reg is allowed --- src/switch_core_sqldb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index 1274213893..2a6f9e3bf2 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -1696,7 +1696,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, c return SWITCH_STATUS_FALSE; } - sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_hostname()); + if (!zstr(token) && runtime.multiple_registrations) { + sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q' and token='%q'", user, realm, switch_core_get_hostname(), token); + } else { + sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_hostname()); + } switch_cache_db_execute_sql(dbh, sql, NULL); switch_cache_db_release_db_handle(&dbh); @@ -1723,7 +1727,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) if (force) { sql = switch_mprintf("delete from registrations where hostname='%q'", switch_core_get_hostname()); } else { - sql = switch_mprintf("delete from registrations where expires <= %ld and hostname='%q'", now, switch_core_get_hostname()); + sql = switch_mprintf("delete from registrations where expires > 0 and expires <= %ld and hostname='%q'", now, switch_core_get_hostname()); } switch_cache_db_execute_sql(dbh, sql, NULL); From e7a8189b2b87d600c290998772d0aa53c26bc964 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Wed, 23 Feb 2011 00:57:19 +0100 Subject: [PATCH 034/154] Skinny: centralized registration --- src/mod/endpoints/mod_skinny/mod_skinny.c | 27 ++++++++++++++++++++ src/mod/endpoints/mod_skinny/skinny_server.c | 10 ++++++++ 2 files changed, 37 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 5320ca52d1..91d81dacf2 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1243,6 +1243,21 @@ static void walk_listeners(skinny_listener_callback_func_t callback, void *pvt) switch_mutex_unlock(globals.mutex); } +static int flush_listener_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + char *profile_name = argv[0]; + char *value = argv[1]; + char *domain_name = argv[2]; + char *device_name = argv[3]; + char *device_instance = argv[4]; + + char *token = switch_mprintf("skinny/%q/%q/%q:%q", profile_name, value, device_name, device_instance); + switch_core_del_registration(value, domain_name, token); + switch_safe_free(token); + + return 0; +} + static void flush_listener(listener_t *listener) { @@ -1250,6 +1265,18 @@ static void flush_listener(listener_t *listener) skinny_profile_t *profile = listener->profile; char *sql; + if ((sql = switch_mprintf( + "SELECT '%q', value, '%q', '%q', '%d' " + "FROM skinny_lines " + "WHERE device_name='%s' AND device_instance=%d " + "ORDER BY position", + profile->name, profile->domain, listener->device_name, listener->device_instance, + listener->device_name, listener->device_instance + ))) { + skinny_execute_sql_callback(profile, profile->sql_mutex, sql, flush_listener_callback, NULL); + switch_safe_free(sql); + } + if ((sql = switch_mprintf( "DELETE FROM skinny_devices " "WHERE name='%s' and instance=%d", diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index e7c761dfca..7d66a9c281 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -1006,6 +1006,10 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r } if ((xbuttons = switch_xml_child(xskinny, "buttons"))) { uint32_t line_instance = 1; + char *network_ip = inet_ntoa(request->data.reg.ip); + int network_port = 0; + char network_port_c[6]; + snprintf(network_port_c, sizeof(network_port_c), "%d", network_port); for (xbutton = switch_xml_child(xbuttons, "button"); xbutton; xbutton = xbutton->next) { uint32_t position = atoi(switch_xml_attr_soft(xbutton, "position")); uint32_t type = skinny_str2button(switch_xml_attr_soft(xbutton, "type")); @@ -1031,8 +1035,14 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r label, value, caller_name, ring_on_idle, ring_on_active, busy_trigger, forward_all, forward_busy, forward_noanswer, noanswer_duration))) { + char *token, *url; skinny_execute_sql(profile, sql, profile->sql_mutex); switch_safe_free(sql); + token = switch_mprintf("skinny/%q/%q/%q:%d", profile->name, value, request->data.reg.device_name, request->data.reg.instance); + url = switch_mprintf("skinny/%q/%q", profile->name, value); + switch_core_add_registration(value, profile->domain, token, url, 0, network_ip, network_port_c, "tcp"); + switch_safe_free(token); + switch_safe_free(url); } if (line_instance == 1) { switch_event_t *message_query_event = NULL; From 0a14839bf9e760fa10b186d4ea5ab561a508902b Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Wed, 23 Feb 2011 00:58:05 +0100 Subject: [PATCH 035/154] Skinny: mark ring ready --- src/mod/endpoints/mod_skinny/skinny_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 7d66a9c281..538100359f 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -555,6 +555,7 @@ int skinny_ring_lines_callback(void *pArg, int argc, char **argv, char **columnN skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_BLINK); send_set_ringer(listener, SKINNY_RING_INSIDE, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id); + switch_channel_mark_ring_ready(channel); } return 0; } From e0a0a56f240ec397d2074cf28d3c9976ab3bfd83 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Wed, 23 Feb 2011 01:13:00 +0100 Subject: [PATCH 036/154] mod_command reg_url: correct field name --- src/mod/applications/mod_commands/mod_commands.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 517134d2e1..a6df087cb0 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -90,11 +90,11 @@ static switch_status_t select_url(const char *user, if (exclude_contact) { sql = switch_mprintf("select url, '%q' " - "from registrations where user='%q' and realm='%q' " + "from registrations where reg_user='%q' and realm='%q' " "and url not like '%%%s%%'", (concat != NULL) ? concat : "", user, domain, exclude_contact); } else { sql = switch_mprintf("select url, '%q' " - "from registrations where user='%q' and realm='%q'", + "from registrations where reg_user='%q' and realm='%q'", (concat != NULL) ? concat : "", user, domain); } From b53a68484361c9581a26f6b090a58b0803bd412d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 22 Feb 2011 18:35:54 -0600 Subject: [PATCH 037/154] fix bug in switch_itodtmf from a8f5bf60a87fb27420846bd9d9af5e61f1f947d6 --- src/include/switch_utils.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index a1a93c6ac8..7fcb1e8c75 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -189,10 +189,12 @@ static inline char switch_itodtmf(char i) char r = i; if (i > 9 && i < 14) { - r = i + 55; + r += 55; + } else { + r += 48; } - return r + 48; + return r; } static inline int switch_dtmftoi(char *s) From cb6f1ed61da20672600db792d3a86e051fa484c5 Mon Sep 17 00:00:00 2001 From: Mathieu Rene Date: Tue, 22 Feb 2011 20:25:16 -0500 Subject: [PATCH 038/154] Fix storage class for 'cause' in user_outgoing_channel() so that each call has its very own hangup cause --- src/mod/applications/mod_dptools/mod_dptools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 9a1258a5b6..346cdaa9f8 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -2893,7 +2893,7 @@ static switch_call_cause_t user_outgoing_channel(switch_core_session_t *session, switch_xml_t x_domain = NULL, xml = NULL, x_user = NULL, x_group = NULL, x_param, x_params; char *user = NULL, *domain = NULL, *dup_domain = NULL; const char *dest = NULL; - static switch_call_cause_t cause = SWITCH_CAUSE_NONE; + switch_call_cause_t cause = SWITCH_CAUSE_NONE; unsigned int timelimit = 60; switch_channel_t *new_channel = NULL; switch_event_t *params = NULL, *var_event_orig = var_event; From 512eaaa87c13f85fc46a135f08ae61e7334d0cf9 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Wed, 23 Feb 2011 15:01:29 -0500 Subject: [PATCH 039/154] freetdm: removed warning log when a SETUP message without channel IE is received --- .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 50c7cf74ab..5b16a3404b 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -836,7 +836,7 @@ static ftdm_status_t sngisdn_get_frame_info(uint8_t *data, uint32_t data_len, ft uint32_t tmp_len = 0; char tmp[1000]; print_hex_dump(tmp, &tmp_len, data, 0, data_len); - ftdm_log(FTDM_LOG_WARNING, "Failed to determine b-channel on SETUP message\n%s\n", tmp); + ftdm_log(FTDM_LOG_DEBUG, "Failed to determine b-channel on SETUP message\n%s\n", tmp); } } From dd40e50e7ae7812a6cb26b702034d73033e88e8e Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Wed, 23 Feb 2011 16:20:26 -0500 Subject: [PATCH 040/154] freetdm: span id specified in raw trace if channel was not mapped --- .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 9feb5c8c43..e8b8bdbd9a 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -241,6 +241,11 @@ void sngisdn_trace_raw_q931(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t d sigev.span_id = ftdmchan->physical_span_id; sigev.chan_id = ftdmchan->physical_chan_id; sigev.channel = ftdmchan; + } else { + /* We could not map the channel, but at least set the span */ + if (signal_data->ftdm_span->channels[1]) { + sigev.span_id = signal_data->ftdm_span->channels[1]->physical_span_id; + } } sigev.event_id = FTDM_SIGEVENT_TRACE_RAW; From c22816c39c6fa2585d4bd3c811acbe0b6c1f6e41 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Wed, 23 Feb 2011 16:20:26 -0500 Subject: [PATCH 041/154] freetdm: span id specified in raw trace if channel was not mapped --- .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 5b16a3404b..e24e64b326 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -241,6 +241,11 @@ void sngisdn_trace_raw_q931(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t d sigev.span_id = ftdmchan->physical_span_id; sigev.chan_id = ftdmchan->physical_chan_id; sigev.channel = ftdmchan; + } else { + /* We could not map the channel, but at least set the span */ + if (signal_data->ftdm_span->channels[1]) { + sigev.span_id = signal_data->ftdm_span->channels[1]->physical_span_id; + } } sigev.event_id = FTDM_SIGEVENT_TRACE_RAW; From 155aafd3f4942a8cf42c3dedf5421645d051d7dd Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 10:17:33 -0600 Subject: [PATCH 042/154] FS-3097 regression from 4f93ea25ece53df029e6dc6c913ad7c190df22d5 --- src/include/switch_channel.h | 1 + src/switch_ivr_bridge.c | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index c0bef4be59..25273bafc4 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -79,6 +79,7 @@ SWITCH_DECLARE(int) switch_channel_test_ready(switch_channel_t *channel, switch_ #define switch_channel_ready(_channel) switch_channel_test_ready(_channel, SWITCH_TRUE, SWITCH_FALSE) #define switch_channel_media_ready(_channel) switch_channel_test_ready(_channel, SWITCH_TRUE, SWITCH_TRUE) +#define switch_channel_media_up(_channel) (switch_channel_test_flag(_channel, CF_ANSWERED) || switch_channel_test_flag(_channel, CF_EARLY_MEDIA)) #define switch_channel_up(_channel) (switch_channel_get_state(_channel) < CS_HANGUP) #define switch_channel_down(_channel) (switch_channel_get_state(_channel) >= CS_HANGUP) diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index f130625a75..88c72bf1b8 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -1398,10 +1398,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(const char *originator_uu return SWITCH_STATUS_FALSE; } - //if (!switch_channel_test_flag(originator_channel, CF_ANSWERED)) { - if (!switch_channel_media_ready(originator_channel)) { - if (switch_channel_media_ready(originatee_channel)) { - //if (switch_channel_test_flag(originatee_channel, CF_ANSWERED)) { + if (!switch_channel_media_up(originator_channel)) { + if (switch_channel_media_up(originatee_channel)) { swap_session = originator_session; originator_session = originatee_session; originatee_session = swap_session; From deec244b9d1c55f46efaf87f908462838d4f8416 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 10:22:47 -0600 Subject: [PATCH 043/154] FS-3098 You are probably right about the sql statement, we must have missed that when the contributor gave us the patch. Be aware this code is depricated in favor of the broadsoft SCA shared appeareances and will probably be removed eventually --- src/mod/endpoints/mod_sofia/sofia_reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 5109e0bc72..6e3649a68f 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -653,7 +653,7 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) "and profile_name='%s' and expires <= %ld", mod_sofia_globals.hostname, profile->name, (long) now); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sla_dialog_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%s' and expires <= %ld", + switch_snprintf(sql, sizeof(sql), "delete from sip_shared_appearance_dialogs where expires > 0 and hostname='%s' and expires <= %ld", mod_sofia_globals.hostname, (long) now); From 62d000ed20305dcccefd5ac8b9ddbb40016e879b Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 13:14:23 -0500 Subject: [PATCH 044/154] freetdm: remove sanity state check in outgoing function --- libs/freetdm/src/ftdm_io.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 504c00f33c..133420922b 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -2430,11 +2430,6 @@ static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *f goto done; } - if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot place call in channel in state %s!\n", ftdm_channel_state2str(ftdmchan->state)); - goto done; - } - status = ftdmchan->span->outgoing_call(ftdmchan); if (status == FTDM_BREAK) { /* the signaling module detected glare on time */ From 69d1d984feff8bf616ba8a0c7c921ccfe07d3b16 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 12:17:33 -0600 Subject: [PATCH 045/154] sleep 2 seconds on confernece outcall at the end to prevent auto-answer race --- src/mod/applications/mod_conference/mod_conference.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 71ed1ed4b7..35412afb17 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -1396,6 +1396,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v if (switch_test_flag(conference, CFLAG_OUTCALL)) { conference->cancel_cause = SWITCH_CAUSE_ORIGINATOR_CANCEL; + switch_yield(2000000); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Ending pending outcall channels for Conference: '%s'\n", conference->name); while(conference->originating) { switch_yield(200000); From cbf610c5aa1747e930e8678375285f59c56f08c2 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 14:24:53 -0600 Subject: [PATCH 046/154] reswig --- .../src/org/freeswitch/swig/freeswitch.java | 4 + .../org/freeswitch/swig/freeswitchJNI.java | 1 + .../languages/mod_java/switch_swig_wrap.cpp | 61 ++++++++ .../languages/mod_managed/freeswitch_wrap.cxx | 112 ++++++++++++++ src/mod/languages/mod_managed/managed/swig.cs | 62 ++++++++ src/mod/languages/mod_perl/freeswitch.pm | 1 + src/mod/languages/mod_perl/mod_perl_wrap.cpp | 138 ++++++++++++++++-- src/mod/languages/mod_python/freeswitch.py | 1 + .../languages/mod_python/mod_python_wrap.cpp | 122 +++++++++++++++- 9 files changed, 479 insertions(+), 23 deletions(-) diff --git a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitch.java b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitch.java index 7b53db475c..ea825c9014 100644 --- a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitch.java +++ b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitch.java @@ -17,6 +17,10 @@ public class freeswitch { freeswitchJNI.consoleCleanLog(msg); } + public static boolean email(String to, String from, String headers, String body, String file, String convert_cmd, String convert_ext) { + return freeswitchJNI.email(to, from, headers, body, file, convert_cmd, convert_ext); + } + public static void console_log(String level_str, String msg) { freeswitchJNI.console_log(level_str, msg); } diff --git a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java index f90a9cca11..5e778de59c 100644 --- a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java +++ b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java @@ -11,6 +11,7 @@ package org.freeswitch.swig; class freeswitchJNI { public final static native void consoleLog(String jarg1, String jarg2); public final static native void consoleCleanLog(String jarg1); + public final static native boolean email(String jarg1, String jarg2, String jarg3, String jarg4, String jarg5, String jarg6, String jarg7); public final static native long new_IVRMenu(long jarg1, IVRMenu jarg1_, String jarg2, String jarg3, String jarg4, String jarg5, String jarg6, String jarg7, String jarg8, String jarg9, String jarg10, int jarg11, int jarg12, int jarg13, int jarg14, int jarg15, int jarg16); public final static native void delete_IVRMenu(long jarg1); public final static native void IVRMenu_bindAction(long jarg1, IVRMenu jarg1_, String jarg2, String jarg3, String jarg4); diff --git a/src/mod/languages/mod_java/switch_swig_wrap.cpp b/src/mod/languages/mod_java/switch_swig_wrap.cpp index 720e7571db..34f62d5c9d 100644 --- a/src/mod/languages/mod_java/switch_swig_wrap.cpp +++ b/src/mod/languages/mod_java/switch_swig_wrap.cpp @@ -244,6 +244,67 @@ SWIGEXPORT void JNICALL Java_org_freeswitch_swig_freeswitchJNI_consoleCleanLog(J } +SWIGEXPORT jboolean JNICALL Java_org_freeswitch_swig_freeswitchJNI_email(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2, jstring jarg3, jstring jarg4, jstring jarg5, jstring jarg6, jstring jarg7) { + jboolean jresult = 0 ; + char *arg1 = (char *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; + char *arg5 = (char *) NULL ; + char *arg6 = (char *) NULL ; + char *arg7 = (char *) NULL ; + bool result; + + (void)jenv; + (void)jcls; + arg1 = 0; + if (jarg1) { + arg1 = (char *)jenv->GetStringUTFChars(jarg1, 0); + if (!arg1) return 0; + } + arg2 = 0; + if (jarg2) { + arg2 = (char *)jenv->GetStringUTFChars(jarg2, 0); + if (!arg2) return 0; + } + arg3 = 0; + if (jarg3) { + arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); + if (!arg3) return 0; + } + arg4 = 0; + if (jarg4) { + arg4 = (char *)jenv->GetStringUTFChars(jarg4, 0); + if (!arg4) return 0; + } + arg5 = 0; + if (jarg5) { + arg5 = (char *)jenv->GetStringUTFChars(jarg5, 0); + if (!arg5) return 0; + } + arg6 = 0; + if (jarg6) { + arg6 = (char *)jenv->GetStringUTFChars(jarg6, 0); + if (!arg6) return 0; + } + arg7 = 0; + if (jarg7) { + arg7 = (char *)jenv->GetStringUTFChars(jarg7, 0); + if (!arg7) return 0; + } + result = (bool)email(arg1,arg2,arg3,arg4,arg5,arg6,arg7); + jresult = (jboolean)result; + if (arg1) jenv->ReleaseStringUTFChars(jarg1, (const char *)arg1); + if (arg2) jenv->ReleaseStringUTFChars(jarg2, (const char *)arg2); + if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + if (arg4) jenv->ReleaseStringUTFChars(jarg4, (const char *)arg4); + if (arg5) jenv->ReleaseStringUTFChars(jarg5, (const char *)arg5); + if (arg6) jenv->ReleaseStringUTFChars(jarg6, (const char *)arg6); + if (arg7) jenv->ReleaseStringUTFChars(jarg7, (const char *)arg7); + return jresult; +} + + SWIGEXPORT jlong JNICALL Java_org_freeswitch_swig_freeswitchJNI_new_1IVRMenu(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2, jstring jarg3, jstring jarg4, jstring jarg5, jstring jarg6, jstring jarg7, jstring jarg8, jstring jarg9, jstring jarg10, jint jarg11, jint jarg12, jint jarg13, jint jarg14, jint jarg15, jint jarg16) { jlong jresult = 0 ; IVRMenu *arg1 = (IVRMenu *) 0 ; diff --git a/src/mod/languages/mod_managed/freeswitch_wrap.cxx b/src/mod/languages/mod_managed/freeswitch_wrap.cxx index 71e9f99819..1c6adf9aff 100644 --- a/src/mod/languages/mod_managed/freeswitch_wrap.cxx +++ b/src/mod/languages/mod_managed/freeswitch_wrap.cxx @@ -7473,6 +7473,42 @@ SWIGEXPORT char * SWIGSTDCALL CSharp_switch_core_get_variable(char * jarg1) { } +SWIGEXPORT char * SWIGSTDCALL CSharp_switch_core_get_variable_dup(char * jarg1) { + char * jresult ; + char *arg1 = (char *) 0 ; + char *result = 0 ; + + arg1 = (char *)jarg1; + result = (char *)switch_core_get_variable_dup((char const *)arg1); + jresult = SWIG_csharp_string_callback((const char *)result); + return jresult; +} + + +SWIGEXPORT char * SWIGSTDCALL CSharp_switch_core_get_variable_pdup(char * jarg1, void * jarg2) { + char * jresult ; + char *arg1 = (char *) 0 ; + switch_memory_pool_t *arg2 = (switch_memory_pool_t *) 0 ; + char *result = 0 ; + + arg1 = (char *)jarg1; + arg2 = (switch_memory_pool_t *)jarg2; + result = (char *)switch_core_get_variable_pdup((char const *)arg1,arg2); + jresult = SWIG_csharp_string_callback((const char *)result); + return jresult; +} + + +SWIGEXPORT char * SWIGSTDCALL CSharp_switch_core_get_hostname() { + char * jresult ; + char *result = 0 ; + + result = (char *)switch_core_get_hostname(); + jresult = SWIG_csharp_string_callback((const char *)result); + return jresult; +} + + SWIGEXPORT void SWIGSTDCALL CSharp_switch_core_set_variable(char * jarg1, char * jarg2) { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -10913,6 +10949,60 @@ SWIGEXPORT unsigned long SWIGSTDCALL CSharp_switch_default_ptime(char * jarg1, u } +SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_add_registration(char * jarg1, char * jarg2, char * jarg3, char * jarg4, unsigned long jarg5, char * jarg6, char * jarg7, char * jarg8) { + int jresult ; + char *arg1 = (char *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + uint32_t arg5 ; + char *arg6 = (char *) 0 ; + char *arg7 = (char *) 0 ; + char *arg8 = (char *) 0 ; + switch_status_t result; + + arg1 = (char *)jarg1; + arg2 = (char *)jarg2; + arg3 = (char *)jarg3; + arg4 = (char *)jarg4; + arg5 = (uint32_t)jarg5; + arg6 = (char *)jarg6; + arg7 = (char *)jarg7; + arg8 = (char *)jarg8; + result = (switch_status_t)switch_core_add_registration((char const *)arg1,(char const *)arg2,(char const *)arg3,(char const *)arg4,arg5,(char const *)arg6,(char const *)arg7,(char const *)arg8); + jresult = result; + return jresult; +} + + +SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_del_registration(char * jarg1, char * jarg2, char * jarg3) { + int jresult ; + char *arg1 = (char *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) 0 ; + switch_status_t result; + + arg1 = (char *)jarg1; + arg2 = (char *)jarg2; + arg3 = (char *)jarg3; + result = (switch_status_t)switch_core_del_registration((char const *)arg1,(char const *)arg2,(char const *)arg3); + jresult = result; + return jresult; +} + + +SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_expire_registration(int jarg1) { + int jresult ; + int arg1 ; + switch_status_t result; + + arg1 = (int)jarg1; + result = (switch_status_t)switch_core_expire_registration(arg1); + jresult = result; + return jresult; +} + + SWIGEXPORT void SWIGSTDCALL CSharp_switch_loadable_module_interface_module_name_set(void * jarg1, char * jarg2) { switch_loadable_module_interface *arg1 = (switch_loadable_module_interface *) 0 ; char *arg2 = (char *) 0 ; @@ -26411,6 +26501,18 @@ SWIGEXPORT void SWIGSTDCALL CSharp_switch_change_sln_volume(void * jarg1, unsign } +SWIGEXPORT void SWIGSTDCALL CSharp_switch_change_sln_volume_granular(void * jarg1, unsigned long jarg2, int jarg3) { + int16_t *arg1 = (int16_t *) 0 ; + uint32_t arg2 ; + int32_t arg3 ; + + arg1 = (int16_t *)jarg1; + arg2 = (uint32_t)jarg2; + arg3 = (int32_t)jarg3; + switch_change_sln_volume_granular(arg1,arg2,arg3); +} + + SWIGEXPORT unsigned long SWIGSTDCALL CSharp_switch_merge_sln(void * jarg1, unsigned long jarg2, void * jarg3, unsigned long jarg4) { unsigned long jresult ; int16_t *arg1 = (int16_t *) 0 ; @@ -29829,6 +29931,16 @@ SWIGEXPORT void SWIGSTDCALL CSharp_switch_rtp_set_telephony_recv_event(void * ja } +SWIGEXPORT void SWIGSTDCALL CSharp_switch_rtp_set_recv_pt(void * jarg1, unsigned char jarg2) { + switch_rtp_t *arg1 = (switch_rtp_t *) 0 ; + switch_payload_t arg2 ; + + arg1 = (switch_rtp_t *)jarg1; + arg2 = (switch_payload_t)jarg2; + switch_rtp_set_recv_pt(arg1,arg2); +} + + SWIGEXPORT void SWIGSTDCALL CSharp_switch_rtp_set_cng_pt(void * jarg1, unsigned char jarg2) { switch_rtp_t *arg1 = (switch_rtp_t *) 0 ; switch_payload_t arg2 ; diff --git a/src/mod/languages/mod_managed/managed/swig.cs b/src/mod/languages/mod_managed/managed/swig.cs index c640199353..e1d81a820c 100644 --- a/src/mod/languages/mod_managed/managed/swig.cs +++ b/src/mod/languages/mod_managed/managed/swig.cs @@ -1384,6 +1384,21 @@ public class freeswitch { return ret; } + public static string switch_core_get_variable_dup(string varname) { + string ret = freeswitchPINVOKE.switch_core_get_variable_dup(varname); + return ret; + } + + public static string switch_core_get_variable_pdup(string varname, SWIGTYPE_p_apr_pool_t pool) { + string ret = freeswitchPINVOKE.switch_core_get_variable_pdup(varname, SWIGTYPE_p_apr_pool_t.getCPtr(pool)); + return ret; + } + + public static string switch_core_get_hostname() { + string ret = freeswitchPINVOKE.switch_core_get_hostname(); + return ret; + } + public static void switch_core_set_variable(string varname, string value) { freeswitchPINVOKE.switch_core_set_variable(varname, value); } @@ -2415,6 +2430,21 @@ public class freeswitch { return ret; } + public static switch_status_t switch_core_add_registration(string user, string realm, string token, string url, uint expires, string network_ip, string network_port, string network_proto) { + switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_core_add_registration(user, realm, token, url, expires, network_ip, network_port, network_proto); + return ret; + } + + public static switch_status_t switch_core_del_registration(string user, string realm, string token) { + switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_core_del_registration(user, realm, token); + return ret; + } + + public static switch_status_t switch_core_expire_registration(int force) { + switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_core_expire_registration(force); + return ret; + } + public static switch_status_t switch_loadable_module_init(switch_bool_t autoload) { switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_loadable_module_init((int)autoload); return ret; @@ -3974,6 +4004,10 @@ public class freeswitch { freeswitchPINVOKE.switch_change_sln_volume(SWIGTYPE_p_short.getCPtr(data), samples, vol); } + public static void switch_change_sln_volume_granular(SWIGTYPE_p_short data, uint samples, int vol) { + freeswitchPINVOKE.switch_change_sln_volume_granular(SWIGTYPE_p_short.getCPtr(data), samples, vol); + } + public static uint switch_merge_sln(SWIGTYPE_p_short data, uint samples, SWIGTYPE_p_short other_data, uint other_samples) { uint ret = freeswitchPINVOKE.switch_merge_sln(SWIGTYPE_p_short.getCPtr(data), samples, SWIGTYPE_p_short.getCPtr(other_data), other_samples); return ret; @@ -4857,6 +4891,10 @@ public class freeswitch { freeswitchPINVOKE.switch_rtp_set_telephony_recv_event(SWIGTYPE_p_switch_rtp.getCPtr(rtp_session), te); } + public static void switch_rtp_set_recv_pt(SWIGTYPE_p_switch_rtp rtp_session, byte pt) { + freeswitchPINVOKE.switch_rtp_set_recv_pt(SWIGTYPE_p_switch_rtp.getCPtr(rtp_session), pt); + } + public static void switch_rtp_set_cng_pt(SWIGTYPE_p_switch_rtp rtp_session, byte pt) { freeswitchPINVOKE.switch_rtp_set_cng_pt(SWIGTYPE_p_switch_rtp.getCPtr(rtp_session), pt); } @@ -7569,6 +7607,15 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_core_get_variable")] public static extern string switch_core_get_variable(string jarg1); + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_get_variable_dup")] + public static extern string switch_core_get_variable_dup(string jarg1); + + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_get_variable_pdup")] + public static extern string switch_core_get_variable_pdup(string jarg1, HandleRef jarg2); + + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_get_hostname")] + public static extern string switch_core_get_hostname(); + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_set_variable")] public static extern void switch_core_set_variable(string jarg1, string jarg2); @@ -8349,6 +8396,15 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_default_ptime")] public static extern uint switch_default_ptime(string jarg1, uint jarg2); + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_add_registration")] + public static extern int switch_core_add_registration(string jarg1, string jarg2, string jarg3, string jarg4, uint jarg5, string jarg6, string jarg7, string jarg8); + + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_del_registration")] + public static extern int switch_core_del_registration(string jarg1, string jarg2, string jarg3); + + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_expire_registration")] + public static extern int switch_core_expire_registration(int jarg1); + [DllImport("mod_managed", EntryPoint="CSharp_switch_loadable_module_interface_module_name_set")] public static extern void switch_loadable_module_interface_module_name_set(HandleRef jarg1, string jarg2); @@ -12042,6 +12098,9 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_change_sln_volume")] public static extern void switch_change_sln_volume(HandleRef jarg1, uint jarg2, int jarg3); + [DllImport("mod_managed", EntryPoint="CSharp_switch_change_sln_volume_granular")] + public static extern void switch_change_sln_volume_granular(HandleRef jarg1, uint jarg2, int jarg3); + [DllImport("mod_managed", EntryPoint="CSharp_switch_merge_sln")] public static extern uint switch_merge_sln(HandleRef jarg1, uint jarg2, HandleRef jarg3, uint jarg4); @@ -12738,6 +12797,9 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_rtp_set_telephony_recv_event")] public static extern void switch_rtp_set_telephony_recv_event(HandleRef jarg1, byte jarg2); + [DllImport("mod_managed", EntryPoint="CSharp_switch_rtp_set_recv_pt")] + public static extern void switch_rtp_set_recv_pt(HandleRef jarg1, byte jarg2); + [DllImport("mod_managed", EntryPoint="CSharp_switch_rtp_set_cng_pt")] public static extern void switch_rtp_set_cng_pt(HandleRef jarg1, byte jarg2); diff --git a/src/mod/languages/mod_perl/freeswitch.pm b/src/mod/languages/mod_perl/freeswitch.pm index cb31101489..e55ffac7de 100644 --- a/src/mod/languages/mod_perl/freeswitch.pm +++ b/src/mod/languages/mod_perl/freeswitch.pm @@ -50,6 +50,7 @@ package freeswitch; *consoleLog = *freeswitchc::consoleLog; *consoleCleanLog = *freeswitchc::consoleCleanLog; +*email = *freeswitchc::email; *console_log = *freeswitchc::console_log; *console_clean_log = *freeswitchc::console_clean_log; *msleep = *freeswitchc::msleep; diff --git a/src/mod/languages/mod_perl/mod_perl_wrap.cpp b/src/mod/languages/mod_perl/mod_perl_wrap.cpp index e8a4b9f409..9be038585a 100644 --- a/src/mod/languages/mod_perl/mod_perl_wrap.cpp +++ b/src/mod/languages/mod_perl/mod_perl_wrap.cpp @@ -1566,6 +1566,19 @@ SWIG_AsCharPtrAndSize(SV *obj, char** cptr, size_t* psize, int *alloc) +SWIGINTERNINLINE SV * +SWIG_From_bool SWIG_PERL_DECL_ARGS_1(bool value) +{ + SV *obj = sv_newmortal(); + if (value) { + sv_setsv(obj, &PL_sv_yes); + } else { + sv_setsv(obj, &PL_sv_no); + } + return obj; +} + + #include #if !defined(SWIG_NO_LLONG_MAX) # if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) @@ -1780,19 +1793,6 @@ SWIG_From_char SWIG_PERL_DECL_ARGS_1(char c) } -SWIGINTERNINLINE SV * -SWIG_From_bool SWIG_PERL_DECL_ARGS_1(bool value) -{ - SV *obj = sv_newmortal(); - if (value) { - sv_setsv(obj, &PL_sv_yes); - } else { - sv_setsv(obj, &PL_sv_no); - } - return obj; -} - - SWIGINTERN int SWIG_AsVal_unsigned_SS_long SWIG_PERL_DECL_ARGS_2(SV *obj, unsigned long *val) { @@ -1982,6 +1982,111 @@ XS(_wrap_consoleCleanLog) { } +XS(_wrap_email) { + { + char *arg1 = (char *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; + char *arg5 = (char *) NULL ; + char *arg6 = (char *) NULL ; + char *arg7 = (char *) NULL ; + bool result; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + int res5 ; + char *buf5 = 0 ; + int alloc5 = 0 ; + int res6 ; + char *buf6 = 0 ; + int alloc6 = 0 ; + int res7 ; + char *buf7 = 0 ; + int alloc7 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 2) || (items > 7)) { + SWIG_croak("Usage: email(to,from,headers,body,file,convert_cmd,convert_ext);"); + } + res1 = SWIG_AsCharPtrAndSize(ST(0), &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "email" "', argument " "1"" of type '" "char *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + res2 = SWIG_AsCharPtrAndSize(ST(1), &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "email" "', argument " "2"" of type '" "char *""'"); + } + arg2 = reinterpret_cast< char * >(buf2); + if (items > 2) { + res3 = SWIG_AsCharPtrAndSize(ST(2), &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "email" "', argument " "3"" of type '" "char *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + } + if (items > 3) { + res4 = SWIG_AsCharPtrAndSize(ST(3), &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "email" "', argument " "4"" of type '" "char *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + } + if (items > 4) { + res5 = SWIG_AsCharPtrAndSize(ST(4), &buf5, NULL, &alloc5); + if (!SWIG_IsOK(res5)) { + SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "email" "', argument " "5"" of type '" "char *""'"); + } + arg5 = reinterpret_cast< char * >(buf5); + } + if (items > 5) { + res6 = SWIG_AsCharPtrAndSize(ST(5), &buf6, NULL, &alloc6); + if (!SWIG_IsOK(res6)) { + SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "email" "', argument " "6"" of type '" "char *""'"); + } + arg6 = reinterpret_cast< char * >(buf6); + } + if (items > 6) { + res7 = SWIG_AsCharPtrAndSize(ST(6), &buf7, NULL, &alloc7); + if (!SWIG_IsOK(res7)) { + SWIG_exception_fail(SWIG_ArgError(res7), "in method '" "email" "', argument " "7"" of type '" "char *""'"); + } + arg7 = reinterpret_cast< char * >(buf7); + } + result = (bool)email(arg1,arg2,arg3,arg4,arg5,arg6,arg7); + ST(argvi) = SWIG_From_bool SWIG_PERL_CALL_ARGS_1(static_cast< bool >(result)); argvi++ ; + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + if (alloc5 == SWIG_NEWOBJ) delete[] buf5; + if (alloc6 == SWIG_NEWOBJ) delete[] buf6; + if (alloc7 == SWIG_NEWOBJ) delete[] buf7; + XSRETURN(argvi); + fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + if (alloc5 == SWIG_NEWOBJ) delete[] buf5; + if (alloc6 == SWIG_NEWOBJ) delete[] buf6; + if (alloc7 == SWIG_NEWOBJ) delete[] buf7; + SWIG_croak_null(); + } +} + + XS(_wrap_new_IVRMenu) { { IVRMenu *arg1 = (IVRMenu *) 0 ; @@ -9336,6 +9441,7 @@ static swig_variable_info swig_variables[] = { static swig_command_info swig_commands[] = { {"freeswitchc::consoleLog", _wrap_consoleLog}, {"freeswitchc::consoleCleanLog", _wrap_consoleCleanLog}, +{"freeswitchc::email", _wrap_email}, {"freeswitchc::new_IVRMenu", _wrap_new_IVRMenu}, {"freeswitchc::delete_IVRMenu", _wrap_delete_IVRMenu}, {"freeswitchc::IVRMenu_bindAction", _wrap_IVRMenu_bindAction}, @@ -9793,17 +9899,17 @@ XS(SWIG_init) { SWIG_TypeClientData(SWIGTYPE_p_IVRMenu, (void*) "freeswitch::IVRMenu"); SWIG_TypeClientData(SWIGTYPE_p_API, (void*) "freeswitch::API"); SWIG_TypeClientData(SWIGTYPE_p_input_callback_state, (void*) "freeswitch::input_callback_state_t"); - /*@SWIG:/usr/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { + /*@SWIG:/usr/local/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { SV *sv = get_sv((char*) SWIG_prefix "S_HUP", TRUE | 0x2 | GV_ADDMULTI); sv_setsv(sv, SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(S_HUP))); SvREADONLY_on(sv); } while(0) /*@SWIG@*/; - /*@SWIG:/usr/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { + /*@SWIG:/usr/local/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { SV *sv = get_sv((char*) SWIG_prefix "S_FREE", TRUE | 0x2 | GV_ADDMULTI); sv_setsv(sv, SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(S_FREE))); SvREADONLY_on(sv); } while(0) /*@SWIG@*/; - /*@SWIG:/usr/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { + /*@SWIG:/usr/local/share/swig/1.3.35/perl5/perltypemaps.swg,64,%set_constant@*/ do { SV *sv = get_sv((char*) SWIG_prefix "S_RDLOCK", TRUE | 0x2 | GV_ADDMULTI); sv_setsv(sv, SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(S_RDLOCK))); SvREADONLY_on(sv); diff --git a/src/mod/languages/mod_python/freeswitch.py b/src/mod/languages/mod_python/freeswitch.py index 03deb31d13..d49016d305 100644 --- a/src/mod/languages/mod_python/freeswitch.py +++ b/src/mod/languages/mod_python/freeswitch.py @@ -50,6 +50,7 @@ del types consoleLog = _freeswitch.consoleLog consoleCleanLog = _freeswitch.consoleCleanLog +email = _freeswitch.email class IVRMenu(_object): __swig_setmethods__ = {} __setattr__ = lambda self, name, value: _swig_setattr(self, IVRMenu, name, value) diff --git a/src/mod/languages/mod_python/mod_python_wrap.cpp b/src/mod/languages/mod_python/mod_python_wrap.cpp index 3326a19dcd..ccb5cac78e 100644 --- a/src/mod/languages/mod_python/mod_python_wrap.cpp +++ b/src/mod/languages/mod_python/mod_python_wrap.cpp @@ -2687,6 +2687,13 @@ SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) +SWIGINTERNINLINE PyObject* + SWIG_From_bool (bool value) +{ + return PyBool_FromLong(value ? 1 : 0); +} + + #include #if !defined(SWIG_NO_LLONG_MAX) # if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) @@ -2916,13 +2923,6 @@ SWIG_From_char (char c) } -SWIGINTERNINLINE PyObject* - SWIG_From_bool (bool value) -{ - return PyBool_FromLong(value ? 1 : 0); -} - - SWIGINTERN int SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) { @@ -3071,6 +3071,113 @@ fail: } +SWIGINTERN PyObject *_wrap_email(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + char *arg1 = (char *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; + char *arg5 = (char *) NULL ; + char *arg6 = (char *) NULL ; + char *arg7 = (char *) NULL ; + bool result; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + int res5 ; + char *buf5 = 0 ; + int alloc5 = 0 ; + int res6 ; + char *buf6 = 0 ; + int alloc6 = 0 ; + int res7 ; + char *buf7 = 0 ; + int alloc7 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"OO|OOOOO:email",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "email" "', argument " "1"" of type '" "char *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "email" "', argument " "2"" of type '" "char *""'"); + } + arg2 = reinterpret_cast< char * >(buf2); + if (obj2) { + res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "email" "', argument " "3"" of type '" "char *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + } + if (obj3) { + res4 = SWIG_AsCharPtrAndSize(obj3, &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "email" "', argument " "4"" of type '" "char *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + } + if (obj4) { + res5 = SWIG_AsCharPtrAndSize(obj4, &buf5, NULL, &alloc5); + if (!SWIG_IsOK(res5)) { + SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "email" "', argument " "5"" of type '" "char *""'"); + } + arg5 = reinterpret_cast< char * >(buf5); + } + if (obj5) { + res6 = SWIG_AsCharPtrAndSize(obj5, &buf6, NULL, &alloc6); + if (!SWIG_IsOK(res6)) { + SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "email" "', argument " "6"" of type '" "char *""'"); + } + arg6 = reinterpret_cast< char * >(buf6); + } + if (obj6) { + res7 = SWIG_AsCharPtrAndSize(obj6, &buf7, NULL, &alloc7); + if (!SWIG_IsOK(res7)) { + SWIG_exception_fail(SWIG_ArgError(res7), "in method '" "email" "', argument " "7"" of type '" "char *""'"); + } + arg7 = reinterpret_cast< char * >(buf7); + } + result = (bool)email(arg1,arg2,arg3,arg4,arg5,arg6,arg7); + resultobj = SWIG_From_bool(static_cast< bool >(result)); + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + if (alloc5 == SWIG_NEWOBJ) delete[] buf5; + if (alloc6 == SWIG_NEWOBJ) delete[] buf6; + if (alloc7 == SWIG_NEWOBJ) delete[] buf7; + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + if (alloc5 == SWIG_NEWOBJ) delete[] buf5; + if (alloc6 == SWIG_NEWOBJ) delete[] buf6; + if (alloc7 == SWIG_NEWOBJ) delete[] buf7; + return NULL; +} + + SWIGINTERN PyObject *_wrap_new_IVRMenu(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; IVRMenu *arg1 = (IVRMenu *) 0 ; @@ -8878,6 +8985,7 @@ SWIGINTERN PyObject *Session_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObje static PyMethodDef SwigMethods[] = { { (char *)"consoleLog", _wrap_consoleLog, METH_VARARGS, NULL}, { (char *)"consoleCleanLog", _wrap_consoleCleanLog, METH_VARARGS, NULL}, + { (char *)"email", _wrap_email, METH_VARARGS, NULL}, { (char *)"new_IVRMenu", _wrap_new_IVRMenu, METH_VARARGS, NULL}, { (char *)"delete_IVRMenu", _wrap_delete_IVRMenu, METH_VARARGS, NULL}, { (char *)"IVRMenu_bindAction", _wrap_IVRMenu_bindAction, METH_VARARGS, NULL}, From 3a10d6a1c51ffc1aba78e7864fc8ba707494bb9d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 14:54:13 -0600 Subject: [PATCH 047/154] use strdup instead of core_session_strdup in hangup hook --- src/switch_core_state_machine.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 276acb8585..e2d50704b5 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -468,7 +468,7 @@ static void api_hook(switch_core_session_t *session, const char *hook_var, int u { if (!zstr(hook_var)) { switch_stream_handle_t stream = { 0 }; - char *cmd = switch_core_session_strdup(session, hook_var); + char *cmd = strdup(hook_var); char *arg = NULL; char *expanded = NULL; @@ -485,7 +485,7 @@ static void api_hook(switch_core_session_t *session, const char *hook_var, int u switch_channel_get_variables(session->channel, &stream.param_event); switch_channel_event_set_data(session->channel, stream.param_event); - expanded = switch_channel_expand_variables(session->channel, arg); + expanded = switch_event_expand_headers(stream.param_event, arg); switch_api_execute(cmd, expanded, use_session ? session : NULL, &stream); @@ -496,6 +496,9 @@ static void api_hook(switch_core_session_t *session, const char *hook_var, int u if (expanded != arg) { switch_safe_free(expanded); } + + switch_safe_free(cmd); + switch_safe_free(stream.data); } } From 61d3c56fdeed7b2a6c8000dda2343d9a1a693677 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 15:29:22 -0600 Subject: [PATCH 048/154] fix jb + no timer situations --- src/switch_rtp.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index f06771880c..029f067ab5 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -235,7 +235,6 @@ struct switch_rtp { uint32_t sync_packets; int rtcp_interval; switch_bool_t rtcp_fresh_frame; - uint8_t checked_jb; #ifdef ENABLE_ZRTP zrtp_session_t *zrtp_session; zrtp_profile_t *zrtp_profile; @@ -2500,7 +2499,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes = 0; } - if (rtp_session->jb && !rtp_session->pause_jb && !rtp_session->checked_jb) { + if (rtp_session->jb && !rtp_session->pause_jb) { if ((jb_frame = stfu_n_read_a_frame(rtp_session->jb))) { memcpy(rtp_session->recv_msg.body, jb_frame->data, jb_frame->dlen); @@ -2514,8 +2513,6 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->recv_msg.header.pt = jb_frame->pt; status = SWITCH_STATUS_SUCCESS; } - - rtp_session->checked_jb++; } return status; @@ -2660,8 +2657,6 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ READ_INC(rtp_session); - rtp_session->checked_jb = 0; - while (switch_rtp_ready(rtp_session)) { int do_cng = 0; int read_pretriggered = 0; From c9aab94aa428bec3e9354abb47cbdd481ad84384 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 24 Feb 2011 22:25:59 +0100 Subject: [PATCH 049/154] Skinny: use a const for SKINNY_KEY_SET_IN_USE_HINT --- src/mod/endpoints/mod_skinny/mod_skinny.c | 2 +- src/mod/endpoints/mod_skinny/skinny_tables.c | 1 + src/mod/endpoints/mod_skinny/skinny_tables.h | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 91d81dacf2..46acdb6bac 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -651,7 +651,7 @@ int channel_on_routing_callback(void *pArg, int argc, char **argv, char **column } else { send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON); skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY); - send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0xffff); + send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, SKINNY_KEY_SET_IN_USE_HINT, 0xffff); send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE, line_instance, helper->tech_pvt->call_id); skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance); diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index 466a70538d..4488b7571c 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -216,6 +216,7 @@ struct skinny_table SKINNY_KEY_SETS[] = { {"KeySetConnectedWithConference", SKINNY_KEY_SET_CONNECTED_WITH_CONFERENCE}, {"KeySetRingOut", SKINNY_KEY_SET_RING_OUT}, {"KeySetOffHookWithFeatures", SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES}, + {"KeySetInUseHint", SKINNY_KEY_SET_IN_USE_HINT}, {NULL, 0} }; SKINNY_DECLARE_ID2STR(skinny_soft_key_set2str, SKINNY_KEY_SETS, "UNKNOWN_SOFT_KEY_SET") diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.h b/src/mod/endpoints/mod_skinny/skinny_tables.h index bc92f9f4bf..e1144a9203 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.h +++ b/src/mod/endpoints/mod_skinny/skinny_tables.h @@ -210,8 +210,9 @@ enum skinny_key_set { SKINNY_KEY_SET_CONNECTED_WITH_CONFERENCE = 7, SKINNY_KEY_SET_RING_OUT = 8, SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES = 9, + SKINNY_KEY_SET_IN_USE_HINT = 10, }; -extern struct skinny_table SKINNY_KEY_SETS[11]; +extern struct skinny_table SKINNY_KEY_SETS[12]; const char *skinny_soft_key_set2str(uint32_t id); uint32_t skinny_str2soft_key_set(const char *str); #define SKINNY_PUSH_SOFT_KEY_SETS SKINNY_DECLARE_PUSH_MATCH(SKINNY_KEY_SETS) From cffd62aa36840f1b132ee60cf012aeb5f3542883 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 24 Feb 2011 22:32:19 +0100 Subject: [PATCH 050/154] Skinny: complete list of softkeysets --- conf/skinny_profiles/internal.xml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/conf/skinny_profiles/internal.xml b/conf/skinny_profiles/internal.xml index 52da89741d..5ea6a56439 100644 --- a/conf/skinny_profiles/internal.xml +++ b/conf/skinny_profiles/internal.xml @@ -16,12 +16,16 @@ - - - + + + + + + + From 2148d81d74e18c49e92375c1cdc970a08d7fac21 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 24 Feb 2011 23:53:53 +0100 Subject: [PATCH 051/154] Skinny: avoid crash when loading config with blank soft-key-set value --- src/mod/endpoints/mod_skinny/mod_skinny.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 46acdb6bac..4061b94518 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1811,6 +1811,9 @@ static switch_status_t load_skinny_config(void) size_t string_len = strlen(val); size_t string_pos, start = 0; int field_no = 0; + if (zstr(val)) { + continue; + } if (soft_key_set_id > 15) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "soft-key-set name '%s' is greater than 15 in soft-key-set-set '%s' in profile %s.\n", From 646ad887d7d91388e68330374ab1cc7d1632352b Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Fri, 25 Feb 2011 00:05:46 +0100 Subject: [PATCH 052/154] Skinny: enhance soft-key-sets --- conf/skinny_profiles/internal.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/skinny_profiles/internal.xml b/conf/skinny_profiles/internal.xml index 5ea6a56439..0ca4ed0c46 100644 --- a/conf/skinny_profiles/internal.xml +++ b/conf/skinny_profiles/internal.xml @@ -20,12 +20,12 @@ - + - - + + - + From 269906c8918dfb4247e4087fbb0e5390c4d44fa6 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 18:41:07 -0500 Subject: [PATCH 053/154] freetdm: Added support for hardware (native) R2 MF generation --- libs/freetdm/Makefile.am | 2 +- libs/freetdm/src/ftdm_cpu_monitor.c | 6 +- libs/freetdm/src/ftdm_dso.c | 2 +- libs/freetdm/src/ftdm_threadmutex.c | 6 +- .../src/ftmod/ftmod_r2/ftmod_r2.2008.vcproj | 4 + libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 39 ++++- .../src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c | 163 ++++++++++++++++++ .../src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.h | 73 ++++++++ libs/freetdm/src/include/freetdm.h | 13 ++ libs/freetdm/src/include/ftdm_dso.h | 2 +- libs/freetdm/src/include/private/ftdm_types.h | 1 + 11 files changed, 298 insertions(+), 13 deletions(-) mode change 100644 => 100755 libs/freetdm/src/ftdm_dso.c mode change 100644 => 100755 libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c create mode 100755 libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c create mode 100755 libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.h mode change 100644 => 100755 libs/freetdm/src/include/freetdm.h mode change 100644 => 100755 libs/freetdm/src/include/ftdm_dso.h mode change 100644 => 100755 libs/freetdm/src/include/private/ftdm_types.h diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index a26035b2ef..4b08dd3d3c 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -248,7 +248,7 @@ endif if HAVE_OPENR2 mod_LTLIBRARIES += ftmod_r2.la -ftmod_r2_la_SOURCES = $(SRC)/ftmod/ftmod_r2/ftmod_r2.c +ftmod_r2_la_SOURCES = $(SRC)/ftmod/ftmod_r2/ftmod_r2.c $(SRC)/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c ftmod_r2_la_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS) ftmod_r2_la_LDFLAGS = -shared -module -avoid-version -lopenr2 ftmod_r2_la_LIBADD = libfreetdm.la diff --git a/libs/freetdm/src/ftdm_cpu_monitor.c b/libs/freetdm/src/ftdm_cpu_monitor.c index 4ebe353b57..b543f05210 100644 --- a/libs/freetdm/src/ftdm_cpu_monitor.c +++ b/libs/freetdm/src/ftdm_cpu_monitor.c @@ -37,8 +37,10 @@ */ #ifdef WIN32 -#define _WIN32_WINNT 0x0501 // To make GetSystemTimes visible in windows.h -#include +# if (_WIN32_WINNT < 0x0501) +# error "Need to target at least Windows XP/Server 2003 because GetSystemTimes is needed" +# endif +# include #else /* LINUX */ #include diff --git a/libs/freetdm/src/ftdm_dso.c b/libs/freetdm/src/ftdm_dso.c old mode 100644 new mode 100755 index 6e7403165d..5b33a44347 --- a/libs/freetdm/src/ftdm_dso.c +++ b/libs/freetdm/src/ftdm_dso.c @@ -122,5 +122,5 @@ FT_DECLARE(void *) ftdm_dso_func_sym(ftdm_dso_lib_t lib, const char *sym, char * * c-basic-offset:4 * End: * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: */ diff --git a/libs/freetdm/src/ftdm_threadmutex.c b/libs/freetdm/src/ftdm_threadmutex.c index 6efa27714c..57a8fb4830 100644 --- a/libs/freetdm/src/ftdm_threadmutex.c +++ b/libs/freetdm/src/ftdm_threadmutex.c @@ -22,8 +22,10 @@ */ #ifdef WIN32 -/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ -#define _WIN32_WINNT 0x0400 +# if (_WIN32_WINNT < 0x0400) +# error "Need to target at least Windows 95/WINNT 4.0 because TryEnterCriticalSection is needed" +# endif +# include #endif #include "private/ftdm_core.h" diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.2008.vcproj index 8942a8f5b6..565a31e505 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.2008.vcproj +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.2008.vcproj @@ -185,6 +185,10 @@ RelativePath=".\ftmod_r2.c" > + + diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c old mode 100644 new mode 100755 index 540d7cb6e9..3908535a67 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -47,8 +47,10 @@ #endif #include #include -#include "freetdm.h" -#include "private/ftdm_core.h" +#include +#include + +#include "ftmod_r2_io_mf_lib.h" // ftdm_r2_get_native_channel_mf_generation_iface /* when the user stops a span, we clear FTDM_R2_SPAN_STARTED, so that the signaling thread * knows it must stop, and we wait for FTDM_R2_RUNNING to be clear, which tells us the @@ -105,6 +107,7 @@ typedef struct ft_r2_conf_s { int charge_calls; int forced_release; int allow_collect_calls; + int use_channel_native_mf_generation; } ft_r2_conf_t; /* r2 configuration stored in span->signal_data */ @@ -1447,7 +1450,8 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) /* .double_answer */ -1, /* .charge_calls */ -1, /* .forced_release */ -1, - /* .allow_collect_calls */ -1 + /* .allow_collect_calls */ -1, + /* .use_channel_native_mf_generation */ 0 }; ftdm_assert_return(sig_cb != NULL, FTDM_FAIL, "No signaling cb provided\n"); @@ -1566,6 +1570,9 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) } else if (!strcasecmp(var, "max_dnis")) { r2conf.max_dnis = atoi(val); ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with max dnis = %d\n", span->name, r2conf.max_dnis); + } else if (!strcasecmp(var, "use_channel_native_mf_generation")) { + r2conf.use_channel_native_mf_generation = ftdm_true(val); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with \"use native channel MF generation\" = %d\n", span->name, r2conf.use_channel_native_mf_generation); } else { snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var); return FTDM_FAIL; @@ -1617,6 +1624,10 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file); } + if(r2conf.use_channel_native_mf_generation) { + openr2_context_set_mflib_interface(r2data->r2context, ftdm_r2_get_native_channel_mf_generation_iface()); + } + spanpvt->r2calls = create_hashtable(FTDM_MAX_CHANNELS_SPAN, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); if (!spanpvt->r2calls) { snprintf(span->last_error, sizeof(span->last_error), "Cannot create channel calls hash for span."); @@ -1634,13 +1645,29 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) openr2_chan_enable_call_files(r2chan); } - r2call = ftdm_malloc(sizeof(*r2call)); + if (r2conf.use_channel_native_mf_generation) { + /* Allocate a new write handle per r2chan */ + ftdm_r2_mf_write_handle_t *mf_write_handle = ftdm_calloc(1, sizeof(*mf_write_handle)); + /* Associate to the FreeTDM channel */ + mf_write_handle->ftdmchan = span->channels[i]; + /* Make sure the FreeTDM channel supports MF the generation feature */ + if (!ftdm_channel_test_feature(mf_write_handle->ftdmchan, FTDM_CHANNEL_FEATURE_MF_GENERATE)) { + ftdm_log_chan_msg(mf_write_handle->ftdmchan, FTDM_LOG_ERROR, + "FreeTDM channel does not support native MF generation: " + "\"use_channel_native_mf_generation\" configuration parameter cannot" + " be used\n"); + goto fail; + } + /* Associate the mf_write_handle to the openR2 channel */ + openr2_chan_set_mflib_handles(r2chan, mf_write_handle, NULL); + } + + r2call = ftdm_calloc(1, sizeof(*r2call)); if (!r2call) { snprintf(span->last_error, sizeof(span->last_error), "Cannot create all R2 call data structures for the span."); ftdm_safe_free(r2chan); goto fail; } - memset(r2call, 0, sizeof(*r2call)); openr2_chan_set_logging_func(r2chan, ftdm_r2_on_chan_log); openr2_chan_set_client_data(r2chan, span->channels[i]); r2call->r2chan = r2chan; @@ -2370,5 +2397,5 @@ EX_DECLARE_DATA ftdm_module_t ftdm_module = { * c-basic-offset:4 * End: * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: */ diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c new file mode 100755 index 0000000000..961da2cede --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011 Sebastien Trottier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include + +#include "ftmod_r2_io_mf_lib.h" + +/* Convert openr2 MF tone enum value to FreeTDM MF tone value + 1-15 bitwise OR FTDM_MF_DIRECTION_FORWARD/BACKWARD + 0 (stop playing) + openr2_mf_tone_t defined in r2proto.h +*/ +static int ftdm_r2_openr2_mf_tone_to_ftdm_mf_tone(openr2_mf_tone_t + openr2_tone_value, int forward_signals) +{ + int tone; + + switch (openr2_tone_value) { + case 0: return 0; +#define TONE_FROM_NAME(name) case OR2_MF_TONE_##name: tone = name; break; + TONE_FROM_NAME(1) + TONE_FROM_NAME(2) + TONE_FROM_NAME(3) + TONE_FROM_NAME(4) + TONE_FROM_NAME(5) + TONE_FROM_NAME(6) + TONE_FROM_NAME(7) + TONE_FROM_NAME(8) + TONE_FROM_NAME(9) + TONE_FROM_NAME(10) + TONE_FROM_NAME(11) + TONE_FROM_NAME(12) + TONE_FROM_NAME(13) + TONE_FROM_NAME(14) + TONE_FROM_NAME(15) +#undef TONE_FROM_NAME + default: + ftdm_assert(0, "Invalid openr2_tone_value\n"); + return -1; + } + + /* Add flag corresponding to direction */ + if (forward_signals) { + tone |= FTDM_MF_DIRECTION_FORWARD; + } else { + tone |= FTDM_MF_DIRECTION_BACKWARD; + } + + return tone; +} + +/* MF generation routines (using IO command of a FreeTDM channel) + write_init stores the direction of the MF to generate */ +static void *ftdm_r2_io_mf_write_init(ftdm_r2_mf_write_handle_t *handle, int forward_signals) +{ + ftdm_log_chan(handle->ftdmchan, FTDM_LOG_DEBUG, "ftdm_r2_io_mf_write_init, " + "forward = %d\n", forward_signals); + + handle->fwd = forward_signals; + return handle; +} + +static int ftdm_r2_io_mf_generate_tone(ftdm_r2_mf_write_handle_t *handle, int16_t buffer[], int samples) +{ + /* Our mf_want_generate implementation always return 0, so mf_generate_tone should never be called */ + ftdm_assert(0, "ftdm_r2_io_mf_generate_tone not implemented\n"); + return 0; +} + +/* \brief mf_select_tone starts tone generation or stops current tone + * \return 0 on success, -1 on error + */ +static int ftdm_r2_io_mf_select_tone(ftdm_r2_mf_write_handle_t *handle, char signal) +{ + int tone; /* (0, 1-15) (0 meaning to stop playing) */ + + ftdm_log_chan(handle->ftdmchan, FTDM_LOG_DEBUG, "ftdm_r2_io_mf_select_tone, " + "signal = %c\n", signal); + + if (-1 == (tone = ftdm_r2_openr2_mf_tone_to_ftdm_mf_tone(signal, handle->fwd))) { + return -1; + } + + /* Start/stop playback directly here, as select tone is called each time a tone + is started or stopped (called if tone changes, but silence is tone 0, + triggering a tone change) */ + if (tone > 0) { + ftdm_channel_command(handle->ftdmchan, FTDM_COMMAND_START_MF_PLAYBACK, &tone); + } else { + /* tone 0 means to stop current tone */ + ftdm_channel_command(handle->ftdmchan, FTDM_COMMAND_STOP_MF_PLAYBACK, NULL); + } + return 0; +} + +static int ftdm_r2_io_mf_want_generate(ftdm_r2_mf_write_handle_t *handle, int signal) +{ + /* Return 0, meaning mf_generate_tone doesn't need to be called */ + return 0; +} + +/* MF lib interface that generate MF tones via FreeTDM channel IO commands + MF detection using the default openr2 provider (r2engine) */ +static openr2_mflib_interface_t g_mf_ftdm_io_iface = { + /* .mf_read_init */ (openr2_mf_read_init_func)openr2_mf_rx_init, + /* .mf_write_init */ (openr2_mf_write_init_func)ftdm_r2_io_mf_write_init, + /* .mf_detect_tone */ (openr2_mf_detect_tone_func)openr2_mf_rx, + /* .mf_generate_tone */ (openr2_mf_generate_tone_func)ftdm_r2_io_mf_generate_tone, + /* .mf_select_tone */ (openr2_mf_select_tone_func)ftdm_r2_io_mf_select_tone, + /* .mf_want_generate */ (openr2_mf_want_generate_func)ftdm_r2_io_mf_want_generate, + /* .mf_read_dispose */ NULL, + /* .mf_write_dispose */ NULL +}; + +openr2_mflib_interface_t *ftdm_r2_get_native_channel_mf_generation_iface() +{ + return &g_mf_ftdm_io_iface; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.h b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.h new file mode 100755 index 0000000000..10fe3f3aab --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Sebastien Trottier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FTMOD_R2_IO_MFLIB_H_ +#define _FTMOD_R2_IO_MFLIB_H_ + +#include + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* MFC/R2 tone generator handle (mf_write_handle) */ +typedef struct { + /*! FTDM channel performing the MF generation */ + ftdm_channel_t *ftdmchan; + /*! 1 if generating forward tones, otherwise generating reverse tones. */ + int fwd; +} ftdm_r2_mf_write_handle_t; + +/* MF lib interface that generate MF tones via FreeTDM channel IO commands + MF detection using the default openr2 provider (r2engine) */ +openr2_mflib_interface_t *ftdm_r2_get_native_channel_mf_generation_iface(void); + +#if defined(__cplusplus) +} /* endif extern "C" */ +#endif + +#endif /* endif defined _FTMOD_R2_IO_MFLIB_H_ */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h old mode 100644 new mode 100755 index 77abf6ab3e..b1bcd46ae5 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -673,6 +673,9 @@ typedef enum { FTDM_COMMAND_SET_RX_QUEUE_SIZE = 54, FTDM_COMMAND_SET_TX_QUEUE_SIZE = 55, FTDM_COMMAND_SET_POLARITY = 56, + FTDM_COMMAND_START_MF_PLAYBACK = 57, + FTDM_COMMAND_STOP_MF_PLAYBACK = 58, + FTDM_COMMAND_COUNT, } ftdm_command_t; @@ -847,6 +850,16 @@ typedef enum { FTDM_ALARM_GENERAL = (1 << 30) } ftdm_alarm_flag_t; +/*! \brief MF generation direction flags + * \note Used in bitwise OR with channel ID as argument to MF_PLAYBACK I/O command, so value must be higher that 255 + * \see FTDM_COMMAND_START_MF_PLAYBACK + * */ + +typedef enum { + FTDM_MF_DIRECTION_FORWARD = (1 << 8), + FTDM_MF_DIRECTION_BACKWARD = (1 << 9) +} ftdm_mf_direction_flag_t; + /*! \brief Override the default queue handler */ FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *handler); diff --git a/libs/freetdm/src/include/ftdm_dso.h b/libs/freetdm/src/include/ftdm_dso.h old mode 100644 new mode 100755 index b56ad93e39..8cdc13cba4 --- a/libs/freetdm/src/include/ftdm_dso.h +++ b/libs/freetdm/src/include/ftdm_dso.h @@ -49,6 +49,6 @@ FT_DECLARE(char *) ftdm_build_dso_path(const char *name, char *path, ftdm_size_t * c-basic-offset:4 * End: * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: */ diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h old mode 100644 new mode 100755 index 9e8df1f5f2..b263b64a2d --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -203,6 +203,7 @@ typedef enum { FTDM_CHANNEL_FEATURE_HWEC = (1<<7), /*!< Channel has a hardware echo canceller */ FTDM_CHANNEL_FEATURE_HWEC_DISABLED_ON_IDLE = (1<<8), /*!< hardware echo canceller is disabled when there are no calls on this channel */ FTDM_CHANNEL_FEATURE_IO_STATS = (1<<9), /*!< Channel supports IO statistics (HDLC channels only) */ + FTDM_CHANNEL_FEATURE_MF_GENERATE = (1<<10), /*!< Channel can generate R2 MF tones (read-only) */ } ftdm_channel_feature_t; /*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */ From 327def8c219f9be9a33fe4e14205da7e62c0a4c0 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 18:45:54 -0500 Subject: [PATCH 054/154] freetdm: Do not move to dialing if signaling module already moved the state --- libs/freetdm/src/ftdm_io.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index bd7b043a34..251d9d4989 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -2410,6 +2410,8 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func return FTDM_SUCCESS; } +/* this function MUST be called with the channel lock held with lock recursivity of 1 exactly, + * and the caller must be aware we might unlock the channel for a brief period of time and then lock it again */ static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_FAIL; @@ -2452,10 +2454,22 @@ static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *f ftdm_set_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED); ftdm_call_set_call_id(ftdmchan, &ftdmchan->caller_data); - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 1); - } else { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0); + + /* if the signaling stack left the channel in state down on success, is expecting us to move to DIALING */ + if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 1); + } else { + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0); + } + } else if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) && + !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { + + ftdm_channel_unlock(ftdmchan); + + ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, 500); + + ftdm_channel_lock(ftdmchan); } done: @@ -2472,6 +2486,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char ftdm_status_t status; ftdm_channel_lock(ftdmchan); + /* be aware that _ftdm_channl_call_place_nl can unlock/lock the channel quickly if working in blocking mode */ status = _ftdm_channel_call_place_nl(file, func, line, ftdmchan); ftdm_channel_unlock(ftdmchan); @@ -2515,6 +2530,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, i ftdm_channel_set_caller_data(fchan, caller_data); + /* be aware that _ftdm_channl_call_place_nl can unlock/lock the channel quickly if working in blocking mode */ status = _ftdm_channel_call_place_nl(file, func, line, fchan); if (status != FTDM_SUCCESS) { _ftdm_channel_call_hangup_nl(file, func, line, fchan); From e96acac3c803d00d5fe0802dfa1e17865290e50e Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 17:48:36 -0600 Subject: [PATCH 055/154] add optional job-uuid param to bgapi in oop mod --- libs/esl/java/esl_wrap.cpp | 11 +++++++++-- .../org/freeswitch/esl/ESLconnection.java | 4 ++-- libs/esl/java/org/freeswitch/esl/eslJNI.java | 2 +- libs/esl/lua/esl_wrap.cpp | 9 +++++++-- libs/esl/managed/ESLPINVOKE.cs | 2 +- libs/esl/managed/ESLconnection.cs | 4 ++-- libs/esl/managed/esl_wrap.cpp | 6 ++++-- libs/esl/perl/esl_wrap.cpp | 19 ++++++++++++++++--- libs/esl/php/ESL.php | 5 +++-- libs/esl/php/esl_wrap.cpp | 14 +++++++++++--- libs/esl/python/esl_wrap.cpp | 18 ++++++++++++++++-- libs/esl/ruby/esl_wrap.cpp | 17 +++++++++++++++-- libs/esl/src/esl_oop.cpp | 11 ++++++++--- libs/esl/src/include/esl_oop.h | 2 +- 14 files changed, 96 insertions(+), 28 deletions(-) diff --git a/libs/esl/java/esl_wrap.cpp b/libs/esl/java/esl_wrap.cpp index d508aa2074..9a764dd030 100644 --- a/libs/esl/java/esl_wrap.cpp +++ b/libs/esl/java/esl_wrap.cpp @@ -790,11 +790,12 @@ SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1api(JNIEn } -SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1bgapi(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2, jstring jarg3) { +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1bgapi(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2, jstring jarg3, jstring jarg4) { jlong jresult = 0 ; ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; (void)jenv; @@ -811,10 +812,16 @@ SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1bgapi(JNI arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); if (!arg3) return 0; } - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + arg4 = 0; + if (jarg4) { + arg4 = (char *)jenv->GetStringUTFChars(jarg4, 0); + if (!arg4) return 0; + } + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); *(ESLevent **)&jresult = result; if (arg2) jenv->ReleaseStringUTFChars(jarg2, (const char *)arg2); if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + if (arg4) jenv->ReleaseStringUTFChars(jarg4, (const char *)arg4); return jresult; } diff --git a/libs/esl/java/org/freeswitch/esl/ESLconnection.java b/libs/esl/java/org/freeswitch/esl/ESLconnection.java index 6dbc37728b..e20509bf10 100644 --- a/libs/esl/java/org/freeswitch/esl/ESLconnection.java +++ b/libs/esl/java/org/freeswitch/esl/ESLconnection.java @@ -72,8 +72,8 @@ public class ESLconnection { return (cPtr == 0) ? null : new ESLevent(cPtr, true); } - public ESLevent bgapi(String cmd, String arg) { - long cPtr = eslJNI.ESLconnection_bgapi(swigCPtr, this, cmd, arg); + public ESLevent bgapi(String cmd, String arg, String job_uuid) { + long cPtr = eslJNI.ESLconnection_bgapi(swigCPtr, this, cmd, arg, job_uuid); return (cPtr == 0) ? null : new ESLevent(cPtr, true); } diff --git a/libs/esl/java/org/freeswitch/esl/eslJNI.java b/libs/esl/java/org/freeswitch/esl/eslJNI.java index c08ee2f83d..e6e576f0dc 100644 --- a/libs/esl/java/org/freeswitch/esl/eslJNI.java +++ b/libs/esl/java/org/freeswitch/esl/eslJNI.java @@ -39,7 +39,7 @@ class eslJNI { public final static native int ESLconnection_send(long jarg1, ESLconnection jarg1_, String jarg2); public final static native long ESLconnection_sendRecv(long jarg1, ESLconnection jarg1_, String jarg2); public final static native long ESLconnection_api(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3); - public final static native long ESLconnection_bgapi(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3); + public final static native long ESLconnection_bgapi(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3, String jarg4); public final static native long ESLconnection_sendEvent(long jarg1, ESLconnection jarg1_, long jarg2, ESLevent jarg2_); public final static native long ESLconnection_recvEvent(long jarg1, ESLconnection jarg1_); public final static native long ESLconnection_recvEventTimed(long jarg1, ESLconnection jarg1_, int jarg2); diff --git a/libs/esl/lua/esl_wrap.cpp b/libs/esl/lua/esl_wrap.cpp index bcdb11d2e0..25fceb7bea 100644 --- a/libs/esl/lua/esl_wrap.cpp +++ b/libs/esl/lua/esl_wrap.cpp @@ -2498,12 +2498,14 @@ static int _wrap_ESLconnection_bgapi(lua_State* L) { ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; - SWIG_check_num_args("bgapi",2,3) + SWIG_check_num_args("bgapi",2,4) if(!SWIG_isptrtype(L,1)) SWIG_fail_arg("bgapi",1,"ESLconnection *"); if(!lua_isstring(L,2)) SWIG_fail_arg("bgapi",2,"char const *"); if(lua_gettop(L)>=3 && !lua_isstring(L,3)) SWIG_fail_arg("bgapi",3,"char const *"); + if(lua_gettop(L)>=4 && !lua_isstring(L,4)) SWIG_fail_arg("bgapi",4,"char const *"); if (!SWIG_IsOK(SWIG_ConvertPtr(L,1,(void**)&arg1,SWIGTYPE_p_ESLconnection,0))){ SWIG_fail_ptr("ESLconnection_bgapi",1,SWIGTYPE_p_ESLconnection); @@ -2513,7 +2515,10 @@ static int _wrap_ESLconnection_bgapi(lua_State* L) { if(lua_gettop(L)>=3){ arg3 = (char *)lua_tostring(L, 3); } - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + if(lua_gettop(L)>=4){ + arg4 = (char *)lua_tostring(L, 4); + } + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); SWIG_arg=0; SWIG_NewPointerObj(L,result,SWIGTYPE_p_ESLevent,1); SWIG_arg++; return SWIG_arg; diff --git a/libs/esl/managed/ESLPINVOKE.cs b/libs/esl/managed/ESLPINVOKE.cs index d7dfb6f2ff..1e487895d9 100644 --- a/libs/esl/managed/ESLPINVOKE.cs +++ b/libs/esl/managed/ESLPINVOKE.cs @@ -275,7 +275,7 @@ class ESLPINVOKE { public static extern IntPtr ESLconnection_Api(HandleRef jarg1, string jarg2, string jarg3); [DllImport("ESL", EntryPoint="CSharp_ESLconnection_Bgapi")] - public static extern IntPtr ESLconnection_Bgapi(HandleRef jarg1, string jarg2, string jarg3); + public static extern IntPtr ESLconnection_Bgapi(HandleRef jarg1, string jarg2, string jarg3, string jarg4); [DllImport("ESL", EntryPoint="CSharp_ESLconnection_SendEvent")] public static extern IntPtr ESLconnection_SendEvent(HandleRef jarg1, HandleRef jarg2); diff --git a/libs/esl/managed/ESLconnection.cs b/libs/esl/managed/ESLconnection.cs index 10a77748c2..88435830ad 100644 --- a/libs/esl/managed/ESLconnection.cs +++ b/libs/esl/managed/ESLconnection.cs @@ -80,8 +80,8 @@ public class ESLconnection : IDisposable { return ret; } - public ESLevent Bgapi(string cmd, string arg) { - IntPtr cPtr = ESLPINVOKE.ESLconnection_Bgapi(swigCPtr, cmd, arg); + public ESLevent Bgapi(string cmd, string arg, string job_uuid) { + IntPtr cPtr = ESLPINVOKE.ESLconnection_Bgapi(swigCPtr, cmd, arg, job_uuid); ESLevent ret = (cPtr == IntPtr.Zero) ? null : new ESLevent(cPtr, true); return ret; } diff --git a/libs/esl/managed/esl_wrap.cpp b/libs/esl/managed/esl_wrap.cpp index eb9559b684..4d0e27d261 100644 --- a/libs/esl/managed/esl_wrap.cpp +++ b/libs/esl/managed/esl_wrap.cpp @@ -677,17 +677,19 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_ESLconnection_Api(void * jarg1, char * jarg } -SWIGEXPORT void * SWIGSTDCALL CSharp_ESLconnection_Bgapi(void * jarg1, char * jarg2, char * jarg3) { +SWIGEXPORT void * SWIGSTDCALL CSharp_ESLconnection_Bgapi(void * jarg1, char * jarg2, char * jarg3, char * jarg4) { void * jresult ; ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; arg1 = (ESLconnection *)jarg1; arg2 = (char *)jarg2; arg3 = (char *)jarg3; - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + arg4 = (char *)jarg4; + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); jresult = (void *)result; return jresult; } diff --git a/libs/esl/perl/esl_wrap.cpp b/libs/esl/perl/esl_wrap.cpp index e965458cb5..ccce4bb6d0 100644 --- a/libs/esl/perl/esl_wrap.cpp +++ b/libs/esl/perl/esl_wrap.cpp @@ -3072,6 +3072,7 @@ XS(_wrap_ESLconnection_bgapi) { ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; void *argp1 = 0 ; int res1 = 0 ; @@ -3081,11 +3082,14 @@ XS(_wrap_ESLconnection_bgapi) { int res3 ; char *buf3 = 0 ; int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; int argvi = 0; dXSARGS; - if ((items < 2) || (items > 3)) { - SWIG_croak("Usage: ESLconnection_bgapi(self,cmd,arg);"); + if ((items < 2) || (items > 4)) { + SWIG_croak("Usage: ESLconnection_bgapi(self,cmd,arg,job_uuid);"); } res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); if (!SWIG_IsOK(res1)) { @@ -3104,16 +3108,25 @@ XS(_wrap_ESLconnection_bgapi) { } arg3 = reinterpret_cast< char * >(buf3); } - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + if (items > 3) { + res4 = SWIG_AsCharPtrAndSize(ST(3), &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "ESLconnection_bgapi" "', argument " "4"" of type '" "char const *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + } + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); ST(argvi) = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLevent, SWIG_OWNER | SWIG_SHADOW); argvi++ ; if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; XSRETURN(argvi); fail: if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; SWIG_croak_null(); } } diff --git a/libs/esl/php/ESL.php b/libs/esl/php/ESL.php index 96d96e3e90..70ff62ac0d 100644 --- a/libs/esl/php/ESL.php +++ b/libs/esl/php/ESL.php @@ -152,10 +152,11 @@ class ESLconnection { return is_resource($r) ? new ESLevent($r) : $r; } - function bgapi($cmd,$arg=null) { + function bgapi($cmd,$arg=null,$job_uuid=null) { switch (func_num_args()) { case 1: $r=ESLconnection_bgapi($this->_cPtr,$cmd); break; - default: $r=ESLconnection_bgapi($this->_cPtr,$cmd,$arg); + case 2: $r=ESLconnection_bgapi($this->_cPtr,$cmd,$arg); break; + default: $r=ESLconnection_bgapi($this->_cPtr,$cmd,$arg,$job_uuid); } return is_resource($r) ? new ESLevent($r) : $r; } diff --git a/libs/esl/php/esl_wrap.cpp b/libs/esl/php/esl_wrap.cpp index c2eea23034..c38ea1fa62 100644 --- a/libs/esl/php/esl_wrap.cpp +++ b/libs/esl/php/esl_wrap.cpp @@ -2075,13 +2075,14 @@ ZEND_NAMED_FUNCTION(_wrap_ESLconnection_bgapi) { ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; - zval **args[3]; + zval **args[4]; int arg_count; SWIG_ResetError(); arg_count = ZEND_NUM_ARGS(); - if(arg_count<2 || arg_count>3 || + if(arg_count<2 || arg_count>4 || zend_get_parameters_array_ex(arg_count,args)!=SUCCESS) WRONG_PARAM_COUNT; @@ -2104,7 +2105,14 @@ ZEND_NAMED_FUNCTION(_wrap_ESLconnection_bgapi) { /*@SWIG@*/; } - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + if(arg_count > 3) { + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[3]); + arg4 = (char *) Z_STRVAL_PP(args[3]); + /*@SWIG@*/; + + } + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); { SWIG_SetPointerZval(return_value, (void *)result, SWIGTYPE_p_ESLevent, 0); } diff --git a/libs/esl/python/esl_wrap.cpp b/libs/esl/python/esl_wrap.cpp index a7040ef200..3ef587561c 100644 --- a/libs/esl/python/esl_wrap.cpp +++ b/libs/esl/python/esl_wrap.cpp @@ -4151,6 +4151,7 @@ SWIGINTERN PyObject *_wrap_ESLconnection_bgapi(PyObject *SWIGUNUSEDPARM(self), P ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; void *argp1 = 0 ; int res1 = 0 ; @@ -4160,12 +4161,16 @@ SWIGINTERN PyObject *_wrap_ESLconnection_bgapi(PyObject *SWIGUNUSEDPARM(self), P int res3 ; char *buf3 = 0 ; int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; SWIG_PYTHON_THREAD_BEGIN_BLOCK; - if (!PyArg_ParseTuple(args,(char *)"OO|O:ESLconnection_bgapi",&obj0,&obj1,&obj2)) SWIG_fail; + if (!PyArg_ParseTuple(args,(char *)"OO|OO:ESLconnection_bgapi",&obj0,&obj1,&obj2,&obj3)) SWIG_fail; res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ESLconnection_bgapi" "', argument " "1"" of type '" "ESLconnection *""'"); @@ -4183,19 +4188,28 @@ SWIGINTERN PyObject *_wrap_ESLconnection_bgapi(PyObject *SWIGUNUSEDPARM(self), P } arg3 = reinterpret_cast< char * >(buf3); } + if (obj3) { + res4 = SWIG_AsCharPtrAndSize(obj3, &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "ESLconnection_bgapi" "', argument " "4"" of type '" "char const *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + } { SWIG_PYTHON_THREAD_BEGIN_ALLOW; - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); SWIG_PYTHON_THREAD_END_ALLOW; } resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLevent, SWIG_POINTER_OWN | 0 ); if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; SWIG_PYTHON_THREAD_END_BLOCK; return resultobj; fail: if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; SWIG_PYTHON_THREAD_END_BLOCK; return NULL; } diff --git a/libs/esl/ruby/esl_wrap.cpp b/libs/esl/ruby/esl_wrap.cpp index 7892ed6d00..3769b5bc24 100644 --- a/libs/esl/ruby/esl_wrap.cpp +++ b/libs/esl/ruby/esl_wrap.cpp @@ -3055,6 +3055,7 @@ _wrap_ESLconnection_bgapi(int argc, VALUE *argv, VALUE self) { ESLconnection *arg1 = (ESLconnection *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) NULL ; + char *arg4 = (char *) NULL ; ESLevent *result = 0 ; void *argp1 = 0 ; int res1 = 0 ; @@ -3064,9 +3065,12 @@ _wrap_ESLconnection_bgapi(int argc, VALUE *argv, VALUE self) { int res3 ; char *buf3 = 0 ; int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; VALUE vresult = Qnil; - if ((argc < 1) || (argc > 2)) { + if ((argc < 1) || (argc > 3)) { rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)",argc); SWIG_fail; } res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); @@ -3086,14 +3090,23 @@ _wrap_ESLconnection_bgapi(int argc, VALUE *argv, VALUE self) { } arg3 = reinterpret_cast< char * >(buf3); } - result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3); + if (argc > 2) { + res4 = SWIG_AsCharPtrAndSize(argv[2], &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), Ruby_Format_TypeError( "", "char const *","bgapi", 4, argv[2] )); + } + arg4 = reinterpret_cast< char * >(buf4); + } + result = (ESLevent *)(arg1)->bgapi((char const *)arg2,(char const *)arg3,(char const *)arg4); vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLevent, SWIG_POINTER_OWN | 0 ); if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; return vresult; fail: if (alloc2 == SWIG_NEWOBJ) delete[] buf2; if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; return Qnil; } diff --git a/libs/esl/src/esl_oop.cpp b/libs/esl/src/esl_oop.cpp index 0ea88e17cc..8ea3dfa4d3 100644 --- a/libs/esl/src/esl_oop.cpp +++ b/libs/esl/src/esl_oop.cpp @@ -105,7 +105,7 @@ ESLevent *ESLconnection::api(const char *cmd, const char *arg) return event; } -ESLevent *ESLconnection::bgapi(const char *cmd, const char *arg) +ESLevent *ESLconnection::bgapi(const char *cmd, const char *arg, const char *job_uuid) { size_t len; char *cmd_buf; @@ -115,12 +115,17 @@ ESLevent *ESLconnection::bgapi(const char *cmd, const char *arg) return NULL; } - len = strlen(cmd) + (arg ? strlen(arg) : 0) + 10; + len = strlen(cmd) + (arg ? strlen(arg) : 0) + (job_uuid ? strlen(job_uuid) + 12 : 0) + 10; cmd_buf = (char *) malloc(len + 1); assert(cmd_buf); + + if (job_uuid) { + snprintf(cmd_buf, len, "bgapi %s%s%s\nJob-UUID: %s", cmd, arg ? " " : "", arg ? arg : "", job_uuid); + } else { + snprintf(cmd_buf, len, "bgapi %s%s%s", cmd, arg ? " " : "", arg ? arg : ""); + } - snprintf(cmd_buf, len, "bgapi %s %s", cmd, arg ? arg : ""); *(cmd_buf + (len)) = '\0'; event = sendRecv(cmd_buf); diff --git a/libs/esl/src/include/esl_oop.h b/libs/esl/src/include/esl_oop.h index 8562a9d965..3947d0cfb7 100644 --- a/libs/esl/src/include/esl_oop.h +++ b/libs/esl/src/include/esl_oop.h @@ -82,7 +82,7 @@ class ESLconnection { int send(const char *cmd); ESLevent *sendRecv(const char *cmd); ESLevent *api(const char *cmd, const char *arg = NULL); - ESLevent *bgapi(const char *cmd, const char *arg = NULL); + ESLevent *bgapi(const char *cmd, const char *arg = NULL, const char *job_uuid = NULL); ESLevent *sendEvent(ESLevent *send_me); ESLevent *recvEvent(); ESLevent *recvEventTimed(int ms); From 518e0341878e6d75ca6fcac44d1c8cf0b9e49780 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 19:26:40 -0500 Subject: [PATCH 056/154] freetdm: remove remaining references to boost (sample code, visual studio project references) --- libs/freetdm/freetdm.2008.sln | 15 - libs/freetdm/freetdm.2010.sln | 6 - libs/freetdm/sample/CMakeLists.txt | 1 - libs/freetdm/sample/boost/CMakeLists.txt | 12 - libs/freetdm/sample/boost/Makefile | 10 - libs/freetdm/sample/boost/ftdmstart.c | 469 ---------------------- libs/freetdm/src/testboost.c | 83 ---- libs/freetdm/src/testsangomaboost.c | 472 ----------------------- 8 files changed, 1068 deletions(-) delete mode 100644 libs/freetdm/sample/boost/CMakeLists.txt delete mode 100644 libs/freetdm/sample/boost/Makefile delete mode 100644 libs/freetdm/sample/boost/ftdmstart.c delete mode 100644 libs/freetdm/src/testboost.c delete mode 100644 libs/freetdm/src/testsangomaboost.c diff --git a/libs/freetdm/freetdm.2008.sln b/libs/freetdm/freetdm.2008.sln index 0e374545eb..4bf2c795a6 100644 --- a/libs/freetdm/freetdm.2008.sln +++ b/libs/freetdm/freetdm.2008.sln @@ -43,21 +43,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_wanpipe", "src\ftmod\ {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_sangoma_boost", "src\ftmod\ftmod_sangoma_boost\ftmod_sangoma_boost.2008.vcproj", "{D021EF2A-460D-4827-A0F7-41FDECF46F1B}" - ProjectSection(ProjectDependencies) = postProject - {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testboost", "msvc\testboost\testboost.2008.vcproj", "{2B1BAF36-0241-43E7-B865-A8338AD48E2E}" - ProjectSection(ProjectDependencies) = postProject - {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsangomaboost", "msvc\testboost\testsangomaboost.2008.vcproj", "{0DA69C18-4FA1-4E8C-89CE-12498637C5BE}" - ProjectSection(ProjectDependencies) = postProject - {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_sangoma_isdn", "src\ftmod\ftmod_sangoma_isdn\ftmod_sangoma_isdn.2008.vcproj", "{B2AF4EA6-0CD7-4529-9EB5-5AF43DB90395}" ProjectSection(ProjectDependencies) = postProject {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} diff --git a/libs/freetdm/freetdm.2010.sln b/libs/freetdm/freetdm.2010.sln index 44431d675a..b6259e5960 100644 --- a/libs/freetdm/freetdm.2010.sln +++ b/libs/freetdm/freetdm.2010.sln @@ -19,12 +19,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_pika", "src\ftmod\ftm EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_wanpipe", "src\ftmod\ftmod_wanpipe\ftmod_wanpipe.2010.vcxproj", "{1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_sangoma_boost", "src\ftmod\ftmod_sangoma_boost\ftmod_sangoma_boost.2010.vcxproj", "{D021EF2A-460D-4827-A0F7-41FDECF46F1B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testboost", "msvc\testboost\testboost.2010.vcxproj", "{2B1BAF36-0241-43E7-B865-A8338AD48E2E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsangomaboost", "msvc\testboost\testsangomaboost.2010.vcxproj", "{0DA69C18-4FA1-4E8C-89CE-12498637C5BE}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_sangoma_isdn", "src\ftmod\ftmod_sangoma_isdn\ftmod_sangoma_isdn.2010.vcxproj", "{B2AF4EA6-0CD7-4529-9EB5-5AF43DB90395}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_r2", "src\ftmod\ftmod_r2\ftmod_r2.2010.vcxproj", "{08C3EA27-A51D-47F8-B47D-B189C649CF30}" diff --git a/libs/freetdm/sample/CMakeLists.txt b/libs/freetdm/sample/CMakeLists.txt index 330d590fd4..9baf1bdf16 100644 --- a/libs/freetdm/sample/CMakeLists.txt +++ b/libs/freetdm/sample/CMakeLists.txt @@ -4,5 +4,4 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(sample) -ADD_SUBDIRECTORY(boost) #ADD_SUBDIRECTORY(sched) FIXME: this code doesnt compile diff --git a/libs/freetdm/sample/boost/CMakeLists.txt b/libs/freetdm/sample/boost/CMakeLists.txt deleted file mode 100644 index 6f36f106a5..0000000000 --- a/libs/freetdm/sample/boost/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# Arnaldo M Pereira -# -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) -PROJECT(boost) - -IF(NOT DEFINED WIN32) - INCLUDE_DIRECTORIES(/usr/local/freeswitch/include) - ADD_DEFINITIONS(-Wall) - ADD_EXECUTABLE(ftdmstart ftdmstart.c) - TARGET_LINK_LIBRARIES(ftdmstart freetdm) -ENDIF(NOT DEFINED WIN32) diff --git a/libs/freetdm/sample/boost/Makefile b/libs/freetdm/sample/boost/Makefile deleted file mode 100644 index 981f6f072f..0000000000 --- a/libs/freetdm/sample/boost/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -CC=gcc -CFLAGS=-Wall -I/usr/local/freeswitch/include -LDFLAGS=-L/usr/local/freeswitch/lib -lfreetdm - -ftdmstart: ftdmstart.o - -clean: - rm -rf ftdmstart.o - - diff --git a/libs/freetdm/sample/boost/ftdmstart.c b/libs/freetdm/sample/boost/ftdmstart.c deleted file mode 100644 index bff0664bce..0000000000 --- a/libs/freetdm/sample/boost/ftdmstart.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (c) 2010, Sangoma Technologies - * Moises Silva - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Sample program for the boost signaling absraction. - * Usage: testboostalone ... -d [number-to-dial-if-any] - * compile this program linking to the freetdm library (ie -lfreetdm) - **/ - -#ifndef __linux__ -#define _CRT_SECURE_NO_WARNINGS 1 -#endif - -#include - -#include "freetdm.h" -#include -#include -#include - - -/* arbitrary limit for max calls in this sample program */ -#define MAX_CALLS 255 - -/* some timers (in seconds) to fake responses in incoming calls */ -#define PROGRESS_TIMER 1 -#define ANSWER_TIMER 5 -#define HANGUP_TIMER 15 - -/* simple variable used to stop the application */ -static int app_running = 0; - -typedef void (*expired_function_t)(ftdm_channel_t *channel); -typedef struct dummy_timer_s { - int time; - ftdm_channel_t *channel; - expired_function_t expired; -} dummy_timer_t; - -/* dummy second resolution timers */ -static dummy_timer_t g_timers[MAX_CALLS]; - -/* mutex to protect the timers (both, the test thread and the signaling thread may modify them) */ -static ftdm_mutex_t *g_schedule_mutex; - -/* unique outgoing channel */ -static ftdm_channel_t *g_outgoing_channel = NULL; - -static void interrupt_requested(int signal) -{ - app_running = 0; -} - -static void schedule_timer(ftdm_channel_t *channel, int sec, expired_function_t expired) -{ - int i; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* check the timer slot is free to use */ - if (!g_timers[i].time) { - g_timers[i].time = sec; - g_timers[i].channel = channel; - g_timers[i].expired = expired; - ftdm_mutex_unlock(g_schedule_mutex); - return; - } - } - ftdm_log(FTDM_LOG_ERROR, "Failed to schedule timer\n"); - ftdm_mutex_unlock(g_schedule_mutex); -} - -static void run_timers(void) -{ - int i; - void *channel; - expired_function_t expired_func = NULL; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* if there's time left, decrement */ - if (g_timers[i].time) { - g_timers[i].time--; - } - - /* if time expired and we have an expired function, call it */ - if (!g_timers[i].time && g_timers[i].expired) { - expired_func = g_timers[i].expired; - channel = g_timers[i].channel; - memset(&g_timers[i], 0, sizeof(g_timers[i])); - expired_func(channel); - } - } - ftdm_mutex_unlock(g_schedule_mutex); -} - -static void release_timers(ftdm_channel_t *channel) -{ - int i; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* clear any timer belonging to the given channel */ - if (g_timers[i].channel == channel) { - memset(&g_timers[i], 0, sizeof(g_timers[i])); - } - } - ftdm_mutex_unlock(g_schedule_mutex); -} - -/* hangup the call */ -static void send_hangup(ftdm_channel_t *channel) -{ - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting hangup in channel %d:%d\n", spanid, chanid); - ftdm_channel_call_hangup(channel); -} - -/* send answer for an incoming call */ -static void send_answer(ftdm_channel_t *channel) -{ - /* we move the channel signaling state machine to UP (answered) */ - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting answer in channel %d:%d\n", spanid, chanid); - ftdm_channel_call_answer(channel); - schedule_timer(channel, HANGUP_TIMER, send_hangup); -} - -/* send progress for an incoming */ -static void send_progress(ftdm_channel_t *channel) -{ - /* we move the channel signaling state machine to UP (answered) */ - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting progress\n", spanid, chanid); - ftdm_channel_call_indicate(channel, FTDM_CHANNEL_INDICATE_PROGRESS); - schedule_timer(channel, ANSWER_TIMER, send_answer); -} - -/* This function will be called in an undetermined signaling thread, you must not do - * any blocking operations here or the signaling stack may delay other call event processing - * The arguments for this function are defined in FIO_SIGNAL_CB_FUNCTION prototype, I just - * name them here for your convenience: - * ftdm_sigmsg_t *sigmsg - * - The sigmsg structure contains the ftdm_channel structure that represents the channel where - * the event occurred and the event_id of the signaling event that just occurred. - * */ -static FIO_SIGNAL_CB_FUNCTION(on_signaling_event) -{ - switch (sigmsg->event_id) { - /* This event signals the start of an incoming call */ - case FTDM_SIGEVENT_START: - ftdm_log(FTDM_LOG_NOTICE, "Incoming call received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - schedule_timer(sigmsg->channel, PROGRESS_TIMER, send_progress); - break; - /* This event signals progress on an outgoing call */ - case FTDM_SIGEVENT_PROGRESS_MEDIA: - ftdm_log(FTDM_LOG_NOTICE, "Progress message received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - break; - /* This event signals answer in an outgoing call */ - case FTDM_SIGEVENT_UP: - ftdm_log(FTDM_LOG_NOTICE, "Answer received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - /* now the channel is answered and we can use - * ftdm_channel_wait() to wait for input/output in a channel (equivalent to poll() or select()) - * ftdm_channel_read() to read available data in a channel - * ftdm_channel_write() to write to the channel */ - break; - /* This event signals hangup from the other end */ - case FTDM_SIGEVENT_STOP: - ftdm_log(FTDM_LOG_NOTICE, "Hangup received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - if (g_outgoing_channel == sigmsg->channel) { - g_outgoing_channel = NULL; - } - /* release any timer for this channel */ - release_timers(sigmsg->channel); - /* acknowledge the hangup */ - ftdm_channel_call_hangup(sigmsg->channel); - break; - default: - ftdm_log(FTDM_LOG_WARNING, "Unhandled event %s in channel %d:%d\n", ftdm_signal_event2str(sigmsg->event_id), - sigmsg->span_id, sigmsg->chan_id); - break; - } - return FTDM_SUCCESS; -} - -static void place_call(const ftdm_span_t *span, const char *number) -{ - ftdm_channel_t *ftdmchan = NULL; - ftdm_caller_data_t caller_data = {{ 0 }}; - ftdm_status_t status = FTDM_FAIL; - - /* set destiny number */ - ftdm_set_string(caller_data.dnis.digits, number); - - /* set callerid */ - ftdm_set_string(caller_data.cid_name, "testsangomaboost"); - ftdm_set_string(caller_data.cid_num.digits, "1234"); - - /* request to search for an outgoing channel top down with the given caller data. - * it is also an option to use ftdm_channel_open_by_group to let freetdm hunt - * an available channel in a given group instead of per span - * */ - status = ftdm_channel_open_by_span(ftdm_span_get_id(span), FTDM_TOP_DOWN, &caller_data, &ftdmchan); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); - return; - } - - g_outgoing_channel = ftdmchan; - - /* set the caller data for the outgoing channel */ - ftdm_channel_set_caller_data(ftdmchan, &caller_data); - - status = ftdm_channel_call_place(ftdmchan); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); - return; - } - - /* this is required to initialize the outgoing channel */ - ftdm_channel_init(ftdmchan); -} - -#define ARRLEN(arr) (sizeof(arr)/sizeof(arr[0])) -int main(int argc, char *argv[]) -{ - /* span names can be any null-terminated string, does not need to be a wanpipe port */ - int span_numbers[32]; - char span_names[ARRLEN(span_numbers)][ARRLEN(span_numbers)]; - const char *spanname = NULL; - char wpchans[25]; - unsigned configured = 0; - int i, spanno; - int numspans = 0; - ftdm_status_t status; - ftdm_span_t *span_list[ARRLEN(span_numbers)]; - ftdm_span_t *span; - ftdm_channel_config_t chan_config; - ftdm_conf_parameter_t parameters[20]; - char *todial = NULL; - int32_t ticks = 0; - - /* register a handler to shutdown things properly */ -#ifdef _WIN64 - // still trying to figure this one out otherwise triggers error - if (signal(SIGINT, interrupt_requested) < 0) { -#else - if (signal(SIGINT, interrupt_requested) == SIG_ERR) { -#endif - fprintf(stderr, "Could not set the SIGINT signal handler: %s\n", strerror(errno)); - exit(-1); - } - - for (i = 1; i < argc; i++) { - if (argv[i][0] == '-' && argv[i][1] == 'd') { - i++; - if (i >= argc) { - fprintf(stderr, "Error, -d specified but no number to dial!\n"); - exit(1); - } - todial = argv[i]; - if (!strlen(todial)) { - todial = NULL; - } - printf("Number to dial: %s\n", todial); - continue; - } - spanno = atoi(argv[i]); - span_numbers[numspans] = spanno; - snprintf(span_names[numspans], sizeof(span_names[numspans]), "wanpipe%d", spanno); - numspans++; - } - - if (!numspans) { - fprintf(stderr, "please specify a at least 1 wanpipe port number\n"); - exit(-1); - } - - /* clear any outstanding timers */ - memset(&g_timers, 0, sizeof(g_timers)); - - /* set the logging level to use */ - ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); - - /* Initialize the FTDM library */ - if (ftdm_global_init() != FTDM_SUCCESS) { - fprintf(stderr, "Error loading FreeTDM\n"); - exit(-1); - } - - /* create the schedule mutex */ - ftdm_mutex_create(&g_schedule_mutex); - - /* now we can start creating spans */ - memset(&chan_config, 0, sizeof(chan_config)); - strncpy(chan_config.group_name, "mygroup", sizeof(chan_config.group_name)-1); - chan_config.group_name[sizeof(chan_config.group_name)-1] = 0; - for (i = 0; i < numspans; i++) { - spanname = span_names[i]; - /* "wanpipe" is the special I/O identifier for Sangoma devices */ - ftdm_log(FTDM_LOG_NOTICE, "Creating span %s\n", spanname); - status = ftdm_span_create("wanpipe", spanname, &span_list[i]); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_CRIT, "Failed to create span %s\n", spanname); - goto done; - } - span = span_list[i]; - spanno = span_numbers[i]; - - /* set the trunk type for the span */ - ftdm_span_set_trunk_type(span_list[i], FTDM_TRUNK_T1); - - /* configure B channels (syntax for wanpipe channels is span:low_chan-high_chan) */ - chan_config.type = FTDM_CHAN_TYPE_B; - snprintf(wpchans, sizeof(wpchans), "%d:1-23", spanno); - ftdm_configure_span_channels(span, wpchans, &chan_config, &configured); - ftdm_log(FTDM_LOG_NOTICE, "registered %d b channels\n", configured); - } - - /* At this point FreeTDM is ready to be used, the spans defined in freetdm.conf have the basic I/O board configuration - * but no telephony signaling configuration at all. */ - ftdm_log(FTDM_LOG_NOTICE, "FreeTDM loaded ...\n"); - - /* now we can start configuring signaling for the previously created spans */ - for (i = 0; i < numspans; i++) { - spanname = span_names[i]; - - /* Retrieve a span by name (as specified in ftdm_span_create()) */ - if (ftdm_span_find_by_name(spanname, &span) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", ftdm_span_get_name(span)); - goto done; - } - - /* prepare the configuration parameters that will be sent down to the signaling stack, the array of paramters must be terminated by an - * array element with a null .var member */ - - /* for sangoma_boost signaling (abstraction signaling used by Sangoma for PRI, BRI and SS7) the first parameter you must send - * is sigmod, which must be either sangoma_prid, if you have the PRI stack available, or sangoma_brid for the BRI stack */ - parameters[0].var = "sigmod"; - parameters[0].val = "sangoma_prid"; - - /* following parameters are signaling stack specific, this ones are for PRI */ - parameters[1].var = "switchtype"; - parameters[1].val = "national"; - - parameters[2].var = "signalling"; - parameters[2].val = "pri_cpe"; - - /* - * parameters[3].var = "nfas_primary"; - * parameters[3].val = "4"; //span number - * - * parameters[4].var = "nfas_secondary"; - * parameters[4].val = "2"; //span number - * - * parameters[5].var = "nfas_group"; - * parameters[5].val = "1"; - * */ - - - /* the last parameter .var member must be NULL! */ - parameters[3].var = NULL; - - /* send the configuration values down to the stack */ - if (ftdm_configure_span_signaling(span, "sangoma_boost", on_signaling_event, parameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring sangoma_boost signaling abstraction in span %s\n", ftdm_span_get_name(span)); - goto done; - } - - } - - - /* configuration succeeded, we can proceed now to start each span - * This step will launch at least 1 background (may be more, depending on the signaling stack used) - * to handle *ALL* signaling events for this span, your on_signaling_event callback will be called always - * in one of those infraestructure threads and you MUST NOT block in that handler to avoid delays and errors - * in the signaling processing for any call. - * */ - for (i = 0; i < numspans; i++) { - spanname = span_names[i]; - /* Retrieve a span by name (as specified in ftdm_span_create()) */ - if (ftdm_span_find_by_name(spanname, &span) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", ftdm_span_get_name(span)); - goto done; - } - - if (ftdm_span_start(span) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failing starting signaling on span %s\n", ftdm_span_get_name(span)); - goto done; - } - - } - - app_running = 1; - - /* Retrieve the first created span to place the call (if dialing was specified) */ - if (ftdm_span_find(1, &span) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span 1\n"); - goto done; - } - - /* The application thread can go on and do anything else, like waiting for a shutdown signal */ - while(ftdm_running() && app_running) { - ftdm_sleep(1000); - run_timers(); - ticks++; - if (!(ticks % 10) && todial && !g_outgoing_channel) { - ftdm_log(FTDM_LOG_NOTICE, "Originating call to number %s\n", todial); - place_call(span, todial); - } - } - - done: - - ftdm_log(FTDM_LOG_NOTICE, "Shutting down FreeTDM ...\n"); - - ftdm_mutex_destroy(&g_schedule_mutex); - - /* whenever you're done, this function will shutdown the signaling threads in any span that was started */ - ftdm_global_destroy(); - - printf("Terminated!\n"); - - sleep(2); - - exit(0); -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ diff --git a/libs/freetdm/src/testboost.c b/libs/freetdm/src/testboost.c deleted file mode 100644 index f55a46a11a..0000000000 --- a/libs/freetdm/src/testboost.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "freetdm.h" -#include - -static FIO_SIGNAL_CB_FUNCTION(on_signal) -{ - return FTDM_FAIL; -} - -static int R = 0; -#if 0 -static void handle_SIGINT(int sig) -{ - if (sig); - R = 0; - return; -} -#endif -int main(int argc, char *argv[]) -{ - ftdm_conf_parameter_t parameters[20]; - ftdm_span_t *span; - int local_port, remote_port; - - local_port = remote_port = 53000; - - ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); -#if 0 - if (argc < 2) { - printf("invalid arguments\n"); - exit(-1); - } -#endif - - if (ftdm_global_init() != FTDM_SUCCESS) { - fprintf(stderr, "Error loading FreeTDM\n"); - exit(-1); - } - if (ftdm_global_configuration() != FTDM_SUCCESS) { - fprintf(stderr, "Error configuring FreeTDM\n"); - exit(-1); - } - - printf("FreeTDM loaded\n"); - - if (ftdm_span_find_by_name("wp1", &span) != FTDM_SUCCESS) { - fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); - goto done; - } - parameters[0].var = "sigmod"; - parameters[0].val = "sangoma_prid"; - parameters[1].var = "switchtype"; - parameters[1].val = "euroisdn"; - parameters[1].var = "signalling"; - parameters[1].val = "pri_cpe"; - parameters[2].var = NULL; - if (ftdm_configure_span_signaling(span, "sangoma_boost", on_signal, parameters) == FTDM_SUCCESS) { - ftdm_span_start(span); - } else { - fprintf(stderr, "Error starting SS7_BOOST\n"); - goto done; - } - - while(ftdm_running() && R) { - ftdm_sleep(1 * 1000); - } - - done: - - ftdm_global_destroy(); - - return 0; -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ diff --git a/libs/freetdm/src/testsangomaboost.c b/libs/freetdm/src/testsangomaboost.c deleted file mode 100644 index b007fc186b..0000000000 --- a/libs/freetdm/src/testsangomaboost.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright (c) 2010, Sangoma Technologies - * Moises Silva - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Sample program for the boost signaling absraction. - * Usage: boostsample - * The span name must be a valid span defined in freetdm.conf - * compile this program linking to the freetdm library (ie -lfreetdm) - **/ - -#ifndef __linux__ -#define _CRT_SECURE_NO_WARNINGS 1 -#endif - -#include - -#include -#include -#include - -#include "freetdm.h" - - -/* arbitrary limit for max calls in this sample program */ -#define MAX_CALLS 255 - -/* some timers (in seconds) to fake responses in incoming calls */ -#define PROGRESS_TIMER 1 -#define ANSWER_TIMER 5 -#define HANGUP_TIMER 15 - -/* simple variable used to stop the application */ -static int app_running = 0; - -typedef void (*expired_function_t)(ftdm_channel_t *channel); -typedef struct dummy_timer_s { - int time; - ftdm_channel_t *channel; - expired_function_t expired; -} dummy_timer_t; - -/* dummy second resolution timers */ -static dummy_timer_t g_timers[MAX_CALLS]; - -/* mutex to protect the timers (both, the test thread and the signaling thread may modify them) */ -static ftdm_mutex_t *g_schedule_mutex; - -/* mutex to protect the channel */ -static ftdm_mutex_t *g_channel_mutex; - -/* unique outgoing channel */ -static ftdm_channel_t *g_outgoing_channel = NULL; - -static void interrupt_requested(int signal) -{ - app_running = 0; -} - -static void schedule_timer(ftdm_channel_t *channel, int sec, expired_function_t expired) -{ - int i; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* check the timer slot is free to use */ - if (!g_timers[i].time) { - g_timers[i].time = sec; - g_timers[i].channel = channel; - g_timers[i].expired = expired; - ftdm_mutex_unlock(g_schedule_mutex); - return; - } - } - ftdm_log(FTDM_LOG_ERROR, "Failed to schedule timer\n"); - ftdm_mutex_unlock(g_schedule_mutex); -} - -static void run_timers(void) -{ - int i; - void *channel; - expired_function_t expired_func = NULL; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* if there's time left, decrement */ - if (g_timers[i].time) { - g_timers[i].time--; - } - - /* if time expired and we have an expired function, call it */ - if (!g_timers[i].time && g_timers[i].expired) { - expired_func = g_timers[i].expired; - channel = g_timers[i].channel; - memset(&g_timers[i], 0, sizeof(g_timers[i])); - expired_func(channel); - } - } - ftdm_mutex_unlock(g_schedule_mutex); -} - -static void release_timers(ftdm_channel_t *channel) -{ - int i; - ftdm_mutex_lock(g_schedule_mutex); - for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { - /* clear any timer belonging to the given channel */ - if (g_timers[i].channel == channel) { - memset(&g_timers[i], 0, sizeof(g_timers[i])); - } - } - ftdm_mutex_unlock(g_schedule_mutex); -} - -/* hangup the call */ -static void send_hangup(ftdm_channel_t *channel) -{ - char dtmfbuff[100]; - int rc; - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - rc = ftdm_channel_dequeue_dtmf(channel, dtmfbuff, sizeof(dtmfbuff)); - if (rc) { - ftdm_log(FTDM_LOG_NOTICE, "Not hanging up channel %d:%d because has DTMF: %s\n", spanid, chanid, dtmfbuff); - schedule_timer(channel, HANGUP_TIMER, send_hangup); - return; - } - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting hangup in channel %d:%d\n", spanid, chanid); - ftdm_channel_call_hangup(channel); -} - -/* send answer for an incoming call */ -static void send_answer(ftdm_channel_t *channel) -{ - /* we move the channel signaling state machine to UP (answered) */ - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting answer in channel %d:%d\n", spanid, chanid); - ftdm_channel_call_answer(channel); - schedule_timer(channel, HANGUP_TIMER, send_hangup); -} - -/* send progress for an incoming */ -static void send_progress(ftdm_channel_t *channel) -{ - /* we move the channel signaling state machine to UP (answered) */ - int spanid = ftdm_channel_get_span_id(channel); - int chanid = ftdm_channel_get_id(channel); - ftdm_log(FTDM_LOG_NOTICE, "-- Requesting progress\n", spanid, chanid); - ftdm_channel_call_indicate(channel, FTDM_CHANNEL_INDICATE_PROGRESS); - schedule_timer(channel, ANSWER_TIMER, send_answer); -} - -/* This function will be called in an undetermined signaling thread, you must not do - * any blocking operations here or the signaling stack may delay other call event processing - * The arguments for this function are defined in FIO_SIGNAL_CB_FUNCTION prototype, I just - * name them here for your convenience: - * ftdm_sigmsg_t *sigmsg - * - The sigmsg structure contains the ftdm_channel structure that represents the channel where - * the event occurred and the event_id of the signaling event that just occurred. - * */ -static FIO_SIGNAL_CB_FUNCTION(on_signaling_event) -{ - switch (sigmsg->event_id) { - /* This event signals the start of an incoming call */ - case FTDM_SIGEVENT_START: - ftdm_log(FTDM_LOG_NOTICE, "Incoming call received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - schedule_timer(sigmsg->channel, PROGRESS_TIMER, send_progress); - break; - /* This event signals progress on an outgoing call */ - case FTDM_SIGEVENT_PROGRESS_MEDIA: - ftdm_log(FTDM_LOG_NOTICE, "Progress message received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - break; - /* This event signals answer in an outgoing call */ - case FTDM_SIGEVENT_UP: - ftdm_log(FTDM_LOG_NOTICE, "Answer received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL); - /* now the channel is answered and we can use - * ftdm_channel_wait() to wait for input/output in a channel (equivalent to poll() or select()) - * ftdm_channel_read() to read available data in a channel - * ftdm_channel_write() to write to the channel */ - break; - /* This event signals hangup from the other end */ - case FTDM_SIGEVENT_STOP: - ftdm_log(FTDM_LOG_NOTICE, "Hangup received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); - ftdm_mutex_lock(g_channel_mutex); - if (g_outgoing_channel == sigmsg->channel) { - g_outgoing_channel = NULL; - } - ftdm_mutex_unlock(g_channel_mutex); - /* release any timer for this channel */ - release_timers(sigmsg->channel); - /* acknowledge the hangup */ - ftdm_channel_call_hangup(sigmsg->channel); - break; - default: - ftdm_log(FTDM_LOG_WARNING, "Unhandled event %s in channel %d:%d\n", ftdm_signal_event2str(sigmsg->event_id), - sigmsg->span_id, sigmsg->chan_id); - break; - } - return FTDM_SUCCESS; -} - -static void place_call(const ftdm_span_t *span, const char *number) -{ - ftdm_channel_t *ftdmchan = NULL; - ftdm_caller_data_t caller_data = {{ 0 }}; - ftdm_status_t status = FTDM_FAIL; - - /* set destiny number */ - ftdm_set_string(caller_data.dnis.digits, number); - - /* set callerid */ - ftdm_set_string(caller_data.cid_name, "testsangomaboost"); - ftdm_set_string(caller_data.cid_num.digits, "1234"); - - /* request to search for an outgoing channel top down with the given caller data. - * it is also an option to use ftdm_channel_open_by_group to let freetdm hunt - * an available channel in a given group instead of per span - * */ - status = ftdm_channel_open_by_span(ftdm_span_get_id(span), FTDM_TOP_DOWN, &caller_data, &ftdmchan); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); - return; - } - - ftdm_mutex_lock(g_channel_mutex); - g_outgoing_channel = ftdmchan; - ftdm_mutex_unlock(g_channel_mutex); - - /* set the caller data for the outgoing channel */ - ftdm_channel_set_caller_data(ftdmchan, &caller_data); - - status = ftdm_channel_call_place(ftdmchan); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); - return; - } - - /* this is required to initialize the outgoing channel */ - ftdm_channel_init(ftdmchan); -} - -static void *media_thread(ftdm_thread_t *th, void *data) -{ - /* The application thread can go on and do anything else, like waiting for a shutdown signal */ - ftdm_wait_flag_t flags = FTDM_NO_FLAGS; - ftdm_status_t status; - ftdm_channel_t *chan; - char iobuff[160]; - char dnis_str[] = "1234"; - int tx_dtmf = 0; - ftdm_size_t datalen = 0; - memset(iobuff, 0, sizeof(iobuff)); - while(ftdm_running() && app_running) { - ftdm_mutex_lock(g_channel_mutex); - chan = g_outgoing_channel; - ftdm_mutex_unlock(g_channel_mutex); - if (chan && tx_dtmf) { - flags = FTDM_WRITE | FTDM_READ; - ftdm_channel_wait(chan, &flags, 100); - if (flags & FTDM_WRITE) { - datalen = sizeof(iobuff); - status = ftdm_channel_write(chan, iobuff, datalen, &datalen); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "writing to channel failed\n"); - } - } - if (flags & FTDM_READ) { - datalen = sizeof(iobuff); - status = ftdm_channel_read(chan, iobuff, &datalen); - if (status != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "reading from channel failed\n"); - } - } - } else if (chan && ftdm_channel_call_check_answered(chan)) { - ftdm_log(FTDM_LOG_NOTICE, "Transmitting DNIS %s\n", dnis_str); - ftdm_channel_command(g_outgoing_channel, FTDM_COMMAND_SEND_DTMF, dnis_str); - tx_dtmf = 1; - } else { - tx_dtmf = 0; - ftdm_sleep(100); - } - } - printf("Shutting down media thread ...\n"); - return NULL; -} - -int main(int argc, char *argv[]) -{ - ftdm_conf_parameter_t parameters[20]; - ftdm_span_t *span; - char *todial = NULL; - const char *sigtype = NULL; - int32_t ticks = 0; - - if (argc < 3) { - fprintf(stderr, "Usage: %s [number to dial if any]\n", argv[0]); - exit(-1); - } - - /* register a handler to shutdown things properly */ -#ifdef _WIN64 - // still trying to figure this one out otherwise triggers error - if (signal(SIGINT, interrupt_requested) < 0) { -#else - if (signal(SIGINT, interrupt_requested) == SIG_ERR) { -#endif - fprintf(stderr, "Could not set the SIGINT signal handler: %s\n", strerror(errno)); - exit(-1); - } - - if (!strcmp(argv[2], "cpe")) { - sigtype = "pri_cpe"; - } else if (!strcmp(argv[2], "net")) { - sigtype = "pri_net"; - } else { - fprintf(stderr, "Valid signaling types are cpe and net only\n"); - exit(-1); - } - printf("Using signalling %s\n", sigtype); - - if (argc >= 4) { - todial = argv[3]; - if (!strlen(todial)) { - todial = NULL; - } - } - - /* clear any outstanding timers */ - memset(&g_timers, 0, sizeof(g_timers)); - - /* set the logging level to use */ - ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); - - /* Initialize the FTDM library */ - if (ftdm_global_init() != FTDM_SUCCESS) { - fprintf(stderr, "Error loading FreeTDM\n"); - exit(-1); - } - - /* create the schedule and channel mutex */ - ftdm_mutex_create(&g_schedule_mutex); - ftdm_mutex_create(&g_channel_mutex); - - /* Load the FreeTDM configuration */ - if (ftdm_global_configuration() != FTDM_SUCCESS) { - fprintf(stderr, "Error configuring FreeTDM\n"); - exit(-1); - } - - /* At this point FreeTDM is ready to be used, the spans defined in freetdm.conf have the basic I/O board configuration - * but no telephony signaling configuration at all. */ - printf("FreeTDM loaded ...\n"); - - /* Retrieve a span by name (according to freetdm.conf) */ - if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) { - fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); - goto done; - } - - /* prepare the configuration parameters that will be sent down to the signaling stack, the array of paramters must be terminated by an - * array element with a null .var member */ - - /* for sangoma_boost signaling (abstraction signaling used by Sangoma for PRI, BRI and SS7) the first parameter you must send - * is sigmod, which must be either sangoma_prid, if you have the PRI stack available, or sangoma_brid for the BRI stack */ - parameters[0].var = "sigmod"; - parameters[0].val = "sangoma_prid"; - - /* following parameters are signaling stack specific, this ones are for PRI */ - parameters[1].var = "switchtype"; - parameters[1].val = "national"; - - parameters[2].var = "signalling"; - parameters[2].val = sigtype; - - /* - * parameters[3].var = "nfas_primary"; - * parameters[3].val = "4"; //span number - * - * parameters[4].var = "nfas_secondary"; - * parameters[4].val = "2"; //span number - * - * parameters[5].var = "nfas_group"; - * parameters[5].val = "1"; - * */ - - /* the last parameter .var member must be NULL! */ - parameters[3].var = NULL; - - /* send the configuration values down to the stack */ - if (ftdm_configure_span_signaling(span, "sangoma_boost", on_signaling_event, parameters) != FTDM_SUCCESS) { - fprintf(stderr, "Error configuring sangoma_boost signaling abstraction in span %s\n", ftdm_span_get_name(span)); - goto done; - } - - /* configuration succeeded, we can proceed now to start the span - * This step will launch at least 1 background (may be more, depending on the signaling stack used) - * to handle *ALL* signaling events for this span, your on_signaling_event callback will be called always - * in one of those infraestructure threads and you MUST NOT block in that handler to avoid delays and errors - * in the signaling processing for any call. - * */ - ftdm_span_start(span); - - if (ftdm_thread_create_detached(media_thread, NULL) != FTDM_SUCCESS){ - fprintf(stderr, "Error launching media thread\n"); - goto done; - } - - app_running = 1; - - /* The application thread can go on and do anything else, like waiting for a shutdown signal */ - while(ftdm_running() && app_running) { - ftdm_sleep(1000); - run_timers(); - ticks++; - if (!(ticks % 10) && todial && !g_outgoing_channel) { - ftdm_log(FTDM_LOG_NOTICE, "Originating call to number %s\n", todial); - place_call(span, todial); - } - } - printf("Shutting down FreeTDM ...\n"); - done: - - ftdm_mutex_destroy(&g_schedule_mutex); - ftdm_mutex_destroy(&g_channel_mutex); - - /* whenever you're done, this function will shutdown the signaling threads in any span that was started */ - ftdm_global_destroy(); - - return 0; -} - -/* 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: - */ From 99b64f39a1605ea81b573416a7fdbe613fe77f5d Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 19:32:40 -0500 Subject: [PATCH 057/154] freetdm: add _WIN32_WINNT version to vs project --- libs/freetdm/msvc/freetdm.2008.vcproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj index 0539ff3f42..2619f23d74 100644 --- a/libs/freetdm/msvc/freetdm.2008.vcproj +++ b/libs/freetdm/msvc/freetdm.2008.vcproj @@ -46,7 +46,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0501" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" From f0da352917d29ca02c20fcad056e18082824a9b6 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 19:38:37 -0500 Subject: [PATCH 058/154] freetdm: more visual studio 2008 and 2010 updates to set the proper _WIN32_WINNT target --- libs/freetdm/msvc/freetdm.2008.vcproj | 6 +++--- libs/freetdm/msvc/freetdm.2010.vcxproj | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj index 2619f23d74..c3a0d7ad08 100644 --- a/libs/freetdm/msvc/freetdm.2008.vcproj +++ b/libs/freetdm/msvc/freetdm.2008.vcproj @@ -122,7 +122,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0501" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -198,7 +198,7 @@ Disabled ../src/include;../src/include/private;../src/isdn/include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -112,7 +112,7 @@ ../src/include;../src/include/private;../src/isdn/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions) MultiThreadedDLL false false @@ -134,7 +134,7 @@ Disabled ../src/include;../src/include/private;../src/isdn/include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -161,7 +161,7 @@ ../src/include;../src/include/private;../src/isdn/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions) MultiThreadedDLL false false @@ -222,4 +222,4 @@ - \ No newline at end of file + From 986f258db05f7b841fcb68231ae07d5174fbcb38 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 24 Feb 2011 18:48:16 -0600 Subject: [PATCH 059/154] let ctl-c work until you are connected --- libs/esl/fs_cli.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index a70d63fab5..19c5795bee 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -53,6 +53,7 @@ static unsigned char esl_console_complete(const char *buffer, const char *cursor #endif static char prompt_str[512] = ""; +static int CONNECTED = 0; typedef struct { char name[128]; @@ -537,6 +538,11 @@ static void handle_SIGINT(int sig) { if (sig); + if (!CONNECTED) { + fprintf(stdout, "Interrupted.\n"); + exit(1); + } + WARN_STOP = 1; signal(SIGINT, handle_SIGINT); @@ -1235,6 +1241,8 @@ int main(int argc, char *argv[]) connect: + CONNECTED = 0; + while (--loops > 0) { memset(&handle, 0, sizeof(handle)); if (esl_connect(&handle, profile->host, profile->port, profile->user, profile->pass)) { @@ -1252,6 +1260,8 @@ int main(int argc, char *argv[]) esl_log(ESL_LOG_INFO, "Retrying\n"); } } else { + CONNECTED = 1; + if (temp_log < 0 ) { esl_global_set_default_logger(profile->debug); } else { From c18835d3f919c7ba260dfa0e61254fb305850518 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Fri, 25 Feb 2011 01:52:58 +0100 Subject: [PATCH 060/154] Skinny: Allow to listen on ipv6 also allow to specify a blank ip which means: all --- src/mod/endpoints/mod_skinny/mod_skinny.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 4061b94518..a30b0fd666 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1524,7 +1524,7 @@ static void *SWITCH_THREAD_FUNC skinny_profile_run(switch_thread_t *thread, void new_socket: while(globals.running) { switch_clear_flag_locked(profile, PFLAG_RESPAWN); - rv = switch_sockaddr_info_get(&sa, profile->ip, SWITCH_INET, profile->port, 0, tmp_pool); + rv = switch_sockaddr_info_get(&sa, profile->ip, SWITCH_UNSPEC, profile->port, 0, tmp_pool); if (rv) goto fail; rv = switch_socket_create(&profile->sock, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, tmp_pool); @@ -1663,7 +1663,7 @@ switch_status_t skinny_profile_set(skinny_profile_t *profile, const char *var, c profile->domain = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "ip")) { if (!profile->ip || strcmp(val, profile->ip)) { - profile->ip = switch_core_strdup(profile->pool, val); + profile->ip = switch_core_strdup(profile->pool, zstr(val) ? NULL : val); switch_set_flag_locked(profile, PFLAG_SHOULD_RESPAWN); } } else if (!strcasecmp(var, "port")) { From f785fa11e4163d31db16e26b998be464004aeb4d Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 24 Feb 2011 21:02:51 -0500 Subject: [PATCH 061/154] freetdm: fix bugzilla 5600 - allow_collect_calls ftmod_r2 param not implemented --- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 3908535a67..0b76f6d526 100755 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -676,8 +676,8 @@ static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, cons ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); ftdm_r2_data_t *r2data = ftdmchan->span->signal_data; - ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Call offered with ANI = %s, DNIS = %s, Category = %d, ANI restricted = %s\n", - ani, dnis, category, ani_restricted ? "Yes" : "No"); + ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Call offered with ANI = %s, DNIS = %s, Category = %s, ANI restricted = %s\n", + ani, dnis, openr2_proto_get_category_string(category), ani_restricted ? "Yes" : "No"); /* check if this is a collect call and if we should accept it */ if (!r2data->allow_collect_calls && category == OR2_CALLING_PARTY_CATEGORY_COLLECT_CALL) { @@ -1678,6 +1678,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) } r2data->mf_dump_size = r2conf.mf_dump_size; r2data->category = r2conf.category; + r2data->allow_collect_calls = r2conf.allow_collect_calls; r2data->flags = 0; spanpvt->r2context = r2data->r2context; From 49ac70defd1134abc55c2d540a984d1e14d6b634 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 25 Feb 2011 09:58:15 -0500 Subject: [PATCH 062/154] freetdm: User variables now stored in ftdm_usrmsg_t --- libs/freetdm/docs/variables.txt | 114 ++++---- libs/freetdm/mod_freetdm/mod_freetdm.c | 15 +- libs/freetdm/src/ftdm_io.c | 254 +++++++++++------- libs/freetdm/src/ftdm_state.c | 15 +- libs/freetdm/src/ftdm_variables.c | 42 ++- .../freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c | 4 +- .../src/ftmod/ftmod_libpri/ftmod_libpri.c | 4 +- .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.c | 21 +- .../ftmod_sangoma_isdn_stack_hndl.c | 4 +- .../ftmod_sangoma_isdn_stack_out.c | 2 +- .../ftmod_sangoma_isdn_support.c | 25 +- .../ftmod_sangoma_isdn_trace.c | 2 - libs/freetdm/src/include/freetdm.h | 132 +++++---- libs/freetdm/src/include/ftdm_declare.h | 1 + libs/freetdm/src/include/private/ftdm_core.h | 52 +++- libs/freetdm/src/include/private/ftdm_state.h | 4 +- 16 files changed, 403 insertions(+), 288 deletions(-) diff --git a/libs/freetdm/docs/variables.txt b/libs/freetdm/docs/variables.txt index b849677277..7d95a67c47 100644 --- a/libs/freetdm/docs/variables.txt +++ b/libs/freetdm/docs/variables.txt @@ -1,41 +1,45 @@ +Using FreeTDM Variables + 1. User application sending variables or raw buffer to FreeTDM ============================================================== -The User can attach a ftdm_sigmsg_t to ftdm_caller_data_t before sending an event to freetdm. +The User can include a ftdm_usrmsg_t before sending an event to freetdm. -example #1 - Adding a variable: -------------------------------- +example #1a - Making an outbound call: +-------------------------------------- +To make an outbound call: + ftdm_usrmsg_t usrmsg; + + /* Attach variable to usrmsg */ + ftdm_usrmsg_add_var(&usrmsg, "isdn.prog_ind.descr", "inband-info-available"); + + /* Request FreeTDM to send a PROCEED msg */ + ftdm_channel_call_place_ex(ftdmchan, &usrmsg); + +example #1b - Adding a variable: +-------------------------------- When using ftmod_sangoma_isdn, user want to specify progress indicator inside PROCEED message. - ftdm_caller_data_t *caller_data; - ftdm_sigmsg_t sigmsg; + ftdm_usrmsg_t usrmsg; - /* Attach variable to sigmsg */ - ftdm_event_add_var(&sigmsg, "isdn.prog_ind.descr", "inband-info-available"); + /* Attach variable to usrmsg */ + ftdm_usrmsg_add_var(&usrmsg, "isdn.prog_ind.descr", "inband-info-available"); - - /* Attach sigmsg to channel's caller_data */ - caller_data = ftdm_channel_get_caller_data(ftdmchan); - caller_data->sigmsg = &sigmsg; - /* Request FreeTDM to send a PROCEED msg */ - ftdm_channel_call_indicate(ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED); - -Note: -1.When ftdm_channel_call_indicate returns, caller_data->sigmsg will be NULL. + ftdm_channel_call_indicate_ex(ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED, &usrmsg); - -example #2a - Setting raw data without autofree feature: +example #2 - Setting raw data: -------------------------------------------------------- When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, inside a FACILITY message. - ftdm_caller_data_t *caller_data; + ftdm_usrmsg_t usrmsg; ftdm_sigmsg_t sigmsg; - uint8_t my_facility_ie [200]; + + uint8_t *my_facility_ie = ftdm_calloc(1, 200); /*memory has to be allocated using ftdm_calloc !! */ unsigned my_facility_ie_len = 0; /* Fill my_facility_ie with custom data here */ @@ -44,49 +48,12 @@ When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, insi my_facility_ie[my_facility_ie_len++] = 0x01; my_facility_ie[my_facility_ie_len++] = 0x02; my_facility_ie[my_facility_ie_len++] = 0x03; + + ftdm_usrmsg_set_raw_data(&usrmsg, my_facility_ie, my_facility_ie_len); - - sigmsg.raw.data = my_facility_ie; - sigmsg.raw.len = my_facility_ie_len; - sigmsg.raw.autofree = 0; - - /* Attach sigmsg to channel's caller_data */ - caller_data = ftdm_channel_get_caller_data(ftdmchan); - caller_data->sigmsg = &sigmsg; - sigmsg.event_id = FTDM_SIGEVENT_FACILITY; - ftdm_channel_call_send_msg(ftdmchan, sigmsg); - - - -example #2b - Setting raw data with autofree feature: ------------------------------------------------------ - - ftdm_caller_data_t *caller_data; - ftdm_sigmsg_t sigmsg; - uint8_t *my_facility_ie = ftdm_calloc(1, 200); - unsigned my_facility_ie_len = 0; - - /* Fill my_facility_ie with custom data here */ - my_facility_ie[my_facility_ie_len++] = 0x1C; /* Q.931 Facility IE ID */ - my_facility_ie[my_facility_ie_len++] = 0x03; /* Length of facility IE */ - my_facility_ie[my_facility_ie_len++] = 0x01; - my_facility_ie[my_facility_ie_len++] = 0x02; - my_facility_ie[my_facility_ie_len++] = 0x03; - - - sigmsg.raw.data = my_facility_ie; - sigmsg.raw.len = my_facility_ie_len; - sigmsg.raw.autofree = 1; - - /* Attach sigmsg to channel's caller_data */ - caller_data = ftdm_channel_get_caller_data(ftdmchan); - caller_data->sigmsg = &sigmsg; - - sigmsg.event_id = FTDM_SIGEVENT_FACILITY; - - ftdm_channel_call_send_msg(ftdmchan, sigmsg); + ftdm_channel_call_send_msg(ftdmchan, sigmsg, usrmsg); /* FreeTDM will automatically free my_facility_ie */ @@ -104,7 +71,7 @@ example #1 - print all variables received from FreeTDM const char *var_value = NULL; /* Read all variables attached to this event */ - iter = ftdm_event_get_var_iterator(sigmsg, iter); + iter = ftdm_sigmsg_get_var_iterator(sigmsg, iter); for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { ftdm_get_current_var(curr, &var_name, &var_value); fprintf("Call Variable: %s=%s\n", var_name, var_value); @@ -117,20 +84,39 @@ example #2 - accessing a specific variable /* Inside event call-back function */ char *string = NULL; - string = ftdm_event_get_var(sigmsg, "isdn.prog_ind.descr"); + string = ftdm_sigmsg_get_var(sigmsg, "isdn.prog_ind.descr"); if (string && *string) { fprintf("Progress indicator:%s\n", string); } -example #3 - accessing raw data +example #3a - accessing raw data ------------------------------- /* Inside event call-back function */ ftdm_size_t len; uint8_t *mydata; - if (ftdm_event_get_raw_data(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (ftdm_sig_get_raw_data(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { /* raw data is available, do something with mydata here */ } + /* Once this function returns, raw data will be free'd inside FreeTDM */ +example #3b - accessing raw data +------------------------------- + + /* Inside event call-back function */ + ftdm_size_t len; + uint8_t *mydata; + if (ftdm_sig_get_raw_data_detached(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + /* raw data is available, do something with mydata here */ + + } + + : + : + : + + /* User owns raw data and is responsible for free'ing it*/ + ftdm_safe_free(mydata); + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 4a995bc8de..f1675f2ff1 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -1152,10 +1152,10 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi int argc = 0; const char *var; const char *dest_num = NULL, *callerid_num = NULL; - ftdm_hunting_scheme_t hunting; - ftdm_sigmsg_t sigmsg; + ftdm_hunting_scheme_t hunting; + ftdm_usrmsg_t usrmsg; - memset(&sigmsg, 0, sizeof(ftdm_sigmsg_t)); + memset(&usrmsg, 0, sizeof(ftdm_usrmsg_t)); if (!outbound_profile) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); @@ -1384,7 +1384,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi char *v = h->name + FREETDM_VAR_PREFIX_LEN; if (!zstr(v)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding outbound freetdm variable %s=%s to channel %d:%d\n", v, h->value, span_id, chan_id); - ftdm_event_add_var(&sigmsg, v, h->value); + ftdm_usrmsg_add_var(&usrmsg, v, h->value); } } } @@ -1411,9 +1411,8 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi hunt_data.caller_profile = caller_profile; hunt_data.tech_pvt = tech_pvt; caller_data.priv = &hunt_data; - caller_data.sigmsg = &sigmsg; - if ((status = ftdm_call_place(&caller_data, &hunting)) != FTDM_SUCCESS) { + if ((status = ftdm_call_place_ex(&caller_data, &hunting, &usrmsg)) != FTDM_SUCCESS) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } @@ -1579,9 +1578,9 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session } /* Add any call variable to the dial plan */ - iter = ftdm_event_get_var_iterator(sigmsg, iter); + iter = ftdm_sigmsg_get_var_iterator(sigmsg, iter); for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) { - ftdm_event_get_current_var(curr, &var_name, &var_value); + ftdm_get_current_var(curr, &var_name, &var_value); snprintf(name, sizeof(name), FREETDM_VAR_PREFIX "%s", var_name); switch_channel_set_variable_printf(channel, name, "%s", var_value); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Call Variable: %s=%s\n", name, var_value); diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index bd7b043a34..0b8d51facc 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -65,6 +65,8 @@ ftdm_time_t time_current_throttle_log = 0; static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data); static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data); static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan); +static ftdm_status_t ftdm_channel_sig_indicate(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_usrmsg_t *usrmsg); + static int time_is_init = 0; @@ -1898,10 +1900,9 @@ openchan: ftdm_set_flag(check, FTDM_CHANNEL_INUSE); ftdm_set_flag(check, FTDM_CHANNEL_OUTBOUND); *ftdmchan = check; -#if 1 + /* we've got the channel, do not unlock it */ goto done; -#endif unlockchan: ftdm_mutex_unlock(check->mutex); @@ -2062,25 +2063,25 @@ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_done(const ftdm_channel_t *ftdmc return condition; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status; ftdm_channel_lock(ftdmchan); ftdm_set_flag(ftdmchan, FTDM_CHANNEL_HOLD); - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0, usrmsg); ftdm_channel_unlock(ftdmchan); return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status; ftdm_channel_lock(ftdmchan); - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0, usrmsg); ftdm_channel_unlock(ftdmchan); @@ -2107,7 +2108,7 @@ FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indicat } /*! Answer call without locking the channel. The caller must have locked first */ -static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; @@ -2117,7 +2118,7 @@ static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char * * use FTDM_SPAN_USE_SKIP_STATES for now while we update the sig modules */ if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1, usrmsg); if (status != FTDM_SUCCESS) { status = FTDM_ECANCELED; goto done; @@ -2132,7 +2133,7 @@ static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char * } if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1, usrmsg); if (status != FTDM_SUCCESS) { status = FTDM_ECANCELED; goto done; @@ -2147,7 +2148,7 @@ static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char * } } - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1, usrmsg); if (status != FTDM_SUCCESS) { status = FTDM_ECANCELED; goto done; @@ -2158,19 +2159,19 @@ done: return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status; /* we leave the locking up to ftdm_channel_call_indicate, DO NOT lock here since ftdm_channel_call_indicate expects * the lock recursivity to be 1 */ - status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER); + status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, usrmsg); return status; } /* lock must be acquired by the caller! */ -static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char *func, int line, ftdm_channel_t *chan) +static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char *func, int line, ftdm_channel_t *chan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; @@ -2189,7 +2190,7 @@ static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char * if (ftdm_test_flag(chan, FTDM_CHANNEL_STATE_CHANGE)) { ftdm_channel_cancel_state(file, func, line, chan); } - status = ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1); + status = ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1, usrmsg); } else { /* the signaling stack did not touch the state, * core is responsible from clearing flags and stuff, however, because ftmod_analog @@ -2205,20 +2206,20 @@ static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char * return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t cause) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t cause, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; ftdm_channel_lock(ftdmchan); ftdmchan->caller_data.hangup_cause = cause; - status = _ftdm_channel_call_hangup_nl(file, func, line, ftdmchan); + status = _ftdm_channel_call_hangup_nl(file, func, line, ftdmchan, usrmsg); ftdm_channel_unlock(ftdmchan); return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; @@ -2226,7 +2227,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; - status = _ftdm_channel_call_hangup_nl(file, func, line, ftdmchan); + status = _ftdm_channel_call_hangup_nl(file, func, line, ftdmchan, usrmsg); ftdm_channel_unlock(ftdmchan); return status; @@ -2286,7 +2287,7 @@ FT_DECLARE(uint32_t) ftdm_channel_get_ph_span_id(const ftdm_channel_t *ftdmchan) * someone *MUST* acknowledge the indication, either the signaling stack, this function or the core * at some later point * */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; @@ -2329,10 +2330,10 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch /* FIXME: ring and busy cannot be used with all signaling stacks * (particularly isdn stacks I think, we should emulate or just move to hangup with busy cause) */ case FTDM_CHANNEL_INDICATE_RINGING: - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1, usrmsg); break; case FTDM_CHANNEL_INDICATE_BUSY: - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1, usrmsg); break; case FTDM_CHANNEL_INDICATE_PROCEED: if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE) || @@ -2340,15 +2341,15 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch ftdm_ack_indication(ftdmchan, indication, status); goto done; } - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1, usrmsg); break; case FTDM_CHANNEL_INDICATE_PROGRESS: - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1, usrmsg); break; case FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA: if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1, usrmsg); if (status != FTDM_SUCCESS) { goto done; } @@ -2360,14 +2361,14 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch goto done; } } - status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1, usrmsg); break; case FTDM_CHANNEL_INDICATE_ANSWER: - status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan); + status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan, usrmsg); break; default: - ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication); - status = FTDM_EINVAL; + /* See if signalling module can provide this indication */ + status = ftdm_channel_sig_indicate(ftdmchan, indication, usrmsg); break; } @@ -2377,40 +2378,17 @@ done: return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg) -{ - ftdm_status_t status = FTDM_FAIL; - ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "null channel"); -#ifdef __WINDOWS__ - UNREFERENCED_PARAMETER(file); - UNREFERENCED_PARAMETER(func); - UNREFERENCED_PARAMETER(line); -#endif - - ftdm_channel_lock(ftdmchan); - ftdm_channel_save_event_data(ftdmchan); - if (ftdmchan->span->send_msg) { - status = ftdmchan->span->send_msg(ftdmchan, sigmsg); - } else { - status = FTDM_NOTIMPL; - ftdm_log(FTDM_LOG_ERROR, "send_msg method not implemented in this span!\n"); - } - ftdm_channel_clear_event_data(ftdmchan); - ftdm_channel_unlock(ftdmchan); - return status; -} - -FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "null channel"); ftdm_channel_lock(ftdmchan); - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RESET, 1); + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RESET, 1, usrmsg); ftdm_channel_unlock(ftdmchan); return FTDM_SUCCESS; } -static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_FAIL; @@ -2453,9 +2431,9 @@ static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *f ftdm_set_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED); ftdm_call_set_call_id(ftdmchan, &ftdmchan->caller_data); if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 1); + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 1, usrmsg); } else { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0); + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0, usrmsg); } done: @@ -2467,19 +2445,19 @@ done: return status; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status; ftdm_channel_lock(ftdmchan); - status = _ftdm_channel_call_place_nl(file, func, line, ftdmchan); + status = _ftdm_channel_call_place_nl(file, func, line, ftdmchan, usrmsg); ftdm_channel_unlock(ftdmchan); return status; } FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, int line, - ftdm_caller_data_t *caller_data, ftdm_hunting_scheme_t *hunting) + ftdm_caller_data_t *caller_data, ftdm_hunting_scheme_t *hunting, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; ftdm_channel_t *fchan = NULL; @@ -2515,9 +2493,9 @@ FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, i ftdm_channel_set_caller_data(fchan, caller_data); - status = _ftdm_channel_call_place_nl(file, func, line, fchan); + status = _ftdm_channel_call_place_nl(file, func, line, fchan, usrmsg); if (status != FTDM_SUCCESS) { - _ftdm_channel_call_hangup_nl(file, func, line, fchan); + _ftdm_channel_call_hangup_nl(file, func, line, fchan, usrmsg); goto done; } @@ -2605,6 +2583,29 @@ FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signa } } +static ftdm_status_t ftdm_channel_sig_indicate(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_usrmsg_t *usrmsg) +{ + ftdm_status_t status = FTDM_FAIL; + if (ftdmchan->span->indicate) { + + ftdm_channel_save_usrmsg(ftdmchan, usrmsg); + + status = ftdmchan->span->indicate(ftdmchan, indication); + if (status == FTDM_NOTIMPL) { + ftdm_log(FTDM_LOG_WARNING, "Do not know how to indicate %s\n", ftdm_channel_indication2str(indication)); + } else if (status != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_WARNING, "Failed to indicate %s\n", ftdm_channel_indication2str(indication)); + } else { /* SUCCESS */ + ftdm_ack_indication(ftdmchan, indication, FTDM_SUCCESS); + } + ftdm_usrmsg_free(&ftdmchan->usrmsg); + } else { + return FTDM_NOTIMPL; + } + return status; +} + + /* this function must be called with the channel lock */ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan) { @@ -3507,7 +3508,8 @@ skipdebug: ftdmchan->dtmf_hangup_buf[ftdmchan->span->dtmf_hangup_len - 1] = *p; if (!strcmp(ftdmchan->dtmf_hangup_buf, ftdmchan->span->dtmf_hangup)) { ftdm_log(FTDM_LOG_DEBUG, "DTMF hangup detected.\n"); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + + ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, ftdmchan, FTDM_CHANNEL_STATE_HANGUP, 0, NULL); break; } } @@ -5278,9 +5280,7 @@ FT_DECLARE(ftdm_status_t) ftdm_group_create(ftdm_group_t **group, const char *na static ftdm_status_t ftdm_span_trigger_signal(const ftdm_span_t *span, ftdm_sigmsg_t *sigmsg) { ftdm_status_t status = span->signal_cb(sigmsg); - if (sigmsg->channel) { - ftdm_channel_clear_event_data(sigmsg->channel); - } + ftdm_sigmsg_free(&sigmsg); return status; } @@ -5303,7 +5303,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span) ftdm_sigmsg_t *sigmsg = NULL; while ((sigmsg = ftdm_queue_dequeue(span->pendingsignals))) { ftdm_span_trigger_signal(span, sigmsg); - ftdm_safe_free(sigmsg); } return FTDM_SUCCESS; } @@ -5316,7 +5315,7 @@ static void execute_safety_hangup(void *data) fchan->hangup_timer = 0; if (fchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Forcing hangup since the user did not confirmed our hangup after %dms\n", FORCE_HANGUP_TIMER); - _ftdm_channel_call_hangup_nl(__FILE__, __FUNCTION__, __LINE__, fchan); + _ftdm_channel_call_hangup_nl(__FILE__, __FUNCTION__, __LINE__, fchan, NULL); } else { ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Not performing safety hangup, channel state is %s\n", ftdm_channel_state2str(fchan->state)); } @@ -6019,7 +6018,7 @@ static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data) return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen) { if (!sigmsg || !sigmsg->raw.len) { return FTDM_FAIL; @@ -6031,57 +6030,112 @@ FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void ** return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_event_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen, uint8_t autofree) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_get_raw_data_detached(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen) { - ftdm_assert_return(sigmsg, FTDM_FAIL, "Trying to set raw data on a NULL event\n"); - ftdm_assert_return(sigmsg->raw.len, FTDM_FAIL, "Overwriting existing raw data\n"); - - sigmsg->raw.data = ftdm_calloc(1, datalen); - memcpy(sigmsg->raw.data, data, datalen); - sigmsg->raw.len = datalen; - sigmsg->raw.autofree = autofree; - + if (!sigmsg || !sigmsg->raw.len) { + return FTDM_FAIL; + } + + *data = sigmsg->raw.data; + *datalen = sigmsg->raw.len; return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_channel_save_event_data(ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen) { - ftdm_assert_return(!ftdmchan->sigmsg, FTDM_FAIL, "Info from previous event was not cleared\n"); - if (ftdmchan->caller_data.sigmsg) { + ftdm_assert_return(sigmsg, FTDM_FAIL, "Trying to set raw data on a NULL event\n"); + ftdm_assert_return(!sigmsg->raw.len, FTDM_FAIL, "Overwriting existing raw data\n"); + ftdm_assert_return(datalen, FTDM_FAIL, "Data length not set\n"); + + sigmsg->raw.data = data; + sigmsg->raw.len = datalen; + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_get_raw_data(ftdm_usrmsg_t *usrmsg, void **data, ftdm_size_t *datalen) +{ + if (!usrmsg || !usrmsg->raw.len) { + return FTDM_FAIL; + } + + *data = usrmsg->raw.data; + *datalen = usrmsg->raw.len; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_set_raw_data(ftdm_usrmsg_t *usrmsg, void *data, ftdm_size_t datalen) +{ + ftdm_assert_return(usrmsg, FTDM_FAIL, "Trying to set raw data on a NULL event\n"); + ftdm_assert_return(!usrmsg->raw.len, FTDM_FAIL, "Overwriting existing raw data\n"); + ftdm_assert_return(datalen, FTDM_FAIL, "Data length not set\n"); + + usrmsg->raw.data = data; + usrmsg->raw.len = datalen; + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_save_usrmsg(ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg) +{ + ftdm_assert_return(!ftdmchan->usrmsg, FTDM_FAIL, "Info from previous event was not cleared\n"); + if (usrmsg) { /* Copy sigmsg from user to internal copy so user can set new variables without race condition */ - ftdmchan->sigmsg = ftdm_calloc(1, sizeof(ftdm_sigmsg_t)); - memcpy(ftdmchan->sigmsg, ftdmchan->caller_data.sigmsg, sizeof(ftdm_sigmsg_t)); + ftdmchan->usrmsg = ftdm_calloc(1, sizeof(ftdm_usrmsg_t)); + memcpy(ftdmchan->usrmsg, usrmsg, sizeof(ftdm_usrmsg_t)); - if (ftdmchan->caller_data.sigmsg->raw.data) { - ftdm_event_set_raw_data(ftdmchan->sigmsg, ftdmchan->caller_data.sigmsg->raw.data, ftdmchan->caller_data.sigmsg->raw.len, 1); - - if (ftdmchan->caller_data.sigmsg->raw.autofree) { - ftdm_safe_free(ftdmchan->caller_data.sigmsg->raw.data); - } + if (usrmsg->raw.data) { + ftdmchan->usrmsg->raw.data = usrmsg->raw.data; + ftdmchan->usrmsg->raw.len = usrmsg->raw.len; + + usrmsg->raw.data = NULL; + usrmsg->raw.len = 0; + } + if (usrmsg->variables) { + usrmsg->variables = NULL; } - ftdmchan->caller_data.sigmsg = NULL; } return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_channel_clear_event_data(ftdm_channel_t *ftdmchan) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_free(ftdm_sigmsg_t **sigmsg) { - if (!ftdmchan->sigmsg) { + if (!*sigmsg) { return FTDM_SUCCESS; } - - if (ftdmchan->sigmsg->variables) { - ftdm_event_clear_vars(ftdmchan->sigmsg); + + if ((*sigmsg)->variables) { + hashtable_destroy((*sigmsg)->variables); + (*sigmsg)->variables = NULL; } - if (ftdmchan->sigmsg->raw.data && ftdmchan->sigmsg->raw.autofree) { - ftdm_safe_free(ftdmchan->sigmsg->raw.data); - ftdmchan->sigmsg->raw.data = NULL; - ftdmchan->sigmsg->raw.len = 0; + if ((*sigmsg)->raw.data) { + ftdm_safe_free((*sigmsg)->raw.data); + (*sigmsg)->raw.data = NULL; + (*sigmsg)->raw.len = 0; } - ftdm_safe_free(ftdmchan->sigmsg); - ftdmchan->sigmsg = NULL; + ftdm_safe_free(*sigmsg); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_free(ftdm_usrmsg_t **usrmsg) +{ + if (!*usrmsg) { + return FTDM_SUCCESS; + } + + if ((*usrmsg)->variables) { + hashtable_destroy((*usrmsg)->variables); + (*usrmsg)->variables = NULL; + } + + if ((*usrmsg)->raw.data) { + ftdm_safe_free((*usrmsg)->raw.data); + (*usrmsg)->raw.data = NULL; + (*usrmsg)->raw.len = 0; + } + + ftdm_safe_free(*usrmsg); return FTDM_SUCCESS; } diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 08902421cd..eea3a6a0c0 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -68,6 +68,8 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c return FTDM_SUCCESS; } + ftdm_usrmsg_free(&fchan->usrmsg); + ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); if (state == FTDM_CHANNEL_STATE_PROGRESS) { @@ -226,12 +228,14 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char } -FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq, ftdm_usrmsg_t *usrmsg) { - if (ftdm_channel_save_event_data(ftdmchan) == FTDM_SUCCESS) { - return ftdm_core_set_state(file, func, line, ftdmchan, state, waitrq); + ftdm_channel_save_usrmsg(ftdmchan, usrmsg); + + if (ftdm_core_set_state(file, func, line, ftdmchan, state, waitrq) != FTDM_SUCCESS) { + ftdm_usrmsg_free(&ftdmchan->usrmsg); } - return FTDM_FAIL; + return FTDM_SUCCESS; } /* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */ @@ -524,8 +528,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan) * already completed implicitly by the state_processor() function via some internal * call to ftdm_set_state() */ fchan->state_status = FTDM_STATE_STATUS_PROCESSED; - } - ftdm_channel_clear_event_data(fchan); + } } return FTDM_SUCCESS; diff --git a/libs/freetdm/src/ftdm_variables.c b/libs/freetdm/src/ftdm_variables.c index 706970247b..96db383b6e 100644 --- a/libs/freetdm/src/ftdm_variables.c +++ b/libs/freetdm/src/ftdm_variables.c @@ -39,7 +39,7 @@ #include "private/ftdm_core.h" -FT_DECLARE(ftdm_status_t) ftdm_event_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value) { char *t_name = 0, *t_val = 0; @@ -59,7 +59,7 @@ FT_DECLARE(ftdm_status_t) ftdm_event_add_var(ftdm_sigmsg_t *sigmsg, const char * return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_event_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name) +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name) { if (sigmsg && sigmsg->variables) { hashtable_remove(sigmsg->variables, (void *)var_name); @@ -67,7 +67,7 @@ FT_DECLARE(ftdm_status_t) ftdm_event_remove_var(ftdm_sigmsg_t *sigmsg, const cha return FTDM_SUCCESS; } -FT_DECLARE(const char *) ftdm_event_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name) +FT_DECLARE(const char *) ftdm_sigmsg_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name) { const char *var = NULL; @@ -79,7 +79,7 @@ FT_DECLARE(const char *) ftdm_event_get_var(ftdm_sigmsg_t *sigmsg, const char *v return var; } -FT_DECLARE(ftdm_iterator_t *) ftdm_event_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter) +FT_DECLARE(ftdm_iterator_t *) ftdm_sigmsg_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter) { ftdm_hash_iterator_t *hashiter = NULL; if (!sigmsg) { @@ -99,7 +99,7 @@ FT_DECLARE(ftdm_iterator_t *) ftdm_event_get_var_iterator(const ftdm_sigmsg_t *s return iter; } -FT_DECLARE(ftdm_status_t) ftdm_event_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val) +FT_DECLARE(ftdm_status_t) ftdm_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val) { const void *key = NULL; void *val = NULL; @@ -117,11 +117,35 @@ FT_DECLARE(ftdm_status_t) ftdm_event_get_current_var(ftdm_iterator_t *iter, cons return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_event_clear_vars(ftdm_sigmsg_t *sigmsg) + +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_add_var(ftdm_usrmsg_t *usrmsg, const char *var_name, const char *value) { - if (sigmsg->variables) { - hashtable_destroy(sigmsg->variables); - sigmsg->variables = NULL; + char *t_name = 0, *t_val = 0; + + if (!usrmsg || !var_name || !value) { + return FTDM_FAIL; } + + if (!usrmsg->variables) { + /* initialize on first use */ + usrmsg->variables = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + ftdm_assert_return(usrmsg->variables, FTDM_FAIL, "Failed to create hash table\n"); + } + + t_name = ftdm_strdup(var_name); + t_val = ftdm_strdup(value); + hashtable_insert(usrmsg->variables, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE); return FTDM_SUCCESS; } + +FT_DECLARE(const char *) ftdm_usrmsg_get_var(ftdm_usrmsg_t *usrmsg, const char *var_name) +{ + const char *var = NULL; + + if (!usrmsg || !usrmsg->variables || !var_name) { + return NULL; + } + + var = (const char *)hashtable_search(((struct hashtable*)usrmsg->variables), (void *)var_name); + return var; +} diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c index f9674a5046..a7b84ea5d8 100644 --- a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c @@ -285,9 +285,7 @@ static FIO_SPAN_GET_SIG_STATUS_FUNCTION(isdn_get_span_sig_status) */ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) { - ftdm_status_t status = FTDM_SUCCESS; - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - return status; + return FTDM_SUCCESS; } /** diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index 5f0ae716e1..e051477346 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -93,9 +93,7 @@ static FIO_SPAN_GET_SIG_STATUS_FUNCTION(isdn_get_span_sig_status) */ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) { - ftdm_status_t status = FTDM_SUCCESS; - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); - return status; + return FTDM_SUCCESS; } /** diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c index 9947d40d76..1437b45883 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c @@ -610,9 +610,6 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm sigev.span_id = ftdmchan->span_id; sigev.channel = ftdmchan; - /* Acknowledge the state change */ - ftdm_channel_complete_state(ftdmchan); - #ifdef FTDM_DEBUG_CHAN_MEMORY if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) { ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ) == 0, "Failed to mprotect"); @@ -876,15 +873,18 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm break; } + /* Acknowledge the state change */ + ftdm_channel_complete_state(ftdmchan); + /* If sngisdn_info->variables is not NULL, it means did not send any - * sigevent to the user, therefore we have to free that sigmsg */ + * sigevent to the user, therefore we have to free that hashtable */ if (sngisdn_info->variables) { hashtable_destroy(sngisdn_info->variables); sngisdn_info->variables = NULL; } /* If sngisdn_info->raw_data is not NULL, it means did not send any - * sigevent to the user, therefore we have to free that sigmsg */ + * sigevent to the user, therefore we have to free that raw data */ if (sngisdn_info->raw_data) { ftdm_safe_free(sngisdn_info->raw_data); sngisdn_info->raw_data = NULL; @@ -902,18 +902,17 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm return FTDM_SUCCESS; } -static FIO_CHANNEL_SEND_MSG_FUNCTION(ftdm_sangoma_isdn_send_msg) +static FIO_CHANNEL_INDICATE_FUNCTION(ftdm_sangoma_isdn_indicate) { ftdm_status_t status = FTDM_FAIL; - switch (sigmsg->event_id) { - case FTDM_SIGEVENT_FACILITY: + switch (indication) { + case FTDM_CHANNEL_INDICATE_FACILITY: sngisdn_snd_fac_req(ftdmchan); status = FTDM_SUCCESS; break; default: - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Unsupported signalling msg requested\n"); - status = FTDM_BREAK; + status = FTDM_NOTIMPL; } return status; } @@ -1092,7 +1091,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config) span->stop = ftdm_sangoma_isdn_stop; span->signal_type = FTDM_SIGTYPE_ISDN; span->outgoing_call = ftdm_sangoma_isdn_outgoing_call; - span->send_msg = ftdm_sangoma_isdn_send_msg; + span->indicate = ftdm_sangoma_isdn_indicate; span->channel_request = NULL; span->signal_cb = sig_cb; span->get_channel_sig_status = ftdm_sangoma_isdn_get_chan_sig_status; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c index bcc7d938b7..cfc5017a8d 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c @@ -160,7 +160,8 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) } } - /* this should be in get_facility_ie function, fix this later */ +#if 1 + /* this section will not be needed once asn decoding function with key-value pairs is implemented */ if (signal_data->facility == SNGISDN_OPT_TRUE && conEvnt->facilityStr.eh.pres) { /* Verify whether the Caller Name will come in a subsequent FACILITY message */ uint16_t ret_val; @@ -187,6 +188,7 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) strcpy(ftdmchan->caller_data.cid_name, retrieved_str); } } +#endif if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c index be204e5eb3..b56c95b10d 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c @@ -273,7 +273,7 @@ void sngisdn_snd_fac_req(ftdm_channel_t *ftdmchan) facEvnt.facElmt.facStr.val[0] = 0x1C; facEvnt.facElmt.facStr.val[1] = (uint8_t)facEvnt.facElmt.facStr.len; facEvnt.facElmt.facStr.len +=2; /* Need to include the size of identifier + len */ - + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending FACILITY (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if (sng_isdn_facility_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &facEvnt, MI_FACIL, signal_data->dchan_id, sngisdn_info->ces)) { diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c index 55e72a84f5..85048bfbf0 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c @@ -253,6 +253,7 @@ ftdm_status_t get_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) ftdm_status_t get_called_num(ftdm_channel_t *ftdmchan, CdPtyNmb *cdPtyNmb) { ftdm_caller_data_t *caller_data = &ftdmchan->caller_data; + if (cdPtyNmb->eh.pres != PRSNT_NODEF) { return FTDM_FAIL; } @@ -369,7 +370,6 @@ ftdm_status_t get_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8 my_data[1] = data_len; memcpy(&my_data[2], data, data_len); - sngisdn_add_raw_data((sngisdn_chan_data_t*)ftdmchan->call_data, data, data_len+2); ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Raw Facility IE copied available\n"); @@ -381,7 +381,7 @@ ftdm_status_t get_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8 ftdm_status_t get_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd) { - uint8_t val; + uint8_t val; if (!progInd->eh.pres) { return FTDM_FAIL; } @@ -492,7 +492,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) uint8_t len,val; ftdm_caller_data_t *caller_data = &ftdmchan->caller_data; - string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.digits"); + string = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.cg_pty2.digits"); if ((string == NULL) || !(*string)) { return FTDM_FAIL; } @@ -509,7 +509,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->screenInd.pres = PRSNT_NODEF; val = FTDM_SCREENING_INVALID; - string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.screening_ind"); + string = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.cg_pty2.screening_ind"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_screening(string); } @@ -526,7 +526,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->presInd0.pres = PRSNT_NODEF; val = FTDM_PRES_INVALID; - string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.presentation_ind"); + string = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.cg_pty2.presentation_ind"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_presentation(string); } @@ -541,7 +541,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) cgPtyNmb->nmbPlanId.pres = PRSNT_NODEF; val = FTDM_NPI_INVALID; - string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.npi"); + string = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.cg_pty2.npi"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_npi(string); } @@ -556,7 +556,7 @@ ftdm_status_t set_calling_num2(ftdm_channel_t *ftdmchan, CgPtyNmb *cgPtyNmb) /* Type of Number */ val = FTDM_TON_INVALID; - string = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.cg_pty2.ton"); + string = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.cg_pty2.ton"); if ((string != NULL) && (*string)) { val = ftdm_str2ftdm_ton(string); } @@ -690,7 +690,7 @@ ftdm_status_t set_calling_name(ftdm_channel_t *ftdmchan, ConEvnt *conEvnt) ftdm_status_t set_calling_subaddr(ftdm_channel_t *ftdmchan, CgPtySad *cgPtySad) { const char* clg_subaddr = NULL; - clg_subaddr = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.calling_subaddr"); + clg_subaddr = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.calling_subaddr"); if ((clg_subaddr != NULL) && (*clg_subaddr)) { unsigned len = strlen (clg_subaddr); cgPtySad->eh.pres = PRSNT_NODEF; @@ -723,7 +723,7 @@ ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8 ftdm_size_t len; uint8_t *mydata; - if (ftdm_event_get_raw_data(ftdmchan->sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (ftdm_usrmsg_get_raw_data(ftdmchan->usrmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { if (len > 2 && mydata[0] == SNGISDN_Q931_FACILITY_IE_ID) { len = mydata[1]; memcpy(data, &mydata[2], len); @@ -740,7 +740,7 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s int descr = prog_ind.descr; int loc = prog_ind.loc; - str = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.prog_ind.descr"); + str = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.prog_ind.descr"); if (str && *str) { /* User wants to override progress indicator */ descr = ftdm_str2ftdm_sngisdn_progind_descr(str); @@ -751,7 +751,7 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s return FTDM_SUCCESS; } - str = ftdm_event_get_var(ftdmchan->sigmsg, "isdn.prog_ind.loc"); + str = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.prog_ind.loc"); if (str && *str) { loc = ftdm_str2ftdm_sngisdn_progind_loc(str); } @@ -1219,7 +1219,7 @@ ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* d sngisdn_info->raw_data = ftdm_calloc(1, data_len); ftdm_assert_return(sngisdn_info->raw_data, FTDM_FAIL, "Failed to allocate raw data\n"); - + memcpy(sngisdn_info->raw_data, data, data_len); sngisdn_info->raw_data_len = data_len; return FTDM_SUCCESS; @@ -1256,7 +1256,6 @@ void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t sigev.raw.data = sngisdn_info->raw_data; sigev.raw.len = sngisdn_info->raw_data_len; - sigev.raw.autofree = 1; sngisdn_info->raw_data = NULL; sngisdn_info->raw_data_len = 0; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index 9feb5c8c43..88b166a3af 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -139,7 +139,6 @@ void sngisdn_trace_raw_q921(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t d memcpy(raw_data, data, data_len); sigev.raw.data = raw_data; sigev.raw.len = data_len; - sigev.raw.autofree = 1; ftdm_span_send_signal(signal_data->ftdm_span, &sigev); } @@ -253,7 +252,6 @@ void sngisdn_trace_raw_q931(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t d memcpy(raw_data, data, data_len); sigev.raw.data = raw_data; sigev.raw.len = data_len; - sigev.raw.autofree = 1; ftdm_span_send_signal(signal_data->ftdm_span, &sigev); } } diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 84adf327dd..86c9b739c9 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -339,8 +339,7 @@ typedef struct ftdm_caller_data { ftdm_calling_party_category_t cpc; /*!< Calling party category */ ftdm_channel_t *fchan; /*!< FreeTDM channel associated (can be NULL) */ - ftdm_sigmsg_t *sigmsg; /*!< Set by user if additional parameters need to sent */ - + /* * We need call_id inside caller_data for the user to be able to retrieve * the call_id when ftdm_channel_call_place is called. This is the only time @@ -534,9 +533,10 @@ typedef enum { FTDM_CHANNEL_INDICATE_BUSY, /* Using this indication is equivalent to call ftdm_channel_call_answer API */ FTDM_CHANNEL_INDICATE_ANSWER, + FTDM_CHANNEL_INDICATE_FACILITY, FTDM_CHANNEL_INDICATE_INVALID, } ftdm_channel_indication_t; -#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "INVALID" +#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "FACILITY", "INVALID" /*! \brief Move from string to ftdm_channel_indication_t and viceversa */ FTDM_STR2ENUM_P(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t) @@ -550,7 +550,7 @@ typedef struct { typedef void * ftdm_variable_container_t; -/*! \brief Generic signaling message */ +/*! \brief Generic signaling message received from the stack */ struct ftdm_sigmsg { ftdm_signal_event_t event_id; /*!< The type of message */ ftdm_channel_t *channel; /*!< Related channel */ @@ -566,7 +566,16 @@ struct ftdm_sigmsg { ftdm_event_indication_completed_t indication_completed; /*!< valid if the event_id is FTDM_SIGEVENT_INDICATION_COMPLETED */ } ev_data; struct { - uint8_t autofree; /*!< Whether the freetdm core will free it after message delivery */ + ftdm_size_t len; /*!< Data len */ + void *data; /*!< Signaling module specific data */ + } raw; +}; + +/*! \brief Generic user message sent to the stack */ +struct ftdm_usrmsg { + uint32_t call_id; /*!< unique call id for this call */ + ftdm_variable_container_t variables; + struct { ftdm_size_t len; /*!< Data len */ void *data; /*!< Signaling module specific data */ } raw; @@ -702,7 +711,7 @@ struct ftdm_memory_handler { * You don't need these unless your implementing an I/O interface module (most users don't) */ #define FIO_CHANNEL_REQUEST_ARGS (ftdm_span_t *span, uint32_t chan_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) #define FIO_CHANNEL_OUTGOING_CALL_ARGS (ftdm_channel_t *ftdmchan) -#define FIO_CHANNEL_SEND_MSG_ARGS (ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg) +#define FIO_CHANNEL_INDICATE_ARGS (ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication) #define FIO_CHANNEL_SET_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) #define FIO_CHANNEL_GET_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *status) #define FIO_SPAN_SET_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t status) @@ -735,7 +744,7 @@ struct ftdm_memory_handler { * You don't need these unless your implementing an I/O interface module (most users don't) */ typedef ftdm_status_t (*fio_channel_request_t) FIO_CHANNEL_REQUEST_ARGS ; typedef ftdm_status_t (*fio_channel_outgoing_call_t) FIO_CHANNEL_OUTGOING_CALL_ARGS ; -typedef ftdm_status_t (*fio_channel_send_msg_t) FIO_CHANNEL_SEND_MSG_ARGS; +typedef ftdm_status_t (*fio_channel_indicate_t) FIO_CHANNEL_INDICATE_ARGS; typedef ftdm_status_t (*fio_channel_set_sig_status_t) FIO_CHANNEL_SET_SIG_STATUS_ARGS; typedef ftdm_status_t (*fio_channel_get_sig_status_t) FIO_CHANNEL_GET_SIG_STATUS_ARGS; typedef ftdm_status_t (*fio_span_set_sig_status_t) FIO_SPAN_SET_SIG_STATUS_ARGS; @@ -782,7 +791,7 @@ typedef ftdm_status_t (*fio_api_t) FIO_API_ARGS ; * You don't need these unless your implementing an I/O interface module (most users don't) */ #define FIO_CHANNEL_REQUEST_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_REQUEST_ARGS #define FIO_CHANNEL_OUTGOING_CALL_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_OUTGOING_CALL_ARGS -#define FIO_CHANNEL_SEND_MSG_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_SEND_MSG_ARGS +#define FIO_CHANNEL_INDICATE_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_INDICATE_ARGS #define FIO_CHANNEL_SET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_SET_SIG_STATUS_ARGS #define FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_GET_SIG_STATUS_ARGS #define FIO_SPAN_SET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_SPAN_SET_SIG_STATUS_ARGS @@ -870,23 +879,26 @@ FT_DECLARE(int) ftdm_channel_get_availability(ftdm_channel_t *ftdmchan); * there is no guarantee of whether the event will arrive after or before your execution thread returns * from ftdm_channel_call_answer */ -#define ftdm_channel_call_answer(ftdmchan) _ftdm_channel_call_answer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_call_answer(ftdmchan) _ftdm_channel_call_answer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_call_answer_ex(ftdmchan, usrmsg) _ftdm_channel_call_answer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg)) /*! \brief Answer call recording the source code point where the it was called (see ftdm_channel_call_answer for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Place an outgoing call in the given channel * \deprecated This macro is deprecated since leaves the door open to glare issues, use ftdm_call_place instead */ -#define ftdm_channel_call_place(ftdmchan) _ftdm_channel_call_place(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_call_place(ftdmchan) _ftdm_channel_call_place(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_call_place_ex(ftdmchan, usrmsg) _ftdm_channel_call_place_ex(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg)) /*! \brief Place an outgoing call recording the source code point where it was called (see ftdm_channel_call_place for an easy to use macro) * \deprecated This function is deprecated since leaves the door open to glare issues, use ftdm_call_place instead */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Place an outgoing call with the given caller data in a channel according to the hunting scheme provided */ -#define ftdm_call_place(callerdata, hunting) _ftdm_call_place(__FILE__, __FUNCTION__, __LINE__, (callerdata), (hunting)) +#define ftdm_call_place(callerdata, hunting) _ftdm_call_place(__FILE__, __FUNCTION__, __LINE__, (callerdata), (hunting), NULL) +#define ftdm_call_place_ex(callerdata, hunting, usrmsg) _ftdm_call_place(__FILE__, __FUNCTION__, __LINE__, (callerdata), (hunting), (usrmsg)) /*! \brief Place an outgoing call with the given caller data in a channel according to the hunting scheme provided and records * the place where it was called. See ftdm_call_place for an easy to use macro @@ -903,7 +915,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char * \note When this function is successful you are guaranteed to receive FTDM_SIGEVENT_DIALING, this event could even be delivered * before your execution thread returns from this function */ -FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, int line, ftdm_caller_data_t *caller_data, ftdm_hunting_scheme_t *hunting); +FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, int line, ftdm_caller_data_t *caller_data, ftdm_hunting_scheme_t *hunting, ftdm_usrmsg_t *usrmsg); /*! \brief Indicate a new condition in an incoming call * @@ -918,48 +930,48 @@ FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, i * \note You cannot send more than one indication at the time. You must wait for the completed event before * calling this function again (unless the return code was different than FTDM_SUCCESS) */ -#define ftdm_channel_call_indicate(ftdmchan, indication) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication)) +#define ftdm_channel_call_indicate(ftdmchan, indication) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication), NULL) +#define ftdm_channel_call_indicate_ex(ftdmchan, indication, usrmsg) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication), (usrmsg)) /*! \brief Indicate a new condition in an incoming call recording the source code point where it was called (see ftdm_channel_call_indicate for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication); - -/*! \brief Send a message on a call */ -#define ftdm_channel_call_send_msg(ftdmchan, sigmsg) _ftdm_channel_call_send_msg(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (sigmsg)) - -/*! \brief Send a signal on a call recording the source code point where it was called (see ftdm_channel_call_send_msg for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_usrmsg_t *usrmsg); /*! \brief Hangup the call without cause */ -#define ftdm_channel_call_hangup(ftdmchan) _ftdm_channel_call_hangup(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_call_hangup(ftdmchan) _ftdm_channel_call_hangup(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_call_hangup_ex(ftdmchan, usrmsg) _ftdm_channel_call_hangup(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg)) /*! \brief Hangup the call without cause recording the source code point where it was called (see ftdm_channel_call_hangup for an easy to use macro)*/ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Hangup the call with cause */ -#define ftdm_channel_call_hangup_with_cause(ftdmchan, cause) _ftdm_channel_call_hangup_with_cause(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (cause)) +#define ftdm_channel_call_hangup_with_cause(ftdmchan, cause) _ftdm_channel_call_hangup_with_cause(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (cause), NULL) +#define ftdm_channel_call_hangup_with_cause_ex(ftdmchan, cause, usrmsg) _ftdm_channel_call_hangup_with_cause(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (cause), (usrmsg)) /*! \brief Hangup the call with cause recording the source code point where it was called (see ftdm_channel_call_hangup_with_cause for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t, ftdm_usrmsg_t *usrmsg); /*! \brief Reset the channel */ -#define ftdm_channel_reset(ftdmchan) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_reset(ftdmchan) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_reset_ex(ftdmchan, usrmsg) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), usrmsg) /*! \brief Reset the channel (see _ftdm_channel_reset for an easy to use macro) * \note if there was a call on this channel, call will be cleared without any notifications to the user */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Put a call on hold (if supported by the signaling stack) */ -#define ftdm_channel_call_hold(ftdmchan) _ftdm_channel_call_hold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_call_hold(ftdmchan) _ftdm_channel_call_hold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_call_hold_ex(ftdmchan, usrmsg) _ftdm_channel_call_hold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg)) /*! \brief Put a call on hold recording the source code point where it was called (see ftdm_channel_call_hold for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Unhold a call */ -#define ftdm_channel_call_unhold(ftdmchan) _ftdm_channel_call_unhold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan)) +#define ftdm_channel_call_unhold(ftdmchan) _ftdm_channel_call_unhold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL) +#define ftdm_channel_call_unhold_ex(ftdmchan, usrmsg) _ftdm_channel_call_unhold(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg)) /*! \brief Unhold a call recording the source code point where it was called (see ftdm_channel_call_unhold for an easy to use macro) */ -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); /*! \brief Check if the call is answered already */ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_answered(const ftdm_channel_t *ftdmchan); @@ -1373,40 +1385,29 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data */ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t datasize, ftdm_size_t *datalen); -/*! \brief Add a custom variable to the event - * \note This variables may be used by signaling modules to override signaling parameters - * \todo Document which signaling variables are available - * */ -FT_DECLARE(ftdm_status_t) ftdm_event_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value); - -/*! \brief Remove a custom variable from the event +/*! \brief Get a custom variable from the sigmsg * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ -FT_DECLARE(ftdm_status_t) ftdm_event_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name); +FT_DECLARE(const char *) ftdm_sigmsg_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name); -/*! \brief Get a custom variable from the event - * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ -FT_DECLARE(const char *) ftdm_event_get_var(ftdm_sigmsg_t *sigmsg, const char *var_name); - -/*! \brief Get an iterator to iterate over the event variables +/*! \brief Get an iterator to iterate over the sigmsg variables * \param sigmsg The message structure containing the variables * \param iter Optional iterator. You can reuse an old iterator (not previously freed) to avoid the extra allocation of a new iterator. * \note The iterator pointer returned is only valid while the channel is open and it'll be destroyed when the channel is closed. * This iterator is completely non-thread safe, if you are adding variables or removing variables while iterating * results are unpredictable */ -FT_DECLARE(ftdm_iterator_t *) ftdm_event_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter); +FT_DECLARE(ftdm_iterator_t *) ftdm_sigmsg_get_var_iterator(const ftdm_sigmsg_t *sigmsg, ftdm_iterator_t *iter); - -/*! \brief Attach raw data to event +/*! \brief Get raw data from sigmsg * \param sigmsg The message structure containing the variables - * \param data pointer to data - * \param datalen datalen length of data - * \param autofree when set to non-zero, freetdm will automatically free data once event is processed - * \retval FTDM_SUCCESS success, data was successfully saved - * \retval FTDM_FAIL failed, event already had data attached to it. - * \note When using autofree, data must have been allocated using ftdm_calloc. + * \param data data will point to available data pointer if available + * \param datalen datalen will be set to length of data available + * \retval FTDM_SUCCESS data is available + * \retval FTDM_FAIL no data available + * \note data is only valid within the duration of the callback, to receive a data pointer that does not get + * \note destroyed when callback returns, see ftdm_sigmsg_get_raw_data_detached */ -FT_DECLARE(ftdm_status_t) ftdm_event_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen, uint8_t autofree); +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen); /*! \brief Get raw data from event * \param sigmsg The message structure containing the variables @@ -1414,8 +1415,25 @@ FT_DECLARE(ftdm_status_t) ftdm_event_set_raw_data(ftdm_sigmsg_t *sigmsg, void *d * \param datalen datalen will be set to length of data available * \retval FTDM_SUCCESS data is available * \retval FTDM_FAIL no data available + * \note Once this function returns, User owns data, and is responsible to free data using ftdm_safe_free(); */ -FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen); +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_get_raw_data_detached(ftdm_sigmsg_t *sigmsg, void **data, ftdm_size_t *datalen); + +/*! \brief Add a custom variable to the user message + * \note This variables may be used by signaling modules to override signaling parameters + * \todo Document which signaling variables are available + * */ +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_add_var(ftdm_usrmsg_t *usrmsg, const char *var_name, const char *value); + +/*! \brief Attach raw data to usrmsg + * \param usrmsg The message structure containing the variables + * \param data pointer to data + * \param datalen datalen length of data + * \retval FTDM_SUCCESS success, data was successfully saved + * \retval FTDM_FAIL failed, event already had data attached to it. + * \note data must have been allocated using ftdm_calloc, FreeTDM will free data once the usrmsg is processed. + */ +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_set_raw_data(ftdm_usrmsg_t *usrmsg, void *data, ftdm_size_t datalen); /*! \brief Get iterator current value (depends on the iterator type) * \note Channel iterators return a pointer to ftdm_channel_t @@ -1424,7 +1442,7 @@ FT_DECLARE(ftdm_status_t) ftdm_event_get_raw_data(ftdm_sigmsg_t *sigmsg, void ** FT_DECLARE(void *) ftdm_iterator_current(ftdm_iterator_t *iter); /*! \brief Get variable name and value for the current iterator position */ -FT_DECLARE(ftdm_status_t) ftdm_event_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val); +FT_DECLARE(ftdm_status_t) ftdm_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val); /*! \brief Advance iterator */ FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter); diff --git a/libs/freetdm/src/include/ftdm_declare.h b/libs/freetdm/src/include/ftdm_declare.h index b443f2d5f5..9c7f9cbe4a 100644 --- a/libs/freetdm/src/include/ftdm_declare.h +++ b/libs/freetdm/src/include/ftdm_declare.h @@ -223,6 +223,7 @@ typedef struct ftdm_conf_node ftdm_conf_node_t; typedef struct ftdm_group ftdm_group_t; typedef size_t ftdm_size_t; typedef struct ftdm_sigmsg ftdm_sigmsg_t; +typedef struct ftdm_usrmsg ftdm_usrmsg_t; typedef struct ftdm_io_interface ftdm_io_interface_t; typedef struct ftdm_stream_handle ftdm_stream_handle_t; typedef struct ftdm_queue ftdm_queue_t; diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index fa519be9f5..ae8adfb203 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -468,7 +468,7 @@ struct ftdm_channel { ftdm_interrupt_t *state_completed_interrupt; /*!< Notify when a state change is completed */ int32_t txdrops; int32_t rxdrops; - ftdm_sigmsg_t *sigmsg; + ftdm_usrmsg_t *usrmsg; }; struct ftdm_span { @@ -494,7 +494,7 @@ struct ftdm_span { teletone_multi_tone_t tone_finder[FTDM_TONEMAP_INVALID+1]; ftdm_channel_t *channels[FTDM_MAX_CHANNELS_SPAN+1]; fio_channel_outgoing_call_t outgoing_call; - fio_channel_send_msg_t send_msg; + fio_channel_indicate_t indicate; fio_channel_set_sig_status_t set_channel_sig_status; fio_channel_get_sig_status_t get_channel_sig_status; fio_span_set_sig_status_t set_span_sig_status; @@ -636,14 +636,50 @@ FT_DECLARE(void) ftdm_set_echocancel_call_begin(ftdm_channel_t *chan); /*! \brief adjust echocanceller for end of call */ FT_DECLARE(void) ftdm_set_echocancel_call_end(ftdm_channel_t *chan); -/*! \brief clear variables hashtable inside sigmsg */ -FT_DECLARE(ftdm_status_t) ftdm_event_clear_vars(ftdm_sigmsg_t *sigmsg); -/*! \brief save data from user(ftdmchan->caller_data.sigmsg) into internal copy (ftdmchan->sigmsg) */ -FT_DECLARE(ftdm_status_t) ftdm_channel_save_event_data(ftdm_channel_t *ftdmchan); +/*! \brief save data from user */ +FT_DECLARE(ftdm_status_t) ftdm_channel_save_usrmsg(ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg); -/*! \brief free data from internal copy (ftdmchan->sigmsg) */ -FT_DECLARE(ftdm_status_t) ftdm_channel_clear_event_data(ftdm_channel_t *ftdmchan); +/*! \brief free usrmsg and variables/raw data attached to it */ +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_free(ftdm_usrmsg_t **usrmsg); + +/*! \brief Get a custom variable from the user message + * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ +FT_DECLARE(const char *) ftdm_usrmsg_get_var(ftdm_usrmsg_t *usrmsg, const char *var_name); + +/*! \brief Get raw data from user message + * \param usrmsg The message structure containing the variables + * \param data data will point to available data pointer if available + * \param datalen datalen will be set to length of data available + * \retval FTDM_SUCCESS data is available + * \retval FTDM_FAIL no data available + * \note data is only valid within the duration of the callback, to receive a data pointer that does not get + * \note destroyed when callback returns, see ftdm_sigmsg_get_raw_data_detached + */ +FT_DECLARE(ftdm_status_t) ftdm_usrmsg_get_raw_data(ftdm_usrmsg_t *usrmsg, void **data, ftdm_size_t *datalen); + +/*! \brief free sigmsg and variables/raw data attached to it */ +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_free(ftdm_sigmsg_t **sigmsg); + +/*! \brief Add a custom variable to the event + * \note This variables may be used by signaling modules to override signaling parameters + * \todo Document which signaling variables are available + * */ +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_add_var(ftdm_sigmsg_t *sigmsg, const char *var_name, const char *value); + +/*! \brief Remove a custom variable from the event + * \note The variable pointer returned is only valid while the before the event is processed and it'll be destroyed once the event is processed. */ +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_remove_var(ftdm_sigmsg_t *sigmsg, const char *var_name); + +/*! \brief Attach raw data to sigmsg + * \param sigmsg The message structure containing the variables + * \param data pointer to data + * \param datalen datalen length of data + * \retval FTDM_SUCCESS success, data was successfully saved + * \retval FTDM_FAIL failed, event already had data attached to it. + * \note data must have been allocated using ftdm_calloc, FreeTDM will free data once the usrmsg is processed. + */ +FT_DECLARE(ftdm_status_t) ftdm_sigmsg_set_raw_data(ftdm_sigmsg_t *sigmsg, void *data, ftdm_size_t datalen); /*! \brief Assert condition diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h index 5672839d15..191f8aef82 100644 --- a/libs/freetdm/src/include/private/ftdm_state.h +++ b/libs/freetdm/src/include/private/ftdm_state.h @@ -188,7 +188,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char * \note If this function is called with the wait parameter set to a non-zero value, the recursivity * of the channel lock must be == 1 because the channel will be unlocked/locked when waiting */ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, - ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait); + ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait, ftdm_usrmsg_t *usrmsg); /*!\brief Set the state of a channel immediately and implicitly complete the previous state if needed * \note FTDM_SIGEVENT_INDICATION_COMPLETED will be sent if the state change @@ -207,7 +207,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, in #define ftdm_set_state_locked(obj, s) \ do { \ ftdm_channel_lock(obj); \ - ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \ + ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0, NULL); \ ftdm_channel_unlock(obj); \ } while(0); From 687d39f259cf62bea5c6508acd76d8732dcc0e11 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 25 Feb 2011 10:35:25 -0500 Subject: [PATCH 063/154] freetdm: ftmod_analog - Add timeout parameter for dial tone. 0 means not waiting for dial tone. --- libs/freetdm/conf/freetdm.conf.xml | 3 +++ libs/freetdm/mod_freetdm/mod_freetdm.c | 4 ++++ libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/conf/freetdm.conf.xml b/libs/freetdm/conf/freetdm.conf.xml index 43197af4bc..7d5de5a189 100644 --- a/libs/freetdm/conf/freetdm.conf.xml +++ b/libs/freetdm/conf/freetdm.conf.xml @@ -54,6 +54,9 @@ with the signaling protocols that you can run on top of your I/O interfaces. + + + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 4dc6cfcfd2..b746f51b10 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -2790,6 +2790,7 @@ static switch_status_t load_config(void) const char *hangup_polarity = "false"; int polarity_delay = 600; int callwaiting = 1; + int dialtone_timeout = 5000; uint32_t span_id = 0, to = 0, max = 0; ftdm_span_t *span = NULL; @@ -2830,6 +2831,8 @@ static switch_status_t load_config(void) tonegroup = val; } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { digit_timeout = val; + } else if (!strcasecmp(var, "wait-dialtone-timeout")) { + dialtone_timeout = atoi(val); } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "dialplan")) { @@ -2930,6 +2933,7 @@ static switch_status_t load_config(void) "hangup_polarity_reverse", hangup_polarity, "polarity_delay", &polarity_delay, "callwaiting", &callwaiting, + "wait_dialtone_timeout", &dialtone_timeout, FTDM_TAG_END) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span)); continue; diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c index 818f1c5754..d25d38f3d7 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c @@ -182,7 +182,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) const char *tonemap = "us"; const char *hotline = ""; uint32_t digit_timeout = 10; - uint32_t wait_dialtone_timeout = 30000; + uint32_t wait_dialtone_timeout = 5000; uint32_t max_dialstr = MAX_DTMF; uint32_t polarity_delay = 600; const char *var, *val; From 06bd6330533a3406ce86f866206d2aadeae2dfb4 Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 25 Feb 2011 11:05:11 -0500 Subject: [PATCH 064/154] freetdm: updated variables.txt --- libs/freetdm/docs/variables.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libs/freetdm/docs/variables.txt b/libs/freetdm/docs/variables.txt index 7d95a67c47..bb8729051e 100644 --- a/libs/freetdm/docs/variables.txt +++ b/libs/freetdm/docs/variables.txt @@ -14,7 +14,6 @@ To make an outbound call: /* Attach variable to usrmsg */ ftdm_usrmsg_add_var(&usrmsg, "isdn.prog_ind.descr", "inband-info-available"); - /* Request FreeTDM to send a PROCEED msg */ ftdm_channel_call_place_ex(ftdmchan, &usrmsg); example #1b - Adding a variable: @@ -37,7 +36,6 @@ example #2 - Setting raw data: When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, inside a FACILITY message. ftdm_usrmsg_t usrmsg; - ftdm_sigmsg_t sigmsg; uint8_t *my_facility_ie = ftdm_calloc(1, 200); /*memory has to be allocated using ftdm_calloc !! */ unsigned my_facility_ie_len = 0; @@ -51,9 +49,7 @@ When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, insi ftdm_usrmsg_set_raw_data(&usrmsg, my_facility_ie, my_facility_ie_len); - sigmsg.event_id = FTDM_SIGEVENT_FACILITY; - - ftdm_channel_call_send_msg(ftdmchan, sigmsg, usrmsg); + ftdm_channel_call_indicate(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, &usrmsg); /* FreeTDM will automatically free my_facility_ie */ @@ -96,7 +92,7 @@ example #3a - accessing raw data /* Inside event call-back function */ ftdm_size_t len; uint8_t *mydata; - if (ftdm_sig_get_raw_data(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (ftdm_sigmsg_get_raw_data(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { /* raw data is available, do something with mydata here */ } /* Once this function returns, raw data will be free'd inside FreeTDM */ @@ -108,7 +104,7 @@ example #3b - accessing raw data /* Inside event call-back function */ ftdm_size_t len; uint8_t *mydata; - if (ftdm_sig_get_raw_data_detached(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { + if (ftdm_sigmsg_get_raw_data_detached(sigmsg, (void**)&mydata, &len) == FTDM_SUCCESS) { /* raw data is available, do something with mydata here */ } From f8e1fa666d8efa5122f0de5020bd961c4c0ed79f Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 25 Feb 2011 11:07:39 -0500 Subject: [PATCH 065/154] freetdm: updated variables.txt --- libs/freetdm/docs/variables.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/docs/variables.txt b/libs/freetdm/docs/variables.txt index bb8729051e..0beecd016c 100644 --- a/libs/freetdm/docs/variables.txt +++ b/libs/freetdm/docs/variables.txt @@ -49,7 +49,7 @@ When using ftmod_sangoma_isdn, user wants to transmit a custom Facility IE, insi ftdm_usrmsg_set_raw_data(&usrmsg, my_facility_ie, my_facility_ie_len); - ftdm_channel_call_indicate(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, &usrmsg); + ftdm_channel_call_indicate(ftdmchan, FTDM_CHANNEL_INDICATE_FACILITY, &usrmsg); /* FreeTDM will automatically free my_facility_ie */ From bc397ab6000d8e8acaeecf351a3281428226d0d9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 25 Feb 2011 10:55:33 -0600 Subject: [PATCH 066/154] FS-2971 --- libs/esl/src/esl_event.c | 2 + libs/esl/src/include/esl_event.h | 2 + src/include/switch_types.h | 2 + src/switch_channel.c | 3 ++ src/switch_event.c | 2 + src/switch_ivr_play_say.c | 78 +++++++++++++++++++++++++++++--- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index db7c581ee9..9bd78a838d 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -131,6 +131,8 @@ static const char *EVENT_NAMES[] = { "NAT", "RECORD_START", "RECORD_STOP", + "PLAYBACK_START", + "PLAYBACK_STOP", "CALL_UPDATE", "FAILURE", "SOCKET_DATA", diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h index 0e6d3e37db..7e619f4764 100644 --- a/libs/esl/src/include/esl_event.h +++ b/libs/esl/src/include/esl_event.h @@ -119,6 +119,8 @@ typedef enum { ESL_EVENT_NAT, ESL_EVENT_RECORD_START, ESL_EVENT_RECORD_STOP, + ESL_EVENT_PLAYBACK_START, + ESL_EVENT_PLAYBACK_STOP, ESL_EVENT_CALL_UPDATE, ESL_EVENT_FAILURE, ESL_EVENT_SOCKET_DATA, diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 3972404290..4253d0e542 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1504,6 +1504,8 @@ typedef enum { SWITCH_EVENT_NAT, SWITCH_EVENT_RECORD_START, SWITCH_EVENT_RECORD_STOP, + SWITCH_EVENT_PLAYBACK_START, + SWITCH_EVENT_PLAYBACK_STOP, SWITCH_EVENT_CALL_UPDATE, SWITCH_EVENT_FAILURE, SWITCH_EVENT_SOCKET_DATA, diff --git a/src/switch_channel.c b/src/switch_channel.c index 9e3064b625..179f63da79 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -2042,10 +2042,13 @@ SWITCH_DECLARE(void) switch_channel_event_set_extended_data(switch_channel_t *ch event->event_id == SWITCH_EVENT_REQUEST_PARAMS || event->event_id == SWITCH_EVENT_CHANNEL_DATA || event->event_id == SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE || + event->event_id == SWITCH_EVENT_CHANNEL_DESTROY || event->event_id == SWITCH_EVENT_SESSION_HEARTBEAT || event->event_id == SWITCH_EVENT_API || event->event_id == SWITCH_EVENT_RECORD_START || event->event_id == SWITCH_EVENT_RECORD_STOP || + event->event_id == SWITCH_EVENT_PLAYBACK_START || + event->event_id == SWITCH_EVENT_PLAYBACK_STOP || event->event_id == SWITCH_EVENT_CALL_UPDATE || event->event_id == SWITCH_EVENT_MEDIA_BUG_START || event->event_id == SWITCH_EVENT_MEDIA_BUG_STOP || diff --git a/src/switch_event.c b/src/switch_event.c index b626d5b846..56ee9e9a9c 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -189,6 +189,8 @@ static char *EVENT_NAMES[] = { "NAT", "RECORD_START", "RECORD_STOP", + "PLAYBACK_START", + "PLAYBACK_STOP", "CALL_UPDATE", "FAILURE", "SOCKET_DATA", diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index dc263e9be5..98dfd0f71e 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -746,12 +746,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se } - if (switch_event_create(&event, SWITCH_EVENT_RECORD_STOP) == SWITCH_STATUS_SUCCESS) { - switch_channel_event_set_data(channel, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", file); - switch_event_fire(&event); - } - if (fill_cng || waste_resources) { switch_core_codec_destroy(&write_codec); } @@ -766,6 +760,12 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se switch_channel_set_variable_printf(channel, "record_samples", "%d", fh->samples_out); + if (switch_event_create(&event, SWITCH_EVENT_RECORD_STOP) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", file); + switch_event_fire(&event); + } + switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); return status; } @@ -951,6 +951,38 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_release_file_handle(switch_core_sessi #define FILE_BLOCKSIZE 1024 * 8 #define FILE_BUFSIZE 1024 * 64 +static void add_playback_vars_to_event(switch_core_session_t *session, switch_event_t *event, char *vars) +{ + char *tmp; + + if (!session || !event || !vars) + return; + + if ((tmp = switch_core_session_strdup(session, vars))) { + char *argv[128] = { 0 }; + int argc, i; + + if (!(argc = switch_separate_string(tmp, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) + return; + + for (i = 0; i < argc; i++) { + char *var, *val; + + if ((var = strchr(argv[i], '='))) { + *var = '\0'; + val = var+1; + var = argv[i]; + + if (var && *var && val && *val) { + if ((var = switch_core_session_sprintf(session, "playback_variable_%s", var))) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, var, val); + } + } + } + } + } +} + SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *session, switch_file_handle_t *fh, const char *file, switch_input_args_t *args) { switch_channel_t *channel = switch_core_session_get_channel(session); @@ -990,6 +1022,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess int timeout_samples = 0; const char *var; int more_data = 0; + char *playback_vars, *tmp; + switch_event_t *event; if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; @@ -1138,6 +1172,19 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess } } + /* Try to parse extra parameters for this playback (parameters within {} at the end of the filename */ + playback_vars = NULL; + if ((tmp = strchr(file, '{'))) { + char *tfile, *e; + + if ((tfile = switch_core_session_strdup(session, tmp))) { + if ((e = switch_find_end_paren(tfile, '{', '}')) && *(e + 1) == '\0') { + *tmp = '\0'; + *e = '\0'; + playback_vars = tfile+1; + } + } + } if (!fh) { fh = &lfh; @@ -1293,6 +1340,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess ilen = samples; + if (switch_event_create(&event, SWITCH_EVENT_PLAYBACK_START) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Playback-File-Path", file); + add_playback_vars_to_event(session, event, playback_vars); + switch_event_fire(&event); + } + for (;;) { int do_speed = 1; int last_speed = -1; @@ -1586,6 +1640,18 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess } switch_channel_set_variable_printf(channel, "playback_samples", "%d", fh->samples_in); + if (switch_event_create(&event, SWITCH_EVENT_PLAYBACK_STOP) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Playback-File-Path", file); + if (status == SWITCH_STATUS_BREAK) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Playback-Status", "break"); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Playback-Status", "done"); + } + add_playback_vars_to_event(session, event, playback_vars); + switch_event_fire(&event); + } + switch_core_session_io_write_lock(session); switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); From b2b50a1030874fa1cac33052d20327d4d18be0fe Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Fri, 25 Feb 2011 12:20:32 -0500 Subject: [PATCH 067/154] freetdm: Removed unnecessary parameter copy Added ftdm_raw_data_t --- libs/freetdm/src/ftdm_io.c | 7 ++----- libs/freetdm/src/ftdm_variables.c | 2 +- libs/freetdm/src/include/freetdm.h | 16 +++++++--------- libs/freetdm/src/include/private/ftdm_core.h | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 4aea149234..8059d1ee4c 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -4049,7 +4049,7 @@ done: return status; } -FT_DECLARE(ftdm_iterator_t) *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter) +FT_DECLARE(ftdm_iterator_t) *ftdm_get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter) { int allocated = 0; if (iter) { @@ -4075,7 +4075,7 @@ FT_DECLARE(ftdm_iterator_t) *get_iterator(ftdm_iterator_type_t type, ftdm_iterat FT_DECLARE(ftdm_iterator_t *) ftdm_span_get_chan_iterator(const ftdm_span_t *span, ftdm_iterator_t *iter) { - if (!(iter = get_iterator(FTDM_ITERATOR_CHANS, iter))) { + if (!(iter = ftdm_get_iterator(FTDM_ITERATOR_CHANS, iter))) { return NULL; } iter->pvt.chaniter.index = 1; @@ -6100,9 +6100,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_save_usrmsg(ftdm_channel_t *ftdmchan, ftd memcpy(ftdmchan->usrmsg, usrmsg, sizeof(ftdm_usrmsg_t)); if (usrmsg->raw.data) { - ftdmchan->usrmsg->raw.data = usrmsg->raw.data; - ftdmchan->usrmsg->raw.len = usrmsg->raw.len; - usrmsg->raw.data = NULL; usrmsg->raw.len = 0; } diff --git a/libs/freetdm/src/ftdm_variables.c b/libs/freetdm/src/ftdm_variables.c index 96db383b6e..a2899c4ca9 100644 --- a/libs/freetdm/src/ftdm_variables.c +++ b/libs/freetdm/src/ftdm_variables.c @@ -92,7 +92,7 @@ FT_DECLARE(ftdm_iterator_t *) ftdm_sigmsg_get_var_iterator(const ftdm_sigmsg_t * return NULL; } - if (!(iter = get_iterator(FTDM_ITERATOR_VARS, iter))) { + if (!(iter = ftdm_get_iterator(FTDM_ITERATOR_VARS, iter))) { return NULL; } iter->pvt.hashiter = hashiter; diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 86c9b739c9..a34a2ca188 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -550,6 +550,11 @@ typedef struct { typedef void * ftdm_variable_container_t; +typedef struct { + ftdm_size_t len; + void *data; +} ftdm_raw_data_t; + /*! \brief Generic signaling message received from the stack */ struct ftdm_sigmsg { ftdm_signal_event_t event_id; /*!< The type of message */ @@ -565,20 +570,13 @@ struct ftdm_sigmsg { ftdm_event_collected_t collected; /*!< valid if event_id is FTDM_SIGEVENT_COLLECTED_DIGIT */ ftdm_event_indication_completed_t indication_completed; /*!< valid if the event_id is FTDM_SIGEVENT_INDICATION_COMPLETED */ } ev_data; - struct { - ftdm_size_t len; /*!< Data len */ - void *data; /*!< Signaling module specific data */ - } raw; + ftdm_raw_data_t raw; }; /*! \brief Generic user message sent to the stack */ struct ftdm_usrmsg { - uint32_t call_id; /*!< unique call id for this call */ ftdm_variable_container_t variables; - struct { - ftdm_size_t len; /*!< Data len */ - void *data; /*!< Signaling module specific data */ - } raw; + ftdm_raw_data_t raw; }; /*! \brief Crash policy diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index ae8adfb203..464b1c0209 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -590,7 +590,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan); FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status); -FT_DECLARE(ftdm_iterator_t) *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); +FT_DECLARE(ftdm_iterator_t) *ftdm_get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); /*! From f614a52fbbc22d5b2a297bd09667fe665c87f4d2 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 25 Feb 2011 12:45:01 -0500 Subject: [PATCH 068/154] freetdm: ftmod_r2 - Use unallocated number as reason to reject collect calls Brazil, the only country using this do not really makes a difference --- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 0b76f6d526..4a817482e3 100755 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -289,9 +289,6 @@ static ftdm_call_cause_t ftdm_r2_cause_to_ftdm_cause(ftdm_channel_t *fchan, open case OR2_CAUSE_UNSPECIFIED: return FTDM_CAUSE_NORMAL_UNSPECIFIED; - case OR2_CAUSE_COLLECT_CALL_REJECTED: - return FTDM_CAUSE_CALL_REJECTED; - case OR2_CAUSE_FORCED_RELEASE: return FTDM_CAUSE_NORMAL_CLEARING; @@ -682,7 +679,7 @@ static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, cons /* check if this is a collect call and if we should accept it */ if (!r2data->allow_collect_calls && category == OR2_CALLING_PARTY_CATEGORY_COLLECT_CALL) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Rejecting collect call\n"); - openr2_chan_disconnect_call(r2chan, OR2_CAUSE_COLLECT_CALL_REJECTED); + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_UNALLOCATED_NUMBER); } else { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } From d59d41d7b4d0609ebdbbab1592baa3f45240481e Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 25 Feb 2011 11:59:45 -0600 Subject: [PATCH 069/154] add param to jb to try to recapture latency (disabled by default) --- src/include/switch_rtp.h | 2 +- src/mod/endpoints/mod_sofia/mod_sofia.c | 13 +++++++++---- src/mod/endpoints/mod_sofia/sofia_glue.c | 12 ++++++++---- src/switch_ivr.c | 2 +- src/switch_rtp.c | 17 +++++++++++------ 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index 2870bba3af..94c9d671e9 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -232,7 +232,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_sessi SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session, uint32_t queue_frames, uint32_t max_queue_frames, - uint32_t samples_per_packet, uint32_t samples_per_second); + uint32_t samples_per_packet, uint32_t samples_per_second, uint32_t max_drift); SWITCH_DECLARE(switch_status_t) switch_rtp_debug_jitter_buffer(switch_rtp_t *rtp_session, const char *name); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 9e8d8200f4..e8e78e51f5 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -1349,10 +1349,10 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER: { if (switch_rtp_ready(tech_pvt->rtp_session)) { - int len, maxlen = 0, qlen = 0, maxqlen = 50; + int len, maxlen = 0, qlen = 0, maxqlen = 50, max_drift = 0; if (msg->string_arg) { - char *p; + char *p, *q; const char *s; if (!strcasecmp(msg->string_arg, "pause")) { @@ -1379,6 +1379,10 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi if ((p = strchr(msg->string_arg, ':'))) { p++; maxlen = atol(p); + if ((q = strchr(p, ':'))) { + q++; + max_drift = abs(atol(q)); + } } } @@ -1391,9 +1395,10 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi if (qlen) { if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen, tech_pvt->read_impl.samples_per_packet, - tech_pvt->read_impl.samples_per_second) == SWITCH_STATUS_SUCCESS) { + tech_pvt->read_impl.samples_per_second, max_drift) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), - SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames)\n", len, qlen, maxqlen); + SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames) (%d max drift)\n", + len, qlen, maxqlen, max_drift); switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 01a70f32b6..66f5e52140 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -3166,12 +3166,16 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f if ((val = switch_channel_get_variable(tech_pvt->channel, "jitterbuffer_msec")) || (val = tech_pvt->profile->jb_msec)) { int jb_msec = atoi(val); - int maxlen = 0; - char *p; - + int maxlen = 0, max_drift = 0; + char *p, *q; + if ((p = strchr(val, ':'))) { p++; maxlen = atoi(p); + if ((q = strchr(p, ':'))) { + q++; + max_drift = abs(atoi(q)); + } } if (jb_msec < 20 || jb_msec > 10000) { @@ -3188,7 +3192,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen, tech_pvt->read_impl.samples_per_packet, - tech_pvt->read_impl.samples_per_second) == SWITCH_STATUS_SUCCESS) { + tech_pvt->read_impl.samples_per_second, max_drift) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames)\n", jb_msec, qlen); switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER); diff --git a/src/switch_ivr.c b/src/switch_ivr.c index a40da1b077..5a082763b2 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2289,7 +2289,7 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3 qlen = delay_ms / (interval); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); - jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second); + jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0); write_frame.codec = switch_core_session_get_read_codec(session); diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 029f067ab5..b6b3832081 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -1909,7 +1909,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t * uint32_t queue_frames, uint32_t max_queue_frames, uint32_t samples_per_packet, - uint32_t samples_per_second) + uint32_t samples_per_second, + uint32_t max_drift) { if (!switch_rtp_ready(rtp_session)) { @@ -1920,7 +1921,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t * if (rtp_session->jb) { stfu_n_resize(rtp_session->jb, queue_frames); } else { - rtp_session->jb = stfu_n_init(queue_frames, max_queue_frames ? max_queue_frames : 50, samples_per_packet, samples_per_second); + rtp_session->jb = stfu_n_init(queue_frames, max_queue_frames ? max_queue_frames : 50, samples_per_packet, samples_per_second, max_drift); } READ_DEC(rtp_session); @@ -2402,7 +2403,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t uint32_t ts; switch_assert(bytes); - + more: *bytes = sizeof(rtp_msg_t); status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock_input, 0, (void *) &rtp_session->recv_msg, bytes); ts = ntohl(rtp_session->recv_msg.header.ts); @@ -2489,9 +2490,13 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t stfu_n_reset(rtp_session->jb); } - stfu_n_eat(rtp_session->jb, rtp_session->last_read_ts, - rtp_session->recv_msg.header.pt, - rtp_session->recv_msg.body, *bytes - rtp_header_len, rtp_session->timer.samplecount); + if (stfu_n_eat(rtp_session->jb, rtp_session->last_read_ts, + rtp_session->recv_msg.header.pt, + rtp_session->recv_msg.body, *bytes - rtp_header_len, rtp_session->timer.samplecount) == STFU_ITS_TOO_LATE) { + printf("doh\n"); + goto more; + } + status = SWITCH_STATUS_FALSE; if (!return_jb_packet) { return status; From 4ef6280a05cea21e52a4be29fb31d33aa50a19a7 Mon Sep 17 00:00:00 2001 From: Michael S Collins Date: Fri, 25 Feb 2011 10:27:18 -0800 Subject: [PATCH 070/154] Add ring (Israel ring tone) to vars.xml --- conf/vars.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/vars.xml b/conf/vars.xml index 0e8782cff0..94529aa9bc 100644 --- a/conf/vars.xml +++ b/conf/vars.xml @@ -207,6 +207,7 @@ + + + + + + diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h index ab0777ed3b..0ef64d1079 100644 --- a/src/include/private/switch_core_pvt.h +++ b/src/include/private/switch_core_pvt.h @@ -249,6 +249,8 @@ struct switch_runtime { switch_dbtype_t odbc_dbtype; char hostname[256]; int multiple_registrations; + uint32_t max_db_handles; + uint32_t db_handle_timeout; }; extern struct switch_runtime runtime; diff --git a/src/switch_core.c b/src/switch_core.c index 50fe313f82..96e4b4ae27 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1283,7 +1283,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc memset(&runtime, 0, sizeof(runtime)); gethostname(runtime.hostname, sizeof(runtime.hostname)); - + runtime.max_db_handles = 50; + runtime.db_handle_timeout = 5000000;; + runtime.runlevel++; runtime.sql_buffer_len = 1024 * 32; runtime.max_sql_buffer_len = 1024 * 1024; @@ -1550,6 +1552,23 @@ static void switch_load_core_config(const char *file) if (tmp > -1 && tmp < 11) { switch_core_session_ctl(SCSC_DEBUG_LEVEL, &tmp); } + } else if (!strcasecmp(var, "max-db-handles")) { + long tmp = atol(val); + + if (tmp > 4 && tmp < 5001) { + runtime.max_db_handles = (uint32_t) tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "max-db-handles must be between 5 and 5000\n"); + } + } else if (!strcasecmp(var, "db-handle-timeout")) { + long tmp = atol(val); + + if (tmp > 0 && tmp < 5001) { + runtime.db_handle_timeout = (uint32_t) tmp * 1000000; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "db-handle-timeout must be between 1 and 5000\n"); + } + } else if (!strcasecmp(var, "multiple-registrations")) { runtime.multiple_registrations = switch_true(val); } else if (!strcasecmp(var, "sql-buffer-len")) { diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index 8dbdd44895..a4ed5ca83c 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -66,8 +66,8 @@ static struct { switch_cache_db_handle_t *handle_pool; switch_thread_cond_t *cond; switch_mutex_t *cond_mutex; - int total_handles; - int total_used_handles; + uint32_t total_handles; + uint32_t total_used_handles; } sql_manager; @@ -273,11 +273,29 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h char db_callsite_str[CACHE_DB_LEN] = ""; switch_cache_db_handle_t *new_dbh = NULL; switch_ssize_t hlen = -1; + int waiting = 0; + uint32_t yield_len = 100000, total_yield = 0; const char *db_name = NULL; const char *db_user = NULL; const char *db_pass = NULL; + while(runtime.max_db_handles && sql_manager.total_handles >= runtime.max_db_handles && sql_manager.total_used_handles >= sql_manager.total_handles) { + if (!waiting++) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_WARNING, "Max handles %u exceeded, blocking....\n", + runtime.max_db_handles); + } + + switch_yield(yield_len); + total_yield += yield_len; + + if (runtime.db_handle_timeout && total_yield > runtime.db_handle_timeout) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "Error connecting\n"); + *dbh = NULL; + return SWITCH_STATUS_FALSE; + } + } + switch (type) { case SCDB_TYPE_ODBC: { From fac74d55916f0fba7f2dd25698c2ab00eaa288f2 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 4 Mar 2011 22:20:40 +0000 Subject: [PATCH 137/154] don't ignore 393 files which are in the tree This partially reverts commit 17d521128014853b24f983307fc28317f11e950b. Some changes in that commit caused 393 files which are in the tree to be mistakenly ignored. --- .gitignore | 68 ++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index 2480b83823..31421b8d33 100644 --- a/.gitignore +++ b/.gitignore @@ -46,47 +46,45 @@ core.* !/w32/Console/FreeSwitchConsole.vcproj.user !/w32/Setup/inno_setup/vcredist_x64.exe !/w32/Setup/inno_setup/vcredist_x86.exe -.version -AUTHORS -COPYING -ChangeLog -Makefile -Makefile.in -NEWS -README -TAGS +/.version +/AUTHORS +/COPYING +/ChangeLog +/Makefile +/Makefile.in +/NEWS +/README +/TAGS aclocal.m4 autom4te.cache -build/Makefile -build/Makefile.in -build/config/compile -build/config/config.guess -build/config/depcomp -build/config/install-sh -build/config/ltmain.sh -build/config/missing -build/freeswitch.pc -build/getlib.sh -build/getsounds.sh -build/modmake.rules -build/getg729.sh +/build/Makefile +/build/Makefile.in +/build/config/compile +/build/config/config.guess +/build/config/depcomp +/build/config/install-sh +/build/config/ltmain.sh +/build/config/missing +/build/freeswitch.pc +/build/getlib.sh +/build/getsounds.sh +/build/modmake.rules config.cache config.log config.status -configure +/configure configure.lineno -freeswitch -fs_cli -fs_encode -fs_ivrd -libtool -noreg -modules.conf -quiet_libtool -tone2wav -scripts/fsxs -scripts/gentls_cert -a.out.dSYM +/freeswitch +/fs_cli +/fs_encode +/fs_ivrd +/libtool +/modules.conf +/quiet_libtool +/tone2wav +/scripts/fsxs +/scripts/gentls_cert +/a.out.dSYM src/mod/applications/mod_easyroute/Makefile src/mod/applications/mod_lcr/Makefile src/mod/applications/mod_nibblebill/Makefile From 541a99153d3a539d13e8d8c4c4e65a3716e807eb Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 4 Mar 2011 22:36:37 +0000 Subject: [PATCH 138/154] organize root .gitignore --- .gitignore | 164 ++++++++++++++++++++++++++++------------------------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 31421b8d33..1fc3016d3f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,6 @@ .deps .\#* \#* -/Debug/ -/Release/ -/All/ -/bin/ *.user *.suo *.ncb @@ -39,26 +35,46 @@ *.xz *.bz2 *.tbz2 +aclocal.m4 +autom4te.cache +config.cache +configure.lineno +config.log +config.status core.* -/Path + /w32/Library/lastversion /w32/Library/tmpVersion.Bat !/w32/Console/FreeSwitchConsole.vcproj.user !/w32/Setup/inno_setup/vcredist_x64.exe !/w32/Setup/inno_setup/vcredist_x86.exe + /.version +/a.out.dSYM /AUTHORS -/COPYING /ChangeLog +/configure +/COPYING +/freeswitch +/fs_cli +/fs_encode +/fs_ivrd +/libtool /Makefile /Makefile.in +/modules.conf /NEWS +/Path +/quiet_libtool /README /TAGS -aclocal.m4 -autom4te.cache -/build/Makefile -/build/Makefile.in +/tone2wav + +/All/ +/Debug/ +/bin/ +/Release/ + /build/config/compile /build/config/config.guess /build/config/depcomp @@ -68,74 +84,66 @@ autom4te.cache /build/freeswitch.pc /build/getlib.sh /build/getsounds.sh +/build/Makefile +/build/Makefile.in /build/modmake.rules -config.cache -config.log -config.status -/configure -configure.lineno -/freeswitch -/fs_cli -/fs_encode -/fs_ivrd -/libtool -/modules.conf -/quiet_libtool -/tone2wav + +/libs/curl/lib/ca-bundle.h +/libs/esl/fs_cli +/libs/esl/ivrd +/libs/esl/testclient +/libs/esl/testserver +/libs/freetdm/detect_dtmf +/libs/freetdm/detect_tones +/libs/freetdm/testanalog +/libs/freetdm/testapp +/libs/freetdm/testcid +/libs/freetdm/testpri +/libs/freetdm/testr2 +/libs/freetdm/testsangomaboost +/libs/freetdm/testtones +/libs/fsg729-*-installer +/libs/g729/ +/libs/libcodec2/config.guess +/libs/libcodec2/config.sub +/libs/libcodec2/configure +/libs/libcodec2/depcomp +/libs/libcodec2/install-sh +/libs/libcodec2/libtool +/libs/libcodec2/ltmain.sh +/libs/libcodec2/Makefile +/libs/libcodec2/Makefile.in +/libs/libcodec2/missing +/libs/libcodec2/src/Makefile +/libs/libcodec2/src/Makefile.in +/libs/libcodec2/unittest/Makefile +/libs/libcodec2/unittest/Makefile.in + /scripts/fsxs /scripts/gentls_cert -/a.out.dSYM -src/mod/applications/mod_easyroute/Makefile -src/mod/applications/mod_lcr/Makefile -src/mod/applications/mod_nibblebill/Makefile -src/mod/applications/mod_rss/Makefile -src/mod/applications/mod_snipe_hunt/Makefile -src/mod/codecs/mod_dahdi_codec/Makefile -src/mod/dialplans/mod_dialplan_directory/Makefile -src/mod/formats/mod_shell_stream/Makefile -src/mod/say/mod_say_de/Makefile -src/mod/say/mod_say_es/Makefile -src/mod/say/mod_say_fr/Makefile -src/mod/say/mod_say_it/Makefile -src/mod/say/mod_say_nl/Makefile -src/mod/say/mod_say_th/Makefile -src/mod/say/mod_say_zh/Makefile -libs/curl/lib/ca-bundle.h -libs/g729/ -libs/fsg729-*-installer -src/mod/codecs/mod_com_g729/ -src/mod/languages/mod_lua/mod_lua_wrap.cpp.orig -src/mod/languages/mod_perl/mod_perl_wrap.cpp.orig -src/mod/languages/mod_python/mod_python_wrap.cpp.orig -libs/freetdm/detect_dtmf -libs/freetdm/detect_tones -libs/freetdm/testanalog -libs/freetdm/testapp -libs/freetdm/testcid -libs/freetdm/testpri -libs/freetdm/testr2 -libs/freetdm/testsangomaboost -libs/freetdm/testtones -libs/esl/fs_cli -libs/esl/ivrd -libs/esl/testserver -libs/esl/testclient -libs/libcodec2/Makefile -libs/libcodec2/Makefile.in -libs/libcodec2/config.guess -libs/libcodec2/config.sub -libs/libcodec2/configure -libs/libcodec2/depcomp -libs/libcodec2/install-sh -libs/libcodec2/libtool -libs/libcodec2/ltmain.sh -libs/libcodec2/missing -libs/libcodec2/src/Makefile -libs/libcodec2/src/Makefile.in -libs/libcodec2/unittest/Makefile -libs/libcodec2/unittest/Makefile.in -src/mod/applications/mod_osp/Makefile -src/mod/applications/mod_osp/Makefile.in -src/mod/applications/mod_hash/Makefile -src/mod/applications/mod_hash/Makefile.in -src/mod/applications/mod_hash/mod_hash.log + +/src/mod/applications/mod_easyroute/Makefile +/src/mod/applications/mod_hash/Makefile +/src/mod/applications/mod_hash/Makefile.in +/src/mod/applications/mod_hash/mod_hash.log +/src/mod/applications/mod_lcr/Makefile +/src/mod/applications/mod_nibblebill/Makefile +/src/mod/applications/mod_osp/Makefile +/src/mod/applications/mod_osp/Makefile.in +/src/mod/applications/mod_rss/Makefile +/src/mod/applications/mod_snipe_hunt/Makefile +/src/mod/codecs/mod_com_g729/ +/src/mod/codecs/mod_dahdi_codec/Makefile +/src/mod/dialplans/mod_dialplan_directory/Makefile +/src/mod/formats/mod_shell_stream/Makefile +/src/mod/languages/mod_lua/mod_lua_wrap.cpp.orig +/src/mod/languages/mod_perl/mod_perl_wrap.cpp.orig +/src/mod/languages/mod_python/mod_python_wrap.cpp.orig +/src/mod/say/mod_say_de/Makefile +/src/mod/say/mod_say_es/Makefile +/src/mod/say/mod_say_fr/Makefile +/src/mod/say/mod_say_it/Makefile +/src/mod/say/mod_say_nl/Makefile +/src/mod/say/mod_say_th/Makefile +/src/mod/say/mod_say_zh/Makefile + From 0911ed740861f7e173c155c67388284b0053df57 Mon Sep 17 00:00:00 2001 From: Mathieu Rene Date: Sat, 5 Mar 2011 12:49:19 -0500 Subject: [PATCH 139/154] FS-3124 Use the channel's sound_prefix if it's not set in the conference's config --- conf/autoload_configs/conference.conf.xml | 11 ++++++----- src/mod/applications/mod_conference/mod_conference.c | 10 +++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/conf/autoload_configs/conference.conf.xml b/conf/autoload_configs/conference.conf.xml index cab0578a57..d4e5aa60ee 100644 --- a/conf/autoload_configs/conference.conf.xml +++ b/conf/autoload_configs/conference.conf.xml @@ -52,8 +52,9 @@ - - + + + @@ -106,7 +107,7 @@ - + @@ -131,7 +132,7 @@ - + @@ -156,7 +157,7 @@ - + diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 6892cbab9e..56c85ad2f3 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -6423,6 +6423,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c comfort_noise_level = 1400; } } else if (!strcasecmp(var, "sound-prefix") && !zstr(val)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "override sound-prefix with: %s\n", val); sound_prefix = val; } else if (!strcasecmp(var, "max-members") && !zstr(val)) { errno = 0; /* sanity first */ @@ -6527,8 +6528,15 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c set_cflags(conference_flags, &conference->flags); } - if (sound_prefix) { + if (!zstr(sound_prefix)) { conference->sound_prefix = switch_core_strdup(conference->pool, sound_prefix); + } else { + const char *val; + if ((val = switch_channel_get_variable(channel, "sound_prefix")) && !zstr(val)) { + /* if no sound_prefix was set, use the channel sound_prefix */ + conference->sound_prefix = switch_core_strdup(conference->pool, val); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "using channel sound prefix: %s\n", conference->sound_prefix); + } } if (!zstr(enter_sound)) { From 02b29263514248d927c397a4ad8b8f5e8da97997 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sun, 6 Mar 2011 19:34:03 +0100 Subject: [PATCH 140/154] [FreeTDM] Fix segfault in ftdm_analog_configure_span() on startup. Using ftdm_log_chan() in ftdm_analog_configure_span() is a bad idea, since the span won't have any channels assigned. This bug powered by declaring all variables at the top of the function, even if they are used in an if branch at the end. A C99'ish: if (callwaiting) { for (unsigned int i = 1; i <= span->span->chan_count; i++) { /* ... */ } } would have alerted the developer adding the log statement. But since we can't have nice things (thanks MSVC for not supporting C99!) Signed-off-by: Stefan Knoblich --- libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c index d25d38f3d7..21008e6c96 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c @@ -226,7 +226,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) if (wait_dialtone_timeout < 0) { wait_dialtone_timeout = 0; } - ftdm_log_chan(span->channels[i], FTDM_LOG_DEBUG, "Wait dial tone ms = %d\n", wait_dialtone_timeout); + ftdm_log(FTDM_LOG_DEBUG, "Wait dial tone ms = %d\n", wait_dialtone_timeout); } else if (!strcasecmp(var, "enable_callerid")) { if (!(val = va_arg(ap, char *))) { break; From 53e867bcf28736687129df3f295a058034bd8f03 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Sun, 6 Mar 2011 13:34:24 -0600 Subject: [PATCH 141/154] fix vs2008 x64 build --- libs/freetdm/msvc/testboost/testboost.2008.vcproj | 5 ++++- libs/freetdm/msvc/testboost/testsangomaboost.2008.vcproj | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/freetdm/msvc/testboost/testboost.2008.vcproj b/libs/freetdm/msvc/testboost/testboost.2008.vcproj index 5707033f33..c273b3c02b 100644 --- a/libs/freetdm/msvc/testboost/testboost.2008.vcproj +++ b/libs/freetdm/msvc/testboost/testboost.2008.vcproj @@ -146,8 +146,9 @@ /> @@ -146,8 +146,9 @@ /> Date: Sun, 6 Mar 2011 15:25:53 -0500 Subject: [PATCH 142/154] mod_sangoma_codec: Add release port function pointer --- .../mod_sangoma_codec/mod_sangoma_codec.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c index 97d9f6de9e..dd97453c7a 100644 --- a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c +++ b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c @@ -227,6 +227,23 @@ static int sangoma_create_rtp_port(void *usr_priv, uint32_t host_ip, uint32_t *p return 0; } +static int sangoma_release_rtp_port(void *usr_priv, uint32_t host_ip, uint32_t p_rtp_port, void *rtp_fd) +{ + struct in_addr local_ip_addr = { 0 }; + char local_ip[255]; + switch_port_t rtp_port = p_rtp_port; + + local_ip_addr.s_addr = htonl(host_ip); + + switch_inet_ntop(AF_INET, &local_ip_addr, local_ip, sizeof(local_ip)); + + /* release the port */ + switch_rtp_release_port(local_ip, rtp_port); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Released port %d for IP %s/%d.%d.%d.%d\n", rtp_port, local_ip, + SNGTC_NIPV4(host_ip)); + return 0; +} + static int sangoma_create_rtp(void *usr_priv, sngtc_codec_request_leg_t *codec_req_leg, sngtc_codec_reply_leg_t* codec_reply_leg, void **rtp_fd) { switch_status_t status; @@ -1180,6 +1197,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sangoma_codec_load) g_init_cfg.create_rtp = sangoma_create_rtp; g_init_cfg.create_rtp_port = sangoma_create_rtp_port; g_init_cfg.destroy_rtp = sangoma_destroy_rtp; + g_init_cfg.release_rtp_port = sangoma_release_rtp_port; if (sngtc_detect_init_modules(&g_init_cfg, &detected)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to detect vocallo modules\n"); From 8c3651fa66df604f4ad63ebf2d44f57df22b08ac Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 6 Mar 2011 14:49:31 -0600 Subject: [PATCH 143/154] FS-640 --comment-only can you see if this patch helps, I think it should really be fixed in sofia but this shold keep it at bay --- src/mod/endpoints/mod_sofia/sofia.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 90193ac63a..fac089d12c 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -864,7 +864,6 @@ void sofia_event_callback(nua_event_t event, case nua_r_unsubscribe: case nua_r_publish: case nua_i_cancel: - case nua_r_cancel: case nua_i_error: case nua_i_active: case nua_i_terminated: @@ -872,6 +871,13 @@ void sofia_event_callback(nua_event_t event, case nua_i_prack: case nua_r_prack: break; + case nua_r_cancel: + { + if (status > 299 && nh) { + nua_handle_destroy(nh); + } + } + break; case nua_i_ack: { if (channel && sip) { From 05e9d3477bf1df9234b63b2a2a11b43d90d857b3 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 6 Mar 2011 15:57:31 -0600 Subject: [PATCH 144/154] remove unneeded line --- src/mod/applications/mod_conference/mod_conference.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 56c85ad2f3..d0f5fac081 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -1404,7 +1404,6 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v if (switch_test_flag(conference, CFLAG_OUTCALL)) { conference->cancel_cause = SWITCH_CAUSE_ORIGINATOR_CANCEL; - switch_yield(2000000); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Ending pending outcall channels for Conference: '%s'\n", conference->name); while(conference->originating) { switch_yield(200000); From e7b3c3b1ad130272c2083c6cff0c61811debfcf3 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 6 Mar 2011 19:57:05 -0600 Subject: [PATCH 145/154] add -i --interrupt to fs_cli to allow control-c to exit the program --- libs/esl/fs_cli.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 19c5795bee..f5975342cc 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -577,6 +577,7 @@ static int usage(char *name){ printf(" -P, --port=port Port to connect (1 - 65535)\n"); printf(" -u, --user=user@domain user@domain\n"); printf(" -p, --password=password Password\n"); + printf(" -i, --interrupt Allow Control-c to interrupt"); printf(" -x, --execute=command Execute Command and Exit\n"); printf(" -l, --loglevel=command Log Level\n"); printf(" -q, --quiet Disable logging\n"); @@ -1029,6 +1030,7 @@ int main(int argc, char *argv[]) {"loglevel", 1, 0, 'l'}, {"quiet", 0, 0, 'q'}, {"retry", 0, 0, 'r'}, + {"interrupt", 0, 0, 'i'}, {"reconnect", 0, 0, 'R'}, {0, 0, 0, 0} }; @@ -1044,6 +1046,7 @@ int main(int argc, char *argv[]) int temp_log = -1; int argv_error = 0; int argv_exec = 0; + int ctl_c = 0; char argv_command[1024] = ""; char argv_loglevel[128] = ""; int argv_quiet = 0; @@ -1072,7 +1075,7 @@ int main(int argc, char *argv[]) for(;;) { int option_index = 0; - opt = getopt_long(argc, argv, "H:U:P:S:u:p:d:x:l:qrRh?", options, &option_index); + opt = getopt_long(argc, argv, "H:U:P:S:u:p:d:x:l:qrRhi?", options, &option_index); if (opt == -1) break; switch (opt) { @@ -1116,6 +1119,9 @@ int main(int argc, char *argv[]) case 'q': argv_quiet = 1; break; + case 'i': + ctl_c = 1; + break; case 'r': loops += 120; break; @@ -1260,7 +1266,9 @@ int main(int argc, char *argv[]) esl_log(ESL_LOG_INFO, "Retrying\n"); } } else { - CONNECTED = 1; + if (!ctl_c) { + CONNECTED = 1; + } if (temp_log < 0 ) { esl_global_set_default_logger(profile->debug); From ede19cad7279bbcab1268d98c58015b3fed2738c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 6 Mar 2011 20:57:26 -0600 Subject: [PATCH 146/154] no this is better the other way --- src/switch_core_db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_core_db.c b/src/switch_core_db.c index c64629c32b..475bcfd649 100644 --- a/src/switch_core_db.c +++ b/src/switch_core_db.c @@ -94,7 +94,7 @@ SWITCH_DECLARE(int) switch_core_db_exec(switch_core_db_t *db, const char *sql, s if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED) { if (sane > 1) { switch_core_db_free(err); - switch_yield(1000); /* Was 100000. I think it's too much */ + switch_yield(100000); continue; } } else { From dcf515ffafd6765f852d7dc656e64e7c56b7d68b Mon Sep 17 00:00:00 2001 From: Konrad Hammel Date: Mon, 7 Mar 2011 11:36:18 -0500 Subject: [PATCH 147/154] freetdm: ss7 - bug fix for race condition on startup causing seg fault --- .../ftmod_sangoma_ss7_handle.c | 20 +++++++++++++++++++ .../ftmod_sangoma_ss7_main.c | 3 +++ 2 files changed, 23 insertions(+) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c index 331312fafb..cb742ac4c9 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c @@ -884,6 +884,12 @@ ftdm_status_t handle_sta_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info ; ftdm_channel_t *ftdmchan; + /* confirm that the circuit is active on our side otherwise move to the next circuit */ + if (!sngss7_test_flag(&g_ftdm_sngss7_data.cfg.isupCkt[circuit], SNGSS7_ACTIVE)) { + SS7_ERROR("[CIC:%d]Circuit is not active yet, skipping!\n",g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic); + return FTDM_FAIL; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -1189,6 +1195,13 @@ ftdm_status_t handle_pause(uint32_t suInstId, uint32_t spInstId, uint32_t circui /* check that the infId matches and that this is not a siglink */ if ((g_ftdm_sngss7_data.cfg.isupCkt[i].infId == infId) && (g_ftdm_sngss7_data.cfg.isupCkt[i].type == VOICE)) { + + /* confirm that the circuit is active on our side otherwise move to the next circuit */ + if (!sngss7_test_flag(&g_ftdm_sngss7_data.cfg.isupCkt[i], SNGSS7_ACTIVE)) { + SS7_ERROR("[CIC:%d]Circuit is not active yet, skipping!\n",g_ftdm_sngss7_data.cfg.isupCkt[i].cic); + i++; + continue; + } /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { @@ -1248,6 +1261,13 @@ ftdm_status_t handle_resume(uint32_t suInstId, uint32_t spInstId, uint32_t circu if ((g_ftdm_sngss7_data.cfg.isupCkt[i].infId == infId) && (g_ftdm_sngss7_data.cfg.isupCkt[i].type == VOICE)) { + /* confirm that the circuit is active on our side otherwise move to the next circuit */ + if (!sngss7_test_flag(&g_ftdm_sngss7_data.cfg.isupCkt[i], SNGSS7_ACTIVE)) { + SS7_ERROR("[CIC:%d]Circuit is not active yet, skipping!\n",g_ftdm_sngss7_data.cfg.isupCkt[i].cic); + i++; + continue; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index 2f630fe462..10e73d3fbd 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -1397,6 +1397,9 @@ static ftdm_status_t ftdm_sangoma_ss7_start(ftdm_span_t * span) /* lock the channel */ ftdm_mutex_lock(ftdmchan->mutex); + /* flag the circuit as active */ + sngss7_set_flag(sngss7_info->circuit, SNGSS7_ACTIVE); + /* check if the interface is paused or resumed */ if (sngss7_test_flag(sngss7_intf, SNGSS7_PAUSED)) { SS7_DEBUG_CHAN(ftdmchan, "ISUP intf %d is PAUSED\n", sngss7_intf->id); From c0a2a225ba3f4dce536c1ce558c5337547e6d60e Mon Sep 17 00:00:00 2001 From: Konrad Hammel Date: Tue, 8 Mar 2011 17:51:10 -0500 Subject: [PATCH 148/154] freetdm: ss7 - bug fix in GRS handling --- .../ftmod_sangoma_ss7/ftmod_sangoma_ss7_cfg.c | 15 +- .../ftmod_sangoma_ss7_handle.c | 439 +++++++++++++----- .../ftmod_sangoma_ss7_support.c | 41 +- 3 files changed, 364 insertions(+), 131 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cfg.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cfg.c index df0414a7f5..b7eaa37639 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cfg.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cfg.c @@ -396,16 +396,13 @@ int ft_to_sngss7_cfg_all(void) x = (g_ftdm_sngss7_data.cfg.procId * 1000) + 1; while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) { - if ( g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) { - if (ftmod_ss7_isup_ckt_config(x)) { - SS7_CRITICAL("ISUP CKT %d configuration FAILED!\n", x); - return 1; - } else { - SS7_INFO("ISUP CKT %d configuration DONE!\n", x); - } - - } /* if ( g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) */ + if (ftmod_ss7_isup_ckt_config(x)) { + SS7_CRITICAL("ISUP CKT %d configuration FAILED!\n", x); + return 1; + } else { + SS7_INFO("ISUP CKT %d configuration DONE!\n", x); + } /* set the SNGSS7_CONFIGURED flag */ g_ftdm_sngss7_data.cfg.isupCkt[x].flags |= SNGSS7_CONFIGURED; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c index cb742ac4c9..e0b6ae08c0 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c @@ -881,238 +881,187 @@ ftdm_status_t handle_resm_ind(uint32_t suInstId, uint32_t spInstId, uint32_t cir /******************************************************************************/ ftdm_status_t handle_sta_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, uint8_t globalFlg, uint8_t evntType, SiStaEvnt *siStaEvnt) { - sngss7_chan_data_t *sngss7_info ; - ftdm_channel_t *ftdmchan; + SS7_FUNC_TRACE_ENTER(__FUNCTION__); /* confirm that the circuit is active on our side otherwise move to the next circuit */ if (!sngss7_test_flag(&g_ftdm_sngss7_data.cfg.isupCkt[circuit], SNGSS7_ACTIVE)) { - SS7_ERROR("[CIC:%d]Circuit is not active yet, skipping!\n",g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic); + SS7_ERROR("[CIC:%d]Rx %s but circuit is not active yet, skipping!\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); return FTDM_FAIL; } - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); - SS7_FUNC_TRACE_EXIT(__FUNCTION__); - return FTDM_FAIL; - } - - SS7_FUNC_TRACE_ENTER(__FUNCTION__); - switch (evntType) { /**************************************************************************/ case SIT_STA_REATTEMPT: /* reattempt indication */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Reattempt indication\n", sngss7_info->circuit->cic); handle_reattempt(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_ERRORIND: /* error indication */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Error indication\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CONTCHK: /* continuity check */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CCR start\n", sngss7_info->circuit->cic); handle_cot_start(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CONTREP: /* continuity report */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx COT report\n", sngss7_info->circuit->cic); handle_cot(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_STPCONTIN: /* stop continuity */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CCR stop\n", sngss7_info->circuit->cic); handle_cot_stop(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CGQRYRSP: /* circuit grp query response from far end forwarded to upper layer by ISUP */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CQM\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CONFUSION: /* confusion */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CFN\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_LOOPBACKACK: /* loop-back acknowledge */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx LPA\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CIRRSRVREQ: /* circuit reservation request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Ckt Resveration req\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CIRRSRVACK: /* circuit reservation acknowledgement */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Ckt Res ack\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CIRBLOREQ: /* circuit blocking request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx BLO\n", sngss7_info->circuit->cic); handle_blo_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRBLORSP: /* circuit blocking response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx BLA\n", sngss7_info->circuit->cic); handle_blo_rsp(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRUBLREQ: /* circuit unblocking request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx UBL\n", sngss7_info->circuit->cic); handle_ubl_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRUBLRSP: /* circuit unblocking response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx UBA\n", sngss7_info->circuit->cic); handle_ubl_rsp(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRRESREQ: /* circuit reset request - RSC */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx RSC\n", sngss7_info->circuit->cic); handle_rsc_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRLOCRES: /* reset initiated locally by the software */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Local RSC\n", sngss7_info->circuit->cic); handle_local_rsc_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRRESRSP: /* circuit reset response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx RSC-RLC\n", sngss7_info->circuit->cic); handle_rsc_rsp(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CGBREQ: /* CGB request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CGB\n", sngss7_info->circuit->cic); handle_cgb_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CGUREQ: /* CGU request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CGU\n", sngss7_info->circuit->cic); handle_cgu_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CGQRYREQ: /* circuit group query request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CQM\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CGBRSP: /* mntc. oriented CGB response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx mntc CGB\n", sngss7_info->circuit->cic); /*handle_cgb_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt);*/ break; /**************************************************************************/ case SIT_STA_CGURSP: /* mntc. oriented CGU response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx mntc CGU\n", sngss7_info->circuit->cic); /*SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType));*/ break; /**************************************************************************/ case SIT_STA_GRSREQ: /* circuit group reset request */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx GRS\n", sngss7_info->circuit->cic); handle_grs_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRUNEQPD: /* circuit unequipped indication */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx UCIC\n", sngss7_info->circuit->cic); handle_ucic(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_GRSRSP: /* circuit group reset response */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx GRA\n", sngss7_info->circuit->cic); handle_grs_rsp(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_PAUSEIND: /* pause indication */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx SUS\n", sngss7_info->circuit->cic); handle_pause(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_RESUMEIND: /* resume indication */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx RES\n", sngss7_info->circuit->cic); handle_resume(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_USRPARTA: /* user part available */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx UPA\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_RMTUSRUNAV: /* remote user not available */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Remote User not Available\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_MTPCONG0: /* congestion indication level 0 */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Congestion L0\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_MTPCONG1: /* congestion indication level 1 */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Congestion L1\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_MTPCONG2: /* congestion indication level 2 */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Congestion L2\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_MTPCONG3: /* congestion indication level 3 */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Congestion L3\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_MTPSTPCONG: /* stop congestion indication level 0 */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Stop Congestion\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CIRLOCALBLOIND: /* Mngmt local blocking */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Local BLO\n", sngss7_info->circuit->cic); handle_local_blk(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_CIRLOCALUBLIND: /* Mngmt local unblocking */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Local UBL\n", sngss7_info->circuit->cic); handle_local_ubl(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_OVERLOAD: /* Overload */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Overload\n", sngss7_info->circuit->cic); handle_olm_msg(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt); break; /**************************************************************************/ case SIT_STA_LMCGBREQ: /* when LM requests ckt grp blocking */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx LM CGB\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_LMCGUREQ: /* when LM requests ckt grp unblocking */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx LM CGU\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_LMGRSREQ: /* when LM requests ckt grp reset */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx LM RSC\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CGBINFOIND: /* circuit grp blking ind , no resp req */ - /*SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx CGB no resp req\n", sngss7_info->circuit->cic);*/ /* handle_cgb_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt);*/ break; /**************************************************************************/ case SIT_STA_LMCQMINFOREQ: /* when LM requests ckt grp query */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx LM CQM\n", sngss7_info->circuit->cic); // SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ case SIT_STA_CIRLOCGRS: /* group reset initiated locally by the software */ - SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx Local GRS\n", sngss7_info->circuit->cic); SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType)); break; /**************************************************************************/ @@ -1134,11 +1083,25 @@ ftdm_status_t handle_reattempt(uint32_t suInstId, uint32_t spInstId, uint32_t ci sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1311,11 +1274,25 @@ ftdm_status_t handle_cot_start(uint32_t suInstId, uint32_t spInstId, uint32_t ci sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1352,11 +1329,25 @@ ftdm_status_t handle_cot_stop(uint32_t suInstId, uint32_t spInstId, uint32_t cir sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1383,11 +1374,25 @@ ftdm_status_t handle_cot(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1437,11 +1442,25 @@ ftdm_status_t handle_blo_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1473,11 +1492,25 @@ ftdm_status_t handle_blo_rsp(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1500,11 +1533,25 @@ ftdm_status_t handle_ubl_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1539,11 +1586,25 @@ ftdm_status_t handle_ubl_rsp(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1566,11 +1627,25 @@ ftdm_status_t handle_rsc_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1611,13 +1686,26 @@ ftdm_status_t handle_local_rsc_req(uint32_t suInstId, uint32_t spInstId, uint32_ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; - } + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + } /* lock the channel */ ftdm_mutex_lock(ftdmchan->mutex); @@ -1656,11 +1744,25 @@ ftdm_status_t handle_rsc_rsp(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1733,10 +1835,25 @@ ftdm_status_t handle_grs_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ int range; - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* extract the range value from the event structure */ @@ -1769,10 +1886,25 @@ ftdm_status_t handle_grs_rsp(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_span_data_t *sngss7_span = NULL; int range; - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* extract the range value from the event structure */ @@ -1811,11 +1943,25 @@ ftdm_status_t handle_local_blk(uint32_t suInstId, uint32_t spInstId, uint32_t ci sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1847,11 +1993,25 @@ ftdm_status_t handle_local_ubl(uint32_t suInstId, uint32_t spInstId, uint32_t ci sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* lock the channel */ @@ -1885,11 +2045,25 @@ ftdm_status_t handle_ucic(uint32_t suInstId, uint32_t spInstId, uint32_t circuit ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* check if we just sent a GRS request...*/ @@ -1934,13 +2108,26 @@ ftdm_status_t handle_cgb_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ memset(&status[0], '\0', sizeof(status)); - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; - } + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + } /* grab the span info */ sngss7_span = ftdmchan->span->signal_data; @@ -2069,11 +2256,25 @@ ftdm_status_t handle_cgu_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ memset(&sigev, 0, sizeof (sigev)); memset(&status[0], '\0', sizeof(status)); - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* grab the span info */ @@ -2187,11 +2388,25 @@ ftdm_status_t handle_olm_msg(uint32_t suInstId, uint32_t spInstId, uint32_t circ sngss7_chan_data_t *sngss7_info = NULL; ftdm_channel_t *ftdmchan = NULL; - /* get the ftdmchan and ss7_chan_data from the circuit */ - if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { - SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); + /* confirm that the circuit is voice channel */ + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("[CIC:%d]Rx %s on non-voice CIC\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); return FTDM_FAIL; + } else { + /* get the ftdmchan and ss7_chan_data from the circuit */ + if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { + SS7_ERROR("Failed to extract channel data for ISUP circuit = %d!\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return FTDM_FAIL; + } + + SS7_INFO_CHAN(ftdmchan, "[CIC:%d]Rx %s\n", + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic, + DECODE_LCC_EVENT(evntType)); } /* handle overload */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c index 9296dbc08a..814f418a13 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c @@ -531,8 +531,18 @@ ftdm_status_t check_if_rx_grs_started(ftdm_span_t *ftdmspan) sngss7_span_data_t *sngss7_span = (sngss7_span_data_t *)ftdmspan->signal_data; int i; + + SS7_INFO("Rx GRS (%d:%d)\n", + g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic, + (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic + sngss7_span->rx_grs.range)); + for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) { + /* confirm this is a voice channel, otherwise we do nothing */ + if (g_ftdm_sngss7_data.cfg.isupCkt[i].type != VOICE) { + continue; + } + /* extract the channel in question */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i); @@ -553,10 +563,6 @@ ftdm_status_t check_if_rx_grs_started(ftdm_span_t *ftdmspan) ftdm_sangoma_ss7_process_state_change (ftdmchan); } - SS7_INFO_CHAN(ftdmchan, "Rx GRS (%d:%d)\n", - g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic, - (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic + sngss7_span->rx_grs.range)); - /* flag the channel as having received a reset */ sngss7_set_ckt_flag(sngss7_info, FLAG_GRP_RESET_RX); @@ -595,12 +601,14 @@ ftdm_status_t check_if_rx_grs_processed(ftdm_span_t *ftdmspan) int byte = 0; int bit = 0; - - ftdm_log(FTDM_LOG_DEBUG, "Found Rx GRS on span %s...checking circuits\n", ftdmspan->name); - /* check all the circuits in the range to see if they are done resetting */ for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) { + /* confirm this is a voice channel, otherwise we do nothing */ + if (g_ftdm_sngss7_data.cfg.isupCkt[i].type != VOICE) { + continue; + } + /* extract the channel in question */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i); @@ -630,6 +638,11 @@ ftdm_status_t check_if_rx_grs_processed(ftdm_span_t *ftdmspan) /* check all the circuits in the range to see if they are done resetting */ for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) { + /* confirm this is a voice channel, otherwise we do nothing */ + if (g_ftdm_sngss7_data.cfg.isupCkt[i].type != VOICE) { + continue; + } + /* extract the channel in question */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n",i); @@ -664,6 +677,12 @@ ftdm_status_t check_if_rx_grs_processed(ftdm_span_t *ftdmspan) GRS_UNLOCK_ALL: for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) { + + /* confirm this is a voice channel, otherwise we do nothing */ + if (g_ftdm_sngss7_data.cfg.isupCkt[i].type != VOICE) { + continue; + } + /* extract the channel in question */ if (extract_chan_data(i, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i); @@ -685,6 +704,10 @@ ftdm_status_t check_if_rx_gra_started(ftdm_span_t *ftdmspan) sngss7_span_data_t *sngss7_span = (sngss7_span_data_t *)ftdmspan->signal_data; int i; + SS7_INFO("Rx GRA (%d:%d)\n", + g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic, + (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic + sngss7_span->rx_gra.range)); + for (i = sngss7_span->rx_gra.circuit; i < (sngss7_span->rx_gra.circuit + sngss7_span->rx_gra.range + 1); i++) { /* extract the channel in question */ @@ -707,9 +730,7 @@ ftdm_status_t check_if_rx_gra_started(ftdm_span_t *ftdmspan) ftdm_sangoma_ss7_process_state_change (ftdmchan); } - SS7_INFO_CHAN(ftdmchan, "Rx GRA (%d:%d)\n", - g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic, - (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic + sngss7_span->rx_gra.range)); + switch (ftdmchan->state) { /**********************************************************************/ From 93bfa6d1e99e8037bd6f2d01fce280b306eafabf Mon Sep 17 00:00:00 2001 From: Konrad Hammel Date: Tue, 15 Feb 2011 11:45:17 -0500 Subject: [PATCH 149/154] chlog freetdm: ss7 - adding initial support for RELAY --- .../ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c | 22 +++--- .../ftmod_sangoma_ss7_cntrl.c | 67 ++++++++++++++++ .../ftmod_sangoma_ss7_logger.c | 13 +++- .../ftmod_sangoma_ss7_main.c | 63 +++++++++------ .../ftmod_sangoma_ss7_main.h | 27 +++++++ .../ftmod_sangoma_ss7_relay.c | 76 ++++++++++--------- .../ftmod_sangoma_ss7_support.c | 44 +++++++++++ .../ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c | 2 +- 8 files changed, 246 insertions(+), 68 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c index 2f56619863..f8b12ea588 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c @@ -967,12 +967,14 @@ static ftdm_status_t handle_show_inreset(ftdm_stream_handle_t *stream, int span, /******************************************************************************/ static ftdm_status_t handle_show_flags(ftdm_stream_handle_t *stream, int span, int chan, int verbose) { - int x; - int bit; - sngss7_chan_data_t *ss7_info; - ftdm_channel_t *ftdmchan; - int lspan; - int lchan; + sngss7_chan_data_t *ss7_info; + ftdm_channel_t *ftdmchan; + int x; + int bit; + int lspan; + int lchan; + const char *text; + int flag; x = (g_ftdm_sngss7_data.cfg.procId * 1000) + 1; while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) { @@ -1001,11 +1003,11 @@ static ftdm_status_t handle_show_flags(ftdm_stream_handle_t *stream, int span, i ss7_info->circuit->cic); for (bit = 0; bit < 33; bit++) { - stream->write_function(stream, "|"); if (ss7_info->ckt_flags & ( 0x1 << bit)) { - stream->write_function(stream, "%2d=1", bit); - } else { - stream->write_function(stream, "%2d=0", bit); + stream->write_function(stream, "|"); + flag = bit; + text = ftmod_ss7_ckt_flag2str(flag); + stream->write_function(stream, "%s",text); } } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c index c1b18e529a..83ef5e0a42 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c @@ -68,11 +68,14 @@ int ftmod_ss7_shutdown_isup(void); int ftmod_ss7_shutdown_mtp3(void); int ftmod_ss7_shutdown_mtp2(void); int ftmod_ss7_shutdown_relay(void); +int ftmod_ss7_disable_relay_channel(uint32_t chanId); int ftmod_ss7_disable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_enable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_disable_grp_mtp2Link(uint32_t procId); + +int ftmod_ss7_block_isup_ckt(uint32_t cktId); /******************************************************************************/ /* FUNCTIONS ******************************************************************/ @@ -712,6 +715,38 @@ int ftmod_ss7_shutdown_relay(void) return (sng_cntrl_relay(&pst, &cntrl)); } +/******************************************************************************/ +int ftmod_ss7_disable_relay_channel(uint32_t chanId) +{ + RyMngmt cntrl; + Pst pst; + + /* initalize the post structure */ + smPstInit(&pst); + + /* insert the destination Entity */ + pst.dstEnt = ENTRY; + + /* initalize the control structure */ + memset(&cntrl, 0x0, sizeof(RyMngmt)); + + /* initalize the control header */ + smHdrInit(&cntrl.hdr); + + cntrl.hdr.msgType = TCNTRL; /* this is a control request */ + cntrl.hdr.entId.ent = ENTRY; + cntrl.hdr.entId.inst = S_INST; + cntrl.hdr.elmId.elmnt = STGEN; + + + cntrl.hdr.elmId.elmntInst1 = chanId; + + cntrl.t.cntrl.action = ADISIMM; /* Deactivate */ + cntrl.t.cntrl.subAction = SAELMNT; /* specificed element */ + + return (sng_cntrl_relay(&pst, &cntrl)); +} + /******************************************************************************/ int ftmod_ss7_disable_grp_mtp3Link(uint32_t procId) { @@ -808,6 +843,38 @@ int ftmod_ss7_disable_grp_mtp2Link(uint32_t procId) } +/******************************************************************************/ +int ftmod_ss7_block_isup_ckt(uint32_t cktId) +{ + SiMngmt cntrl; + Pst pst; + + /* initalize the post structure */ + smPstInit(&pst); + + /* insert the destination Entity */ + pst.dstEnt = ENTSI; + + /* initalize the control structure */ + memset(&cntrl, 0x0, sizeof(SiMngmt)); + + /* initalize the control header */ + smHdrInit(&cntrl.hdr); + + cntrl.hdr.msgType = TCNTRL; /* this is a control request */ + cntrl.hdr.entId.ent = ENTSI; + cntrl.hdr.entId.inst = S_INST; + cntrl.hdr.elmId.elmnt = STICIR; + + cntrl.t.cntrl.s.siElmnt.elmntId.circuit = cktId; + cntrl.t.cntrl.s.siElmnt.elmntParam.cir.flag = LSI_CNTRL_CIR_FORCE; + + cntrl.t.cntrl.action = ADISIMM; /* block via BLO */ + cntrl.t.cntrl.subAction = SAELMNT; /* specificed element */ + + return (sng_cntrl_isup(&pst, &cntrl)); +} + /******************************************************************************/ /* For Emacs: * Local Variables: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_logger.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_logger.c index f4143fc864..2a395c9ad1 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_logger.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_logger.c @@ -766,7 +766,18 @@ void handle_sng_relay_alarm(Pst *pst, RyMngmt *sta) DECODE_LRY_REASON(sta->t.usta.s.ryErrUsta.reason)); /* process the event */ - handle_relay_disconnect_on_error(sta); + switch (sta->t.usta.s.ryErrUsta.reason) { + /**********************************************************************/ + case (LRYRSNMGMTREQ): + /* do nothing since this is a shutdown */ + break; + /**********************************************************************/ + default: + /* handle the error */ + handle_relay_disconnect_on_error(sta); + break; + /**********************************************************************/ + } /* switch (sta->t.usta.s.ryErrUsta.reason) */ break; /**************************************************************************/ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index 10e73d3fbd..7b885102b9 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -365,6 +365,9 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj) /* check each channel on the span to see if there is an un-procressed SUS/RES flag */ check_for_res_sus_flag(ftdmspan); + /* check each channel on the span to see if it needs to be reconfigured */ + check_for_reconfig_flag(ftdmspan); + /* Poll for events, e.g HW DTMF */ switch (ftdm_span_poll_event(ftdmspan, 0, NULL)) { /**********************************************************************/ @@ -926,33 +929,27 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) } } - /* if we're not coming from HANGUP_COMPLETE we need to check for resets - * we can also check if we are in a PAUSED state (no point in sending message - */ - if ((ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP_COMPLETE) && - (!sngss7_test_ckt_flag(sngss7_info, FLAG_INFID_PAUSED))) { - /* check if this is an outgoing RSC */ - if ((sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_TX)) && - !(sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_SENT))) { + /* check if this is an outgoing RSC */ + if ((sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_TX)) && + !(sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_SENT))) { - /* send a reset request */ - ft_to_sngss7_rsc (ftdmchan); - sngss7_set_ckt_flag(sngss7_info, FLAG_RESET_SENT); + /* send a reset request */ + ft_to_sngss7_rsc (ftdmchan); + sngss7_set_ckt_flag(sngss7_info, FLAG_RESET_SENT); - } /* if (sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_TX)) */ - - /* check if this is the first channel of a GRS (this flag is thrown when requesting reset) */ - if ( (sngss7_test_ckt_flag (sngss7_info, FLAG_GRP_RESET_TX)) && - !(sngss7_test_ckt_flag(sngss7_info, FLAG_GRP_RESET_SENT)) && - (sngss7_test_ckt_flag(sngss7_info, FLAG_GRP_RESET_BASE))) { + } /* if (sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_TX)) */ - /* send out the grs */ - ft_to_sngss7_grs (ftdmchan); - sngss7_set_ckt_flag(sngss7_info, FLAG_GRP_RESET_SENT); + /* check if this is the first channel of a GRS (this flag is thrown when requesting reset) */ + if ( (sngss7_test_ckt_flag (sngss7_info, FLAG_GRP_RESET_TX)) && + !(sngss7_test_ckt_flag(sngss7_info, FLAG_GRP_RESET_SENT)) && + (sngss7_test_ckt_flag(sngss7_info, FLAG_GRP_RESET_BASE))) { - }/* if ( sngss7_test_ckt_flag ( sngss7_info, FLAG_GRP_RESET_TX ) ) */ - } /* if ( last_state != HANGUP && !PAUSED */ + /* send out the grs */ + ft_to_sngss7_grs (ftdmchan); + sngss7_set_ckt_flag(sngss7_info, FLAG_GRP_RESET_SENT); + + }/* if ( sngss7_test_ckt_flag ( sngss7_info, FLAG_GRP_RESET_TX ) ) */ /* if the sig_status is up...bring it down */ if (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_SIG_UP)) { @@ -1598,6 +1595,8 @@ static FIO_SIG_UNLOAD_FUNCTION(ftdm_sangoma_ss7_unload) { /*this function is called by the FT-core to unload the signaling module */ + int x; + ftdm_log (FTDM_LOG_INFO, "Starting ftmod_sangoma_ss7 unload...\n"); if (sngss7_test_flag(&g_ftdm_sngss7_data.cfg, SNGSS7_CC)) { @@ -1625,6 +1624,26 @@ static FIO_SIG_UNLOAD_FUNCTION(ftdm_sangoma_ss7_unload) } if (sngss7_test_flag(&g_ftdm_sngss7_data.cfg, SNGSS7_RY)) { + /* go through all the relays channels and configure it */ + x = 1; + while (g_ftdm_sngss7_data.cfg.relay[x].id != 0) { + /* check if this relay channel has been configured already */ + if ((g_ftdm_sngss7_data.cfg.relay[x].flags & SNGSS7_CONFIGURED)) { + + /* send the specific configuration */ + if (ftmod_ss7_disable_relay_channel(x)) { + SS7_CRITICAL("Relay Channel %d disable failed!\n", x); + return 1; + } else { + SS7_INFO("Relay Channel %d disable DONE!\n", x); + } + + /* set the SNGSS7_CONFIGURED flag */ + g_ftdm_sngss7_data.cfg.relay[x].flags &= !SNGSS7_CONFIGURED; + } /* if !SNGSS7_CONFIGURED */ + x++; + } /* while (g_ftdm_sngss7_data.cfg.relay[x].id != 0) */ + ftmod_ss7_shutdown_relay(); sng_isup_free_relay(); sngss7_clear_flag(&g_ftdm_sngss7_data.cfg, SNGSS7_RY); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h index 4d09dfa7e3..291eea0985 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h @@ -506,6 +506,27 @@ typedef enum { FLAG_RELAY_DOWN = (1 << 30) } sng_ckt_flag_t; +#define CKT_FLAGS_STRING \ + "RX_RSC", \ + "TX_RSC", \ + "TX_RSC_REQ_SENT", \ + "TX_RSC_RSP_RECIEVED", \ + "RX_GRS", \ + "RX_GRS_DONE", \ + "RX_GRS_CMPLT", \ + "GRS_BASE", \ + "TX_GRS", \ + "TX_GRS_REQ_SENT", \ + "TX_GRS_RSP_RECIEVED", \ + "REMOTE_REL", \ + "LOCAL_REL", \ + "GLARE", \ + "INF_RESUME", \ + "INF_PAUSED", \ + "RELAY_DOWN", \ + "CKT_RECONFIG" +FTDM_STR2ENUM_P(ftmod_ss7_ckt_state2flag, ftmod_ss7_ckt_flag2str, sng_ckt_flag_t) + /* ckt blocking flags */ typedef enum { FLAG_CKT_UCIC_BLOCK = (1 << 0), @@ -626,12 +647,17 @@ int ftmod_ss7_shutdown_isup(void); int ftmod_ss7_shutdown_mtp3(void); int ftmod_ss7_shutdown_mtp2(void); int ftmod_ss7_shutdown_relay(void); +int ftmod_ss7_disable_relay_channel(uint32_t chanId); int ftmod_ss7_disable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_enable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_disable_grp_mtp2Link(uint32_t procId); +int ftmod_ss7_block_isup_ckt(uint32_t cktId); + + + /* in ftmod_sangoma_ss7_sta.c */ int ftmod_ss7_mtp1link_sta(uint32_t id, L1Mngmt *cfm); int ftmod_ss7_mtp2link_sta(uint32_t id, SdMngmt *cfm); @@ -756,6 +782,7 @@ int find_ssf_type_in_map(const char *ssfType); int find_cic_cntrl_in_map(const char *cntrlType); ftdm_status_t check_status_of_all_isup_intf(void); +ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan); void sngss7_send_signal(sngss7_chan_data_t *sngss7_info, ftdm_signal_event_t event_id); void sngss7_set_sig_status(sngss7_chan_data_t *sngss7_info, ftdm_signaling_status_t status); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c index d003371ff2..d34d62c217 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c @@ -61,23 +61,28 @@ ftdm_status_t handle_relay_connect(RyMngmt *sta) /* test if this is the first time the channel comes up */ if (!sngss7_test_flag(sng_relay, SNGSS7_RELAY_INIT)) { - SS7_DEBUG("Relay Channel %d initial connection UP\n", sng_relay->id); + SS7_INFO("Relay Channel %d initial connection UP\n", sng_relay->id); /* mark the channel as being up */ sngss7_set_flag(sng_relay, SNGSS7_RELAY_INIT); } else { - SS7_DEBUG("Relay Channel %d connection UP\n", sng_relay->id); + SS7_INFO("Relay Channel %d connection UP\n", sng_relay->id); /* react based on type of channel */ switch (sng_relay->type) { /******************************************************************/ case (LRY_CT_TCP_CLIENT): + /* check the status of all isup intfs in case we weren't connected when + * the interface became active + */ + check_status_of_all_isup_intf(); + /* reconfigure all ISUP ckts, since the main system would have lost all configs */ if (reconfig_all_ckts_for_relay()) { SS7_ERROR("Failed to reconfigure ISUP Ckts!\n"); - /* we're done....this is very bad! */ - } else { + } else { + /* if the circuits reconfiged then bring then back up */ enable_all_ckts_for_relay(); } @@ -104,16 +109,20 @@ ftdm_status_t handle_relay_disconnect_on_error(RyMngmt *sta) /* check which procId is in error, if it is 1, disable the ckts */ if (sta->t.usta.s.ryErrUsta.errPid == 1 ) { - disable_all_ckts_for_relay(); - + /* we've lost the server, bring down the mtp2 links */ disble_all_mtp2_sigs_for_relay(); + + /* we've lost the server, bring the sig status down on all ckts */ + disable_all_ckts_for_relay(); } /* check if the channel is a server, means we just lost a MGW */ if (g_ftdm_sngss7_data.cfg.relay[sta->t.usta.s.ryErrUsta.errPid].type == LRY_CT_TCP_SERVER) { - block_all_ckts_for_relay(sta->t.usta.s.ryErrUsta.errPid); - + /* we've lost the client, bring down all mtp3 links for this procId */ disable_all_sigs_for_relay(sta->t.usta.s.ryErrUsta.errPid); + + /* we've lost the client, bring down all the ckts for this procId */ + block_all_ckts_for_relay(sta->t.usta.s.ryErrUsta.errPid); } return FTDM_SUCCESS; @@ -222,30 +231,26 @@ ftdm_status_t enable_all_ckts_for_relay(void) /******************************************************************************/ ftdm_status_t reconfig_all_ckts_for_relay(void) { -#if 1 int x; - int ret; + sngss7_chan_data_t *sngss7_info = NULL; + /* go through all the circuits on our ProcId */ x = (g_ftdm_sngss7_data.cfg.procId * 1000) + 1; while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) { + /**************************************************************************/ if ( g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) { - - ret = ftmod_ss7_isup_ckt_config(x); - if (ret) { - SS7_CRITICAL("ISUP CKT %d configuration FAILED (%d)!\n", x, ret); - return 1; - } else { - SS7_INFO("ISUP CKT %d configuration DONE!\n", x); - } + /* grab the private data structure */ + sngss7_info = g_ftdm_sngss7_data.cfg.isupCkt[x].obj; + + /* mark the circuit for re-configuration */ + sngss7_set_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG); + } - } /* if ( g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) */ - - /* set the SNGSS7_CONFIGURED flag */ - g_ftdm_sngss7_data.cfg.isupCkt[x].flags |= SNGSS7_CONFIGURED; - + /* move to the next circuit */ x++; + /**************************************************************************/ } /* while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) */ -#endif + return FTDM_SUCCESS; } @@ -253,23 +258,26 @@ ftdm_status_t reconfig_all_ckts_for_relay(void) ftdm_status_t block_all_ckts_for_relay(uint32_t procId) { int x; - - SS7_INFO("BLOcking all ckts on ProcID = %d\n", procId); + int ret; /* we just lost connection to this procId, send out a block for all these circuits */ x = (procId * 1000) + 1; while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) { /**************************************************************************/ if (g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) { - /* send out a BLO */ - sng_cc_sta_request (1, - 0, - 0, - g_ftdm_sngss7_data.cfg.isupCkt[x].id, - 0, - SIT_STA_CIRBLOREQ, - NULL); + /* send a block request via stack manager */ + ret = ftmod_ss7_block_isup_ckt(g_ftdm_sngss7_data.cfg.isupCkt[x].id); + if (ret) { + SS7_INFO("Successfully BLOcked CIC:%d(ckt:%d) due to Relay failure\n", + g_ftdm_sngss7_data.cfg.isupCkt[x].cic, + g_ftdm_sngss7_data.cfg.isupCkt[x].id); + } else { + SS7_ERROR("Failed to BLOck CIC:%d(ckt:%d) due to Relay failure\n", + g_ftdm_sngss7_data.cfg.isupCkt[x].cic, + g_ftdm_sngss7_data.cfg.isupCkt[x].id); + } + } /* if (g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) */ /* move along */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c index 814f418a13..ded695c205 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c @@ -83,6 +83,7 @@ int find_ssf_type_in_map(const char *ssfType); int find_cic_cntrl_in_map(const char *cntrlType); ftdm_status_t check_status_of_all_isup_intf(void); +ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan); void sngss7_send_signal(sngss7_chan_data_t *sngss7_info, ftdm_signal_event_t event_id); void sngss7_set_sig_status(sngss7_chan_data_t *sngss7_info, ftdm_signaling_status_t status); @@ -90,6 +91,9 @@ ftdm_status_t sngss7_add_var(sngss7_chan_data_t *ss7_info, const char* var, cons ftdm_status_t sngss7_add_raw_data(sngss7_chan_data_t *sngss7_info, uint8_t* data, ftdm_size_t data_len); /******************************************************************************/ +FTDM_ENUM_NAMES(CKT_FLAGS_NAMES, CKT_FLAGS_STRING) +FTDM_STR2ENUM(ftmod_ss7_ckt_state2flag, ftmod_ss7_ckt_flag2str, sng_ckt_flag_t, CKT_FLAGS_NAMES, 31) + /* FUNCTIONS ******************************************************************/ uint8_t copy_cgPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum) { @@ -1475,6 +1479,46 @@ void sngss7_set_sig_status(sngss7_chan_data_t *sngss7_info, ftdm_signaling_statu return; } +/******************************************************************************/ +ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan) +{ + ftdm_channel_t *ftdmchan = NULL; + sngss7_chan_data_t *sngss7_info = NULL; + int x; + int ret; + + for (x = 1; x < (ftdmspan->chan_count + 1); x++) { + /**************************************************************************/ + /* extract the channel structure and sngss7 channel data */ + ftdmchan = ftdmspan->channels[x]; + + /* if the call data is NULL move on */ + if (ftdmchan->call_data == NULL) { + SS7_WARN_CHAN(ftdmchan, "Reconfiguring channel that has not call_data!%s\n", " "); + continue; + } + + /* grab the private data */ + sngss7_info = ftdmchan->call_data; + + /* check the reconfig flag */ + if (sngss7_test_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG)) { + ret = ftmod_ss7_isup_ckt_config(sngss7_info->circuit->id); + + if (ret) { + SS7_CRITICAL("ISUP CKT %d re-configuration FAILED!\n", x); + } else { + SS7_INFO("ISUP CKT %d re-configuration DONE!\n", x); + } + + /* clear the re-config flag ... no matter what */ + sngss7_clear_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG); + + } /* if ((sngss7_test_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG)) */ + } /* for (x = 1; x < (span->chan_count + 1); x++) */ + + return FTDM_SUCCESS; +} /******************************************************************************/ /* For Emacs: * Local Variables: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c index 166fe5dc04..49da10cdf1 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c @@ -2668,7 +2668,7 @@ static int ftmod_ss7_fill_in_isup_interface(sng_isup_inf_t *sng_isup) if (sng_isup->tpause != 0) { g_ftdm_sngss7_data.cfg.isupIntf[i].tpause = sng_isup->tpause; } else { - g_ftdm_sngss7_data.cfg.isupIntf[i].tpause = 150; + g_ftdm_sngss7_data.cfg.isupIntf[i].tpause = 3000; } if (sng_isup->tstaenq != 0) { g_ftdm_sngss7_data.cfg.isupIntf[i].tstaenq = sng_isup->tstaenq; From 21c60c698dbf82014f053f202de73f2bc4c5986f Mon Sep 17 00:00:00 2001 From: Konrad Hammel Date: Wed, 9 Mar 2011 15:10:14 -0500 Subject: [PATCH 150/154] freetdm: ss7 - bug fix for compile bug introduced by merging --- .../src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h index 291eea0985..02c450b94a 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h @@ -503,7 +503,8 @@ typedef enum { FLAG_GLARE = (1 << 13), FLAG_INFID_RESUME = (1 << 14), FLAG_INFID_PAUSED = (1 << 15), - FLAG_RELAY_DOWN = (1 << 30) + FLAG_RELAY_DOWN = (1 << 30), + FLAG_CKT_RECONFIG = (1 << 31) } sng_ckt_flag_t; #define CKT_FLAGS_STRING \ From 75ba53cfb2ac746e832f8f61d6bc519c80ecabf1 Mon Sep 17 00:00:00 2001 From: Arnaldo Pereira Date: Fri, 11 Mar 2011 17:02:51 -0500 Subject: [PATCH 151/154] freetdm: minor changes to mkrelease.sh --- libs/freetdm/mkrelease.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/freetdm/mkrelease.sh b/libs/freetdm/mkrelease.sh index 4b9a18bf9f..46d4fbd93f 100755 --- a/libs/freetdm/mkrelease.sh +++ b/libs/freetdm/mkrelease.sh @@ -129,8 +129,7 @@ cp -r ./* $INSTALLPREFIX/$release # copy ABI compatibility reports to release if [ -d compat_reports ]; then - cp -r ./compat_reports $INSTALLPREFIX/$release - rm -rf ./compat_reports + mv ./compat_reports $INSTALLPREFIX/$release fi cp -r ./* $INSTALLPREFIX/bin-releases/$major/$release @@ -141,9 +140,9 @@ find $INSTALLPREFIX/$release -name .deps -exec rm -rf {} \; find $INSTALLPREFIX/$release -name *.so -exec rm -rf {} \; find $INSTALLPREFIX/$release -name *.lo -exec rm -rf {} \; rm -rf $INSTALLPREFIX/$release/{$LIBSNG_ISDN_DIR,$LIBSNG_SS7_DIR,*.tgz} +rm -rf $INSTALLPREFIX/bin-releases/$major/$release/{$LIBSNG_ISDN_DIR,$LIBSNG_SS7_DIR,*.tgz} # clean the source tree -rm -rf $LIBSNG_ISDN_DIR $LIBSNG_SS7_DIR make clean make mod_freetdm-clean From acd6d4442b882e33f7a67ff8c942b6576982527f Mon Sep 17 00:00:00 2001 From: Arnaldo Pereira Date: Fri, 11 Mar 2011 17:36:42 -0500 Subject: [PATCH 152/154] freetdm: fixes to mkrelease.sh --- libs/freetdm/mkrelease.sh | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/libs/freetdm/mkrelease.sh b/libs/freetdm/mkrelease.sh index 46d4fbd93f..b079158541 100755 --- a/libs/freetdm/mkrelease.sh +++ b/libs/freetdm/mkrelease.sh @@ -125,6 +125,12 @@ echo "Creating $release ($major.$minor.$micro) at $INSTALLPREFIX/$release (direc mkdir -p $INSTALLPREFIX/$release $INSTALLPREFIX/bin-releases/$major/$release +cp -r ./* $INSTALLPREFIX/bin-releases/$major/$release +cp -r ./.libs $INSTALLPREFIX/bin-releases/$major/$release + +make clean +make mod_freetdm-clean + cp -r ./* $INSTALLPREFIX/$release # copy ABI compatibility reports to release @@ -132,20 +138,7 @@ if [ -d compat_reports ]; then mv ./compat_reports $INSTALLPREFIX/$release fi -cp -r ./* $INSTALLPREFIX/bin-releases/$major/$release -cp -r ./.libs $INSTALLPREFIX/bin-releases/$major/$release - -find $INSTALLPREFIX/$release -name .libs -exec rm -rf {} \; -find $INSTALLPREFIX/$release -name .deps -exec rm -rf {} \; -find $INSTALLPREFIX/$release -name *.so -exec rm -rf {} \; -find $INSTALLPREFIX/$release -name *.lo -exec rm -rf {} \; rm -rf $INSTALLPREFIX/$release/{$LIBSNG_ISDN_DIR,$LIBSNG_SS7_DIR,*.tgz} rm -rf $INSTALLPREFIX/bin-releases/$major/$release/{$LIBSNG_ISDN_DIR,$LIBSNG_SS7_DIR,*.tgz} -# clean the source tree -make clean -make mod_freetdm-clean - tar -C $INSTALLPREFIX -czf $INSTALLPREFIX/$release.tar.gz $release/ - - From c8e17e0864c9fa26e2eef31d04d4f62cf7995d1c Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Mon, 14 Mar 2011 16:46:56 -0400 Subject: [PATCH 153/154] chlog:FreeTDM - Added PRI-BRI Debug document --- libs/freetdm/docs/PRI-BRI-Debug.pdf | Bin 0 -> 399483 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 libs/freetdm/docs/PRI-BRI-Debug.pdf diff --git a/libs/freetdm/docs/PRI-BRI-Debug.pdf b/libs/freetdm/docs/PRI-BRI-Debug.pdf new file mode 100755 index 0000000000000000000000000000000000000000..53386b1b52eb580ef551c5e26954d5d6b202b119 GIT binary patch literal 399483 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 z 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�&~09jn}FRe}M_3 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

sek&& z)3Jj5N@>@Na3Qeq-a5!H`lJH)k-Mv1>U!oSoMcs>=D$L|o$IHEs|b4!&CXZ^C=pz% z`Tu*MYUCKE_lwA0-JT8d>-YObbWO`+`~)5zddh@Z5WOHZ%D5F5=-%9WsB<7UDB@O6fHQvS=iYFyqRX z{cC*RPYKCl@$9Jy*UR`&=|i7;+kF|sJZXF?!rQTpe7*Q2oAeVmiIXQguSW6UY~J5e zJ)4J+2nH~91Qiij~w!@p_p%a|ZAR4lC~1Q#ZKS+BG4T1ltz65%J;A90~u1~b0A$OHeDf{W|MMc7yMS_*q-(NF( zp7489=ywoJM6#_8U8A){wAmYPjH0_XI`QGFe`TU}6NwhgCTu+G`;iHhIMOMhPIlNf zUGyM!)9cGbsLX#IOQ@mXAEk_(1rhQk)T-bg3ICajpFHZg%lzMg-^o&sKY!?x+{n@R zS#GdJ-xqvq=lVY(Ich2UOMCY#MX@^Y$-+ufLik>Nq7D|2@Bhg6I`r2?ihrv6bF=?2 zSh#m;Hr(w^h6-{l$*+2Xj_A+lem$+rqg_kCx={%X*-51GG!4@u z&ikjjtd`GFv>uhCC#h3GT@J*ZfrRa=_gpZpc@j9ZMl}3e+6`FLxtLaEkLw0;KNMD{ z7ff{Gwi^4zV-D2a$0&C}#?~9E8vWI64KryRY|h;|B&;IFO~M@!rwS1(cgKGxPga zYgun-!TRR>Nj8P@KH&GSFP`3NlMY!iXU-W#>q6w)yL?Lj4XVY3_>=l$1%G?=-kIlx z$3xI<%Pm|mT{#>3hmG~rcH#lg8)%tIHn3V-&sCeIdDFC>`!5wwFz9ToCwH~Gs=HR& z0^;Jy4c#>A=PNgWwp>I={ukePj6(alP4SI`VlTm&oH6|@58tv7qhYG!1;$}$?b9oGOPHU*ss z*z5_+aQ}*j?YQ<)QRq|h-zVVs$i0m`fyQD**8fZe1S3Vp`E~MT{fB8LLa{_A)OuY* zv*GL-324fJujvf255fm#(Y=f*4w7!@Z`H`Vf3Yw0264$Ve?H2v ze_nSKZ|$$^kJ6$vZoEk*+lUL)ar`QWmB09}{FTHQ0Iby6nHqvYHD}STSrel) z^Dz54D@;#7L&QSart6h`lWXl9W36V=8nC1laQanJ$OIQ#vqzjzWs{NO=cxF+$0tE- zOW?K%j9=455s(?L{+Ftl`uEXAG0EDj(F_6qKK!q!o5lZh?+o*5VWn%+Dq-){3RnL48e{ThchSx@>nQ)T-gaMnXI z;OIKHDDK$9veji+<^lQY!Cv~HyJ?Y7LmIeGm%Mn{$qc62Ut%59v^W$y=C&La(XRKs zwV|oLDx$wUufc&raMZZpC)b0&qCL`4f&8;|I)8 z)QA@Z;}&IAAE(s8zqR*$Y*iN2i($5sQx=lWt)NCG?gltvM~Ym zN7f<9o;OKrXz0}#R0cZl*x_UR&8VcG?!0^-;J4=90WftfM~uO;O!{6h=j6aF?|;Rs)eEdXmoJ2%g7+5-|ib|bZEpC z_$9Zs>8w&b8syabTC$*^xN?g2HdWA@sI9;1c;9^Av-dktIM05(j2+1?aDfWhts~Q( zXc5z)0p%$8?w4BIkal^R9IOpRLL*wZ*yC$m(F|fc(rTldVb&EF-hoG+;u}=ARwx1Yp%UR{n2O zvQ_Hu>KJ5V~ApUk{5Z`zPg&e)B6uj^PSH|ji)%E~`U`=5{wWj6=a`!{|^?}u%77O~3QGPCs^ zhrNAK=f>Dv2=L9{I=GMhN4s+WplJQWAeC?8<(og(<3GVf^ z-ot3fc;hE23*Efx&~rj zjQS3fhlD8&f(p)TSOu?B&S6E%Md|J|G)sGoglWK&(b6~O)|YMImf>%aQONjCr}!C< zz9c+;sz=6Z?Jc)MTpFKDt7B&JXVsLl1&iYoLd%MgF^^^E-D*%epGqC7b{(`e&~{oU zv+--~w)1qx(te`#RM2e$8__vAzTUT>7q;{QN=ja^;w%p0Ou;JMD6|5!V_Izcsk9cm)0jYi6V6Mh%K@$MO(N7D(8V1wNkg~NuwUd zT7o82;xd8Iwbtyg4>I>A$bMSVeSY?)pQar~gP^}`UZca2sQ6GsqYQ4=uY_X_yq?>f zr=9t3t-717yWbo69GO#0Z+r6?iqKDGlK#w+|liY zr!!oSX^+5jDGkn%v@;&#Xk-Uu*NX0Cm)T}4(iy|`rgED+(Fqu_q#xL;xh~T;>6yBX z=&#ya-C1PYt;uY-MxDc`_{?Z1`Y*ADmm1>yh6chLm0gZ)&)0u_9>2LvXZYFELT`Yl zQW(C4I!kzHYvcEhZcP`{9r%11_F*^?*GbEV5Z9@P^br3cys4_dcknxb1#VW&OC_x! zdKQxJ>vl}PRC;vg-W;kdOhC!|+j&;0{) z*vK%DP82}6%g~p{nn730U0#H%!jUIglI~;F4uXUXL(mVzgbe$)b1XB>AMDOX;|^;C zEq190-_bnAH7YeDeocUFkzK9KyYJR6m+`E}j2CjuzhBI4S-MYa7wxU`S7^0%c)B$4 zeryd=*Y^j`!AU%}Fe?;BHW%yp%>DV4YEL|=L>lmupbt;IcSh7mEqOc5X8WpKL4~k> z>w2t^XP_qT6jwgmU^hb8zUH*$vihz0kBl2Je)Lu=^YHpnnelRy6_n){<^-jyvsxPc zt`R&V{MwztW`wIAf_4^Kz}19h=MN9c4ADzj%s6N|w*#uv=YdA*(bN!Lf#cGk~sZxzArP zWko_W^}Gg%#-HkU0ri^s6ouso^sw0sa(8Pgp213QCGfSbBZIRwg4uD9*V)v%um~!k zXwx4_>H^ROJMaqpAM@CMoh^QOZv0doRR?w5fA-}fnp)XYBWimhwaDr?Y)Q)jtLv9q ztHvG2awm7MTEH(V7;gqkbto=)ilgc^uZPFv_g3EdJO!l%C%1m2&GV~wetHL}ZcVfL zVx{e4<(uEAhzqq!cgwO`XKZe@f{a^Nl7YKdx0*oqYVagy%JPL6U{cyjh*_-(W`XQ$ zMZE{#*s8B*9v2LR_q0M!mAauj`ocRa5Z?)n85QU+*y6F~n5oxO>(A39*LLqj7QiA# zD;91Y zwn-NOU|r^f4lz{bgg1G*rEzc4w4W#Aj3jq$+9M$z)~Z$mL1Bk2;z@O@<-XPo_(uB= zc$N*guIVZw5Ggyc492#=^!CRc&V#kr3S`?Jl0t|#3AyY@t(_T}ww7UMtXMzWYlDtw zN@i-dIeqt}y_>$B5=5XnJ}fW&en0z!bYd54o{egqJ8vL*$Kk!!VieYXd(*jbIajBJ zec7dM-nM4l{;J3L;0ZO!t<{mD6aRA0rBI%oKkwXq&+pG5qh%$Q`m}5me%$(9qch?~d2n&(w>l;Y*01L^+gvepPNc-e0(- zzk6*EvU6*>LDLexW5YzU;Mt+{HhZ87YAIfm>}l!E8Ld=!I>7aqA{CA=`(2qj_|?0F zPZ#HS!`BuhlnU$lmfl>^v4xT#*~{G%~`z#CklT1r{J2O z)?KdgDy45D)B_)mHW~X3njNI7IG5{7yHi%XTVDW0`WP<&;nd|}dmk=k33Ox}3IxiX zDS+=b8I2ocgXimb^XD7x?8b7H%4ktRp9l0?Cusf7_GIfk^CiPbKp5Q1udP}*rreE- zPy6gI1jZu#r7&Eb*fr?hqY{xeP9G>Z5RBpJ@;qLMj7mkP#m^BkV^e&@KK_VS?+H$S zq2qxectz}CQYfi=<_1BoG#8RBkm6A~dBUjEUUn9+D?j_*Qa=PAg|FGjU z2@QLpNRZnhOij&vPYY4KW&6$Cvs|!8lmUDmyX)N*C8Bu8pv6DdjCT zQSY7ytvS8!T9HECKP_|r`H0skIn&)mUxUUBXpWT(vlXsq&1S_mTN58xZSu2ib=*Qv z?So_ampDI)-b2PAmE*{qk1rrW0Q10nXRh&>oIYclQ9*X><>y-?nxA^lL=LGnY<^qU z#v8220w9#Xn+Qav`CTjay~XZ;*QDOa+1-TkPLKfxm`Q@wIRxW@QM`RHBa`e-Y+G-+ zd1hl}?@6N9heMa=NxW3{IQSn#cd(xuydlf;+cyGnD!&k^Lpc@clV}r(_4d&-c)1|$ z9_*i6Q@|eOLlYiYQRVZ@JM$3F1H_*(Ts3hZh@^P2_pHdF>`lgK)%#!ab%~P=nTi_?P|*P5aUla?Fq0lDP9v9rkk)}$TXk=d z(tkPaP|0D@#h~c#DBvWgGVWV47LGDskfB`&5yxxf(T#(-h}F|t*o+;Hv@<4q_#w)` z-<*l+0Tw-~IYX)^5x#U3=w<#U^VDpCMJCl*Mn*L-hAA;n-hdYBeB!dnUMLAd7+I{0 z=I={kQsho?=_i>SD9wp;vy`C zBwR*5E{v>s47w2YQ|y&~bU*HH!rGC$c zK@DoZ9#7|oP86)D5p~5I;qphy^SynO!ndjKWXxpqI&Zg?;!TR)TJ7e2kx5_+$Nnx4 z@cHK2R8k#3>=DtZH=pjHV&@)sDCwl9e7c`|4YIvVXobY2j3*RKk) zV^!}@sTn+55dC8PEA)h>;i;Yfz}NPe*7O;NYgCh&ns<9;SpP<1vZgBncLPt>DMzlQ z-#<=7fw^DH}|iu?~Kd;MYm$mgF9+ghuwmR&8C1B}blG zeN#nhfs8VA(Mp_b+6U{2uez*Lfq|nnZk^&bN}IFYo-nYw_KbZVC5^QiOKPNg%oWVx z(*kj5sJJE%1;2K7EgdOrDUbU}FFS_fXy%gk$oeo+A^1CwsaHCX#(RxuN?1-Ml3Rm5 zn!7Ii^PL)-ZRjeJJFYMKWO(!!hK;7+(zinmu2eIu-_g*ZR?NmQ4LZ+(=t2PFCIu^y zk;@BB;UZxve`aUS_pJomV|F=Qij6=Fva6tfMjWiiJ7&B^#4dfS`wj#Z0oSE7W?(Sa zSCLp1E#*FR>XfOlYz(V=USMXkdlBf>az`-F6764HIVaiD4%OvNmic$j0g;QU$b+h` zWhu{8H_j>*%t0=apIZAbCe^-0seU>m3Xa{qT<~mEYH;)=mZl;(8R27`_#(^2E*+z( zprXk>DphbCcc-iTpd$vH9?tabn)-mx%N~m5wDYU~6!9e}n>Tr+VQa{F4Nu`QcmtTj zkihKz@ET<5GXGE3Z+?=MGPvaG`?fxmE0^WUc^sK&l6KsvxwM}2U5zCMcLIG2lAXZWmfHNyB_YC2nx)N zzFv5puUBGgU|0NGf^t;JkUsg$W$TlJU4)aWQNzuPnG37R-}rM<%}dv&nA2I*Za;lH z_vJ6?8Jqn)S2HCEKjl8Y!!JU1QL19JG3Rc;D<~Q8O7lp1eEV8bF_(^;!49ogA-Cb3 z8c%S*R8H|beD2`9Lggv^h_qL2b~io-JpJkS4=X~aI~;CTQpUP+UeYfRP9(%QXme}R zA{0Z_4}HLj!R#>vC}>Unz}t*=%KGqKJoPLjJ)7I$V(Nxf&$J~`r)aLU@YCHR3wc?LSJ15)Z+K+ME z-;FSLohP>-b3_+un&OPpU$Lw|h0gQJ9HxIqeBp%O%z56r(C@s}x?qMmB(+UbFQZYO zJsB17&bV0k0x#P??={NgOrpbc->O$=j9z0+f#79`+m4?T;;jL3OWtzK!I zl^ScHlg!>`6D&aP#T5@p8&DtFU<}oPwXCFd^s=(LIt(Bdm{l$|eYp~0&Y9 z6-$lfn+Knlj!$g{4v;2y{pG8(Qu#rE)+j~=WY|Cp*2x`rUqSAJ`ip>avPm0Okk^?k zt(XrLJQtDp^~ruj;aBNCn{$}v=o455_FwP4*5_V_j7C?aIjE&O$Rl1VeKeFya+rgA zq6O`dUAN^|xh}!=lAj*o=^mLT`%ci>2{Qg4B-^%CtY?=R$#|`}$Djk-n)36unm(^c z3APwJ^JAV%JEPd4tf3b2jOIWl@4n@muC)v-y@!aix-=%xPE%+b$)MTN7qP|f?UI3I zNH%4)=e)&xz-T~0J?!TSgwdlGv6taS%#1^?j1Jrrl<7h<=orr%==QaBc7AjkYuSrt zdvRlA14gH(umQtdX3;q$aH2IzVLC9fer{giabXQAQIC}trI)hri(EF|f&(0i$?=>H z1SMu;BB?GpIeNUW)|~lH1c|JlJ4rH+i#31cxg(E^-fAX5Nm+5$Z5!Rsp{%8lP zZebeS#XDCkzRfqcEg-)7bKOQ@_S#wF3hG*s zj$v9cL9D|F@0?vw6}w5iJF;s+9mM7t=={DM3+r=P z@?Maz+4dZtR44N;spV$%nw!A9X;E?Hd*}uO+{Rs34T++No-O=$2Q%@@;SgZuHROZn zvoi@6nwm&9!p=2_gvTkjquMEE)HNNh;S^H@=wq+#Y2C|kw_my`C}+9Egb~UUchn{* ztDhS%+E8%M$TMm$<7=Q*g88Od`NK{%4RKdUSMVg|1Kr40YxtP2_Rbrr@Cpx#i!ViF zxKPFQ7&vKFoP4)P%2{vd-lto<@1h@V-lHzack#r(?!=K| z|GwZ>tQSbMB2O8^dwTiW%~J~hfD&|`_eBqXJn|X2*ZKD^S@`4e&pba%Diz`e8hWaZ zvmm?NE10YtevNKruPXA=`k;iqV@V)fR#bFBccjU5Hy_@0ZywZuim;7p$!B05 z?(Nzc*|E8I6`J!1LwBR28H40O)xuQ@Y=r27@KF!9jBfJn7t1DzqDsS5gWVDy9;dW< z)z>JCwM7*8xyp63&o)%EN5~a}_E%$1=-r<$2VEH=?cdQ>;=ihlYHY)48@a2k>kHd^ zJx@YYZA@nr_R1kz2(?#o<*h7}Qpkio+6zhLEG+YILVm(WbTNA@Z}ib%^k0snHxv@T z&>CL#J%_US^|+J=%h;^E*1Vpc6b0Ggs&LoRBCQvGS-AC9fCq`dY>g9*FY=ZMGhG5ZO+ z=SR>E`a=%RZ8w!YL)MC1%{BiK; z+t>Zp2rmMK-ID{FXBy3D)BQ2uZ7GvHEa!LR4ei{ArE1 zQIcH$_E*t1BqC*Foge#=a};Bj zCi6DFG0uBsaF=7%_|RtO`u8YM(eyy^#+|8oWjT|OaQTO_aRmu8wMAx%4S49PIl~c` zd(dHaHwK493kFDxrlq%y&OLqNKKN`o*N14U)7v8CYIfz>#;a42ZPpM(sYvQ%yiO2m z^>^ZltM^=go^DakzMa|j_3UXWlyDz0(ayR#FHdvoQba33m9!^0{r+6AC|hXf6`7-NSNUW70(6XG;@ENc=0=CnY) zae5@Qq;&K_!tfjov(Rb{x(RJw*ADFrshhR*b1W0yvDO_U_bDX4DpXQYE#Ue*H$p(F zko_arpHPo&!^OP)JY!E}m@fyLw(yyg%Czk`mY@XsSyOOu~1;VYmU_KCs z6HfeddB3CG3D#ALgU__c_C+{u5spQSNBEnH>V+hO>dS?UP2D;rUX<8`N>u`bf4Ogu zM(S7m-3)PET9-j^zlRqpeWX4eO1J|ZB^%7GN2P+li2JK_)qSuDgfAiez8A9-!tf8(SSC-k z*ucg+oFkDSrX;6c$8wx?G>&ERb@I&&ehG~C>$@JzwNV90RU5H1i?(D$l;g)*lgC+% zIX(~3TeqaS?W;O|Op?2zyE4h@%@;ZJ=v$uO%xgV8m9wE*K6-3oje5@dS2`hEA>)fU zTBKsu;uh8~Z83ApaGkjW%B6zJJ+{T)GryP?-?4ltjG0?Rb#;tY}s8Sj62`&x`re8>2u4@Pb*)j3Cy$X>0^3tf!d zNGfNJAl8s^$=1(kxbR$^`>}ytBT^1ZaddGD!JdkB#6-PT!w+IO(e4~9l^Oa5E-MYCgO~*48UH1!!PUtEFKp%_Yc*(@vR*S$ z=$hAZKy6FC88!Wa4^GWoUK>BC52Qij5|yHemv*vQylzNSmS&>qtrBg}Tf{EC)Nh}G zs*hO?_>?F{OGga>MFjec;1n34S#8O+nFkqSqv%?vqCzWS7!$xUi&Ul#eR$$N(Y}$7 z$o6GEreO_L6i?7Z2>Uz^-D7u;U&=K$9^5c~C3(Vi>v|dfW(?cZxqux*s;er;365G* z^kkoRO~A&=e_#GlIEUg*TX&CJ$~6|=is71|ymx5$G5aYwU0rvNYsxGZ-h!c#puA0J z_#wLkIbC^ok4s7e7T(M%+|uV`X=wNbdld!UbobhPMq+?kncCRN%LUQU`W)2dzCJ%g zxM0n3mrx3J>D%JZ6O!W;_y2sGrJ8?IUjnPWvoT(>Nz_uRnZjeakdS-ul@+7`J@7nF zjQ(6OuU2yD3&k-Z4Ct{nwId8*uuDjNna9clCY5x_FSf&nMRN*vX6zw5!6u?8%xHg< zzDP}c1AS!z2v95J86-7z4H#DZI)}d=O-=ir|(Vh4&f?n{KgUj*j(tD82J?=b6xKcTRC% z0HTv!%ZrJ^7>lHD`L+3l8HtJx;pZo!4^!iMmmqJ);z|JjG$Rqpc2;n3<@%=szBn3n zvXZV4^~NFxaS1=ObrG}mHosf7A40glmwAX4MVCa>grbSCh$e?8D?Dt*c*hb89fFB< zR_k>PwLXaQdrNXT1k;s|Cp33Otx={GdE+{~S~iUAFAmG5nqM(##@*`5Hsrm#44^N} zJ)0cAvD~@m^5Uy<#7Nwfu>ppCbN?1iG-u&Un`M%Jm$>uDHc^_ohey%!=H4o+?vTEZwZguqq668E7Jmv+=AHG7`{-`e z>}X=GZ`1X6O#(MdY{=J7b|xxPgooggGhmO}HOdZl+FUJX2^VfTklsmNlKWXK>Sh*p z=*d?cidRO?7dPtaM;;C@V^KM(38kh3W`c&_udDQli)}TY4%NJ;y1VD=x#wE#RzuwY zC(9pjO`OqrE=!Fq?AVgd=VuS@sds33YA79tp>6FDa)^^9d&Q9Dy5V{f{eim3tiwF> z<{izTEXhA$``w(>X)Kmz)(5!`^JnwR*2oRv_c7zmVQ`P*e4XR@lMF{2(qUm1D}S{eu3niS;MKcCYBi*DcBi!QcB6&l}wcnk`J-SDW8U?gP(eyBMYNV<`3$axL#Vk7S^I_yBrgN52{nq zoPobm!GG~<`XYT!2l_ip+58Z~-rt4RK!)dsy_2R1!p9!Fx0OFWl=ga$0zz1fwR#_A zTFHkX^=^h^y0h<-RGSo)!ia0Vh|SE@*??Zwo9LIno>mEMPP({vcu$9+80^#8koQh= z*a*MTN0J0xwA8C{d&p(-%wio1z-6*`dPS*jmBZyYK46$YKJn#MFc21<-FiE4q;7xa zkclEx?P*=3c4&A1Y?~%MTokbFg7keZ%u|nZt)FC*f*47W+KB(+ItAWkfheV ztceNbX_}A*1@m&^gGp`*J@&WI2s2ALZRW3mCLGqSoxfjKX=E`B_4acz^oJ>J1plV4 z(hJ$7rGCej3=GHoZRL?aO9x86ifkW3*$AwTy^r?(c$ql4DOcUD!>0OMxiQu6X`^JJ zX_a#UJql9+R$M>C7o065a{+wV@hWtXorI4&>6kt9yG{wKC zN|TCd9SgO}lZHf#6Rip|3IHVle(f^#>>vDA znObb{;y6ay6}4Cp=Y-s**J1HuRVb zS>pAM1HFhCG`VVbh)oF|nk+&bBOaA&l*!d=8g^)0%qIFJ7p5d7o4tLexZ47Kd|jZ& zQY#~jN9hO6x=Y8+lWs+~;cQ2d5UZ8ew!0}c{lw#s7nK$Byz0Vh>04eB!h7r4h&5-| z6soZ|6j#FKh~J@?qVTF$=au$FDJbL^8@xR8%S)kk$NIdr$oE)#$4kHUNc%vpSo8*P zJ6+P)mH>iSo%$G3et+fa*DT>z8K z%R9%b-9M{(kfLtZf7sv>;DcD-e&KRgbjbzjdTe|Ma*23Ic!+W-)CIL$M%S*?C2Qdw zm|j{ze(D)lRM|n{Ru%Pj^}O@xrOgK{mxTKi3$?oYdR|2j!7kaERqfT_1K~@({g3UN z^`+U@UUy#Fut(oVF>A2jm-Zi?v!FW^&DdL%J8US$S@LacHN{o(O)NC)+;7A$?xLo> zOLP!?mAxkl^IZ08@{76%@GJEz}zYP1K_L05}_Go^8F--zLdn77tzkyS_=|7{C{MPmWxRB?2 zCX-U0RZbjdFa8FnJP7kmz?a3)FK?)+snH@M%Ja$xtI*KO^8$97A`oX?P1Q$WrajLm zj5#?OtICR0jE>=G6z5k{g7J56-afs8t3Naq-X1nJH7|p=OUg^MHkj*N1>yF3b(aEg zSH0Rx{)LQ8Kpk{u&YIl$lK-w$d=xkeoHpDh-6q`+J1cXV71%>8=+P8?p^dU|_bO00 zpxZk-055>|dLD_Wxoc)F+T}T7ku0m7(n`D?rCSOpS8yQn;4b+t=`QJ*<%;}DcV*$| z?&z+~ZE^F+YNh#z#0{|zky~c-ME&5>n%za2Lw&QLPj-{nqRoob)fca>dWC$$Q=bAK zfxfHSaxmn!4F+>*InJr8Jrpbq6xg`aqu+hIf~qS$bM^2&eNSygW=XdG3_Y_%@CLHS z+7v0Ocs1p7f7+Byu~vT}c}wkQD=enJr!VNB3 z{r&ap`gQ+6{f2(Se}R5ezv&;Om+9sH!TJNe%73B$M6dN!F{*ii<&hn4apX)FD zm+SpH+kb^Vpbz-340Q_)^j{Sk6dLTmF*H0h+<#MOL};Y{=Fq6nX#cIDF`+B`lS5aA zuJ%t2T^pL{zbkZo=nwvx;kDtl{(Hh3!<+qo4u2WW_RkI<3;*b!Q@da70se?mxOw-ZNJUJ1(aE@pYdB5lwA zwkg=Ic$tN+PVizpOb@?7FA&kdtk zlErojY~-mg=d^aSJ;JU@brN4YQ22cy^wMYx%(;4L`SQwQz1V%KHYf57OgqLgvQGWv zDrUGu*=2A4xo(>0kXKt(F=4XueBOG#lfxA&XMxmrN6Nr{qkDVtw)f-s;gnDJz-jkh zq5=gy;|BM<`)stHXV!R!ybru(-l5;R91&yiT7}d2wD4Juqy&Ugj1HYCZ*k>GbF^lxP}tx{Up+?3XP>vK<%OOTth<*>H`o!;urkIIa$ zFD#l1N;3&<%luR8b3yIMYq8O%Y=CQyVUZ&mV(zK$r#!~H63661vO;C|GNX3%DN~EK zbKZQk0jv0~=6Y#)Wv`qbAN}AO(;G(5jv|Mp-Z01trOy3mUhkb2JtF)fo7?~F$|%p- zGlkn!^e@Z3XA82t-_Cej3Z0jLY>U*d_1WZ0zH2kIeg0Q8NyOr_xDnSA_$qwOAT^*EN_rk$7^WXwgSCM8*YzGkFg|QFJjvMHk8b7az0x0l#+^p zSWBZ}qIVrd=8#0|z``79ZXDCf3^V^-={h`qL;auPH{QddK zUrNcln7-Tl&N~+6-RAfxa-3OXTFfGCZV9-f=(EhP-QHIy`{sxQ*yez@F3K<$uvQ%P z4o6sG32AThw(&D4+&i(HeP#@bM-8BT`L zed&E++>*+}VfN+vvbk+O!)%0~SNX>6>Q!=4&Z_aQs{q_H!l0%EpX*s zb3pq`*8JoWuT;9>8nYz#tRd%Z4#p2&=E-a?>`i6=T(oBMjpebyN#4tRwg$(-XumU` z+dOkNq10Z_w2N6@Kjxw5uw)kOa!8g(Se7>$Z!?Q!Q`zn|_yG**>u4?uBfrt~W_ewK zPQv&d%^H$_Y;bC!y!RA7^Y*ZQ@6H){He)4ce!L`aoI9Va+~0l-KIY%0xg)1*@}uI4 z8-7Mh%_waCRQat}VlJiRpPBc*i@9p*$U=mfg?g957~e?dcnPS9GotYqqU=UGi|zc< z#sN^*`@uCwWL&&o{=6!9CzbMrFQ;EBXYAz_88h&*QF9lSc=br+n@uq+-_-X-w5V82 zTVi^}m__-HavPvEqv^-W82UHwC(pErepmlZMq#3LriAY#-sj8_iqsk-kAAz0#Q%*- zU-N*P-Nn)rPV(0fGjA}3&-U_YOV0O>0;m)3VPWwp@>MgYxZ9h=cqcLjxs>e)$lu}f zsa(!Te#_Z4BZ8w!NN)|#tLSIuHS&2l-?gQjTGyO0or2%)nRBi;b45!m?AaWrHTZQ& z?pY@CR{VS-$Yqh3sO?&NZQF0j#ev7ATkvWz@BE*QbW#y6|fIh^JTa~8D7ysryb z5_uPsBHxhtZLQ&PTJtq}A=d&kYw1EBSIw7OG`+ca_e*#j`2~K5`NwJJ^K~;@C^7=P zw?IkE9Du$5Gw-6{SD3Da#>{W_!d|NgUgz(ByIIpx_{_Y8^(>3dLx{}2V0U3@BdPor zS*s{#%siq2W{f;ivlp}WM&5K}gx$R7aeS@791oUcc~9`@dRFvJX6DbUP~NKwpOrkH z+W~K6Cu@5dnQloun!gN3z6eLYD46qw-fPjfX5^;NUs%i& zHY3w(Oe=JDM$^x($(`@Lgj=Q+C68<6>oLt-xTScCpB5W#t=FRBWkS!8#l6i^Gg}?U ziqgNG=DfA2ZkN)F<%=^ZC%0m+mSKKpsi{Y2Z*t8&5b4LfX%$Pe*nA3q^SF>wsU8mY zM|H4sg>pa|-rG1nIL&yGx>Y9B7}d=({&(hdO)i4Z`7QL%uoB~KK+Q94^TwQcCdGuw z`OnZy)YD=7w)1L~^k(LU+m-)IRe6lJ-Jpr6UOXJFT}!-Hx|0lqX#t~_4Voq*2nGKW{Q+O6z z8BJ&CbjHa&W-kHuFUhN6r*Fzy2CreE^R8z0n%DJA{hRa5n8II`@h-AYZh%I`oIG5v z1T*mbYj`!-Uyt8KM)^xu)-n|HF0%jczl%I`@_*nv;mI<6fv6-sF@v$B%Lqxjz< zMZcit{fi{>C8O-)w%l`+)SFA1LOwEm)c_huy=f5LL49dD&7jG2H_fG~G>=}Od+8sv zh8ECgluhr^*RWgY0PI#eC?stYeo=)Ei)x|vO=j?lCU^0QCU^6S zCNpIV*+R_X)i~}6%nJM{?hX88*AhS5w(UsUPO_6_(4JsVklMc1zE+0p>+G9k*uKTS zMb@)#wWrDYysk!5dzL**Hn;DypO!6n{fut*i}niH!~W1-C9kkQQOR-~uZr=8s;}zH zrK+K7EZ^i+G5*V|V!WeTtJd;;m9Em|GL@mdQh#kr20&KW_4BT)n==k+N!o$z0?=#3#*UX zsdia?)jqY)>aVg@wl#oP!WgK&RR^sL)M0hl8qDiq3{fZ432UhO)v>H$j^7Dbm-6Zt zS36-R*_!CobDCH;I?bHs)@{7{#S~usV!D&*bhPer&UHFl_c-01p4PoiZ)cG80Iz=W zD6f9;m~)kLul2Mu+nHy5$ZK7!bsl%-TkD(!&H`(L^NjP1wb6OeS!```UUOcvwm93I zZPr%jbLVqwo4!xawziu!9;`3)!}?)shgs>t+Nqz?3#>i*S^d2ArG8N_vcA$U>6ff; z^auI_>wsRRS6SccPxL3&L9?QRbx3d0o2(;dMF;DsS<%5dW>$2teh767b+>*D^#~2H zPJ{-B2K!`acxZ&r3XKen_Qiz8gvR({Lsy2b^3@0EAokJS7F0O~@ zTnfW>!S&kIl@ej=QxdG1@z(^UN};AGLo-T4sajGiq|}<)L(+jdp*-hOFXY>s`k*|0 zVdJ5@{ZI;{y|tjd7orqHX%u2z1{*`8X$n?iqvO+(9YFRtDP+mvR*HlzDt zQ|SSE5Lf4*1!;*EWFDo{!>}3j2y8q03v4Pq3i~_yD{Lk`25Z(#dmNJau$ zN9)rVtETY)YJ&=)*ARf zqm2l;iFTkoJ7F8sF4}`q?L~{0!7W+~ZqeG&LHZHlkJHa6)i31H?*yTZYmYY0Pc21^ zh#?`WpuLm4?voPLMRoFvSP@HAL=Cin)zAXQQw@M2M0_ScIvLs4Z$!Cy^);sk2BDNpzk_7Rgjk)Dd;4fv78*q72Q@ z!uok#DJ9y9j_{k+rK+K|?T6g^i~h)cfEa+>%}P@VXnBXB3}(G4B`z12Bm5X)R&u{m z{2sYa5Z560iQ;DDev7yb`Q9$3BFAas4&->JxD#=wi|L5_lvsf0@U(cEnuuq_-zZHy zE1siNu~0luO~ni1MQSD%iN%oY61%96*e&)@H?dcI3I9QHkot;4;t=&0hoOi4#1V0X z28g5LDCFOX?;t-WjzRvt_#W~f#E*z|TpXt!;)FOsy~I!AC+aDF7C%#O@r(Ec@?XWT zke?JMA@_twz0r$E>Mkv5(fQIReenCGAO08_1Ai4+1%C7~)Y|kh)W-BN@S~4`AAJn` z=wskVA45r|kD-C+V;-Uofq8+)DJC#K@C3yMo(w!oPT;w~B60(Z1AnKwftLcW!2hqn zt5iSmTHrlO3%nm#NsR-m0;?%C@Nr-rwG6BeWKpZYmcUj>wgq;=zbmkZeizsqI7A|F zIPfF+(UTD=RGAnG+EwhTRL8Dn$H5)|Z!dr3mHv2Y;w{N#6!#~BI zf{;_~sqjy;r%?z!<(-sdPq$|vU-XyM5dGzSaA(`Isl9!_eLvMSYf@2D`$79b_~+Pj z;Gb*Hr4IH(_CrW#o;?p~K5YM$>e`Rlk3&A+o{y3|VLwUL?WgRgkoyArX@vQk{Wqli ztotOFGj4t+b<#3KkS#0<`Vm#2>*)xFRE?-+kO=(zh=LNdw<=2 z9asNj{|6;`!+ryjrS?*!`KJ9QQh3XL3n{#9zl*r<+3!((`+fU;$d}p6$Y(FNms2f! zg}nlI`l0D-mXuy$W$Zu|GkMYwR_MyVhO{`KR_egj{d0M+zJ4ETp`} z{v08{u)n}_-eK>+UF@{?p;Y_ruW+Yd+utIWgZ5!aj@ZX=ci-E`5$lBg6Q1_Z_DQ7g zDT_KPpQ=LVsH&<4)m1fB9G$D;RRVQUwN!w*q6*klUnv!&hRRi%YN?Py52L~=Otlp( z#i~S=NbxF3p?^`yDw!OEW~ik>GjJQKhLoxrsYdWOR*k8NK{nJ3$R>@Ns+OuHgTi<5aU67($+_y29U0b%VdVIv-bis2U8FU5z&FW@^zeU{w$uu<$Vg97Otf?M?H)%X5XQ%~6uc}YGPT^S(WzEI@`3sYC0waiB863I6~eDps}bg7^)aq~qCSCqjaox3)LONc znj6dq$$GUO?gq61W!R`TB85$A6T)v+n^A_XYAf7rY8y#|2uT@42p5Qusu)B_oeUzR zI3U7rDPUGe!&Try(gqh&vcZMaz^s!7*K;J*Fsr4JFsr4Ja$=n7RL88D21!k)Ci$H> zC!VS~2@ZNOr)UNTGw%0arUZ z=-q%#ft;MqPG`jG?sSLze5WTu_ICOswE@l`gc;%tp(@UW&V?x9NM{sEewi}{DPQIM z9;Kb&T#Jwso$E+D*E=^NwMot&DbcyrnT&j=ICmh&JDus&$(iBYMV+0yotbo=Gs~HU z9Pe@NMfqntvk~%s=YCv$zJBuL)ZiWQ7nUV}{rX=9z`zY0{kVY*HdWH-1 z49TPVQEICHs{cx9`Z4_&{6NxhpViMH%tHMcu??Oc3d9`GSh+W2luOBja>N#;q9{w*@Y~ACd=PGk}X91j?KPn+bG`K9U}S&196E!6>;C zqvXzvlDjZU?#d{+8>8egjFP)EO6~!a`~>dmN!TQMik^diArNyCy$G90i)b-5r@sR? zX971b0ml6&Y$m-zufhL1kaQ-H^ir^lH-V>HGM-Kco_>eg(7Uj$!S+|c{{d`kpzIG( z!jE8E18bXg)mOo$197j0{A1wkbovxF9ccSANY>GMp!p5389?5f@Lo5=W>OYyhyQb+ z@JyiaUAVdjHUn6EFYf(I+6R`iAGo{|eFa>eM&AIJcLpv$NaxWZ*e-OKzK8z@AoVVc z)Vl$x|3c@}udrR|BvAYL!YpdjLr7rv9>Nk9WiW!Tn-9UqimIY2P;xb3_gKd6aiXS( zgCB?;uEFbZjMrlsug5W7k7c}Gi}AY6cwI4G@5^|-KjZa*jMp{e_1eJeb;&2{iF(Ml zzDR+;X)c17qPb{}lv{{2@Q0S775r_0>8kEzmMn(e}8cS+`(c9+zZ7}gdYYb5DO-75z-kh zh9jMe#ij6%0xO6ED;R^TW5Eq#!40m&on9rz0sW5$PpBoX22ZfXwc=U|f-OwMyN`u!t~N#56pIJHRDsGna^EE>TU)5HsMvOFRke{uDSv z8gqt`%o#3Z&M=ZW!==m_E@RFxiaEn*<_woIXSj?x!zktqqnR^|WX^CAbB2qVGYn_W zFoHS5Ma&s4X3j92Il~C%3`3bS3}enPlsUsN<_trbGYpe8WDV*l<76D2BNJo-`~ew& zUr7ajPzK?5rAu{WqD-V1Sx44^+gLV++fue9SEkE!_&dsu@DG>6=@NN`yaMi6IhOo# zoE!&tqMQhKvYZU}G5HuZH0$J2W4S;sfcvz3no{Jm@>v=zUz9IWefdxMPq?qhS8(-B z`6j}@E#F3%kK{+x%&d?LcY_3~kelQtN|jkMi-yR3avvnw@+)d;R>h@M3x*n)^>C?? zRn4jfx4KoGnpichnl#7?SOKbMg{?5$+E#5`ZD=(_c*Bk^w>nxKsg5<-noK>dKUsIe zonieM?iiACepNMaJaC+91Wp7_K=M=ICyERF491lJ#w97vwroHAF<@VDU|&_h z#HxaW1;N3p!yjwcgg*{^Oo5NZleQDU%EHXb`Y!@ClhQ}tpt70+zS zVK&u^*_6d>D#D}UnLEYXFW4_2O~ajfF<**jrsOaqN@PaVi@8udbD^FndW-SEwr>F&w5nbC~nh@73=Sa)O$GySrLljXSzVO+=XM)OEo3*Q@Ih_Xc$X zBsZy>;2O4*!EC2Hvz;VnJ9X7`H67(KoF|hxPYX3$&4%AFpEPDZt(f_wgZVr{t<_)D zUy#nDU_oubf}VnWfqDj#zkvm{0}EP&5-tW8>HseE56E9uFC*j<@S+UxqE}G1f2n_i zoxTc=)QLG#XKPJY9tK;BHhAGtsQ~DJlJ>^j>Ab@}y;Y&Wp2kvB8 zQv$Olm5((^u%Li0r z#X5DIy2!J!>MAysRp7v7l2vCI)k0T@Ed*=%ls)drT z*j?|gr~2-8cRMw754Z<$^+)$OoAyD ziU#x0t#m86t#un*P1o(f(>m&N5T=tp7m{we8{9#95Y^K|^-yYL*3qUWdW0T9gY+f( z63EBuvD83M(l_JkH2o*Y@6vZcGE>ilWR{+V@PF2ShQzSPOnpCiW2SycgJpm{J_6UU z$1Y%uPr+TFp9T;78+c_aOg3e+YM#Mvn~k_$hVPpXtxQ zJU8eK)KhQQ+acet_rpD=k0H(D`Z#sfC-e#Grhn2uQ4eO4U6@JM4V@o4pE8*@c4FSx znHgheW{h>2F=jGjObcBSx&;1_V2tU^7&DkLb_iV_x*Yzop|RAJdE*%7jXgr+LgOeY zG(I!|u_lHlQk&2pLVutx;Z5N!)Hu8?ybbP`;cRLa{yO|M++*P%sZsbu_yjc~8Ok#2 zcPCD$cMVueW&C@qV#cNDKPt-et-t7O%*afheDisxcynNH%2^>h*JMx)@2I!Kn|bQ> z`HJLmxX7HNRBtnXf8LXu^MyPIziae3Uh@Laq7YoAW zo8{$=I^BHAZ%%v0phByCEIMl_G6Ohg#-q%snWdQ1Zy8c^=S0cq;huIH`AnH-)1{)_ zo$1x%x3tBaaY^=gspNWF%$qB=tw=4{hH(GL_Z*$kS61Vi=bY0sQ*PYI%JxO()1O}2 zMNLk8ezK?e-_VlQfh)^flF?B0ObNV$x&M(%^`=D2vealok^j!si>B`uQ=iIE!y@Zp z<}c~l(KpnaDL8Wex#mJ+mUwy3&n@&@xp^PV|7N*fonof4x4P7;Mf$-KObzJF(%h$Q z&JLdHX?q(>4pFopJe4V5ZxO|@xp|b~|0fqle^a?VU+XR8wZ|gbf6&Yg$Gy+@M&P&b zY;kYA+baEqz4Wvj(S>tNa2XFTQ8 zj#Xm0%GNLN8!eDRahSgFijt-6SW~8djF|VD_oQSczv+^4Ek&;jw#cWqZV|WWE6UsE zq9rjJW&USzT3Di_{|lEn*GP{cxy>$RWx8^tUB-5-q?YVd|KBdxbFLTZ*H5uhL~%^7 zLfns^^AgH=&Xw9LpXxd1tQ3h>y4aB3uHv6Xh3j(!*UDTpRJM2(tIuWL3#_sY-mbxV zW__(vQERmKGjD?XzUV3)b?6j*VVTNs*49}&lkIfb+l|vqw@`fs7SFj->35UYk5sHa zm)TUp$W-aLxWrgX%AKJpb7%0u&E99;!suB}s=Q8JDI-Ctz~0jg%80mB=6~%P&g<3i za#+}w9L~jzc&2w_Nq7lii~R@F$+C=#6?i+R*+!Mrc2laPd?0zkQ0xD)g`H$}knE>E+9BespB-Z08$oyQ29@x|ZkTkgOx?k}&&Ulf|=g ze6pErMt<2`HjmEGsUq9T45}*I%T5$4yUXsBAp6VyR7(z&11TVf%ZtgDm&i-Vk(bHK zC@8OxW671{_(NlucJC|TYjZ=pK!HaUgr$~)u?YAEN(xs)R3 z$$8Yw?1fFKJjbWGTp*vJ7V=s79JMk#VpAKlBQ~YWm*va!JNc@7l``d0`4+X8|CRrx zbL29)j5^5=dLczx|w~ksk_`LH_`brOKzc_W_N7rZFa||zH*P; zLjz>C{Dua~gYqa1kw43yX}CNoJ-XQJj!l>FY@m^5cWk=Us&2*7D66IwN2ARi*)+!N zkxgSQZD|^3HLx1dc&mxkgsyhBJKO0R=L=^iU2Aqart8g4$MgrY(=pv(b~>gT%}&QO z$vGaZL4OPegGn?y*dzFRdN_Dv@LqZ&I4`(})&<`TE~l@88(l%)nO%=XUAK!nL8O@d zj>Qo7QFnnDY4$r7SDO8f#W=Iyu^4amI~LcQ{f@;AX1`-`v)S)h{L$=pET)+Kj>UAd z-?5lu_B$4HbyXcN9yU82izjqYCy6J`9>-##Zls%wMP`p<@rv2wSiE6&Hx}=h-HpXE z^Ho-?HoF^(kInAJ;#0G`u~=vJH5Qx9zQ!UeyeYg{>@(jRMRxdT_^3EwzBh_*Q6o2U zjg&k?Ov<{bmp-nSRk>bDu9sE0UdD30tbuyj7IoCjACoczb<~GC+8$v#$d0&rjywlf zJE7LL;98r`wKkP&Z9AT6mLV@j-EF~jH=XNlD$hO3;JIgl=bi~U9`)GbdR&F;vEX`K zh3j#3uE)($k8h)D@^*Q9be5WsQ&6)l)a*MD@=iG&VP>F~`%%m1KnLcct|xF^uf=uU z=DP0ke6~2A&lZx;px(FQS#2%lbEy5bQTtzjYi77P(1FEp{|-%XSrZai6KX*dmLjz` zp$`u0Lo3#YI;;PB-Dm{e*hr0eUR(;#i)$mdKu?-L zPqxGT9NJP3+Oh-gPUuV{)|t9IYp%Zh8k*A-nsX3o9+HQl)kowJ*0)5~x2n*$FW~Ng*41FGOJ%J~Wv#2iT2}>H_buFm(7ooYdy;j}2i@yWRf7Y9 z11L5)FgT2A1TTU%#;`WFU~No?Hoi}(!DZ0Nc06|?BX|IsDU4>4W%eVdDsC^gH&r*g zl2dc{B6kAMte8yF=qklPSKp!9?z`^0m1ZeDXvfUN#3QBT6a96^=%3XyptKHSO zy2f2YaXbej#B(rOy6fGIxVpvN0(Yys72%=V@b7W=QZ4sOcOSX#es@3Q*={z{Jm4OJ zT0?gHPh8~O=_UybR0F&@j4z?(RNT{p6Ss>yE+89neS0Y*Vc(t-|U-?xM))# zZ=@ST-bANB-c&b3`l&h;0&;?yvh(vL2uZQ9uvYgAsCw9)hc8CW+>mBx(8* zeJSLZ>&xMe(PIz`?HByl>FeOXUSE$CCh1!!sQ;*MqeOkXz8zO5>&ZxGik<@hR6P}b zGyfz_-=XiIFxorlQFwEBGr4H>zJz~YIGY-owvVcz?K?(^;qU(+dtc)hS8?w>GYbR} z3E3C+KoBV+CNUyniZs$l5hEfZ#guYWL`pG@h%q9U_@0IcMkfH#6V) z&iI`n<#UvT2wq2(zT2qM_a3VB-A0wZU!_Xl2dUCGk1BohsnYkWRO$O5Rr=;prEfk} z`fj62-}|W2_kODM&7n%)T&ncFk1Bodr%K-(s`SmJO5d$i>3c6#`fjC4-+QUjcPmx; z-b+=yxBL%`R?&Ig-=On4ip#MSm!toJ%c`z7mZESbDI!IH1u7P2lgG$ofEp?yXHZ1m zPM#)DqlYLiKR|Iglk6tD!M}>m*`$Wlfd6MmE#Qud)ET6X)PsK&uOIjeUOz{k1I+zf z)NUk=f5Gk;(nMZ_=PwcbC9)r$RUD5YuK7=&Q|!K*Vs{o9B114@si=J$)g|9Xb;(+)OI}ZP$$E;^cawJj zqf;nGr&5gGLVgYCoI=i!Gl(I-Av5r-B6TV`OU}Zxs$fo~czrv?>okhjn<-wWkn`j` zj131hwlt@+437RNPG(4-=y+N}Muse||q&H9m*HHvFPz1k=B6y-^y=Fa%(^=+|+PYj=+48Zzj6l1t-xD0m{AOkaMbSx(Nr2ZB@iUWLNg}#~;(m70KPUY&N>4hH^fUC8q|u~N zc>a0PI6SN4K?WTUwkN%lG>aahqrwAJQKyay5C3IUh@qo`hK>q%!l>{HilTZuLiKj) z*bszq;>X4x!(AN@lBfbtPZfAtivD_v{#jIkcbjn*#(-NX`X^B}-g@I7jDLVHD()vy zWuBHI{@oPuZ>9L2MOAw1sY*{vReE}=(pwKhss^xZ9kUMZD$1+)ek(lR1}L8lD4z(= zs**32qWtX?FZY2fn?<_)t+01?L zUDf|>r24;9iuGyCgUo~AGas-%g`)gsss&7CzQKG0Vk=^b;Jc~|Ol69hVn}xAC{=48=)e&x>=&z&be;3se#xpfA2E;M7OfC3P zM}kB;5^Q3=$9xZ-Rh3}^^Bl7Wp1%)cLmbn@G{LhvLTqB1nOERh)gW%5BZPsD5OFX< z`~beIqrzQOm6*V=3=3aWZQ=&1P1I3sVgl9VX{jbpOEr0^RFjuVHF+DTCU3*W@{Q#v z5#gw*sA+^@#;;X3_4ou*EvAZPVyieO?Se~?PK(>5WjRI86}QQLc~Bk}SL6vXLo5_4 z#a1azDv_GO*EsknmMg?;YN?hX2bqru7GLMSSf5y}ougc?JwAt`hmuEo$= zI3}zOZwcpw=0XR+UwSYX!t8@ES3_~3)KFolGPE;vFm&WgpP|-ZOK2`!q)aK(%8W9n z%qxq^l5$NFl(hf~#4AGqt(YV)D2o9`O+UOR+)UG72zNsI*VqkL838DFz$#r-j!S*wHb}V?(iXzYL4Wv4@Ngs{k`mbwY6Kg6nQlcmCNdhC z49!K(L}nwGB2|&gk*ksG!HT}PzQpjwNHD^N)>M1JFMA{Xq4~&gNQz7Z^3<9L6evdm zIf0l!W*|*GA|^>ik|HjK!XaMHhw>Z%*CTBa8>9g#Mmi@sq?7ViIbR-y+Bzss$wcJU zSj24aj-PR2kV1X z!Iq#oXbJX*hJu4&+cUx0P*Nx{v^}(+*6gv+bm&?*Pqi+%%y3#L6?_c`N5cg{SEwQ6 z37!f`K{j|ZG!r}=;zMKbt3Q|(tO=Ec2I70HSe2N!~uBC}yfFc)kNZ6SO*d;xmQTOw2Nm5d!a%H=`SFV>BiIFeKm&Ki8hJ0PVsYEGp;&G`!$yT;0h00FoFV)hj zbW}PdjYubygMbEtIHv4ZT9pGzk91USQ9Ozy9#@9sS+LMCg;yGsG3B^ymg=NV2(=0% zC`+!A&xlLnHD!~OC!JMz*(EMg1UgO8$1e|4)Y%5ebP!4xhT7jIP6v*LvH`cS?je}@ z57ND(2-(14da4ScyGAw0i~3L$8bYrD%DoOwkH)|m=&s8oy2sLpJ^;s{3*eIJDJCiC zQ*ay6e}GFxpMkp_eGV>-?s&Wd$KX3qI^K-8pzSyZ=b!?*xA5!uA^Z^9LH7+lOm_(u z;-~Oas0e=(SE5Jov-nx`C_N)&C*F(qqQ`I(ZbBuv6}O_taSyhlQv5o89X(08ZaFC= zh3F~Dcq=I5{U$Kp5>!b_$rET7@ZMdhihx74l<__b47LN+(Yd+_c;K&5JIu`IkOdgs zB65(6z}947Yo8*8&Z~ozp@qqRkyR8S*EI}_Ui5}&9Xb=86m3NB$E?Jxpx@pa ze``FN)&5rdTl9hUL+yuXPKR|E{Z4m_?iO@Tr`H+K?{z7<+tG(~Z_*#=uA~M1CjDmg z5j`p4V||YPe)Ng{0sVvMQ+ocvWx9{(GkvMP6s_o=(LaO!7v1@Dh3@(JqrOr90=lYy zQQwTN>09(|Xiaa{o6$}E8~QiUpY`wS&tjziK>r0M>i!dakGlT^Z&mk};Ct2mBsg2$ zNrLZF_mJQmL$V>er$GL{hhI{Y>>TVF6XL!Ky2!7B|ZK%d2hE_u>e%#+0Y3=Ha4+yQ(k8C%sEFZKZ!x)X~q%e@ z&N=ZJm~95}Z%n&n#|HvF-UdLCNVP+U#XWn2&@P9L-%rEetn75d> z@SmBtnMs0}Ddrs#rS5tsG0Y4zL)I~K%qodxt}}lko0%_?HRN7(e*)Q-{8;i+k^FS>x5>lm4g^x1T$@}^9!+jY-b+f88nXN#5jE zzFm?tMgrP_{mamA6SLkls$w1eV+!mA zMT%8Uis!(V{V+0M-Dcgr^#5Tz24lnR@N_rY1gM`4E*0f~yB&QETpD^9+#RR{J&w}R zli)JYQ>X&niJk#>7d;Cs3+(}SH);g83AKRRj4Y@JZNW$I5p*v}B@d!(@-?y@mD2U( zGU6ox!1xe33iv%t-at)cl#HVNWQ>fVmtju-7HTGMlS%Yyv?f}E+N0M+uS4eOTcU44 z9nrT&---@I-xhrv`a$&i==G>G+7N9(UD1z3KY}dUf71RF>ejxleI51CowL05j+-k|6EjOxa8M;DA!bV4&SZf~yPQY{XVl6)|lnIUE zG&k=Z6qh6|@I$R*%C`j=bBVMIcu*amDeZB@@ml+!qZ)YekWeO0ar4ey4&X{s8n>wW z6qg*;Qi0U0#_g@=u8F*~haZ=;;CGrwH0A+_H&M#*pOK2Vux}ao%!o8DnSgr>k^;;I z<_{3MQLN>bq;rtYnCOv~Rp#X#RAW-Z+6Sc~;6C+Iox>xy$YyXBndR1;IlzFXq_c89 zpUEw%hFetgFBLhqgPQ`oWQ&s2#Eppu#5r+ZToJF?XCVdk zeM~$ic}JXm5K=B;&0?)o!XuFv8tr=>Q+y^rZog^2Ddjk(fUV6)d!TNP zh&^DlHDIU5)tspg82c8fQ(TiyNLG86bW&R8)}$P;)h?Kgcz{!mgRMqj#*z-KeyeIJ zh&dBDX{nSaw>S;}Ys?bL9E;%Z2KZ`m%<)>VYBuC{iCdEQNp;+sBTBkxzX=>yZ@0+B zqDLkUnDru}y9avz-~0amQ++?CVcneC_wh0GJVKU>f4~0kzDTuqP0)5X^^^)HoU=Vu zwljjzdrjEw%k9bS$@jT@L7~|<2!3J&t&r(x?sf?0da8s~@Ug>J+LJ3>u+931xIX`J z|Ga-qjImFNd18rJF75`VVdcj83T~X;1{`gi%M)Y76Tnag*h&aB{QeA^OA!2dCjP z^i=h@p!8a64wN|P+vn$dvV8t-!SAt5`z3$apV5undBSd6u`Sp=V0H8qL&`_|LsBeX z@1K{_?SUN?xRX8bJ>>lvV`i6b!J%@d4+PYKR-FlTULfT49tW)1 z-QyBhff0Ni5948k@CY737?0sGMDPTjKpOl@{7V#t-@)&oX#6gI7scTB@Ox+-ejmS& zV(|y~19S`iJ^nq4!wYx;-AY)(qIkj+9^IzN)?}lEsMn%iL+hh{67^G*7&Q_#f{anK zQRfj8H6JyP?uc55T14qlm!g)?ol&1feTK54uEAvYZtbhuSJ5_Yo3;&oRokv@M|s)~ zZ3lW#`vdI{P`#FM3F8)7nsh?qS`-=#9+qZnthX`i8DXSA&XlwYpmLh^|gohl+Lex_b1eu0hv;cIv*b`#yTi@C(B)P>JD9 z!<*=F!&`>8P^sZQg3DCeim&rc$O*L(0jNljymWQz@s=o{jfy%t7D(`?}rV*X{nk zZukG|x?N0TO!Hr7>#ydjmu9nL#xiTIw#+(_Q)_0OIhMoSp6+YTW@o3h!D4Y9>blXk zwKvvzw9Vxlcb?!g%?TZi@V&q?3cl*Pu2>tKj^0@KGT@$Zp6ov0JZDaEHhHwp8{G$8 z39fX{ZcnqR&@XU({;mghR-xjnG>8jtVJ!UYt%LD>@>Bq z7S|-(-!ax3+jgm^zg9+T^`?S|LSHM04xny!)dw5}`O zpsU5!$KEs*I+6D<-`t+lZuJhku6jpZH@y>T?k(5N!`|Ya3qq~6$1yK#vh}ICHzh&N zrY!Ysdb6c##F_zl?-ZsTGc4P6!<}TBa&I#4aBu6`;@%Fq%lR^I)@paFoA2Gx+hPj4 zrQUtLE$z+ju=#p>rZdkyw4d`{ z@?N&}2~k3vsnF~440Inbr+D@5F()Hz60%K++=2FTVViRozeU(C6mmn>vGzmV2fa6i zBPt^n4J{n9dp8d+a4%UtvAbuEcK>Tv&F}7$An{^ zX5SWHPV2ZY&sWga?<;YUZcke=YwoCZ%y&mw<9zXLEf6Nn*~HeEE7~r(u6xh4nZ1LS zGu~O(f;-A}$sOlCWwzM1bPSn>oJ{M8HOY38o$XpRZR$<;PIe!+OxQ9V^Ui_xL(qCH zY=UE{Yt`D&!9yF)Y~QWctALER-jyYBw*DU?e?yVii zoN4r}%30#;7E9=5(+ z@SOJQ*;!AH%VHg}47*r}4|+rUxvpjRO!szIisy!V#W~{1vvsnimKtHKE!SM(o#c@z zQK)3k^e%9fLbb`G_6gwtv?qyga+a9Sdv2IpxN1|X?V{~s`_Xp694nML*1ESln*`ok zsIFc))`SLUxe#WHoiXkP>oL<1^r7ourSo2wWz?4Gp6b2q9B^4WYE5Hpd)<|gc4J$v zwGwP~+Pz}yb6&75yVsmooU5Kp==Ei;ea->T9&?Mg#<671@7Qlc9%QXn>!tmWYr=KP zd)Pav_CjyJJJFlv&1%b4udWg3)2$snrfOjZEEjYQI?uUk*u%nc=zIOH!>(ELb<2c1 z)tznW@80BEVEf&*mQixtCn7wjehLs8Q%1+^Q{>GzCAt>@a}qpxmQm=VS3M;z3(I;doJ~*zJ3Q7lm&f5zxND|D z6ZCLzy?1YWIUn!2=vj6Z+m^j8UJILI_N(aXWdS4dthKg2&lTrcTVK}|Po2l)IST!% zi0|Z^JZYXSu6ma3>GK?7&wEx~dd~>k5BRM9zabL!X4C|XGQR`HsP11$0N!^C+5|3{ zuIQ!EwY-h!ad4@0E$?=^mX}7?^3v&A-dE@Cp0rzvqi_N$!->E?zm2y5 z`}{uToqH+cd>%gmjI)t4&KD@-Y@&>FA7z{`QLeck_X5j&86N?b`3hZCe3h;$`bYt= zOc7Y-6Tk?cBo%0YR05|QqMY(|;w66c4v|Ozy-U{|-=k}eAJa9*Pw8so5;;LmqW>VL zfOlR6-nocYRmO?qRmO>LBg)L`zoVZ1iEDI?x)%J5 zu2t88_v(J2>&5$YHk}W@s;+qA9(An~_o{21*ru*^VqX2*1=y~e)V+%xy7zQvuwPy4 z#FE~qPsXx-qdpY}^=bOMa2OcvRy?53*5}~Y^tt+N_^>`tza77>GFp60WwiJW_2f!C zqB2_iGku-D4v(s5P2!)c=S$)-{mc4h{HFSsiSS$cc6}#)TW`@@@H={|-ilA^VY-Tc zrE*++Ny3R|=j3PpVb5A=z#G!1z1z z9aV8oYK<3+7s#{51>*v#Q&)t^chwbPQm?KElkcf3!lc1?-S{W+obd|=ljnh9$B=!h zs*p6Rs&Vp)suw41%m!uyF)@0^K-yI$A?aX}nT_NClgVU~E|q^1i^{)Ax5~dskIKJE zFH^ugOsvc|m`4e(?nEJORqsc9OgZy3@v9m?5@f2Ge;^@M-A5wKGt9H3Pu2C2AE_Lj z98@_v`LW8;$$-ky$v-j{rk4ydHpWg4F-}G#uQM_eBBQ|9`^cM2Kl3B<7Bj%SM&4$A z!W<#*08<|&?=rstcK#l)^J(%s<}|SL3zVHNFlU)L@)2{6`G{O%J_dGv1=x9+W80Pz|Oy|xm`U2UX!Vw;-|T5W7)=XnD=4iLegLP_w&c-bsPP=NLv6C^8n3?Q5mX4 zyHOq5i}oR6YwXTP8e4ZAY5t zt8CkB`{B2(wjlhLWvjQb@Rnw4f;Yyt3;z8pLSuR!H9|OQCTfA^E=@fh@Ok~;UZna! zqOH|@!Zv2RW{YcMYz=Ht>)K28Y+c8sZJHf1_1mV~R>4oB>Z^loV_w?Y#<20=qnYjO zm^3TgDegRXm5<^x_(Fas-@qT>J-oypx$ zvk)c@!Zd<^9(*6+hxjFa?Mt5!GR~f67r3n6)faZZddh0)Eiz4TkTRFsn$*3+RMoM- z?O;!~CvnAU`t}j~IGFWaviNMz4-Ubn@W zPjL0NA#UFb%dZ^dSoR{soeuTU$_?74P5oR&8)I=;9G%15MDLAuo||lA%meNGOZA+E z^K);*4$DHpA`$do;DJvPYY$Y{zW#>`Cqt2YKXfa@V1L z61mI#CjJ_q4Ru?|SMxopb@?e;v&gRf-|T&TR2$u$-pn9VmJpW3GLgM3VZE$F2q7$` zgb+e7A%tM8*DnSk%eIW9k&Ikw&$eh^*tkTHFHPz1tX9j$!}F6sga4uW~3VQLrTQgsso>{wZ?+8S3#d0M*2bh%`F?MMF0zE zZ_NT0;%e<`?QI=s9g5(s#gWm-C>XI^(33sDTF$|@lc0W`p`Or!>$!l0V?*)bG~a0p z7H;5X!vo>o(1KXyOL3*EDVP-5X*mQf00$WT($2B!@7ysM_KU{254!m3q( z*1FnWwT%Ev)wLbAJ#Bj$VcM$NtZlbIi#sFJEw4eJ=L1_v1nD)f+qOTBZ@bo(-PYLF z+}7PT9LZ@b37O@g5dOH0N#avNgU$(eiR*c2I@C`op-n{yCxlKZgOZ zEmvC7I@XZ)!9e&Ky}uU zhrrC#6B^_vTn2hJ*x8aESx}={X!bQfMG(z@(Hz3q7Vj&Y?<kxfQ${|lJ(^55F{rI>^vd6l! zn|ADH-agy7IO1OyXPIhogYZi6Qj&GZk?!szPyK0?i}X42EK5(e z6~R;Hx+14ZoxUDVhO5vzscgsyZX~Azjo_*!3UQ7q&%AL;He0L|HuX626oaQsPIkVL zb)ZbY)9GvX6f3Vircjcl#+x3xYQv-+Il&YHVds9j{K<|YI@O{Y~ zZm@1yD(oh_TJ0a}n70b_&$um2o<7d-WqOv?-mxB#r}&y>8b584$b8qh{j6z%7z3ZC z$N{_J+6XQ&X5v_GBYh#KXUm=-cZ5jWIUb?YJ&R-{#DGt_tdrhn$_CM(WP7`vIiZN` zl6>+2QKq(u?6GHsb)=chC(b+x)~>KlZIQKuEV9n{1}*LQzO|R=^_II&ET^EBGj_#$ zPoBej`~%hj_c#~F8R%PTjnz65%T1DXw`l^5-z#>vX{Tx5-Atx{QoG6(cGptmZgz() z73$c_yYA+;@Q+cMO zJ4dZUmT7C7=h)idJ;Xah``&ZQep8Bat*M8cR!1I;(U~JXw26=6`+;VB-?M1zCQROL zu(spr?#}P7ljx^iz@v5M&Oh+eggUKbidE+1A#ar&wGD?POTL^& zo;uPkPSR(|QHGgRyx*huEPA%wc@h&!2@hG0q*BX~Z8+>8&Xm5#1>%%_#{1gRPAoIk zKC|~3StQjE1>&AHOFZ;|aaO8rW6$Vjd!_v8#^L`FOg<(YK) zq+IFtmEtvzv;2s)%aI3sGr<#Z;!0j9wW-J25vs68@fuqnx$i!qz{pz1$O&hTb;~nn zKYJp0DhX3K*Rmfj2v(?;>;wKq7KJ%7Gu&&hRV_Ij^(^BHWIM3-9_s+G`{Y1&*d%3! zU4e9b0dP5x_YcuTSn&ixo8V59;k3|cIDy2&V;-*^us+LDI3rve?jX)A6@e1m=u3S( z7A_7nlkJ|Z$H(C=@~S&8JVQ>{6T)S{3u~ko-YVjZG~4C@IEz)D`BFWICl68=-bfeyCm9mdD5`aZS%^!JI*rg8w~ow z%gknY3ny{#56n1MeMfi%?+KrUqm~LV+b)HXkQuaUnQjgrdlEvM!T#_;=tT?Yi!cIe z)&p!|F}%_e=PmbEQ3aL>JSo`ksr9bf=7HVp;-ii{SD{k!_?YY@`$;e}C?iVPnL{)H z>s$uonEu!$7mKsjHf3Cy2i{QV)ygi9f*V8e$~ma>hP6X!mflEfA&s;t?Syhjr$-+= z3YCUxltz1*ZwFX=xjz>4sn4@$sgbi}y*Exa$+7A=&w(^S-?9&hdvb$kMtbQrxD#cs zw?wLzsx1{vK1oWQptbuUGZ`UYD9=N806#60 z9l%Z#WRurG97{dY3u#(;D(6ZIo(yGH*;7tHOZSyqq5M#(x7m>=4JwAmD~gruk&~4w z>B!opG~;X51M3XnAW7&Nx`rU=578eYsAfd-7l65pYbKFc&9r6#`G96g^BqK|`M&1+ zNUG)snja#+r}>d)3rW}fM6-un1srD|xt83L+=6`M;%6?FAek5IFV-Vj7abQJ$j2@| zy7&mm2H(;9545wk7s-OW=?j20eFKzMc~dpi$&f=SkVAzb zhiZWwDgrrFE96jZkVE}3)a~1$Zr=fQ`xjU-;73u&kG>7}1pf%?^Lwg3k7BAmkH)Hd zg3*7Zs`O|a_V2K7qaRT9c~l2@(_f=GP?^ue_OLzlA47dUANyGhf__qEOz7vKCVv-d z@()zTgf>8B-l8%l6o<-u;|27>htNk*fft|xFRJ_qm7oqULmgg$I(!J~@L{OKw?G{} za`EEDOK9uG4_!<{pQ<~I(N3thPpLbM(SHk7_i3oQ&p_4vTj~yD^rgDP82u0G4r6p4 zs_x&_en?w{zJZGRDOA+|R%KG?8PwJP4(jUX+6P(-`rlMOg<;x9S`Ld*RdnpKs-k08 zR23cjU2Uhf6H8TD7501DrAru=rgA9kZIwe|HL7Ng-BvYotWIT5m{Da<*d3KUVfR$_ zgxy!!6GlMg+zr^%yBG;o@jr#C_?J{>gnb?A;ZdlEe*@~_BTx_jCe*`6p&tGhP!Au2 zdic9g4!uYCJAsWOU&=6tSaVHDyxe5tjellN>x@Bb6sUsF=Z;N zius(5yBrf!4!F--F$;k8cw@c?RpP%vP_z*3{Uw*V{q#5P__c~Q(M?q$F~qaFGVqQO zZFNQ9RiX6YGmey~VvzGaP6avAa764NSmQiZ0&-TvHh9Mf6O{#WwRIXw2XfW6F_5b; zED$~akLJ-|sO1;-FP!}H8MwE%|JMWiF)al<9ybSv=!r*1j4rrqu5{*7aa20vx*w10 znHg}buv#|#zJ_gP!|XG*8!w51Thxe}CdF%8r;s)RD&2-C%kvD)DB z{dhK;ExfOJc;H+QcJV{}48Owf3UNZAP%apRX5p#OEj$!(JVicyX9edT%MA=a=NS)Yt%lK zwxnbEnoP)r!ijwA(Ghh{r_edv0>^Njw@8=7u*ERxH=imRVti| zB%3Z8g;kI?%$4`LgI#KB(!I0G^gcQQ)=|bRQ;Ad|HSAn(SP1q$C}-0titS@ZZf`KL z>{M{f>B2kc1qSp+aEyH(j8Y@ORwk(IN11FM_0*C^zhJd^E>l2HQy60cmbK1yJ6EW2 zrkHw7tx_A*cChQ6-6mj#td_b(N7!q@W9kZCz*5dMCYh;ZU-OInvN+9esaC-s^Jjuq z)CgCgMO6ucKmm(-1?=fWOcFE2YH?H@KaeDm5&Oj#Lbi}6P6!yVq&+T0I0SzYVvdk5 zY>O$t0^;~6zb=f3xNwV~6Mg)ckSIJ8i^K|GiwokC_y&|SfL@*wX2q*wsc=u|12(!P zln9N%UT0cx$P#Tb(M$BEwcLHgWChO{eN!7%Zo}`#GYYf9U2UX*Po%R3SEp-<6|5Wg zHO5?Sl$+)vbcJvN+O#DVOJ!1;xFaP?l~SyvmumTyCY`t@9!Uk<5#M${US5@7bDQ)& zk5JpRlTYB&Sc2>1b5+~pD|tO{vaa%8-W%NI-f(aD*xQFpEwAHUd@@baJ$#pSgHg=O z@+#lJJAl{wxE*f)eg&Vw7xQKGW@Dl>AU~HAC5IT16sb*$N|VxzG$-5>eR7<12JNt1 zu9EM`c`_vna-WRJn9wYZNz2l%a8GWQV);pV1oZTY)F8Qly;|j2d0y&}x}5gLCYyM>$PVw(3bIWu+RaSJeE&=))+x`k;jFXKL%p?mcI> z**ylSEyZiO_(w-v65ilkuf56)F#}ALndEAiHl~A1;WD`#oO`&=-eav)Ggr!0+@m-n zHO0=>_jBg^MlO}Z>%44ZaKI7`E;3#wz$`Mm%rSd~En&x*3?`QnI1N+F%yC655$p&K z0bfg_v_GGN!L?xi4Q_$i_nA8LHt8)i*kM_y23C`|m*A|39lJ7WpYau^4i2L31G%IfTCf{8#uJz%l#{;Jpip7ZQ<^ z3rQEW$lt(U0?r}QMIh2eA-csNx{ZP8Rs+%P1@)HzH1^^ZfNtZo=>VaA5F*q}h)_QY z5o#7hs2_s}H5($-kE;k3%~cU9`bTh|#~0u}k4m`DQF~cgI0&)>Br>1~& z0(Ag#ly!w#2j|wPmmue&7O2f%@v&c^tT=647Od+P1Q=nZ@IQ{2Gy5sEN$pUl4>IX2 zLd1Z*0(rUdo*Yv&oQ#-?tQ*W5?%_aU7 z|C(=p@P;W7mL8@HI}Y7Lt?A6VWE{D@AXeHc9V?=#R&R;3cXA!3Xnlh?(-6hW#a*`Z z?yRF%c;j#g`}nLFTW=DJ#WKf=zVgngsEBRiq&Oq4hz@)fzsD4cuz$QWwfYB{)}@;a zy$JfpOTAHhry)u&vbYJU8^Pm@!z{^Uvp#E&zCgdi*615pGwWm{jxOgO+bP7`JF6Dh z9wEs-YRtABSVv8{!c|8vQEiAg285i3sE~j6mLn<@u~&sscF-}Vw#goUr|6;9(Q6#B z#NA%7Rhs717D-nuakYATr>GNW?486W+o@l-4cSla5o*&|$Sys|WZ%>k;s%?-l+zt* zjnz6*J9ylkOip34xh{P!H^fD`F>cZ@-H^u3aC7Ve*Wj43YpfA&k<)RxoP!Q<0eX?+ z?h1~mqte_c)R=M|%k~t<`VG_VB*(F%7Sz0h@58qrYWX~F#h56J8X^x)g#~l^!)%+& zc=9k0j9W6dZamR1!;wFDh?%0oL<3@Pf0%Bp5|-)%57V9VQsG0bR3auz27F2)M3dTD zwSB^lIKyT>me@{d)mCX4k+!8h>F`debRwNwrs^A{sd}WoK^zQ5LdDG^B68=5ec_m4+i!F*ujxg{q+^oKGDMiy&gYEQ z9-LOY=lnH3%-rL* znJNdzpEM0Kdwe55Z=ayoxify8-@9ALCpuU8Th7CqrFjXC>Od$prL*sKrSDaFr)9eh?etq&MijobBzm}XrPGiu9J zOBNeMhb2zz5PQWjeU@s;;-a{9v%fCe(JLm4$+ip;p_9d0x=jqwWA+y!2lEBANgM#@ zy<(SmAjS%7!V&QDWqsGp9O&B#Vx>4_ir$@-u)7B4M2Zu0>l`WjKRBQTeNHJ@yrM_&gWHYtF;eE=EUyf3442!!Qo||GS8Y;&3TSwM}ob` z6;;PxO{~$O6LM~Js7c6VM=f!nrD|)>?VYBx2i5gi^=Xfa4fC0EAjIg@3|GR3uVUanW4YqW9&T(LF6 zo^lCX23^bzaB0*Uz36-j{Ca|Y$#pQZPY8@_PG}ci3X*jKU$tvo zQG2@_FDJRijESZMhf@7ia5{D!XU09_xILatk?!3owRZv^&XdZeD#>ab$EPe>F-v+b zjod(_adGkP?A=**Q<|4HY|BQWHYyF@UXU84X6YHYYD0SJa2U76WJ{cs4bp42^G>O( z!3(8Zk|1>(BGPO4a9-4 z@ivD;sV}&bR6k@C%#DsVM~5xLJP+pA*t>b}Br=qpw9N9)?bFq->HCe(^fo-SfV}{l3EezQX+*p>QJ|z@vVF>Whbe zar|25QHl98keHfIVja9=UpOM(fY<%{8St4vEcgMt_`-&<4dlGU6j_U4<_Ui?5<^z| zll_2)eBq^M0-Q4t{k}s4tI^ljfLtZ<%(wf0oKb$kLSh0#Y@YU=+S;0QwaxAXwa@S5xO0hkBc-+(lf2ZA0QPby}^342U zFxKjbYTs(*?(I`UN?l&*Vby-s{!O1D#kXxW8Y&30-U~)a9Vd09%wUvG173fBpv+$p zNDE~7EBrZufPX(w>tFKs0CLhB81P2|L$JgPkWT%h{x#ogcdTDSM2IDS3iv$VU*zxi z5BjGAh@bIGz_wr_U)^aB9FrsD6nRLFle6SWFtIBCrte0y`e`t(GWzzJA<1y+@%gri zqTm(JggQcXd3AZW&Az8^cU&L5vEq5*c~N)XOd2NKlfLcigF!7(<1RB}?y?)v8_`a`4_Ci1i-@l7WR}_#^&yKMBUKEzl5f z1YCirp9$pp2i2Uv#$W9>1_}bjerG`ETMcvtI&QZI6#rF!sUHti{=H?ZHr)x@JK^sk zS78*mvWuUG3c#2C;UYmH3P>ME!XV`!5s*HCv?1-_-sdIaOSN2cCr^h~nYS0VFXJJIC>u`lFgDb2VMwDv6O0dr&7VJ899kF4b!#;=L*yphd z#EyLt`y%oXtH!F4Myw92LmpxEz$Be;Eq)Bw;%4|XSQ~sAtQ|(6`V;s}SO<(i_0O=E z*h}QkurCAA!9IZ)w?im z)em6Ys&yE*>W46H)qjFtk_=%sIQh8-!xE<`g9cPRU@OL@94gy>p`FBdUbuMU-vcL*HB9Lb=}ud zS{K!Q17&pI)QzH??p@uxsGytFO`)Q0S~ra(9X|*opN0axKi?|CQs{uHR&V_bOPAbID3?Cdg%yrQr3dHyeDG zO6Gg7fSlHw3v!9%H8Kw5V!?GWBtq)c>)j{bI3&h>NXC1PL2lQx269{OGtcR-%p1Ra znyKSL8;nj?0T4QpU-AC&e}ACQ3pWe*%Zkg8LQP@y=bjZDl(d)h7rrbXy;c2IV>w=^ zDaSvR`}t_;-sg93;g1^L5(;WRnO?X_j+Yg`HD0K>zE;xy)_D2oC$;&{%kip6dA@q( zTaDn{K6&WTc=EwI)AQfiJ0GYxwmQ#OS5vlVu}|7+IFhuvr!(o5vzzL_~}+VkX8cgNTvILej7$fA@Fq{oUI|7a5hVlRc0ei$|AF4K-Vu z*PAPwi~K>&5gGGh9x_S`pJ(vtvF67GLQZeS{0X<119L6OLs165e=zsav8vqI^3Frb zyqG+5erGkgCbuMGp!|lv)!*Uo_MdHf+N|(r`!5ndWjJGPzcypD_;F)x27e^Ttjdo) zCOa}*Q7Wb`rk9tU`>6cFA!T{z{?L;7LzRbW@>cUZkJKLDInnK(&Yd&Y<~=S*6?IWq zoFAONoR{stRv?&d%@O`tQZ7;=|APOP|4wr%x%>HJRsO*OA+3p&!R#-~2uW*d9wYau zDepPqCi=K2Zq22~)%&$gkIZ|UX38fI-w;cCeCE(lMrqT1vn{7jEOXAfsPlu`3QgMW z)T?5da~~b<&%biSmL6MZE`D4v(R8C}F@NUZLZ0FHNM3P)a6IF9PTuzMQ+bS8mG}5? z8>t(f_z!dG%S{i>d(%4$H%_>lmUD(qRAub6@k# zrn}|gN6sF(Shhs2$s)5yaz}E936R{A++*G*S(Yp_fm|||%)C9cCA5VJ3MEb$^HOMA zXd4q8+8)}@yd$(Dw1Wu=?F{W?-Wl2z+Qo#1c87K|VWB;tJxqAmyTUZgpM~keGMRUU z<%Z=lNu|cuveJeu)451=1|zHVXrcIVb(A!lOJXevoi%@ z&M+ra80HFdF-2jXFc0&x{LAt$GsW_+$iKoImVZ_LRi;G#HTl<=Bl54yzs{7(zajqy z@yuTMaxZ+j7rxw|q%S9#A-4G|A8DWR#r}V=|6ZThqxD{5IPWFz74KDs^%yK+S^8sPI<3-C&`i9J4lXf-bU|Pa#Z8BkfTa(pLgJ2 z^?35;y4*?5@#fo-G!Ds#-k6%B8?3ydZ_DD%4|IDyo1SOsS2Ee$oq|AbOzwy`CTHDi zDDTWj_2zktOGgSe^J?`*w@f$axvXzHX!dOC`!m@jA#uyA@@n@tl9=lSfq9LEjEDEy z+@qu}p8LXmYF{|16Sw!VFTfYi=_p~9w~?|8^-FdH1ySn zdj}g74VuF32E8|?A!Pq}p}8S5X=rBpiKAY}Hf1=ZxWTcjAy;Z2GL0^(Dr~aIK)z|4g>+|?ddAq%S z-z8s$?}AV6-SCb0CVex$Io}O(4XH^|mvZm6x5{(dD?2pcx#`fjSG{gQ?L6ykb)NOH zz5?&8cR@Eu>U2=2^*uTg)R61DTUhJc@h$uAH}DOWSw8J}R$pmSpPshesSj`HZ79u}XsBrr(wB31qTR<01Ei*gyzYj}#1cDvCB*isednA@-do-~WM6XI zgd|U?uiv-mi*;#)aPf%rI_EulA>7yI9rc~`Ieb^^7x%^bt`I$Sc!zyGV)@;p?qOon ztKL>$fcJsE0B`Zhe&M^l@ZDbcZhxA-8)+}SZ;pNMcK?1qL>Lf;7*4n>j0qFuXv8ij zoDt@TY{VuLu8^Z~;TAdS7S57SRu~d)lB1hKyKtKvT@psfQJZi|=waBC%R((VY7}h3 z#b5OkZ~kAznsnpM-#Ch06nFOT^lkbXVa6!v+sv(|n7FBZPmi{kWa%@;B3*6VigC*{ zy02QeTU3xxrP|2iQ#w@}CY8P|B}teuY=~EhSL}P5@-%Kmd2Zi1#A`zxmsMk<(~3R*M#f3YsIxrx*-+W&F<`qQrD&{ z#9ijDa@V@uU9E5>dseI=;jS<#Oqxbjp~{Ms zxuY%S1@l7O%F&kOn)s(~nLDqj09xI0ZSH$&Zq?7|iCw6sje_#Nxz*g7b(3gTw(lI# z>J^*2s550*cfP3Lz$uf;ypY|hoDt3$s!UAE6JkXx!nu^IFRz=#-(?+x-wxEr9@{I@kuIJRF~_rSbwfH*wbFOmRLrkSbwBU6YgTSjnuK= z65M`QrR$#SnX8>>w$e4^QWEKwJBFMU?IG25oy0#wq{I$gHN-~8h+WoJT(6jSce~G$ z`p9p@@kc_F+o|oTHsWpz57mmedvR077Gn#^(QN#T zXrBjL(`QnW;%=K4h{ZiKX_Z$~I*o$8N7zx$8P`)Tq+C%2rOcQb>sfazDc!1j(EXU$ zVQ_sou|&D6x4wW_R*7hbq-EB++UrjdT`OJl^%eCMz3qNppH!bwUtRBUW!4A8j~E5xmT^MgW_Vy6h%1eoH!6!d^C#7rMV)a| zMV-3rjLmGBir9>)Tb-G5Qg|Xf%-W0(AlpM>(cEF|HEs%bvzLsSD#QLe=GLNuES~gF z3X@8=*SsL~A1^VHboI;1YEfqeeZmFfrt+?7)U++0+h3cd$zF}Gj=OGZ6s{VJ;?E_w z#Lt*)h7H3;e3Eb_$CBKoysE2J-HIPk&g|PU74NH1-W49j-$-s(>kVb9P-BZ~Shz&m z^oq%^JW1@_pYk+*Mz|jztbAhcIdCcEt})m2T>Z>EY^sVMNq%OU-FG3Oim=~UY1o@N zmvTS8O*xa!5{<{C-$-v$_vwuWgPJ$&6V+?kYZ%lKPb)tmqO5EKylrib_E-Q4r?&1jqYvEp)~EUv@!)N)<^ z+_F+4le;bJmaXVvYmjxXRTbHF+@ie@8F9SmSX)9s+BHkBrOz^;Ra?d^^Ok$cdvdp` zK<*~78LRfV#cI~KYt`1Ex(T^o8*5al&Bi>tChBDJs&&xRsn4~ym|_*ZDH{8faY2`% z=}QdNG&zE_GmgE{!?s5@we3mbv{7Xb)b>~}rpza=D#nb(k((y4+x9n%4PFrs!Pe+Ex{fUjG_mW%X-NtJvN@s?8P%cwzO%+(tS< z$SF-~#p6S_Yyp~kQL*x3UD7Mh6DCuy%SUbDI*-Yd=2nELE+kxy@3X1(>o&F~P%TTV zvc=kx)V9B`zJF(=%}K5ffU^qRsQjgbS6j!3<>&CzWu zaGbSO=z7F59m-1_G%Y938m}n?ol!lQ@I)-pi6pJuL`tPDQ=1iA>elj=x~YU^lFPwp zn_@trsd#KV9et~|v$QF*_m$_i0QqQzOy^OLr@50?O#um4qZ=(-wj270)a$01sAcWk z(LCE?)MQF!fcD%N&4&7Ztvn=DiB@0X8iGwLQH1Lcj%F~u{r*)gin8?&<>q+YHZ zO72$lrL3j!ad$K|j#Wc&;&9@qvD@)H@lM^8lTE4AcG?z=7nNhShotm*aof%SLx$sy zzS6O6G^nc*^R%(a)6ol&A-evU;L>}}E1CgaQgmLzj9P2FM#@*MyCV0i&CX?QK>3QI z%W%W_$kb!>o6ad}6hlYP>T6O45@$8H6?&b~nGtm=tt_hF`7q(WR<3C>=4mDrmlYwl z632OmO%dX7AGqxpjGS^@l+W6#ZARNk(+!8euEshjUx*BhEUFvQp0-<}hf`A(y-_C> z>j}XJhU_bum5F9$r6NQfWPPj(w_Y^n?eC8s)Lu0i)sMC6w2OwML|OE({FY^cw3(>` zH|5VuWR}gSMbZXj+7fG!=2=2DX$gj$Va1f#V&YSEj`)_;rqnLETkeipv}}>6T`+Y{^V)7Kkb3qoLHs|ObAF> ztMfT?oF1b&>Y=kI`Htb-!3#%i3cco@wm*5)c`EUF@~XBX@{D>kP8+qX%c!ZW>oTe| zxrPdBxAm+&H+eL9)LyAsx3`;<6utKO)G2GN)n;E$4ALekgp})sYV~;ZHF3Ae>-rq6 z^6k1B`L-@6>Zw*v>fnsHE4FMs@>}G1+YWx^!$%D3;tOtrU%hs^~WvT57X_x1; zGq#cBn5r4uCBs$4gzhBidmOe?wz;%%+p_JxtyA7u+EOa0r%j#mTa~%W2~wjIx;f{L zw%;-CxOU8uvS#m!+fGxZOr$M2mg?Fa56HG*^lLM;;f8Rd!7=QZR&+TQbR%gS>Tdmt zO`Z6_a8h06d@Asc$GTIALAG#xYP7*I>$s&EtB5gFSf1%yqB@mhQL)ZTrX=T8gV7co zb?NAA+;(P!{$6sB^X>s()I(Am_YbV79~hEMH&Tb3i#lV392`Bl5kff`kI+liKjQ7U#@yjr_ds6tSC0ZAo31;<`FJvP+Yx&@1%HK)cUQ7&%}s zC1b?-lzHWt^_q1$I>NQFipKbwhDan_<755~#lyHKOs^C#-I(+kVs9X!YAS z&GM9~oS3K?hru2ZKUUYCn`?WjUz0a#lM+YM8m*)Drc~Z~$GW7Ot7}hNO1Yi36y0rI zwLZ|@wN|N%H4_O&?P6+RYGC3WM|SewsLrSh+9d6by32{h_8LcVqB&J*3s0O*9ChT` z2kd?JA*D}SqUcrhMsB8EOe>CDNgTI7+n;=Wbx<5n(00h-?gV!T7J>vl+?_!1;O_43 z5ZoPpcyQ-%2=4AKhuh&C`trV2-@ji~KQqtvPWM)A)l65->~kAT*W>U+c zs>B{I$=5lqji8j1AmFr`{TfIi3{OFR7GDSKr#M1n>hj%rm|~(*S13azsKd<`7hH*0`^VUu_3;;# zjbI&{VJyi6%{rx2(!W4(Qp}ecBr2&wRss4voBrv@Qn1B#@dQrX@Jwikos-K&@iX`) zCU;Mx0eOV<9=+>k$|N@4=lGr5d&{2W`7OYDGvZ-wSKIA@yH}x`R&nmaH_Pn-rhe(n z@6TcQrqe~-%6q)ed$R|NiM@F&>}o9>oO(i4ijP}-A!EHeteY`Eu}7{A!HBu_HpPlf zU_K9%U9br`dZh$%A=nuRAzBdyl#b|0#EOk@q5R3?bRi?5%|bh7jsJx|?1px{m>Bb0 z=0yFO{+34AO_M7r%L)m zcr?CQxKD*3uGGAoEYs%@aBF2$ih1dJ>Ebh@P)TjnGp=aWFJO;oWn@X0n^eVTr|Bl6 zjoP%71e2tVgqaqy6DR!9_%$*(;)aGRWNASJWJT%ain0~qlAE0@beFP-X(H zU({(4Hd-3JIJ1*$Upu*=JJ5>06KVo2y>9_O>j2Iyz$? zhR7XFcV9k6#ekamAdBkso2!f;_TM_17CtnKdsS7r1YYm{MedQ}OuPWrOS&ZaMt^^I z|0lEbsQmV?eTT&C3t5hu`!)JEYi?X2XT|N_!EYbEW&iH9t6sUk)2t_gH$wmg)SiJ7E%} zJ(}<3TjATg(@DSo7M4Go%nMFm3KcEB1M;=Qclh}TA75f0SQ)Nf>uv11j4wrV5~@kR z0zo}){ZRB4L_hND-wq@|)iEweF5-z$QQwRyh z^c$6K0eHO4p_ZH{TI5EFJ~BSS0OJZ^vBF<#w!eD7y<~gfdB;+A(mg9k3&vQRw&PCO ziM*lukiIZJ;y{Ru%qU)DAg0p0SZ|lxr9JP6dP6JnnC7kBpWaDaC?>gkL>)+=LU9gA zyFTdeTU=o)zfmG?X}{LMI!f2jTz47>RQGPQI!m7iZ$A&Tmff;q`N7&z?92B53b=%M z2re^#`HFxM$ZJ54SaC2()ztl&m1^Ss6R*>l?`N0bJgIDyuNxE*FSRv+t`xH-WoB=r zkB+suDvh@I?j}M(*Kdi0RO>K#LH?|(t>AE@bYK2QyWw{j&){ay%)Co#A3*C9D$R;C z;nNQ1f21$}g?McI#xn9lMGYglAAE=~UqSjEbzT1scwygdqE@Ay07 z5Kd|9>Cg%bK1T!0-92W0IHc>IzFD4mhKD5zr(r!&9+cJzF&=`VbM(8(y6veR90RsT zR|x5_7@|sWNGEDR-we6hv zsX%T@skJnC74J`m=d*E_iwVQ7zq4RHKkfeaen=&(DR>B%g^6IAP|$SEN=XviaHOW0 zEZ0Vm$XFZ73-1?QD}pNLDZRe^EOE_-c!@Q#S(+@3j_K~GJ9{(#y9Vjga* zdA7=RNdG&pibpJeR|rH5g8zsHfoVa!2AI(yYpsWD9C#0GNM!NmzT8R!r2!*C4Bult_-z}MUICktK((eI0mgx_Df z_s_U)eFbEbd)fpZpPT@XWDvC?3|%Y~EGV+{4}C!Hr>bo>FocTsnTx7U#ICRN4S8p5 z8%`NRuXn>gx&puF9UjVe8|-CLHuy$XDe=M|?@#ce(=Qa8SiroFc_j8$Lpg~-c?|bc zC3kgMSK0tjkKP~Rr&%s$kse(%^gWEM&x3K4FeEMgl5H1}_5gvG{#+y3AH|TgH#p*5 zXxmU=#zg~ zL#<>NLWuA6bQ~v!A%UvYE+-YqXSY4Za;3})rC@&~^gZoH2C^nr+u#=fMU$0uQJ%zO zl7XOCFdRe`_3HHzW5)08PJ{}I1rDN-IolU^isW~fd87e1MMVCLmdG1i?dN$R=;u+1 z=25wY&SK16p_s>Aj3?F->~PMIA9&mf<&2~CDks<=Om@*cIWbRAK6$3z-{h@o;dx_Z zH&NR!P)v+ivN3NU5cS|@-|g4{deIQm0>7}W5Xoi>puQ1HPlq??{`#DY(`h8i7X6kg zK&NtmB{wlQ;|5nO7}>rQbSd(J!A^jruOYc6^#6p|(a;PBua1 zgj`)r4%8HSmDYBzX#or<5q>X2RxSD>PeRp8qNpF$93p#L}H&JK0Lv4 z-aBLd7$e8M6n)W~(Mb)qf@D$Lj`06opfqjZVck~zV=}S(5@B1~6xKiv_Zshmp|OE) zd_8AS`BzC$gyxx%h2c@p3lIE}+pFIf*%V2FWl4-!BH|-eg@tN%WG%g0n*_0#yhmKN zcX+g8Hk=Lo7-h{ClxkEEu zFZPY$6f$$tSC?~TmhLkc9-QSpWLA)p?rpbT0KGhMMNceW8tTfaxqicEf|-w%-LiB8KVA6#FzXf7yxFD|h%k4P16tps|CM^ii`x zC=k_XmX~0H*b9*Bwa1Ux_YS6w@2LQPe*m*F?R?(?+iaUtzM~`NWlQF}T^CGJtk zz#PlN7*4{n9~o|jeg~@I`|&?3QE-kMDT)tTCro3b5F7j~C7Cd1e>C65%035lBg#p# zG(eJg`R4jjBZ^%^cyXifhfe-!{L`nJI>PZ7r}h{x_t1`0kS--E+T_2>$Z~8(Fyx~v z__l0`+1zg=*(2)BeBS3j7=~jbGK6a}N}2Dv?87(27BCry2(laSKi|;607wkZuTnOx^{jooPInfu=^D$WKHBG#-IEuU*|0+Ub!ZB zl0*nTcW@;Qs~Ua_jgiuyzjq7PG8-fCuNFGl4}{kna+qVA-BUz@fWIhY#oGqPDyw5N ziy^%1azj|rk>20qHUa!wfH9L}A{5)!n8pQaL0y>Vhi&t14WP)=JV@N0y7-Nw6ZzDD zd2vXyQlnPW(s1UzR&(EYW^ZKL?BPH6!~$E{x?DO5rLWKb6p5)Pxd$E*2EH>*^FVZt zkg*4O2%U!KQfQ-zm=}e#ZiRr0{T`LZkomo0CX9jIOvZ;l(qz`wzz#$%d~rq1+f8Pm z5B{kiGbzFr`eejXJ%mKFa?qiwCOpD3;CE!Q5UigHnC#a3Wz7*EnlOEZbqcCPE+Rv8 z8J??)iyeTd6uG>@cIHLjLytU7j%12lssrzo$e3t=y5awZCk=0Rgu*g@GCK{30+cWZ zkvX0>aqrh_b*qI!L`8Qm984|9Yba=dFIJ)Ii2a9pWQX8v0EQc2NVO z?H5=FPk)z^T0!oe=;tzfyD@3F@e7`KA3c z&Nkg~u6GH=tVmOtXL!eT^119Wh98>aC2cbCItRo*{dIx+qW7&mb`xF?MrldTS-HTS zf_^%(i0p>K!|A2V`S%|$MC%l97TX*J^FpDwG68x_rqm9+J0 z_RP7u`V`VgRDfr~qqZVBcCAXk{`2@ZcOROfN4UVDds?#PGW{~w_Qp!}U;y+YUik7N zPxv|rqsJL%k8nS&=I)UN^ui-FlVLw8{r7$tPn1y_!Nk(I(DQI$U`*1Xp6kaA^Xh!N z^z#UW^mzX#+*a<`CZ}37&qRG#_vouwQ(#7Na&_H*;_UNLsMa&=x4P_=vtvgfY0b4m zOQl#ftlU*VS5>^B9aX67mF66UU+CA;t+(kF)uba!=SR9IUxjguXH&>CRjX3emNzzr zaTE+#O0qbV6Rg%S%wCydmgZ@R!$EfbkvM07KXm;R9yeANDN zrcft8X?Mw2+EN*kB33c-Zv%{fP&?O!8rNRZ{@G<0ku;)?Ar%Km6T7nAhJv5iReu!W0-@P zO*JEmAN~|d-0|tD+&WK+vr8)eY7^A+pui(Hio3L(ASDSI#YW-a{@{zY@79nSZ!)?8@%4+N^!^!Im-yyLwizn=xy0q=iaj8q^qVxK)RdW%YWi<&=5ll0pM(lH_G4>u_} z-}5A{85)CMw*k`AI`P&M(c@0EK3^_q(TnkyKXE&qXJx-;H~70XdK1gXnfJx@;6ZeE zw9Ax1l5%VK0ygEG-M2ovBC5!5GFHL#8z3FOD=jDf(NO`M+sC~%$ z$^HtNRH}sMLioUQUjXxmEdBtJ&*3ZSt5s6|fgxmiLL+L{J5lj^1Kzpc3*kBeli?tZ zm^x>@Ug{m&STIDZ_cpJMdj}buhI0|DuvSIA{*g%{0VnNHy%yUooO&BtD+GiqfN^ED zObjA}f2QmXa1iK4=Buzr^;GT6v8^7D=&VR6idPEVuf^}cy{8Db(^ca`!;wrn`J}{$ z>#48uM&uV1QY#o}g79={*i>O;`E0M@;3fJfrs=_H8M zVcUxI!Apczhf#=xYbPajU!CPq^AgLw4Ge=QMtW2YNsavaS01+cA-0ozwzxZ{5M~cP z$Mu#vmL0CtYo47!uq}{n&G8{Hq$Yrrq>Q6F_D3M8uI}#YPSFkfI_#3zNv;>JipGL2 zIPY4eFnvhDfk`?0^^@Peh1}5e;(1O#P*i^K599EPeS%BX0c_uWh$o+Ky2}?wOY#wl z(JRa)L0vV@ovbo|MlbP>ixF1<0}YyC;Cw6<`JbX6$2=7_&ftk-v2{I^)%FV|^4+tH zaVA%5jm)Ee6U?!RtEkc_D)XGKT1^M%x}w~Im#luGqnU9HRFXWEGrMyq6QhWN*JiCu898u3IWyBP%7}E{ zx*3p@Uj32_U=6O)GCehXpe?^po4Woz2vWNlV$>pUB;Vr}oPomqFPcv7UBCAc<;i0h1bnOuJF zMiMIc9F^LuZG)cxQtVLWb0iRZMFQHmM`g^XuFVK0)~rY@CMD}QrAesWh3Mv0ncu|S zr`A!yhE~!D5Cu ziwR29B@LQyv(*~qQU5hmgAQX!J=1jaC@0eBNu)t~_LQGQRzTgR& zuNIpUn6}kfj%o-t5^A-HbG6@H8HlhTj zezD*#vfC^n{J48hSJ3xIH=h$?1s{K-0-Ya`6ZOQVwk1QK@ibk((JNvsf7Xj=Zcj`K zl%Ax*=uNGhd^fJxk7TguJGkM$ey6ozIGx2mQ$v%Xmzt;)o+a4q*Lg;o^pg;BV7TTo zYVd4RFDToQwoyRyRNAn4aH@zhS#QRY(*=#*Z`#hB2ovk0nYi<|?aWPb4?>*mXrX=zd@@IlQ3$>uOyUtl+y>~ooxzEian;=G;+2quc(t7iD z>>H8Zlzzfb{1IKft7Vz9c|GL~J2f_WVm{yl-S~pswePeQPt=4~WmC9tl5`SY^kXDt z8%%FjEgRa|^ZWgmHd|Qb_?{I?e5`#t*K~#~id;a#$@(QPg>pT%9gfy!LE71#MOy#X zh1_b1ag-TNsf*>@_HNO~@BD>CSoFVi+imQ&D4NKcap#w=T}~PV_;EJj2$H74w_Rw& zW_Dmb^K&?~Pd3!h&O5iqQp*7hpdedtMp(>_RxQk#a!TDE_vk5`${X!(Sbuo`9oauF zM7_;mAIXJ>uzhg(H^<4c#74S{h~}v(B0~?NO6#(>pSty>L|o0To5}N@sr0#==2H~= z8j;S;EK<%}j=dKtRPr z7Q#?-$`>&NGKh^Ujy8yT@DsV#r=0jWl?c?R&B8ITmcW7Lik_RpRwxip#wBT#a@ZYQl{ zowmiP{c}%+tsD}(siA1+|Ljg}o_lI614$S2>^7{vs<~hZGifOF-3%C`Y|zMCX#B6C zrhaU-J-MCVS5J%tY4JnpVEP8~7N_|wQTmNl1^3z<{oZF!} zif}yfv)#SysD|j-r_C=s8Qce+AsPG?bBLf2UmhI+YqQO?dUf<4Q(AG6!KQuQu`kMc zWT~m2tnuH-<6G{2cbOv@yki(Oq1Wrv4hzOmq@H}rM51_DsRFnEAd;o;VO07grxCA~ zPQkweIAgv}7xA{sxw3H?OL}6ZBsY?qaAs0Z8GqgksWvGa@$+UvGMYpGwte>LBxP;N zpTIQyeC#rAeW4&wI$a394g*#Tgx1p3T{m1ejV`9ZB%#paeMg#Z$!pFR4s7#N9W-+h8&W^|S!R)-*CFB%61l=&?1I`i z&t;#_7(Jf-T2_}w8~rOY@owjaWjB-VMR2W7c|j)F9e74pbIDC5zGYi|7$eT+lE3ZL zCbb0XRH2%Rw>prYQiB$0uMnA zHdctH{(UW;x5ms=dsAr*i42qpd|O9*tE(6L$jdp>B|hQD_{wFe7@o&7+g@)hKJ)A; zcWhV-_NW*>m%@zF55NulzP}4*v+ScL6n5jkDStWogdRMk*+8sj5Hx$ zXFxTbyMF)sBx7|OLDy$iiOt9tR!Mf)T!56rc|-Oq+3QOnWFz~$XGN$FLwYD&x??p& zC_F16xrgd^pW2a#JEadDLlVv;#>4Iqu;gTH5SeRXr?+)G z_Ydnu=Z$u}5%g-(-A%jS3xMX(!$c-d8>^a>|B6*EtWO-W`AE!bq_eMtv!85?1<@}> z{E|C5$2-^G!gH3H+^C?Vi&k6b8&2)!WdJW?$G?EpbDRr><&;%rY0_y68I7K?9S0RM zC9>+Usu>Pfy^TL$jgXnr%wDy58IGwYm!{aK|4RPJ)WZ3|sHC~D9-S=H4$nkWVXQ8^ zs6&vYSr#_7L#K0GZOh3z@Mjg(!gv~gC=gm+*G!jk6Cs@|w%2=ql9jF=_nE$Shxi%g zB4>%oD*MW$rxp{7J7kG8I+^OxDc4_~w4L)EB&yn#oLNB3Qyd5q@IZ*H#5+5~TJ%Gt-)u za`%+R$1BNPQtEC=Oi`B|Cc4q+3VI?~KG}}=n$rIMG?L}>r47Fk?G5iDEX$*sXD7j; z=dLcf<(*a9BMsUJx4&xd+xtw>nMxNcPH#nR_@p_aGhW&S_m>6cU>;`kt?Rif*B$}L zd?IMw4|BjQqOs_cW_WG+%so3}OJOe@;|I(gQ*wAH9-dJPnD+rSoGLy!-={-;8ZZqs z!~#BcYaZYli7#JctO+q-m+MFzp6lz2fC+~McTmNtTSnxZ$KMslJDv6kb7peC&qX;z zHsfe5bgmArH?lzZJI3o9ciy)cW*sJd-ZDG9G#9!DZj|g?su$1JX8nJ1K$$t9RjB8G zLO;)h)q!b;Ze9%-3UxQKt(S@G+{zbO1U5Wm_Xy{UoH>5AiWWAdX29uW9;p?~ zI~5pyFnyc>tQDq8m$xwf0rMCdSurPhab^ej5bjVq;JMt(TpNcKD|l9D8-;S&&dsh! zdpp-qhdlJ`Og783AlV?+8J{BF8p61R#qo?)HNYhH;`aIKN zLdbT3LuzZ=OAxt9cuIUHd!l}9>p^4n+%S|Ko?)-RE@D4eb780btNGkHY_?7vO6dG@ zwnBSz|AD0>PpW^25@rLT;e&Y0&adGH>)hU34F$p|u406S9S#qwK}DFwNZDGrlv<8_ z*lv80`&alJ{h-yeE4Mey+5ouawVEuphA&g!_PNtR7siT1b~;efvN4cuMg$p+nXHT_21VKwtU7e3Mjb=H^nhWU2o-Nj+Lz457QOg-6y3Ki}}g@zPe z4_KYJ+RMg<{gd1E5o*G_gWUUC<2(1Ah>tO)Zd|;Wj^n$2n_XJ$YEFtd1vS)QCJEfP z(KFTZf}57x1#!{$&Rjey)>;*iFP@nnSaRohjeq4q)3&hc?SVbHR(V&!Q%kg6&jk?m zGaCO=++N6!*>{0;1w}pcee1R+OF6ef8RlP!0@5Oq1ShJp)4pkkFCHzOCF4P_uKn(d z4O{;~*IJi2=8@fMr9Xyl5{Nwb?O2Pn!k%&qlut3MsJG@Ob+A<|6e5CxG;!}_dtwbN>^ew((h$=9X+W`lp-5 z>Yex;cP3j(*Xr!y7=ap+jz*e2YhRZ%y3_d(go!3rQ+a{1*L#O_8OFnHLQrSx9IqoW zQ@$ekj5d{3Sx0ZTe4^i7-vm)s9B0edzrR76`6rBIq7~ErLXJ=Dj<(BJAsS$h-zD>V z@snrOL2KGl&{GdL-L>4s9q6Qi&adZOpamrkMl&0HYBA-ByejIqzNB=)UlzQ+PkG_Q z=bm@D%5b$7Pp5wiTWcl_B61=>UlJ%BQhQ#nrd%S9XXi`P%md$HGF2409t{?4cg0vK zGWDo{r%dh5sjUvY?t5yyNJ#$P4zfX01+aTc<*{5*ANa z+OGw0{I-ttcPkXeF!sagUJ`%?z5XihUwpN;g0(lwE*ke3IqGLmzS(XmO^hQ-ogQq; zS`S_2Vy^1!SF!Ktdrzf98a5l~Y&ttFFWomUjIe9d|hd?_1QleiHxveP?M zkHFc#Y$;VsORDkse$X|D439e#CV|GEC7f*?ov-lnquNIm>?6{IbTxEMn=1Mh z9<`G_a?}>c2&yi%LbnFUfz#TTQP@I_$mG%3_KnC&(b##7$o|pTQ;oBT!h@=Y;V^ zSQdE7*dZeOa?0o9e6$}TDcf<$`0{A!BP|QOV*HR7 z8QGUB#tvE8j%!BXndH7|BAI=P+eQNuU^o^qyL-vDN>}Q^A1<+Dp62Nous~s*kPW!KHs*{ZQ zNo?DGxLxcS;q@ROHTFT7brgrVA}7INL~}t>i?Ry=px+>ADXbtZLn#gmrytkR`c8LD zTr->TB&x%Lay4?YK;<`MHKqoNJ|}kP0HR|~i^w{SS;SgcQ#PkODQKQ7x`(q8?i8)v z?95~XR=p>-miP>SfYEL?izq-88=kb|!hTJ+yGggNINtoP$0C`^vn}(}D6gYTCacI$eIhqHdzWO*MC<{D;+vg`HqwRR62ift{&Arcrz!qPxp&hV5NKeX3oFZ;!&w= z{JG@nK`sHZ3F{ghq$$3sOJ9`Z!EYD}1 zL1(Y!XUrZt{Z@@?4bf1aQkQ;d#*gYP?Y+mWq&R+zGRKZX@ZPs3Ymzu2sNY6r&dbY& zTb_@P%r*FwmC)hZv1XKLNY|Rgogdl)nu}w9wVSp4@8Q3bEv5HDr!$VyCwIfU%h{7H zwfCQAem52V1RC$vf8TVyuT>wE@_r+~BIIU|!B|_9*z-ak|AhXCy{vKU_&a!2PBc{E z?_%S168K}af0|UN{D$tkjeN|WC;bpP*E@I*>qnztfn-_C7wx?kNfsiu*1@Y}q9IkK zYT80UKIn@DM;{mMUIui4l4J8d=po@X$~>rBkxsSjS0?C}3apconci^K2=KD%2Zu4O zPpf+M51H21SFhGCFT0uy2G4K0bNoLBi1I6ol`rxoGrTJ<&tFrT5;!@Av^d<;zoz~n z-3{QOo1?>^Jvec>(8_nRb|Oa28y7IQkmEO$1iH$z+P{DM)jrN|#`XRugZ$#~A#}X` z;34kdQNT}sI6Whach&S!^gxXRt%KD?CKP;>JvdVqHGoXop9c8MbN z2WsXz4k|b9(j|4)cO5@@$;>G0T)S*tlUqLz=UC&pVK;w+>~XY~dJMV(_vV0m80L%4 z2cT@vFb-K*Fjwn@#fFwdr9i)yC};8RzWSk`@oFTo>ayF~Xli}!)5E|ebf{S5 zWg1^<7?B#uMV@~azS1kmXr7lw-#D$oqFZ3~GT?qn>X~%r4bGw)hv+OCzWu*{ zC^H7ML>-7|ZW6%+#J()w1^#Tnc(y~`v(jzfCOD}#d3Vi?s?+T!EAf;f`Jqu6?laUA zKlZkBO8o2n*__Gz*>1?LYIbW|G`waBy5lj%qy~JQQeI*-qe(p(@WJ7=q^x~bFW>KX z_ci~J|C8+adNzH#jza#%bO=m!_N!KUJ#7Br z#GCNoQd`pJ*qi+X;q^b}8@X4NjN3jx&61bDdGVG)d*&v&-CkO?>IPbk_xf6PEZy~~ zP0&%*$|jA)W;Z?0@+QbA-(oY9QiHm#7B??Fv=X?{k*|7v{p*`re5Xp2s;!B>^+F0x z6|IJ%tgGx@Vp0{F#-?Ev$f>z$d6d7ZN`10UUE4FQX==1}k)7$XnM;dzO51o}VDU54 zVd-Ge>8SVI-R#}3C70LRjFVlLLKhrYnWxHIt#YY0)3)L^<_*3L$-72>Ce=Gd{T#A_ zr=l7K{fejJp^~BUw_}-O87pBgE3eEpDqm->ur~HruP0Ym*QbeF9V_W4@uv`?Og2@8 zAIyd9vS?H3@ADDzf=L?G92jO~zY5tgOv%&>Ihfev%1_VMWMjrEM=MqKIIKC8%1${h z%>~uDP0&rz%PcbrS0b&v?3^x^PHI}W4pZ#1e!!v>7M>sEM|r7qYfBH#G$zb8sR+t&WoppNxj zJF84hpUHVBen=rK@IUwM)2vx<($yZZoqyWzx^MCrTQt3Dt{OASs~X!SRZS~2>L!2K z+tt-69Pn7vz7R9XPQ1+G|z$2SCsTAWbOz@u~g}=RGtI8uc+@DaRi60Jq6M7rZ8t?XP||N zH(+M@9=;&*98Fwx>rEoYtn1Lbpq2c_xF9qSf>Tgz+0Znwa?E$!R*+(Ay8C-=RNVRl z#2@hf0K(d6KeRa|Qt<Y{sj8dLC4L*PMSU^*faM2FKVbWT|J7N4 zz!=4KEVIsu;Ih8`-jtu4~OFo=MwA2 zYbdnL5>@1#c~i{|D3@v-j_>b3z4tSuI=E#uQmR>eOI%csQ)fww+vOfoFIWYXy=rfx zX6c1F^X-7b&3K+gT?y9j74!oRYgG|W}ycm z2jLeOEJ!RERj5@+RT#FYw&=DP=g6I8s{~IJw~j~3M`P2Vd}Bi_^bqPD6CUzv(rN-b zTsyLAL=cKQvOC%X;sX)}Dh5&6`eP>~5b_T~uw&mV)kjaIM}sRPUZ0?y(3Sw7V3wesz?Bf2Fe?2ZT{9doygdvV zP9F{#P9A1S&`RJl18LfNlv!J5Cs z0W;E^gcDNMQl}Ec5?NAA1?L4h1-gX-1-=Dr*7abQR7poJZbc&tv@K@Z<7h|Kt2) z^yB$e>lNIU#8vSX-j&)_)0O#d)~?Pj|8D!9_ulg!`G8Oic`SLPKY>uLa%7@7bAlo+ zjw|*Zjsi9dP8AL*wl4MtwgAp6b}+UWP9e@7P7=->4hK#H&J8vSP87}@klBqE)S(;o;Hj*pnrp{yd$t;XI+RX(vYAdVtXr`>Gwk&H}&80T_D+s3KX{3zL z^=E7K#xe=aGJ}c7CTv>tT0$!{E9@(LD{NXBXSru7B=IEKB)KF>*#?r_p!AusZ14;C zb%b_geBz=N=g8|wTp~h}K25g_nL@6#XPGi%!f2LXonHCRa?kSK zXmh^ewi2(*t1$`=M$#Ojyv*#(+{}#3oXj-sbnR^IR4?WxzNT+Yly^o})>ax;_9vb5 zLuGws_9{}U^s{<=$%;h{$J)nj$MDBy$Lhyi$FawwE)Om>w*j}?w~*WPTi7}KN%<+o zDW$DEy+plSy$ro1y}Zrz&FszGP1C2D*JHqO@G!XvWu5AN^J$DPj4Vw3 z7*hkU?ze&4_Pq|C_Tl%#4kQl;vo_qH-+F9C3*s0msUSzD7tf#KWu74!2 zY<+M-a6)j_2X{@7jz#(Obl@aHJ6`3sp_0&4DAz~Payw`))DJ2G&4;o3 zr?WS(_g^nTs6Fz~pK?tVdEABp?SZxd_yMy4^#QJd*nv@_2cw$Zp55u)qus|{@b2yI z`0m+m$8PWL?r#0==x*lj)2<{(;mXf--F2OH{g=OwmRAl}(pT-fS57rRmE3PNYI>j! z5zmHe_5Jfv{uFnX%b%MyJ`PaGHQPSUXle46ieQD0A~fiF;9YVftw&1D#CvHLi83VK zWvk6~?pPq_B4XGmUO_xM@DsLh5D^yEcOz6RtiVr(IPnT@D^ySz(^zMETcs}e_%;R&NEOV}?m^{Ai1XoXNq(2*6 zl&qIJfxvcCRZ|;T?W_;N7y0YdtwYXA2e~7!l-o!}2=$-cC@>@7kUviVssN;)a{#&k z0e~-HKQJ&bKX50IEl?(qFc2DO8rT#V6NmuF`m6-k4BL!w0`LYre@6Hm{#okt5P%+F z{#giC;NPu*`ZPLxP@REDt!J%ktyrydtxl~{Es*D{CLswH32`=dHdZ$AZ<60wzlqns z_Luc=ipr{*uvey>OtZ0)q}i*aix9;0rFjx}k#mu{O*!g1`gbHXvp1tUb7_>TDLGE;Y?QAFT;yD2U!+@P zQ^!5UKgByGI3*{>t%#0^h>1~%ocO~MT@_IkwL#$h_1?qA*~Z#N%0}s|Y58sW-}27# z%yQ&%+42f7F)cGK;c&wFTGaEZqr{WtW)u&mbV(bg#f_Uw%H)A^lJZtvPr})#|c`hwBBR^`rZ4GDvS#rx%k1V&`@c=dH>-c!H`D((jfOBd9?i>A-pZ@EwY-p z%b3e3@(A)j7VJ3>@VwIp^+(}<4D{rN!IVn0BI%zp6IEh^cf~iO76Q}6(v*PHOZAZkLb&a= z{cW3UTW_0~kwXn}<+DNgKl6Yo0LE9FPkd-?a}-JN8JvAvaO;3|l=;G0$|Q8fww;ce zTC7LH=~Nh7!mtL?7L*o*7J|P+Tw(HboSgsu(*4&UV&0czV=)ZBh4QL*>&3PhrN?^f zo&UdAuWS5HEHt724g12#M%6ytVruR44XrB2UzIChn(S z^tEv^`85m#u#M1z5kL-0kJv8KZ$h*I>+`vVum!CJr-k^D5#s9FgeQ9Mw@nJq4FG-G zQs=?ZHZO3e^ZMVWaWZ5vW10`=x`C2|IC~|`Sy}@G=|o>W`L7B+(G^nne@rpXHaT$9 zGvdf++*R->^h-OYAann6=r(K6fy9w+NL|!)J7Ok*$PJZ8Q$$x8L_qo*TMOLZJ7zd@ z22N%;e}>1gfH6Z;UXxTYGu}$(@Ra@9dJ^^2iZ9ml&FV(;veDX+Kba`IiCz}AYvBEV zJX-f?0Zy%5P%X<-e?7uFa5xY<&^vHL)c$L0CZb%!O*i{cp(tD1BJz>8&3(M|441ww zq;)hOyuVUAqhEdSj+}nkSQtY%@>y_J+0o4ZpEcPUEW6fpe9X_seid>Ha^RInFoqAx z6U(jk7wi0`AiocK1adh$c8yPl2YqTHLm)vQB7u-XoP`eq?)y^76aA#04975Z{Fbl# zQuXz1LsyMH6@LDL*Yo!iSZq7I2P2UDvkn>I_7Gjol3ac_z7hXtXf1(RA$GY z*2=fcp~c(Dt%!bvkNexdJ~0KV^;i*5@?yKhL~YLp=~cf!jo_b7k?Rz=Q-!Wplm?9b z(~8_gi}%Uk)@YHNidZ;>GOj~E(k*wQPF5Uk)X?$z{gllr~hAL#%U(Z1!!E_ z{<5vP?XeB}AK9VnkmuzBmdazg~J0pGXrxf=~&k*e}?2_+qP}b?ASK0_uaj__b=W>{OGQXtVF3RqR?5HO~YxThPX!8 zpk1T8W_qx8IJIb{BQy>p?%$O)rTsEkkB}m^AXU!PY^AT){CBwG-G=NN{FZ4;;9`cy zgFdAk&|)B#et-R%dc#(+*|5vTdN=$R{9JSKZgWooBFZ*^HT7RBETT?D4M~ld1T?|_ zmjXY}w8O~jLzV&-oB1M<*W0w7j3us5VEPV1JS@T5tX;GJG9y7?L0!uU)ro*lEYJfm zu6Ikww8#T4u2~NArQFBJ4W|7&&%=+qBw#&GW+RJ-ss)@KE4z7_ zO#HsnGOe7AJ0&b+)UONcjuAL4^X#?Uztf|DsaK!P__J*_yG9E;X605+bW>>Vn&-^( zj5Xs8{%E#GSV&Va)PsO&7WJy)z)`5c6WIP?<^yi$UXk9zx9dgD9)*&s4PO2AoR^H+kV_S^ z;El7)dmkf%hCKaj9SIfqDMUGo=y95s+?6TfI2O+@NH4W(`&`q2Y7lJ*55yCZ+!Q|@ zU=NOy!`x%NCD{U7!xl$x75CCHUhS(5x0dy(kvfYKBH#r?|B$_liaS$KR5`LNQXsoz{TM-g}kbq(*ns1#z_~#H|u`y$H{wD2uy(@`euL`i$X)EoaH)O4(D*fmPE!c-2>iGjvjK1M!qnoY|E z%kdEfl;ps)W_C2bgCB>i426EM_YVCg6LX~lhD>^VR`k@ia zFpqzdTPC$AJGa^dn)ahS4S^n&s$Ev99<3B=v;APDNsyl#b`#nQu@x7v zzJavDsKI-owTcvMYolB(m0pW1mOivysw{MytzQ;zS*_z1Zy9S8ZP`?I$+x1u5ZZA8 z77hqI^xvMy{?CYRcmVK%>%a34j3Bz<1bY3vV;CKw-@p(ZqNfa&XE=$`x0=L_z#N|Z zX=oD6z%-DWraw7JZ(@SM#1xr^+1&qAQ$M(hen2HvpK7ue)npZ#$qEd9|Arkd=I?(6 zHU~LiHUc#55T!M8iYMmKJTiGD6wJ(jf{SNoQ4BK-te75}>03cU$I(T8L9e2L`cYw+sOt1AW zAM_i^uB;v|U~l6XJBRu$;&J`^g_7L^PO@Yp{8;8az`P~xOKj>rMY>}F5Z>E+xN!#a zwMBVC4bh7xpho1T2~r2GZ-UTG9rQbVSC`=+f7Po3Hs)r-AQd-v%kS%jc=*|cwYZ^~U= zu#XBqU(}nL|0MdaspvJM&T8v>Rnd2=VCYgs)}RirK>xM;bK3;m%{bO57jK?}(*0u- z{Hq{W7x)U)B8QS8L~=3@$+0x(Iq9YwX1PHOsB zDVd(EbDvtqCma5|FH@L1DZ>o$QQ9DPLaHg!y|h8L#EU#^PsN@q%SUza&eBZ>mXGSd zwT0?0%O;H~BhsE9mEH?!ZIg7k1wDtcNsHkm9+ zxQ_iqyQiO0ZHHVzSUx##m~Rm5_&lDm-(>zk@b^G`0KLt$LQX%|Iy7v+e9U>JbYqvA ze=LQvfkEpLJ+Lx1*7s-lJ>~Ul`+4p_&dKRoiffdrlA4P=AY*gI$_mF%7Y2cf!_OxS zO3*#d%jse8Lyz)5TF8LMVUL0xh&mv*#r`B}DMKt6Gh*4daiBYu`h^c4XE{oG*BI{^ z%q>Sb24h6lmUcnvIZA#P?h!@cxl|F{Df<*xXT;~8B8`7tvEp!8(j z6pwrW_}?cpubnk!7Pvk;TS_w)!klV7oLdsc9HKtVr7&yEL_@eE0q~A=J>sI!+YC=Z zZsLT$J_9a4a5TdSvMcVQgl$g$0KY3On)G@B%Y%z!e7(-)K+1*STwJ!UV(syOiq&7H zB(X}5%8b)9kw+4{j%+o7Ign>6yEyFv^NHHJzq!7OIisszCr{^z;63(jRsM;gX_f2( z*`h&i4T^G2!X^I5Zk*!LF@%db1;RZP+$~7VEOf-}7wkQ;+dnd#R{j~sI9+CHCifGFFy!V<@m2I6ZY}ZW7vMapzWXpDo|5wMg&Q8~Z#15X9 z+9i57pd)0-kt6+ww91J%h%tYS%qFn z2aJ6^31Ic($Z<;!0mmL364TF-@i2#+Gq?_cAAzQ6-1zWr%j56|`ZGLtM6YqCUEG8* z9;~B=cdf@d?t(t2?)=%wB|I_g0%Rxdi({@x*)p>Jio5Qc=4Z_J9~}#u=BMuC96RRc z?!)HSjnAFCKkb9vbewD)%DFc(ElSq!oe z)R~7h?yIvM9y!kV!xv4<=uYK=V<{QqBgquUaOzVl39&~55A5%#*Q$B3ZDT9jy&hr_mrX3G7q^|wr+@+wsayxgrc zM~utrO!&FwL$9D_Ua_q8^5+?7*Td3#)knY~|I1bQ0MewJ=-8H&QB1~&mo<*Vgz!Er zY|o9_nREc-Iv+3QtPN z*N%9Lj2zcf{(3$7`V)~OD%9)!op9gOM)+ehDs<;$gi#I?w85^<3j%dFHki}&q{K8=)^y$Gy^sNpP z?~Mx0ISYNkK1tf2Sw_5_*ujj84HI7#CN>~MVnGZ`3mudZzLOC~A|(X09vC^Un3V81 zJ(SsTCH$KkIqis)kw)4`BYm`yF^*$LxDe1OFj7KmY2o$s&_*UiC_+N_&`}PLq8Aun zQ(?~3P%SgU`8nYOLgYAEQo<}5VOj=63P!}XMd89#;lheA@vty)HsrV~QbHbSVUP6C z#PrYyM#NL=Wl22bvhUp-2ipgX z&&2Gaoq4>In8*f{Wxt|^Vd?~9COYIv6Gt~LoiW>eWiO1HBewg!4>aC@opG_djTds> z^q4&sFKWDzk^A+;&}*NMtt&tPGsw?WA8B$P*%)Nenct7EB|CH163~j@ap02!+{95u z^-A-xW#|A`&uZZ2gXy)@^Qo_?BI&hjR;*)3*6y17ho3J|gDFU|PziL+-61`bx_dQuT_18D$>Tj5lL`}+`xUQPK5V)Z*L&wL9^L-m8+@Z5Igck? zDw(=lb@N!RJ;>r0{XHDtJ=+ToeKl{XKVpaj`G)Zn)2J0O&HZ#I>Go3YXgjg5`=s1$d1G})Aa47- zAbYY^Z?Io}`~0=lcI83e4s%sV=enP^SQhCNKczdec)Ul_5#;XVx|UUNliN-xywmAK zH_xp;ZX%Ycs?=Qi&_2&vJRxK$7nXQdQuus9{gCuQr8`!=|N6k<_0xUVq(WS#J3^;A zPqF)4e35!B8uvio7E!+*?w~m5af3JRN=%_V?{c>__RiIiF%KP}Tc%T4rc+)9u#0-k zKiT&4L`%qf=;W6pl0=HVuJfp$6jT(FTAGNbPE;8+1fS2heIJ>?KC|dM2q!B*na;x6 zPG*AUbC3)im#UZiisyovvsm*n8yThwWz5i~ignHk&`=kP<2ji_-ppxXXYpa@YRlpB znMLo-GLIHOW#)lB;H=Bb|Cl^*uV*&r(kM^ab(dpkEWnsyuO~MbT$)380Bu`ytqKV%-;yr`VD{DfYk>MUyS0gW?YTT*O(faN;R>E}Ga*Yo;&(zE3?<~7@P!?tbLj(gAZ*Y4+>m{)n`oqf(o%D&Vi%T3WM zR{2>;9NoamYRakQ{=}W}d(ez+mmzNBb`wCY1x_7LEu;#*DyFhWL#(n#U92jm%5a%% z+5TMdJm@j)vF-7X^ZeU9r*T$ucYSyBwAzlEX_al&b>;Qax!Ac_UUFi&F*l(|VK_LU z3^U4Ue%vTRlt^*4jc{|dO;C|ebL?4C0cEBZB7y)Sl0G7$JR-6?A?=-rAhGh6W3(CN zv~tpnd-;_)fkwE>zLz|?Mg)v7_MA#1@}&rbeclNh%1U9HdL)Hd~7&ODB9+s6DWB}*63vWx0h zH9d;tG$;$36X)YN=II=b`GbwrM2(b&jj|&rywYN6jH%qasS~RX!Rn)|sOS=Ljmg~W zE~x3^MD7&SsXIoi>xErXx{Pes#B^xi#~I!KbSmhOOdKxN_jHQF#jcF|-*Iw?!^N#k zK)VOxP}(LY-I1yvCbDm3Udh5AGnSMno;^5taCv7=WOd7?EzGp_x6QYWQ@B>Uym76t zgJfD}`A<7fkT#GpuGEb**r+=CjGfkvjP6Dh|`L4bfefz)Dz8$~OzY@Oa zK1)7IK6T$`UV6qZi+q&-8=UvClD2meWQ{k9wExdi&DWu)S>7DXQ76 zo?mf}lHF;x?jYgOcdfNGfVHL0WzW8|$9bNT&J4~Ss4W^?wrP~JTK`wa()(0)zG zv2YxGNM%iNFQQ&)&t>iUm?2Q0%u8s9EH^#{BjVIo#BWF$Z6UHuE4xXLMu}E_6gtVI z+~Hy|r!m3g&Bv+yTg2qk@{cILGP=1&)0=j)drH+k;$58wVtaPAYTCZ2M@_t2?3euQ zciZxnsNC~6OcixeJkJw6*`e*^w(d^RgEm4o=i`^ zEXwbmEZ?3?hls_pt)pW{yCSus`sn9a+Y>DzQ>>B7m>~nU1q3Oj?Asz|jm8!A;1j(p z7SlpbyhCyhjp#TQhe{l>kqHeBf$3PLLuwAq={VQ%Hizs?I@Qr6ha@eUmC=qv4fhzW z=Zumw)t{BiitI#`I~7R{ zNi26R5*`xmtSw9m%(IK-nPc|U_@sSB)0QlQii9_c>yWd;X0K$3WxXsZkY#rjDXhBb7TR3WMJziWhn#Sb0xMk=hsK^GLhV8-_4O?~F z1+pQFx?~%)^Fi=-5i6AEdGx{0eZ+PdDqnTJsC z44w!dlbNP#Q;H|NOI5W+pDEsPTIbeC+-@W&TOm|Mt(0BjN5yT7E2flAg&&bYk z$P2^={dcVI;P-gUP0ww?hoMj8wt(quniE=<%<(MOsTOCD z$^xnriMEL90?UI z-i~LZ%A*5~dEhk<_`YY1fMhku?RU>hbWA9n3dfVI3*;?Uqrp6<_DiyNusRGxris8HU##Yq%Ld*n>|;H2J7BeKgIbkO>^Q( z*QaDFE@UoM4{(}v-N)KIVhCs!e<=!ToM!#e%5_QhDZJ!+SwQ=0)O+(m>A5R`{V=b& zYmKJ-ek89UjYBzHx+Ip#x?YeFdP#=c!5Uzr^J=)L#Z}fhT$6~0;Ul_XGD+F^l<5DZ z2Tyq1yK!{WjM`}pV!Kh6wM2@$)Qv@qK5$Qa(+DQ0&!y(A}NGrx%uj30{gXzUMa#vjR8Sqi^)K?(!sp z*6Wknx3^D|$@+J_$tdyssp&5wBJl;?DokYoL>sndFaj5<4jyweRSK@R_dcf;Q#b5C zEeviM5Vn7BSn;+=cde1O(Ryc+O^*2V=`Pvo!Q80b892A4ANGVu;MvNC)>XZK1=Gjh>pv?|L9b~E_JR_#Tmp|94 zzzkErta>80ac<;{_p0bv$|9~*s7NHPqN=2l@ON%9p<}<6V5G^Af3m(b#(r{0<)9_=Hq}?|4t((NkUv84i#?xEV8uJV8 z%kI$+{f<>{?2pqn4sG|r_oNg9$W_(j6w?%>ha6`j)x(OFZ-2*>K@jApX-%}(08t@o zWhpHQDkH`<$j?jeP(Q)GK@J?6d>AiDU6Qk`J*{)B*95DI?k@#9n!NqDhHWfc8a67N zSthHSZJR?jT2_fJG%w@gC1`mVMT*D48D&@#PZ=p-4mj$|xK&vbj>nGb|KN~T98Nj3 zc8WyDwv5N!v2w`E#wQ%2xOZeymW-X=@vV2flVex#!0obv=p9oh_kD8n0P*DYYl0;gQzpeJPQ{ zGu`adQKwcvL!t8+HAwW!QEIR_yXo+Gr{u&@a)Z&7hfK2&Vh^7q6tpNX5ub$|84%7p zz(L?M*MH4|mvv(jV0rhtdBLcg%5#L>u;OJ2ml}8!%V8y(nsAi(Dj?j$^MqG1(IMMroP%JGY*r=T0DM936pCd^TiW6TPjF_Ti(`H2}e zO|K!#WwKg3OUG*K%~6^s^>a%6SOBoG>r300b~NF1%mkaZoK`R`YFJ)7v9fRG>d54o z_Bjr94F793#^Bg{YmYV3uWkd|QoXTiebr3Mi$1^AHGO#uX9v+Hl;$qVz=7I^40vkk zRwx^qlTS)5)qs zMmKW4_jv2j<*$>Sa*gWtXB)$|lWom&J!5a`=}7E4<)G?j+3mc5YQlr7tESU?o8UUg zHMor{`jm5x?yu>K^2eEvbx+>T&fn|jn3s~mSOr0FM#2Rnku(y^(f0Z}D_YG7xcZ0- z+M0yVgCBs+b`sG6VAq2-7Uoc%NozXR=8&gFoid^9khMwNGLGxevPlIGxKwO(P>Kv! z4Wkq(s*qMzUQ&8gMqUzZlFw3#KFfP6)nfU5Smvdi3%!KFq9dasSAl8KoL+-g#$lgvuTeM~-sF_D$9cA(WttGmV%V8ZcVb0ZxeVHo zU=OgJ(mR8K?VvTYMQXw+d&=!NR)LS2Zduj625+p}xm1XsaA#aDJ1-i(CxP|2j>UyB>E)uJa`8DMF zfbq2O_a!^K7svw%;*{cMVUBN?o(I-P^i2$>h>ze+BCF5-mn$%t86;U*pyDE!r=%OZ zjQ58)$@K3(IcAaifLq0ZY)t!LZSxCh_@GYVuOWNB*HpYP6U1B0Q$ zAn8UwWms>St!Q%*6~mKRp_qs0HHntgG=_=i>q*0k(~LBxgUs8*X77rbr|2mf10;%2 zVUd|B)9RTQ3g^a)ak^#`OVmjNb&M!)yv+jI8!67_Om(cq?w6a&;30qs*wlJ|chcAE3`j!kS*I%Wu+nPHgA%KP7n&0V27KBN8n! zItxbn&OmLvhZEj)>(H zBskT>orls^DLR$Y7EGKmYs;3LL$~MC)-XAdWR+2!Q@#mi717Nr7pPu^h7>#}M@;)d zjxgzT-Y5k?2Px4gH;2l*mU$tT=qNdY84M4hguSHV8OTN`)GZlQMBq0AtTf{=-hW@1 z#o=@iyOf0v{)*%wR@kQ;G7e|7-`a1FXlxbRA9nB>C?xf&TThnJNMxrlFMe^ovhMYk zB4W6%Y}VBJb?!E6qwGOJiUdos`j zoVzU7HJY2N4A0j+n!By_u-C0N#+#c}S6Q2@tW7sI-~d;NMm3FDfqi|m_Y|V6 zlTdn8KXxWL%PL1@htbG4`Do+pB$0O~@01=SkVMq989nmp+E1$GztARC2JF8b9t#{R z{jwajU97!F9VsE9y3VBJE+I{+{2puTi(Ms95HXy~{Cfd6^o;R&_?1sINDeUG4GXin zFOE<*s8%CKi#W8RS_zXeuzbK)95OxrF2(d6*YF+QNmZ9Zwqsjw92^h(i^N9ElklK5 z-#OYj8Zde`8vK2Rll%gqK8(Tw3vFy;!n)s|N z?ArMFOq%$NjPwA61wdh703d7tgq0D1aA@MQFtGs;1_l6$oeiIfk@;VaRTH0`f#o0e zPlEMdDUAOl82@QA0t#f|VEV^pVg#VfZ1~K8idk4#@R`~Fi83$&Dq!Yd29Ox>8QK1W z#0;pMh3TI<12a2-%fO7!%tVjR#Kitj>K~Z}P%InUzv@{S0L3zM0Gh0g|6lsX_<#EL zAN~0+ET;ctadXp&np-&;JJ5+*={p$<8yngh8PiD{+n73;0Y-s^mlqcLKL^b%Q#-cL zVu1c<@U;&#?uJlZf{u`$(@zIAw)9IN9L=$x5!NWju`M+h!EmIt0$<;9AIY6gHCe7u z2V`Iih5>W#QmsKrKl}X>?(-G&vLrlJZJ5r38Kv z;p@ex8naSg9lfj(^Ju*vAq@kD zNGt*iKZEk+wP=`awW|1SzfZYXB9Fw7J1mtOI#ib0TA!aM53hNI(vm{&6LAyqIRfrq zv=o0bD86n2fqL2E{P$CY`Ty5gG0-#6v$FlKkz)Y-`iva(^#9+{yYPZ?(;R9$ndDes zDbP+f(!7f^(u}78Jm{IYbs(J3%p7vL&>K=eP?OC>7|o6kVh6$z%xvU0>!toP{~o&0 z_y@U}^qDHQ+5AMXTWn3Eoj=zpkEG@l_)w6r$2XdIgq0x3bLZUq`knjf!*rT&gYTR5 zJCd3x5`hs2A@AuVfBES5i5gEn3^1g+jaj5{N4C!WLJ?#XBV^eztLPC6%H{b2z7u+9 zguG*8U6}hyH{6_iP4D~u(bJ!ZX73CI{D)BmVTu$sy0hq)?QXhY@~;j^YeqUP6zX%Y ztK;9pJ$~wcKUW^D&E9MVgApc(gg*;?H_(pb+po|O%RS(?(JR+$-`d$6+FJ+?x)2GJ zSI)YpuwxPH==q$)CeNHVRJ}<;)*@#wu!6Xc`uw{GH_nfK_Py{krL#anzPh4l-+URn zxx(wT90o%~mD~2MDnqt4Yjf3}zO_GV;0c&P%*uV*Z@qnW1EFAp%;&~fpR{*lv!1|} zZU=phE3>iKZu>XoD713rX?Y5X*-O2sA=l=qNZBrHAU|cEbq9hg~Cw?0)($B7Y-S^-D-f zg&8%f5$93NsS8IriZ>E6J$;DCk~#D{{EW9_jC?PA-aF2M6$O1wK7}1ygx5MV?crMosVNuh zu23#tXYL+?FZqM&C{2RN93b<(5q2fi z@zr7eqDnT$bRsZ%j(`1?_-33kF!N8}WL`S`e+vHJnXec!%Fh*iQQ-unKe3=aWO`qm zpVz{>S+d{CP@k9u#UAr7F%Oj!D&N0)h?rNUZSz`m`WLwP=9IV(Wk~pG41!94N?8e* z>b)p&TllvlY((C7+wh`tL4cp6PF$QI+L3Vs?o#ITo!HvRw!Xe*)K+|b(+Azd-yTd< z(}YjRet?WZk@RbFC=_rjMGBHf5|*M)iD{R@!{)M>fvkr&7u4k2a$%O}dug4>XmH6R zQp&Vshpgv0^Jzpa=gBB){Jo7C=0g~bnT@-}EA-JT4$Haq!SOk7YWZT!bJRKLz_LMr zl`=THn$096+W_ibZ%m4s2EUAYM>k!GA<(N+t2sg zQB{k*er#RiPGMpr+t6<@R;MvZn9E&Z(1_9~QW`Z@2vZYQ!~m;JYGHj~u0CwQ+V2dH zs*g-irD5me8zt)68am6&)2k%k3P*o;H!}Mhb;&b=po;pJw`bB@wwW_sA*D1Q9Fz_^O!Kn28*7zP6jmE{A1{t7HAcm=W2uonx+4%iYp z6OIu3zx4`w*-9C-$~ky_?sg9-LAMx5gvt{M15%i8=1~x*S-4k53P7d}{S$~FVwlW~ zH)|wo!@7%jxVIo+qY2g**jIt;n+77!;y=YE!Ad(fSl;}~Jn$w0z{0AHqU zuN>|-d=&m26(=I7qXSwj5f2WRdO*&oeP@_m=ylZY2XTgC^uuiHHlvP`WK$Dtk4txB zMsqAWfRk}AGtFsDb&ifjEc&$#h;Uw#du1e_Th&m)V1ceOqX^5>bX@u5_}j)Zhu@H@ z5fduAp~P*mIn?@G+`LZ@e6T|STv|r_S9AppDU`Xw~G%ABSerX4URle@5 zE|6apbwJF*<5FG;2Ue{(@#LSg<;1|Q43=ldtL#+=T7X`jUrIPq zNaC@UH>y^tPb_;x)|zYt+6&xkdqLWWsmd*7J#Sk*RNG!=IkcWP64YJ_GCeul%lJs^ zQ@oz?c|TZ5Heaerv#`VKxZoOP70erDK-*cij~r;B(U^A(5EymWRGs;YQjE19x5~h_ z_?T%Yh(N4I5kMk-w@*x03OisHqsW-o^$8j>`zud{7(eLw>n{cryE=G?{I@;jA45iV zXuEurXk@#x()>A2Na_=PffJ}~%j-47M`P~Dt~6D6yYAYZgAFK* z>}qX%N1!y>Fqi2P(ID+cz=zNK?D<2bl`|a7l`;HNb}&=z;YG& z$Sv`xP=H{E>&S*jK8)tGS&V*^An)g)HK(VzDH?`%8RhF)3C|=$ZG;UDP;tMH3c)oZ zijnJG+(0J`DM-hc``!7VmB0O!-ED?^qysiaaUZMc&+h-(TTOch2dS9#+pLSQS`gTcT3h^~V*zGF2X!&ofY2;py`zJ?@A+ znJ369K7j^?1oTqp`ocN{9NCgC6h<@U0_Jc$6glH?8H$ReN;MMjdiCGGh8Hd?g-R%T z`mR?5;b9jDLJs7tjJSQz0e=%^M#{k+Mnk2&U*RZx#uky~0-RW@fwV9YH(gH{n=}n>Eqy9x&f(Zs3ggw{Kb zGI^iIZt!_RN^J24>UGBcQK<>PyM8|PWLdM*HP&ogF?XQWh1Ofn@&g<^>dC*a3)@;f z1MqsPUTcFuDyM~8?sG3@wdWAZ${p>v2hG=Ipusa6b;}d)@c3$g>Z;%MSN;bAh}~q) z<(j^GPt*6S&-z-;axfI*Z00!>lNdyBKYVn3+p=e8_YF_M>JDT0HLvt*+Q6gM%j1CB z*v6}Vc6PzAg7lTphg!T(_`Dp$3TMCv1pajhcfIObtGKRSmPcX__=Dsdt+MHe=bi^qoRN;h->tOzEl<4p$owtTU}`cQL+h#p7N8{KzNX zSIASKuf^AGckw}BJbc+bo9AO=_z9dN= za+{=eMzTWw8o6Xmylvj|SWj5=sBcjcRb0G^g=xXs9mW>bj;QV2cFa}EP6ZFaH_3cv zFKy6G zEHYU*Se~#fqsl~e3##H)#4S#+loo!S^>=o|HaUy9fdn7tL*m`F;Fe@%d8PG4rL;Uh z;S9V;G4$RoTG!IJ+~#lSkzE9Uti*3;|7DL*x(2HWVA$W-_w2#Ae{XmNg&KG=8hK$< zXeHtT2Xa)00635QIyJvfelR>oq5h5${Tdi%@%{+0GkE_IEriE9{0hH%a->vM69>=y zbPMM9P(McjvTy#EC>5Ut!rf~7r=m3}$@cilfJe!KqqHpA_BJK~SrjWT&@rf|r)_3S z9U|o0p0$c6-C#`IR0bQK5JC}70c7ltlgrDrgeb?~z@|bj{LTEVLd!zS0?UFbLMr^p zLdgQj{AfaG_V>tmWB@OR6t}YbEnzK4w!}VhZXEWcw}Sg4VR$66WU?gPIqYd}r1sRe zYWsTo{078zeD;Qu{}PGGRPnTVZ*F)xu)=Jag%05?%pwstE?)Wjgzag#zN6 zQ5Xm4_((}b*Cb;@i)%{mA0IEOkkUzrk}k#LiU~&O1}E#c_b3lo-;|BB+Cy9D@kI?c zMP1q_un$B)pT><4u>FoGxcp7z(X2Z5vClOo#br<{d+M!_oo79HpRJ%jm%`zSZ;e}j z@NmxAL0XH@I%O^ZzBfvF}qooAdxM?XaO^NOd30({(zO+oNuGV#vsJ<AL!pdl5!oC zdedfWSNirqpQiE>)o*= z$5kfNQ7I?#q($B4W=Ru<22}tC7GjLb6b$d1Xf@hgCDZEr)A1vgPi)MOD;$ki>3$2A z@n_e)=(Lh2-7v~8@Foo-s&P4}KmV*X%NBG{A0K7zU;I|gYpHH78$}(ZvlFlOec3zU z%75q8A$dC%+W|f^zuS_YtLY=ntc|AkK-qr_zWP;7h)_Hb((0%Fi(N^T{ zRD4l-BGBn3HZy@LTKG^_DUszpw(BmL7DrTouUp^cnBTR_;C4&5l8VG6PQ-cmXDUc_I2?m#WU z3?&!2J3OzuXLax{!M{{;+h?~KLl(BEUsSAnKYWy}X6qX^Xt|X(F6KlDDZQUA$iEav zY9e5co^`2<$)-urz$V`bsScp&B zv2NZ5CP)y7(etmCbXaoO{bz_7ZE(u@F`XSEJ$a)mIJ-Y)AxGC*LV z9c2KjrzYcID5Hf(L3KNRr`7pzv-`tiC*RJ&)%hH_xiY_ALy_K6+xK0s%6MjWzl`&2 z+|kL_O4Hp=J+I&0`q;XwD_s0Na3^y_Jz=JKsg)%kg0_1=`-;}Oen7as-##k$+wXKdcBe_=FX3}BtRvW8x@LD(8EKU605WV-9@)N}Rp zm7QJ1I9l$fU#3gms7QA1UG#%ugyVEf zRUNV3V}WmD1z8E3UXBuHdmI;kqU@oLg%oJXN9*|X z>By&<&W91{&9ES_Fg>`3#tWTzd34<;Azbi`+K;=CGzo+yNyaTFTCQ8?qc)D&n3|a+ zHZXy0Gpypq znvL}!Cl75_Yh}3ZzWjA8vFjJSSewHlGnqhl65o=)c`0GdruRZ!i}etFu`38F22)N( zPWl+yeE(&McAX6}!dyeAptVUa^*K3@tE={F)5kPI*m{yy{8!_)FS~}~rn*B!Ygdtv z(7E;weT^FH?~_OxuXg&_s{O0gtW897eHjZ8&~L#;n_3%Lb5jO0ZMxNGRem@QGer$( zj!1`?d46nB4w67&LWoOtYDT6^H2sUlZ9#iphfQfcC!y-S1IgFP4+)E zL3xd+rw2X$B*53PtFdkI`X0J2cYRG)^KC8M+8s1D7mqI>j6+pg)#$+1X?o4Q?0k>t zx@biDYIm&2#pdDJ8pLa2urdu+T6}=)_D;9E>Ii z-An3n!(UkHb6V`b%O=Bl)^bAc`Z;y-=9%e7L!A8u^uj2$MF0Jst7)VE?b|!eGl}uq zF%iKsZAzIn`aHdh=2De}^;bcrr~O2dtLOFb+ zulDWORdz_|V=ce+b&d9f59_!0N%jubhTG*P$_5mbse2N-8D#lS;Te%LnOoW+9_&3j zQ(-L%J)W^yR;`5Iiob|!T+U`|h75XqV^Fx};MIBwoj=+FT4oTqW4q13hc&a0%9N^# z7pd(=)a!LQPy1DI&qa6tEiMx7_jk*J7f?*(@S2%S0jBA!*h0=1P!M`b6s+7}VZt3K zF9Hfen7yX_V*_A?*VZ?;yV^CbpRYFCA7R;`KUS-IMG7AuMGL-^bXV{XS9W@SHb1)J(4}#PgFJZ< z`g*6rf&k~&X5O7Qm`QuiqI_JxH$CSM5F`mv{bfy-ySmV*zG8|SO>e<$nLiBV=_S<8 z&vc7yYo^e!Pfpd(UqSvEQ&o!-ulmK2hLkPW+?d->ksilm_=MZ~Q)DOsNEDIMP|Ka> zaPFAt$C5MmFd?W6?noBN3e^M7ofv?QN+7Qrh1|2^?Y(}_r8-BOyqwnrqt(pY+63sV zNJ^u=_lzsyp_(p0orN!6hS3kzUz?p_=@9VLF!Xb5QNoL)_I1`UbTsz;tx#9Y?P;P` zJ#M}Psc$_pX1`~-xEm*vu7owK`$iH8(g9zHS`IE-olUmN3$=H zDKmOV=s$Pu_+rU|xKTV$kGYb*OZ}mB<6q5iU$L+C!Ed{gYM!gs_3{2F7j z5uNn*H{B!-KjD2ivv0Iqa1etvm<)dRRQuu;kXF)DLE+Jb>2nK?tgUFA?fs_i!%A7` zj_fe_{OEjMlN`~*Maz~9Eps>O*ImEVb=JG;Ewk;oC9!a7#w!1e9mf_9?L18UmgdGW z1!AP>E4|bN*LfQU%T=}1vG`1B&BwIuR($s@X$`&OIHfuhCnM!PK~qR+DP%Wbq5=Pi z{PQ@|)lOGj7T8aCZd24wqccV`rc!kiq_0yjVwDhuyOivL-AG$MX8Op@Pnas>t~95% zs}zm&Ora?x_BgXCzqPbFe)#MamafpqyqFZ56YRMEJZ3@lDX@k%Liy;@y~M39?p5W( zDeq{(+-~EUZGamId*b%FVcw&Mnp#>y&5NjvSln+#^TME-(PFkbRKz`=;=cfLj29PJ zgUBs|MTQM;C#7VkPn?*d8R0@a{2mDRs5qeZmEr+Ih3T30&?1(5RZae!J}0Dh!k{wL z78Vyb5}E3J(5_kRw__XO))Z|jRmMd3fJF5lyXO;!!;UapVR+Qi9#<%J&PVk_i^`zr zpwUgb1HT|y_nFlDJYZ5bHWf-~Tby!Y0`)hac@Y;M`7CF3@`08SdKe4-u~FPb2KB1s zc5!ycV^1q$av!6z*8DD|zO_M5^mW2j_)sJ!-v6J zEk&Q|#(k$Wr?x{gn;aY_BcG-mzc|!U3OwvmR(IvRJUk*fGX5PX#0mN$d1MhmP za`on?tG;1kgWKJslaGs)xo(>@EZ1qTqUx#YC^2@==G!BiY#Sf9VtU%nWc`JgYsqat z_bR02^Np8(RCz*L8+wTkzU=jQOuU%~Mybeji7|;vPTG{a)TR2FQKe1R+1LVl7{P1Kr803zZ~GL0;+LI&t5u;G3s$l##wOE8eS0cFRt z0jt*adxsZ`*3q|*GUTNZblq=eN?wtfr0o;er?6m@eopGksTTVXxc|EP>W;IEtgyw) z7Q|-<1W?;qCu^VDU9YlQo^6N6#=5b#fsb~!0&RXP%D6Of^bSpb^}DNU%r^!8#lL;C z@$xQqhOeS~TRK^yNRmaOh#t1FcMSgymFktIFJJm!v<47FYH8| zHizD;(=O;d5J|S&)SX$J7N={X8Yah>ESt_d)UOqN(q8<2rPpb_gG2LyFs1OZq^mKVB;qKiQZXR$vXn>J2S!G*tGipv*1^j+?q9ZC6ifS6&?kt&>MGIX%fD#WTu=y`*N-)oFjh;?Nup`1A{ z&+W4tg>$F9Yz{R-lZp9sLwTMqdjrnn!efS#z_zj{51_lm-38~Yp7av#e{YGso9&$G zN|Teicgh}6tzTUvsV`~J#Ile4?iSxO5K8`xj%WoMbB9{y6?wu7NhHp^=vqZyUB9I5 zZ96*NP{>^jFYTbKqPv1=vnnd#kgy;JITZqNEl>YAD9KAReNYHqqpiB;W5kzmIQ7x2a6$z2@}iIr#ak?WJYWUc3uXD8LrO&2W0vNO7Ilrk>5U8w2Uzh&a@ z^80eHUd~(O)0Cxokg6;7Y8fsm@$w$*=<1+6*%xtlywxU%Bc_-G&tmxLEXbQt);?p( zX;Vt7kke^U#W0cnVgJw#)m=%u>3+`dw)H!34q7;(;1`CBuo*|290M5x` zq!Q_@5~d1@8MFdYCIk%OpW+gP@##upOyRhEE5*n#d~BK-M9`Td%nQZMsxBgnYrY>g z=eg`OYS$Zlk6YXC&H10H&C9nv-IqGs+R`>sa=3}4vMJ?=gi#2id1r;RgE*KMB#IVQ z13j1piS!4eb<>mnyWiQdl*`L{N`_?jSS5wFQ|ll1?^^lqumu`q z*&6I;UX?p@xGt`FquFl1a*@Wsq%YRAe^&ORC~Urs{S%%Sf|k#idB2hjQ9A7B}Mtkv@PT+_(!v>2AxJ~D(NHG_9y&hw!>2HO3z+pAC_+b4T8jZdPAUsK6T zl#=*PA2`yuZ?bRlIHE>GmTtMn?quTR#;VXSFe-Y-yI7$lrpCux>GpI5R|y(TI8$}I z;EX0NoHt05FX&;4=JCQF-Yf0-7xB?|wv?|x%I7KdhWqvo5@J1C8dWkwzIZ;5!BMWz zneE9-ya@lpu+72nDc?&teoMT@-SgDfy*rOW(B(E7bWnZ&*yH7}6BLOuC8c^KLFGnCWmZ)& zvUp=a1E12LqWJzv^Cp_ms^dM1B4pX;v_PWqM4F1NJ8 zoovsW5Jh#uC0dVc$3+iG>-`r&oFM+Wyf+gt0gml$3PgXOWBUKJn`z8z|7$8SHzF%2 z>>vEC0o2leuAt^}MewD`M@3+*q8#9?{uD@dsCYz@k5M789dj)d`FyX$%1Wrg68wp0 zdW@G=Bf2eY$~OKrSecBtJLT>lbQk0hfS5EIB9Ros@+pn_)vahmhe;)wI@^ z3u7VV4vu9jmPK1KZpt2-ub3d8*uBvAqblAHTmr(ef$BOtyxJ)=(|(Lgf_kv z8I?+tK27ZpiF0k5TCopInrX6a>V366{BvBIT1u`Hr_!#liMiaQ8|_q1-t;*78I

A2FNH`4p_vD^GO&KA%+ymsts1imY{pEW? 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�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 B{{rj9Ut>5gipmP+t-szd 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 Xrh%{C5`8>-+wU4rH;tp Date: Thu, 10 Mar 2011 15:35:09 -0500 Subject: [PATCH 154/154] chlog: freetdm: ss7 - more work on relay stability --- .../ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c | 12 +- .../ftmod_sangoma_ss7_cntrl.c | 32 ++++ .../ftmod_sangoma_ss7_handle.c | 13 +- .../ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c | 98 ++++++++++-- .../ftmod_sangoma_ss7_main.c | 55 +++---- .../ftmod_sangoma_ss7_main.h | 44 +++++- .../ftmod_sangoma_ss7_relay.c | 62 ++++++-- .../ftmod_sangoma_ss7_support.c | 142 +++++++++++++++++- 8 files changed, 387 insertions(+), 71 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c index f8b12ea588..2cd3a13e30 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c @@ -1011,6 +1011,15 @@ static ftdm_status_t handle_show_flags(ftdm_stream_handle_t *stream, int span, i } } + for (bit = 0; bit < 33; bit++) { + if (ss7_info->blk_flags & ( 0x1 << bit)) { + stream->write_function(stream, "|"); + flag = bit; + text = ftmod_ss7_blk_flag2str(flag); + stream->write_function(stream, "%s",text); + } + } + stream->write_function(stream, "\n"); } /* if ( span and chan) */ @@ -1172,7 +1181,8 @@ static ftdm_status_t handle_show_status(ftdm_stream_handle_t *stream, int span, ftdm_channel_state2str(ftdmchan->state)); if ((sngss7_test_ckt_blk_flag(ss7_info, FLAG_CKT_MN_BLOCK_TX)) || - (sngss7_test_ckt_blk_flag(ss7_info, FLAG_GRP_MN_BLOCK_TX))) { + (sngss7_test_ckt_blk_flag(ss7_info, FLAG_GRP_MN_BLOCK_TX)) || + (sngss7_test_ckt_blk_flag(ss7_info, FLAG_CKT_LC_BLOCK_RX))) { stream->write_function(stream, "l_mn=Y|"); }else { stream->write_function(stream, "l_mn=N|"); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c index 83ef5e0a42..8a7b095675 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cntrl.c @@ -76,6 +76,7 @@ int ftmod_ss7_enable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_disable_grp_mtp2Link(uint32_t procId); int ftmod_ss7_block_isup_ckt(uint32_t cktId); +int ftmod_ss7_unblock_isup_ckt(uint32_t cktId); /******************************************************************************/ /* FUNCTIONS ******************************************************************/ @@ -875,6 +876,37 @@ int ftmod_ss7_block_isup_ckt(uint32_t cktId) return (sng_cntrl_isup(&pst, &cntrl)); } +/******************************************************************************/ +int ftmod_ss7_unblock_isup_ckt(uint32_t cktId) +{ + SiMngmt cntrl; + Pst pst; + + /* initalize the post structure */ + smPstInit(&pst); + + /* insert the destination Entity */ + pst.dstEnt = ENTSI; + + /* initalize the control structure */ + memset(&cntrl, 0x0, sizeof(SiMngmt)); + + /* initalize the control header */ + smHdrInit(&cntrl.hdr); + + cntrl.hdr.msgType = TCNTRL; /* this is a control request */ + cntrl.hdr.entId.ent = ENTSI; + cntrl.hdr.entId.inst = S_INST; + cntrl.hdr.elmId.elmnt = STICIR; + + cntrl.t.cntrl.s.siElmnt.elmntId.circuit = cktId; + cntrl.t.cntrl.s.siElmnt.elmntParam.cir.flag = LSI_CNTRL_CIR_FORCE; + + cntrl.t.cntrl.action = AENA; /* unblock via UBL */ + cntrl.t.cntrl.subAction = SAELMNT; /* specificed element */ + + return (sng_cntrl_isup(&pst, &cntrl)); +} /******************************************************************************/ /* For Emacs: * Local Variables: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c index e0b6ae08c0..3981cd6597 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c @@ -1183,7 +1183,7 @@ ftdm_status_t handle_pause(uint32_t suInstId, uint32_t spInstId, uint32_t circui sngss7_set_ckt_flag(sngss7_info, FLAG_INFID_PAUSED); /* clear the resume flag on the channel */ - sngss7_set_ckt_flag(sngss7_info, FLAG_INFID_RESUME); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INFID_RESUME); } /* unlock the channel again before we exit */ @@ -1565,9 +1565,6 @@ ftdm_status_t handle_ubl_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ /* throw the unblock flag */ sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_UNBLK_RX); - /* clear the block flag */ - sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX); - /* set the channel to suspended state */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); @@ -2017,12 +2014,12 @@ ftdm_status_t handle_local_ubl(uint32_t suInstId, uint32_t spInstId, uint32_t ci /* lock the channel */ ftdm_mutex_lock(ftdmchan->mutex); - /* check if the circuit is already blocked or not */ - if (sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX)) { - SS7_WARN("Received local UBL on circuit that is already unblocked!\n"); + /* check if the circuit is blocked or not */ + if (!sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_BLOCK_RX)) { + SS7_WARN("Received local UBL on circuit that is not blocked!\n"); } - /* throw the ckt block flag */ + /* throw the ckt unblock flag */ sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX); /* set the channel to suspended state */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c index 76984b907b..f143c20eb9 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c @@ -66,6 +66,12 @@ void sngss7_con_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiCo ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -104,6 +110,12 @@ void sngss7_con_cfm(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiCo ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -142,6 +154,12 @@ void sngss7_con_sta(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiCn ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -181,6 +199,12 @@ void sngss7_rel_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiRe ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -219,6 +243,12 @@ void sngss7_rel_cfm(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiRe ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -257,6 +287,12 @@ void sngss7_dat_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiIn ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -295,6 +331,12 @@ void sngss7_fac_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, uint ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -334,6 +376,12 @@ void sngss7_fac_cfm(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, uint ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -373,6 +421,12 @@ void sngss7_umsg_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit) ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -417,21 +471,10 @@ void sngss7_sta_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, uint /**************************************************************************/ case (SIT_STA_PAUSEIND): case (SIT_STA_RESUMEIND): - - /* the circuit for this type of event may not exist on the local system - * so first check if the circuit is local - */ - if ((circuit >= (g_ftdm_sngss7_data.cfg.procId * 1000)) && - (circuit < ((g_ftdm_sngss7_data.cfg.procId +1) * 1000))) { - - /* the circuit is on the local system, handle normally */ - goto sta_ind_local; - } - - /* the circuit is not local, so find a local circuit with the same intfId - * by finding the orginial circuit in our array first, finding the intfId - * from there, then go through the local circuits to see if we find a - * match and use that circuit instead + /* the circuit may or may not be on the local system so we have to find + * circuit with the same intfId. The circuit specified might also be + * a non-voice cic so we also need to find the first voice cic on this + * system with the same intfId. */ intfId = g_ftdm_sngss7_data.cfg.isupCkt[circuit].infId; @@ -444,6 +487,10 @@ void sngss7_sta_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, uint /* compare the intfIds */ if (g_ftdm_sngss7_data.cfg.isupCkt[x].infId == intfId) { + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type == VOICE) { + + } + /* we have a match, setup the pointers to the correct values */ circuit = x; @@ -472,7 +519,14 @@ move_along: break; /**************************************************************************/ default: -sta_ind_local: + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx %s on circuit that is not a voice CIC (%d)\n", + DECODE_LCC_EVENT(evntType), + g_ftdm_sngss7_data.cfg.isupCkt[circuit].cic); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -520,6 +574,12 @@ void sngss7_susp_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiS ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); @@ -561,6 +621,12 @@ void sngss7_resm_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiR ftdm_channel_t *ftdmchan = NULL; sngss7_event_data_t *sngss7_event = NULL; + if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].type != VOICE) { + SS7_ERROR("Rx sig event on circuit that is not a voice CIC (%d)\n", circuit); + SS7_FUNC_TRACE_EXIT(__FUNCTION__); + return; + } + /* get the ftdmchan and ss7_chan_data from the circuit */ if (extract_chan_data(circuit, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", circuit); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index 7b885102b9..46bc53ec57 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -909,8 +909,8 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) case FTDM_CHANNEL_STATE_RESTART: /* CICs needs a Reset */ if (sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_UCIC_BLOCK)) { - if ((sngss7_test_ckt_blk_flag(sngss7_info, FLAG_RESET_RX)) || - (sngss7_test_ckt_blk_flag(sngss7_info, FLAG_GRP_RESET_RX))) { + if ((sngss7_test_ckt_flag(sngss7_info, FLAG_RESET_RX)) || + (sngss7_test_ckt_flag(sngss7_info, FLAG_GRP_RESET_RX))) { SS7_DEBUG_CHAN(ftdmchan,"Incoming Reset request on CIC in UCIC block, removing UCIC block%s\n", ""); @@ -1020,10 +1020,13 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) /**************************************************************************/ case FTDM_CHANNEL_STATE_SUSPENDED: /* circuit has been blocked */ - SS7_DEBUG_CHAN(ftdmchan,"Current flags: 0x%X\n", sngss7_info->ckt_flags); + SS7_DEBUG_CHAN(ftdmchan,"Current flags: ckt=0x%X, blk=0x%X\n", + sngss7_info->ckt_flags, + sngss7_info->blk_flags); /**********************************************************************/ if (sngss7_test_ckt_flag(sngss7_info, FLAG_INFID_RESUME)) { + SS7_DEBUG_CHAN(ftdmchan, "Processing RESUME%s\n", ""); /* clear the RESUME flag */ @@ -1038,27 +1041,23 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) (sngss7_test_ckt_flag (sngss7_info, FLAG_GRP_RESET_TX)) || (sngss7_test_ckt_flag (sngss7_info, FLAG_GRP_RESET_RX))) { - /* go back to the reset state */ + /* don't bring up the sig status but also move to reset */ goto suspend_goto_restart; } else { - /* bring the sig status back up */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_UP); } - - /* go back to the last state */ - goto suspend_goto_last; } /* if (sngss7_test_flag(sngss7_info, FLAG_INFID_RESUME)) */ - if (sngss7_test_ckt_flag(sngss7_info, FLAG_INFID_PAUSED)) { + if ((sngss7_test_ckt_flag(sngss7_info, FLAG_INFID_PAUSED)) && + (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP))) { + SS7_DEBUG_CHAN(ftdmchan, "Processing PAUSE%s\n", ""); /* bring the sig status down */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_DOWN); - - /* go back to the last state */ - goto suspend_goto_last; } /* if (sngss7_test_ckt_flag(sngss7_info, FLAG_INFID_PAUSED)) { */ + /**********************************************************************/ if (sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX) && !sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX_DN)) { @@ -1081,6 +1080,10 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) !sngss7_test_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_UNBLK_RX_DN)){ SS7_DEBUG_CHAN(ftdmchan, "Processing CKT_MN_UNBLK_RX flag %s\n", ""); + /* clear the block flags */ + sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX); + sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX_DN); + /* clear the unblock flag */ sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_UNBLK_RX); @@ -1090,9 +1093,6 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) /* send a uba */ ft_to_sngss7_uba (ftdmchan); - /* throw the done flag */ - sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_UNBLK_RX_DN); - /* check the last state and return to it to allow the call to finish */ goto suspend_goto_last; } @@ -1119,8 +1119,12 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) !sngss7_test_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_UNBLK_TX_DN)){ SS7_DEBUG_CHAN(ftdmchan, "Processing CKT_MN_UNBLK_TX flag %s\n", ""); + /* clear the block flags */ + sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_BLOCK_TX); + sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_BLOCK_TX_DN); + /* clear the unblock flag */ - sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_MN_UNBLK_TX); + sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_UNBLK_TX); /* bring the sig status up */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_UP); @@ -1128,9 +1132,6 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) /* send a ubl */ ft_to_sngss7_ubl (ftdmchan); - /* throw the done flag */ - sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_UNBLK_TX_DN); - /* check the last state and return to it to allow the call to finish */ goto suspend_goto_last; } @@ -1153,15 +1154,17 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) if (sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX) && !sngss7_test_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX_DN)) { SS7_DEBUG_CHAN(ftdmchan, "Processing CKT_LC_UNBLK_RX flag %s\n", ""); + + /* clear the block flags */ + sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_LC_BLOCK_RX); + sngss7_clear_ckt_blk_flag (sngss7_info, FLAG_CKT_LC_BLOCK_RX_DN); /* clear the unblock flag */ - sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_UNBLK_RX); + sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX); /* send a uba */ /*ft_to_sngss7_uba(ftdmchan);*/ - /* throw the done flag */ - sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_UNBLK_RX_DN); /* check the last state and return to it to allow the call to finish */ goto suspend_goto_last; @@ -1191,10 +1194,11 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) if (sngss7_test_ckt_blk_flag (sngss7_info, FLAG_CKT_UCIC_UNBLK) && !sngss7_test_ckt_blk_flag (sngss7_info, FLAG_CKT_UCIC_UNBLK_DN)) { - SS7_DEBUG_CHAN(ftdmchan, "Processing CKT_UCIC_UNBLK flag %s\n", "");; + SS7_DEBUG_CHAN(ftdmchan, "Processing CKT_UCIC_UNBLK flag %s\n", ""); /* remove the UCIC block flag */ sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_UCIC_BLOCK); + sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_UCIC_BLOCK_DN); /* remove the UCIC unblock flag */ sngss7_clear_ckt_blk_flag(sngss7_info, FLAG_CKT_UCIC_UNBLK); @@ -1202,13 +1206,12 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) /* throw the channel into reset to sync states */ sngss7_set_ckt_flag(sngss7_info, FLAG_RESET_TX); - /* throw the done flag */ - sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_UCIC_UNBLK_DN); - /* bring the channel into restart again */ goto suspend_goto_restart; } + SS7_ERROR_CHAN(ftdmchan,"No block flag processed!%s\n", ""); + suspend_goto_last: state_flag = 0; ftdm_set_state(ftdmchan, ftdmchan->last_state); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h index 02c450b94a..df8b086c49 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h @@ -560,6 +560,41 @@ typedef enum { FLAG_GRP_MN_UNBLK_TX_DN = (1 << 27) } sng_ckt_block_flag_t; +#define BLK_FLAGS_STRING \ + "UCIC BLK", \ + "UCIC BLK DN", \ + "UCIC UNBLK", \ + "UCIC UNBLK DN", \ + "RX LC BLK", \ + "RX LC BLK DN", \ + "RX LC UNBLK", \ + "RX LC UNBLK DN", \ + "RX CKT BLK", \ + "RX CKT BLK DN", \ + "RX CKT UNBLK", \ + "RX CKT UNBLK DN", \ + "TX CKT BLK", \ + "TX CKT BLK DN", \ + "TX CKT UNBLK", \ + "TX CKT UNBLK DN", \ + "RX GRP MN BLK", \ + "RX GRP MN BLK DN", \ + "RX GRP MN UNBLK", \ + "RX GRP MN UNBLK DN", \ + "TX GRP MN BLK", \ + "TX GRP MN BLK DN", \ + "TX GRP MN UNBLK", \ + "TX GRP MN UNBLK DN", \ + "RX GRP HW BLK", \ + "RX GRP HW BLK DN", \ + "RX GRP HW UNBLK", \ + "RX GRP HW UNBLK DN", \ + "TX GRP HW BLK", \ + "TX GRP HW BLK DN", \ + "TX GRP HW UNBLK", \ + "TX GRP HW UNBLK DN" +FTDM_STR2ENUM_P(ftmod_ss7_blk_state2flag, ftmod_ss7_blk_flag2str, sng_ckt_block_flag_t) + /* valid for every cfg array except circuits */ typedef enum { SNGSS7_CONFIGURED = (1 << 0), @@ -656,7 +691,7 @@ int ftmod_ss7_enable_grp_mtp3Link(uint32_t procId); int ftmod_ss7_disable_grp_mtp2Link(uint32_t procId); int ftmod_ss7_block_isup_ckt(uint32_t cktId); - +int ftmod_ss7_unblock_isup_ckt(uint32_t cktId); /* in ftmod_sangoma_ss7_sta.c */ @@ -795,6 +830,13 @@ void handle_isup_t35(void *userdata); /******************************************************************************/ /* MACROS *********************************************************************/ +#define SS7_STATE_CHANGE(ftdmchan, new_state) \ +if (ftdmchan->state == new_state) { \ + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_IDLE); \ +} else { \ + ftdm_set_state(ftdmchan, new_state); \ +} + #define SS7_DEBUG(a,...) ftdm_log(FTDM_LOG_DEBUG,a , ##__VA_ARGS__ ); #define SS7_INFO(a,...) ftdm_log(FTDM_LOG_INFO,a , ##__VA_ARGS__ ); #define SS7_WARN(a,...) ftdm_log(FTDM_LOG_WARNING,a , ##__VA_ARGS__ ); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c index d34d62c217..1af538d727 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_relay.c @@ -45,10 +45,11 @@ ftdm_status_t handle_relay_connect(RyMngmt *sta); ftdm_status_t handle_relay_disconnect(RyMngmt *sta); -static ftdm_status_t enable_all_ckts_for_relay(void); +/*static ftdm_status_t enable_all_ckts_for_relay(void);*/ static ftdm_status_t reconfig_all_ckts_for_relay(void); static ftdm_status_t disable_all_ckts_for_relay(void); static ftdm_status_t block_all_ckts_for_relay(uint32_t procId); +static ftdm_status_t unblock_all_ckts_for_relay(uint32_t procId); static ftdm_status_t disable_all_sigs_for_relay(uint32_t procId); static ftdm_status_t disble_all_mtp2_sigs_for_relay(void); /******************************************************************************/ @@ -72,26 +73,20 @@ ftdm_status_t handle_relay_connect(RyMngmt *sta) switch (sng_relay->type) { /******************************************************************/ case (LRY_CT_TCP_CLIENT): - /* check the status of all isup intfs in case we weren't connected when - * the interface became active - */ - check_status_of_all_isup_intf(); - /* reconfigure all ISUP ckts, since the main system would have lost all configs */ if (reconfig_all_ckts_for_relay()) { SS7_ERROR("Failed to reconfigure ISUP Ckts!\n"); /* we're done....this is very bad! */ - } else { - /* if the circuits reconfiged then bring then back up */ - enable_all_ckts_for_relay(); } - break; /******************************************************************/ case (LRY_CT_TCP_SERVER): - /*unblock_all_ckts_for_relay(sta->t.usta.s.ryErrUsta.errPid);*/ + /* bring the sig links on the client system back up */ ftmod_ss7_enable_grp_mtp3Link(sta->t.usta.s.ryUpUsta.id); - + + /* unbloock the ckts on the client system */ + unblock_all_ckts_for_relay(sta->t.usta.s.ryUpUsta.id); + break; /******************************************************************/ default: @@ -179,7 +174,7 @@ ftdm_status_t disable_all_ckts_for_relay(void) return FTDM_SUCCESS; } - +#if 0 /******************************************************************************/ ftdm_status_t enable_all_ckts_for_relay(void) { @@ -227,7 +222,7 @@ ftdm_status_t enable_all_ckts_for_relay(void) return FTDM_SUCCESS; } - +#endif /******************************************************************************/ ftdm_status_t reconfig_all_ckts_for_relay(void) { @@ -244,6 +239,9 @@ ftdm_status_t reconfig_all_ckts_for_relay(void) /* mark the circuit for re-configuration */ sngss7_set_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG); + + /* clear the relay flag */ + sngss7_clear_ckt_flag(sngss7_info, FLAG_RELAY_DOWN); } /* move to the next circuit */ @@ -312,6 +310,42 @@ ftdm_status_t disble_all_mtp2_sigs_for_relay(void) } +/******************************************************************************/ +static ftdm_status_t unblock_all_ckts_for_relay(uint32_t procId) +{ + int x; + int ret; + + /* we just got connection to this procId, send out a unblock for all these circuits + * since we blocked them when we lost the connection + */ + x = (procId * 1000) + 1; + while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) { + /**************************************************************************/ + if (g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) { + + /* send a block request via stack manager */ + ret = ftmod_ss7_unblock_isup_ckt(g_ftdm_sngss7_data.cfg.isupCkt[x].id); + if (ret) { + SS7_INFO("Successfully unblocked CIC:%d(ckt:%d) due to Relay connection\n", + g_ftdm_sngss7_data.cfg.isupCkt[x].cic, + g_ftdm_sngss7_data.cfg.isupCkt[x].id); + } else { + SS7_ERROR("Failed to unblock CIC:%d(ckt:%d) due to Relay connection\n", + g_ftdm_sngss7_data.cfg.isupCkt[x].cic, + g_ftdm_sngss7_data.cfg.isupCkt[x].id); + } + + } /* if (g_ftdm_sngss7_data.cfg.isupCkt[x].type == VOICE) */ + + /* move along */ + x++; + /**************************************************************************/ + } /* while (g_ftdm_sngss7_data.cfg.isupCkt[x].id != 0) */ + + return FTDM_SUCCESS; +} + /******************************************************************************/ /* For Emacs: * Local Variables: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c index ded695c205..2b915b1053 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c @@ -94,6 +94,9 @@ ftdm_status_t sngss7_add_raw_data(sngss7_chan_data_t *sngss7_info, uint8_t* data FTDM_ENUM_NAMES(CKT_FLAGS_NAMES, CKT_FLAGS_STRING) FTDM_STR2ENUM(ftmod_ss7_ckt_state2flag, ftmod_ss7_ckt_flag2str, sng_ckt_flag_t, CKT_FLAGS_NAMES, 31) +FTDM_ENUM_NAMES(BLK_FLAGS_NAMES, BLK_FLAGS_STRING) +FTDM_STR2ENUM(ftmod_ss7_blk_state2flag, ftmod_ss7_blk_flag2str, sng_ckt_block_flag_t, BLK_FLAGS_NAMES, 31) + /* FUNCTIONS ******************************************************************/ uint8_t copy_cgPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum) { @@ -1484,6 +1487,11 @@ ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan) { ftdm_channel_t *ftdmchan = NULL; sngss7_chan_data_t *sngss7_info = NULL; + sng_isup_inf_t *sngss7_intf = NULL; + uint8_t state; + uint8_t bits_ab = 0; + uint8_t bits_cd = 0; + uint8_t bits_ef = 0; int x; int ret; @@ -1494,7 +1502,7 @@ ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan) /* if the call data is NULL move on */ if (ftdmchan->call_data == NULL) { - SS7_WARN_CHAN(ftdmchan, "Reconfiguring channel that has not call_data!%s\n", " "); + SS7_WARN_CHAN(ftdmchan, "Found ftdmchan with no sig module data!%s\n", " "); continue; } @@ -1503,12 +1511,136 @@ ftdm_status_t check_for_reconfig_flag(ftdm_span_t *ftdmspan) /* check the reconfig flag */ if (sngss7_test_ckt_flag(sngss7_info, FLAG_CKT_RECONFIG)) { - ret = ftmod_ss7_isup_ckt_config(sngss7_info->circuit->id); + /* confirm the state of all isup interfaces*/ + check_status_of_all_isup_intf(); - if (ret) { - SS7_CRITICAL("ISUP CKT %d re-configuration FAILED!\n", x); + sngss7_intf = &g_ftdm_sngss7_data.cfg.isupIntf[sngss7_info->circuit->infId]; + + /* check if the interface is paused or resumed */ + if (sngss7_test_flag(sngss7_intf, SNGSS7_PAUSED)) { + SS7_DEBUG_CHAN(ftdmchan, "ISUP intf %d is PAUSED\n", sngss7_intf->id); + /* throw the pause flag */ + sngss7_clear_ckt_flag(sngss7_info, FLAG_INFID_RESUME); + sngss7_set_ckt_flag(sngss7_info, FLAG_INFID_PAUSED); } else { - SS7_INFO("ISUP CKT %d re-configuration DONE!\n", x); + SS7_DEBUG_CHAN(ftdmchan, "ISUP intf %d is RESUMED\n", sngss7_intf->id); + /* throw the resume flag */ + sngss7_clear_ckt_flag(sngss7_info, FLAG_INFID_PAUSED); + sngss7_set_ckt_flag(sngss7_info, FLAG_INFID_RESUME); + } + + /* query for the status of the ckt */ + if (ftmod_ss7_isup_ckt_sta(sngss7_info->circuit->id, &state)) { + SS7_ERROR("Failed to read isup ckt = %d status\n", sngss7_info->circuit->id); + continue; + } + + /* extract the bit sections */ + bits_ab = (state & (SNG_BIT_A + SNG_BIT_B)) >> 0; + bits_cd = (state & (SNG_BIT_C + SNG_BIT_D)) >> 2; + bits_ef = (state & (SNG_BIT_E + SNG_BIT_F)) >> 4; + + if (bits_cd == 0x0) { + /* check if circuit is UCIC or transient */ + if (bits_ab == 0x3) { + /* bit a and bit b are set, unequipped */ + ret = ftmod_ss7_isup_ckt_config(sngss7_info->circuit->id); + if (ret) { + SS7_CRITICAL("ISUP CKT %d re-configuration FAILED!\n",x); + } else { + SS7_INFO("ISUP CKT %d re-configuration DONE!\n", x); + } + + /* reset the circuit to sync states */ + ftdm_mutex_lock(ftdmchan->mutex); + + /* flag the circuit as active */ + sngss7_set_flag(sngss7_info->circuit, SNGSS7_ACTIVE); + + /* throw the channel into reset */ + sngss7_set_ckt_flag(sngss7_info, FLAG_RESET_TX); + + /* throw the channel to suspend */ + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + + /* unlock the channel */ + ftdm_mutex_unlock(ftdmchan->mutex); + + } /* if (bits_ab == 0x3) */ + } else { + /* check the maintenance block status in bits A and B */ + switch (bits_ab) { + /**************************************************************************/ + case (0): + /* no maintenace block...do nothing */ + break; + /**************************************************************************/ + case (1): + /* locally blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_BLOCK_RX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + case (2): + /* remotely blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + case (3): + /* both locally and remotely blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_LC_BLOCK_RX); + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + default: + break; + /**************************************************************************/ + } /* switch (bits_ab) */ + + /* check the hardware block status in bits e and f */ + switch (bits_ef) { + /**************************************************************************/ + case (0): + /* no maintenace block...do nothing */ + break; + /**************************************************************************/ + case (1): + /* locally blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_GRP_HW_BLOCK_TX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + case (2): + /* remotely blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_GRP_HW_BLOCK_RX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + case (3): + /* both locally and remotely blocked */ + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_GRP_HW_BLOCK_TX); + sngss7_set_ckt_blk_flag(sngss7_info, FLAG_GRP_HW_BLOCK_RX); + + /* set the channel to suspended state */ + SS7_STATE_CHANGE(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + break; + /**************************************************************************/ + default: + break; + /**************************************************************************/ + } /* switch (bits_ef) */ } /* clear the re-config flag ... no matter what */