]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
WIP
authorMichael Jerris <mike@jerris.com>
Wed, 11 Jun 2014 17:48:46 +0000 (12:48 -0500)
committerMichael Jerris <mike@jerris.com>
Wed, 11 Jun 2014 17:48:46 +0000 (12:48 -0500)
22 files changed:
build/modules.conf.in
configure.ac
src/mod/endpoints/mod_verto/Makefile.am [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/.gitignore [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/MCAST.i [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/MCAST.pm [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/mcast.c [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/mcast.h [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/mcast_cpp.cpp [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/mcast_cpp.h [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/mcast_wrap.cpp [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/perlxsi.c [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/test.c [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/test.pl [new file with mode: 0644]
src/mod/endpoints/mod_verto/mcast/test2.pl [new file with mode: 0644]
src/mod/endpoints/mod_verto/mod_verto.2008.vcproj [new file with mode: 0644]
src/mod/endpoints/mod_verto/mod_verto.2010.vcxproj [new file with mode: 0644]
src/mod/endpoints/mod_verto/mod_verto.2012.vcxproj [new file with mode: 0644]
src/mod/endpoints/mod_verto/mod_verto.c [new file with mode: 0644]
src/mod/endpoints/mod_verto/mod_verto.h [new file with mode: 0644]
src/mod/endpoints/mod_verto/ws.c [new file with mode: 0644]
src/mod/endpoints/mod_verto/ws.h [new file with mode: 0644]

index 34e16be048ff4e320203c3c2381fb29059ab173a..7368cc8f0175cf96778c5d6953f374ac6775da6b 100644 (file)
@@ -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
index 4da80f2105f0b78e9db18849925fffff355c0507..294f6b469d1933c082eefb17fb6a850b42ca79e7 100644 (file)
@@ -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 (file)
index 0000000..08b82bd
--- /dev/null
@@ -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 (file)
index 0000000..9d22eb4
--- /dev/null
@@ -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 (file)
index 0000000..097a52b
--- /dev/null
@@ -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 (file)
index 0000000..5316650
--- /dev/null
@@ -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 (file)
index 0000000..a3234d7
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <unistd.h>
+#include "mcast.h"
+#include <poll.h>
+
+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 (file)
index 0000000..38afa95
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+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 (file)
index 0000000..2e3eca6
--- /dev/null
@@ -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 <string.h>
+#include <poll.h>
+
+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 (file)
index 0000000..5990871
--- /dev/null
@@ -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 (file)
index 0000000..77725cf
--- /dev/null
@@ -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<typename T> class SwigValueWrapper {
+    T *tt;
+public:
+    SwigValueWrapper() : tt(0) { }
+    SwigValueWrapper(const SwigValueWrapper<T>& 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<T>& rhs);
+};
+
+template <typename T> 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 (<obj is ok>) {                            
+          if (<need new object>) {                    
+            *ptr = <ptr to new allocated object>; 
+            return SWIG_NEWOBJ;                       
+          } else {                                    
+            *ptr = <ptr to old object>;               
+            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 <string.h>
+
+#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<int>" == "Class<int >", 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 <name1>|<name2>|...
+  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 <name1>|<name2>|...
+  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 <math.h>
+#include <stdlib.h>
+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 <patchlevel.h>
+#  endif
+#  if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL)))
+#    include <could_not_find_Perl_patchlevel.h>
+#  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 <string.h>
+#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 <stdlib.h>
+
+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 <stdexcept>
+
+
+#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 <limits.h>
+#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 <float.h>
+
+
+#include <math.h>
+
+
+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 (file)
index 0000000..9ca8fc1
--- /dev/null
@@ -0,0 +1,16 @@
+#include <EXTERN.h>
+#include <perl.h>
+
+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 (file)
index 0000000..2e07518
--- /dev/null
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#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 (file)
index 0000000..ca8b58a
--- /dev/null
@@ -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 (file)
index 0000000..341e87b
--- /dev/null
@@ -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 (file)
index 0000000..548b114
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9.00"\r
+       Name="mod_verto"\r
+       ProjectGUID="{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}"\r
+       RootNamespace="mod_verto"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+               <Platform\r
+                       Name="x64"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Debug|x64"\r
+                       OutputDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                               TargetEnvironment="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="17"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|x64"\r
+                       OutputDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                               TargetEnvironment="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="17"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <File\r
+                       RelativePath=".\mod_verto.c"\r
+                       >\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
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 (file)
index 0000000..1dc1aac
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectName>mod_verto</ProjectName>\r
+    <ProjectGuid>{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}</ProjectGuid>\r
+    <RootNamespace>mod_verto</RootNamespace>\r
+    <Keyword>Win32Proj</Keyword>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup>\r
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="mod_verto.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ProjectReference Include="..\..\..\..\w32\Library\FreeSwitchCore.2010.vcxproj">\r
+      <Project>{202d7a4e-760d-4d0e-afa1-d7459ced30ff}</Project>\r
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>\r
+    </ProjectReference>\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
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 (file)
index 0000000..8930918
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectName>mod_verto</ProjectName>\r
+    <ProjectGuid>{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}</ProjectGuid>\r
+    <RootNamespace>mod_verto</RootNamespace>\r
+    <Keyword>Win32Proj</Keyword>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v110</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v110</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v110</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v110</PlatformToolset>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup>\r
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="mod_verto.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ProjectReference Include="..\..\..\..\w32\Library\FreeSwitchCore.2012.vcxproj">\r
+      <Project>{202d7a4e-760d-4d0e-afa1-d7459ced30ff}</Project>\r
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>\r
+    </ProjectReference>\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c
new file mode 100644 (file)
index 0000000..8e5c94e
--- /dev/null
@@ -0,0 +1,4485 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_verto.c -- HTML5 Verto interface
+ *
+ */
+#include <switch.h>
+#include <switch_json.h>
+
+
+/* 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 <mod_verto.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+
+
+#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, &params);
+                               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, &params);
+               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, &params);
+       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, &params);
+        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, &params);
+
+                       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, &params);
+                                       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, &params);
+               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 (file)
index 0000000..1b4591d
--- /dev/null
@@ -0,0 +1,272 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_html.h -- HTML 5 interface
+ *
+ */
+
+#ifndef MOD_VERTO_H
+#define MOD_VERTO_H
+#include <switch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/wait.h> 
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <openssl/ssl.h>
+#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 (file)
index 0000000..11b9294
--- /dev/null
@@ -0,0 +1,879 @@
+#include "ws.h"
+#include <pthread.h>
+
+#ifndef _MSC_VER
+#include <fcntl.h>
+#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<CRYPTO_num_locks(); i++) {
+               pthread_mutex_destroy(&(lock_cs[i]));
+       }
+       OPENSSL_free(lock_cs);
+       OPENSSL_free(lock_count);
+
+}
+
+static void pthreads_locking_callback(int mode, int type, const char *file, int line)
+{
+
+       if (mode & CRYPTO_LOCK) {
+               pthread_mutex_lock(&(lock_cs[type]));
+               lock_count[type]++;
+       } else {
+               pthread_mutex_unlock(&(lock_cs[type]));
+       }
+}
+
+
+
+static unsigned long pthreads_thread_id(void)
+{
+       return (unsigned long) pthread_self();
+}
+
+
+void init_ssl(void) {
+       SSL_library_init();
+
+
+       OpenSSL_add_all_algorithms();   /* load & register cryptos */
+       SSL_load_error_strings();     /* load all error messages */
+       ws_globals.ssl_method = TLSv1_server_method();   /* create server instance */
+       ws_globals.ssl_ctx = SSL_CTX_new(ws_globals.ssl_method);         /* create context */
+       assert(ws_globals.ssl_ctx);
+       
+       /* set the local certificate from CertFile */
+       SSL_CTX_use_certificate_file(ws_globals.ssl_ctx, ws_globals.cert, SSL_FILETYPE_PEM);
+       /* set the private key from KeyFile */
+       SSL_CTX_use_PrivateKey_file(ws_globals.ssl_ctx, ws_globals.key, SSL_FILETYPE_PEM);
+       /* verify private key */
+       if ( !SSL_CTX_check_private_key(ws_globals.ssl_ctx) ) {
+               abort();
+    }
+
+       SSL_CTX_set_cipher_list(ws_globals.ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH");
+
+       thread_setup();
+}
+
+
+void deinit_ssl(void) {
+       thread_cleanup();
+}
+
+#endif
+
+static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+static int cheezy_get_var(char *data, char *name, char *buf, size_t buflen)
+{
+  char *p=data;
+
+  /* the old way didnt make sure that variable values were used for the name hunt
+   * and didnt ensure that only a full match of the variable name was used
+   */
+
+  do {
+    if(!strncmp(p,name,strlen(name)) && *(p+strlen(name))==':') break;
+  } while((p = (strstr(p,"\n")+1))!=(char *)1);
+
+
+  if (p != (char *)1 && *p!='\0') {
+    char *v, *e = 0;
+
+    v = strchr(p, ':');
+    if (v) {
+      v++;
+      while(v && *v == ' ') {
+       v++;
+      }
+      if (v)  {
+       e = strchr(v, '\r');
+       if (!e) {
+         e = strchr(v, '\n');
+       }
+      }
+                       
+      if (v && e) {
+       int cplen;
+       size_t len = e - v;
+       
+       if (len > 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<ilen;x++) {
+               b = (b<<8) + in[x];
+               l += 8;
+               while (l >= 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 (file)
index 0000000..699779b
--- /dev/null
@@ -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 <sys/types.h>
+#ifndef _MSC_VER
+#include <arpa/inet.h>
+#include <sys/wait.h> 
+#include <sys/socket.h>
+#else
+#pragma warning(disable:4996)
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+//#include "sha1.h"
+#include <openssl/ssl.h>
+
+#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