From 1ffb1ee0d07f3366376e6e2f7e720039684a09d0 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 11 Jun 2014 12:48:46 -0500 Subject: [PATCH] WIP --- build/modules.conf.in | 1 + configure.ac | 1 + src/mod/endpoints/mod_verto/Makefile.am | 38 + src/mod/endpoints/mod_verto/mcast/.gitignore | 2 + src/mod/endpoints/mod_verto/mcast/MCAST.i | 24 + src/mod/endpoints/mod_verto/mcast/MCAST.pm | 108 + src/mod/endpoints/mod_verto/mcast/mcast.c | 173 + src/mod/endpoints/mod_verto/mcast/mcast.h | 78 + .../endpoints/mod_verto/mcast/mcast_cpp.cpp | 69 + src/mod/endpoints/mod_verto/mcast/mcast_cpp.h | 59 + .../endpoints/mod_verto/mcast/mcast_wrap.cpp | 2254 +++++++++ src/mod/endpoints/mod_verto/mcast/perlxsi.c | 16 + src/mod/endpoints/mod_verto/mcast/test.c | 29 + src/mod/endpoints/mod_verto/mcast/test.pl | 19 + src/mod/endpoints/mod_verto/mcast/test2.pl | 23 + .../endpoints/mod_verto/mod_verto.2008.vcproj | 287 ++ .../mod_verto/mod_verto.2010.vcxproj | 131 + .../mod_verto/mod_verto.2012.vcxproj | 135 + src/mod/endpoints/mod_verto/mod_verto.c | 4485 +++++++++++++++++ src/mod/endpoints/mod_verto/mod_verto.h | 272 + src/mod/endpoints/mod_verto/ws.c | 879 ++++ src/mod/endpoints/mod_verto/ws.h | 120 + 22 files changed, 9203 insertions(+) create mode 100644 src/mod/endpoints/mod_verto/Makefile.am create mode 100644 src/mod/endpoints/mod_verto/mcast/.gitignore create mode 100644 src/mod/endpoints/mod_verto/mcast/MCAST.i create mode 100644 src/mod/endpoints/mod_verto/mcast/MCAST.pm create mode 100644 src/mod/endpoints/mod_verto/mcast/mcast.c create mode 100644 src/mod/endpoints/mod_verto/mcast/mcast.h create mode 100644 src/mod/endpoints/mod_verto/mcast/mcast_cpp.cpp create mode 100644 src/mod/endpoints/mod_verto/mcast/mcast_cpp.h create mode 100644 src/mod/endpoints/mod_verto/mcast/mcast_wrap.cpp create mode 100644 src/mod/endpoints/mod_verto/mcast/perlxsi.c create mode 100644 src/mod/endpoints/mod_verto/mcast/test.c create mode 100644 src/mod/endpoints/mod_verto/mcast/test.pl create mode 100644 src/mod/endpoints/mod_verto/mcast/test2.pl create mode 100644 src/mod/endpoints/mod_verto/mod_verto.2008.vcproj create mode 100644 src/mod/endpoints/mod_verto/mod_verto.2010.vcxproj create mode 100644 src/mod/endpoints/mod_verto/mod_verto.2012.vcxproj create mode 100644 src/mod/endpoints/mod_verto/mod_verto.c create mode 100644 src/mod/endpoints/mod_verto/mod_verto.h create mode 100644 src/mod/endpoints/mod_verto/ws.c create mode 100644 src/mod/endpoints/mod_verto/ws.h diff --git a/build/modules.conf.in b/build/modules.conf.in index 34e16be048..7368cc8f01 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -83,6 +83,7 @@ dialplans/mod_dialplan_xml #endpoints/mod_h323 #endpoints/mod_khomp endpoints/mod_rtc +endpoints/mod_verto endpoints/mod_loopback #endpoints/mod_opal #endpoints/mod_portaudio diff --git a/configure.ac b/configure.ac index 4da80f2105..294f6b469d 100644 --- a/configure.ac +++ b/configure.ac @@ -1500,6 +1500,7 @@ AC_CONFIG_FILES([Makefile src/mod/endpoints/mod_sofia/Makefile src/mod/endpoints/mod_unicall/Makefile src/mod/endpoints/mod_rtc/Makefile + src/mod/endpoints/mod_verto/Makefile src/mod/event_handlers/mod_cdr_csv/Makefile src/mod/event_handlers/mod_cdr_mongodb/Makefile src/mod/event_handlers/mod_cdr_pg_csv/Makefile diff --git a/src/mod/endpoints/mod_verto/Makefile.am b/src/mod/endpoints/mod_verto/Makefile.am new file mode 100644 index 0000000000..08b82bd32c --- /dev/null +++ b/src/mod/endpoints/mod_verto/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/build/modmake.rulesam +MODNAME=mod_verto + +mod_LTLIBRARIES = mod_verto.la +mod_verto_la_SOURCES = mod_verto.c ws.c mcast/mcast.c +mod_verto_la_CFLAGS = -D__EXTENSIONS__ -D_GNU_SOURCE $(AM_CFLAGS) +mod_verto_la_CPPFLAGS = -I. -Imcast +mod_verto_la_LIBADD = $(switch_builddir)/libfreeswitch.la +mod_verto_la_LDFLAGS = -avoid-version -module -no-undefined -shared + +if HAVE_PERL +perldir = $(PERL_SITEDIR) +perl_LTLIBRARIES = MCAST.la +MCAST_la_SOURCES = mcast/mcast_wrap.cpp mcast/perlxsi.c mcast/mcast.c mcast/mcast_cpp.cpp +MCAST_la_CFLAGS = $(CC_CFLAGS) $(CFLAGS) $(SWITCH_AM_CFLAGS) $(PERL_CFLAGS) +MCAST_la_CXXFLAGS = $(SWITCH_AM_CXXFLAGS) $(CXXFLAGS) -w $(PERL_INC) +MCAST_la_CPPFLAGS = -I$(switch_srcdir)/src/mod/endpoints/mod_verto/mcast +MCAST_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(PERL_LDFLAGS) + +install-data-local: perlmod-install + +perlmod-install: install-perlLTLIBRARIES + install -m 755 MCAST.pm $(PERL_SITEDIR) +endif + +mcast/esl_wrap.cpp: + cd mcast && swig -module MCAST -shadow -perl5 -c++ -DMULTIPLICITY -I../src/include -o mcast_wrap.cpp ../MCAST.i + +mcast/perlxsi.c: + $(PERL) -MExtUtils::Embed -e xsinit -- -o perlxsi.c + +clean-data-local: + rm -f *.o *.so *~ + +swigclean: + rm -f mcast/mcast_wrap.* mcast/MCAST.so mcast/MCAST.pm mcast/perlxsi.* + +reswig: swigclean mcast/mcast_wrap.cpp mcast/perlxsi.c diff --git a/src/mod/endpoints/mod_verto/mcast/.gitignore b/src/mod/endpoints/mod_verto/mcast/.gitignore new file mode 100644 index 0000000000..9d22eb46a9 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/.gitignore @@ -0,0 +1,2 @@ +*.o +*.so diff --git a/src/mod/endpoints/mod_verto/mcast/MCAST.i b/src/mod/endpoints/mod_verto/mcast/MCAST.i new file mode 100644 index 0000000000..097a52bc77 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/MCAST.i @@ -0,0 +1,24 @@ +%{ +#include "mcast.h" +#include "mcast_cpp.h" +%} + +%newobject McastHANDLE::recv; + +%include "mcast_cpp.h" + +%perlcode %{ +use constant { + MCAST_SEND => (1 << 0), + MCAST_RECV => (1 << 1), + MCAST_TTL_HOST => (1 << 2), + MCAST_TTL_SUBNET => (1 << 3), + MCAST_TTL_SITE => (1 << 4), + MCAST_TTL_REGION => (1 << 5), + MCAST_TTL_CONTINENT => (1 << 6), + MCAST_TTL_UNIVERSE => (1 << 7) +}; +%} + + + diff --git a/src/mod/endpoints/mod_verto/mcast/MCAST.pm b/src/mod/endpoints/mod_verto/mcast/MCAST.pm new file mode 100644 index 0000000000..531665035b --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/MCAST.pm @@ -0,0 +1,108 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 1.3.35 +# +# Don't modify this file, modify the SWIG interface instead. + +package MCAST; +require Exporter; +require DynaLoader; +@ISA = qw(Exporter DynaLoader); +package MCASTc; +bootstrap MCAST; +package MCAST; +@EXPORT = qw( ); + +# ---------- BASE METHODS ------------- + +package MCAST; + +sub TIEHASH { + my ($classname,$obj) = @_; + return bless $obj, $classname; +} + +sub CLEAR { } + +sub FIRSTKEY { } + +sub NEXTKEY { } + +sub FETCH { + my ($self,$field) = @_; + my $member_func = "swig_${field}_get"; + $self->$member_func(); +} + +sub STORE { + my ($self,$field,$newval) = @_; + my $member_func = "swig_${field}_set"; + $self->$member_func($newval); +} + +sub this { + my $ptr = shift; + return tied(%$ptr); +} + + +# ------- FUNCTION WRAPPERS -------- + +package MCAST; + + +############# Class : MCAST::McastHandle ############## + +package MCAST::McastHandle; +use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS); +@ISA = qw( MCAST ); +%OWNER = (); +%ITERATORS = (); +sub new { + my $pkg = shift; + my $self = MCASTc::new_McastHandle(@_); + bless $self, $pkg if defined($self); +} + +sub DESTROY { + return unless $_[0]->isa('HASH'); + my $self = tied(%{$_[0]}); + return unless defined $self; + delete $ITERATORS{$self}; + if (exists $OWNER{$self}) { + MCASTc::delete_McastHandle($self); + delete $OWNER{$self}; + } +} + +*send = *MCASTc::McastHandle_send; +*recv = *MCASTc::McastHandle_recv; +*fileno = *MCASTc::McastHandle_fileno; +sub DISOWN { + my $self = shift; + my $ptr = tied(%$self); + delete $OWNER{$ptr}; +} + +sub ACQUIRE { + my $self = shift; + my $ptr = tied(%$self); + $OWNER{$ptr} = 1; +} + + +# ------- VARIABLE STUBS -------- + +package MCAST; + + +use constant { + MCAST_SEND => (1 << 0), + MCAST_RECV => (1 << 1), + MCAST_TTL_HOST => (1 << 2), + MCAST_TTL_SUBNET => (1 << 3), + MCAST_TTL_SITE => (1 << 4), + MCAST_TTL_REGION => (1 << 5), + MCAST_TTL_CONTINENT => (1 << 6), + MCAST_TTL_UNIVERSE => (1 << 7) +}; +1; diff --git a/src/mod/endpoints/mod_verto/mcast/mcast.c b/src/mod/endpoints/mod_verto/mcast/mcast.c new file mode 100644 index 0000000000..a3234d743f --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/mcast.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2011, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mcast.h" +#include + +int mcast_socket_create(const char *host, int16_t port, mcast_handle_t *handle, mcast_flag_t flags) +{ + uint32_t one = 1; + + memset(handle, 0, sizeof(*handle)); + + if ((!(flags & MCAST_SEND) && !(flags & MCAST_RECV)) || !(handle->sock = socket(AF_INET, SOCK_DGRAM, 0))) { + return -1; + } + + handle->send_addr.sin_family = AF_INET; + handle->send_addr.sin_addr.s_addr = inet_addr(host); + handle->send_addr.sin_port = htons(port); + + setsockopt(handle->sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + + if ((flags & MCAST_RECV)) { + struct ip_mreq mreq; + + handle->recv_addr.sin_family = AF_INET; + handle->recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + handle->recv_addr.sin_port = htons(port); + + mreq.imr_multiaddr.s_addr = inet_addr(host); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(handle->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + close(handle->sock); + handle->sock = -1; + return -1; + } + + if (bind(handle->sock, (struct sockaddr *) &handle->recv_addr, sizeof(handle->recv_addr)) < 0) { + close(handle->sock); + handle->sock = -1; + return -1; + } + + + } + + handle->ttl = 1; + + if ((flags & MCAST_TTL_HOST)) { + handle->ttl = 0; + } + + if ((flags & MCAST_TTL_SUBNET)) { + handle->ttl = 1; + } + + if ((flags & MCAST_TTL_SITE)) { + handle->ttl = 32; + } + + if ((flags & MCAST_TTL_REGION)) { + handle->ttl = 64; + } + + if ((flags & MCAST_TTL_CONTINENT)) { + handle->ttl = 128; + } + + if ((flags & MCAST_TTL_UNIVERSE)) { + handle->ttl = 255; + } + + setsockopt(handle->sock, IPPROTO_IP, IP_MULTICAST_TTL, &handle->ttl, sizeof(handle->ttl)); + + handle->ready = 1; + + return 0; +} + + +void mcast_socket_close(mcast_handle_t *handle) +{ + if (handle->sock > -1) { + close(handle->sock); + handle->sock = -1; + } +} + +ssize_t mcast_socket_send(mcast_handle_t *handle, void *data, size_t datalen) +{ + if (handle->sock <= -1) { + return -1; + } + + if (data == NULL || datalen == 0) { + data = handle->buffer; + datalen = sizeof(handle->buffer); + } + + return sendto(handle->sock, data, datalen, 0, (struct sockaddr *) &handle->send_addr, sizeof(handle->send_addr)); +} + +ssize_t mcast_socket_recv(mcast_handle_t *handle, void *data, size_t datalen, int ms) +{ + socklen_t addrlen = sizeof(handle->recv_addr); + int r; + + if (data == NULL || datalen == 0) { + data = handle->buffer; + datalen = sizeof(handle->buffer); + } + + if (ms > 0) { + struct pollfd pfds[1]; + + pfds[0].fd = handle->sock; + pfds[0].events = POLLIN|POLLERR; + + if ((r = poll(pfds, 1, ms)) <= 0) { + return r; + } + + if (pfds[0].revents & POLLERR) { + return -1; + } + } + + + return recvfrom(handle->sock, data, datalen, 0, (struct sockaddr *) &handle->recv_addr, &addrlen); +} diff --git a/src/mod/endpoints/mod_verto/mcast/mcast.h b/src/mod/endpoints/mod_verto/mcast/mcast.h new file mode 100644 index 0000000000..38afa951e9 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/mcast.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011, 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 __MCAST_H +#define __MCAST_H + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ +#if EMACS_WORKS +} +#endif + +#include +#include +#include +#include + +typedef struct { + int sock; + unsigned char ttl; + struct sockaddr_in send_addr; + struct sockaddr_in recv_addr; + unsigned char buffer[65536]; + int ready; +} mcast_handle_t; + +typedef enum { + MCAST_SEND = (1 << 0), + MCAST_RECV = (1 << 1), + MCAST_TTL_HOST = (1 << 2), + MCAST_TTL_SUBNET = (1 << 3), + MCAST_TTL_SITE = (1 << 4), + MCAST_TTL_REGION = (1 << 5), + MCAST_TTL_CONTINENT = (1 << 6), + MCAST_TTL_UNIVERSE = (1 << 7) +} mcast_flag_t; + +int mcast_socket_create(const char *host, int16_t port, mcast_handle_t *handle, mcast_flag_t flags); +void mcast_socket_close(mcast_handle_t *handle); +ssize_t mcast_socket_send(mcast_handle_t *handle, void *data, size_t datalen); +ssize_t mcast_socket_recv(mcast_handle_t *handle, void *data, size_t datalen, int ms); + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif diff --git a/src/mod/endpoints/mod_verto/mcast/mcast_cpp.cpp b/src/mod/endpoints/mod_verto/mcast/mcast_cpp.cpp new file mode 100644 index 0000000000..2e3eca6c6d --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/mcast_cpp.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011, 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. + */ + +#include "mcast.h" +#include "mcast_cpp.h" +#include +#include + +McastHandle::McastHandle(const char *host, int port, int flags) +{ + mcast_socket_create(host, port, &handle, (mcast_flag_t) flags); +} + +McastHandle::~McastHandle() +{ + mcast_socket_close(&handle); +} + +int McastHandle::send(const char *data) +{ + return (int) mcast_socket_send(&handle, (void *)data, strlen(data) + 1); +} + +char *McastHandle::recv(int ms) +{ + int r; + + if ((r = mcast_socket_recv(&handle, NULL, 0, ms)) > 0) { + *((char *)handle.buffer + r) = '\0'; + return (char *) handle.buffer; + } + + return NULL; +} + +int McastHandle::fileno(void) +{ + return handle.sock; +} diff --git a/src/mod/endpoints/mod_verto/mcast/mcast_cpp.h b/src/mod/endpoints/mod_verto/mcast/mcast_cpp.h new file mode 100644 index 0000000000..5990871a0f --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/mcast_cpp.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, 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 __MCAST_CPP_H +#define __MCAST_CPP_H + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ +#if EMACS_WORKS +} +#endif + +class McastHandle { + private: + mcast_handle_t handle; + public: + McastHandle(const char *host, int port, int flags); + virtual ~McastHandle(); + int send(const char *data); + char *recv(int ms = 0); + int fileno(void); +}; + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif diff --git a/src/mod/endpoints/mod_verto/mcast/mcast_wrap.cpp b/src/mod/endpoints/mod_verto/mcast/mcast_wrap.cpp new file mode 100644 index 0000000000..77725cf4bd --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/mcast_wrap.cpp @@ -0,0 +1,2254 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 1.3.35 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#define SWIGPERL +#define SWIG_CASTRANK_MODE + +#ifdef __cplusplus +template class SwigValueWrapper { + T *tt; +public: + SwigValueWrapper() : tt(0) { } + SwigValueWrapper(const SwigValueWrapper& rhs) : tt(new T(*rhs.tt)) { } + SwigValueWrapper(const T& t) : tt(new T(t)) { } + ~SwigValueWrapper() { delete tt; } + SwigValueWrapper& operator=(const T& t) { delete tt; tt = new T(t); return *this; } + operator T&() const { return *tt; } + T *operator&() { return tt; } +private: + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); +}; + +template T SwigValueInit() { + return T(); +} +#endif + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic CAPI SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "4" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the swig runtime code. + In 99.9% of the cases, swig just needs to declare them as 'static'. + + But only do this if is strictly necessary, ie, if you have problems + with your compiler or so. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 +#define SWIG_CAST_NEW_MEMORY 0x2 + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The swig conversion methods, as ConvertPtr, return and integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old swig versions, you usually write code as: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit as: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + that seems to be the same, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + requires also to SWIG_ConvertPtr to return new result values, as + + int SWIG_ConvertPtr(obj, ptr,...) { + if () { + if () { + *ptr = ; + return SWIG_NEWOBJ; + } else { + *ptr = ; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + swig errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows to return the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() + + + */ +#define SWIG_OK (0) +#define SWIG_ERROR (-1) +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporal objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *, int *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store information on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (int)((l1 - f1) - (l2 - f2)); +} + +/* + Check type equivalence in a name list like ||... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like ||... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCompare(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + + +/* think of this as a c++ template<> or a scheme macro */ +#define SWIG_TypeCheck_Template(comparison, ty) \ + if (ty) { \ + swig_cast_info *iter = ty->cast; \ + while (iter) { \ + if (comparison) { \ + if (iter == ty->cast) return iter; \ + /* Move iter to the top of the linked list */ \ + iter->prev->next = iter->next; \ + if (iter->next) \ + iter->next->prev = iter->prev; \ + iter->next = ty->cast; \ + iter->prev = 0; \ + if (ty->cast) ty->cast->prev = iter; \ + ty->cast = iter; \ + return iter; \ + } \ + iter = iter->next; \ + } \ + } \ + return 0 + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + SWIG_TypeCheck_Template(strcmp(iter->type->name, c) == 0, ty); +} + +/* Same as previous function, except strcmp is replaced with a pointer comparison */ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *into) { + SWIG_TypeCheck_Template(iter->type == from, into); +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + register size_t l = 0; + register size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + register size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + register int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + register size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + register const unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + register unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register char d = *(c++); + register unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + +#ifdef __cplusplus +/* Needed on some windows machines---since MS plays funny games with the header files under C++ */ +#include +#include +extern "C" { +#endif +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +/* Add in functionality missing in older versions of Perl. Much of this is based on Devel-PPPort on cpan. */ + +/* Add PERL_REVISION, PERL_VERSION, PERL_SUBVERSION if missing */ +#ifndef PERL_REVISION +# if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION)) +# define PERL_PATCHLEVEL_H_IMPLICIT +# include +# endif +# if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL))) +# include +# endif +# ifndef PERL_REVISION +# define PERL_REVISION (5) +# define PERL_VERSION PATCHLEVEL +# define PERL_SUBVERSION SUBVERSION +# endif +#endif + +#if defined(WIN32) && defined(PERL_OBJECT) && !defined(PerlIO_exportFILE) +#define PerlIO_exportFILE(fh,fl) (FILE*)(fh) +#endif + +#ifndef SvIOK_UV +# define SvIOK_UV(sv) (SvIOK(sv) && (SvUVX(sv) == SvIVX(sv))) +#endif + +#ifndef SvUOK +# define SvUOK(sv) SvIOK_UV(sv) +#endif + +#if ((PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION <= 5))) +# define PL_sv_undef sv_undef +# define PL_na na +# define PL_errgv errgv +# define PL_sv_no sv_no +# define PL_sv_yes sv_yes +# define PL_markstack_ptr markstack_ptr +#endif + +#ifndef IVSIZE +# ifdef LONGSIZE +# define IVSIZE LONGSIZE +# else +# define IVSIZE 4 /* A bold guess, but the best we can make. */ +# endif +#endif + +#ifndef INT2PTR +# if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE) +# define PTRV UV +# define INT2PTR(any,d) (any)(d) +# else +# if PTRSIZE == LONGSIZE +# define PTRV unsigned long +# else +# define PTRV unsigned +# endif +# define INT2PTR(any,d) (any)(PTRV)(d) +# endif + +# define NUM2PTR(any,d) (any)(PTRV)(d) +# define PTR2IV(p) INT2PTR(IV,p) +# define PTR2UV(p) INT2PTR(UV,p) +# define PTR2NV(p) NUM2PTR(NV,p) + +# if PTRSIZE == LONGSIZE +# define PTR2ul(p) (unsigned long)(p) +# else +# define PTR2ul(p) INT2PTR(unsigned long,p) +# endif +#endif /* !INT2PTR */ + +#ifndef SvPV_nolen +# define SvPV_nolen(x) SvPV(x,PL_na) +#endif + +#ifndef get_sv +# define get_sv perl_get_sv +#endif + +#ifndef ERRSV +# define ERRSV get_sv("@",FALSE) +#endif + +#ifndef pTHX_ +#define pTHX_ +#endif + +#include +#ifdef __cplusplus +} +#endif + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGINTERN const char* +SWIG_Perl_ErrorType(int code) { + const char* type = 0; + switch(code) { + case SWIG_MemoryError: + type = "MemoryError"; + break; + case SWIG_IOError: + type = "IOError"; + break; + case SWIG_RuntimeError: + type = "RuntimeError"; + break; + case SWIG_IndexError: + type = "IndexError"; + break; + case SWIG_TypeError: + type = "TypeError"; + break; + case SWIG_DivisionByZero: + type = "ZeroDivisionError"; + break; + case SWIG_OverflowError: + type = "OverflowError"; + break; + case SWIG_SyntaxError: + type = "SyntaxError"; + break; + case SWIG_ValueError: + type = "ValueError"; + break; + case SWIG_SystemError: + type = "SystemError"; + break; + case SWIG_AttributeError: + type = "AttributeError"; + break; + default: + type = "RuntimeError"; + } + return type; +} + + + + +/* ----------------------------------------------------------------------------- + * perlrun.swg + * + * This file contains the runtime support for Perl modules + * and includes code for managing global variables and pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +#ifdef PERL_OBJECT +#define SWIG_PERL_OBJECT_DECL CPerlObj *SWIGUNUSEDPARM(pPerl), +#define SWIG_PERL_OBJECT_CALL pPerl, +#else +#define SWIG_PERL_OBJECT_DECL +#define SWIG_PERL_OBJECT_CALL +#endif + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_ConvertPtr(obj, pp, type, flags) SWIG_Perl_ConvertPtr(SWIG_PERL_OBJECT_CALL obj, pp, type, flags) +#define SWIG_NewPointerObj(p, type, flags) SWIG_Perl_NewPointerObj(SWIG_PERL_OBJECT_CALL p, type, flags) + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, p, s, type) SWIG_Perl_ConvertPacked(SWIG_PERL_OBJECT_CALL obj, p, s, type) +#define SWIG_NewPackedObj(p, s, type) SWIG_Perl_NewPackedObj(SWIG_PERL_OBJECT_CALL p, s, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) +#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_ConvertPtr(obj, pptr, type, 0) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_NewPointerObj(ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Perl_GetModule() +#define SWIG_SetModule(clientdata, pointer) SWIG_Perl_SetModule(pointer) + + +/* Error manipulation */ + +#define SWIG_ErrorType(code) SWIG_Perl_ErrorType(code) +#define SWIG_Error(code, msg) sv_setpvf(GvSV(PL_errgv),"%s %s\n", SWIG_ErrorType(code), msg) +#define SWIG_fail goto fail + +/* Perl-specific SWIG API */ + +#define SWIG_MakePtr(sv, ptr, type, flags) SWIG_Perl_MakePtr(SWIG_PERL_OBJECT_CALL sv, ptr, type, flags) +#define SWIG_MakePackedObj(sv, p, s, type) SWIG_Perl_MakePackedObj(SWIG_PERL_OBJECT_CALL sv, p, s, type) +#define SWIG_SetError(str) SWIG_Error(SWIG_RuntimeError, str) + + +#define SWIG_PERL_DECL_ARGS_1(arg1) (SWIG_PERL_OBJECT_DECL arg1) +#define SWIG_PERL_CALL_ARGS_1(arg1) (SWIG_PERL_OBJECT_CALL arg1) +#define SWIG_PERL_DECL_ARGS_2(arg1, arg2) (SWIG_PERL_OBJECT_DECL arg1, arg2) +#define SWIG_PERL_CALL_ARGS_2(arg1, arg2) (SWIG_PERL_OBJECT_CALL arg1, arg2) + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +/* For backward compatibility only */ +#define SWIG_POINTER_EXCEPTION 0 + +#ifdef __cplusplus +extern "C" { +#endif + +#define SWIG_OWNER SWIG_POINTER_OWN +#define SWIG_SHADOW SWIG_OWNER << 1 + +#define SWIG_MAYBE_PERL_OBJECT SWIG_PERL_OBJECT_DECL + +/* SWIG Perl macros */ + +/* Macro to declare an XS function */ +#ifndef XSPROTO +# define XSPROTO(name) void name(pTHX_ CV* cv) +#endif + +/* Macro to call an XS function */ +#ifdef PERL_OBJECT +# define SWIG_CALLXS(_name) _name(cv,pPerl) +#else +# ifndef MULTIPLICITY +# define SWIG_CALLXS(_name) _name(cv) +# else +# define SWIG_CALLXS(_name) _name(PERL_GET_THX, cv) +# endif +#endif + +#ifdef PERL_OBJECT +#define MAGIC_PPERL CPerlObj *pPerl = (CPerlObj *) this; + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (CPerlObj::*SwigMagicFunc)(SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + +#define SWIG_MAGIC(a,b) (SV *a, MAGIC *b) +#define SWIGCLASS_STATIC + +#else /* PERL_OBJECT */ + +#define MAGIC_PPERL +#define SWIGCLASS_STATIC static SWIGUNUSED + +#ifndef MULTIPLICITY +#define SWIG_MAGIC(a,b) (SV *a, MAGIC *b) + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (*SwigMagicFunc)(SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + +#else /* MULTIPLICITY */ + +#define SWIG_MAGIC(a,b) (struct interpreter *interp, SV *a, MAGIC *b) + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (*SwigMagicFunc)(struct interpreter *, SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + +#endif /* MULTIPLICITY */ +#endif /* PERL_OBJECT */ + +/* Workaround for bug in perl 5.6.x croak and earlier */ +#if (PERL_VERSION < 8) +# ifdef PERL_OBJECT +# define SWIG_croak_null() SWIG_Perl_croak_null(pPerl) +static void SWIG_Perl_croak_null(CPerlObj *pPerl) +# else +static void SWIG_croak_null() +# endif +{ + SV *err=ERRSV; +# if (PERL_VERSION < 6) + croak("%_", err); +# else + if (SvOK(err) && !SvROK(err)) croak("%_", err); + croak(Nullch); +# endif +} +#else +# define SWIG_croak_null() croak(Nullch) +#endif + + +/* + Define how strict is the cast between strings and integers/doubles + when overloading between these types occurs. + + The default is making it as strict as possible by using SWIG_AddCast + when needed. + + You can use -DSWIG_PERL_NO_STRICT_STR2NUM at compilation time to + disable the SWIG_AddCast, making the casting between string and + numbers less strict. + + In the end, we try to solve the overloading between strings and + numerical types in the more natural way, but if you can avoid it, + well, avoid it using %rename, for example. +*/ +#ifndef SWIG_PERL_NO_STRICT_STR2NUM +# ifndef SWIG_PERL_STRICT_STR2NUM +# define SWIG_PERL_STRICT_STR2NUM +# endif +#endif +#ifdef SWIG_PERL_STRICT_STR2NUM +/* string takes precedence */ +#define SWIG_Str2NumCast(x) SWIG_AddCast(x) +#else +/* number takes precedence */ +#define SWIG_Str2NumCast(x) x +#endif + + + +#include + +SWIGRUNTIME const char * +SWIG_Perl_TypeProxyName(const swig_type_info *type) { + if (!type) return NULL; + if (type->clientdata != NULL) { + return (const char*) type->clientdata; + } + else { + return type->name; + } +} + +SWIGRUNTIME swig_cast_info * +SWIG_TypeProxyCheck(const char *c, swig_type_info *ty) { + SWIG_TypeCheck_Template(( (!iter->type->clientdata && (strcmp(iter->type->name, c) == 0)) + || (iter->type->clientdata && (strcmp((char*)iter->type->clientdata, c) == 0))), ty); +} + + +/* Function for getting a pointer value */ + +SWIGRUNTIME int +SWIG_Perl_ConvertPtr(SWIG_MAYBE_PERL_OBJECT SV *sv, void **ptr, swig_type_info *_t, int flags) { + swig_cast_info *tc; + void *voidptr = (void *)0; + SV *tsv = 0; + /* If magical, apply more magic */ + if (SvGMAGICAL(sv)) + mg_get(sv); + + /* Check to see if this is an object */ + if (sv_isobject(sv)) { + IV tmp = 0; + tsv = (SV*) SvRV(sv); + if ((SvTYPE(tsv) == SVt_PVHV)) { + MAGIC *mg; + if (SvMAGICAL(tsv)) { + mg = mg_find(tsv,'P'); + if (mg) { + sv = mg->mg_obj; + if (sv_isobject(sv)) { + tsv = (SV*)SvRV(sv); + tmp = SvIV(tsv); + } + } + } else { + return SWIG_ERROR; + } + } else { + tmp = SvIV(tsv); + } + voidptr = INT2PTR(void *,tmp); + } else if (! SvOK(sv)) { /* Check for undef */ + *(ptr) = (void *) 0; + return SWIG_OK; + } else if (SvTYPE(sv) == SVt_RV) { /* Check for NULL pointer */ + if (!SvROK(sv)) { + *(ptr) = (void *) 0; + return SWIG_OK; + } else { + return SWIG_ERROR; + } + } else { /* Don't know what it is */ + return SWIG_ERROR; + } + if (_t) { + /* Now see if the types match */ + char *_c = HvNAME(SvSTASH(SvRV(sv))); + tc = SWIG_TypeProxyCheck(_c,_t); + if (!tc) { + return SWIG_ERROR; + } + { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,voidptr,&newmemory); + assert(!newmemory); /* newmemory handling not yet implemented */ + } + } else { + *ptr = voidptr; + } + + /* + * DISOWN implementation: we need a perl guru to check this one. + */ + if (tsv && (flags & SWIG_POINTER_DISOWN)) { + /* + * almost copy paste code from below SWIG_POINTER_OWN setting + */ + SV *obj = sv; + HV *stash = SvSTASH(SvRV(obj)); + GV *gv = *(GV**) hv_fetch(stash, "OWNER", 5, TRUE); + if (isGV(gv)) { + HV *hv = GvHVn(gv); + /* + * To set ownership (see below), a newSViv(1) entry is added. + * Hence, to remove ownership, we delete the entry. + */ + if (hv_exists_ent(hv, obj, 0)) { + hv_delete_ent(hv, obj, 0, 0); + } + } + } + return SWIG_OK; +} + +SWIGRUNTIME void +SWIG_Perl_MakePtr(SWIG_MAYBE_PERL_OBJECT SV *sv, void *ptr, swig_type_info *t, int flags) { + if (ptr && (flags & SWIG_SHADOW)) { + SV *self; + SV *obj=newSV(0); + HV *hash=newHV(); + HV *stash; + sv_setref_pv(obj, (char *) SWIG_Perl_TypeProxyName(t), ptr); + stash=SvSTASH(SvRV(obj)); + if (flags & SWIG_POINTER_OWN) { + HV *hv; + GV *gv=*(GV**)hv_fetch(stash, "OWNER", 5, TRUE); + if (!isGV(gv)) + gv_init(gv, stash, "OWNER", 5, FALSE); + hv=GvHVn(gv); + hv_store_ent(hv, obj, newSViv(1), 0); + } + sv_magic((SV *)hash, (SV *)obj, 'P', Nullch, 0); + SvREFCNT_dec(obj); + self=newRV_noinc((SV *)hash); + sv_setsv(sv, self); + SvREFCNT_dec((SV *)self); + sv_bless(sv, stash); + } + else { + sv_setref_pv(sv, (char *) SWIG_Perl_TypeProxyName(t), ptr); + } +} + +SWIGRUNTIMEINLINE SV * +SWIG_Perl_NewPointerObj(SWIG_MAYBE_PERL_OBJECT void *ptr, swig_type_info *t, int flags) { + SV *result = sv_newmortal(); + SWIG_MakePtr(result, ptr, t, flags); + return result; +} + +SWIGRUNTIME void +SWIG_Perl_MakePackedObj(SWIG_MAYBE_PERL_OBJECT SV *sv, void *ptr, int sz, swig_type_info *type) { + char result[1024]; + char *r = result; + if ((2*sz + 1 + strlen(SWIG_Perl_TypeProxyName(type))) > 1000) return; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + strcpy(r,SWIG_Perl_TypeProxyName(type)); + sv_setpv(sv, result); +} + +SWIGRUNTIME SV * +SWIG_Perl_NewPackedObj(SWIG_MAYBE_PERL_OBJECT void *ptr, int sz, swig_type_info *type) { + SV *result = sv_newmortal(); + SWIG_Perl_MakePackedObj(result, ptr, sz, type); + return result; +} + +/* Convert a packed value value */ +SWIGRUNTIME int +SWIG_Perl_ConvertPacked(SWIG_MAYBE_PERL_OBJECT SV *obj, void *ptr, int sz, swig_type_info *ty) { + swig_cast_info *tc; + const char *c = 0; + + if ((!obj) || (!SvOK(obj))) return SWIG_ERROR; + c = SvPV_nolen(obj); + /* Pointer values must start with leading underscore */ + if (*c != '_') return SWIG_ERROR; + c++; + c = SWIG_UnpackData(c,ptr,sz); + if (ty) { + tc = SWIG_TypeCheck(c,ty); + if (!tc) return SWIG_ERROR; + } + return SWIG_OK; +} + + +/* Macros for low-level exception handling */ +#define SWIG_croak(x) { SWIG_Error(SWIG_RuntimeError, x); SWIG_fail; } + + +typedef XSPROTO(SwigPerlWrapper); +typedef SwigPerlWrapper *SwigPerlWrapperPtr; + +/* Structure for command table */ +typedef struct { + const char *name; + SwigPerlWrapperPtr wrapper; +} swig_command_info; + +/* Information for constant table */ + +#define SWIG_INT 1 +#define SWIG_FLOAT 2 +#define SWIG_STRING 3 +#define SWIG_POINTER 4 +#define SWIG_BINARY 5 + +/* Constant information structure */ +typedef struct swig_constant_info { + int type; + const char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_constant_info; + + +/* Structure for variable table */ +typedef struct { + const char *name; + SwigMagicFunc set; + SwigMagicFunc get; + swig_type_info **type; +} swig_variable_info; + +/* Magic variable code */ +#ifndef PERL_OBJECT +#define swig_create_magic(s,a,b,c) _swig_create_magic(s,a,b,c) + #ifndef MULTIPLICITY + SWIGRUNTIME void _swig_create_magic(SV *sv, char *name, int (*set)(SV *, MAGIC *), int (*get)(SV *,MAGIC *)) + #else + SWIGRUNTIME void _swig_create_magic(SV *sv, char *name, int (*set)(struct interpreter*, SV *, MAGIC *), int (*get)(struct interpreter*, SV *,MAGIC *)) + #endif +#else +# define swig_create_magic(s,a,b,c) _swig_create_magic(pPerl,s,a,b,c) +SWIGRUNTIME void _swig_create_magic(CPerlObj *pPerl, SV *sv, const char *name, int (CPerlObj::*set)(SV *, MAGIC *), int (CPerlObj::*get)(SV *, MAGIC *)) +#endif +{ + MAGIC *mg; + sv_magic(sv,sv,'U',(char *) name,strlen(name)); + mg = mg_find(sv,'U'); + mg->mg_virtual = (MGVTBL *) malloc(sizeof(MGVTBL)); + mg->mg_virtual->svt_get = (SwigMagicFunc) get; + mg->mg_virtual->svt_set = (SwigMagicFunc) set; + mg->mg_virtual->svt_len = 0; + mg->mg_virtual->svt_clear = 0; + mg->mg_virtual->svt_free = 0; +} + + +SWIGRUNTIME swig_module_info * +SWIG_Perl_GetModule(void) { + static void *type_pointer = (void *)0; + SV *pointer; + + /* first check if pointer already created */ + if (!type_pointer) { + pointer = get_sv("swig_runtime_data::type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, FALSE | GV_ADDMULTI); + if (pointer && SvOK(pointer)) { + type_pointer = INT2PTR(swig_type_info **, SvIV(pointer)); + } + } + + return (swig_module_info *) type_pointer; +} + +SWIGRUNTIME void +SWIG_Perl_SetModule(swig_module_info *module) { + SV *pointer; + + /* create a new pointer */ + pointer = get_sv("swig_runtime_data::type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, TRUE | GV_ADDMULTI); + sv_setiv(pointer, PTR2IV(module)); +} + +#ifdef __cplusplus +} +#endif + +/* Workaround perl5 global namespace pollution. Note that undefining library + * functions like fopen will not solve the problem on all platforms as fopen + * might be a macro on Windows but not necessarily on other operating systems. */ +#ifdef do_open + #undef do_open +#endif +#ifdef do_close + #undef do_close +#endif +#ifdef scalar + #undef scalar +#endif +#ifdef list + #undef list +#endif +#ifdef apply + #undef apply +#endif +#ifdef convert + #undef convert +#endif +#ifdef Error + #undef Error +#endif +#ifdef form + #undef form +#endif +#ifdef vform + #undef vform +#endif +#ifdef LABEL + #undef LABEL +#endif +#ifdef METHOD + #undef METHOD +#endif +#ifdef Move + #undef Move +#endif +#ifdef yylex + #undef yylex +#endif +#ifdef yyparse + #undef yyparse +#endif +#ifdef yyerror + #undef yyerror +#endif +#ifdef invert + #undef invert +#endif +#ifdef ref + #undef ref +#endif +#ifdef read + #undef read +#endif +#ifdef write + #undef write +#endif +#ifdef eof + #undef eof +#endif +#ifdef bool + #undef bool +#endif +#ifdef close + #undef close +#endif +#ifdef rewind + #undef rewind +#endif +#ifdef free + #undef free +#endif +#ifdef malloc + #undef malloc +#endif +#ifdef calloc + #undef calloc +#endif +#ifdef Stat + #undef Stat +#endif +#ifdef check + #undef check +#endif +#ifdef seekdir + #undef seekdir +#endif +#ifdef open + #undef open +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else + + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_McastHandle swig_types[0] +#define SWIGTYPE_p_char swig_types[1] +static swig_type_info *swig_types[3]; +static swig_module_info swig_module = {swig_types, 2, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#define SWIG_init boot_MCAST + +#define SWIG_name "MCASTc::boot_MCAST" +#define SWIG_prefix "MCASTc::" + +#define SWIGVERSION 0x010335 +#define SWIG_VERSION SWIGVERSION + + +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),reinterpret_cast< void** >(a)) + + +#include + + +#ifdef __cplusplus +extern "C" +#endif +#ifndef PERL_OBJECT +#ifndef MULTIPLICITY +SWIGEXPORT void SWIG_init (CV* cv); +#else +SWIGEXPORT void SWIG_init (pTHXo_ CV* cv); +#endif +#else +SWIGEXPORT void SWIG_init (CV *cv, CPerlObj *); +#endif + + +#include "mcast.h" +#include "mcast_cpp.h" + + +SWIGINTERN swig_type_info* +SWIG_pchar_descriptor(void) +{ + static int init = 0; + static swig_type_info* info = 0; + if (!init) { + info = SWIG_TypeQuery("_p_char"); + init = 1; + } + return info; +} + + +SWIGINTERN int +SWIG_AsCharPtrAndSize(SV *obj, char** cptr, size_t* psize, int *alloc) +{ + if (SvPOK(obj)) { + STRLEN len = 0; + char *cstr = SvPV(obj, len); + size_t size = len + 1; + if (cptr) { + if (alloc) { + if (*alloc == SWIG_NEWOBJ) { + *cptr = reinterpret_cast< char* >(memcpy((new char[size]), cstr, sizeof(char)*(size))); + } else { + *cptr = cstr; + *alloc = SWIG_OLDOBJ; + } + } + } + if (psize) *psize = size; + return SWIG_OK; + } else { + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + char* vptr = 0; + if (SWIG_ConvertPtr(obj, (void**)&vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = vptr; + if (psize) *psize = vptr ? (strlen(vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; +} + + + + + +#include +#if !defined(SWIG_NO_LLONG_MAX) +# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) +# define LLONG_MAX __LONG_LONG_MAX__ +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +# endif +#endif + + +SWIGINTERN int +SWIG_AsVal_double SWIG_PERL_DECL_ARGS_2(SV *obj, double *val) +{ + if (SvNIOK(obj)) { + if (val) *val = SvNV(obj); + return SWIG_OK; + } else if (SvIOK(obj)) { + if (val) *val = (double) SvIV(obj); + return SWIG_AddCast(SWIG_OK); + } else { + const char *nptr = SvPV_nolen(obj); + if (nptr) { + char *endptr; + double v = strtod(nptr, &endptr); + if (errno == ERANGE) { + errno = 0; + return SWIG_OverflowError; + } else { + if (*endptr == '\0') { + if (val) *val = v; + return SWIG_Str2NumCast(SWIG_OK); + } + } + } + } + return SWIG_TypeError; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx = floor(x); + double cx = ceil(x); + double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff/summ; + if (reps < 8*DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_long SWIG_PERL_DECL_ARGS_2(SV *obj, long* val) +{ + if (SvIOK(obj)) { + if (val) *val = SvIV(obj); + return SWIG_OK; + } else { + int dispatch = 0; + const char *nptr = SvPV_nolen(obj); + if (nptr) { + char *endptr; + long v; + errno = 0; + v = strtol(nptr, &endptr,0); + if (errno == ERANGE) { + errno = 0; + return SWIG_OverflowError; + } else { + if (*endptr == '\0') { + if (val) *val = v; + return SWIG_Str2NumCast(SWIG_OK); + } + } + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double SWIG_PERL_CALL_ARGS_2(obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { + if (val) *val = (long)(d); + return res; + } + } + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int SWIG_PERL_DECL_ARGS_2(SV * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long SWIG_PERL_CALL_ARGS_2(obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = static_cast< int >(v); + } + } + return res; +} + + +SWIGINTERNINLINE SV * +SWIG_From_long SWIG_PERL_DECL_ARGS_1(long value) +{ + SV *obj = sv_newmortal(); + sv_setiv(obj, (IV) value); + return obj; +} + + +SWIGINTERNINLINE SV * +SWIG_From_int SWIG_PERL_DECL_ARGS_1(int value) +{ + return SWIG_From_long SWIG_PERL_CALL_ARGS_1(value); +} + + +SWIGINTERNINLINE SV * +SWIG_FromCharPtrAndSize(const char* carray, size_t size) +{ + SV *obj = sv_newmortal(); + if (carray) { + sv_setpvn(obj, carray, size); + } else { + sv_setsv(obj, &PL_sv_undef); + } + return obj; +} + + +SWIGINTERNINLINE SV * +SWIG_FromCharPtr(const char *cptr) +{ + return SWIG_FromCharPtrAndSize(cptr, (cptr ? strlen(cptr) : 0)); +} + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PERL_OBJECT +#define MAGIC_CLASS _wrap_MCAST_var:: +class _wrap_MCAST_var : public CPerlObj { +public: +#else +#define MAGIC_CLASS +#endif +SWIGCLASS_STATIC int swig_magic_readonly(pTHX_ SV *SWIGUNUSEDPARM(sv), MAGIC *SWIGUNUSEDPARM(mg)) { + MAGIC_PPERL + croak("Value is read-only."); + return 0; +} + + +#ifdef PERL_OBJECT +}; +#endif + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif +XS(_wrap_new_McastHandle) { + { + char *arg1 = (char *) 0 ; + int arg2 ; + int arg3 ; + McastHandle *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 3) || (items > 3)) { + SWIG_croak("Usage: new_McastHandle(host,port,flags);"); + } + res1 = SWIG_AsCharPtrAndSize(ST(0), &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_McastHandle" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_McastHandle" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + ecode3 = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(2), &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "new_McastHandle" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + result = (McastHandle *)new McastHandle((char const *)arg1,arg2,arg3); + ST(argvi) = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_McastHandle, SWIG_OWNER | SWIG_SHADOW); argvi++ ; + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + + XSRETURN(argvi); + fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + + SWIG_croak_null(); + } +} + + +XS(_wrap_delete_McastHandle) { + { + McastHandle *arg1 = (McastHandle *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: delete_McastHandle(self);"); + } + res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_McastHandle, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_McastHandle" "', argument " "1"" of type '" "McastHandle *""'"); + } + arg1 = reinterpret_cast< McastHandle * >(argp1); + delete arg1; + + + + XSRETURN(argvi); + fail: + + SWIG_croak_null(); + } +} + + +XS(_wrap_McastHandle_send) { + { + McastHandle *arg1 = (McastHandle *) 0 ; + char *arg2 = (char *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 2) || (items > 2)) { + SWIG_croak("Usage: McastHandle_send(self,data);"); + } + res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_McastHandle, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "McastHandle_send" "', argument " "1"" of type '" "McastHandle *""'"); + } + arg1 = reinterpret_cast< McastHandle * >(argp1); + res2 = SWIG_AsCharPtrAndSize(ST(1), &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "McastHandle_send" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = reinterpret_cast< char * >(buf2); + result = (int)(arg1)->send((char const *)arg2); + ST(argvi) = SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(result)); argvi++ ; + + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + XSRETURN(argvi); + fail: + + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + SWIG_croak_null(); + } +} + + +XS(_wrap_McastHandle_recv) { + { + McastHandle *arg1 = (McastHandle *) 0 ; + int arg2 = (int) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 2)) { + SWIG_croak("Usage: McastHandle_recv(self,ms);"); + } + res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_McastHandle, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "McastHandle_recv" "', argument " "1"" of type '" "McastHandle *""'"); + } + arg1 = reinterpret_cast< McastHandle * >(argp1); + if (items > 1) { + ecode2 = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "McastHandle_recv" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + } + result = (char *)(arg1)->recv(arg2); + ST(argvi) = SWIG_FromCharPtr((const char *)result); argvi++ ; + + + XSRETURN(argvi); + fail: + + + SWIG_croak_null(); + } +} + + +XS(_wrap_McastHandle_fileno) { + { + McastHandle *arg1 = (McastHandle *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: McastHandle_fileno(self);"); + } + res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_McastHandle, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "McastHandle_fileno" "', argument " "1"" of type '" "McastHandle *""'"); + } + arg1 = reinterpret_cast< McastHandle * >(argp1); + result = (int)(arg1)->fileno(); + ST(argvi) = SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(result)); argvi++ ; + + XSRETURN(argvi); + fail: + + SWIG_croak_null(); + } +} + + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_McastHandle = {"_p_McastHandle", "McastHandle *", 0, 0, (void*)"MCAST::McastHandle", 0}; +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p_McastHandle, + &_swigt__p_char, +}; + +static swig_cast_info _swigc__p_McastHandle[] = { {&_swigt__p_McastHandle, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p_McastHandle, + _swigc__p_char, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_constant_info swig_constants[] = { +{0,0,0,0,0,0} +}; +#ifdef __cplusplus +} +#endif +static swig_variable_info swig_variables[] = { +{0,0,0,0} +}; +static swig_command_info swig_commands[] = { +{"MCASTc::new_McastHandle", _wrap_new_McastHandle}, +{"MCASTc::delete_McastHandle", _wrap_delete_McastHandle}, +{"MCASTc::McastHandle_send", _wrap_McastHandle_send}, +{"MCASTc::McastHandle_recv", _wrap_McastHandle_recv}, +{"MCASTc::McastHandle_fileno", _wrap_McastHandle_fileno}, +{0,0} +}; +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned staticly to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + + +SWIGRUNTIME void +SWIG_InitializeModule(void *clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int found, init; + + clientdata = clientdata; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + init = 1; + } else { + init = 0; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + module_head = &swig_module; + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + found=0; + iter=module_head; + do { + if (iter==&swig_module) { + found=1; + break; + } + iter=iter->next; + } while (iter!= module_head); + + /* if the is found in the list, then all is done and we may leave */ + if (found) return; + /* otherwise we must add out module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* When multiple interpeters are used, a module could have already been initialized in + a different interpreter, but not yet have a pointer in this interpreter. + In this case, we do not want to continue adding types... everything should be + set up already */ + if (init == 0) return; + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %d\n", swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + + +#ifdef __cplusplus +extern "C" +#endif + +XS(SWIG_init) { + dXSARGS; + int i; + + SWIG_InitializeModule(0); + + /* Install commands */ + for (i = 0; swig_commands[i].name; i++) { + newXS((char*) swig_commands[i].name,swig_commands[i].wrapper, (char*)__FILE__); + } + + /* Install variables */ + for (i = 0; swig_variables[i].name; i++) { + SV *sv; + sv = get_sv((char*) swig_variables[i].name, TRUE | 0x2 | GV_ADDMULTI); + if (swig_variables[i].type) { + SWIG_MakePtr(sv,(void *)1, *swig_variables[i].type,0); + } else { + sv_setiv(sv,(IV) 0); + } + swig_create_magic(sv, (char *) swig_variables[i].name, swig_variables[i].set, swig_variables[i].get); + } + + /* Install constant */ + for (i = 0; swig_constants[i].type; i++) { + SV *sv; + sv = get_sv((char*)swig_constants[i].name, TRUE | 0x2 | GV_ADDMULTI); + switch(swig_constants[i].type) { + case SWIG_INT: + sv_setiv(sv, (IV) swig_constants[i].lvalue); + break; + case SWIG_FLOAT: + sv_setnv(sv, (double) swig_constants[i].dvalue); + break; + case SWIG_STRING: + sv_setpv(sv, (char *) swig_constants[i].pvalue); + break; + case SWIG_POINTER: + SWIG_MakePtr(sv, swig_constants[i].pvalue, *(swig_constants[i].ptype),0); + break; + case SWIG_BINARY: + SWIG_MakePackedObj(sv, swig_constants[i].pvalue, swig_constants[i].lvalue, *(swig_constants[i].ptype)); + break; + default: + break; + } + SvREADONLY_on(sv); + } + + SWIG_TypeClientData(SWIGTYPE_p_McastHandle, (void*) "MCAST::McastHandle"); + ST(0) = &PL_sv_yes; + XSRETURN(1); +} + diff --git a/src/mod/endpoints/mod_verto/mcast/perlxsi.c b/src/mod/endpoints/mod_verto/mcast/perlxsi.c new file mode 100644 index 0000000000..9ca8fc1fb1 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/perlxsi.c @@ -0,0 +1,16 @@ +#include +#include + +EXTERN_C void xs_init (pTHX); + +EXTERN_C void boot_DynaLoader (pTHX_ CV* cv); + +EXTERN_C void +xs_init(pTHX) +{ + char *file = __FILE__; + dXSUB_SYS; + + /* DynaLoader is a special case */ + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); +} diff --git a/src/mod/endpoints/mod_verto/mcast/test.c b/src/mod/endpoints/mod_verto/mcast/test.c new file mode 100644 index 0000000000..2e07518285 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/test.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include "mcast.h" + +int main(int argc, char *argv[]) +{ + mcast_handle_t handle; + + if (argc < 2) { + printf("WTF\n"); + exit(-1); + } + + mcast_socket_create("231.3.3.7", 1337, &handle, MCAST_SEND | MCAST_RECV | MCAST_TTL_HOST); + perror("create"); + + if (!strcmp(argv[1], "send")) { + mcast_socket_send(&handle, argv[2], strlen(argv[2])); + exit(0); + } + + for(;;) { + int r = mcast_socket_recv(&handle, NULL, 0); + printf("RECV %d [%s]\n", r, (char *)handle.buffer); + } + +} diff --git a/src/mod/endpoints/mod_verto/mcast/test.pl b/src/mod/endpoints/mod_verto/mcast/test.pl new file mode 100644 index 0000000000..ca8b58aa30 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/test.pl @@ -0,0 +1,19 @@ +use MCAST; + +my $s = new MCAST::McastHandle("231.3.3.7", 1337, MCAST_SEND | MCAST_RECV | MCAST_TTL_HOST); + +my $action = shift; + +if ($action eq "send") { + $s->send("W00t from Perl " . shift); + exit; +} + +for(;;) { + my $foo = $s->recv(100); + if ($foo) { + print "RECV [$foo]\n"; + } else { + print "PING\n"; + } +} diff --git a/src/mod/endpoints/mod_verto/mcast/test2.pl b/src/mod/endpoints/mod_verto/mcast/test2.pl new file mode 100644 index 0000000000..341e87ba99 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mcast/test2.pl @@ -0,0 +1,23 @@ +use MCAST; + + + +my $action = shift; + +if ($action eq "send") { + my $s = new MCAST::McastHandle("224.1.1.1", 1337, MCAST::MCAST_SEND | MCAST::MCAST_TTL_HOST); + $s->send(shift); + print "Sending message"; + exit; +} + +my $s = new MCAST::McastHandle("224.1.1.1", 1338, MCAST::MCAST_RECV | MCAST::MCAST_TTL_HOST); + +for(;;) { + my $foo = $s->recv(); + if ($foo) { + print "RECV [$foo]\n"; + } else { + print "PING\n"; + } +} diff --git a/src/mod/endpoints/mod_verto/mod_verto.2008.vcproj b/src/mod/endpoints/mod_verto/mod_verto.2008.vcproj new file mode 100644 index 0000000000..548b114237 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mod_verto.2008.vcproj @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_verto/mod_verto.2010.vcxproj b/src/mod/endpoints/mod_verto/mod_verto.2010.vcxproj new file mode 100644 index 0000000000..1dc1aac6dc --- /dev/null +++ b/src/mod/endpoints/mod_verto/mod_verto.2010.vcxproj @@ -0,0 +1,131 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + mod_verto + {11C9BC3D-45E9-46E3-BE84-B8CEE4685E39} + mod_verto + Win32Proj + + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + {202d7a4e-760d-4d0e-afa1-d7459ced30ff} + false + + + + + + diff --git a/src/mod/endpoints/mod_verto/mod_verto.2012.vcxproj b/src/mod/endpoints/mod_verto/mod_verto.2012.vcxproj new file mode 100644 index 0000000000..8930918614 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mod_verto.2012.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + mod_verto + {11C9BC3D-45E9-46E3-BE84-B8CEE4685E39} + mod_verto + Win32Proj + + + + DynamicLibrary + MultiByte + v110 + + + DynamicLibrary + MultiByte + v110 + + + DynamicLibrary + MultiByte + v110 + + + DynamicLibrary + MultiByte + v110 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + {202d7a4e-760d-4d0e-afa1-d7459ced30ff} + false + + + + + + diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c new file mode 100644 index 0000000000..8e5c94ef51 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -0,0 +1,4485 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2014, 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 + * + * mod_verto.c -- HTML5 Verto interface + * + */ +#include +#include + + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown); +SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime); + +SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime); + +#define EP_NAME "verto.rtc" +#define WSS_STANDALONE 1 +#include "ws.h" + +////////////////////////// +#include +#include +#include +#include +#include +#include +#include + + + +#define die(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, __VA_ARGS__); goto error + +struct globals_s globals; + + +static struct { + switch_mutex_t *store_mutex; + switch_hash_t *store_hash; +} json_GLOBALS; + + +const char json_sql[] = + "create table json_store (\n" + " name varchar(255) not null,\n" + " data text\n" + ");\n"; + + +typedef enum { + CMD_ADD, + CMD_DEL, + CMD_DUMP, + CMD_COMMIT, + CMD_RETRIEVE +} store_cmd_t; + +typedef struct { + switch_mutex_t *mutex; + cJSON *JSON_STORE; +} json_store_t; + +static void json_cleanup(void) +{ + switch_hash_index_t *hi = NULL; + void *val; + const void *var; + cJSON *json; + + switch_mutex_lock(json_GLOBALS.store_mutex); + top: + + for (hi = switch_core_hash_first_iter(json_GLOBALS.store_hash, hi); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, &var, NULL, &val); + json = (cJSON *) val; + cJSON_Delete(json); + switch_core_hash_delete(json_GLOBALS.store_hash, var); + goto top; + } + switch_safe_free(hi); + + switch_mutex_unlock(json_GLOBALS.store_mutex); + +} + +static switch_bool_t check_name(const char *name) +{ + const char *p; + + for(p = name; p && *p; p++) { + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '-' || *p == '_') continue; + return SWITCH_FALSE; + } + + return SWITCH_TRUE; +} + + +static verto_profile_t *find_profile(const char *name); +static jsock_t *get_jsock(const char *uuid); + +static void verto_deinit_ssl(verto_profile_t *profile) +{ + if (profile->ssl_ctx) { + SSL_CTX_free(profile->ssl_ctx); + profile->ssl_ctx = NULL; + } +} + +static int ssl_init = 0; + +static void verto_init_ssl(verto_profile_t *profile) +{ + if (!ssl_init) { + SSL_library_init(); + ssl_init = 1; + } + + profile->ssl_method = SSLv23_server_method(); /* create server instance */ + profile->ssl_ctx = SSL_CTX_new(profile->ssl_method); /* create context */ + profile->ssl_ready = 1; + assert(profile->ssl_ctx); + + /* set the local certificate from CertFile */ + if (!zstr(profile->chain)) { + SSL_CTX_use_certificate_chain_file(profile->ssl_ctx, profile->chain); + } + + SSL_CTX_use_certificate_file(profile->ssl_ctx, profile->cert, SSL_FILETYPE_PEM); + + /* set the private key from KeyFile */ + SSL_CTX_use_PrivateKey_file(profile->ssl_ctx, profile->key, SSL_FILETYPE_PEM); + /* verify private key */ + if ( !SSL_CTX_check_private_key(profile->ssl_ctx) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SSL NOT AVAILABLE\n"); + profile->ssl_ready = 0; + verto_deinit_ssl(profile); + } else { + SSL_CTX_set_cipher_list(profile->ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH"); + } +} + + +struct jsock_sub_node_head_s; + +typedef struct jsock_sub_node_s { + jsock_t *jsock; + struct jsock_sub_node_head_s *head; + struct jsock_sub_node_s *next; +} jsock_sub_node_t; + +typedef struct jsock_sub_node_head_s { + jsock_sub_node_t *node; + jsock_sub_node_t *tail; + char *event_channel; +} jsock_sub_node_head_t; + +static uint32_t jsock_unsub_head(jsock_t *jsock, jsock_sub_node_head_t *head) +{ + uint32_t x = 0; + + jsock_sub_node_t *thisnp = NULL, *np, *last = NULL; + + np = head->tail = head->node; + + while (np) { + + thisnp = np; + np = np->next; + + if (!jsock || thisnp->jsock == jsock) { + x++; + + if (last) { + last->next = np; + } else { + head->node = np; + } + + if (thisnp->jsock->profile->debug || globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "UNSUBBING %s [%s]\n", thisnp->jsock->name, thisnp->head->event_channel); + } + + thisnp->jsock = NULL; + free(thisnp); + } else { + last = thisnp; + head->tail = last; + } + } + + return x; +} + +static void unsub_all_jsock(void) +{ + switch_hash_index_t *hi; + void *val; + jsock_sub_node_head_t *head; + + switch_thread_rwlock_wrlock(globals.event_channel_rwlock); + top: + head = NULL; + + for (hi = switch_core_hash_first(globals.event_channel_hash); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, NULL, NULL, &val); + head = (jsock_sub_node_head_t *) val; + jsock_unsub_head(NULL, head); + switch_core_hash_delete(globals.event_channel_hash, head->event_channel); + free(head->event_channel); + free(head); + switch_safe_free(hi); + goto top; + } + + switch_thread_rwlock_unlock(globals.event_channel_rwlock); +} + +static uint32_t jsock_unsub_channel(jsock_t *jsock, const char *event_channel) +{ + jsock_sub_node_head_t *head; + uint32_t x = 0; + + switch_thread_rwlock_wrlock(globals.event_channel_rwlock); + + if (!event_channel) { + switch_hash_index_t *hi; + void *val; + + for (hi = switch_core_hash_first(globals.event_channel_hash); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, NULL, NULL, &val); + + if (val) { + head = (jsock_sub_node_head_t *) val; + x += jsock_unsub_head(jsock, head); + } + } + + } else { + if ((head = switch_core_hash_find(globals.event_channel_hash, event_channel))) { + x += jsock_unsub_head(jsock, head); + } + } + + switch_thread_rwlock_unlock(globals.event_channel_rwlock); + + return x; +} + +static void presence_ping(const char *event_channel) +{ + switch_console_callback_match_t *matches; + const char *val = event_channel; + + if (val) { + if (!strcasecmp(val, "presence")) { + val = NULL; + } else { + char *p; + if ((p = strchr(val, '.'))) { + val = (p+1); + } + } + } + + if ((matches = switch_core_session_findall_matching_var("presence_id", val))) { + switch_console_callback_match_node_t *m; + switch_core_session_t *session; + + for (m = matches->head; m; m = m->next) { + if ((session = switch_core_session_locate(m->val))) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *event; + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_CALLSTATE) == SWITCH_STATUS_SUCCESS) { + switch_channel_callstate_t callstate = switch_channel_get_callstate(channel); + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Original-Channel-Call-State", switch_channel_callstate2str(callstate)); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Call-State-Number", "%d", callstate); + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + + switch_core_session_rwunlock(session); + } + } + + switch_console_free_matches(&matches); + } +} + +static switch_status_t jsock_sub_channel(jsock_t *jsock, const char *event_channel) +{ + jsock_sub_node_t *node, *np; + jsock_sub_node_head_t *head; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_thread_rwlock_wrlock(globals.event_channel_rwlock); + + if (!(head = switch_core_hash_find(globals.event_channel_hash, event_channel))) { + switch_zmalloc(head, sizeof(*head)); + head->event_channel = strdup(event_channel); + switch_core_hash_insert(globals.event_channel_hash, event_channel, head); + + switch_zmalloc(node, sizeof(*node)); + node->jsock = jsock; + node->head = head; + head->node = node; + head->tail = node; + status = SWITCH_STATUS_SUCCESS; + } else { + int exist = 0; + + for (np = head->node; np; np = np->next) { + if (np->jsock == jsock) { + exist = 1; + break; + } + } + + if (!exist) { + switch_zmalloc(node, sizeof(*node)); + node->jsock = jsock; + node->head = head; + + if (!head->node) { + head->node = node; + head->tail = node; + } else { + head->tail->next = node; + head->tail = head->tail->next; + } + status = SWITCH_STATUS_SUCCESS; + } + } + + switch_thread_rwlock_unlock(globals.event_channel_rwlock); + + if (status == SWITCH_STATUS_SUCCESS && !strncasecmp(event_channel, "presence", 8)) { + presence_ping(event_channel); + } + + return status; +} + +static uint32_t ID = 1; + +static void close_file(int *sock) +{ + if (*sock > -1) { + close(*sock); + *sock = -1; + } +} + +static void close_socket(int *sock) +{ + if (*sock > -1) { + shutdown(*sock, 2); + close_file(sock); + } +} + +static void del_jsock(jsock_t *jsock) +{ + jsock_t *p, *last = NULL; + + jsock_unsub_channel(jsock, NULL); + switch_event_channel_permission_clear(jsock->uuid_str); + + switch_mutex_lock(jsock->profile->mutex); + for(p = jsock->profile->jsock_head; p; p = p->next) { + if (p == jsock) { + if (last) { + last->next = p->next; + } else { + jsock->profile->jsock_head = p->next; + } + jsock->profile->jsock_count--; + break; + } + + last = p; + } + switch_mutex_unlock(jsock->profile->mutex); + +} + +static void add_jsock(jsock_t *jsock) +{ + + switch_mutex_lock(jsock->profile->mutex); + jsock->next = jsock->profile->jsock_head; + jsock->profile->jsock_head = jsock; + jsock->profile->jsock_count++; + switch_mutex_unlock(jsock->profile->mutex); + +} + +static uint32_t next_id(void) +{ + uint32_t id; + + switch_mutex_lock(globals.mutex); + id = ID++; + switch_mutex_unlock(globals.mutex); + + return id; +} + +static cJSON *jrpc_new(uint32_t id) +{ + cJSON *obj = cJSON_CreateObject(); + cJSON_AddItemToObject(obj, "jsonrpc", cJSON_CreateString("2.0")); + + if (id) { + cJSON_AddItemToObject(obj, "id", cJSON_CreateNumber(id)); + } + + return obj; +} + +static cJSON *jrpc_new_req(const char *method, const char *call_id, cJSON **paramsP) +{ + cJSON *msg, *params = NULL; + uint32_t id = next_id(); + + msg = jrpc_new(id); + + if (paramsP && *paramsP) { + params = *paramsP; + } + + if (!params) { + params = cJSON_CreateObject(); + } + + cJSON_AddItemToObject(msg, "method", cJSON_CreateString(method)); + cJSON_AddItemToObject(msg, "params", params); + + if (call_id) { + cJSON_AddItemToObject(params, "callID", cJSON_CreateString(call_id)); + } + + if (paramsP) { + *paramsP = params; + } + + return msg; +} + +static void jrpc_add_id(cJSON *obj, cJSON *jid, const char *idstr, int id) +{ + if (jid) { + cJSON_AddItemToObject(obj, "id", cJSON_Duplicate(jid, 1)); + } else if (idstr) { + cJSON_AddItemToObject(obj, "id", zstr(idstr) ? cJSON_CreateNull() : cJSON_CreateString(idstr)); + } else { + cJSON_AddItemToObject(obj, "id", cJSON_CreateNumber(id)); + } +} + +static void jrpc_add_error(cJSON *obj, int code, const char *message, cJSON *jid) +{ + cJSON *error = cJSON_CreateObject(); + + cJSON_AddItemToObject(obj, "error", error); + cJSON_AddItemToObject(error, "code", cJSON_CreateNumber(code)); + cJSON_AddItemToObject(error, "message", cJSON_CreateString(message)); + if (!cJSON_GetObjectItem(obj, "id")) { + jrpc_add_id(obj, jid, "", 0); + } +} + +static void jrpc_add_result(cJSON *obj, cJSON *result) +{ + if (result) { + cJSON_AddItemToObject(obj, "result", result); + } +} + +static switch_ssize_t ws_write_json(jsock_t *jsock, cJSON **json, switch_bool_t destroy) +{ + char *json_text; + switch_ssize_t r = -1; + + switch_assert(json); + + if (!*json) { + return r; + } + + if (jsock->uuid_str) { + cJSON *result = cJSON_GetObjectItem(*json, "result"); + + if (result) { + cJSON_AddItemToObject(result, "sessid", cJSON_CreateString(jsock->uuid_str)); + } + } + + if ((json_text = cJSON_PrintUnformatted(*json))) { + if (jsock->profile->debug || globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "WRITE %s [%s]\n", jsock->name, json_text); + } + switch_mutex_lock(jsock->write_mutex); + ws_write_frame(&jsock->ws, WSOC_TEXT, json_text, strlen(json_text)); + switch_mutex_unlock(jsock->write_mutex); + switch_safe_free(json_text); + } + + if (destroy) { + cJSON_Delete(*json); + *json = NULL; + } + + return r; +} + + +static void write_event(const char *event_channel, jsock_t *use_jsock, cJSON *event) +{ + jsock_sub_node_head_t *head; + + if ((head = switch_core_hash_find(globals.event_channel_hash, event_channel))) { + jsock_sub_node_t *np; + + for(np = head->node; np; np = np->next) { + cJSON *msg = NULL, *params; + + if (!use_jsock || use_jsock == np->jsock) { + params = cJSON_Duplicate(event, 1); + msg = jrpc_new_req("verto.event", NULL, ¶ms); + ws_write_json(np->jsock, &msg, SWITCH_TRUE); + } + } + } +} + +static void jsock_send_event(cJSON *event) +{ + + const char *event_channel, *session_uuid = NULL; + jsock_t *use_jsock = NULL; + switch_core_session_t *session = NULL; + + if (!(event_channel = cJSON_GetObjectCstr(event, "eventChannel"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO EVENT CHANNEL SPECIFIED\n"); + return; + } + + + if ((session = switch_core_session_locate(event_channel))) { + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str"); + if (jsock_uuid_str) { + use_jsock = get_jsock(jsock_uuid_str); + } + switch_core_session_rwunlock(session); + } + + if (use_jsock || (use_jsock = get_jsock(event_channel))) { /* implicit subscription to channel identical to the connection uuid or session uuid */ + cJSON *msg = NULL, *params; + params = cJSON_Duplicate(event, 1); + msg = jrpc_new_req("verto.event", NULL, ¶ms); + ws_write_json(use_jsock, &msg, SWITCH_TRUE); + switch_thread_rwlock_unlock(use_jsock->rwlock); + use_jsock = NULL; + return; + } + + + if ((session_uuid = cJSON_GetObjectCstr(event, "sessid"))) { + if (!(use_jsock = get_jsock(session_uuid))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Socket %s not connected\n", session_uuid); + return; + } + } + + switch_thread_rwlock_rdlock(globals.event_channel_rwlock); + write_event(event_channel, use_jsock, event); + if (strchr(event_channel, '.')) { + char *main_channel = strdup(event_channel); + char *p = strchr(main_channel, '.'); + if (p) *p = '\0'; + write_event(main_channel, use_jsock, event); + free(main_channel); + } + switch_thread_rwlock_unlock(globals.event_channel_rwlock); + + if (use_jsock) { + switch_thread_rwlock_unlock(use_jsock->rwlock); + use_jsock = NULL; + } +} + +static jrpc_func_t jrpc_get_func(jsock_t *jsock, const char *method) +{ + jrpc_func_t func = NULL; + char *main_method = NULL; + + switch_assert(method); + + if (jsock->allowed_methods) { + if (strchr(method, '.')) { + char *p; + main_method = strdup(method); + if ((p = strchr(main_method, '.'))) { + *p = '\0'; + } + } + + if (!(switch_event_get_header(jsock->allowed_methods, method) || (main_method && switch_event_get_header(jsock->allowed_methods, main_method)))) { + goto end; + } + } + + switch_mutex_lock(globals.method_mutex); + func = (jrpc_func_t) (intptr_t) switch_core_hash_find(globals.method_hash, method); + switch_mutex_unlock(globals.method_mutex); + + end: + + switch_safe_free(main_method); + + return func; +} + + +static void jrpc_add_func(const char *method, jrpc_func_t func) +{ + switch_assert(method); + switch_assert(func); + + switch_mutex_lock(globals.method_mutex); + switch_core_hash_insert(globals.method_hash, method, (void *) (intptr_t) func); + switch_mutex_unlock(globals.method_mutex); +} + +static char *MARKER = "X"; + +static void set_perm(const char *str, switch_event_t **event) +{ + char delim = ','; + char *cur, *next; + int count = 0; + char *edup; + + if (!zstr(str)) { + if (!strcasecmp(str, "__ANY__")) { + return; + } + } + + switch_event_create(event, SWITCH_EVENT_REQUEST_PARAMS); + + if (!zstr(str)) { + edup = strdup(str); + cur = edup; + + if (strchr(edup, ' ')) { + delim = ' '; + } + + for (cur = edup; cur; count++) { + if ((next = strchr(cur, delim))) { + *next++ = '\0'; + } + + switch_event_add_header_string(*event, SWITCH_STACK_BOTTOM, cur, MARKER); + + cur = next; + } + + switch_safe_free(edup); + + } +} + +static void check_permissions(jsock_t *jsock, switch_xml_t x_user, cJSON *params) +{ + switch_xml_t x_param, x_params; + const char *allowed_methods = NULL, *allowed_jsapi = NULL, *allowed_fsapi = NULL, *allowed_event_channels = NULL; + + if ((x_params = switch_xml_child(x_user, "params"))) { + for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) { + const char *var = switch_xml_attr(x_param, "name"); + const char *val = switch_xml_attr(x_param, "value"); + + if (zstr(val) || zstr(var)) { + continue; + } + + if (!strcasecmp(var, "jsonrpc-allowed-methods")) { + allowed_methods = val; + } + + if (!strcasecmp(var, "jsonrpc-allowed-jsapi")) { + allowed_jsapi = val; + } + + if (!strcasecmp(var, "jsonrpc-allowed-fsapi")) { + allowed_fsapi = val; + } + + if (!strcasecmp(var, "jsonrpc-allowed-event-channels")) { + allowed_event_channels = val; + } + } + } + + set_perm(allowed_methods, &jsock->allowed_methods); + set_perm(allowed_jsapi, &jsock->allowed_jsapi); + set_perm(allowed_fsapi, &jsock->allowed_fsapi); + set_perm(allowed_event_channels, &jsock->allowed_event_channels); + + switch_event_add_header_string(jsock->allowed_methods, SWITCH_STACK_BOTTOM, "login", MARKER); + +} + +static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *message, switch_size_t mlen) +{ + switch_bool_t r = SWITCH_FALSE; + const char *passwd = NULL; + const char *login = NULL; + + if (!params) { + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Missing params"); + goto end; + } + + login = cJSON_GetObjectCstr(params, "login"); + passwd = cJSON_GetObjectCstr(params, "passwd"); + + if (zstr(login)) { + goto end; + } + + if (zstr(passwd)) { + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Missing passwd"); + goto end; + } + + + if (!strcmp(login, "root")) { + if (!(r = !strcmp(passwd, jsock->profile->root_passwd))) { + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Authentication Failure"); + } + + } else if (!zstr(jsock->profile->userauth)) { + switch_xml_t x_user = NULL; + char *id = NULL, *domain = NULL; + switch_event_t *req_params; + + if (*jsock->profile->userauth == '@') { + domain = jsock->profile->userauth + 1; + id = (char *) login; + } else if (switch_true(jsock->profile->userauth)) { + id = switch_core_strdup(jsock->pool, login); + + if ((domain = strchr(id, '@'))) { + *domain++ = '\0'; + } + + } + + if (!(id && domain)) { + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Missing or improper credentials"); + goto end; + } + + switch_event_create(&req_params, SWITCH_EVENT_REQUEST_PARAMS); + switch_assert(req_params); + + switch_event_add_header_string(req_params, SWITCH_STACK_BOTTOM, "action", "jsonrpc-authenticate"); + + if (switch_xml_locate_user_merged("id", id, domain, NULL, &x_user, req_params) != SWITCH_STATUS_SUCCESS) { + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Login Incorrect"); + } else { + switch_xml_t x_param, x_params; + const char *use_passwd = NULL, *verto_context = NULL, *verto_dialplan = NULL; + + jsock->id = switch_core_strdup(jsock->pool, id); + jsock->domain = switch_core_strdup(jsock->pool, domain); + jsock->uid = switch_core_sprintf(jsock->pool, "%s@%s", id, domain); + + + if ((x_params = switch_xml_child(x_user, "params"))) { + for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) { + const char *var = switch_xml_attr_soft(x_param, "name"); + const char *val = switch_xml_attr_soft(x_param, "value"); + + if (!use_passwd && !strcasecmp(var, "password")) { + use_passwd = val; + } else if (!strcasecmp(var, "jsonrpc-password")) { + use_passwd = val; + } else if (!strcasecmp(var, "verto-context")) { + verto_context = val; + } else if (!strcasecmp(var, "verto-dialplan")) { + verto_dialplan = val; + } + + switch_event_add_header(jsock->params, SWITCH_STACK_BOTTOM, var, val); + } + } + + if ((x_params = switch_xml_child(x_user, "variables"))) { + for (x_param = switch_xml_child(x_params, "variable"); x_param; x_param = x_param->next) { + const char *var = switch_xml_attr_soft(x_param, "name"); + const char *val = switch_xml_attr_soft(x_param, "value"); + + switch_event_add_header(jsock->vars, SWITCH_STACK_BOTTOM, var, val); + } + } + + if (!zstr(verto_dialplan)) { + jsock->dialplan = switch_core_strdup(jsock->pool, verto_dialplan); + } + + if (!zstr(verto_context)) { + jsock->context = switch_core_strdup(jsock->pool, verto_context); + } + + if (zstr(use_passwd) || strcmp(passwd, use_passwd)) { + r = SWITCH_FALSE; + *code = CODE_AUTH_FAILED; + switch_snprintf(message, mlen, "Authentication Failure"); + } else { + r = SWITCH_TRUE; + check_permissions(jsock, x_user, params); + } + + switch_xml_free(x_user); + } + + switch_event_destroy(&req_params); + } + + + end: + + return r; + +} + +static void set_call_params(cJSON *params, verto_pvt_t *tech_pvt) { + const char *caller_id_name = NULL; + const char *caller_id_number = NULL; + + if (switch_channel_outbound_display(tech_pvt->channel)) { + caller_id_name = switch_channel_get_variable(tech_pvt->channel, "caller_id_name"); + caller_id_number = switch_channel_get_variable(tech_pvt->channel, "caller_id_number"); + } else { + caller_id_name = switch_channel_get_variable(tech_pvt->channel, "callee_id_name"); + caller_id_number = switch_channel_get_variable(tech_pvt->channel, "callee_id_number"); + } + + if (zstr(caller_id_name)) { + caller_id_name = "Outbound Call"; + } + + if (zstr(caller_id_number)) { + caller_id_number = switch_channel_get_variable(tech_pvt->channel, "destination_number"); + } + + cJSON_AddItemToObject(params, "caller_id_name", cJSON_CreateString(caller_id_name)); + cJSON_AddItemToObject(params, "caller_id_number", cJSON_CreateString(caller_id_number)); +} + +static jsock_t *get_jsock(const char *uuid) +{ + jsock_t *jsock = NULL; + + switch_mutex_lock(globals.jsock_mutex); + if ((jsock = switch_core_hash_find(globals.jsock_hash, uuid))) { + if (switch_thread_rwlock_tryrdlock(jsock->rwlock) != SWITCH_STATUS_SUCCESS) { + jsock = NULL; + } + } + switch_mutex_unlock(globals.jsock_mutex); + + return jsock; +} + +static void attach_jsock(jsock_t *jsock) +{ + switch_mutex_lock(globals.jsock_mutex); + switch_core_hash_insert(globals.jsock_hash, jsock->uuid_str, jsock); + switch_mutex_unlock(globals.jsock_mutex); +} + +static void detach_jsock(jsock_t *jsock) +{ + switch_mutex_lock(globals.jsock_mutex); + switch_core_hash_delete(globals.jsock_hash, jsock->uuid_str); + switch_mutex_unlock(globals.jsock_mutex); +} + +static int attach_wake(void) +{ + switch_status_t status; + int tries = 0; + + top: + + status = switch_mutex_trylock(globals.detach_mutex); + + if (status == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(globals.detach_cond); + switch_mutex_unlock(globals.detach_mutex); + return 1; + } else { + if (switch_mutex_trylock(globals.detach2_mutex) == SWITCH_STATUS_SUCCESS) { + switch_mutex_unlock(globals.detach2_mutex); + } else { + if (++tries < 10) { + switch_cond_next(); + goto top; + } + } + } + + return 0; +} + +static void tech_reattach(verto_pvt_t *tech_pvt, jsock_t *jsock) +{ + cJSON *params = NULL; + cJSON *msg = NULL; + + tech_pvt->detach_time = 0; + globals.detached--; + attach_wake(); + switch_set_flag(tech_pvt, TFLAG_ATTACH_REQ); + msg = jrpc_new_req("verto.attach", tech_pvt->call_id, ¶ms); + cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str)); + set_call_params(params, tech_pvt); + ws_write_json(jsock, &msg, SWITCH_TRUE); +} + +static void drop_detached(void) +{ + verto_pvt_t *tech_pvt; + switch_time_t now = switch_epoch_time_now(NULL); + + switch_thread_rwlock_rdlock(globals.tech_rwlock); + for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) { + if (!switch_channel_up_nosig(tech_pvt->channel)) { + continue; + } + + if (tech_pvt->detach_time && (now - tech_pvt->detach_time) > globals.detach_timeout) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE); + } + } + switch_thread_rwlock_unlock(globals.tech_rwlock); +} + +static void attach_calls(jsock_t *jsock) +{ + verto_pvt_t *tech_pvt; + + switch_thread_rwlock_rdlock(globals.tech_rwlock); + for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) { + if (tech_pvt->detach_time && !strcmp(tech_pvt->jsock_uuid, jsock->uuid_str)) { + if (!switch_channel_up_nosig(tech_pvt->channel)) { + continue; + } + + tech_reattach(tech_pvt, jsock); + } + } + switch_thread_rwlock_unlock(globals.tech_rwlock); +} + +static void detach_calls(jsock_t *jsock) +{ + verto_pvt_t *tech_pvt; + + switch_thread_rwlock_rdlock(globals.tech_rwlock); + for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) { + if (!strcmp(tech_pvt->jsock_uuid, jsock->uuid_str)) { + if (!switch_channel_up_nosig(tech_pvt->channel)) { + continue; + } + + tech_pvt->detach_time = switch_epoch_time_now(NULL); + globals.detached++; + attach_wake(); + } + } + switch_thread_rwlock_unlock(globals.tech_rwlock); +} + +static void process_jrpc_response(jsock_t *jsock, cJSON *json) +{ +} + +static void set_session_id(jsock_t *jsock, const char *uuid) +{ + //cJSON *params, *msg = jrpc_new(0); + + if (!zstr(uuid)) { + switch_set_string(jsock->uuid_str, uuid); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s re-connecting session %s\n", jsock->name, jsock->uuid_str); + } else { + switch_uuid_str(jsock->uuid_str, sizeof(jsock->uuid_str)); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s new RPC session %s\n", jsock->name, jsock->uuid_str); + } + + attach_jsock(jsock); + +} + +static cJSON *process_jrpc(jsock_t *jsock, cJSON *json) +{ + cJSON *reply = NULL, *echo = NULL, *id = NULL, *params = NULL, *response = NULL, *result; + const char *method = NULL, *version = NULL, *sessid = NULL; + jrpc_func_t func = NULL; + + switch_assert(json); + + method = cJSON_GetObjectCstr(json, "method"); + result = cJSON_GetObjectItem(json, "result"); + version = cJSON_GetObjectCstr(json, "jsonrpc"); + id = cJSON_GetObjectItem(json, "id"); + + if ((params = cJSON_GetObjectItem(json, "params"))) { + sessid = cJSON_GetObjectCstr(params, "sessid"); + } + + if (!switch_test_flag(jsock, JPFLAG_INIT)) { + set_session_id(jsock, sessid); + switch_set_flag(jsock, JPFLAG_INIT); + } + + if (zstr(version) || strcmp(version, "2.0")) { + reply = jrpc_new(0); + jrpc_add_error(reply, CODE_INVALID, "Invalid message", id); + goto end; + } + + if (result) { + process_jrpc_response(jsock, json); + return NULL; + } + + reply = jrpc_new(0); + + jrpc_add_id(reply, id, "", 0); + + if (!switch_test_flag(jsock, JPFLAG_AUTHED) && (jsock->profile->userauth || jsock->profile->root_passwd)) { + int code = CODE_AUTH_REQUIRED; + char message[128] = "Authentication Required"; + + if (!check_auth(jsock, params, &code, message, sizeof(message))) { + jrpc_add_error(reply, code, message, id); + goto end; + } + switch_set_flag(jsock, JPFLAG_AUTHED); + } + + if (!method || !(func = jrpc_get_func(jsock, method))) { + jrpc_add_error(reply, -32601, "Invalid Method, Missing Method or Permission Denied", id); + } else { + if (func(method, params, jsock, &response) == SWITCH_TRUE) { + + if (params) { + echo = cJSON_GetObjectItem(params, "echoParams"); + } + if (echo) { + if ((echo->type == cJSON_True || (echo->type == cJSON_String && switch_true(echo->valuestring)))) { + cJSON_AddItemToObject(response, "requestParams", cJSON_Duplicate(params, 1)); + } else { + cJSON_AddItemToObject(response, "requestParams", cJSON_Duplicate(echo, 1)); + } + } + + jrpc_add_result(reply, response); + } else { + if (response) { + cJSON_AddItemToObject(reply, "error", response); + } else { + jrpc_add_error(reply, -32602, "Permission Denied", id); + } + } + } + + end: + + return reply; +} + +static switch_status_t process_input(jsock_t *jsock, uint8_t *data, switch_ssize_t bytes) +{ + cJSON *json = NULL, *reply = NULL; + char *ascii = (char *) data; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (ascii) { + if (jsock->profile->debug || globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "READ %s [%s]\n", jsock->name, ascii); + } + json = cJSON_Parse(ascii); + } + + if (json) { + if (json->type == cJSON_Array) { /* batch mode */ + int i, len = cJSON_GetArraySize(json); + + reply = cJSON_CreateArray(); + + for(i = 0; i < len; i++) { + cJSON *obj, *item = cJSON_GetArrayItem(json, i); + + if ((obj = process_jrpc(jsock, item))) { + cJSON_AddItemToArray(reply, obj); + } + } + } else { + reply = process_jrpc(jsock, json); + } + } else { + reply = jrpc_new(0); + jrpc_add_error(reply, -32600, "Invalid Request", NULL); + } + + if (reply) { + ws_write_json(jsock, &reply, SWITCH_TRUE); + } + + if (json) { + cJSON_Delete(json); + } + + return status; +} + +static void client_run(jsock_t *jsock) +{ + + jsock->local_addr.sin_family = AF_INET; + jsock->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + jsock->local_addr.sin_port = 0; + + + if (ws_init(&jsock->ws, jsock->client_socket, (jsock->ptype & PTYPE_CLIENT_SSL) ? jsock->profile->ssl_ctx : NULL, 0, 1) < 0) { + die("%s WS SETUP FAILED", jsock->name); + } + + while(jsock->profile->running) { + struct pollfd pfds[1]; + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = jsock->client_socket; + pfds[0].events = POLLIN|POLLERR|POLLHUP|POLLRDNORM|POLLRDBAND|POLLPRI; + + + if ((res = poll(pfds, 1, -1)) < 0) { + if (errno != EINTR) { + die("%s POLL FAILED\n", jsock->name); + } + } + + if (res < 0) { + die("%s POLL ERROR\n", jsock->name); + } + + if (jsock->drop) { + die("%s Dropping Connection\n", jsock->name); + } + + + if (pfds[0].revents & POLLERR) { + die("%s POLL ERROR\n", jsock->name); + } + + if (pfds[0].revents & POLLHUP) { + die("%s POLL HANGUP DETECTED\n", jsock->name); + } + + if (pfds[0].revents & POLLNVAL) { + die("%s POLL INVALID SOCKET\n", jsock->name); + } + + if (pfds[0].revents & POLLIN) { + switch_ssize_t bytes; + ws_opcode_t oc; + uint8_t *data; + + bytes = ws_read_frame(&jsock->ws, &oc, &data); + + if (bytes < 0) { + die("BAD READ %ld\n", bytes); + break; + } + + if (bytes) { + if (process_input(jsock, data, bytes) != SWITCH_STATUS_SUCCESS) { + die("Input Error\n"); + } + + if (!switch_test_flag(jsock, JPFLAG_CHECK_ATTACH) && switch_test_flag(jsock, JPFLAG_AUTHED)) { + attach_calls(jsock); + switch_set_flag(jsock, JPFLAG_CHECK_ATTACH); + } + } + } + } + + error: + + detach_jsock(jsock); + ws_destroy(&jsock->ws); + + return; +} + +static void *SWITCH_THREAD_FUNC client_thread(switch_thread_t *thread, void *obj) +{ + jsock_t *jsock = (jsock_t *) obj; + + switch_event_create(&jsock->params, SWITCH_EVENT_CHANNEL_DATA); + switch_event_create(&jsock->vars, SWITCH_EVENT_CHANNEL_DATA); + + + add_jsock(jsock); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Starting client thread.\n", jsock->name); + + if ((jsock->ptype & PTYPE_CLIENT) || (jsock->ptype & PTYPE_CLIENT_SSL)) { + client_run(jsock); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Ending client thread.\n", jsock->name); + } + + detach_calls(jsock); + + del_jsock(jsock); + + switch_event_destroy(&jsock->params); + switch_event_destroy(&jsock->vars); + + if (jsock->client_socket > -1) { + close_socket(&jsock->client_socket); + } + + switch_event_destroy(&jsock->allowed_methods); + switch_event_destroy(&jsock->allowed_fsapi); + switch_event_destroy(&jsock->allowed_jsapi); + switch_event_destroy(&jsock->allowed_event_channels); + + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Ending client thread.\n", jsock->name); + switch_thread_rwlock_wrlock(jsock->rwlock); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Thread ended\n", jsock->name); + switch_thread_rwlock_unlock(jsock->rwlock); + + return NULL; +} + + +static switch_bool_t auth_api_command(jsock_t *jsock, const char *api_cmd, const char *arg) +{ + const char *check_cmd = api_cmd; + char *sneaky_commands[] = { "bgapi", "sched_api", "eval", "expand", "xml_wrap", NULL }; + int x = 0; + char *dup_arg = NULL; + char *next = NULL; + switch_bool_t ok = SWITCH_TRUE; + + top: + + if (!jsock->allowed_fsapi) { + ok = SWITCH_FALSE; + goto end; + } + + if (!switch_event_get_header(jsock->allowed_fsapi, check_cmd)) { + ok = SWITCH_FALSE; + goto end; + } + + while (check_cmd) { + for (x = 0; sneaky_commands[x]; x++) { + if (!strcasecmp(sneaky_commands[x], check_cmd)) { + if (check_cmd == api_cmd) { + if (arg) { + switch_safe_free(dup_arg); + dup_arg = strdup(arg); + check_cmd = dup_arg; + if ((next = strchr(check_cmd, ' '))) { + *next++ = '\0'; + } + } else { + break; + } + } else { + if (next) { + check_cmd = next; + } else { + check_cmd = dup_arg; + } + + if ((next = strchr(check_cmd, ' '))) { + *next++ = '\0'; + } + } + goto top; + } + } + break; + } + + end: + + switch_safe_free(dup_arg); + return ok; + +} + +//// VERTO + +static void track_pvt(verto_pvt_t *tech_pvt) +{ + switch_thread_rwlock_wrlock(globals.tech_rwlock); + tech_pvt->next = globals.tech_head; + globals.tech_head = tech_pvt; + switch_thread_rwlock_unlock(globals.tech_rwlock); +} + +static void untrack_pvt(verto_pvt_t *tech_pvt) +{ + verto_pvt_t *p, *last = NULL; + + switch_thread_rwlock_wrlock(globals.tech_rwlock); + if (tech_pvt->detach_time) { + globals.detached--; + tech_pvt->detach_time = 0; + attach_wake(); + } + + for(p = globals.tech_head; p; p = p->next) { + if (p == tech_pvt) { + if (last) { + last->next = p->next; + } else { + globals.tech_head = p->next; + } + break; + } + + last = p; + } + switch_thread_rwlock_unlock(globals.tech_rwlock); +} + + +static switch_status_t verto_on_hangup(switch_core_session_t *session) +{ + jsock_t *jsock = NULL; + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + untrack_pvt(tech_pvt); + + // get the jsock and send hangup notice + if (!tech_pvt->remote_hangup_cause && (jsock = get_jsock(tech_pvt->jsock_uuid))) { + cJSON *msg = jrpc_new_req("verto.bye", tech_pvt->call_id, NULL); + ws_write_json(jsock, &msg, SWITCH_TRUE); + + switch_thread_rwlock_unlock(jsock->rwlock); + } + + return SWITCH_STATUS_SUCCESS; +} + +static void verto_set_media_options(verto_pvt_t *tech_pvt, verto_profile_t *profile); + +static switch_status_t verto_connect(switch_core_session_t *session, const char *method) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + jsock_t *jsock = NULL; + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + if (!(jsock = get_jsock(tech_pvt->jsock_uuid))) { + status = SWITCH_STATUS_BREAK; + } else { + cJSON *params = NULL; + cJSON *msg = NULL; + const char *var = NULL; + switch_caller_profile_t *caller_profile = switch_channel_get_caller_profile(tech_pvt->channel); + + + switch_channel_set_variable(tech_pvt->channel, "verto_user", jsock->uid); + switch_channel_set_variable(tech_pvt->channel, "verto_host", jsock->domain); + + if ((var = switch_event_get_header(jsock->params, "caller-id-name"))) { + caller_profile->callee_id_name = switch_core_strdup(caller_profile->pool, var); + } + + if ((var = switch_event_get_header(jsock->params, "caller-id-number"))) { + caller_profile->callee_id_number = switch_core_strdup(caller_profile->pool, var); + } + + if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) { + switch_core_media_absorb_sdp(session); + } else { + switch_channel_set_variable(tech_pvt->channel, "media_webrtc", "true"); + switch_core_session_set_ice(tech_pvt->session); + + verto_set_media_options(tech_pvt, jsock->profile); + + switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_TRUE); + switch_channel_set_variable(tech_pvt->channel, "verto_profile_name", jsock->profile->name); + + if (!switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING)) { + if ((status = switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_TRUE)) != SWITCH_STATUS_SUCCESS) { + //if ((status = switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0)) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_thread_rwlock_unlock(jsock->rwlock); + return status; + } + } + + switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0); + } + + msg = jrpc_new_req(method, tech_pvt->call_id, ¶ms); + + if (tech_pvt->mparams->local_sdp_str) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local %s SDP %s:\n%s\n", + method, + switch_channel_get_name(tech_pvt->channel), + tech_pvt->mparams->local_sdp_str); + + cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str)); + set_call_params(params, tech_pvt); + + ws_write_json(jsock, &msg, SWITCH_TRUE); + } else { + status = SWITCH_STATUS_FALSE; + } + + switch_thread_rwlock_unlock(jsock->rwlock); + } + + return status; +} + +switch_status_t verto_tech_media(verto_pvt_t *tech_pvt, const char *r_sdp, switch_sdp_type_t sdp_type) +{ + uint8_t match = 0, p = 0; + + switch_assert(tech_pvt != NULL); + switch_assert(r_sdp != NULL); + + if (zstr(r_sdp)) { + return SWITCH_STATUS_FALSE; + } + + if ((match = switch_core_media_negotiate_sdp(tech_pvt->session, r_sdp, &p, sdp_type))) { + if (switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + //if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + //if (!switch_channel_test_flag(tech_pvt->channel, CF_ANSWERED)) { + // switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA"); + // switch_channel_mark_pre_answered(tech_pvt->channel); + //} + return SWITCH_STATUS_SUCCESS; + } + + + return SWITCH_STATUS_FALSE; +} + +static switch_status_t verto_on_init(switch_core_session_t *session) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + if (switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING_BRIDGE) || switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING)) { + int tries = 120; + + switch_core_session_clear_crypto(session); + + while(--tries > 0) { + + status = verto_connect(session, "verto.attach"); + + if (status == SWITCH_STATUS_SUCCESS) { + switch_set_flag(tech_pvt, TFLAG_ATTACH_REQ); + break; + } else if (status == SWITCH_STATUS_BREAK) { + switch_yield(1000000); + continue; + } else { + tries = 0; + break; + } + } + + if (!tries) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + status = SWITCH_STATUS_FALSE; + } + + switch_channel_set_flag(tech_pvt->channel, CF_VIDEO_BREAK); + switch_core_session_kill_channel(tech_pvt->session, SWITCH_SIG_BREAK); + + tries = 500; + while(--tries > 0 && switch_test_flag(tech_pvt, TFLAG_ATTACH_REQ)) { + switch_yield(10000); + } + + switch_core_session_refresh_video(session); + switch_channel_set_flag(tech_pvt->channel, CF_VIDEO_BREAK); + switch_core_session_kill_channel(tech_pvt->session, SWITCH_SIG_BREAK); + + return status; + } + + if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + if ((status = verto_connect(tech_pvt->session, "verto.invite")) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + } + + return status; +} + + +static switch_state_handler_table_t verto_state_handlers = { + /*.on_init */ verto_on_init, + /*.on_routing */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ verto_on_hangup, + /*.on_exchange_media */ NULL, + /*.on_soft_execute */ NULL, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park */ NULL, + /*.on_reporting */ NULL, + /*.on_destroy */ NULL, + SSH_FLAG_STICKY +}; + + + + +static void verto_set_media_options(verto_pvt_t *tech_pvt, verto_profile_t *profile) +{ + int i; + + tech_pvt->mparams->rtpip = switch_core_session_strdup(tech_pvt->session, profile->rtpip[profile->rtpip_cur++]); + + if (profile->rtpip_cur == profile->rtpip_index) { + profile->rtpip_cur = 0; + } + + tech_pvt->mparams->extrtpip = profile->extrtpip; + + //tech_pvt->mparams->dtmf_type = tech_pvt->profile->dtmf_type; + switch_channel_set_flag(tech_pvt->channel, CF_TRACKABLE); + switch_channel_set_variable(tech_pvt->channel, "secondary_recovery_module", modname); + + switch_core_media_check_dtmf_type(tech_pvt->session); + + //switch_channel_set_cap(tech_pvt->channel, CC_MEDIA_ACK); + switch_channel_set_cap(tech_pvt->channel, CC_BYPASS_MEDIA); + //switch_channel_set_cap(tech_pvt->channel, CC_PROXY_MEDIA); + switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER); + switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP); + + //switch_channel_set_cap(tech_pvt->channel, CC_QUEUEABLE_DTMF_DELAY); + //tech_pvt->mparams->ndlb = tech_pvt->profile->mndlb; + + tech_pvt->mparams->inbound_codec_string = switch_core_session_strdup(tech_pvt->session, profile->inbound_codec_string); + tech_pvt->mparams->outbound_codec_string = switch_core_session_strdup(tech_pvt->session, profile->outbound_codec_string); + + tech_pvt->mparams->jb_msec = "-3"; + switch_media_handle_set_media_flag(tech_pvt->smh, SCMF_SUPPRESS_CNG); + + switch_media_handle_set_media_flag(tech_pvt->smh, SCMF_RENEG_ON_REINVITE); + + //tech_pvt->mparams->auto_rtp_bugs = profile->auto_rtp_bugs; + tech_pvt->mparams->timer_name = profile->timer_name; + //tech_pvt->mparams->vflags = profile->vflags; + //tech_pvt->mparams->manual_rtp_bugs = profile->manual_rtp_bugs; + //tech_pvt->mparams->manual_video_rtp_bugs = profile->manual_video_rtp_bugs; + + + tech_pvt->mparams->local_network = switch_core_session_strdup(tech_pvt->session, profile->local_network); + + + //tech_pvt->mparams->rtcp_audio_interval_msec = profile->rtpp_audio_interval_msec; + //tech_pvt->mparams->rtcp_video_interval_msec = profile->rtpp_video_interval_msec; + //tech_pvt->mparams->sdp_username = profile->sdp_username; + //tech_pvt->mparams->cng_pt = tech_pvt->cng_pt; + //tech_pvt->mparams->rtc_timeout_sec = profile->rtp_timeout_sec; + //tech_pvt->mparams->rtc_hold_timeout_sec = profile->rtp_hold_timeout_sec; + //switch_media_handle_set_media_flags(tech_pvt->media_handle, tech_pvt->profile->media_flags); + + + for(i = 0; i < profile->cand_acl_count; i++) { + switch_core_media_add_ice_acl(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, profile->cand_acl[i]); + switch_core_media_add_ice_acl(tech_pvt->session, SWITCH_MEDIA_TYPE_VIDEO, profile->cand_acl[i]); + } +} + +static switch_status_t verto_media(switch_core_session_t *session) +{ + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + switch_status_t status = SWITCH_STATUS_SUCCESS; + + switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_TRUE); + + if (tech_pvt->r_sdp) { + if (verto_tech_media(tech_pvt, tech_pvt->r_sdp, SDP_TYPE_REQUEST) != SWITCH_STATUS_SUCCESS) { + switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); + return SWITCH_STATUS_FALSE; + } + } + + if ((status = switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_FALSE)) != SWITCH_STATUS_SUCCESS) { + //if ((status = switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0)) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return status; + } + + switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0); + + if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + if (tech_pvt->mparams->local_sdp_str) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel), + tech_pvt->mparams->local_sdp_str); + } else { + status = SWITCH_STATUS_FALSE; + } + + return status; +} + + +static switch_status_t verto_send_media_indication(switch_core_session_t *session, const char *method) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + const char *proxy_sdp = NULL; + + if (switch_test_flag(tech_pvt, TFLAG_SENT_MEDIA)) { + status = SWITCH_STATUS_SUCCESS; + } + + if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) { + if ((proxy_sdp = switch_channel_get_variable(tech_pvt->channel, SWITCH_B_SDP_VARIABLE))) { + status = SWITCH_STATUS_SUCCESS; + switch_core_media_set_local_sdp(session, proxy_sdp, SWITCH_TRUE); + } + } + + + if (status == SWITCH_STATUS_SUCCESS || (status = verto_media(session)) == SWITCH_STATUS_SUCCESS) { + jsock_t *jsock = NULL; + + if (!(jsock = get_jsock(tech_pvt->jsock_uuid))) { + status = SWITCH_STATUS_FALSE; + } else { + cJSON *params = NULL; + cJSON *msg = jrpc_new_req(method, tech_pvt->call_id, ¶ms); + + if (!switch_test_flag(tech_pvt, TFLAG_SENT_MEDIA)) { + cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str)); + } + + switch_set_flag(tech_pvt, TFLAG_SENT_MEDIA); + + ws_write_json(jsock, &msg, SWITCH_TRUE); + switch_thread_rwlock_unlock(jsock->rwlock); + } + } + + return status; +} + +static switch_status_t messagehook (switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_status_t r = SWITCH_STATUS_SUCCESS; + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + switch(msg->message_id) { + case SWITCH_MESSAGE_INDICATE_DISPLAY: + { + const char *name, *number; + cJSON *jmsg = NULL, *params = NULL; + jsock_t *jsock = NULL; + + if ((jsock = get_jsock(tech_pvt->jsock_uuid))) { + name = msg->string_array_arg[0]; + number = msg->string_array_arg[1]; + + if (name || number) { + jmsg = jrpc_new_req("verto.display", tech_pvt->call_id, ¶ms); + cJSON_AddItemToObject(params, "display_name", cJSON_CreateString(name)); + cJSON_AddItemToObject(params, "display_number", cJSON_CreateString(number)); + ws_write_json(jsock, &jmsg, SWITCH_TRUE); + } + + switch_thread_rwlock_unlock(jsock->rwlock); + } + + } + break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + r = verto_send_media_indication(session, "verto.answer"); + break; + case SWITCH_MESSAGE_INDICATE_PROGRESS: + r = verto_send_media_indication(session, "verto.media"); + break; + default: + break; + } + + return r; +} + + + +static int verto_recover_callback(switch_core_session_t *session) +{ + int r = 0; + char name[512]; + verto_pvt_t *tech_pvt = NULL; + verto_profile_t *profile = NULL; + const char *profile_name = NULL, *jsock_uuid_str = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + + + profile_name = switch_channel_get_variable(channel, "verto_profile_name"); + jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str"); + + if (!(profile_name && jsock_uuid_str && (profile = find_profile(profile_name)))) { + return 0; + } + + tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt)); + tech_pvt->session = session; + tech_pvt->channel = channel; + tech_pvt->jsock_uuid = (char *) jsock_uuid_str; + switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_SECONDARY); + + + tech_pvt->call_id = switch_core_session_strdup(session, switch_core_session_get_uuid(session)); + if ((tech_pvt->smh = switch_core_session_get_media_handle(session))) { + tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh); + verto_set_media_options(tech_pvt, profile); + } + + switch_snprintf(name, sizeof(name), "verto.rtc/%s", tech_pvt->jsock_uuid); + switch_channel_set_name(channel, name); + + switch_channel_add_state_handler(channel, &verto_state_handlers); + switch_core_event_hook_add_receive_message(session, messagehook); + + track_pvt(tech_pvt); + + //switch_channel_clear_flag(tech_pvt->channel, CF_ANSWERED); + //switch_channel_clear_flag(tech_pvt->channel, CF_EARLY_MEDIA); + + switch_thread_rwlock_unlock(profile->rwlock); + + r++; + + return r; +} + + +static void pass_sdp(verto_pvt_t *tech_pvt) +{ + switch_core_session_t *other_session = NULL; + switch_channel_t *other_channel = NULL; + + if (switch_core_session_get_partner(tech_pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) { + other_channel = switch_core_session_get_channel(other_session); + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, tech_pvt->r_sdp); + switch_channel_set_flag(other_channel, CF_PROXY_MODE); + switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); + switch_core_session_rwunlock(other_session); + } +} + + +//// METHODS + +#define switch_either(_A, _B) zstr(_A) ? _B : _A + +static switch_bool_t verto__answer_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *obj = cJSON_CreateObject(); + switch_core_session_t *session; + cJSON *dialog = NULL; + const char *call_id = NULL, *sdp = NULL; + int err = 0; + + *response = obj; + + if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing")); + err = 1; goto cleanup; + } + + + if ((session = switch_core_session_locate(call_id))) { + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + tech_pvt->r_sdp = switch_core_session_strdup(session, sdp); + switch_channel_set_variable(tech_pvt->channel, SWITCH_R_SDP_VARIABLE, sdp); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel), sdp); + switch_core_media_set_sdp_codec_string(session, sdp, SDP_TYPE_RESPONSE); + + if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) { + pass_sdp(tech_pvt); + } else { + if (verto_tech_media(tech_pvt, tech_pvt->r_sdp, SDP_TYPE_RESPONSE) != SWITCH_STATUS_SUCCESS) { + switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CODEC ERROR")); + err = 1; + } + + if (!err && switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("MEDIA ERROR")); + err = 1; + } + } + + if (!err) { + switch_channel_mark_answered(tech_pvt->channel); + } + + switch_core_session_rwunlock(session); + } + + cleanup: + + + if (!err) return SWITCH_TRUE; + + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST")); + cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + + + return SWITCH_FALSE; + +} + +static switch_bool_t verto__bye_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *obj = cJSON_CreateObject(); + switch_core_session_t *session; + cJSON *dialog = NULL; + const char *call_id = NULL, *cause_str = NULL; + int err = 0; + switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + + *response = obj; + + if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if ((cause_str = cJSON_GetObjectCstr(params, "cause"))) { + switch_call_cause_t check = switch_channel_str2cause(cause_str); + + if (check != SWITCH_CAUSE_NONE) { + cause = check; + } + } + + cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(call_id)); + + if ((session = switch_core_session_locate(call_id))) { + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + tech_pvt->remote_hangup_cause = cause; + switch_channel_hangup(tech_pvt->channel, cause); + + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL ENDED")); + cJSON_AddItemToObject(obj, "causeCode", cJSON_CreateNumber(cause)); + cJSON_AddItemToObject(obj, "cause", cJSON_CreateString(switch_channel_cause2str(cause))); + switch_core_session_rwunlock(session); + } else { + err = 1; + } + + cleanup: + + + if (!err) return SWITCH_TRUE; + + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST")); + cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + + + return SWITCH_FALSE; +} + +static switch_status_t xfer_hanguphook(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_state_t state = switch_channel_get_state(channel); + + if (state == CS_HANGUP) { + switch_core_session_t *ksession; + const char *uuid = switch_channel_get_variable(channel, "att_xfer_kill_uuid"); + + if (uuid && (ksession = switch_core_session_force_locate(uuid))) { + switch_channel_t *kchannel = switch_core_session_get_channel(ksession); + + switch_channel_clear_flag(kchannel, CF_XFER_ZOMBIE); + switch_channel_clear_flag(kchannel, CF_TRANSFER); + if (switch_channel_up(kchannel)) { + switch_channel_hangup(kchannel, SWITCH_CAUSE_NORMAL_CLEARING); + } + + switch_core_session_rwunlock(ksession); + } + + switch_core_event_hook_remove_state_change(session, xfer_hanguphook); + + } + + return SWITCH_STATUS_SUCCESS; +} + +static void mark_transfer_record(switch_core_session_t *session, const char *br_a, const char *br_b) +{ + switch_core_session_t *br_b_session, *br_a_session; + switch_channel_t *channel; + const char *uvar1, *dvar1, *uvar2, *dvar2; + + channel = switch_core_session_get_channel(session); + + uvar1 = "verto_user"; + dvar1 = "verto_host"; + + if ((br_b_session = switch_core_session_locate(br_b)) ) { + switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session); + switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_b_channel); + + if (switch_channel_direction(br_b_channel) == SWITCH_CALL_DIRECTION_INBOUND) { + uvar2 = "sip_from_user"; + dvar2 = "sip_from_host"; + } else { + uvar2 = "sip_to_user"; + dvar2 = "sip_to_host"; + } + + cp->transfer_source = switch_core_sprintf(cp->pool, + "%ld:%s:att_xfer:%s@%s/%s@%s", + (long) switch_epoch_time_now(NULL), + cp->uuid_str, + switch_channel_get_variable(channel, uvar1), + switch_channel_get_variable(channel, dvar1), + switch_channel_get_variable(br_b_channel, uvar2), + switch_channel_get_variable(br_b_channel, dvar2)); + + switch_channel_add_variable_var_check(br_b_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH); + switch_channel_set_variable(br_b_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source); + + switch_core_session_rwunlock(br_b_session); + } + + + + if ((br_a_session = switch_core_session_locate(br_a)) ) { + switch_channel_t *br_a_channel = switch_core_session_get_channel(br_a_session); + switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_a_channel); + + if (switch_channel_direction(br_a_channel) == SWITCH_CALL_DIRECTION_INBOUND) { + uvar2 = "sip_from_user"; + dvar2 = "sip_from_host"; + } else { + uvar2 = "sip_to_user"; + dvar2 = "sip_to_host"; + } + + cp->transfer_source = switch_core_sprintf(cp->pool, + "%ld:%s:att_xfer:%s@%s/%s@%s", + (long) switch_epoch_time_now(NULL), + cp->uuid_str, + switch_channel_get_variable(channel, uvar1), + switch_channel_get_variable(channel, dvar1), + switch_channel_get_variable(br_a_channel, uvar2), + switch_channel_get_variable(br_a_channel, dvar2)); + + switch_channel_add_variable_var_check(br_a_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH); + switch_channel_set_variable(br_a_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source); + + switch_core_session_rwunlock(br_a_session); + } + + +} + +static switch_bool_t attended_transfer(switch_core_session_t *session, switch_core_session_t *b_session) { + verto_pvt_t *tech_pvt = NULL, *b_tech_pvt = NULL; + switch_bool_t result = SWITCH_FALSE; + const char *br_a = NULL, *br_b = NULL; + + tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + b_tech_pvt = switch_core_session_get_private_class(b_session, SWITCH_PVT_SECONDARY); + + switch_channel_set_variable(tech_pvt->channel, "refer_uuid", switch_core_session_get_uuid(b_tech_pvt->session)); + switch_channel_set_variable(b_tech_pvt->channel, "transfer_disposition", "replaced"); + + br_a = switch_channel_get_partner_uuid(tech_pvt->channel); + br_b = switch_channel_get_partner_uuid(b_tech_pvt->channel); + + if (!switch_ivr_uuid_exists(br_a)) { + br_a = NULL; + } + + if (!switch_ivr_uuid_exists(br_b)) { + br_b = NULL; + } + + if (switch_channel_test_flag(b_tech_pvt->channel, CF_ORIGINATOR)) { + switch_core_session_t *a_session; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, + "Attended Transfer on originating session %s\n", switch_core_session_get_uuid(b_session)); + + + + switch_channel_set_variable_printf(b_tech_pvt->channel, "transfer_to", "satt:%s", br_a); + + switch_channel_set_variable(b_tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + + + switch_channel_clear_flag(b_tech_pvt->channel, CF_LEG_HOLDING); + switch_channel_set_variable(b_tech_pvt->channel, SWITCH_HOLDING_UUID_VARIABLE, br_a); + switch_channel_set_flag(b_tech_pvt->channel, CF_XFER_ZOMBIE); + switch_channel_set_flag(b_tech_pvt->channel, CF_TRANSFER); + + + if ((a_session = switch_core_session_locate(br_a))) { + const char *moh = "local_stream://moh"; + switch_channel_t *a_channel = switch_core_session_get_channel(a_session); + switch_caller_profile_t *prof = switch_channel_get_caller_profile(b_tech_pvt->channel); + const char *tmp; + + switch_core_event_hook_add_state_change(a_session, xfer_hanguphook); + switch_channel_set_variable(a_channel, "att_xfer_kill_uuid", switch_core_session_get_uuid(b_session)); + switch_channel_set_variable(a_channel, "att_xfer_destination_number", prof->destination_number); + switch_channel_set_variable(a_channel, "att_xfer_callee_id_name", prof->callee_id_name); + switch_channel_set_variable(a_channel, "att_xfer_callee_id_number", prof->callee_id_number); + + if ((tmp = switch_channel_get_hold_music(a_channel))) { + moh = tmp; + } + + if (!zstr(moh) && !strcasecmp(moh, "silence")) { + moh = NULL; + } + + if (moh) { + char *xdest; + xdest = switch_core_session_sprintf(a_session, "endless_playback:%s,park", moh); + switch_ivr_session_transfer(a_session, xdest, "inline", NULL); + } else { + switch_ivr_session_transfer(a_session, "park", "inline", NULL); + } + + switch_core_session_rwunlock(a_session); + + result = SWITCH_TRUE; + + if (b_tech_pvt) { + switch_channel_hangup(b_tech_pvt->channel, SWITCH_CAUSE_NORMAL_CLEARING); + } + } else { + result = SWITCH_FALSE; + } + + } else if (br_a && br_b) { + switch_core_session_t *tmp = NULL; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n", + switch_str_nil(br_a), switch_str_nil(br_b)); + + if ((tmp = switch_core_session_locate(br_b))) { + switch_channel_t *tchannel = switch_core_session_get_channel(tmp); + + switch_channel_set_variable(tchannel, "transfer_disposition", "bridge"); + + switch_channel_set_flag(tchannel, CF_ATTENDED_TRANSFER); + switch_core_session_rwunlock(tmp); + } + + if (switch_true(switch_channel_get_variable(tech_pvt->channel, "recording_follow_transfer")) && + (tmp = switch_core_session_locate(br_a))) { + switch_channel_set_variable(switch_core_session_get_channel(tmp), "transfer_disposition", "bridge"); + switch_core_media_bug_transfer_recordings(session, tmp); + switch_core_session_rwunlock(tmp); + } + + + if (switch_true(switch_channel_get_variable(b_tech_pvt->channel, "recording_follow_transfer")) && + (tmp = switch_core_session_locate(br_b))) { + switch_core_media_bug_transfer_recordings(b_session, tmp); + switch_core_session_rwunlock(tmp); + } + + switch_channel_set_variable_printf(tech_pvt->channel, "transfer_to", "att:%s", br_b); + + mark_transfer_record(session, br_a, br_b); + + switch_ivr_uuid_bridge(br_a, br_b); + switch_channel_set_variable(b_tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + + result = SWITCH_TRUE; + + switch_channel_clear_flag(b_tech_pvt->channel, CF_LEG_HOLDING); + switch_channel_set_variable(b_tech_pvt->channel, "park_timeout", "2:attended_transfer"); + switch_channel_set_state(b_tech_pvt->channel, CS_PARK); + + } else { + if (!br_a && !br_b) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "Cannot transfer channels that are not in a bridge.\n"); + result = SWITCH_FALSE; + } else { + switch_core_session_t *t_session, *hup_session; + switch_channel_t *hup_channel; + const char *ext; + + if (br_a && !br_b) { + t_session = switch_core_session_locate(br_a); + hup_channel = b_tech_pvt->channel; + hup_session = b_session; + } else { + verto_pvt_t *h_tech_pvt = (verto_pvt_t *) switch_core_session_get_private_class(b_session, SWITCH_PVT_SECONDARY); + t_session = switch_core_session_locate(br_b); + hup_channel = tech_pvt->channel; + hup_session = session; + switch_channel_clear_flag(h_tech_pvt->channel, CF_LEG_HOLDING); + switch_channel_hangup(b_tech_pvt->channel, SWITCH_CAUSE_ATTENDED_TRANSFER); + } + + if (t_session) { + //switch_channel_t *t_channel = switch_core_session_get_channel(t_session); + const char *idest = switch_channel_get_variable(hup_channel, "inline_destination"); + ext = switch_channel_get_variable(hup_channel, "destination_number"); + + if (switch_true(switch_channel_get_variable(hup_channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(hup_session, t_session); + } + + if (idest) { + switch_ivr_session_transfer(t_session, idest, "inline", NULL); + } else { + switch_ivr_session_transfer(t_session, ext, NULL, NULL); + } + + result = SWITCH_TRUE; + switch_channel_hangup(hup_channel, SWITCH_CAUSE_ATTENDED_TRANSFER); + } else { + result = SWITCH_FALSE; + } + } + } + if (b_session) { + switch_core_session_rwunlock(b_session); + } + + return result; +} + + +static switch_bool_t verto__modify_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *obj = cJSON_CreateObject(); + switch_core_session_t *session; + cJSON *dialog = NULL; + const char *call_id = NULL, *destination = NULL, *action = NULL; + int err = 0; + + *response = obj; + + if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if (!(action = cJSON_GetObjectCstr(params, "action"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("action missing")); + err = 1; goto cleanup; + } + + cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(call_id)); + cJSON_AddItemToObject(obj, "action", cJSON_CreateString(action)); + + + if ((session = switch_core_session_locate(call_id))) { + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + + if (!strcasecmp(action, "transfer")) { + switch_core_session_t *other_session = NULL; + + if (!(destination = cJSON_GetObjectCstr(params, "destination"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("destination missing")); + err = 1; goto rwunlock; + } + + if (switch_core_session_get_partner(tech_pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) { + switch_ivr_session_transfer(other_session, destination, NULL, NULL); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL TRANSFERRED")); + switch_core_session_rwunlock(other_session); + } else { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("call is not bridged")); + err = 1; goto rwunlock; + } + + } else if (!strcasecmp(action, "replace")) { + const char *replace_call_id; + switch_core_session_t *b_session = NULL; + + if (!(replace_call_id = cJSON_GetObjectCstr(params, "replaceCallID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("replaceCallID missing")); + err = 1; goto rwunlock; + } + + if ((b_session = switch_core_session_locate(replace_call_id))) { + err = (int) attended_transfer(session, b_session); + if (err) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("transfer failed")); + } + switch_core_session_rwunlock(b_session); + } else { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("invalid transfer leg")); + err = 1; goto rwunlock; + } + } else if (!strcasecmp(action, "hold")) { + switch_core_media_toggle_hold(session, 1); + } else if (!strcasecmp(action, "unhold")) { + switch_core_media_toggle_hold(session, 0); + } else if (!strcasecmp(action, "toggleHold")) { + switch_core_media_toggle_hold(session, !!!switch_channel_test_flag(tech_pvt->channel, CF_PROTO_HOLD)); + } + + cJSON_AddItemToObject(obj, "holdState", cJSON_CreateString(switch_channel_test_flag(tech_pvt->channel, CF_PROTO_HOLD) ? "held" : "active")); + + + rwunlock: + + switch_core_session_rwunlock(session); + } else { + err = 1; + } + + cleanup: + + + if (!err) return SWITCH_TRUE; + + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST")); + cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + + + return SWITCH_FALSE; +} + +static switch_bool_t verto__attach_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *obj = cJSON_CreateObject(); + switch_core_session_t *session = NULL; + int err = 0; + cJSON *dialog; + verto_pvt_t *tech_pvt = NULL; + const char *call_id = NULL, *sdp = NULL; + uint8_t match = 0, p = 0; + + *response = obj; + + if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if (!(session = switch_core_session_locate(call_id))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Call does not exist")); + err = 1; goto cleanup; + } + + tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + tech_pvt->r_sdp = switch_core_session_strdup(session, sdp); + + switch_channel_set_variable(tech_pvt->channel, SWITCH_R_SDP_VARIABLE, tech_pvt->r_sdp); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n", + switch_channel_get_name(tech_pvt->channel), tech_pvt->r_sdp); + + switch_channel_set_flag(tech_pvt->channel, CF_REINVITE); + + if ((match = switch_core_media_negotiate_sdp(tech_pvt->session, tech_pvt->r_sdp, &p, SDP_TYPE_RESPONSE))) { + if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) { + switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "MEDIA ERROR"); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("MEDIA ERROR")); + err = 1; goto cleanup; + } + } else { + switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CODEC NEGOTIATION ERROR")); + err = 1; goto cleanup; + } + + cleanup: + + if (tech_pvt) { + switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE); + switch_clear_flag(tech_pvt, TFLAG_ATTACH_REQ); + if (switch_channel_test_flag(tech_pvt->channel, CF_CONFERENCE)) { + switch_channel_set_flag(tech_pvt->channel, CF_CONFERENCE_ADV); + } + } + + if (session) { + switch_core_session_rwunlock(session); + } + + if (!err) { + return SWITCH_TRUE; + } + + if (tech_pvt && tech_pvt->channel) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL); + } + + + cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + + return SWITCH_FALSE; +} + +static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *msg = NULL, *dialog = NULL; + const char *call_id = NULL, *dtmf = NULL; + switch_bool_t r = SWITCH_TRUE; + char *proto = VERTO_CHAT_PROTO; + char *pproto = NULL; + + *response = cJSON_CreateObject(); + + if ((dialog = cJSON_GetObjectItem(params, "dialogParams")) && (call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + switch_core_session_t *session = NULL; + + if ((session = switch_core_session_locate(call_id))) { + + if ((dtmf = cJSON_GetObjectCstr(params, "dtmf"))) { + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + char *send = switch_mprintf("~%s", dtmf); + switch_channel_queue_dtmf_string(tech_pvt->channel, send); + free(send); + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("SENT")); + } + + switch_core_session_rwunlock(session); + } + } + + if ((msg = cJSON_GetObjectItem(params, "msg"))) { + switch_event_t *event; + char *to = (char *) cJSON_GetObjectCstr(msg, "to"); + cJSON *indialog = cJSON_GetObjectItem(msg, "inDialog"); + const char *body = cJSON_GetObjectCstr(msg, "body"); + + + if (!zstr(to)) { + if (strchr(to, '+')) { + pproto = strdup(to); + if ((to = strchr(pproto, '+'))) { + *to++ = '\0'; + } + proto = pproto; + } + } + + if (!zstr(to) && !zstr(body) && switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VERTO_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", jsock->uid); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", jsock->id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", jsock->domain); + + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_full", jsock->id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "verto_profile", jsock->profile->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "verto_jsock_uuid", jsock->uuid_str); + + if (indialog && (indialog->type == cJSON_True || (indialog->type == cJSON_String && switch_true(indialog->valuestring))) && call_id) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call_id", call_id); + } + + switch_event_add_body(event, "%s", body); + + if (strcasecmp(proto, VERTO_CHAT_PROTO)) { + switch_core_chat_send(proto, event); + } + + switch_core_chat_send("GLOBAL", event); + + switch_event_destroy(&event); + + } else { + r = SWITCH_FALSE; + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("INVALID MESSAGE to and body params required")); + } + + + switch_safe_free(pproto); + } + + + return r; +} + +static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *obj = cJSON_CreateObject(); + switch_core_session_t *session = NULL; + switch_channel_t *channel; + switch_event_t *var_event; + switch_call_cause_t reason = SWITCH_CAUSE_INVALID_MSG_UNSPECIFIED, cancel_cause = 0; + switch_caller_profile_t *caller_profile; + int err = 0; + cJSON *dialog; + verto_pvt_t *tech_pvt; + char name[512]; + const char *var, *destination_number, *call_id = NULL, *sdp = NULL, *caller_id_name = NULL, *caller_id_number = NULL, *context = NULL; + + *response = obj; + + switch_event_create_plain(&var_event, SWITCH_EVENT_CHANNEL_DATA); + + if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing")); + err = 1; goto cleanup; + } + + if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing")); + err = 1; goto cleanup; + } + + if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing")); + err = 1; goto cleanup; + } + + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "origination_uuid", call_id); + if ((reason = switch_core_session_outgoing_channel(NULL, var_event, "rtc", + NULL, &session, NULL, SOF_NONE, &cancel_cause)) != SWITCH_CAUSE_SUCCESS) { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Cannot create channel")); + err = 1; goto cleanup; + } + + channel = switch_core_session_get_channel(session); + switch_channel_set_direction(channel, SWITCH_CALL_DIRECTION_INBOUND); + + tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt)); + tech_pvt->session = session; + tech_pvt->channel = channel; + tech_pvt->jsock_uuid = switch_core_session_strdup(session, jsock->uuid_str); + tech_pvt->r_sdp = switch_core_session_strdup(session, sdp); + switch_core_media_set_sdp_codec_string(session, sdp, SDP_TYPE_REQUEST); + switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_SECONDARY); + + + tech_pvt->call_id = switch_core_session_strdup(session, call_id); + if ((tech_pvt->smh = switch_core_session_get_media_handle(session))) { + tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh); + verto_set_media_options(tech_pvt, jsock->profile); + } else { + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Cannot create media handle")); + err = 1; goto cleanup; + } + + if (!(destination_number = cJSON_GetObjectCstr(dialog, "destination_number"))) { + destination_number = "service"; + } + + switch_snprintf(name, sizeof(name), "verto.rtc/%s", destination_number); + switch_channel_set_name(channel, name); + switch_channel_set_variable(channel, "jsock_uuid_str", jsock->uuid_str); + switch_channel_set_variable(channel, "verto_user", jsock->uid); + switch_channel_set_variable(channel, "verto_host", jsock->domain); + switch_channel_set_variable(channel, "event_channel_cookie", tech_pvt->jsock_uuid); + switch_channel_set_variable(channel, "verto_profile_name", jsock->profile->name); + + caller_id_name = cJSON_GetObjectCstr(dialog, "caller_id_name"); + caller_id_number = cJSON_GetObjectCstr(dialog, "caller_id_number"); + + if (zstr(caller_id_name)) { + if ((var = switch_event_get_header(jsock->params, "caller-id-name"))) { + caller_id_name = var; + } + } + + if (zstr(caller_id_number)) { + if ((var = switch_event_get_header(jsock->params, "caller-id-number"))) { + caller_id_number = var; + } + } + + if (!(context = switch_event_get_header(jsock->vars, "user_context"))) { + context = switch_either(jsock->context, jsock->profile->context); + } + + if ((caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + jsock->uid, + switch_either(jsock->dialplan, jsock->profile->dialplan), + caller_id_name, + caller_id_number, + inet_ntoa(jsock->remote_addr.sin_addr), + cJSON_GetObjectCstr(dialog, "ani"), + cJSON_GetObjectCstr(dialog, "aniii"), + cJSON_GetObjectCstr(dialog, "rdnis"), + modname, + context, + destination_number))) { + + switch_channel_set_caller_profile(channel, caller_profile); + + } + + + switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, sdp); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel), sdp); + + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL CREATED")); + cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(tech_pvt->call_id)); + + switch_channel_add_state_handler(channel, &verto_state_handlers); + switch_core_event_hook_add_receive_message(session, messagehook); + switch_channel_set_state(channel, CS_INIT); + track_pvt(tech_pvt); + switch_core_session_thread_launch(session); + + cleanup: + + switch_event_destroy(&var_event); + + if (!err) { + return SWITCH_TRUE; + } + + if (session) { + switch_core_session_destroy(&session); + } + + cJSON_AddItemToObject(obj, "causeCode", cJSON_CreateNumber(reason)); + cJSON_AddItemToObject(obj, "cause", cJSON_CreateString(switch_channel_cause2str(reason))); + cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL ERROR")); + cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + + return SWITCH_FALSE; + +} + +static switch_bool_t event_channel_check_auth(jsock_t *jsock, const char *event_channel) +{ + + char *main_event_channel = NULL; + switch_bool_t ok = SWITCH_TRUE, pre_ok = SWITCH_FALSE; + switch_core_session_t *session = NULL; + + switch_assert(event_channel); + + pre_ok = switch_event_channel_permission_verify(jsock->uuid_str, event_channel); + + if (!pre_ok && (session = switch_core_session_locate(event_channel))) { + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str"); + + if (jsock_uuid_str && !strcmp(jsock_uuid_str, jsock->uuid_str)) { + pre_ok = SWITCH_TRUE; + } + + switch_core_session_rwunlock(session); + } + + if (pre_ok) { + return pre_ok; + } + + if (jsock->allowed_event_channels) { + if (strchr(event_channel, '.')) { + char *p; + main_event_channel = strdup(event_channel); + if ((p = strchr(main_event_channel, '.'))) { + *p = '\0'; + } + } + + if (!(switch_event_get_header(jsock->allowed_event_channels, event_channel) || + (main_event_channel && switch_event_get_header(jsock->allowed_event_channels, main_event_channel)))) { + ok = SWITCH_FALSE; + } + } + + switch_safe_free(main_event_channel); + return ok; + +} + +static switch_bool_t parse_subs(jsock_t *jsock, const char *event_channel, cJSON **sub_list, cJSON **err_list, cJSON **exist_list) +{ + switch_bool_t r = SWITCH_FALSE; + + if (event_channel_check_auth(jsock, event_channel)) { + if (!*sub_list) { + *sub_list = cJSON_CreateArray(); + } + + if (jsock_sub_channel(jsock, event_channel) == SWITCH_STATUS_SUCCESS) { + cJSON_AddItemToArray(*sub_list, cJSON_CreateString(event_channel)); + } else { + if (!*exist_list) { + *exist_list = cJSON_CreateArray(); + } + cJSON_AddItemToArray(*exist_list, cJSON_CreateString(event_channel)); + } + + r = SWITCH_TRUE; + } else { + if (!*err_list) { + *err_list = cJSON_CreateArray(); + } + cJSON_AddItemToArray(*err_list, cJSON_CreateString(event_channel)); + } + + return r; +} + +static switch_bool_t verto__subscribe_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + switch_bool_t r = SWITCH_TRUE; + cJSON *subs = NULL, *errs = NULL, *exist = NULL; + + *response = cJSON_CreateObject(); + + if (params) { + cJSON *jchannel = cJSON_GetObjectItem(params, "eventChannel"); + + if (jchannel) { + if (jchannel->type == cJSON_String) { + parse_subs(jsock, jchannel->valuestring, &subs, &errs, &exist); + } else if (jchannel->type == cJSON_Array) { + int i, len = cJSON_GetArraySize(jchannel); + + for(i = 0; i < len; i++) { + cJSON *str = cJSON_GetArrayItem(jchannel, i); + if (str->type == cJSON_String) { + parse_subs(jsock, str->valuestring, &subs, &errs, &exist); + } + } + } + } + } + + if (subs) { + cJSON_AddItemToObject(*response, "subscribedChannels", subs); + } + + if (errs) { + cJSON_AddItemToObject(*response, "unauthorizedChannels", errs); + } + + if (exist) { + cJSON_AddItemToObject(*response, "alreadySubscribedChannels", exist); + } + + if (!subs) { + r = SWITCH_FALSE; + } + + return r; +} + +static void do_unsub(jsock_t *jsock, const char *event_channel, cJSON **subs, cJSON **errs) +{ + if (jsock_unsub_channel(jsock, event_channel)) { + if (!*subs) { + *subs = cJSON_CreateArray(); + } + cJSON_AddItemToArray(*subs, cJSON_CreateString(event_channel)); + } else { + if (!*errs) { + *errs = cJSON_CreateArray(); + } + cJSON_AddItemToArray(*errs, cJSON_CreateString(event_channel)); + } +} + +static switch_bool_t verto__unsubscribe_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + switch_bool_t r = SWITCH_TRUE; + cJSON *subs = NULL, *errs = NULL; + + *response = cJSON_CreateObject(); + + if (params) { + cJSON *jchannel = cJSON_GetObjectItem(params, "eventChannel"); + + if (jchannel) { + if (jchannel->type == cJSON_String) { + do_unsub(jsock, jchannel->valuestring, &subs, &errs); + } else if (jchannel->type == cJSON_Array) { + int i, len = cJSON_GetArraySize(jchannel); + + for(i = 0; i < len; i++) { + cJSON *str = cJSON_GetArrayItem(jchannel, i); + if (str->type == cJSON_String) { + do_unsub(jsock, str->valuestring, &subs, &errs); + } + } + } + } + } + + if (subs) { + cJSON_AddItemToObject(*response, "unsubscribedChannels", subs); + } + + if (errs) { + cJSON_AddItemToObject(*response, "notSubscribedChannels", errs); + } + + if (errs && !subs) { + r = SWITCH_FALSE; + } + + return r; +} + +static switch_bool_t verto__broadcast_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + char *json_text = NULL; + switch_bool_t r = SWITCH_FALSE; + const char *event_channel = cJSON_GetObjectCstr(params, "eventChannel"); + cJSON *jevent; + + *response = cJSON_CreateObject(); + + + if (!event_channel) { + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("eventChannel not specified.")); + cJSON_AddItemToObject(*response, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + goto end; + } + + if (!event_channel_check_auth(jsock, event_channel)) { + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("Permission Denied.")); + cJSON_AddItemToObject(*response, "code", cJSON_CreateNumber(CODE_SESSION_ERROR)); + goto end; + } + + + cJSON_AddItemToObject(params, "userid", cJSON_CreateString(jsock->uid)); + + jevent = cJSON_Duplicate(params, 1); + switch_event_channel_broadcast(event_channel, &jevent, modname, globals.event_channel_id); + + if (jsock->profile->mcast_pub.sock > -1) { + if ((json_text = cJSON_PrintUnformatted(params))) { + + mcast_socket_send(&jsock->profile->mcast_pub, json_text, strlen(json_text) + 1); + + + free(json_text); + json_text = NULL; + r = SWITCH_TRUE; + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Sent")); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "JSON ERROR!\n"); + } + } + + end: + + return r; +} + +static switch_bool_t login_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + *response = cJSON_CreateObject(); + cJSON_AddItemToObject(*response, "message", cJSON_CreateString("logged in")); + + return SWITCH_TRUE; +} + +static switch_bool_t echo_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + *response = cJSON_Duplicate(params, 1); + return SWITCH_TRUE; +} + +static switch_bool_t jsapi_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + if (jsock->allowed_jsapi) { + const char *function; + + if (params) { + if ((function = cJSON_GetObjectCstr(params, "command"))) { + if (!switch_event_get_header(jsock->allowed_jsapi, function)) { + return SWITCH_FALSE; + } + + if (jsock->allowed_fsapi && !strcmp(function, "fsapi")) { + cJSON *cmd = cJSON_GetObjectItem(params, "cmd"); + cJSON *arg = cJSON_GetObjectItem(params, "arg"); + + if (cmd->type == cJSON_String && cmd->valuestring && !auth_api_command(jsock, cmd->valuestring, arg ? arg->valuestring : NULL)) { + return SWITCH_FALSE; + } + } + } + } + } + + switch_json_api_execute(params, NULL, response); + + return *response ? SWITCH_TRUE : SWITCH_FALSE; +} + +static switch_bool_t fsapi_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response) +{ + cJSON *cmd, *arg, *reply; + switch_stream_handle_t stream = { 0 }; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + cmd = cJSON_GetObjectItem(params, "cmd"); + arg = cJSON_GetObjectItem(params, "arg"); + + + if (jsock->allowed_fsapi) { + if (cmd->type == cJSON_String && cmd->valuestring && !auth_api_command(jsock, cmd->valuestring, arg ? arg->valuestring : NULL)) { + return SWITCH_FALSE; + } + } + + if (cmd && !cmd->valuestring) { + cmd = NULL; + } + + if (arg && !arg->valuestring) { + arg = NULL; + } + + reply = cJSON_CreateObject(); + + SWITCH_STANDARD_STREAM(stream); + + if (cmd && (status = switch_api_execute(cmd->valuestring, arg ? arg->valuestring : NULL, NULL, &stream)) == SWITCH_STATUS_SUCCESS) { + cJSON_AddItemToObject(reply, "message", cJSON_CreateString((char *) stream.data)); + } else { + cJSON_AddItemToObject(reply, "message", cJSON_CreateString("INVALID CALL")); + } + + switch_safe_free(stream.data); + + if (reply) { + *response = reply; + return SWITCH_TRUE; + } + + return SWITCH_FALSE; +} + +//// + +static void jrpc_init(void) +{ + jrpc_add_func("echo", echo_func); + jrpc_add_func("jsapi", jsapi_func); + jrpc_add_func("fsapi", fsapi_func); + jrpc_add_func("login", login_func); + + jrpc_add_func("verto.invite", verto__invite_func); + jrpc_add_func("verto.info", verto__info_func); + jrpc_add_func("verto.attach", verto__attach_func); + jrpc_add_func("verto.bye", verto__bye_func); + jrpc_add_func("verto.answer", verto__answer_func); + jrpc_add_func("verto.subscribe", verto__subscribe_func); + jrpc_add_func("verto.unsubscribe", verto__unsubscribe_func); + jrpc_add_func("verto.broadcast", verto__broadcast_func); + jrpc_add_func("verto.modify", verto__modify_func); + +} + + + + +static int start_jsock(verto_profile_t *profile, int sock) +{ + jsock_t *jsock = NULL; + int flag = 1; + int i; + unsigned int len; + jsock_type_t ptype = PTYPE_CLIENT; + switch_thread_data_t *td; + switch_memory_pool_t *pool; + + switch_core_new_memory_pool(&pool); + + + jsock = (jsock_t *) switch_core_alloc(pool, sizeof(*jsock)); + jsock->pool = pool; + + len = sizeof(jsock->remote_addr); + + if ((jsock->client_socket = accept(sock, (struct sockaddr *) &jsock->remote_addr, &len)) < 0) { + die("ACCEPT FAILED\n"); + } + + for (i = 0; i < profile->i; i++) { + if ( profile->server_socket[i] == sock ) { + if (profile->ip[i].secure) { + ptype = PTYPE_CLIENT_SSL; + } + break; + } + } + + jsock->local_sock = sock; + jsock->profile = profile; + + if (zstr(jsock->name)) { + jsock->name = switch_core_sprintf(pool, "%s:%d", inet_ntoa(jsock->remote_addr.sin_addr), ntohs(jsock->remote_addr.sin_port)); + } + + jsock->ptype = ptype; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Client Connect.\n", jsock->name); + + /* no nagle please */ + setsockopt(jsock->client_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + + td = switch_core_alloc(jsock->pool, sizeof(*td)); + + td->alloc = 0; + td->func = client_thread; + td->obj = jsock; + td->pool = pool; + + switch_mutex_init(&jsock->write_mutex, SWITCH_MUTEX_NESTED, jsock->pool); + switch_thread_rwlock_create(&jsock->rwlock, jsock->pool); + switch_thread_pool_launch_thread(&td); + + return 0; + + error: + + if (jsock) { + if (jsock->client_socket > -1) { + close_socket(&jsock->client_socket); + } + + switch_core_destroy_memory_pool(&pool); + } + + return -1; +} + +static int prepare_socket(int ip, int port) +{ + int sock = -1; + int reuse_addr = 1; + struct sockaddr_in addr; + + if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + die("Socket Error!\n"); + } + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ip; + addr.sin_port = htons(port); + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + die("Bind Error!\n"); + return -1; + } + + if (listen(sock, MAXPENDING) < 0) { + die("Listen error\n"); + return -1; + } + + return sock; + + error: + + close_file(&sock); + + return -1; +} + +static void handle_mcast_sub(verto_profile_t *profile) +{ + int bytes = mcast_socket_recv(&profile->mcast_sub, NULL, 0, 0); + + if (bytes > 0) { + cJSON *json; + + profile->mcast_sub.buffer[bytes] = '\0'; + + if ((json = cJSON_Parse((char *)profile->mcast_sub.buffer))) { + jsock_send_event(json); + cJSON_Delete(json); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MCAST JSON PARSE ERR: %s\n", (char *)profile->mcast_sub.buffer); + } + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MCAST INVALID READ %d\n", bytes); + } + +} + +static int runtime(verto_profile_t *profile) +{ + int max = 2; + int i; + + for (i = 0; i < profile->i; i++) { + if ((profile->server_socket[i] = prepare_socket(profile->ip[i].local_ip_addr, profile->ip[i].local_port)) < 0) { + die("Client Socket Error!\n"); + } + } + + if (profile->mcast_ip) { + if (mcast_socket_create(profile->mcast_ip, profile->mcast_port, &profile->mcast_sub, MCAST_RECV | MCAST_TTL_HOST) < 0) { + die("mcast recv socket create"); + } + + if (mcast_socket_create(profile->mcast_ip, profile->mcast_port + 1, &profile->mcast_pub, MCAST_SEND | MCAST_TTL_HOST) > 0) { + mcast_socket_close(&profile->mcast_sub); + die("mcast send socket create"); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MCAST Bound to %s:%d/%d\n", profile->mcast_ip, profile->mcast_port, profile->mcast_port + 1); + } + + + while(profile->running) { + struct pollfd pfds[MAX_BIND+4]; + int res, x = 0; + int i = 0; + + memset(&pfds[0], 0, sizeof(pfds[0]) * MAX_BIND+2); + + for (i = 0; i < profile->i; i++) { + pfds[i].fd = profile->server_socket[i]; + pfds[i].events = POLLIN|POLLERR; + } + + if (profile->mcast_ip) { + pfds[i].fd = profile->mcast_sub.sock; + pfds[i++].events = POLLIN|POLLERR; + } + + max = i; + + if ((res = poll(pfds, max, 1000)) < 0) { + if (errno != EINTR) { + die("POLL FAILED\n"); + } + } + + if (res == 0) { + continue; + } + + for (x = 0; x < max; x++) { + if (pfds[x].revents & POLLERR) { + die("POLL ERROR\n"); + } + + if (pfds[x].revents & POLLHUP) { + die("POLL HUP\n"); + } + + if (pfds[x].revents & POLLIN) { + if (pfds[x].fd == profile->mcast_sub.sock) { + handle_mcast_sub(profile); + } else { + start_jsock(profile, pfds[x].fd); + } + } + } + } + + if (profile->mcast_sub.sock > -1) { + mcast_socket_close(&profile->mcast_sub); + } + + if (profile->mcast_pub.sock > -1) { + mcast_socket_close(&profile->mcast_pub); + } + + return 0; + + error: + + return -1; + +} + +static void kill_profile(verto_profile_t *profile) +{ + jsock_t *p; + int i; + + profile->running = 0; + + //if (switch_thread_rwlock_tryrdlock(profile->rwlock) != SWITCH_STATUS_SUCCESS) { + // return; + //} + + switch_mutex_lock(profile->mutex); + for (i = 0; i < profile->i; i++) { + close_socket(&profile->server_socket[i]); + } + + for(p = profile->jsock_head; p; p = p->next) { + close_socket(&p->client_socket); + } + switch_mutex_unlock(profile->mutex); + + + + //switch_thread_rwlock_unlock(profile->rwlock); +} + +static void kill_profiles(void) +{ + verto_profile_t *pp; + int sanity = 50; + + switch_mutex_lock(globals.mutex); + for(pp = globals.profile_head; pp; pp = pp->next) { + kill_profile(pp); + } + switch_mutex_unlock(globals.mutex); + + + while(--sanity > 0 && globals.profile_threads > 0) { + usleep(100000); + } +} + + +static void do_shutdown(void) +{ + globals.running = 0; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down (SIG %d)\n", globals.sig); + + kill_profiles(); + + unsub_all_jsock(); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Done\n"); +} + +static void parse_ip(char *host, int *port, in_addr_t *addr, char *input) +{ + char *p; + struct hostent *hent; + + strncpy(host, input, 255); + + if ((p = strchr(host, ':')) != NULL) { + *p++ = '\0'; + *port = atoi(p); + } + + if ( host[0] < '0' || host[0] > '9' ) { + // Non-numeric host (at least it doesn't start with one). Convert it to ip addr first + if ((hent = gethostbyname(host)) != NULL) { + if (hent->h_addrtype == AF_INET) { + memcpy(addr, hent->h_addr_list[0], 4); + } + } + + } else { + *addr = inet_addr(host); + } +} + +static verto_profile_t *find_profile(const char *name) +{ + verto_profile_t *p, *r = NULL; + switch_mutex_lock(globals.mutex); + for(p = globals.profile_head; p; p = p->next) { + if (!strcmp(name, p->name)) { + r = p; + break; + } + } + + if (!r->in_thread || !r->running) { + r = NULL; + } + + if (switch_thread_rwlock_tryrdlock(r->rwlock) != SWITCH_STATUS_SUCCESS) { + r = NULL; + } + switch_mutex_unlock(globals.mutex); + + return r; +} + +static switch_bool_t profile_exists(const char *name) +{ + switch_bool_t r = SWITCH_FALSE; + verto_profile_t *p; + + switch_mutex_lock(globals.mutex); + for(p = globals.profile_head; p; p = p->next) { + if (!strcmp(p->name, name)) { + r = SWITCH_TRUE; + break; + } + } + switch_mutex_unlock(globals.mutex); + + return r; +} + +static void del_profile(verto_profile_t *profile) +{ + verto_profile_t *p, *last = NULL; + + switch_mutex_lock(globals.mutex); + for(p = globals.profile_head; p; p = p->next) { + if (p == profile) { + if (last) { + last->next = p->next; + } else { + globals.profile_head = p->next; + } + globals.profile_count--; + break; + } + + last = p; + } + switch_mutex_unlock(globals.mutex); +} + +static switch_status_t add_profile(verto_profile_t *profile) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(globals.mutex); + + if (!profile_exists(profile->name)) { + status = SWITCH_STATUS_SUCCESS; + } + + if (status == SWITCH_STATUS_SUCCESS) { + profile->next = globals.profile_head; + globals.profile_head = profile; + globals.profile_count++; + } + + switch_mutex_unlock(globals.mutex); + + return status; +} + +static switch_status_t parse_config(const char *cf) +{ + + switch_xml_t cfg, xml, settings, param, xprofile, xprofiles; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf); + return SWITCH_STATUS_TERM; + } + + if ((xprofiles = switch_xml_child(cfg, "profiles"))) { + for (xprofile = switch_xml_child(xprofiles, "profile"); xprofile; xprofile = xprofile->next) { + verto_profile_t *profile; + switch_memory_pool_t *pool; + const char *name = switch_xml_attr(xprofile, "name"); + + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Required field name missing\n"); + continue; + } + + if (profile_exists(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile %s already exists\n", name); + continue; + } + + + switch_core_new_memory_pool(&pool); + profile = switch_core_alloc(pool, sizeof(*profile)); + profile->pool = pool; + profile->name = switch_core_strdup(profile->pool, name); + switch_mutex_init(&profile->mutex, SWITCH_MUTEX_NESTED, profile->pool); + switch_thread_rwlock_create(&profile->rwlock, profile->pool); + add_profile(profile); + + profile->local_network = "localnet.auto"; + + for (param = switch_xml_child(xprofile, "param"); param; param = param->next) { + char *var = NULL; + char *val = NULL; + int i = 0; + + var = (char *) switch_xml_attr_soft(param, "name"); + val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "bind-local")) { + const char *secure = switch_xml_attr_soft(param, "secure"); + if (i < MAX_BIND) { + parse_ip(profile->ip[profile->i].local_ip, &profile->ip[profile->i].local_port, &profile->ip[profile->i].local_ip_addr, val); + if (switch_true(secure)) { + profile->ip[profile->i].secure = 1; + } + profile->i++; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max Bindings Reached!\n"); + } + } else if (!strcasecmp(var, "secure-combined")) { + set_string(profile->cert, val); + set_string(profile->key, val); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Secure key and cert specified\n"); + } else if (!strcasecmp(var, "secure-cert")) { + set_string(profile->cert, val); + } else if (!strcasecmp(var, "secure-key")) { + set_string(profile->key, val); + } else if (!strcasecmp(var, "secure-chain")) { + set_string(profile->chain, val); + } else if (!strcasecmp(var, "userauth") && !zstr(val)) { + profile->userauth = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "root-password") && !zstr(val)) { + profile->root_passwd = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "context") && !zstr(val)) { + profile->context = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "dialplan") && !zstr(val)) { + profile->dialplan = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "mcast-ip") && val) { + profile->mcast_ip = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "mcast-port") && val) { + profile->mcast_port = (switch_port_t) atoi(val); + } else if (!strcasecmp(var, "timer-name") && !zstr(var)) { + profile->timer_name = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "local-network") && !zstr(val)) { + profile->local_network = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "apply-candidate-acl")) { + if (profile->cand_acl_count < SWITCH_MAX_CAND_ACL) { + profile->cand_acl[profile->cand_acl_count++] = switch_core_strdup(profile->pool, val); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SWITCH_MAX_CAND_ACL); + } + } else if (!strcasecmp(var, "rtp-ip")) { + if (zstr(val)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid RTP IP.\n"); + } else { + if (profile->rtpip_index < MAX_RTPIP -1) { + profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, val); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Too many RTP IP.\n"); + } + } + } else if (!strcasecmp(var, "ext-rtp-ip")) { + if (zstr(val)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid External RTP IP.\n"); + } else { + profile->extrtpip = switch_core_strdup(profile->pool, val); + } + } else if (!strcasecmp(var, "debug")) { + if (val) { + profile->debug = atoi(val); + } + } + } + + if (zstr(profile->outbound_codec_string)) { + profile->outbound_codec_string = "opus,vp8"; + } + + if (zstr(profile->inbound_codec_string)) { + profile->outbound_codec_string = profile->outbound_codec_string; + } + + if (zstr(profile->timer_name)) { + profile->timer_name = "soft"; + } + + if (zstr(profile->dialplan)) { + profile->dialplan = "XML"; + } + + if (zstr(profile->context)) { + profile->context = "default"; + } + + if (zstr(profile->ip[0].local_ip) ) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: local_ip bad\n", profile->name); + if (profile->ip[0].local_port <= 0 ) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: local_port bad\n", profile->name); + + if (zstr(profile->ip[0].local_ip) || profile->ip[0].local_port <= 0) { + del_profile(profile); + switch_core_destroy_memory_pool(&pool); + } else { + int i; + + for (i = 0; i < profile->i; i++) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Bound to %s:%d\n", + profile->name, profile->ip[i].local_ip, profile->ip[i].local_port); + } + } + } + } + + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = NULL; + char *val = NULL; + + var = (char *) switch_xml_attr_soft(param, "name"); + val = (char *) switch_xml_attr_soft(param, "value"); + + + if (!strcasecmp(var, "debug")) { + if (val) { + globals.debug = atoi(val); + } + } else if (!strcasecmp(var, "enable-presence") && val) { + globals.enable_presence = switch_true(val); + } else if (!strcasecmp(var, "detach-timeout-sec") && val) { + int tmp = atoi(val); + if (tmp > 0) { + globals.detach_timeout = tmp; + } + } + } + } + + switch_xml_free(xml); + + return status; +} + +static int init(void) +{ + verto_profile_t *p; + + parse_config("verto.conf"); + + switch_mutex_lock(globals.mutex); + for(p = globals.profile_head; p; p = p->next) { + verto_init_ssl(p); + } + switch_mutex_unlock(globals.mutex); + + globals.running = 1; + + return 0; +} + + +#if 0 +static void print_status(verto_profile_t *profile, switch_stream_handle_t *stream) +{ + jsock_t *p; + + stream->write_function(stream, "REMOTE\t\t\tLOCAL\n"); + + for(p = profile->jsock_head; p; p = p->next) { + if (p->ptype & PTYPE_CLIENT) { + int i; + + for (i = 0; i < profile->i; i++) { + if (profile->server_socket[i] == p->local_sock) { + stream->write_function(stream, "%s\t%s:%d\n", p->name, profile->ip[i].local_ip, profile->ip[i].local_port); + } + } + } + } +} +#endif + +SWITCH_STANDARD_API(verto_function) +{ + + int argc = 0; + char *argv[5] = { 0 }; + char *mydata = NULL; + + if (cmd) { + mydata = strdup(cmd); + argc = switch_split(mydata, ' ', argv); + } + + if (argc > 0) { + if (!strcasecmp(argv[0], "connections")) { + //print_status(profile, stream); + } + } + + switch_safe_free(mydata); + + return SWITCH_STATUS_SUCCESS; +} + +static void *SWITCH_THREAD_FUNC profile_thread(switch_thread_t *thread, void *obj) +{ + verto_profile_t *profile = (verto_profile_t *) obj; + int sanity = 50; + + switch_mutex_lock(globals.mutex); + globals.profile_threads++; + switch_mutex_unlock(globals.mutex); + + profile->in_thread = 1; + profile->running = 1; + + + runtime(profile); + profile->running = 0; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "profile %s shutdown, Waiting for %d threads\n", profile->name, profile->jsock_count); + + while(--sanity > 0 && profile->jsock_count > 0) { + usleep(100000); + } + + verto_deinit_ssl(profile); + + del_profile(profile); + + switch_thread_rwlock_wrlock(profile->rwlock); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Thread ending\n", profile->name); + switch_thread_rwlock_unlock(profile->rwlock); + profile->in_thread = 0; + + switch_mutex_lock(globals.mutex); + globals.profile_threads--; + switch_mutex_unlock(globals.mutex); + + return NULL; + +} + +static void run_profile_thread(verto_profile_t *profile) { + switch_thread_data_t *td; + + td = switch_core_alloc(profile->pool, sizeof(*td)); + + td->alloc = 0; + td->func = profile_thread; + td->obj = profile; + td->pool = profile->pool; + + switch_thread_pool_launch_thread(&td); +} + +static void run_profiles(void) +{ + verto_profile_t *p; + + switch_mutex_lock(globals.mutex); + for(p = globals.profile_head; p; p = p->next) { + if (!p->in_thread) { + run_profile_thread(p); + } + } + switch_mutex_unlock(globals.mutex); + +} + + +//// ENDPOINT + +switch_endpoint_interface_t *verto_endpoint_interface; +static switch_call_cause_t verto_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause); +switch_io_routines_t verto_io_routines = { + /*.outgoing_channel */ verto_outgoing_channel +}; + +static char *verto_get_dial_string(const char *uid, switch_stream_handle_t *rstream) +{ + jsock_t *jsock; + verto_profile_t *profile; + switch_stream_handle_t *use_stream = NULL, stream = { 0 }; + char *gen_uid = NULL; + int hits = 0; + + if (!strchr(uid, '@')) { + gen_uid = switch_mprintf("%s@%s", uid, switch_core_get_domain(SWITCH_FALSE)); + uid = gen_uid; + } + + if (rstream) { + use_stream = rstream; + } else { + SWITCH_STANDARD_STREAM(stream); + use_stream = &stream; + } + + switch_mutex_lock(globals.mutex); + for(profile = globals.profile_head; profile; profile = profile->next) { + + switch_mutex_lock(profile->mutex); + + for(jsock = profile->jsock_head; jsock; jsock = jsock->next) { + if (!strcmp(uid, jsock->uid)) { + use_stream->write_function(use_stream, "%s/u:%s,", EP_NAME, jsock->uuid_str); + hits++; + } + } + + switch_mutex_unlock(profile->mutex); + } + switch_mutex_unlock(globals.mutex); + + switch_safe_free(gen_uid); + + if (!hits) { + use_stream->write_function(use_stream, "error/user_not_registered"); + } + + if (use_stream->data) { + char *p = use_stream->data; + if (end_of(p) == ',') { + end_of(p) = '\0'; + } + } + + return use_stream->data; +} + +SWITCH_STANDARD_API(verto_contact_function) +{ + char *uid = (char *) cmd; + + if (!zstr(uid)) { + verto_get_dial_string(uid, stream); + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_call_cause_t verto_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause) +{ + switch_call_cause_t cause = SWITCH_CAUSE_CHANNEL_UNACCEPTABLE; + char *dest = NULL; + + if (!zstr(outbound_profile->destination_number)) { + dest = strdup(outbound_profile->destination_number); + } + + if (zstr(dest)) { + goto end; + } + + if (!switch_stristr("u:", dest)) { + char *dial_str = verto_get_dial_string(dest, NULL); + + cause = SWITCH_CAUSE_USER_NOT_REGISTERED; + + if (dial_str) { + switch_originate_flag_t myflags = SOF_NONE; + + if ((flags & SOF_NO_LIMITS)) { + myflags |= SOF_NO_LIMITS; + } + + if ((flags & SOF_FORKED_DIAL)) { + myflags |= SOF_NOBLOCK; + } + + if (switch_ivr_originate(session, new_session, &cause, dial_str, 0, NULL, + NULL, NULL, outbound_profile, var_event, myflags, cancel_cause) == SWITCH_STATUS_SUCCESS) { + switch_core_session_rwunlock(*new_session); + } + + free(dial_str); + } + + return cause; + } + + if ((cause = switch_core_session_outgoing_channel(session, var_event, "rtc", + outbound_profile, new_session, NULL, SOF_NONE, cancel_cause)) == SWITCH_CAUSE_SUCCESS) { + switch_channel_t *channel = switch_core_session_get_channel(*new_session); + char *jsock_uuid_str = outbound_profile->destination_number + 2; + switch_caller_profile_t *caller_profile; + verto_pvt_t *tech_pvt = NULL; + char name[512]; + + tech_pvt = switch_core_session_alloc(*new_session, sizeof(*tech_pvt)); + tech_pvt->session = *new_session; + tech_pvt->channel = channel; + tech_pvt->jsock_uuid = switch_core_session_strdup(*new_session, jsock_uuid_str); + switch_core_session_set_private_class(*new_session, tech_pvt, SWITCH_PVT_SECONDARY); + + if (session) { + switch_channel_t *ochannel = switch_core_session_get_channel(session); + if (switch_true(switch_channel_get_variable(ochannel, SWITCH_BYPASS_MEDIA_VARIABLE))) { + switch_channel_set_flag(channel, CF_PROXY_MODE); + switch_channel_set_flag(ochannel, CF_PROXY_MODE); + switch_channel_set_cap(channel, CC_BYPASS_MEDIA); + } + } + + tech_pvt->call_id = switch_core_session_strdup(*new_session, switch_core_session_get_uuid(*new_session)); + if ((tech_pvt->smh = switch_core_session_get_media_handle(*new_session))) { + tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh); + } + + switch_snprintf(name, sizeof(name), "verto.rtc/%s", tech_pvt->jsock_uuid); + switch_channel_set_name(channel, name); + switch_channel_set_variable(channel, "jsock_uuid_str", tech_pvt->jsock_uuid); + switch_channel_set_variable(channel, "event_channel_cookie", tech_pvt->jsock_uuid); + + if ((caller_profile = switch_caller_profile_dup(switch_core_session_get_pool(*new_session), outbound_profile))) { + switch_channel_set_caller_profile(channel, caller_profile); + } + + switch_channel_add_state_handler(channel, &verto_state_handlers); + switch_core_event_hook_add_receive_message(*new_session, messagehook); + switch_channel_set_state(channel, CS_INIT); + track_pvt(tech_pvt); + } + + end: + + switch_safe_free(dest); + + return cause; +} + +void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id) +{ + + { + char *json_text; + if ((json_text = cJSON_Print(json))) { + if (globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "EVENT BROADCAST %s %s\n", event_channel, json_text); + } + free(json_text); + } + } + + + + + jsock_send_event(json); +} + + +static int verto_send_chat(const char *uid, const char *call_id, cJSON *msg) +{ + jsock_t *jsock; + verto_profile_t *profile; + int hits = 0; + int done = 0; + + if (!strchr(uid, '@')) { + return 0; + } + + if (call_id) { + switch_core_session_t *session; + if ((session = switch_core_session_locate(call_id))) { + verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY); + jsock_t *jsock; + + if ((jsock = get_jsock(tech_pvt->jsock_uuid))) { + ws_write_json(jsock, &msg, SWITCH_FALSE); + switch_thread_rwlock_unlock(jsock->rwlock); + done = 1; + } + + switch_core_session_rwunlock(session); + } + } + + if (done) { + return 1; + } + + switch_mutex_lock(globals.mutex); + for(profile = globals.profile_head; profile; profile = profile->next) { + + switch_mutex_lock(profile->mutex); + + for(jsock = profile->jsock_head; jsock; jsock = jsock->next) { + if (!strcmp(uid, jsock->uid)) { + ws_write_json(jsock, &msg, SWITCH_FALSE); + hits++; + } + } + + switch_mutex_unlock(profile->mutex); + } + switch_mutex_unlock(globals.mutex); + + return hits; +} + +static switch_status_t chat_send(switch_event_t *message_event) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + const char *to = switch_event_get_header(message_event, "to"); + const char *from = switch_event_get_header(message_event, "from"); + const char *body = switch_event_get_body(message_event); + const char *call_id = switch_event_get_header(message_event, "call_id"); + + DUMP_EVENT(message_event); + + + if (!zstr(to) && !zstr(body) && !zstr(from)) { + cJSON *obj = NULL, *msg = NULL, *params = NULL; + + obj = jrpc_new_req("verto.info", call_id, ¶ms); + msg = json_add_child_obj(params, "msg", NULL); + + cJSON_AddItemToObject(msg, "from", cJSON_CreateString(from)); + cJSON_AddItemToObject(msg, "to", cJSON_CreateString(to)); + cJSON_AddItemToObject(msg, "body", cJSON_CreateString(body)); + verto_send_chat(to, call_id, obj); + cJSON_Delete(obj); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "INVALID EVENT\n"); + status = SWITCH_STATUS_FALSE; + } + + + return status; +} + + + +static switch_cache_db_handle_t *json_get_db_handle(void) +{ + + switch_cache_db_handle_t *dbh = NULL; + const char *dsn; + + + if (!(dsn = switch_core_get_variable("json_db_handle"))) { + dsn = "json"; + } + + + if (switch_cache_db_get_db_handle_dsn(&dbh, dsn) != SWITCH_STATUS_SUCCESS) { + dbh = NULL; + } + + return dbh; +} + + +static int jcallback(void *pArg, int argc, char **argv, char **columnNames) +{ + char **data = (char **) pArg; + + if (argv[0] && !*data) { + *data = strdup(argv[0]); + } + + return 0; +} + +static cJSON *json_retrieve(const char *name, switch_mutex_t *mutex) +{ + char *sql, *errmsg; + switch_cache_db_handle_t *dbh; + char *ascii = NULL; + cJSON *json = NULL; + + if (!check_name(name)) { + return NULL; + } + + sql = switch_mprintf("select data from json_store where name='%q'", name); + + dbh = json_get_db_handle(); + + if (mutex) switch_mutex_lock(mutex); + switch_cache_db_execute_sql_callback(dbh, sql, jcallback, &ascii, &errmsg); + + switch_cache_db_release_db_handle(&dbh); + + if (errmsg) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg); + free(errmsg); + } else { + if (ascii) { + json = cJSON_Parse(ascii); + } + } + + if (mutex) switch_mutex_unlock(mutex); + + + switch_safe_free(ascii); + + return json; + +} + +static switch_bool_t json_commit(cJSON *json, const char *name, switch_mutex_t *mutex) +{ + char *ascii = cJSON_PrintUnformatted(json); + char *sql; + char del_sql[128] = ""; + switch_cache_db_handle_t *dbh; + char *err; + + if (!check_name(name)) { + return SWITCH_FALSE; + } + + if (!(ascii = cJSON_PrintUnformatted(json))) { + return SWITCH_FALSE; + } + + + sql = switch_mprintf("insert into json_store (name,data) values('%q','%q')", name, ascii); + switch_snprintf(del_sql, sizeof(del_sql), "delete from json_store where name='%q'", name); + + dbh = json_get_db_handle(); + + + if (mutex) switch_mutex_lock(mutex); + switch_cache_db_execute_sql(dbh, del_sql, &err); + + if (err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql err [%s]\n", err); + free(err); + } else { + switch_cache_db_execute_sql(dbh, sql, &err); + + if (err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql err [%s]\n", err); + free(err); + } + } + + if (mutex) switch_mutex_unlock(mutex); + + switch_safe_free(sql); + switch_safe_free(ascii); + + switch_cache_db_release_db_handle(&dbh); + + return SWITCH_TRUE; +} + +static switch_status_t json_hanguphook(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_state_t state = switch_channel_get_state(channel); + json_store_t *session_store = NULL; + char *ascii = NULL; + + if (state == CS_HANGUP) { + if ((session_store = (json_store_t *) switch_channel_get_private(channel, "_json_store_"))) { + if ((ascii = cJSON_PrintUnformatted(session_store->JSON_STORE))) { + switch_channel_set_variable(channel, "json_store_data", ascii); + free(ascii); + } + cJSON_Delete(session_store->JSON_STORE); + session_store->JSON_STORE = NULL; + switch_channel_set_private(channel, "_json_store_", NULL); + } + switch_core_event_hook_remove_state_change(session, json_hanguphook); + } + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_JSON_API(json_store_function) +{ + cJSON *JSON_STORE, *reply = NULL, *data = cJSON_GetObjectItem(json, "data"); + switch_status_t status = SWITCH_STATUS_FALSE; + const char *cmd_attr = cJSON_GetObjectCstr(data, "cmd"); + const char *uuid = cJSON_GetObjectCstr(data, "uuid"); + const char *error = NULL, *message = NULL; + store_cmd_t cmd; + const char *key = cJSON_GetObjectCstr(data, "key"); + const char *verbose = cJSON_GetObjectCstr(data, "verbose"); + const char *commit = cJSON_GetObjectCstr(data, "commit"); + const char *file = cJSON_GetObjectCstr(data, "file"); + const char *storename = cJSON_GetObjectCstr(data, "storeName"); + cJSON *obj, **use_store = NULL; + switch_core_session_t *tsession = NULL; + switch_channel_t *tchannel = NULL; + json_store_t *session_store = NULL; + + reply = cJSON_CreateObject(); + + if (uuid) { + if ((tsession = switch_core_session_locate(uuid))) { + tchannel = switch_core_session_get_channel(tsession); + } else { + error = "Invalid INPUT, Missing UUID"; + goto end; + } + } else { + if (zstr(storename)) { + storename = "global"; + } + } + + + if (zstr(cmd_attr)) { + error = "INVALID INPUT, Command not supplied"; + goto end; + } + + + if (!strcasecmp(cmd_attr, "add")) { + cmd = CMD_ADD; + } else if (!strcasecmp(cmd_attr, "del")) { + cmd = CMD_DEL; + } else if (!strcasecmp(cmd_attr, "dump")) { + cmd = CMD_DUMP; + } else if (!strcasecmp(cmd_attr, "commit")) { + cmd = CMD_COMMIT; + } else if (!strcasecmp(cmd_attr, "retrieve")) { + cmd = CMD_RETRIEVE; + } else { + error = "INVALID INPUT, Unknown Command"; + goto end; + } + + + if (cmd == CMD_ADD) { + if (zstr(key)) { + error = "INVALID INPUT, No key supplied"; + goto end; + } + } + + + if (cmd == CMD_RETRIEVE || cmd == CMD_COMMIT) { + if (zstr(file)) { + error = "INVALID INPUT, No file specified"; + goto end; + } + } + + switch_mutex_lock(json_GLOBALS.store_mutex); + if (tsession) { + if (!(session_store = (json_store_t *) switch_channel_get_private(tchannel, "_json_store_"))) { + session_store = switch_core_session_alloc(tsession, sizeof(*session_store)); + switch_mutex_init(&session_store->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(tsession)); + session_store->JSON_STORE = cJSON_CreateObject(); + switch_channel_set_private(tchannel, "_json_store_", session_store); + switch_core_event_hook_add_state_change(tsession, json_hanguphook); + } + + use_store = &session_store->JSON_STORE; + switch_mutex_lock(session_store->mutex); + switch_mutex_unlock(json_GLOBALS.store_mutex); + } else { + JSON_STORE = switch_core_hash_find(json_GLOBALS.store_hash, storename); + + if (!JSON_STORE) { + JSON_STORE = cJSON_CreateObject(); + switch_core_hash_insert(json_GLOBALS.store_hash, storename, JSON_STORE); + } + use_store = &JSON_STORE; + } + + switch(cmd) { + case CMD_RETRIEVE: + obj = json_retrieve(file, NULL); + + if (!obj) { + error = "CANNOT LOAD DATA"; + + if (session_store) { + switch_mutex_unlock(session_store->mutex); + } else { + switch_mutex_unlock(json_GLOBALS.store_mutex); + } + + goto end; + } + + cJSON_Delete(*use_store); + *use_store = obj; + message = "Store Loaded"; + + break; + case CMD_ADD: + + if (!(obj = cJSON_GetObjectItem(data, key))) { + error = "INVALID INPUT"; + + if (session_store) { + switch_mutex_unlock(session_store->mutex); + } else { + switch_mutex_unlock(json_GLOBALS.store_mutex); + } + + goto end; + } + + cJSON_DeleteItemFromObject(*use_store, key); + obj = cJSON_Duplicate(obj, 1); + cJSON_AddItemToObject(*use_store, key, obj); + message = "Item Added"; + break; + + case CMD_DEL: + + if (!key) { + cJSON_Delete(*use_store); + *use_store = cJSON_CreateObject(); + message = "Store Deleted"; + } else { + cJSON_DeleteItemFromObject(*use_store, key); + message = "Item Deleted"; + } + break; + + default: + break; + } + + + if (switch_true(verbose) || cmd == CMD_DUMP) { + cJSON *dump; + + if (key) { + dump = cJSON_GetObjectItem(*use_store, key); + } else { + dump = *use_store; + } + + if (dump) { + dump = cJSON_Duplicate(dump, 1); + cJSON_AddItemToObject(reply, "data", dump); + message = "Data Dumped"; + } else { + error = "Key not found"; + } + } + + if (session_store) { + switch_mutex_unlock(session_store->mutex); + } else { + switch_mutex_unlock(json_GLOBALS.store_mutex); + } + + if (cmd == CMD_COMMIT || commit) { + switch_bool_t ok; + + if (commit && zstr(file)) { + file = commit; + } + + if (session_store) { + ok = json_commit(session_store->JSON_STORE, file, session_store->mutex); + } else { + ok = json_commit(JSON_STORE, file, json_GLOBALS.store_mutex); + } + + cJSON_AddItemToObject(reply, "commitStatus", cJSON_CreateString(ok ? "success" : "fail")); + if (!message) { + message = "Message Comitted"; + } + status = SWITCH_STATUS_SUCCESS; + } + + + end: + + if (!zstr(error)) { + cJSON_AddItemToObject(reply, "errorMessage", cJSON_CreateString(error)); + } + + if (!zstr(message)) { + cJSON_AddItemToObject(reply, "message", cJSON_CreateString(message)); + status = SWITCH_STATUS_SUCCESS; + } + + *json_reply = reply; + + if (tsession) { + switch_core_session_rwunlock(tsession); + } + + return status; +} + +#define add_it(_name, _ename) if ((tmp = switch_event_get_header(event, _ename))) { cJSON_AddItemToObject(data, _name, cJSON_CreateString(tmp));} + +static void presence_event_handler(switch_event_t *event) +{ + cJSON *msg = NULL, *data = NULL; + const char *tmp; + switch_event_header_t *hp; + char *event_channel; + const char *presence_id = switch_event_get_header(event, "channel-presence-id"); + + if (!globals.enable_presence || zstr(presence_id)) { + return; + } + + msg = cJSON_CreateObject(); + data = json_add_child_obj(msg, "data", NULL); + + event_channel = switch_mprintf("presence.%s", presence_id); + + cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(event_channel)); + add_it("channelCallState", "channel-call-state"); + add_it("originalChannelCallState", "original-channel-call-state"); + add_it("channelState", "channel-state"); + + add_it("callerUserName", "caller-username"); + add_it("callerIDName", "caller-caller-id-name"); + add_it("callerIDNumber", "caller-caller-id-number"); + add_it("calleeIDName", "caller-callee-id-name"); + add_it("calleeIDNumber", "caller-callee-id-number"); + add_it("channelUUID", "unique-id"); + + add_it("presenceCallDirection", "presence-call-direction"); + add_it("channelPresenceID", "channel-presence-id"); + add_it("channelPresenceData", "channel-presence-data"); + + for(hp = event->headers; hp; hp = hp->next) { + if (!strncasecmp(hp->name, "PD-", 3)) { + add_it(hp->name, hp->name); + } + } + + switch_event_channel_broadcast(event_channel, &msg, __FILE__, NO_EVENT_CHANNEL_ID); + + free(event_channel); + +} + + +/* Macro expands to: switch_status_t mod_verto_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load) +{ + switch_api_interface_t *api_interface = NULL; + switch_chat_interface_t *chat_interface = NULL; + switch_json_api_interface_t *json_api_interface = NULL; + int r; + switch_cache_db_handle_t *dbh; + //switch_application_interface_t *app_interface = NULL; + + memset(&globals, 0, sizeof(globals)); + globals.pool = pool; + globals.ready = SIGUSR1; + globals.enable_presence = SWITCH_TRUE; + + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool); + + switch_mutex_init(&globals.method_mutex, SWITCH_MUTEX_NESTED, globals.pool); + switch_core_hash_init(&globals.method_hash); + + switch_thread_rwlock_create(&globals.event_channel_rwlock, globals.pool); + switch_core_hash_init(&globals.event_channel_hash); + + switch_mutex_init(&globals.jsock_mutex, SWITCH_MUTEX_NESTED, globals.pool); + switch_core_hash_init(&globals.jsock_hash); + + switch_thread_rwlock_create(&globals.tech_rwlock, globals.pool); + + switch_mutex_init(&globals.detach_mutex, SWITCH_MUTEX_NESTED, globals.pool); + switch_mutex_init(&globals.detach2_mutex, SWITCH_MUTEX_NESTED, globals.pool); + switch_thread_cond_create(&globals.detach_cond, globals.pool); + globals.detach_timeout = 120; + + + switch_event_bind(modname, SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, presence_event_handler, NULL); + + + + memset(&json_GLOBALS, 0, sizeof(json_GLOBALS)); + switch_mutex_init(&json_GLOBALS.store_mutex, SWITCH_MUTEX_NESTED, pool); + switch_core_hash_init(&json_GLOBALS.store_hash); + + + dbh = json_get_db_handle(); + switch_cache_db_test_reactive(dbh, "select name from json_store where name=''", "drop table json_store", json_sql); + switch_cache_db_release_db_handle(&dbh); + + + + switch_event_channel_bind(SWITCH_EVENT_CHANNEL_GLOBAL, verto_broadcast, &globals.event_channel_id); + + + r = init(); + + if (r) return SWITCH_STATUS_TERM; + + jrpc_init(); + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + SWITCH_ADD_API(api_interface, "verto", "Verto API", verto_function, "syntax"); + SWITCH_ADD_API(api_interface, "verto_contact", "Generate a verto endpoint dialstring", verto_contact_function, "user@domain"); + SWITCH_ADD_JSON_API(json_api_interface, "store", "JSON store", json_store_function, ""); + + verto_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + verto_endpoint_interface->interface_name = EP_NAME; + verto_endpoint_interface->io_routines = &verto_io_routines; + + SWITCH_ADD_CHAT(chat_interface, VERTO_CHAT_PROTO, chat_send); + + switch_core_register_secondary_recover_callback(modname, verto_recover_callback); + + run_profiles(); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* + Called when the system shuts down + Macro expands to: switch_status_t mod_verto_shutdown() */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown) +{ + json_cleanup(); + switch_core_hash_destroy(&json_GLOBALS.store_hash); + + switch_event_channel_unbind(NULL, verto_broadcast); + switch_event_unbind_callback(presence_event_handler); + + switch_core_unregister_secondary_recover_callback(modname); + do_shutdown(); + attach_wake(); + attach_wake(); + + switch_core_hash_destroy(&globals.method_hash); + switch_core_hash_destroy(&globals.event_channel_hash); + switch_core_hash_destroy(&globals.jsock_hash); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime) +{ + switch_mutex_lock(globals.detach_mutex); + + while(globals.running) { + if (globals.detached) { + drop_detached(); + switch_yield(1000000); + } else { + switch_mutex_lock(globals.detach2_mutex); + if (globals.running) { + switch_thread_cond_wait(globals.detach_cond, globals.detach_mutex); + } + switch_mutex_unlock(globals.detach2_mutex); + } + } + + switch_mutex_unlock(globals.detach_mutex); + + return SWITCH_STATUS_TERM; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ diff --git a/src/mod/endpoints/mod_verto/mod_verto.h b/src/mod/endpoints/mod_verto/mod_verto.h new file mode 100644 index 0000000000..1b4591d898 --- /dev/null +++ b/src/mod/endpoints/mod_verto/mod_verto.h @@ -0,0 +1,272 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, 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 + * + * mod_html.h -- HTML 5 interface + * + */ + +#ifndef MOD_VERTO_H +#define MOD_VERTO_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mcast.h" + +#define MAXPENDING 10000 +#define STACK_SIZE 80 * 1024 + +#define VERTO_CHAT_PROTO "verto" + +#define copy_string(x,y,z) strncpy(x, y, z - 1) +#define set_string(x,y) strncpy(x, y, sizeof(x)-1) + +#define CODE_INVALID -32600 +#define CODE_AUTH_REQUIRED -32000 +#define CODE_AUTH_FAILED -32001 +#define CODE_SESSION_ERROR -32002 + + +typedef enum { + PTYPE_CLIENT = (1 << 0), + PTYPE_CLIENT_SSL = (1 << 1) +} jsock_type_t; + +typedef enum { + JPFLAG_INIT = (1 << 0), + JPFLAG_AUTHED = (1 << 1), + JPFLAG_CHECK_ATTACH = (1 << 2) +} jpflag_t; + +struct verto_profile_s; + +struct jsock_s { + int client_socket; + switch_memory_pool_t *pool; + switch_thread_t *thread; + wsh_t ws; + unsigned char buf[65535]; + char *name; + jsock_type_t ptype; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + struct sockaddr_in send_addr; + struct ucred credentials; + struct passwd pw; + + int drop; + int local_sock; + SSL *ssl; + + jpflag_t flags; + + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_event_t *allowed_methods; + switch_event_t *allowed_jsapi; + switch_event_t *allowed_fsapi; + switch_event_t *allowed_event_channels; + + + char *id; + char *domain; + char *uid; + char *dialplan; + char *context; + + struct verto_profile_s *profile; + switch_thread_rwlock_t *rwlock; + + switch_mutex_t *write_mutex; + + switch_event_t *params; + switch_event_t *vars; + + struct jsock_s *next; +}; + +typedef struct jsock_s jsock_t; + +#define MAX_BIND 25 +#define MAX_RTPIP 25 + +struct ips { + char local_ip[256]; + in_addr_t local_ip_addr; + int local_port; + int secure; +}; + +typedef enum { + TFLAG_SENT_MEDIA = (1 << 0), + TFLAG_ATTACH_REQ = (1 << 1) +} tflag_t; + +typedef struct verto_pvt_s { + char *jsock_uuid; + char *call_id; + char *r_sdp; + tflag_t flags; + switch_core_session_t *session; + switch_channel_t *channel; + switch_media_handle_t *smh; + switch_core_media_params_t *mparams; + switch_call_cause_t remote_hangup_cause; + time_t detach_time; + struct verto_pvt_s *next; +} verto_pvt_t; + +struct verto_profile_s { + char *name; + switch_mutex_t *mutex; + switch_memory_pool_t *pool; + switch_thread_rwlock_t *rwlock; + + struct ips ip[MAX_BIND]; + int i; + + const SSL_METHOD *ssl_method; + SSL_CTX *ssl_ctx; + char cert[512]; + char key[512]; + char chain[512]; + + jsock_t *jsock_head; + int jsock_count; + int server_socket[MAX_BIND]; + int running; + + int ssl_ready; + int ready; + int debug; + + int in_thread; + + char *userauth; + char *root_passwd; + + char *context; + char *dialplan; + + char *mcast_ip; + switch_port_t mcast_port; + + mcast_handle_t mcast_sub; + mcast_handle_t mcast_pub; + + char *extrtpip; + + char *rtpip[MAX_RTPIP]; + int rtpip_index; + int rtpip_cur; + + char *cand_acl[SWITCH_MAX_CAND_ACL]; + uint32_t cand_acl_count; + + char *inbound_codec_string; + char *outbound_codec_string; + + char *timer_name; + char *local_network; + + + + struct verto_profile_s *next; +}; + +typedef struct verto_profile_s verto_profile_t; + +struct globals_s { + switch_mutex_t *mutex; + switch_memory_pool_t *pool; + + int profile_count; + verto_profile_t *profile_head; + int sig; + int running; + + switch_hash_t *method_hash; + switch_mutex_t *method_mutex; + + switch_hash_t *event_channel_hash; + switch_thread_rwlock_t *event_channel_rwlock; + + int debug; + int ready; + int profile_threads; + int enable_presence; + + switch_hash_t *jsock_hash; + switch_mutex_t *jsock_mutex; + + verto_pvt_t *tech_head; + switch_thread_rwlock_t *tech_rwlock; + + switch_thread_cond_t *detach_cond; + switch_mutex_t *detach_mutex; + switch_mutex_t *detach2_mutex; + + uint32_t detached; + uint32_t detach_timeout; + + switch_event_channel_id_t event_channel_id; +}; + + +extern struct globals_s globals; + +typedef switch_bool_t (*jrpc_func_t)(const char *method, cJSON *params, jsock_t *jsock, cJSON **response); + + +void set_log_path(const char *path); + + +/** @} */ +#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/src/mod/endpoints/mod_verto/ws.c b/src/mod/endpoints/mod_verto/ws.c new file mode 100644 index 0000000000..11b92945c8 --- /dev/null +++ b/src/mod/endpoints/mod_verto/ws.c @@ -0,0 +1,879 @@ +#include "ws.h" +#include + +#ifndef _MSC_VER +#include +#endif + +#ifndef _MSC_VER +#define ms_sleep(x) usleep( x * 1000); +#else +#define ms_sleep(x) Sleep( x ); +#endif + +#define WS_BLOCK 1 +#define WS_NOBLOCK 0 + +#define SHA1_HASH_SIZE 20 +struct ws_globals_s ws_globals; + +#ifndef WSS_STANDALONE + +void init_ssl(void) +{ + SSL_library_init(); +} +void deinit_ssl(void) +{ + return; +} + +#else +static unsigned long pthreads_thread_id(void); +static void pthreads_locking_callback(int mode, int type, const char *file, int line); + +static pthread_mutex_t *lock_cs; +static long *lock_count; + + + +static void thread_setup(void) +{ + int i; + + lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); + lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long)); + + for (i = 0; i < CRYPTO_num_locks(); i++) { + lock_count[i] = 0; + pthread_mutex_init(&(lock_cs[i]), NULL); + } + + CRYPTO_set_id_callback(pthreads_thread_id); + CRYPTO_set_locking_callback(pthreads_locking_callback); +} + +static void thread_cleanup(void) +{ + int i; + + CRYPTO_set_locking_callback(NULL); + + for (i=0; i buflen - 1) { + cplen = buflen -1; + } else { + cplen = len; + } + + strncpy(buf, v, cplen); + *(buf+cplen) = '\0'; + return 1; + } + + } + } + return 0; +} + +static int b64encode(unsigned char *in, size_t ilen, unsigned char *out, size_t olen) +{ + int y=0,bytes=0; + size_t x=0; + unsigned int b=0,l=0; + + if(olen) { + } + + for(x=0;x= 6) { + out[bytes++] = c64[(b>>(l-=6))%64]; + if(++y!=72) { + continue; + } + //out[bytes++] = '\n'; + y=0; + } + } + + if (l > 0) { + out[bytes++] = c64[((b%16)<<(6-l))%64]; + } + if (l != 0) while (l < 6) { + out[bytes++] = '=', l += 2; + } + + return 0; +} + +#ifdef NO_OPENSSL +static void sha1_digest(char *digest, unsigned char *in) +{ + SHA1Context sha; + char *p; + int x; + + + SHA1Init(&sha); + SHA1Update(&sha, in, strlen(in)); + SHA1Final(&sha, digest); +} +#else + +static void sha1_digest(unsigned char *digest, char *in) +{ + SHA_CTX sha; + + SHA1_Init(&sha); + SHA1_Update(&sha, in, strlen(in)); + SHA1_Final(digest, &sha); + +} + +#endif + +int ws_handshake(wsh_t *wsh) +{ + char key[256] = ""; + char version[5] = ""; + char proto[256] = ""; + char proto_buf[384] = ""; + char uri[256] = ""; + char input[256] = ""; + unsigned char output[SHA1_HASH_SIZE] = ""; + char b64[256] = ""; + char respond[512] = ""; + ssize_t bytes; + char *p, *e = 0; + + if (wsh->sock == ws_sock_invalid) { + return -3; + } + + while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_BLOCK)) > 0) { + wsh->datalen += bytes; + if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) { + break; + } + } + + if (bytes > sizeof(wsh->buffer) -1) { + goto err; + } + + *(wsh->buffer+bytes) = '\0'; + + if (strncasecmp(wsh->buffer, "GET ", 4)) { + goto err; + } + + p = wsh->buffer + 4; + + e = strchr(p, ' '); + if (!e) { + goto err; + } + + strncpy(uri, p, e-p); + + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Key", key, sizeof(key)); + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Version", version, sizeof(version)); + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Protocol", proto, sizeof(proto)); + + if (!*key) { + goto err; + } + + snprintf(input, sizeof(input), "%s%s", key, WEBSOCKET_GUID); + sha1_digest(output, input); + b64encode((unsigned char *)output, SHA1_HASH_SIZE, (unsigned char *)b64, sizeof(b64)); + + if (*proto) { + snprintf(proto_buf, sizeof(proto_buf), "Sec-WebSocket-Protocol: %s\r\n", proto); + } + + snprintf(respond, sizeof(respond), + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "%s\r\n", + b64, + proto_buf); + + + ws_raw_write(wsh, respond, strlen(respond)); + wsh->handshake = 1; + + return 0; + + err: + + snprintf(respond, sizeof(respond), "HTTP/1.1 400 Bad Request\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"); + + //printf("ERR:\n%s\n", respond); + + + ws_raw_write(wsh, respond, strlen(respond)); + + ws_close(wsh, WS_NONE); + + return -1; + +} + +ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block) +{ + ssize_t r; + int err = 0; + + if (wsh->ssl) { + do { + r = SSL_read(wsh->ssl, data, bytes); + + ms_sleep(10); + + if (r == -1) { + err = SSL_get_error(wsh->ssl, r); + + if (!block && err == SSL_ERROR_WANT_READ) { + r = -2; + goto end; + } + } + + } while (r == -1 && err == SSL_ERROR_WANT_READ && wsh->x < 100); + + goto end; + } + + do { + r = recv(wsh->sock, data, bytes, 0); + ms_sleep(10); + } while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 100); + + if (wsh->x >= 100) { + r = -1; + } + + end: + + if (r > 0) { + *((char *)data + r) = '\0'; + } + + if (r >= 0) { + wsh->x = 0; + } + + return r; +} + +ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) +{ + size_t r; + + if (wsh->ssl) { + do { + r = SSL_write(wsh->ssl, data, bytes); + } while (r == -1 && SSL_get_error(wsh->ssl, r) == SSL_ERROR_WANT_WRITE); + + return r; + } + + do { + r = send(wsh->sock, data, bytes, 0); + } while (r == -1 && xp_is_blocking(xp_errno())); + + //if (r<0) { + //printf("wRITE FAIL: %s\n", strerror(errno)); + //} + + return r; +} + +#ifdef _MSC_VER +static int setup_socket(ws_socket_t sock) +{ + unsigned long v = 1; + + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + + return 0; + +} + +static int restore_socket(ws_socket_t sock) +{ + unsigned long v = 0; + + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + + return 0; + +} + +#else + +static int setup_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + return fcntl(sock, F_SETFL, flags | O_NONBLOCK); +} + +static int restore_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + + flags &= ~O_NONBLOCK; + + return fcntl(sock, F_SETFL, flags); + +} + +#endif + + +static int establish_logical_layer(wsh_t *wsh) +{ + + if (!wsh->sanity) { + return -1; + } + + if (wsh->logical_established) { + return 0; + } + + if (wsh->secure && !wsh->secure_established) { + int code; + + if (!wsh->ssl) { + wsh->ssl = SSL_new(wsh->ssl_ctx); + assert(wsh->ssl); + + SSL_set_fd(wsh->ssl, wsh->sock); + } + + do { + code = SSL_accept(wsh->ssl); + + if (code == 1) { + wsh->secure_established = 1; + break; + } + + if (code == 0) { + return -1; + } + + if (code < 0) { + if (code == -1 && SSL_get_error(wsh->ssl, code) != SSL_ERROR_WANT_READ) { + return -1; + } + } + + if (wsh->block) { + ms_sleep(10); + } else { + ms_sleep(1); + } + + wsh->sanity--; + + if (!wsh->block) { + return -2; + } + + } while (wsh->sanity > 0); + + if (!wsh->sanity) { + return -1; + } + + } + + while (!wsh->down && !wsh->handshake) { + int r = ws_handshake(wsh); + + if (r < 0) { + wsh->down = 1; + return -1; + } + + if (!wsh->handshake && !wsh->block) { + return -2; + } + + } + + wsh->logical_established = 1; + + return 0; +} + + +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock, int block) +{ + memset(wsh, 0, sizeof(*wsh)); + + wsh->sock = sock; + wsh->block = block; + wsh->sanity = 5000; + wsh->ssl_ctx = ssl_ctx; + + if (!ssl_ctx) { + ssl_ctx = ws_globals.ssl_ctx; + } + + if (close_sock) { + wsh->close_sock = 1; + } + + wsh->buflen = sizeof(wsh->buffer); + wsh->secure = ssl_ctx ? 1 : 0; + + setup_socket(sock); + + if (establish_logical_layer(wsh) == -1) { + return -1; + } + + if (wsh->down) { + return -1; + } + + return 0; +} + +void ws_destroy(wsh_t *wsh) +{ + + if (!wsh) { + return; + } + + if (!wsh->down) { + ws_close(wsh, WS_NONE); + } + + if (wsh->down > 1) { + return; + } + + wsh->down = 2; + + if (wsh->ssl) { + int code; + do { + code = SSL_shutdown(wsh->ssl); + } while (code == -1 && SSL_get_error(wsh->ssl, code) == SSL_ERROR_WANT_READ); + + SSL_free(wsh->ssl); + wsh->ssl = NULL; + } +} + +ssize_t ws_close(wsh_t *wsh, int16_t reason) +{ + + if (wsh->down) { + return -1; + } + + wsh->down = 1; + + if (reason && wsh->sock != ws_sock_invalid) { + uint16_t *u16; + uint8_t fr[4] = {WSOC_CLOSE | 0x80, 2, 0}; + + u16 = (uint16_t *) &fr[2]; + *u16 = htons((int16_t)reason); + ws_raw_write(wsh, fr, 4); + } + + restore_socket(wsh->sock); + + if (wsh->close_sock && wsh->sock != ws_sock_invalid) { + close(wsh->sock); + } + + wsh->sock = ws_sock_invalid; + + return reason * -1; + +} + +ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) +{ + + ssize_t need = 2; + char *maskp; + int ll = 0; + + again: + need = 2; + maskp = NULL; + *data = NULL; + + ll = establish_logical_layer(wsh); + + if (ll < 0) { + return ll; + } + + if (wsh->down) { + return -1; + } + + if (!wsh->handshake) { + return ws_close(wsh, WS_PROTO_ERR); + } + + if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 9, wsh->block)) < 0) { + if (wsh->datalen == -2) { + return -2; + } + return ws_close(wsh, WS_PROTO_ERR); + } + + if (wsh->datalen < need) { + if ((wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 9 - wsh->datalen, WS_BLOCK)) < need) { + /* too small - protocol err */ + return ws_close(wsh, WS_PROTO_ERR); + } + } + + *oc = *wsh->buffer & 0xf; + + switch(*oc) { + case WSOC_CLOSE: + { + wsh->plen = wsh->buffer[1] & 0x7f; + *data = (uint8_t *) &wsh->buffer[2]; + return ws_close(wsh, 1000); + } + break; + case WSOC_CONTINUATION: + case WSOC_TEXT: + case WSOC_BINARY: + case WSOC_PING: + case WSOC_PONG: + { + //int fin = (wsh->buffer[0] >> 7) & 1; + int mask = (wsh->buffer[1] >> 7) & 1; + + if (mask) { + need += 4; + + if (need > wsh->datalen) { + /* too small - protocol err */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + } + + wsh->plen = wsh->buffer[1] & 0x7f; + wsh->payload = &wsh->buffer[2]; + + if (wsh->plen == 127) { + uint64_t *u64; + + need += 8; + + if (need > wsh->datalen) { + /* too small - protocol err */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + u64 = (uint64_t *) wsh->payload; + wsh->payload += 8; + + wsh->plen = ntohl((u_long)*u64); + + } else if (wsh->plen == 126) { + uint16_t *u16; + + need += 2; + + if (need > wsh->datalen) { + /* too small - protocol err */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + u16 = (uint16_t *) wsh->payload; + wsh->payload += 2; + wsh->plen = ntohs(*u16); + } + + if (mask) { + maskp = (char *)wsh->payload; + wsh->payload += 4; + } + + need = (wsh->plen - (wsh->datalen - need)); + + if (need < 0) { + /* invalid read - protocol err .. */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + if ((need + wsh->datalen) > (ssize_t)wsh->buflen) { + /* too big - Ain't nobody got time fo' dat */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_DATA_TOO_BIG); + } + + wsh->rplen = wsh->plen - need; + + while(need) { + ssize_t r = ws_raw_read(wsh, wsh->payload + wsh->rplen, need, WS_BLOCK); + + if (r < 1) { + /* invalid read - protocol err .. */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + wsh->datalen += r; + wsh->rplen += r; + need -= r; + } + + if (mask && maskp) { + ssize_t i; + + for (i = 0; i < wsh->datalen; i++) { + wsh->payload[i] ^= maskp[i % 4]; + } + } + + + if (*oc == WSOC_PING) { + ws_write_frame(wsh, WSOC_PONG, wsh->payload, wsh->rplen); + goto again; + } + + + *(wsh->payload+wsh->rplen) = '\0'; + *data = (uint8_t *)wsh->payload; + + //printf("READ[%ld][%d]-----------------------------:\n[%s]\n-------------------------------\n", wsh->rplen, *oc, (char *)*data); + + + return wsh->rplen; + } + break; + default: + { + /* invalid op code - protocol err .. */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + break; + } +} + +ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes) +{ + + if (bytes + wsh->wdatalen > wsh->buflen) { + return -1; + } + + memcpy(wsh->wbuffer + wsh->wdatalen, data, bytes); + + wsh->wdatalen += bytes; + + return bytes; +} + +ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc) +{ + ssize_t r = 0; + + if (!wsh->wdatalen) { + return -1; + } + + r = ws_write_frame(wsh, oc, wsh->wbuffer, wsh->wdatalen); + + wsh->wdatalen = 0; + + return r; +} + + +ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) +{ + uint8_t hdr[14] = { 0 }; + size_t hlen = 2; + + if (wsh->down) { + return -1; + } + + //printf("WRITE[%ld]-----------------------------:\n[%s]\n-----------------------------------\n", bytes, (char *) data); + + hdr[0] = (uint8_t)(oc | 0x80); + + if (bytes < 126) { + hdr[1] = (uint8_t)bytes; + } else if (bytes < 0x10000) { + uint16_t *u16; + + hdr[1] = 126; + hlen += 2; + + u16 = (uint16_t *) &hdr[2]; + *u16 = htons((uint16_t) bytes); + + } else { + uint64_t *u64; + + hdr[1] = 127; + hlen += 8; + + u64 = (uint64_t *) &hdr[2]; + *u64 = htonl(bytes); + } + + if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != (ssize_t)hlen) { + return -1; + } + + if (ws_raw_write(wsh, data, bytes) != (ssize_t)bytes) { + return -2; + } + + return bytes; +} + +#ifdef _MSC_VER + +int xp_errno(void) +{ + return WSAGetLastError(); +} + +int xp_is_blocking(int errcode) +{ + return errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS; +} + +#else + +int xp_errno(void) +{ + return errno; +} + +int xp_is_blocking(int errcode) +{ + return errcode == EAGAIN || errcode == EWOULDBLOCK || errcode == EINPROGRESS || errcode == EINTR || errcode == ETIMEDOUT; +} + +#endif diff --git a/src/mod/endpoints/mod_verto/ws.h b/src/mod/endpoints/mod_verto/ws.h new file mode 100644 index 0000000000..699779b515 --- /dev/null +++ b/src/mod/endpoints/mod_verto/ws.h @@ -0,0 +1,120 @@ +#ifndef _WS_H +#define _WS_H + +//#define WSS_STANDALONE 1 + +#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define B64BUFFLEN 1024 + +#include +#ifndef _MSC_VER +#include +#include +#include +#else +#pragma warning(disable:4996) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +//#include "sha1.h" +#include + +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define snprintf _snprintf +#ifdef _WIN64 +#define WS_SSIZE_T __int64 +#elif _MSC_VER >= 1400 +#define WS_SSIZE_T __int32 __w64 +#else +#define WS_SSIZE_T __int32 +#endif +typedef WS_SSIZE_T ssize_t; +#endif + + +struct ws_globals_s { + const SSL_METHOD *ssl_method; + SSL_CTX *ssl_ctx; + char cert[512]; + char key[512]; +}; + +extern struct ws_globals_s ws_globals; + +typedef int ws_socket_t; +#define ws_sock_invalid -1 + + +typedef enum { + WS_NONE = 0, + WS_NORMAL = 1000, + WS_PROTO_ERR = 1002, + WS_DATA_TOO_BIG = 1009 +} ws_cause_t; + +typedef enum { + WSOC_CONTINUATION = 0x0, + WSOC_TEXT = 0x1, + WSOC_BINARY = 0x2, + WSOC_CLOSE = 0x8, + WSOC_PING = 0x9, + WSOC_PONG = 0xA +} ws_opcode_t; + +typedef struct wsh_s { + ws_socket_t sock; + char buffer[65536]; + char wbuffer[65536]; + size_t buflen; + ssize_t datalen; + ssize_t wdatalen; + char *payload; + ssize_t plen; + ssize_t rplen; + SSL *ssl; + int handshake; + uint8_t down; + int secure; + uint8_t close_sock; + SSL_CTX *ssl_ctx; + int block; + int sanity; + int secure_established; + int logical_established; + int x; +} wsh_t; + +ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc); +ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes); + + +ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block); +ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes); +ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); +ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock, int block); +ssize_t ws_close(wsh_t *wsh, int16_t reason); +void ws_destroy(wsh_t *wsh); +void init_ssl(void); +void deinit_ssl(void); +int xp_errno(void); +int xp_is_blocking(int errcode); + + + +#ifndef _MSC_VER +static inline uint64_t get_unaligned_uint64(const void *p) +{ + const struct { uint64_t d; } __attribute__((packed)) *pp = p; + return pp->d; +} +#endif + +#endif