--- /dev/null
+/* ----------------------------------------------------------------------------
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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, ¶ms);
+ ws_write_json(np->jsock, &msg, SWITCH_TRUE);
+ }
+ }
+ }
+}
+
+static void jsock_send_event(cJSON *event)
+{
+
+ const char *event_channel, *session_uuid = NULL;
+ jsock_t *use_jsock = NULL;
+ switch_core_session_t *session = NULL;
+
+ if (!(event_channel = cJSON_GetObjectCstr(event, "eventChannel"))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO EVENT CHANNEL SPECIFIED\n");
+ return;
+ }
+
+
+ if ((session = switch_core_session_locate(event_channel))) {
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ const char *jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str");
+ if (jsock_uuid_str) {
+ use_jsock = get_jsock(jsock_uuid_str);
+ }
+ switch_core_session_rwunlock(session);
+ }
+
+ if (use_jsock || (use_jsock = get_jsock(event_channel))) { /* implicit subscription to channel identical to the connection uuid or session uuid */
+ cJSON *msg = NULL, *params;
+ params = cJSON_Duplicate(event, 1);
+ msg = jrpc_new_req("verto.event", NULL, ¶ms);
+ ws_write_json(use_jsock, &msg, SWITCH_TRUE);
+ switch_thread_rwlock_unlock(use_jsock->rwlock);
+ use_jsock = NULL;
+ return;
+ }
+
+
+ if ((session_uuid = cJSON_GetObjectCstr(event, "sessid"))) {
+ if (!(use_jsock = get_jsock(session_uuid))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Socket %s not connected\n", session_uuid);
+ return;
+ }
+ }
+
+ switch_thread_rwlock_rdlock(globals.event_channel_rwlock);
+ write_event(event_channel, use_jsock, event);
+ if (strchr(event_channel, '.')) {
+ char *main_channel = strdup(event_channel);
+ char *p = strchr(main_channel, '.');
+ if (p) *p = '\0';
+ write_event(main_channel, use_jsock, event);
+ free(main_channel);
+ }
+ switch_thread_rwlock_unlock(globals.event_channel_rwlock);
+
+ if (use_jsock) {
+ switch_thread_rwlock_unlock(use_jsock->rwlock);
+ use_jsock = NULL;
+ }
+}
+
+static jrpc_func_t jrpc_get_func(jsock_t *jsock, const char *method)
+{
+ jrpc_func_t func = NULL;
+ char *main_method = NULL;
+
+ switch_assert(method);
+
+ if (jsock->allowed_methods) {
+ if (strchr(method, '.')) {
+ char *p;
+ main_method = strdup(method);
+ if ((p = strchr(main_method, '.'))) {
+ *p = '\0';
+ }
+ }
+
+ if (!(switch_event_get_header(jsock->allowed_methods, method) || (main_method && switch_event_get_header(jsock->allowed_methods, main_method)))) {
+ goto end;
+ }
+ }
+
+ switch_mutex_lock(globals.method_mutex);
+ func = (jrpc_func_t) (intptr_t) switch_core_hash_find(globals.method_hash, method);
+ switch_mutex_unlock(globals.method_mutex);
+
+ end:
+
+ switch_safe_free(main_method);
+
+ return func;
+}
+
+
+static void jrpc_add_func(const char *method, jrpc_func_t func)
+{
+ switch_assert(method);
+ switch_assert(func);
+
+ switch_mutex_lock(globals.method_mutex);
+ switch_core_hash_insert(globals.method_hash, method, (void *) (intptr_t) func);
+ switch_mutex_unlock(globals.method_mutex);
+}
+
+static char *MARKER = "X";
+
+static void set_perm(const char *str, switch_event_t **event)
+{
+ char delim = ',';
+ char *cur, *next;
+ int count = 0;
+ char *edup;
+
+ if (!zstr(str)) {
+ if (!strcasecmp(str, "__ANY__")) {
+ return;
+ }
+ }
+
+ switch_event_create(event, SWITCH_EVENT_REQUEST_PARAMS);
+
+ if (!zstr(str)) {
+ edup = strdup(str);
+ cur = edup;
+
+ if (strchr(edup, ' ')) {
+ delim = ' ';
+ }
+
+ for (cur = edup; cur; count++) {
+ if ((next = strchr(cur, delim))) {
+ *next++ = '\0';
+ }
+
+ switch_event_add_header_string(*event, SWITCH_STACK_BOTTOM, cur, MARKER);
+
+ cur = next;
+ }
+
+ switch_safe_free(edup);
+
+ }
+}
+
+static void check_permissions(jsock_t *jsock, switch_xml_t x_user, cJSON *params)
+{
+ switch_xml_t x_param, x_params;
+ const char *allowed_methods = NULL, *allowed_jsapi = NULL, *allowed_fsapi = NULL, *allowed_event_channels = NULL;
+
+ if ((x_params = switch_xml_child(x_user, "params"))) {
+ for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
+ const char *var = switch_xml_attr(x_param, "name");
+ const char *val = switch_xml_attr(x_param, "value");
+
+ if (zstr(val) || zstr(var)) {
+ continue;
+ }
+
+ if (!strcasecmp(var, "jsonrpc-allowed-methods")) {
+ allowed_methods = val;
+ }
+
+ if (!strcasecmp(var, "jsonrpc-allowed-jsapi")) {
+ allowed_jsapi = val;
+ }
+
+ if (!strcasecmp(var, "jsonrpc-allowed-fsapi")) {
+ allowed_fsapi = val;
+ }
+
+ if (!strcasecmp(var, "jsonrpc-allowed-event-channels")) {
+ allowed_event_channels = val;
+ }
+ }
+ }
+
+ set_perm(allowed_methods, &jsock->allowed_methods);
+ set_perm(allowed_jsapi, &jsock->allowed_jsapi);
+ set_perm(allowed_fsapi, &jsock->allowed_fsapi);
+ set_perm(allowed_event_channels, &jsock->allowed_event_channels);
+
+ switch_event_add_header_string(jsock->allowed_methods, SWITCH_STACK_BOTTOM, "login", MARKER);
+
+}
+
+static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *message, switch_size_t mlen)
+{
+ switch_bool_t r = SWITCH_FALSE;
+ const char *passwd = NULL;
+ const char *login = NULL;
+
+ if (!params) {
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Missing params");
+ goto end;
+ }
+
+ login = cJSON_GetObjectCstr(params, "login");
+ passwd = cJSON_GetObjectCstr(params, "passwd");
+
+ if (zstr(login)) {
+ goto end;
+ }
+
+ if (zstr(passwd)) {
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Missing passwd");
+ goto end;
+ }
+
+
+ if (!strcmp(login, "root")) {
+ if (!(r = !strcmp(passwd, jsock->profile->root_passwd))) {
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Authentication Failure");
+ }
+
+ } else if (!zstr(jsock->profile->userauth)) {
+ switch_xml_t x_user = NULL;
+ char *id = NULL, *domain = NULL;
+ switch_event_t *req_params;
+
+ if (*jsock->profile->userauth == '@') {
+ domain = jsock->profile->userauth + 1;
+ id = (char *) login;
+ } else if (switch_true(jsock->profile->userauth)) {
+ id = switch_core_strdup(jsock->pool, login);
+
+ if ((domain = strchr(id, '@'))) {
+ *domain++ = '\0';
+ }
+
+ }
+
+ if (!(id && domain)) {
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Missing or improper credentials");
+ goto end;
+ }
+
+ switch_event_create(&req_params, SWITCH_EVENT_REQUEST_PARAMS);
+ switch_assert(req_params);
+
+ switch_event_add_header_string(req_params, SWITCH_STACK_BOTTOM, "action", "jsonrpc-authenticate");
+
+ if (switch_xml_locate_user_merged("id", id, domain, NULL, &x_user, req_params) != SWITCH_STATUS_SUCCESS) {
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Login Incorrect");
+ } else {
+ switch_xml_t x_param, x_params;
+ const char *use_passwd = NULL, *verto_context = NULL, *verto_dialplan = NULL;
+
+ jsock->id = switch_core_strdup(jsock->pool, id);
+ jsock->domain = switch_core_strdup(jsock->pool, domain);
+ jsock->uid = switch_core_sprintf(jsock->pool, "%s@%s", id, domain);
+
+
+ if ((x_params = switch_xml_child(x_user, "params"))) {
+ for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
+ const char *var = switch_xml_attr_soft(x_param, "name");
+ const char *val = switch_xml_attr_soft(x_param, "value");
+
+ if (!use_passwd && !strcasecmp(var, "password")) {
+ use_passwd = val;
+ } else if (!strcasecmp(var, "jsonrpc-password")) {
+ use_passwd = val;
+ } else if (!strcasecmp(var, "verto-context")) {
+ verto_context = val;
+ } else if (!strcasecmp(var, "verto-dialplan")) {
+ verto_dialplan = val;
+ }
+
+ switch_event_add_header(jsock->params, SWITCH_STACK_BOTTOM, var, val);
+ }
+ }
+
+ if ((x_params = switch_xml_child(x_user, "variables"))) {
+ for (x_param = switch_xml_child(x_params, "variable"); x_param; x_param = x_param->next) {
+ const char *var = switch_xml_attr_soft(x_param, "name");
+ const char *val = switch_xml_attr_soft(x_param, "value");
+
+ switch_event_add_header(jsock->vars, SWITCH_STACK_BOTTOM, var, val);
+ }
+ }
+
+ if (!zstr(verto_dialplan)) {
+ jsock->dialplan = switch_core_strdup(jsock->pool, verto_dialplan);
+ }
+
+ if (!zstr(verto_context)) {
+ jsock->context = switch_core_strdup(jsock->pool, verto_context);
+ }
+
+ if (zstr(use_passwd) || strcmp(passwd, use_passwd)) {
+ r = SWITCH_FALSE;
+ *code = CODE_AUTH_FAILED;
+ switch_snprintf(message, mlen, "Authentication Failure");
+ } else {
+ r = SWITCH_TRUE;
+ check_permissions(jsock, x_user, params);
+ }
+
+ switch_xml_free(x_user);
+ }
+
+ switch_event_destroy(&req_params);
+ }
+
+
+ end:
+
+ return r;
+
+}
+
+static void set_call_params(cJSON *params, verto_pvt_t *tech_pvt) {
+ const char *caller_id_name = NULL;
+ const char *caller_id_number = NULL;
+
+ if (switch_channel_outbound_display(tech_pvt->channel)) {
+ caller_id_name = switch_channel_get_variable(tech_pvt->channel, "caller_id_name");
+ caller_id_number = switch_channel_get_variable(tech_pvt->channel, "caller_id_number");
+ } else {
+ caller_id_name = switch_channel_get_variable(tech_pvt->channel, "callee_id_name");
+ caller_id_number = switch_channel_get_variable(tech_pvt->channel, "callee_id_number");
+ }
+
+ if (zstr(caller_id_name)) {
+ caller_id_name = "Outbound Call";
+ }
+
+ if (zstr(caller_id_number)) {
+ caller_id_number = switch_channel_get_variable(tech_pvt->channel, "destination_number");
+ }
+
+ cJSON_AddItemToObject(params, "caller_id_name", cJSON_CreateString(caller_id_name));
+ cJSON_AddItemToObject(params, "caller_id_number", cJSON_CreateString(caller_id_number));
+}
+
+static jsock_t *get_jsock(const char *uuid)
+{
+ jsock_t *jsock = NULL;
+
+ switch_mutex_lock(globals.jsock_mutex);
+ if ((jsock = switch_core_hash_find(globals.jsock_hash, uuid))) {
+ if (switch_thread_rwlock_tryrdlock(jsock->rwlock) != SWITCH_STATUS_SUCCESS) {
+ jsock = NULL;
+ }
+ }
+ switch_mutex_unlock(globals.jsock_mutex);
+
+ return jsock;
+}
+
+static void attach_jsock(jsock_t *jsock)
+{
+ switch_mutex_lock(globals.jsock_mutex);
+ switch_core_hash_insert(globals.jsock_hash, jsock->uuid_str, jsock);
+ switch_mutex_unlock(globals.jsock_mutex);
+}
+
+static void detach_jsock(jsock_t *jsock)
+{
+ switch_mutex_lock(globals.jsock_mutex);
+ switch_core_hash_delete(globals.jsock_hash, jsock->uuid_str);
+ switch_mutex_unlock(globals.jsock_mutex);
+}
+
+static int attach_wake(void)
+{
+ switch_status_t status;
+ int tries = 0;
+
+ top:
+
+ status = switch_mutex_trylock(globals.detach_mutex);
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ switch_thread_cond_signal(globals.detach_cond);
+ switch_mutex_unlock(globals.detach_mutex);
+ return 1;
+ } else {
+ if (switch_mutex_trylock(globals.detach2_mutex) == SWITCH_STATUS_SUCCESS) {
+ switch_mutex_unlock(globals.detach2_mutex);
+ } else {
+ if (++tries < 10) {
+ switch_cond_next();
+ goto top;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void tech_reattach(verto_pvt_t *tech_pvt, jsock_t *jsock)
+{
+ cJSON *params = NULL;
+ cJSON *msg = NULL;
+
+ tech_pvt->detach_time = 0;
+ globals.detached--;
+ attach_wake();
+ switch_set_flag(tech_pvt, TFLAG_ATTACH_REQ);
+ msg = jrpc_new_req("verto.attach", tech_pvt->call_id, ¶ms);
+ cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str));
+ set_call_params(params, tech_pvt);
+ ws_write_json(jsock, &msg, SWITCH_TRUE);
+}
+
+static void drop_detached(void)
+{
+ verto_pvt_t *tech_pvt;
+ switch_time_t now = switch_epoch_time_now(NULL);
+
+ switch_thread_rwlock_rdlock(globals.tech_rwlock);
+ for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) {
+ if (!switch_channel_up_nosig(tech_pvt->channel)) {
+ continue;
+ }
+
+ if (tech_pvt->detach_time && (now - tech_pvt->detach_time) > globals.detach_timeout) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE);
+ }
+ }
+ switch_thread_rwlock_unlock(globals.tech_rwlock);
+}
+
+static void attach_calls(jsock_t *jsock)
+{
+ verto_pvt_t *tech_pvt;
+
+ switch_thread_rwlock_rdlock(globals.tech_rwlock);
+ for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) {
+ if (tech_pvt->detach_time && !strcmp(tech_pvt->jsock_uuid, jsock->uuid_str)) {
+ if (!switch_channel_up_nosig(tech_pvt->channel)) {
+ continue;
+ }
+
+ tech_reattach(tech_pvt, jsock);
+ }
+ }
+ switch_thread_rwlock_unlock(globals.tech_rwlock);
+}
+
+static void detach_calls(jsock_t *jsock)
+{
+ verto_pvt_t *tech_pvt;
+
+ switch_thread_rwlock_rdlock(globals.tech_rwlock);
+ for(tech_pvt = globals.tech_head; tech_pvt; tech_pvt = tech_pvt->next) {
+ if (!strcmp(tech_pvt->jsock_uuid, jsock->uuid_str)) {
+ if (!switch_channel_up_nosig(tech_pvt->channel)) {
+ continue;
+ }
+
+ tech_pvt->detach_time = switch_epoch_time_now(NULL);
+ globals.detached++;
+ attach_wake();
+ }
+ }
+ switch_thread_rwlock_unlock(globals.tech_rwlock);
+}
+
+static void process_jrpc_response(jsock_t *jsock, cJSON *json)
+{
+}
+
+static void set_session_id(jsock_t *jsock, const char *uuid)
+{
+ //cJSON *params, *msg = jrpc_new(0);
+
+ if (!zstr(uuid)) {
+ switch_set_string(jsock->uuid_str, uuid);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s re-connecting session %s\n", jsock->name, jsock->uuid_str);
+ } else {
+ switch_uuid_str(jsock->uuid_str, sizeof(jsock->uuid_str));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s new RPC session %s\n", jsock->name, jsock->uuid_str);
+ }
+
+ attach_jsock(jsock);
+
+}
+
+static cJSON *process_jrpc(jsock_t *jsock, cJSON *json)
+{
+ cJSON *reply = NULL, *echo = NULL, *id = NULL, *params = NULL, *response = NULL, *result;
+ const char *method = NULL, *version = NULL, *sessid = NULL;
+ jrpc_func_t func = NULL;
+
+ switch_assert(json);
+
+ method = cJSON_GetObjectCstr(json, "method");
+ result = cJSON_GetObjectItem(json, "result");
+ version = cJSON_GetObjectCstr(json, "jsonrpc");
+ id = cJSON_GetObjectItem(json, "id");
+
+ if ((params = cJSON_GetObjectItem(json, "params"))) {
+ sessid = cJSON_GetObjectCstr(params, "sessid");
+ }
+
+ if (!switch_test_flag(jsock, JPFLAG_INIT)) {
+ set_session_id(jsock, sessid);
+ switch_set_flag(jsock, JPFLAG_INIT);
+ }
+
+ if (zstr(version) || strcmp(version, "2.0")) {
+ reply = jrpc_new(0);
+ jrpc_add_error(reply, CODE_INVALID, "Invalid message", id);
+ goto end;
+ }
+
+ if (result) {
+ process_jrpc_response(jsock, json);
+ return NULL;
+ }
+
+ reply = jrpc_new(0);
+
+ jrpc_add_id(reply, id, "", 0);
+
+ if (!switch_test_flag(jsock, JPFLAG_AUTHED) && (jsock->profile->userauth || jsock->profile->root_passwd)) {
+ int code = CODE_AUTH_REQUIRED;
+ char message[128] = "Authentication Required";
+
+ if (!check_auth(jsock, params, &code, message, sizeof(message))) {
+ jrpc_add_error(reply, code, message, id);
+ goto end;
+ }
+ switch_set_flag(jsock, JPFLAG_AUTHED);
+ }
+
+ if (!method || !(func = jrpc_get_func(jsock, method))) {
+ jrpc_add_error(reply, -32601, "Invalid Method, Missing Method or Permission Denied", id);
+ } else {
+ if (func(method, params, jsock, &response) == SWITCH_TRUE) {
+
+ if (params) {
+ echo = cJSON_GetObjectItem(params, "echoParams");
+ }
+ if (echo) {
+ if ((echo->type == cJSON_True || (echo->type == cJSON_String && switch_true(echo->valuestring)))) {
+ cJSON_AddItemToObject(response, "requestParams", cJSON_Duplicate(params, 1));
+ } else {
+ cJSON_AddItemToObject(response, "requestParams", cJSON_Duplicate(echo, 1));
+ }
+ }
+
+ jrpc_add_result(reply, response);
+ } else {
+ if (response) {
+ cJSON_AddItemToObject(reply, "error", response);
+ } else {
+ jrpc_add_error(reply, -32602, "Permission Denied", id);
+ }
+ }
+ }
+
+ end:
+
+ return reply;
+}
+
+static switch_status_t process_input(jsock_t *jsock, uint8_t *data, switch_ssize_t bytes)
+{
+ cJSON *json = NULL, *reply = NULL;
+ char *ascii = (char *) data;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ if (ascii) {
+ if (jsock->profile->debug || globals.debug) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "READ %s [%s]\n", jsock->name, ascii);
+ }
+ json = cJSON_Parse(ascii);
+ }
+
+ if (json) {
+ if (json->type == cJSON_Array) { /* batch mode */
+ int i, len = cJSON_GetArraySize(json);
+
+ reply = cJSON_CreateArray();
+
+ for(i = 0; i < len; i++) {
+ cJSON *obj, *item = cJSON_GetArrayItem(json, i);
+
+ if ((obj = process_jrpc(jsock, item))) {
+ cJSON_AddItemToArray(reply, obj);
+ }
+ }
+ } else {
+ reply = process_jrpc(jsock, json);
+ }
+ } else {
+ reply = jrpc_new(0);
+ jrpc_add_error(reply, -32600, "Invalid Request", NULL);
+ }
+
+ if (reply) {
+ ws_write_json(jsock, &reply, SWITCH_TRUE);
+ }
+
+ if (json) {
+ cJSON_Delete(json);
+ }
+
+ return status;
+}
+
+static void client_run(jsock_t *jsock)
+{
+
+ jsock->local_addr.sin_family = AF_INET;
+ jsock->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ jsock->local_addr.sin_port = 0;
+
+
+ if (ws_init(&jsock->ws, jsock->client_socket, (jsock->ptype & PTYPE_CLIENT_SSL) ? jsock->profile->ssl_ctx : NULL, 0, 1) < 0) {
+ die("%s WS SETUP FAILED", jsock->name);
+ }
+
+ while(jsock->profile->running) {
+ struct pollfd pfds[1];
+ int res;
+
+ memset(&pfds[0], 0, sizeof(pfds[0]));
+
+ pfds[0].fd = jsock->client_socket;
+ pfds[0].events = POLLIN|POLLERR|POLLHUP|POLLRDNORM|POLLRDBAND|POLLPRI;
+
+
+ if ((res = poll(pfds, 1, -1)) < 0) {
+ if (errno != EINTR) {
+ die("%s POLL FAILED\n", jsock->name);
+ }
+ }
+
+ if (res < 0) {
+ die("%s POLL ERROR\n", jsock->name);
+ }
+
+ if (jsock->drop) {
+ die("%s Dropping Connection\n", jsock->name);
+ }
+
+
+ if (pfds[0].revents & POLLERR) {
+ die("%s POLL ERROR\n", jsock->name);
+ }
+
+ if (pfds[0].revents & POLLHUP) {
+ die("%s POLL HANGUP DETECTED\n", jsock->name);
+ }
+
+ if (pfds[0].revents & POLLNVAL) {
+ die("%s POLL INVALID SOCKET\n", jsock->name);
+ }
+
+ if (pfds[0].revents & POLLIN) {
+ switch_ssize_t bytes;
+ ws_opcode_t oc;
+ uint8_t *data;
+
+ bytes = ws_read_frame(&jsock->ws, &oc, &data);
+
+ if (bytes < 0) {
+ die("BAD READ %ld\n", bytes);
+ break;
+ }
+
+ if (bytes) {
+ if (process_input(jsock, data, bytes) != SWITCH_STATUS_SUCCESS) {
+ die("Input Error\n");
+ }
+
+ if (!switch_test_flag(jsock, JPFLAG_CHECK_ATTACH) && switch_test_flag(jsock, JPFLAG_AUTHED)) {
+ attach_calls(jsock);
+ switch_set_flag(jsock, JPFLAG_CHECK_ATTACH);
+ }
+ }
+ }
+ }
+
+ error:
+
+ detach_jsock(jsock);
+ ws_destroy(&jsock->ws);
+
+ return;
+}
+
+static void *SWITCH_THREAD_FUNC client_thread(switch_thread_t *thread, void *obj)
+{
+ jsock_t *jsock = (jsock_t *) obj;
+
+ switch_event_create(&jsock->params, SWITCH_EVENT_CHANNEL_DATA);
+ switch_event_create(&jsock->vars, SWITCH_EVENT_CHANNEL_DATA);
+
+
+ add_jsock(jsock);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Starting client thread.\n", jsock->name);
+
+ if ((jsock->ptype & PTYPE_CLIENT) || (jsock->ptype & PTYPE_CLIENT_SSL)) {
+ client_run(jsock);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Ending client thread.\n", jsock->name);
+ }
+
+ detach_calls(jsock);
+
+ del_jsock(jsock);
+
+ switch_event_destroy(&jsock->params);
+ switch_event_destroy(&jsock->vars);
+
+ if (jsock->client_socket > -1) {
+ close_socket(&jsock->client_socket);
+ }
+
+ switch_event_destroy(&jsock->allowed_methods);
+ switch_event_destroy(&jsock->allowed_fsapi);
+ switch_event_destroy(&jsock->allowed_jsapi);
+ switch_event_destroy(&jsock->allowed_event_channels);
+
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Ending client thread.\n", jsock->name);
+ switch_thread_rwlock_wrlock(jsock->rwlock);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Thread ended\n", jsock->name);
+ switch_thread_rwlock_unlock(jsock->rwlock);
+
+ return NULL;
+}
+
+
+static switch_bool_t auth_api_command(jsock_t *jsock, const char *api_cmd, const char *arg)
+{
+ const char *check_cmd = api_cmd;
+ char *sneaky_commands[] = { "bgapi", "sched_api", "eval", "expand", "xml_wrap", NULL };
+ int x = 0;
+ char *dup_arg = NULL;
+ char *next = NULL;
+ switch_bool_t ok = SWITCH_TRUE;
+
+ top:
+
+ if (!jsock->allowed_fsapi) {
+ ok = SWITCH_FALSE;
+ goto end;
+ }
+
+ if (!switch_event_get_header(jsock->allowed_fsapi, check_cmd)) {
+ ok = SWITCH_FALSE;
+ goto end;
+ }
+
+ while (check_cmd) {
+ for (x = 0; sneaky_commands[x]; x++) {
+ if (!strcasecmp(sneaky_commands[x], check_cmd)) {
+ if (check_cmd == api_cmd) {
+ if (arg) {
+ switch_safe_free(dup_arg);
+ dup_arg = strdup(arg);
+ check_cmd = dup_arg;
+ if ((next = strchr(check_cmd, ' '))) {
+ *next++ = '\0';
+ }
+ } else {
+ break;
+ }
+ } else {
+ if (next) {
+ check_cmd = next;
+ } else {
+ check_cmd = dup_arg;
+ }
+
+ if ((next = strchr(check_cmd, ' '))) {
+ *next++ = '\0';
+ }
+ }
+ goto top;
+ }
+ }
+ break;
+ }
+
+ end:
+
+ switch_safe_free(dup_arg);
+ return ok;
+
+}
+
+//// VERTO
+
+static void track_pvt(verto_pvt_t *tech_pvt)
+{
+ switch_thread_rwlock_wrlock(globals.tech_rwlock);
+ tech_pvt->next = globals.tech_head;
+ globals.tech_head = tech_pvt;
+ switch_thread_rwlock_unlock(globals.tech_rwlock);
+}
+
+static void untrack_pvt(verto_pvt_t *tech_pvt)
+{
+ verto_pvt_t *p, *last = NULL;
+
+ switch_thread_rwlock_wrlock(globals.tech_rwlock);
+ if (tech_pvt->detach_time) {
+ globals.detached--;
+ tech_pvt->detach_time = 0;
+ attach_wake();
+ }
+
+ for(p = globals.tech_head; p; p = p->next) {
+ if (p == tech_pvt) {
+ if (last) {
+ last->next = p->next;
+ } else {
+ globals.tech_head = p->next;
+ }
+ break;
+ }
+
+ last = p;
+ }
+ switch_thread_rwlock_unlock(globals.tech_rwlock);
+}
+
+
+static switch_status_t verto_on_hangup(switch_core_session_t *session)
+{
+ jsock_t *jsock = NULL;
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ untrack_pvt(tech_pvt);
+
+ // get the jsock and send hangup notice
+ if (!tech_pvt->remote_hangup_cause && (jsock = get_jsock(tech_pvt->jsock_uuid))) {
+ cJSON *msg = jrpc_new_req("verto.bye", tech_pvt->call_id, NULL);
+ ws_write_json(jsock, &msg, SWITCH_TRUE);
+
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void verto_set_media_options(verto_pvt_t *tech_pvt, verto_profile_t *profile);
+
+static switch_status_t verto_connect(switch_core_session_t *session, const char *method)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ jsock_t *jsock = NULL;
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ if (!(jsock = get_jsock(tech_pvt->jsock_uuid))) {
+ status = SWITCH_STATUS_BREAK;
+ } else {
+ cJSON *params = NULL;
+ cJSON *msg = NULL;
+ const char *var = NULL;
+ switch_caller_profile_t *caller_profile = switch_channel_get_caller_profile(tech_pvt->channel);
+
+
+ switch_channel_set_variable(tech_pvt->channel, "verto_user", jsock->uid);
+ switch_channel_set_variable(tech_pvt->channel, "verto_host", jsock->domain);
+
+ if ((var = switch_event_get_header(jsock->params, "caller-id-name"))) {
+ caller_profile->callee_id_name = switch_core_strdup(caller_profile->pool, var);
+ }
+
+ if ((var = switch_event_get_header(jsock->params, "caller-id-number"))) {
+ caller_profile->callee_id_number = switch_core_strdup(caller_profile->pool, var);
+ }
+
+ if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) {
+ switch_core_media_absorb_sdp(session);
+ } else {
+ switch_channel_set_variable(tech_pvt->channel, "media_webrtc", "true");
+ switch_core_session_set_ice(tech_pvt->session);
+
+ verto_set_media_options(tech_pvt, jsock->profile);
+
+ switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_TRUE);
+ switch_channel_set_variable(tech_pvt->channel, "verto_profile_name", jsock->profile->name);
+
+ if (!switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING)) {
+ if ((status = switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_TRUE)) != SWITCH_STATUS_SUCCESS) {
+ //if ((status = switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0)) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ return status;
+ }
+ }
+
+ switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0);
+ }
+
+ msg = jrpc_new_req(method, tech_pvt->call_id, ¶ms);
+
+ if (tech_pvt->mparams->local_sdp_str) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local %s SDP %s:\n%s\n",
+ method,
+ switch_channel_get_name(tech_pvt->channel),
+ tech_pvt->mparams->local_sdp_str);
+
+ cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str));
+ set_call_params(params, tech_pvt);
+
+ ws_write_json(jsock, &msg, SWITCH_TRUE);
+ } else {
+ status = SWITCH_STATUS_FALSE;
+ }
+
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ }
+
+ return status;
+}
+
+switch_status_t verto_tech_media(verto_pvt_t *tech_pvt, const char *r_sdp, switch_sdp_type_t sdp_type)
+{
+ uint8_t match = 0, p = 0;
+
+ switch_assert(tech_pvt != NULL);
+ switch_assert(r_sdp != NULL);
+
+ if (zstr(r_sdp)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if ((match = switch_core_media_negotiate_sdp(tech_pvt->session, r_sdp, &p, sdp_type))) {
+ if (switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
+ //if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+ //if (!switch_channel_test_flag(tech_pvt->channel, CF_ANSWERED)) {
+ // switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA");
+ // switch_channel_mark_pre_answered(tech_pvt->channel);
+ //}
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+
+ return SWITCH_STATUS_FALSE;
+}
+
+static switch_status_t verto_on_init(switch_core_session_t *session)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ if (switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING_BRIDGE) || switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING)) {
+ int tries = 120;
+
+ switch_core_session_clear_crypto(session);
+
+ while(--tries > 0) {
+
+ status = verto_connect(session, "verto.attach");
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ switch_set_flag(tech_pvt, TFLAG_ATTACH_REQ);
+ break;
+ } else if (status == SWITCH_STATUS_BREAK) {
+ switch_yield(1000000);
+ continue;
+ } else {
+ tries = 0;
+ break;
+ }
+ }
+
+ if (!tries) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ status = SWITCH_STATUS_FALSE;
+ }
+
+ switch_channel_set_flag(tech_pvt->channel, CF_VIDEO_BREAK);
+ switch_core_session_kill_channel(tech_pvt->session, SWITCH_SIG_BREAK);
+
+ tries = 500;
+ while(--tries > 0 && switch_test_flag(tech_pvt, TFLAG_ATTACH_REQ)) {
+ switch_yield(10000);
+ }
+
+ switch_core_session_refresh_video(session);
+ switch_channel_set_flag(tech_pvt->channel, CF_VIDEO_BREAK);
+ switch_core_session_kill_channel(tech_pvt->session, SWITCH_SIG_BREAK);
+
+ return status;
+ }
+
+ if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+ if ((status = verto_connect(tech_pvt->session, "verto.invite")) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ }
+ }
+
+ return status;
+}
+
+
+static switch_state_handler_table_t verto_state_handlers = {
+ /*.on_init */ verto_on_init,
+ /*.on_routing */ NULL,
+ /*.on_execute */ NULL,
+ /*.on_hangup */ verto_on_hangup,
+ /*.on_exchange_media */ NULL,
+ /*.on_soft_execute */ NULL,
+ /*.on_consume_media */ NULL,
+ /*.on_hibernate */ NULL,
+ /*.on_reset */ NULL,
+ /*.on_park */ NULL,
+ /*.on_reporting */ NULL,
+ /*.on_destroy */ NULL,
+ SSH_FLAG_STICKY
+};
+
+
+
+
+static void verto_set_media_options(verto_pvt_t *tech_pvt, verto_profile_t *profile)
+{
+ int i;
+
+ tech_pvt->mparams->rtpip = switch_core_session_strdup(tech_pvt->session, profile->rtpip[profile->rtpip_cur++]);
+
+ if (profile->rtpip_cur == profile->rtpip_index) {
+ profile->rtpip_cur = 0;
+ }
+
+ tech_pvt->mparams->extrtpip = profile->extrtpip;
+
+ //tech_pvt->mparams->dtmf_type = tech_pvt->profile->dtmf_type;
+ switch_channel_set_flag(tech_pvt->channel, CF_TRACKABLE);
+ switch_channel_set_variable(tech_pvt->channel, "secondary_recovery_module", modname);
+
+ switch_core_media_check_dtmf_type(tech_pvt->session);
+
+ //switch_channel_set_cap(tech_pvt->channel, CC_MEDIA_ACK);
+ switch_channel_set_cap(tech_pvt->channel, CC_BYPASS_MEDIA);
+ //switch_channel_set_cap(tech_pvt->channel, CC_PROXY_MEDIA);
+ switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER);
+ switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP);
+
+ //switch_channel_set_cap(tech_pvt->channel, CC_QUEUEABLE_DTMF_DELAY);
+ //tech_pvt->mparams->ndlb = tech_pvt->profile->mndlb;
+
+ tech_pvt->mparams->inbound_codec_string = switch_core_session_strdup(tech_pvt->session, profile->inbound_codec_string);
+ tech_pvt->mparams->outbound_codec_string = switch_core_session_strdup(tech_pvt->session, profile->outbound_codec_string);
+
+ tech_pvt->mparams->jb_msec = "-3";
+ switch_media_handle_set_media_flag(tech_pvt->smh, SCMF_SUPPRESS_CNG);
+
+ switch_media_handle_set_media_flag(tech_pvt->smh, SCMF_RENEG_ON_REINVITE);
+
+ //tech_pvt->mparams->auto_rtp_bugs = profile->auto_rtp_bugs;
+ tech_pvt->mparams->timer_name = profile->timer_name;
+ //tech_pvt->mparams->vflags = profile->vflags;
+ //tech_pvt->mparams->manual_rtp_bugs = profile->manual_rtp_bugs;
+ //tech_pvt->mparams->manual_video_rtp_bugs = profile->manual_video_rtp_bugs;
+
+
+ tech_pvt->mparams->local_network = switch_core_session_strdup(tech_pvt->session, profile->local_network);
+
+
+ //tech_pvt->mparams->rtcp_audio_interval_msec = profile->rtpp_audio_interval_msec;
+ //tech_pvt->mparams->rtcp_video_interval_msec = profile->rtpp_video_interval_msec;
+ //tech_pvt->mparams->sdp_username = profile->sdp_username;
+ //tech_pvt->mparams->cng_pt = tech_pvt->cng_pt;
+ //tech_pvt->mparams->rtc_timeout_sec = profile->rtp_timeout_sec;
+ //tech_pvt->mparams->rtc_hold_timeout_sec = profile->rtp_hold_timeout_sec;
+ //switch_media_handle_set_media_flags(tech_pvt->media_handle, tech_pvt->profile->media_flags);
+
+
+ for(i = 0; i < profile->cand_acl_count; i++) {
+ switch_core_media_add_ice_acl(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, profile->cand_acl[i]);
+ switch_core_media_add_ice_acl(tech_pvt->session, SWITCH_MEDIA_TYPE_VIDEO, profile->cand_acl[i]);
+ }
+}
+
+static switch_status_t verto_media(switch_core_session_t *session)
+{
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_TRUE);
+
+ if (tech_pvt->r_sdp) {
+ if (verto_tech_media(tech_pvt, tech_pvt->r_sdp, SDP_TYPE_REQUEST) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
+ return SWITCH_STATUS_FALSE;
+ }
+ }
+
+ if ((status = switch_core_media_choose_ports(tech_pvt->session, SWITCH_TRUE, SWITCH_FALSE)) != SWITCH_STATUS_SUCCESS) {
+ //if ((status = switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0)) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ return status;
+ }
+
+ switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
+
+ if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ }
+
+ if (tech_pvt->mparams->local_sdp_str) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel),
+ tech_pvt->mparams->local_sdp_str);
+ } else {
+ status = SWITCH_STATUS_FALSE;
+ }
+
+ return status;
+}
+
+
+static switch_status_t verto_send_media_indication(switch_core_session_t *session, const char *method)
+{
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ const char *proxy_sdp = NULL;
+
+ if (switch_test_flag(tech_pvt, TFLAG_SENT_MEDIA)) {
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) {
+ if ((proxy_sdp = switch_channel_get_variable(tech_pvt->channel, SWITCH_B_SDP_VARIABLE))) {
+ status = SWITCH_STATUS_SUCCESS;
+ switch_core_media_set_local_sdp(session, proxy_sdp, SWITCH_TRUE);
+ }
+ }
+
+
+ if (status == SWITCH_STATUS_SUCCESS || (status = verto_media(session)) == SWITCH_STATUS_SUCCESS) {
+ jsock_t *jsock = NULL;
+
+ if (!(jsock = get_jsock(tech_pvt->jsock_uuid))) {
+ status = SWITCH_STATUS_FALSE;
+ } else {
+ cJSON *params = NULL;
+ cJSON *msg = jrpc_new_req(method, tech_pvt->call_id, ¶ms);
+
+ if (!switch_test_flag(tech_pvt, TFLAG_SENT_MEDIA)) {
+ cJSON_AddItemToObject(params, "sdp", cJSON_CreateString(tech_pvt->mparams->local_sdp_str));
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_SENT_MEDIA);
+
+ ws_write_json(jsock, &msg, SWITCH_TRUE);
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ }
+ }
+
+ return status;
+}
+
+static switch_status_t messagehook (switch_core_session_t *session, switch_core_session_message_t *msg)
+{
+ switch_status_t r = SWITCH_STATUS_SUCCESS;
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ switch(msg->message_id) {
+ case SWITCH_MESSAGE_INDICATE_DISPLAY:
+ {
+ const char *name, *number;
+ cJSON *jmsg = NULL, *params = NULL;
+ jsock_t *jsock = NULL;
+
+ if ((jsock = get_jsock(tech_pvt->jsock_uuid))) {
+ name = msg->string_array_arg[0];
+ number = msg->string_array_arg[1];
+
+ if (name || number) {
+ jmsg = jrpc_new_req("verto.display", tech_pvt->call_id, ¶ms);
+ cJSON_AddItemToObject(params, "display_name", cJSON_CreateString(name));
+ cJSON_AddItemToObject(params, "display_number", cJSON_CreateString(number));
+ ws_write_json(jsock, &jmsg, SWITCH_TRUE);
+ }
+
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ }
+
+ }
+ break;
+ case SWITCH_MESSAGE_INDICATE_ANSWER:
+ r = verto_send_media_indication(session, "verto.answer");
+ break;
+ case SWITCH_MESSAGE_INDICATE_PROGRESS:
+ r = verto_send_media_indication(session, "verto.media");
+ break;
+ default:
+ break;
+ }
+
+ return r;
+}
+
+
+
+static int verto_recover_callback(switch_core_session_t *session)
+{
+ int r = 0;
+ char name[512];
+ verto_pvt_t *tech_pvt = NULL;
+ verto_profile_t *profile = NULL;
+ const char *profile_name = NULL, *jsock_uuid_str = NULL;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+
+ profile_name = switch_channel_get_variable(channel, "verto_profile_name");
+ jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str");
+
+ if (!(profile_name && jsock_uuid_str && (profile = find_profile(profile_name)))) {
+ return 0;
+ }
+
+ tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt));
+ tech_pvt->session = session;
+ tech_pvt->channel = channel;
+ tech_pvt->jsock_uuid = (char *) jsock_uuid_str;
+ switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_SECONDARY);
+
+
+ tech_pvt->call_id = switch_core_session_strdup(session, switch_core_session_get_uuid(session));
+ if ((tech_pvt->smh = switch_core_session_get_media_handle(session))) {
+ tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh);
+ verto_set_media_options(tech_pvt, profile);
+ }
+
+ switch_snprintf(name, sizeof(name), "verto.rtc/%s", tech_pvt->jsock_uuid);
+ switch_channel_set_name(channel, name);
+
+ switch_channel_add_state_handler(channel, &verto_state_handlers);
+ switch_core_event_hook_add_receive_message(session, messagehook);
+
+ track_pvt(tech_pvt);
+
+ //switch_channel_clear_flag(tech_pvt->channel, CF_ANSWERED);
+ //switch_channel_clear_flag(tech_pvt->channel, CF_EARLY_MEDIA);
+
+ switch_thread_rwlock_unlock(profile->rwlock);
+
+ r++;
+
+ return r;
+}
+
+
+static void pass_sdp(verto_pvt_t *tech_pvt)
+{
+ switch_core_session_t *other_session = NULL;
+ switch_channel_t *other_channel = NULL;
+
+ if (switch_core_session_get_partner(tech_pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) {
+ other_channel = switch_core_session_get_channel(other_session);
+ switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, tech_pvt->r_sdp);
+ switch_channel_set_flag(other_channel, CF_PROXY_MODE);
+ switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER);
+ switch_core_session_rwunlock(other_session);
+ }
+}
+
+
+//// METHODS
+
+#define switch_either(_A, _B) zstr(_A) ? _B : _A
+
+static switch_bool_t verto__answer_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *obj = cJSON_CreateObject();
+ switch_core_session_t *session;
+ cJSON *dialog = NULL;
+ const char *call_id = NULL, *sdp = NULL;
+ int err = 0;
+
+ *response = obj;
+
+ if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing"));
+ err = 1; goto cleanup;
+ }
+
+
+ if ((session = switch_core_session_locate(call_id))) {
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ tech_pvt->r_sdp = switch_core_session_strdup(session, sdp);
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_R_SDP_VARIABLE, sdp);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel), sdp);
+ switch_core_media_set_sdp_codec_string(session, sdp, SDP_TYPE_RESPONSE);
+
+ if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE)) {
+ pass_sdp(tech_pvt);
+ } else {
+ if (verto_tech_media(tech_pvt, tech_pvt->r_sdp, SDP_TYPE_RESPONSE) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CODEC ERROR"));
+ err = 1;
+ }
+
+ if (!err && switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("MEDIA ERROR"));
+ err = 1;
+ }
+ }
+
+ if (!err) {
+ switch_channel_mark_answered(tech_pvt->channel);
+ }
+
+ switch_core_session_rwunlock(session);
+ }
+
+ cleanup:
+
+
+ if (!err) return SWITCH_TRUE;
+
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST"));
+ cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+
+
+ return SWITCH_FALSE;
+
+}
+
+static switch_bool_t verto__bye_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *obj = cJSON_CreateObject();
+ switch_core_session_t *session;
+ cJSON *dialog = NULL;
+ const char *call_id = NULL, *cause_str = NULL;
+ int err = 0;
+ switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
+
+ *response = obj;
+
+ if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if ((cause_str = cJSON_GetObjectCstr(params, "cause"))) {
+ switch_call_cause_t check = switch_channel_str2cause(cause_str);
+
+ if (check != SWITCH_CAUSE_NONE) {
+ cause = check;
+ }
+ }
+
+ cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(call_id));
+
+ if ((session = switch_core_session_locate(call_id))) {
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ tech_pvt->remote_hangup_cause = cause;
+ switch_channel_hangup(tech_pvt->channel, cause);
+
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL ENDED"));
+ cJSON_AddItemToObject(obj, "causeCode", cJSON_CreateNumber(cause));
+ cJSON_AddItemToObject(obj, "cause", cJSON_CreateString(switch_channel_cause2str(cause)));
+ switch_core_session_rwunlock(session);
+ } else {
+ err = 1;
+ }
+
+ cleanup:
+
+
+ if (!err) return SWITCH_TRUE;
+
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST"));
+ cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+
+
+ return SWITCH_FALSE;
+}
+
+static switch_status_t xfer_hanguphook(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_channel_state_t state = switch_channel_get_state(channel);
+
+ if (state == CS_HANGUP) {
+ switch_core_session_t *ksession;
+ const char *uuid = switch_channel_get_variable(channel, "att_xfer_kill_uuid");
+
+ if (uuid && (ksession = switch_core_session_force_locate(uuid))) {
+ switch_channel_t *kchannel = switch_core_session_get_channel(ksession);
+
+ switch_channel_clear_flag(kchannel, CF_XFER_ZOMBIE);
+ switch_channel_clear_flag(kchannel, CF_TRANSFER);
+ if (switch_channel_up(kchannel)) {
+ switch_channel_hangup(kchannel, SWITCH_CAUSE_NORMAL_CLEARING);
+ }
+
+ switch_core_session_rwunlock(ksession);
+ }
+
+ switch_core_event_hook_remove_state_change(session, xfer_hanguphook);
+
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void mark_transfer_record(switch_core_session_t *session, const char *br_a, const char *br_b)
+{
+ switch_core_session_t *br_b_session, *br_a_session;
+ switch_channel_t *channel;
+ const char *uvar1, *dvar1, *uvar2, *dvar2;
+
+ channel = switch_core_session_get_channel(session);
+
+ uvar1 = "verto_user";
+ dvar1 = "verto_host";
+
+ if ((br_b_session = switch_core_session_locate(br_b)) ) {
+ switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session);
+ switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_b_channel);
+
+ if (switch_channel_direction(br_b_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+ uvar2 = "sip_from_user";
+ dvar2 = "sip_from_host";
+ } else {
+ uvar2 = "sip_to_user";
+ dvar2 = "sip_to_host";
+ }
+
+ cp->transfer_source = switch_core_sprintf(cp->pool,
+ "%ld:%s:att_xfer:%s@%s/%s@%s",
+ (long) switch_epoch_time_now(NULL),
+ cp->uuid_str,
+ switch_channel_get_variable(channel, uvar1),
+ switch_channel_get_variable(channel, dvar1),
+ switch_channel_get_variable(br_b_channel, uvar2),
+ switch_channel_get_variable(br_b_channel, dvar2));
+
+ switch_channel_add_variable_var_check(br_b_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
+ switch_channel_set_variable(br_b_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source);
+
+ switch_core_session_rwunlock(br_b_session);
+ }
+
+
+
+ if ((br_a_session = switch_core_session_locate(br_a)) ) {
+ switch_channel_t *br_a_channel = switch_core_session_get_channel(br_a_session);
+ switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_a_channel);
+
+ if (switch_channel_direction(br_a_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+ uvar2 = "sip_from_user";
+ dvar2 = "sip_from_host";
+ } else {
+ uvar2 = "sip_to_user";
+ dvar2 = "sip_to_host";
+ }
+
+ cp->transfer_source = switch_core_sprintf(cp->pool,
+ "%ld:%s:att_xfer:%s@%s/%s@%s",
+ (long) switch_epoch_time_now(NULL),
+ cp->uuid_str,
+ switch_channel_get_variable(channel, uvar1),
+ switch_channel_get_variable(channel, dvar1),
+ switch_channel_get_variable(br_a_channel, uvar2),
+ switch_channel_get_variable(br_a_channel, dvar2));
+
+ switch_channel_add_variable_var_check(br_a_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
+ switch_channel_set_variable(br_a_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source);
+
+ switch_core_session_rwunlock(br_a_session);
+ }
+
+
+}
+
+static switch_bool_t attended_transfer(switch_core_session_t *session, switch_core_session_t *b_session) {
+ verto_pvt_t *tech_pvt = NULL, *b_tech_pvt = NULL;
+ switch_bool_t result = SWITCH_FALSE;
+ const char *br_a = NULL, *br_b = NULL;
+
+ tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ b_tech_pvt = switch_core_session_get_private_class(b_session, SWITCH_PVT_SECONDARY);
+
+ switch_channel_set_variable(tech_pvt->channel, "refer_uuid", switch_core_session_get_uuid(b_tech_pvt->session));
+ switch_channel_set_variable(b_tech_pvt->channel, "transfer_disposition", "replaced");
+
+ br_a = switch_channel_get_partner_uuid(tech_pvt->channel);
+ br_b = switch_channel_get_partner_uuid(b_tech_pvt->channel);
+
+ if (!switch_ivr_uuid_exists(br_a)) {
+ br_a = NULL;
+ }
+
+ if (!switch_ivr_uuid_exists(br_b)) {
+ br_b = NULL;
+ }
+
+ if (switch_channel_test_flag(b_tech_pvt->channel, CF_ORIGINATOR)) {
+ switch_core_session_t *a_session;
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE,
+ "Attended Transfer on originating session %s\n", switch_core_session_get_uuid(b_session));
+
+
+
+ switch_channel_set_variable_printf(b_tech_pvt->channel, "transfer_to", "satt:%s", br_a);
+
+ switch_channel_set_variable(b_tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
+
+
+ switch_channel_clear_flag(b_tech_pvt->channel, CF_LEG_HOLDING);
+ switch_channel_set_variable(b_tech_pvt->channel, SWITCH_HOLDING_UUID_VARIABLE, br_a);
+ switch_channel_set_flag(b_tech_pvt->channel, CF_XFER_ZOMBIE);
+ switch_channel_set_flag(b_tech_pvt->channel, CF_TRANSFER);
+
+
+ if ((a_session = switch_core_session_locate(br_a))) {
+ const char *moh = "local_stream://moh";
+ switch_channel_t *a_channel = switch_core_session_get_channel(a_session);
+ switch_caller_profile_t *prof = switch_channel_get_caller_profile(b_tech_pvt->channel);
+ const char *tmp;
+
+ switch_core_event_hook_add_state_change(a_session, xfer_hanguphook);
+ switch_channel_set_variable(a_channel, "att_xfer_kill_uuid", switch_core_session_get_uuid(b_session));
+ switch_channel_set_variable(a_channel, "att_xfer_destination_number", prof->destination_number);
+ switch_channel_set_variable(a_channel, "att_xfer_callee_id_name", prof->callee_id_name);
+ switch_channel_set_variable(a_channel, "att_xfer_callee_id_number", prof->callee_id_number);
+
+ if ((tmp = switch_channel_get_hold_music(a_channel))) {
+ moh = tmp;
+ }
+
+ if (!zstr(moh) && !strcasecmp(moh, "silence")) {
+ moh = NULL;
+ }
+
+ if (moh) {
+ char *xdest;
+ xdest = switch_core_session_sprintf(a_session, "endless_playback:%s,park", moh);
+ switch_ivr_session_transfer(a_session, xdest, "inline", NULL);
+ } else {
+ switch_ivr_session_transfer(a_session, "park", "inline", NULL);
+ }
+
+ switch_core_session_rwunlock(a_session);
+
+ result = SWITCH_TRUE;
+
+ if (b_tech_pvt) {
+ switch_channel_hangup(b_tech_pvt->channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ }
+ } else {
+ result = SWITCH_FALSE;
+ }
+
+ } else if (br_a && br_b) {
+ switch_core_session_t *tmp = NULL;
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n",
+ switch_str_nil(br_a), switch_str_nil(br_b));
+
+ if ((tmp = switch_core_session_locate(br_b))) {
+ switch_channel_t *tchannel = switch_core_session_get_channel(tmp);
+
+ switch_channel_set_variable(tchannel, "transfer_disposition", "bridge");
+
+ switch_channel_set_flag(tchannel, CF_ATTENDED_TRANSFER);
+ switch_core_session_rwunlock(tmp);
+ }
+
+ if (switch_true(switch_channel_get_variable(tech_pvt->channel, "recording_follow_transfer")) &&
+ (tmp = switch_core_session_locate(br_a))) {
+ switch_channel_set_variable(switch_core_session_get_channel(tmp), "transfer_disposition", "bridge");
+ switch_core_media_bug_transfer_recordings(session, tmp);
+ switch_core_session_rwunlock(tmp);
+ }
+
+
+ if (switch_true(switch_channel_get_variable(b_tech_pvt->channel, "recording_follow_transfer")) &&
+ (tmp = switch_core_session_locate(br_b))) {
+ switch_core_media_bug_transfer_recordings(b_session, tmp);
+ switch_core_session_rwunlock(tmp);
+ }
+
+ switch_channel_set_variable_printf(tech_pvt->channel, "transfer_to", "att:%s", br_b);
+
+ mark_transfer_record(session, br_a, br_b);
+
+ switch_ivr_uuid_bridge(br_a, br_b);
+ switch_channel_set_variable(b_tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
+
+ result = SWITCH_TRUE;
+
+ switch_channel_clear_flag(b_tech_pvt->channel, CF_LEG_HOLDING);
+ switch_channel_set_variable(b_tech_pvt->channel, "park_timeout", "2:attended_transfer");
+ switch_channel_set_state(b_tech_pvt->channel, CS_PARK);
+
+ } else {
+ if (!br_a && !br_b) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
+ "Cannot transfer channels that are not in a bridge.\n");
+ result = SWITCH_FALSE;
+ } else {
+ switch_core_session_t *t_session, *hup_session;
+ switch_channel_t *hup_channel;
+ const char *ext;
+
+ if (br_a && !br_b) {
+ t_session = switch_core_session_locate(br_a);
+ hup_channel = b_tech_pvt->channel;
+ hup_session = b_session;
+ } else {
+ verto_pvt_t *h_tech_pvt = (verto_pvt_t *) switch_core_session_get_private_class(b_session, SWITCH_PVT_SECONDARY);
+ t_session = switch_core_session_locate(br_b);
+ hup_channel = tech_pvt->channel;
+ hup_session = session;
+ switch_channel_clear_flag(h_tech_pvt->channel, CF_LEG_HOLDING);
+ switch_channel_hangup(b_tech_pvt->channel, SWITCH_CAUSE_ATTENDED_TRANSFER);
+ }
+
+ if (t_session) {
+ //switch_channel_t *t_channel = switch_core_session_get_channel(t_session);
+ const char *idest = switch_channel_get_variable(hup_channel, "inline_destination");
+ ext = switch_channel_get_variable(hup_channel, "destination_number");
+
+ if (switch_true(switch_channel_get_variable(hup_channel, "recording_follow_transfer"))) {
+ switch_core_media_bug_transfer_recordings(hup_session, t_session);
+ }
+
+ if (idest) {
+ switch_ivr_session_transfer(t_session, idest, "inline", NULL);
+ } else {
+ switch_ivr_session_transfer(t_session, ext, NULL, NULL);
+ }
+
+ result = SWITCH_TRUE;
+ switch_channel_hangup(hup_channel, SWITCH_CAUSE_ATTENDED_TRANSFER);
+ } else {
+ result = SWITCH_FALSE;
+ }
+ }
+ }
+ if (b_session) {
+ switch_core_session_rwunlock(b_session);
+ }
+
+ return result;
+}
+
+
+static switch_bool_t verto__modify_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *obj = cJSON_CreateObject();
+ switch_core_session_t *session;
+ cJSON *dialog = NULL;
+ const char *call_id = NULL, *destination = NULL, *action = NULL;
+ int err = 0;
+
+ *response = obj;
+
+ if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(action = cJSON_GetObjectCstr(params, "action"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("action missing"));
+ err = 1; goto cleanup;
+ }
+
+ cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(call_id));
+ cJSON_AddItemToObject(obj, "action", cJSON_CreateString(action));
+
+
+ if ((session = switch_core_session_locate(call_id))) {
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+ if (!strcasecmp(action, "transfer")) {
+ switch_core_session_t *other_session = NULL;
+
+ if (!(destination = cJSON_GetObjectCstr(params, "destination"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("destination missing"));
+ err = 1; goto rwunlock;
+ }
+
+ if (switch_core_session_get_partner(tech_pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) {
+ switch_ivr_session_transfer(other_session, destination, NULL, NULL);
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL TRANSFERRED"));
+ switch_core_session_rwunlock(other_session);
+ } else {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("call is not bridged"));
+ err = 1; goto rwunlock;
+ }
+
+ } else if (!strcasecmp(action, "replace")) {
+ const char *replace_call_id;
+ switch_core_session_t *b_session = NULL;
+
+ if (!(replace_call_id = cJSON_GetObjectCstr(params, "replaceCallID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("replaceCallID missing"));
+ err = 1; goto rwunlock;
+ }
+
+ if ((b_session = switch_core_session_locate(replace_call_id))) {
+ err = (int) attended_transfer(session, b_session);
+ if (err) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("transfer failed"));
+ }
+ switch_core_session_rwunlock(b_session);
+ } else {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("invalid transfer leg"));
+ err = 1; goto rwunlock;
+ }
+ } else if (!strcasecmp(action, "hold")) {
+ switch_core_media_toggle_hold(session, 1);
+ } else if (!strcasecmp(action, "unhold")) {
+ switch_core_media_toggle_hold(session, 0);
+ } else if (!strcasecmp(action, "toggleHold")) {
+ switch_core_media_toggle_hold(session, !!!switch_channel_test_flag(tech_pvt->channel, CF_PROTO_HOLD));
+ }
+
+ cJSON_AddItemToObject(obj, "holdState", cJSON_CreateString(switch_channel_test_flag(tech_pvt->channel, CF_PROTO_HOLD) ? "held" : "active"));
+
+
+ rwunlock:
+
+ switch_core_session_rwunlock(session);
+ } else {
+ err = 1;
+ }
+
+ cleanup:
+
+
+ if (!err) return SWITCH_TRUE;
+
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL DOES NOT EXIST"));
+ cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+
+
+ return SWITCH_FALSE;
+}
+
+static switch_bool_t verto__attach_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *obj = cJSON_CreateObject();
+ switch_core_session_t *session = NULL;
+ int err = 0;
+ cJSON *dialog;
+ verto_pvt_t *tech_pvt = NULL;
+ const char *call_id = NULL, *sdp = NULL;
+ uint8_t match = 0, p = 0;
+
+ *response = obj;
+
+ if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(session = switch_core_session_locate(call_id))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Call does not exist"));
+ err = 1; goto cleanup;
+ }
+
+ tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ tech_pvt->r_sdp = switch_core_session_strdup(session, sdp);
+
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_R_SDP_VARIABLE, tech_pvt->r_sdp);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n",
+ switch_channel_get_name(tech_pvt->channel), tech_pvt->r_sdp);
+
+ switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
+
+ if ((match = switch_core_media_negotiate_sdp(tech_pvt->session, tech_pvt->r_sdp, &p, SDP_TYPE_RESPONSE))) {
+ if (switch_core_media_activate_rtp(tech_pvt->session) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "MEDIA ERROR");
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("MEDIA ERROR"));
+ err = 1; goto cleanup;
+ }
+ } else {
+ switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CODEC NEGOTIATION ERROR"));
+ err = 1; goto cleanup;
+ }
+
+ cleanup:
+
+ if (tech_pvt) {
+ switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE);
+ switch_clear_flag(tech_pvt, TFLAG_ATTACH_REQ);
+ if (switch_channel_test_flag(tech_pvt->channel, CF_CONFERENCE)) {
+ switch_channel_set_flag(tech_pvt->channel, CF_CONFERENCE_ADV);
+ }
+ }
+
+ if (session) {
+ switch_core_session_rwunlock(session);
+ }
+
+ if (!err) {
+ return SWITCH_TRUE;
+ }
+
+ if (tech_pvt && tech_pvt->channel) {
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ }
+
+
+ cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+
+ return SWITCH_FALSE;
+}
+
+static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *msg = NULL, *dialog = NULL;
+ const char *call_id = NULL, *dtmf = NULL;
+ switch_bool_t r = SWITCH_TRUE;
+ char *proto = VERTO_CHAT_PROTO;
+ char *pproto = NULL;
+
+ *response = cJSON_CreateObject();
+
+ if ((dialog = cJSON_GetObjectItem(params, "dialogParams")) && (call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ switch_core_session_t *session = NULL;
+
+ if ((session = switch_core_session_locate(call_id))) {
+
+ if ((dtmf = cJSON_GetObjectCstr(params, "dtmf"))) {
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ char *send = switch_mprintf("~%s", dtmf);
+ switch_channel_queue_dtmf_string(tech_pvt->channel, send);
+ free(send);
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("SENT"));
+ }
+
+ switch_core_session_rwunlock(session);
+ }
+ }
+
+ if ((msg = cJSON_GetObjectItem(params, "msg"))) {
+ switch_event_t *event;
+ char *to = (char *) cJSON_GetObjectCstr(msg, "to");
+ cJSON *indialog = cJSON_GetObjectItem(msg, "inDialog");
+ const char *body = cJSON_GetObjectCstr(msg, "body");
+
+
+ if (!zstr(to)) {
+ if (strchr(to, '+')) {
+ pproto = strdup(to);
+ if ((to = strchr(pproto, '+'))) {
+ *to++ = '\0';
+ }
+ proto = pproto;
+ }
+ }
+
+ if (!zstr(to) && !zstr(body) && switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VERTO_CHAT_PROTO);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", jsock->uid);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", jsock->id);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", jsock->domain);
+
+
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_full", jsock->id);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "verto_profile", jsock->profile->name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "verto_jsock_uuid", jsock->uuid_str);
+
+ if (indialog && (indialog->type == cJSON_True || (indialog->type == cJSON_String && switch_true(indialog->valuestring))) && call_id) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call_id", call_id);
+ }
+
+ switch_event_add_body(event, "%s", body);
+
+ if (strcasecmp(proto, VERTO_CHAT_PROTO)) {
+ switch_core_chat_send(proto, event);
+ }
+
+ switch_core_chat_send("GLOBAL", event);
+
+ switch_event_destroy(&event);
+
+ } else {
+ r = SWITCH_FALSE;
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("INVALID MESSAGE to and body params required"));
+ }
+
+
+ switch_safe_free(pproto);
+ }
+
+
+ return r;
+}
+
+static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *obj = cJSON_CreateObject();
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel;
+ switch_event_t *var_event;
+ switch_call_cause_t reason = SWITCH_CAUSE_INVALID_MSG_UNSPECIFIED, cancel_cause = 0;
+ switch_caller_profile_t *caller_profile;
+ int err = 0;
+ cJSON *dialog;
+ verto_pvt_t *tech_pvt;
+ char name[512];
+ const char *var, *destination_number, *call_id = NULL, *sdp = NULL, *caller_id_name = NULL, *caller_id_number = NULL, *context = NULL;
+
+ *response = obj;
+
+ switch_event_create_plain(&var_event, SWITCH_EVENT_CHANNEL_DATA);
+
+ if (!(dialog = cJSON_GetObjectItem(params, "dialogParams"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Dialog data missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(call_id = cJSON_GetObjectCstr(dialog, "callID"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CallID missing"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(sdp = cJSON_GetObjectCstr(params, "sdp"))) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("SDP missing"));
+ err = 1; goto cleanup;
+ }
+
+ switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "origination_uuid", call_id);
+ if ((reason = switch_core_session_outgoing_channel(NULL, var_event, "rtc",
+ NULL, &session, NULL, SOF_NONE, &cancel_cause)) != SWITCH_CAUSE_SUCCESS) {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Cannot create channel"));
+ err = 1; goto cleanup;
+ }
+
+ channel = switch_core_session_get_channel(session);
+ switch_channel_set_direction(channel, SWITCH_CALL_DIRECTION_INBOUND);
+
+ tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt));
+ tech_pvt->session = session;
+ tech_pvt->channel = channel;
+ tech_pvt->jsock_uuid = switch_core_session_strdup(session, jsock->uuid_str);
+ tech_pvt->r_sdp = switch_core_session_strdup(session, sdp);
+ switch_core_media_set_sdp_codec_string(session, sdp, SDP_TYPE_REQUEST);
+ switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_SECONDARY);
+
+
+ tech_pvt->call_id = switch_core_session_strdup(session, call_id);
+ if ((tech_pvt->smh = switch_core_session_get_media_handle(session))) {
+ tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh);
+ verto_set_media_options(tech_pvt, jsock->profile);
+ } else {
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("Cannot create media handle"));
+ err = 1; goto cleanup;
+ }
+
+ if (!(destination_number = cJSON_GetObjectCstr(dialog, "destination_number"))) {
+ destination_number = "service";
+ }
+
+ switch_snprintf(name, sizeof(name), "verto.rtc/%s", destination_number);
+ switch_channel_set_name(channel, name);
+ switch_channel_set_variable(channel, "jsock_uuid_str", jsock->uuid_str);
+ switch_channel_set_variable(channel, "verto_user", jsock->uid);
+ switch_channel_set_variable(channel, "verto_host", jsock->domain);
+ switch_channel_set_variable(channel, "event_channel_cookie", tech_pvt->jsock_uuid);
+ switch_channel_set_variable(channel, "verto_profile_name", jsock->profile->name);
+
+ caller_id_name = cJSON_GetObjectCstr(dialog, "caller_id_name");
+ caller_id_number = cJSON_GetObjectCstr(dialog, "caller_id_number");
+
+ if (zstr(caller_id_name)) {
+ if ((var = switch_event_get_header(jsock->params, "caller-id-name"))) {
+ caller_id_name = var;
+ }
+ }
+
+ if (zstr(caller_id_number)) {
+ if ((var = switch_event_get_header(jsock->params, "caller-id-number"))) {
+ caller_id_number = var;
+ }
+ }
+
+ if (!(context = switch_event_get_header(jsock->vars, "user_context"))) {
+ context = switch_either(jsock->context, jsock->profile->context);
+ }
+
+ if ((caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
+ jsock->uid,
+ switch_either(jsock->dialplan, jsock->profile->dialplan),
+ caller_id_name,
+ caller_id_number,
+ inet_ntoa(jsock->remote_addr.sin_addr),
+ cJSON_GetObjectCstr(dialog, "ani"),
+ cJSON_GetObjectCstr(dialog, "aniii"),
+ cJSON_GetObjectCstr(dialog, "rdnis"),
+ modname,
+ context,
+ destination_number))) {
+
+ switch_channel_set_caller_profile(channel, caller_profile);
+
+ }
+
+
+ switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, sdp);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP %s:\n%s\n", switch_channel_get_name(tech_pvt->channel), sdp);
+
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL CREATED"));
+ cJSON_AddItemToObject(obj, "callID", cJSON_CreateString(tech_pvt->call_id));
+
+ switch_channel_add_state_handler(channel, &verto_state_handlers);
+ switch_core_event_hook_add_receive_message(session, messagehook);
+ switch_channel_set_state(channel, CS_INIT);
+ track_pvt(tech_pvt);
+ switch_core_session_thread_launch(session);
+
+ cleanup:
+
+ switch_event_destroy(&var_event);
+
+ if (!err) {
+ return SWITCH_TRUE;
+ }
+
+ if (session) {
+ switch_core_session_destroy(&session);
+ }
+
+ cJSON_AddItemToObject(obj, "causeCode", cJSON_CreateNumber(reason));
+ cJSON_AddItemToObject(obj, "cause", cJSON_CreateString(switch_channel_cause2str(reason)));
+ cJSON_AddItemToObject(obj, "message", cJSON_CreateString("CALL ERROR"));
+ cJSON_AddItemToObject(obj, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+
+ return SWITCH_FALSE;
+
+}
+
+static switch_bool_t event_channel_check_auth(jsock_t *jsock, const char *event_channel)
+{
+
+ char *main_event_channel = NULL;
+ switch_bool_t ok = SWITCH_TRUE, pre_ok = SWITCH_FALSE;
+ switch_core_session_t *session = NULL;
+
+ switch_assert(event_channel);
+
+ pre_ok = switch_event_channel_permission_verify(jsock->uuid_str, event_channel);
+
+ if (!pre_ok && (session = switch_core_session_locate(event_channel))) {
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ const char *jsock_uuid_str = switch_channel_get_variable(channel, "jsock_uuid_str");
+
+ if (jsock_uuid_str && !strcmp(jsock_uuid_str, jsock->uuid_str)) {
+ pre_ok = SWITCH_TRUE;
+ }
+
+ switch_core_session_rwunlock(session);
+ }
+
+ if (pre_ok) {
+ return pre_ok;
+ }
+
+ if (jsock->allowed_event_channels) {
+ if (strchr(event_channel, '.')) {
+ char *p;
+ main_event_channel = strdup(event_channel);
+ if ((p = strchr(main_event_channel, '.'))) {
+ *p = '\0';
+ }
+ }
+
+ if (!(switch_event_get_header(jsock->allowed_event_channels, event_channel) ||
+ (main_event_channel && switch_event_get_header(jsock->allowed_event_channels, main_event_channel)))) {
+ ok = SWITCH_FALSE;
+ }
+ }
+
+ switch_safe_free(main_event_channel);
+ return ok;
+
+}
+
+static switch_bool_t parse_subs(jsock_t *jsock, const char *event_channel, cJSON **sub_list, cJSON **err_list, cJSON **exist_list)
+{
+ switch_bool_t r = SWITCH_FALSE;
+
+ if (event_channel_check_auth(jsock, event_channel)) {
+ if (!*sub_list) {
+ *sub_list = cJSON_CreateArray();
+ }
+
+ if (jsock_sub_channel(jsock, event_channel) == SWITCH_STATUS_SUCCESS) {
+ cJSON_AddItemToArray(*sub_list, cJSON_CreateString(event_channel));
+ } else {
+ if (!*exist_list) {
+ *exist_list = cJSON_CreateArray();
+ }
+ cJSON_AddItemToArray(*exist_list, cJSON_CreateString(event_channel));
+ }
+
+ r = SWITCH_TRUE;
+ } else {
+ if (!*err_list) {
+ *err_list = cJSON_CreateArray();
+ }
+ cJSON_AddItemToArray(*err_list, cJSON_CreateString(event_channel));
+ }
+
+ return r;
+}
+
+static switch_bool_t verto__subscribe_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ switch_bool_t r = SWITCH_TRUE;
+ cJSON *subs = NULL, *errs = NULL, *exist = NULL;
+
+ *response = cJSON_CreateObject();
+
+ if (params) {
+ cJSON *jchannel = cJSON_GetObjectItem(params, "eventChannel");
+
+ if (jchannel) {
+ if (jchannel->type == cJSON_String) {
+ parse_subs(jsock, jchannel->valuestring, &subs, &errs, &exist);
+ } else if (jchannel->type == cJSON_Array) {
+ int i, len = cJSON_GetArraySize(jchannel);
+
+ for(i = 0; i < len; i++) {
+ cJSON *str = cJSON_GetArrayItem(jchannel, i);
+ if (str->type == cJSON_String) {
+ parse_subs(jsock, str->valuestring, &subs, &errs, &exist);
+ }
+ }
+ }
+ }
+ }
+
+ if (subs) {
+ cJSON_AddItemToObject(*response, "subscribedChannels", subs);
+ }
+
+ if (errs) {
+ cJSON_AddItemToObject(*response, "unauthorizedChannels", errs);
+ }
+
+ if (exist) {
+ cJSON_AddItemToObject(*response, "alreadySubscribedChannels", exist);
+ }
+
+ if (!subs) {
+ r = SWITCH_FALSE;
+ }
+
+ return r;
+}
+
+static void do_unsub(jsock_t *jsock, const char *event_channel, cJSON **subs, cJSON **errs)
+{
+ if (jsock_unsub_channel(jsock, event_channel)) {
+ if (!*subs) {
+ *subs = cJSON_CreateArray();
+ }
+ cJSON_AddItemToArray(*subs, cJSON_CreateString(event_channel));
+ } else {
+ if (!*errs) {
+ *errs = cJSON_CreateArray();
+ }
+ cJSON_AddItemToArray(*errs, cJSON_CreateString(event_channel));
+ }
+}
+
+static switch_bool_t verto__unsubscribe_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ switch_bool_t r = SWITCH_TRUE;
+ cJSON *subs = NULL, *errs = NULL;
+
+ *response = cJSON_CreateObject();
+
+ if (params) {
+ cJSON *jchannel = cJSON_GetObjectItem(params, "eventChannel");
+
+ if (jchannel) {
+ if (jchannel->type == cJSON_String) {
+ do_unsub(jsock, jchannel->valuestring, &subs, &errs);
+ } else if (jchannel->type == cJSON_Array) {
+ int i, len = cJSON_GetArraySize(jchannel);
+
+ for(i = 0; i < len; i++) {
+ cJSON *str = cJSON_GetArrayItem(jchannel, i);
+ if (str->type == cJSON_String) {
+ do_unsub(jsock, str->valuestring, &subs, &errs);
+ }
+ }
+ }
+ }
+ }
+
+ if (subs) {
+ cJSON_AddItemToObject(*response, "unsubscribedChannels", subs);
+ }
+
+ if (errs) {
+ cJSON_AddItemToObject(*response, "notSubscribedChannels", errs);
+ }
+
+ if (errs && !subs) {
+ r = SWITCH_FALSE;
+ }
+
+ return r;
+}
+
+static switch_bool_t verto__broadcast_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ char *json_text = NULL;
+ switch_bool_t r = SWITCH_FALSE;
+ const char *event_channel = cJSON_GetObjectCstr(params, "eventChannel");
+ cJSON *jevent;
+
+ *response = cJSON_CreateObject();
+
+
+ if (!event_channel) {
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("eventChannel not specified."));
+ cJSON_AddItemToObject(*response, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+ goto end;
+ }
+
+ if (!event_channel_check_auth(jsock, event_channel)) {
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("Permission Denied."));
+ cJSON_AddItemToObject(*response, "code", cJSON_CreateNumber(CODE_SESSION_ERROR));
+ goto end;
+ }
+
+
+ cJSON_AddItemToObject(params, "userid", cJSON_CreateString(jsock->uid));
+
+ jevent = cJSON_Duplicate(params, 1);
+ switch_event_channel_broadcast(event_channel, &jevent, modname, globals.event_channel_id);
+
+ if (jsock->profile->mcast_pub.sock > -1) {
+ if ((json_text = cJSON_PrintUnformatted(params))) {
+
+ mcast_socket_send(&jsock->profile->mcast_pub, json_text, strlen(json_text) + 1);
+
+
+ free(json_text);
+ json_text = NULL;
+ r = SWITCH_TRUE;
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("MCAST Data Sent"));
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "JSON ERROR!\n");
+ }
+ }
+
+ end:
+
+ return r;
+}
+
+static switch_bool_t login_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ *response = cJSON_CreateObject();
+ cJSON_AddItemToObject(*response, "message", cJSON_CreateString("logged in"));
+
+ return SWITCH_TRUE;
+}
+
+static switch_bool_t echo_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ *response = cJSON_Duplicate(params, 1);
+ return SWITCH_TRUE;
+}
+
+static switch_bool_t jsapi_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ if (jsock->allowed_jsapi) {
+ const char *function;
+
+ if (params) {
+ if ((function = cJSON_GetObjectCstr(params, "command"))) {
+ if (!switch_event_get_header(jsock->allowed_jsapi, function)) {
+ return SWITCH_FALSE;
+ }
+
+ if (jsock->allowed_fsapi && !strcmp(function, "fsapi")) {
+ cJSON *cmd = cJSON_GetObjectItem(params, "cmd");
+ cJSON *arg = cJSON_GetObjectItem(params, "arg");
+
+ if (cmd->type == cJSON_String && cmd->valuestring && !auth_api_command(jsock, cmd->valuestring, arg ? arg->valuestring : NULL)) {
+ return SWITCH_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ switch_json_api_execute(params, NULL, response);
+
+ return *response ? SWITCH_TRUE : SWITCH_FALSE;
+}
+
+static switch_bool_t fsapi_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
+{
+ cJSON *cmd, *arg, *reply;
+ switch_stream_handle_t stream = { 0 };
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ cmd = cJSON_GetObjectItem(params, "cmd");
+ arg = cJSON_GetObjectItem(params, "arg");
+
+
+ if (jsock->allowed_fsapi) {
+ if (cmd->type == cJSON_String && cmd->valuestring && !auth_api_command(jsock, cmd->valuestring, arg ? arg->valuestring : NULL)) {
+ return SWITCH_FALSE;
+ }
+ }
+
+ if (cmd && !cmd->valuestring) {
+ cmd = NULL;
+ }
+
+ if (arg && !arg->valuestring) {
+ arg = NULL;
+ }
+
+ reply = cJSON_CreateObject();
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ if (cmd && (status = switch_api_execute(cmd->valuestring, arg ? arg->valuestring : NULL, NULL, &stream)) == SWITCH_STATUS_SUCCESS) {
+ cJSON_AddItemToObject(reply, "message", cJSON_CreateString((char *) stream.data));
+ } else {
+ cJSON_AddItemToObject(reply, "message", cJSON_CreateString("INVALID CALL"));
+ }
+
+ switch_safe_free(stream.data);
+
+ if (reply) {
+ *response = reply;
+ return SWITCH_TRUE;
+ }
+
+ return SWITCH_FALSE;
+}
+
+////
+
+static void jrpc_init(void)
+{
+ jrpc_add_func("echo", echo_func);
+ jrpc_add_func("jsapi", jsapi_func);
+ jrpc_add_func("fsapi", fsapi_func);
+ jrpc_add_func("login", login_func);
+
+ jrpc_add_func("verto.invite", verto__invite_func);
+ jrpc_add_func("verto.info", verto__info_func);
+ jrpc_add_func("verto.attach", verto__attach_func);
+ jrpc_add_func("verto.bye", verto__bye_func);
+ jrpc_add_func("verto.answer", verto__answer_func);
+ jrpc_add_func("verto.subscribe", verto__subscribe_func);
+ jrpc_add_func("verto.unsubscribe", verto__unsubscribe_func);
+ jrpc_add_func("verto.broadcast", verto__broadcast_func);
+ jrpc_add_func("verto.modify", verto__modify_func);
+
+}
+
+
+
+
+static int start_jsock(verto_profile_t *profile, int sock)
+{
+ jsock_t *jsock = NULL;
+ int flag = 1;
+ int i;
+ unsigned int len;
+ jsock_type_t ptype = PTYPE_CLIENT;
+ switch_thread_data_t *td;
+ switch_memory_pool_t *pool;
+
+ switch_core_new_memory_pool(&pool);
+
+
+ jsock = (jsock_t *) switch_core_alloc(pool, sizeof(*jsock));
+ jsock->pool = pool;
+
+ len = sizeof(jsock->remote_addr);
+
+ if ((jsock->client_socket = accept(sock, (struct sockaddr *) &jsock->remote_addr, &len)) < 0) {
+ die("ACCEPT FAILED\n");
+ }
+
+ for (i = 0; i < profile->i; i++) {
+ if ( profile->server_socket[i] == sock ) {
+ if (profile->ip[i].secure) {
+ ptype = PTYPE_CLIENT_SSL;
+ }
+ break;
+ }
+ }
+
+ jsock->local_sock = sock;
+ jsock->profile = profile;
+
+ if (zstr(jsock->name)) {
+ jsock->name = switch_core_sprintf(pool, "%s:%d", inet_ntoa(jsock->remote_addr.sin_addr), ntohs(jsock->remote_addr.sin_port));
+ }
+
+ jsock->ptype = ptype;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Client Connect.\n", jsock->name);
+
+ /* no nagle please */
+ setsockopt(jsock->client_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
+
+ td = switch_core_alloc(jsock->pool, sizeof(*td));
+
+ td->alloc = 0;
+ td->func = client_thread;
+ td->obj = jsock;
+ td->pool = pool;
+
+ switch_mutex_init(&jsock->write_mutex, SWITCH_MUTEX_NESTED, jsock->pool);
+ switch_thread_rwlock_create(&jsock->rwlock, jsock->pool);
+ switch_thread_pool_launch_thread(&td);
+
+ return 0;
+
+ error:
+
+ if (jsock) {
+ if (jsock->client_socket > -1) {
+ close_socket(&jsock->client_socket);
+ }
+
+ switch_core_destroy_memory_pool(&pool);
+ }
+
+ return -1;
+}
+
+static int prepare_socket(int ip, int port)
+{
+ int sock = -1;
+ int reuse_addr = 1;
+ struct sockaddr_in addr;
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ die("Socket Error!\n");
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = ip;
+ addr.sin_port = htons(port);
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ die("Bind Error!\n");
+ return -1;
+ }
+
+ if (listen(sock, MAXPENDING) < 0) {
+ die("Listen error\n");
+ return -1;
+ }
+
+ return sock;
+
+ error:
+
+ close_file(&sock);
+
+ return -1;
+}
+
+static void handle_mcast_sub(verto_profile_t *profile)
+{
+ int bytes = mcast_socket_recv(&profile->mcast_sub, NULL, 0, 0);
+
+ if (bytes > 0) {
+ cJSON *json;
+
+ profile->mcast_sub.buffer[bytes] = '\0';
+
+ if ((json = cJSON_Parse((char *)profile->mcast_sub.buffer))) {
+ jsock_send_event(json);
+ cJSON_Delete(json);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MCAST JSON PARSE ERR: %s\n", (char *)profile->mcast_sub.buffer);
+ }
+
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MCAST INVALID READ %d\n", bytes);
+ }
+
+}
+
+static int runtime(verto_profile_t *profile)
+{
+ int max = 2;
+ int i;
+
+ for (i = 0; i < profile->i; i++) {
+ if ((profile->server_socket[i] = prepare_socket(profile->ip[i].local_ip_addr, profile->ip[i].local_port)) < 0) {
+ die("Client Socket Error!\n");
+ }
+ }
+
+ if (profile->mcast_ip) {
+ if (mcast_socket_create(profile->mcast_ip, profile->mcast_port, &profile->mcast_sub, MCAST_RECV | MCAST_TTL_HOST) < 0) {
+ die("mcast recv socket create");
+ }
+
+ if (mcast_socket_create(profile->mcast_ip, profile->mcast_port + 1, &profile->mcast_pub, MCAST_SEND | MCAST_TTL_HOST) > 0) {
+ mcast_socket_close(&profile->mcast_sub);
+ die("mcast send socket create");
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MCAST Bound to %s:%d/%d\n", profile->mcast_ip, profile->mcast_port, profile->mcast_port + 1);
+ }
+
+
+ while(profile->running) {
+ struct pollfd pfds[MAX_BIND+4];
+ int res, x = 0;
+ int i = 0;
+
+ memset(&pfds[0], 0, sizeof(pfds[0]) * MAX_BIND+2);
+
+ for (i = 0; i < profile->i; i++) {
+ pfds[i].fd = profile->server_socket[i];
+ pfds[i].events = POLLIN|POLLERR;
+ }
+
+ if (profile->mcast_ip) {
+ pfds[i].fd = profile->mcast_sub.sock;
+ pfds[i++].events = POLLIN|POLLERR;
+ }
+
+ max = i;
+
+ if ((res = poll(pfds, max, 1000)) < 0) {
+ if (errno != EINTR) {
+ die("POLL FAILED\n");
+ }
+ }
+
+ if (res == 0) {
+ continue;
+ }
+
+ for (x = 0; x < max; x++) {
+ if (pfds[x].revents & POLLERR) {
+ die("POLL ERROR\n");
+ }
+
+ if (pfds[x].revents & POLLHUP) {
+ die("POLL HUP\n");
+ }
+
+ if (pfds[x].revents & POLLIN) {
+ if (pfds[x].fd == profile->mcast_sub.sock) {
+ handle_mcast_sub(profile);
+ } else {
+ start_jsock(profile, pfds[x].fd);
+ }
+ }
+ }
+ }
+
+ if (profile->mcast_sub.sock > -1) {
+ mcast_socket_close(&profile->mcast_sub);
+ }
+
+ if (profile->mcast_pub.sock > -1) {
+ mcast_socket_close(&profile->mcast_pub);
+ }
+
+ return 0;
+
+ error:
+
+ return -1;
+
+}
+
+static void kill_profile(verto_profile_t *profile)
+{
+ jsock_t *p;
+ int i;
+
+ profile->running = 0;
+
+ //if (switch_thread_rwlock_tryrdlock(profile->rwlock) != SWITCH_STATUS_SUCCESS) {
+ // return;
+ //}
+
+ switch_mutex_lock(profile->mutex);
+ for (i = 0; i < profile->i; i++) {
+ close_socket(&profile->server_socket[i]);
+ }
+
+ for(p = profile->jsock_head; p; p = p->next) {
+ close_socket(&p->client_socket);
+ }
+ switch_mutex_unlock(profile->mutex);
+
+
+
+ //switch_thread_rwlock_unlock(profile->rwlock);
+}
+
+static void kill_profiles(void)
+{
+ verto_profile_t *pp;
+ int sanity = 50;
+
+ switch_mutex_lock(globals.mutex);
+ for(pp = globals.profile_head; pp; pp = pp->next) {
+ kill_profile(pp);
+ }
+ switch_mutex_unlock(globals.mutex);
+
+
+ while(--sanity > 0 && globals.profile_threads > 0) {
+ usleep(100000);
+ }
+}
+
+
+static void do_shutdown(void)
+{
+ globals.running = 0;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down (SIG %d)\n", globals.sig);
+
+ kill_profiles();
+
+ unsub_all_jsock();
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Done\n");
+}
+
+static void parse_ip(char *host, int *port, in_addr_t *addr, char *input)
+{
+ char *p;
+ struct hostent *hent;
+
+ strncpy(host, input, 255);
+
+ if ((p = strchr(host, ':')) != NULL) {
+ *p++ = '\0';
+ *port = atoi(p);
+ }
+
+ if ( host[0] < '0' || host[0] > '9' ) {
+ // Non-numeric host (at least it doesn't start with one). Convert it to ip addr first
+ if ((hent = gethostbyname(host)) != NULL) {
+ if (hent->h_addrtype == AF_INET) {
+ memcpy(addr, hent->h_addr_list[0], 4);
+ }
+ }
+
+ } else {
+ *addr = inet_addr(host);
+ }
+}
+
+static verto_profile_t *find_profile(const char *name)
+{
+ verto_profile_t *p, *r = NULL;
+ switch_mutex_lock(globals.mutex);
+ for(p = globals.profile_head; p; p = p->next) {
+ if (!strcmp(name, p->name)) {
+ r = p;
+ break;
+ }
+ }
+
+ if (!r->in_thread || !r->running) {
+ r = NULL;
+ }
+
+ if (switch_thread_rwlock_tryrdlock(r->rwlock) != SWITCH_STATUS_SUCCESS) {
+ r = NULL;
+ }
+ switch_mutex_unlock(globals.mutex);
+
+ return r;
+}
+
+static switch_bool_t profile_exists(const char *name)
+{
+ switch_bool_t r = SWITCH_FALSE;
+ verto_profile_t *p;
+
+ switch_mutex_lock(globals.mutex);
+ for(p = globals.profile_head; p; p = p->next) {
+ if (!strcmp(p->name, name)) {
+ r = SWITCH_TRUE;
+ break;
+ }
+ }
+ switch_mutex_unlock(globals.mutex);
+
+ return r;
+}
+
+static void del_profile(verto_profile_t *profile)
+{
+ verto_profile_t *p, *last = NULL;
+
+ switch_mutex_lock(globals.mutex);
+ for(p = globals.profile_head; p; p = p->next) {
+ if (p == profile) {
+ if (last) {
+ last->next = p->next;
+ } else {
+ globals.profile_head = p->next;
+ }
+ globals.profile_count--;
+ break;
+ }
+
+ last = p;
+ }
+ switch_mutex_unlock(globals.mutex);
+}
+
+static switch_status_t add_profile(verto_profile_t *profile)
+{
+ switch_status_t status = SWITCH_STATUS_FALSE;
+
+ switch_mutex_lock(globals.mutex);
+
+ if (!profile_exists(profile->name)) {
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ profile->next = globals.profile_head;
+ globals.profile_head = profile;
+ globals.profile_count++;
+ }
+
+ switch_mutex_unlock(globals.mutex);
+
+ return status;
+}
+
+static switch_status_t parse_config(const char *cf)
+{
+
+ switch_xml_t cfg, xml, settings, param, xprofile, xprofiles;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ if ((xprofiles = switch_xml_child(cfg, "profiles"))) {
+ for (xprofile = switch_xml_child(xprofiles, "profile"); xprofile; xprofile = xprofile->next) {
+ verto_profile_t *profile;
+ switch_memory_pool_t *pool;
+ const char *name = switch_xml_attr(xprofile, "name");
+
+ if (zstr(name)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Required field name missing\n");
+ continue;
+ }
+
+ if (profile_exists(name)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile %s already exists\n", name);
+ continue;
+ }
+
+
+ switch_core_new_memory_pool(&pool);
+ profile = switch_core_alloc(pool, sizeof(*profile));
+ profile->pool = pool;
+ profile->name = switch_core_strdup(profile->pool, name);
+ switch_mutex_init(&profile->mutex, SWITCH_MUTEX_NESTED, profile->pool);
+ switch_thread_rwlock_create(&profile->rwlock, profile->pool);
+ add_profile(profile);
+
+ profile->local_network = "localnet.auto";
+
+ for (param = switch_xml_child(xprofile, "param"); param; param = param->next) {
+ char *var = NULL;
+ char *val = NULL;
+ int i = 0;
+
+ var = (char *) switch_xml_attr_soft(param, "name");
+ val = (char *) switch_xml_attr_soft(param, "value");
+
+ if (!strcasecmp(var, "bind-local")) {
+ const char *secure = switch_xml_attr_soft(param, "secure");
+ if (i < MAX_BIND) {
+ parse_ip(profile->ip[profile->i].local_ip, &profile->ip[profile->i].local_port, &profile->ip[profile->i].local_ip_addr, val);
+ if (switch_true(secure)) {
+ profile->ip[profile->i].secure = 1;
+ }
+ profile->i++;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max Bindings Reached!\n");
+ }
+ } else if (!strcasecmp(var, "secure-combined")) {
+ set_string(profile->cert, val);
+ set_string(profile->key, val);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Secure key and cert specified\n");
+ } else if (!strcasecmp(var, "secure-cert")) {
+ set_string(profile->cert, val);
+ } else if (!strcasecmp(var, "secure-key")) {
+ set_string(profile->key, val);
+ } else if (!strcasecmp(var, "secure-chain")) {
+ set_string(profile->chain, val);
+ } else if (!strcasecmp(var, "userauth") && !zstr(val)) {
+ profile->userauth = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "root-password") && !zstr(val)) {
+ profile->root_passwd = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "context") && !zstr(val)) {
+ profile->context = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "dialplan") && !zstr(val)) {
+ profile->dialplan = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "mcast-ip") && val) {
+ profile->mcast_ip = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "mcast-port") && val) {
+ profile->mcast_port = (switch_port_t) atoi(val);
+ } else if (!strcasecmp(var, "timer-name") && !zstr(var)) {
+ profile->timer_name = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "local-network") && !zstr(val)) {
+ profile->local_network = switch_core_strdup(profile->pool, val);
+ } else if (!strcasecmp(var, "apply-candidate-acl")) {
+ if (profile->cand_acl_count < SWITCH_MAX_CAND_ACL) {
+ profile->cand_acl[profile->cand_acl_count++] = switch_core_strdup(profile->pool, val);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SWITCH_MAX_CAND_ACL);
+ }
+ } else if (!strcasecmp(var, "rtp-ip")) {
+ if (zstr(val)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid RTP IP.\n");
+ } else {
+ if (profile->rtpip_index < MAX_RTPIP -1) {
+ profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, val);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Too many RTP IP.\n");
+ }
+ }
+ } else if (!strcasecmp(var, "ext-rtp-ip")) {
+ if (zstr(val)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid External RTP IP.\n");
+ } else {
+ profile->extrtpip = switch_core_strdup(profile->pool, val);
+ }
+ } else if (!strcasecmp(var, "debug")) {
+ if (val) {
+ profile->debug = atoi(val);
+ }
+ }
+ }
+
+ if (zstr(profile->outbound_codec_string)) {
+ profile->outbound_codec_string = "opus,vp8";
+ }
+
+ if (zstr(profile->inbound_codec_string)) {
+ profile->outbound_codec_string = profile->outbound_codec_string;
+ }
+
+ if (zstr(profile->timer_name)) {
+ profile->timer_name = "soft";
+ }
+
+ if (zstr(profile->dialplan)) {
+ profile->dialplan = "XML";
+ }
+
+ if (zstr(profile->context)) {
+ profile->context = "default";
+ }
+
+ if (zstr(profile->ip[0].local_ip) ) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: local_ip bad\n", profile->name);
+ if (profile->ip[0].local_port <= 0 ) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: local_port bad\n", profile->name);
+
+ if (zstr(profile->ip[0].local_ip) || profile->ip[0].local_port <= 0) {
+ del_profile(profile);
+ switch_core_destroy_memory_pool(&pool);
+ } else {
+ int i;
+
+ for (i = 0; i < profile->i; i++) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Bound to %s:%d\n",
+ profile->name, profile->ip[i].local_ip, profile->ip[i].local_port);
+ }
+ }
+ }
+ }
+
+ if ((settings = switch_xml_child(cfg, "settings"))) {
+ for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+ char *var = NULL;
+ char *val = NULL;
+
+ var = (char *) switch_xml_attr_soft(param, "name");
+ val = (char *) switch_xml_attr_soft(param, "value");
+
+
+ if (!strcasecmp(var, "debug")) {
+ if (val) {
+ globals.debug = atoi(val);
+ }
+ } else if (!strcasecmp(var, "enable-presence") && val) {
+ globals.enable_presence = switch_true(val);
+ } else if (!strcasecmp(var, "detach-timeout-sec") && val) {
+ int tmp = atoi(val);
+ if (tmp > 0) {
+ globals.detach_timeout = tmp;
+ }
+ }
+ }
+ }
+
+ switch_xml_free(xml);
+
+ return status;
+}
+
+static int init(void)
+{
+ verto_profile_t *p;
+
+ parse_config("verto.conf");
+
+ switch_mutex_lock(globals.mutex);
+ for(p = globals.profile_head; p; p = p->next) {
+ verto_init_ssl(p);
+ }
+ switch_mutex_unlock(globals.mutex);
+
+ globals.running = 1;
+
+ return 0;
+}
+
+
+#if 0
+static void print_status(verto_profile_t *profile, switch_stream_handle_t *stream)
+{
+ jsock_t *p;
+
+ stream->write_function(stream, "REMOTE\t\t\tLOCAL\n");
+
+ for(p = profile->jsock_head; p; p = p->next) {
+ if (p->ptype & PTYPE_CLIENT) {
+ int i;
+
+ for (i = 0; i < profile->i; i++) {
+ if (profile->server_socket[i] == p->local_sock) {
+ stream->write_function(stream, "%s\t%s:%d\n", p->name, profile->ip[i].local_ip, profile->ip[i].local_port);
+ }
+ }
+ }
+ }
+}
+#endif
+
+SWITCH_STANDARD_API(verto_function)
+{
+
+ int argc = 0;
+ char *argv[5] = { 0 };
+ char *mydata = NULL;
+
+ if (cmd) {
+ mydata = strdup(cmd);
+ argc = switch_split(mydata, ' ', argv);
+ }
+
+ if (argc > 0) {
+ if (!strcasecmp(argv[0], "connections")) {
+ //print_status(profile, stream);
+ }
+ }
+
+ switch_safe_free(mydata);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void *SWITCH_THREAD_FUNC profile_thread(switch_thread_t *thread, void *obj)
+{
+ verto_profile_t *profile = (verto_profile_t *) obj;
+ int sanity = 50;
+
+ switch_mutex_lock(globals.mutex);
+ globals.profile_threads++;
+ switch_mutex_unlock(globals.mutex);
+
+ profile->in_thread = 1;
+ profile->running = 1;
+
+
+ runtime(profile);
+ profile->running = 0;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "profile %s shutdown, Waiting for %d threads\n", profile->name, profile->jsock_count);
+
+ while(--sanity > 0 && profile->jsock_count > 0) {
+ usleep(100000);
+ }
+
+ verto_deinit_ssl(profile);
+
+ del_profile(profile);
+
+ switch_thread_rwlock_wrlock(profile->rwlock);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Thread ending\n", profile->name);
+ switch_thread_rwlock_unlock(profile->rwlock);
+ profile->in_thread = 0;
+
+ switch_mutex_lock(globals.mutex);
+ globals.profile_threads--;
+ switch_mutex_unlock(globals.mutex);
+
+ return NULL;
+
+}
+
+static void run_profile_thread(verto_profile_t *profile) {
+ switch_thread_data_t *td;
+
+ td = switch_core_alloc(profile->pool, sizeof(*td));
+
+ td->alloc = 0;
+ td->func = profile_thread;
+ td->obj = profile;
+ td->pool = profile->pool;
+
+ switch_thread_pool_launch_thread(&td);
+}
+
+static void run_profiles(void)
+{
+ verto_profile_t *p;
+
+ switch_mutex_lock(globals.mutex);
+ for(p = globals.profile_head; p; p = p->next) {
+ if (!p->in_thread) {
+ run_profile_thread(p);
+ }
+ }
+ switch_mutex_unlock(globals.mutex);
+
+}
+
+
+//// ENDPOINT
+
+switch_endpoint_interface_t *verto_endpoint_interface;
+static switch_call_cause_t verto_outgoing_channel(switch_core_session_t *session,
+ switch_event_t *var_event,
+ switch_caller_profile_t *outbound_profile,
+ switch_core_session_t **new_session,
+ switch_memory_pool_t **pool,
+ switch_originate_flag_t flags,
+ switch_call_cause_t *cancel_cause);
+switch_io_routines_t verto_io_routines = {
+ /*.outgoing_channel */ verto_outgoing_channel
+};
+
+static char *verto_get_dial_string(const char *uid, switch_stream_handle_t *rstream)
+{
+ jsock_t *jsock;
+ verto_profile_t *profile;
+ switch_stream_handle_t *use_stream = NULL, stream = { 0 };
+ char *gen_uid = NULL;
+ int hits = 0;
+
+ if (!strchr(uid, '@')) {
+ gen_uid = switch_mprintf("%s@%s", uid, switch_core_get_domain(SWITCH_FALSE));
+ uid = gen_uid;
+ }
+
+ if (rstream) {
+ use_stream = rstream;
+ } else {
+ SWITCH_STANDARD_STREAM(stream);
+ use_stream = &stream;
+ }
+
+ switch_mutex_lock(globals.mutex);
+ for(profile = globals.profile_head; profile; profile = profile->next) {
+
+ switch_mutex_lock(profile->mutex);
+
+ for(jsock = profile->jsock_head; jsock; jsock = jsock->next) {
+ if (!strcmp(uid, jsock->uid)) {
+ use_stream->write_function(use_stream, "%s/u:%s,", EP_NAME, jsock->uuid_str);
+ hits++;
+ }
+ }
+
+ switch_mutex_unlock(profile->mutex);
+ }
+ switch_mutex_unlock(globals.mutex);
+
+ switch_safe_free(gen_uid);
+
+ if (!hits) {
+ use_stream->write_function(use_stream, "error/user_not_registered");
+ }
+
+ if (use_stream->data) {
+ char *p = use_stream->data;
+ if (end_of(p) == ',') {
+ end_of(p) = '\0';
+ }
+ }
+
+ return use_stream->data;
+}
+
+SWITCH_STANDARD_API(verto_contact_function)
+{
+ char *uid = (char *) cmd;
+
+ if (!zstr(uid)) {
+ verto_get_dial_string(uid, stream);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_call_cause_t verto_outgoing_channel(switch_core_session_t *session,
+ switch_event_t *var_event,
+ switch_caller_profile_t *outbound_profile,
+ switch_core_session_t **new_session,
+ switch_memory_pool_t **pool,
+ switch_originate_flag_t flags,
+ switch_call_cause_t *cancel_cause)
+{
+ switch_call_cause_t cause = SWITCH_CAUSE_CHANNEL_UNACCEPTABLE;
+ char *dest = NULL;
+
+ if (!zstr(outbound_profile->destination_number)) {
+ dest = strdup(outbound_profile->destination_number);
+ }
+
+ if (zstr(dest)) {
+ goto end;
+ }
+
+ if (!switch_stristr("u:", dest)) {
+ char *dial_str = verto_get_dial_string(dest, NULL);
+
+ cause = SWITCH_CAUSE_USER_NOT_REGISTERED;
+
+ if (dial_str) {
+ switch_originate_flag_t myflags = SOF_NONE;
+
+ if ((flags & SOF_NO_LIMITS)) {
+ myflags |= SOF_NO_LIMITS;
+ }
+
+ if ((flags & SOF_FORKED_DIAL)) {
+ myflags |= SOF_NOBLOCK;
+ }
+
+ if (switch_ivr_originate(session, new_session, &cause, dial_str, 0, NULL,
+ NULL, NULL, outbound_profile, var_event, myflags, cancel_cause) == SWITCH_STATUS_SUCCESS) {
+ switch_core_session_rwunlock(*new_session);
+ }
+
+ free(dial_str);
+ }
+
+ return cause;
+ }
+
+ if ((cause = switch_core_session_outgoing_channel(session, var_event, "rtc",
+ outbound_profile, new_session, NULL, SOF_NONE, cancel_cause)) == SWITCH_CAUSE_SUCCESS) {
+ switch_channel_t *channel = switch_core_session_get_channel(*new_session);
+ char *jsock_uuid_str = outbound_profile->destination_number + 2;
+ switch_caller_profile_t *caller_profile;
+ verto_pvt_t *tech_pvt = NULL;
+ char name[512];
+
+ tech_pvt = switch_core_session_alloc(*new_session, sizeof(*tech_pvt));
+ tech_pvt->session = *new_session;
+ tech_pvt->channel = channel;
+ tech_pvt->jsock_uuid = switch_core_session_strdup(*new_session, jsock_uuid_str);
+ switch_core_session_set_private_class(*new_session, tech_pvt, SWITCH_PVT_SECONDARY);
+
+ if (session) {
+ switch_channel_t *ochannel = switch_core_session_get_channel(session);
+ if (switch_true(switch_channel_get_variable(ochannel, SWITCH_BYPASS_MEDIA_VARIABLE))) {
+ switch_channel_set_flag(channel, CF_PROXY_MODE);
+ switch_channel_set_flag(ochannel, CF_PROXY_MODE);
+ switch_channel_set_cap(channel, CC_BYPASS_MEDIA);
+ }
+ }
+
+ tech_pvt->call_id = switch_core_session_strdup(*new_session, switch_core_session_get_uuid(*new_session));
+ if ((tech_pvt->smh = switch_core_session_get_media_handle(*new_session))) {
+ tech_pvt->mparams = switch_core_media_get_mparams(tech_pvt->smh);
+ }
+
+ switch_snprintf(name, sizeof(name), "verto.rtc/%s", tech_pvt->jsock_uuid);
+ switch_channel_set_name(channel, name);
+ switch_channel_set_variable(channel, "jsock_uuid_str", tech_pvt->jsock_uuid);
+ switch_channel_set_variable(channel, "event_channel_cookie", tech_pvt->jsock_uuid);
+
+ if ((caller_profile = switch_caller_profile_dup(switch_core_session_get_pool(*new_session), outbound_profile))) {
+ switch_channel_set_caller_profile(channel, caller_profile);
+ }
+
+ switch_channel_add_state_handler(channel, &verto_state_handlers);
+ switch_core_event_hook_add_receive_message(*new_session, messagehook);
+ switch_channel_set_state(channel, CS_INIT);
+ track_pvt(tech_pvt);
+ }
+
+ end:
+
+ switch_safe_free(dest);
+
+ return cause;
+}
+
+void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id)
+{
+
+ {
+ char *json_text;
+ if ((json_text = cJSON_Print(json))) {
+ if (globals.debug) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "EVENT BROADCAST %s %s\n", event_channel, json_text);
+ }
+ free(json_text);
+ }
+ }
+
+
+
+
+ jsock_send_event(json);
+}
+
+
+static int verto_send_chat(const char *uid, const char *call_id, cJSON *msg)
+{
+ jsock_t *jsock;
+ verto_profile_t *profile;
+ int hits = 0;
+ int done = 0;
+
+ if (!strchr(uid, '@')) {
+ return 0;
+ }
+
+ if (call_id) {
+ switch_core_session_t *session;
+ if ((session = switch_core_session_locate(call_id))) {
+ verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+ jsock_t *jsock;
+
+ if ((jsock = get_jsock(tech_pvt->jsock_uuid))) {
+ ws_write_json(jsock, &msg, SWITCH_FALSE);
+ switch_thread_rwlock_unlock(jsock->rwlock);
+ done = 1;
+ }
+
+ switch_core_session_rwunlock(session);
+ }
+ }
+
+ if (done) {
+ return 1;
+ }
+
+ switch_mutex_lock(globals.mutex);
+ for(profile = globals.profile_head; profile; profile = profile->next) {
+
+ switch_mutex_lock(profile->mutex);
+
+ for(jsock = profile->jsock_head; jsock; jsock = jsock->next) {
+ if (!strcmp(uid, jsock->uid)) {
+ ws_write_json(jsock, &msg, SWITCH_FALSE);
+ hits++;
+ }
+ }
+
+ switch_mutex_unlock(profile->mutex);
+ }
+ switch_mutex_unlock(globals.mutex);
+
+ return hits;
+}
+
+static switch_status_t chat_send(switch_event_t *message_event)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ const char *to = switch_event_get_header(message_event, "to");
+ const char *from = switch_event_get_header(message_event, "from");
+ const char *body = switch_event_get_body(message_event);
+ const char *call_id = switch_event_get_header(message_event, "call_id");
+
+ DUMP_EVENT(message_event);
+
+
+ if (!zstr(to) && !zstr(body) && !zstr(from)) {
+ cJSON *obj = NULL, *msg = NULL, *params = NULL;
+
+ obj = jrpc_new_req("verto.info", call_id, ¶ms);
+ msg = json_add_child_obj(params, "msg", NULL);
+
+ cJSON_AddItemToObject(msg, "from", cJSON_CreateString(from));
+ cJSON_AddItemToObject(msg, "to", cJSON_CreateString(to));
+ cJSON_AddItemToObject(msg, "body", cJSON_CreateString(body));
+ verto_send_chat(to, call_id, obj);
+ cJSON_Delete(obj);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "INVALID EVENT\n");
+ status = SWITCH_STATUS_FALSE;
+ }
+
+
+ return status;
+}
+
+
+
+static switch_cache_db_handle_t *json_get_db_handle(void)
+{
+
+ switch_cache_db_handle_t *dbh = NULL;
+ const char *dsn;
+
+
+ if (!(dsn = switch_core_get_variable("json_db_handle"))) {
+ dsn = "json";
+ }
+
+
+ if (switch_cache_db_get_db_handle_dsn(&dbh, dsn) != SWITCH_STATUS_SUCCESS) {
+ dbh = NULL;
+ }
+
+ return dbh;
+}
+
+
+static int jcallback(void *pArg, int argc, char **argv, char **columnNames)
+{
+ char **data = (char **) pArg;
+
+ if (argv[0] && !*data) {
+ *data = strdup(argv[0]);
+ }
+
+ return 0;
+}
+
+static cJSON *json_retrieve(const char *name, switch_mutex_t *mutex)
+{
+ char *sql, *errmsg;
+ switch_cache_db_handle_t *dbh;
+ char *ascii = NULL;
+ cJSON *json = NULL;
+
+ if (!check_name(name)) {
+ return NULL;
+ }
+
+ sql = switch_mprintf("select data from json_store where name='%q'", name);
+
+ dbh = json_get_db_handle();
+
+ if (mutex) switch_mutex_lock(mutex);
+ switch_cache_db_execute_sql_callback(dbh, sql, jcallback, &ascii, &errmsg);
+
+ switch_cache_db_release_db_handle(&dbh);
+
+ if (errmsg) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
+ free(errmsg);
+ } else {
+ if (ascii) {
+ json = cJSON_Parse(ascii);
+ }
+ }
+
+ if (mutex) switch_mutex_unlock(mutex);
+
+
+ switch_safe_free(ascii);
+
+ return json;
+
+}
+
+static switch_bool_t json_commit(cJSON *json, const char *name, switch_mutex_t *mutex)
+{
+ char *ascii = cJSON_PrintUnformatted(json);
+ char *sql;
+ char del_sql[128] = "";
+ switch_cache_db_handle_t *dbh;
+ char *err;
+
+ if (!check_name(name)) {
+ return SWITCH_FALSE;
+ }
+
+ if (!(ascii = cJSON_PrintUnformatted(json))) {
+ return SWITCH_FALSE;
+ }
+
+
+ sql = switch_mprintf("insert into json_store (name,data) values('%q','%q')", name, ascii);
+ switch_snprintf(del_sql, sizeof(del_sql), "delete from json_store where name='%q'", name);
+
+ dbh = json_get_db_handle();
+
+
+ if (mutex) switch_mutex_lock(mutex);
+ switch_cache_db_execute_sql(dbh, del_sql, &err);
+
+ if (err) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql err [%s]\n", err);
+ free(err);
+ } else {
+ switch_cache_db_execute_sql(dbh, sql, &err);
+
+ if (err) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql err [%s]\n", err);
+ free(err);
+ }
+ }
+
+ if (mutex) switch_mutex_unlock(mutex);
+
+ switch_safe_free(sql);
+ switch_safe_free(ascii);
+
+ switch_cache_db_release_db_handle(&dbh);
+
+ return SWITCH_TRUE;
+}
+
+static switch_status_t json_hanguphook(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_channel_state_t state = switch_channel_get_state(channel);
+ json_store_t *session_store = NULL;
+ char *ascii = NULL;
+
+ if (state == CS_HANGUP) {
+ if ((session_store = (json_store_t *) switch_channel_get_private(channel, "_json_store_"))) {
+ if ((ascii = cJSON_PrintUnformatted(session_store->JSON_STORE))) {
+ switch_channel_set_variable(channel, "json_store_data", ascii);
+ free(ascii);
+ }
+ cJSON_Delete(session_store->JSON_STORE);
+ session_store->JSON_STORE = NULL;
+ switch_channel_set_private(channel, "_json_store_", NULL);
+ }
+ switch_core_event_hook_remove_state_change(session, json_hanguphook);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_STANDARD_JSON_API(json_store_function)
+{
+ cJSON *JSON_STORE, *reply = NULL, *data = cJSON_GetObjectItem(json, "data");
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ const char *cmd_attr = cJSON_GetObjectCstr(data, "cmd");
+ const char *uuid = cJSON_GetObjectCstr(data, "uuid");
+ const char *error = NULL, *message = NULL;
+ store_cmd_t cmd;
+ const char *key = cJSON_GetObjectCstr(data, "key");
+ const char *verbose = cJSON_GetObjectCstr(data, "verbose");
+ const char *commit = cJSON_GetObjectCstr(data, "commit");
+ const char *file = cJSON_GetObjectCstr(data, "file");
+ const char *storename = cJSON_GetObjectCstr(data, "storeName");
+ cJSON *obj, **use_store = NULL;
+ switch_core_session_t *tsession = NULL;
+ switch_channel_t *tchannel = NULL;
+ json_store_t *session_store = NULL;
+
+ reply = cJSON_CreateObject();
+
+ if (uuid) {
+ if ((tsession = switch_core_session_locate(uuid))) {
+ tchannel = switch_core_session_get_channel(tsession);
+ } else {
+ error = "Invalid INPUT, Missing UUID";
+ goto end;
+ }
+ } else {
+ if (zstr(storename)) {
+ storename = "global";
+ }
+ }
+
+
+ if (zstr(cmd_attr)) {
+ error = "INVALID INPUT, Command not supplied";
+ goto end;
+ }
+
+
+ if (!strcasecmp(cmd_attr, "add")) {
+ cmd = CMD_ADD;
+ } else if (!strcasecmp(cmd_attr, "del")) {
+ cmd = CMD_DEL;
+ } else if (!strcasecmp(cmd_attr, "dump")) {
+ cmd = CMD_DUMP;
+ } else if (!strcasecmp(cmd_attr, "commit")) {
+ cmd = CMD_COMMIT;
+ } else if (!strcasecmp(cmd_attr, "retrieve")) {
+ cmd = CMD_RETRIEVE;
+ } else {
+ error = "INVALID INPUT, Unknown Command";
+ goto end;
+ }
+
+
+ if (cmd == CMD_ADD) {
+ if (zstr(key)) {
+ error = "INVALID INPUT, No key supplied";
+ goto end;
+ }
+ }
+
+
+ if (cmd == CMD_RETRIEVE || cmd == CMD_COMMIT) {
+ if (zstr(file)) {
+ error = "INVALID INPUT, No file specified";
+ goto end;
+ }
+ }
+
+ switch_mutex_lock(json_GLOBALS.store_mutex);
+ if (tsession) {
+ if (!(session_store = (json_store_t *) switch_channel_get_private(tchannel, "_json_store_"))) {
+ session_store = switch_core_session_alloc(tsession, sizeof(*session_store));
+ switch_mutex_init(&session_store->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(tsession));
+ session_store->JSON_STORE = cJSON_CreateObject();
+ switch_channel_set_private(tchannel, "_json_store_", session_store);
+ switch_core_event_hook_add_state_change(tsession, json_hanguphook);
+ }
+
+ use_store = &session_store->JSON_STORE;
+ switch_mutex_lock(session_store->mutex);
+ switch_mutex_unlock(json_GLOBALS.store_mutex);
+ } else {
+ JSON_STORE = switch_core_hash_find(json_GLOBALS.store_hash, storename);
+
+ if (!JSON_STORE) {
+ JSON_STORE = cJSON_CreateObject();
+ switch_core_hash_insert(json_GLOBALS.store_hash, storename, JSON_STORE);
+ }
+ use_store = &JSON_STORE;
+ }
+
+ switch(cmd) {
+ case CMD_RETRIEVE:
+ obj = json_retrieve(file, NULL);
+
+ if (!obj) {
+ error = "CANNOT LOAD DATA";
+
+ if (session_store) {
+ switch_mutex_unlock(session_store->mutex);
+ } else {
+ switch_mutex_unlock(json_GLOBALS.store_mutex);
+ }
+
+ goto end;
+ }
+
+ cJSON_Delete(*use_store);
+ *use_store = obj;
+ message = "Store Loaded";
+
+ break;
+ case CMD_ADD:
+
+ if (!(obj = cJSON_GetObjectItem(data, key))) {
+ error = "INVALID INPUT";
+
+ if (session_store) {
+ switch_mutex_unlock(session_store->mutex);
+ } else {
+ switch_mutex_unlock(json_GLOBALS.store_mutex);
+ }
+
+ goto end;
+ }
+
+ cJSON_DeleteItemFromObject(*use_store, key);
+ obj = cJSON_Duplicate(obj, 1);
+ cJSON_AddItemToObject(*use_store, key, obj);
+ message = "Item Added";
+ break;
+
+ case CMD_DEL:
+
+ if (!key) {
+ cJSON_Delete(*use_store);
+ *use_store = cJSON_CreateObject();
+ message = "Store Deleted";
+ } else {
+ cJSON_DeleteItemFromObject(*use_store, key);
+ message = "Item Deleted";
+ }
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (switch_true(verbose) || cmd == CMD_DUMP) {
+ cJSON *dump;
+
+ if (key) {
+ dump = cJSON_GetObjectItem(*use_store, key);
+ } else {
+ dump = *use_store;
+ }
+
+ if (dump) {
+ dump = cJSON_Duplicate(dump, 1);
+ cJSON_AddItemToObject(reply, "data", dump);
+ message = "Data Dumped";
+ } else {
+ error = "Key not found";
+ }
+ }
+
+ if (session_store) {
+ switch_mutex_unlock(session_store->mutex);
+ } else {
+ switch_mutex_unlock(json_GLOBALS.store_mutex);
+ }
+
+ if (cmd == CMD_COMMIT || commit) {
+ switch_bool_t ok;
+
+ if (commit && zstr(file)) {
+ file = commit;
+ }
+
+ if (session_store) {
+ ok = json_commit(session_store->JSON_STORE, file, session_store->mutex);
+ } else {
+ ok = json_commit(JSON_STORE, file, json_GLOBALS.store_mutex);
+ }
+
+ cJSON_AddItemToObject(reply, "commitStatus", cJSON_CreateString(ok ? "success" : "fail"));
+ if (!message) {
+ message = "Message Comitted";
+ }
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+
+ end:
+
+ if (!zstr(error)) {
+ cJSON_AddItemToObject(reply, "errorMessage", cJSON_CreateString(error));
+ }
+
+ if (!zstr(message)) {
+ cJSON_AddItemToObject(reply, "message", cJSON_CreateString(message));
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ *json_reply = reply;
+
+ if (tsession) {
+ switch_core_session_rwunlock(tsession);
+ }
+
+ return status;
+}
+
+#define add_it(_name, _ename) if ((tmp = switch_event_get_header(event, _ename))) { cJSON_AddItemToObject(data, _name, cJSON_CreateString(tmp));}
+
+static void presence_event_handler(switch_event_t *event)
+{
+ cJSON *msg = NULL, *data = NULL;
+ const char *tmp;
+ switch_event_header_t *hp;
+ char *event_channel;
+ const char *presence_id = switch_event_get_header(event, "channel-presence-id");
+
+ if (!globals.enable_presence || zstr(presence_id)) {
+ return;
+ }
+
+ msg = cJSON_CreateObject();
+ data = json_add_child_obj(msg, "data", NULL);
+
+ event_channel = switch_mprintf("presence.%s", presence_id);
+
+ cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(event_channel));
+ add_it("channelCallState", "channel-call-state");
+ add_it("originalChannelCallState", "original-channel-call-state");
+ add_it("channelState", "channel-state");
+
+ add_it("callerUserName", "caller-username");
+ add_it("callerIDName", "caller-caller-id-name");
+ add_it("callerIDNumber", "caller-caller-id-number");
+ add_it("calleeIDName", "caller-callee-id-name");
+ add_it("calleeIDNumber", "caller-callee-id-number");
+ add_it("channelUUID", "unique-id");
+
+ add_it("presenceCallDirection", "presence-call-direction");
+ add_it("channelPresenceID", "channel-presence-id");
+ add_it("channelPresenceData", "channel-presence-data");
+
+ for(hp = event->headers; hp; hp = hp->next) {
+ if (!strncasecmp(hp->name, "PD-", 3)) {
+ add_it(hp->name, hp->name);
+ }
+ }
+
+ switch_event_channel_broadcast(event_channel, &msg, __FILE__, NO_EVENT_CHANNEL_ID);
+
+ free(event_channel);
+
+}
+
+
+/* Macro expands to: switch_status_t mod_verto_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
+SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load)
+{
+ switch_api_interface_t *api_interface = NULL;
+ switch_chat_interface_t *chat_interface = NULL;
+ switch_json_api_interface_t *json_api_interface = NULL;
+ int r;
+ switch_cache_db_handle_t *dbh;
+ //switch_application_interface_t *app_interface = NULL;
+
+ memset(&globals, 0, sizeof(globals));
+ globals.pool = pool;
+ globals.ready = SIGUSR1;
+ globals.enable_presence = SWITCH_TRUE;
+
+ switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+
+ switch_mutex_init(&globals.method_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_core_hash_init(&globals.method_hash);
+
+ switch_thread_rwlock_create(&globals.event_channel_rwlock, globals.pool);
+ switch_core_hash_init(&globals.event_channel_hash);
+
+ switch_mutex_init(&globals.jsock_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_core_hash_init(&globals.jsock_hash);
+
+ switch_thread_rwlock_create(&globals.tech_rwlock, globals.pool);
+
+ switch_mutex_init(&globals.detach_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_mutex_init(&globals.detach2_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_thread_cond_create(&globals.detach_cond, globals.pool);
+ globals.detach_timeout = 120;
+
+
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, presence_event_handler, NULL);
+
+
+
+ memset(&json_GLOBALS, 0, sizeof(json_GLOBALS));
+ switch_mutex_init(&json_GLOBALS.store_mutex, SWITCH_MUTEX_NESTED, pool);
+ switch_core_hash_init(&json_GLOBALS.store_hash);
+
+
+ dbh = json_get_db_handle();
+ switch_cache_db_test_reactive(dbh, "select name from json_store where name=''", "drop table json_store", json_sql);
+ switch_cache_db_release_db_handle(&dbh);
+
+
+
+ switch_event_channel_bind(SWITCH_EVENT_CHANNEL_GLOBAL, verto_broadcast, &globals.event_channel_id);
+
+
+ r = init();
+
+ if (r) return SWITCH_STATUS_TERM;
+
+ jrpc_init();
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ SWITCH_ADD_API(api_interface, "verto", "Verto API", verto_function, "syntax");
+ SWITCH_ADD_API(api_interface, "verto_contact", "Generate a verto endpoint dialstring", verto_contact_function, "user@domain");
+ SWITCH_ADD_JSON_API(json_api_interface, "store", "JSON store", json_store_function, "");
+
+ verto_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
+ verto_endpoint_interface->interface_name = EP_NAME;
+ verto_endpoint_interface->io_routines = &verto_io_routines;
+
+ SWITCH_ADD_CHAT(chat_interface, VERTO_CHAT_PROTO, chat_send);
+
+ switch_core_register_secondary_recover_callback(modname, verto_recover_callback);
+
+ run_profiles();
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/*
+ Called when the system shuts down
+ Macro expands to: switch_status_t mod_verto_shutdown() */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown)
+{
+ json_cleanup();
+ switch_core_hash_destroy(&json_GLOBALS.store_hash);
+
+ switch_event_channel_unbind(NULL, verto_broadcast);
+ switch_event_unbind_callback(presence_event_handler);
+
+ switch_core_unregister_secondary_recover_callback(modname);
+ do_shutdown();
+ attach_wake();
+ attach_wake();
+
+ switch_core_hash_destroy(&globals.method_hash);
+ switch_core_hash_destroy(&globals.event_channel_hash);
+ switch_core_hash_destroy(&globals.jsock_hash);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime)
+{
+ switch_mutex_lock(globals.detach_mutex);
+
+ while(globals.running) {
+ if (globals.detached) {
+ drop_detached();
+ switch_yield(1000000);
+ } else {
+ switch_mutex_lock(globals.detach2_mutex);
+ if (globals.running) {
+ switch_thread_cond_wait(globals.detach_cond, globals.detach_mutex);
+ }
+ switch_mutex_unlock(globals.detach2_mutex);
+ }
+ }
+
+ switch_mutex_unlock(globals.detach_mutex);
+
+ return SWITCH_STATUS_TERM;
+}
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
+ */