├── freeswitchESL ├── __init__.py └── ESL.py ├── swig └── ESL.i ├── setup.py ├── README.md ├── include ├── esl_threadmutex.h ├── esl_oop.h ├── esl_config.h ├── esl_buffer.h ├── esl_json.h ├── esl_event.h └── esl.h └── src ├── esl_threadmutex.c ├── esl_config.c ├── esl_buffer.c ├── esl_oop.cpp ├── esl_json.c ├── esl_event.c └── esl.c /freeswitchESL/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /swig/ESL.i: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "esl.h" 3 | #include "esl_oop.h" 4 | %} 5 | 6 | %include "esl_oop.h" 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | import os 3 | 4 | setup_path = __file__.split('/') 5 | pkg_dir = '/'.join(setup_path[:-1]) + '/' 6 | 7 | setup (name = 'FreeSWITCH-ESL-Python', 8 | version = '1.2', 9 | ext_modules=[Extension('_ESL', 10 | sources=[ 11 | 'swig/esl_wrap.cpp', 12 | 'src/esl.c', 13 | 'src/esl_json.c', 14 | 'src/esl_event.c', 15 | 'src/esl_threadmutex.c', 16 | 'src/esl_config.c', 17 | 'src/esl_oop.cpp', 18 | 'src/esl_buffer.c'])], 19 | include_dirs=[os.path.join(pkg_dir, 'include/')], 20 | packages = ['freeswitchESL'], 21 | pymodules = ['ESL'], 22 | description = 'Standalone FreeSWITCH ESL Python module.',) 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeSWITCH ESL Python 2 | 3 | This is a Python package to distribute the ESL.py module generated by FreeSWITCH. 4 | 5 | It's an auto-generated SWIG module and the idea is to make it easy to use the FreeSWITCH Event Socket Library without having to install it in the same box. This way you can have different servers, one running FreeSWITCH and another one running your business logic or you can also distribute your tests in a SaaS like Codeship. 6 | 7 | ## Installing 8 | 9 | ```shell 10 | $ pip install FreeSWITCH-ESL-Python 11 | ``` 12 | 13 | ## Checking version 14 | 15 | ```shell 16 | $ pip freeze | grep ESL 17 | ``` 18 | 19 | Should return something like `FreeSWITCH-ESL-Python==1.2`. 20 | 21 | ## Importing 22 | 23 | ```python 24 | from freeswitchESL import ESL 25 | ``` 26 | 27 | ## Redundancy 28 | 29 | ```python 30 | try: 31 | import ESL 32 | except ImportError: 33 | from freeswitchESL import ESL 34 | ``` 35 | 36 | Redundancy could be helpful when you have FreeSWITCH running with your system in production environments, in a way you can just import default ESL package, but you also have a test environment (E.g. Codeship) without a FreeSWITCH installation. 37 | 38 | ## Building 39 | 40 | ```shell 41 | $ cd swig 42 | $ swig -module ESL -classic -python -c++ -DMULTIPLICITY -threads -I../include -o esl_wrap.cpp ESL.i 43 | $ mv ESL.py ../freeswitchESL/ESL.py 44 | ``` 45 | 46 | ## Resources 47 | 48 | - Original repository by [gurteshwar](https://github.com/gurteshwar/freeswitch-esl-python) 49 | - FreeSWITCH docs: [ESL](http://wiki.freeswitch.org/wiki/Esl) and [Python ESL](http://wiki.freeswitch.org/wiki/Python_ESL) 50 | - SWIG docs: [SWIG 3.0](http://www.swig.org/Doc3.0/SWIGDocumentation.html) 51 | 52 | ## Alternatives 53 | 54 | - Sangoma’s Python ESL SWIG wrapper - https://github.com/sangoma/python-ESL 55 | -------------------------------------------------------------------------------- /include/esl_threadmutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Cross Platform Thread/Mutex abstraction 3 | * Copyright(C) 2007 Michael Jerris 4 | * 5 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 6 | * copies of the Software, and permit persons to whom the Software is 7 | * furnished to do so. 8 | * 9 | * This work is provided under this license on an "as is" basis, without warranty of any kind, 10 | * either expressed or implied, including, without limitation, warranties that the covered code 11 | * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire 12 | * risk as to the quality and performance of the covered code is with you. Should any covered 13 | * code prove defective in any respect, you (not the initial developer or any other contributor) 14 | * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty 15 | * constitutes an essential part of this license. No use of any covered code is authorized hereunder 16 | * except under this disclaimer. 17 | * 18 | */ 19 | 20 | 21 | #ifndef _ESL_THREADMUTEX_H 22 | #define _ESL_THREADMUTEX_H 23 | 24 | #include "esl.h" 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif /* defined(__cplusplus) */ 29 | 30 | typedef struct esl_mutex esl_mutex_t; 31 | typedef struct esl_thread esl_thread_t; 32 | typedef void *(*esl_thread_function_t) (esl_thread_t *, void *); 33 | 34 | ESL_DECLARE(esl_status_t) esl_thread_create_detached(esl_thread_function_t func, void *data); 35 | esl_status_t esl_thread_create_detached_ex(esl_thread_function_t func, void *data, size_t stack_size); 36 | void esl_thread_override_default_stacksize(size_t size); 37 | ESL_DECLARE(esl_status_t) esl_mutex_create(esl_mutex_t **mutex); 38 | ESL_DECLARE(esl_status_t) esl_mutex_destroy(esl_mutex_t **mutex); 39 | ESL_DECLARE(esl_status_t) esl_mutex_lock(esl_mutex_t *mutex); 40 | ESL_DECLARE(esl_status_t) esl_mutex_trylock(esl_mutex_t *mutex); 41 | ESL_DECLARE(esl_status_t) esl_mutex_unlock(esl_mutex_t *mutex); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif /* defined(__cplusplus) */ 46 | 47 | #endif /* defined(_ESL_THREADMUTEX_H) */ 48 | 49 | /* For Emacs: 50 | * Local Variables: 51 | * mode:c 52 | * indent-tabs-mode:t 53 | * tab-width:4 54 | * c-basic-offset:4 55 | * End: 56 | * For VIM: 57 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 58 | */ 59 | -------------------------------------------------------------------------------- /include/esl_oop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _ESL_OOP_H_ 35 | #define _ESL_OOP_H_ 36 | #include 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | #define this_check(x) do { if (!this) { esl_log(ESL_LOG_ERROR, "object is not initalized\n"); return x;}} while(0) 42 | #define this_check_void() do { if (!this) { esl_log(ESL_LOG_ERROR, "object is not initalized\n"); return;}} while(0) 43 | 44 | 45 | class ESLevent { 46 | private: 47 | esl_event_header_t *hp; 48 | public: 49 | esl_event_t *event; 50 | char *serialized_string; 51 | int mine; 52 | 53 | ESLevent(const char *type, const char *subclass_name = NULL); 54 | ESLevent(esl_event_t *wrap_me, int free_me = 0); 55 | ESLevent(ESLevent *me); 56 | virtual ~ESLevent(); 57 | const char *serialize(const char *format = NULL); 58 | bool setPriority(esl_priority_t priority = ESL_PRIORITY_NORMAL); 59 | const char *getHeader(const char *header_name, int idx = -1); 60 | char *getBody(void); 61 | const char *getType(void); 62 | bool addBody(const char *value); 63 | bool addHeader(const char *header_name, const char *value); 64 | bool pushHeader(const char *header_name, const char *value); 65 | bool unshiftHeader(const char *header_name, const char *value); 66 | bool delHeader(const char *header_name); 67 | const char *firstHeader(void); 68 | const char *nextHeader(void); 69 | }; 70 | 71 | 72 | 73 | class ESLconnection { 74 | private: 75 | esl_handle_t handle; 76 | public: 77 | ESLconnection(const char *host, const int port, const char *user, const char *password); 78 | ESLconnection(const char *host, const int port, const char *password); 79 | ESLconnection(const char *host, const char *port, const char *user, const char *password); 80 | ESLconnection(const char *host, const char *port, const char *password); 81 | ESLconnection(int socket); 82 | virtual ~ESLconnection(); 83 | int socketDescriptor(); 84 | int connected(); 85 | ESLevent *getInfo(); 86 | int send(const char *cmd); 87 | ESLevent *sendRecv(const char *cmd); 88 | ESLevent *api(const char *cmd, const char *arg = NULL); 89 | ESLevent *bgapi(const char *cmd, const char *arg = NULL, const char *job_uuid = NULL); 90 | ESLevent *sendEvent(ESLevent *send_me); 91 | int sendMSG(ESLevent *send_me, const char *uuid = NULL); 92 | ESLevent *recvEvent(); 93 | ESLevent *recvEventTimed(int ms); 94 | ESLevent *filter(const char *header, const char *value); 95 | int events(const char *etype, const char *value); 96 | ESLevent *execute(const char *app, const char *arg = NULL, const char *uuid = NULL); 97 | ESLevent *executeAsync(const char *app, const char *arg = NULL, const char *uuid = NULL); 98 | int setAsyncExecute(const char *val); 99 | int setEventLock(const char *val); 100 | int disconnect(void); 101 | }; 102 | 103 | void eslSetLogLevel(int level); 104 | 105 | 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif 112 | 113 | /* For Emacs: 114 | * Local Variables: 115 | * mode:c++ 116 | * indent-tabs-mode:t 117 | * tab-width:4 118 | * c-basic-offset:4 119 | * End: 120 | * For VIM: 121 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 122 | */ 123 | -------------------------------------------------------------------------------- /include/esl_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @defgroup config Config File Parser 36 | * @ingroup config 37 | * This module implements a basic interface and file format parser 38 | * 39 | *
 40 |  *
 41 |  * EXAMPLE
 42 |  *
 43 |  * [category1]
 44 |  * var1 => val1
 45 |  * var2 => val2
 46 |  * \# lines that begin with \# are comments
 47 |  * \#var3 => val3
 48 |  * 
49 | * @{ 50 | */ 51 | 52 | #ifndef ESL_CONFIG_H 53 | #define ESL_CONFIG_H 54 | 55 | #include "esl.h" 56 | 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif /* defined(__cplusplus) */ 60 | 61 | 62 | #define ESL_URL_SEPARATOR "://" 63 | 64 | 65 | #ifdef WIN32 66 | #define ESL_PATH_SEPARATOR "\\" 67 | #ifndef ESL_CONFIG_DIR 68 | #define ESL_CONFIG_DIR "c:\\openesl" 69 | #endif 70 | #define esl_is_file_path(file) (*(file +1) == ':' || *file == '/' || strstr(file, SWITCH_URL_SEPARATOR)) 71 | #else 72 | #define ESL_PATH_SEPARATOR "/" 73 | #ifndef ESL_CONFIG_DIR 74 | #define ESL_CONFIG_DIR "/etc/openesl" 75 | #endif 76 | #define esl_is_file_path(file) ((*file == '/') || strstr(file, SWITCH_URL_SEPARATOR)) 77 | #endif 78 | 79 | /*! 80 | \brief Evaluate the truthfullness of a string expression 81 | \param expr a string expression 82 | \return true or false 83 | */ 84 | static __inline__ int esl_true(const char *expr) { 85 | return (expr && (!strcasecmp(expr, "yes") 86 | || !strcasecmp(expr, "on") 87 | || !strcasecmp(expr, "true") 88 | || !strcasecmp(expr, "enabled") 89 | || !strcasecmp(expr, "active") 90 | || !strcasecmp(expr, "allow") 91 | || atoi(expr))); 92 | } 93 | 94 | /*! 95 | \brief Evaluate the falsefullness of a string expression 96 | \param expr a string expression 97 | \return true or false 98 | */ 99 | static __inline__ int esl_false(const char *expr) { 100 | return (expr && (!strcasecmp(expr, "no") 101 | || !strcasecmp(expr, "off") 102 | || !strcasecmp(expr, "false") 103 | || !strcasecmp(expr, "disabled") 104 | || !strcasecmp(expr, "inactive") 105 | || !strcasecmp(expr, "disallow") 106 | || !atoi(expr))); 107 | } 108 | 109 | typedef struct esl_config esl_config_t; 110 | 111 | /*! \brief A simple file handle representing an open configuration file **/ 112 | struct esl_config { 113 | /*! FILE stream buffer to the opened file */ 114 | FILE *file; 115 | /*! path to the file */ 116 | char path[512]; 117 | /*! current category */ 118 | char category[256]; 119 | /*! current section */ 120 | char section[256]; 121 | /*! buffer of current line being read */ 122 | char buf[1024]; 123 | /*! current line number in file */ 124 | int lineno; 125 | /*! current category number in file */ 126 | int catno; 127 | /*! current section number in file */ 128 | int sectno; 129 | 130 | int lockto; 131 | }; 132 | 133 | /*! 134 | \brief Open a configuration file 135 | \param cfg (esl_config_t *) config handle to use 136 | \param file_path path to the file 137 | \return 1 (true) on success 0 (false) on failure 138 | */ 139 | ESL_DECLARE(int) esl_config_open_file(esl_config_t * cfg, const char *file_path); 140 | 141 | /*! 142 | \brief Close a previously opened configuration file 143 | \param cfg (esl_config_t *) config handle to use 144 | */ 145 | ESL_DECLARE(void) esl_config_close_file(esl_config_t * cfg); 146 | 147 | /*! 148 | \brief Retrieve next name/value pair from configuration file 149 | \param cfg (esl_config_t *) config handle to use 150 | \param var pointer to aim at the new variable name 151 | \param val pointer to aim at the new value 152 | */ 153 | ESL_DECLARE(int) esl_config_next_pair(esl_config_t * cfg, char **var, char **val); 154 | 155 | /*! 156 | \brief Retrieve the CAS bits from a configuration string value 157 | \param strvalue pointer to the configuration string value (expected to be in format whatever:xxxx) 158 | \param outbits pointer to aim at the CAS bits 159 | */ 160 | ESL_DECLARE(int) esl_config_get_cas_bits(char *strvalue, unsigned char *outbits); 161 | 162 | 163 | /** @} */ 164 | 165 | #ifdef __cplusplus 166 | } 167 | #endif /* defined(__cplusplus) */ 168 | 169 | #endif /* defined(ESL_CONFIG_H) */ 170 | 171 | /* For Emacs: 172 | * Local Variables: 173 | * mode:c 174 | * indent-tabs-mode:t 175 | * tab-width:4 176 | * c-basic-offset:4 177 | * End: 178 | * For VIM: 179 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 180 | */ 181 | -------------------------------------------------------------------------------- /include/esl_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | #include "esl.h" 34 | #ifndef ESL_BUFFER_H 35 | #define ESL_BUFFER_H 36 | /** 37 | * @defgroup esl_buffer Buffer Routines 38 | * @ingroup buffer 39 | * The purpose of this module is to make a plain buffering interface that can be used for read/write buffers 40 | * throughout the application. 41 | * @{ 42 | */ 43 | struct esl_buffer; 44 | typedef struct esl_buffer esl_buffer_t; 45 | 46 | /*! \brief Allocate a new dynamic esl_buffer 47 | * \param buffer returned pointer to the new buffer 48 | * \param blocksize length to realloc by as data is added 49 | * \param start_len ammount of memory to reserve initially 50 | * \param max_len length the buffer is allowed to grow to 51 | * \return status 52 | */ 53 | ESL_DECLARE(esl_status_t) esl_buffer_create(esl_buffer_t **buffer, esl_size_t blocksize, esl_size_t start_len, esl_size_t max_len); 54 | 55 | /*! \brief Get the length of a esl_buffer_t 56 | * \param buffer any buffer of type esl_buffer_t 57 | * \return int size of the buffer. 58 | */ 59 | ESL_DECLARE(esl_size_t) esl_buffer_len(esl_buffer_t *buffer); 60 | 61 | /*! \brief Get the freespace of a esl_buffer_t 62 | * \param buffer any buffer of type esl_buffer_t 63 | * \return int freespace in the buffer. 64 | */ 65 | ESL_DECLARE(esl_size_t) esl_buffer_freespace(esl_buffer_t *buffer); 66 | 67 | /*! \brief Get the in use amount of a esl_buffer_t 68 | * \param buffer any buffer of type esl_buffer_t 69 | * \return int ammount of buffer curently in use 70 | */ 71 | ESL_DECLARE(esl_size_t) esl_buffer_inuse(esl_buffer_t *buffer); 72 | 73 | /*! \brief Read data from a esl_buffer_t up to the ammount of datalen if it is available. Remove read data from buffer. 74 | * \param buffer any buffer of type esl_buffer_t 75 | * \param data pointer to the read data to be returned 76 | * \param datalen amount of data to be returned 77 | * \return int ammount of data actually read 78 | */ 79 | ESL_DECLARE(esl_size_t) esl_buffer_read(esl_buffer_t *buffer, void *data, esl_size_t datalen); 80 | 81 | ESL_DECLARE(esl_size_t) esl_buffer_read_packet(esl_buffer_t *buffer, void *data, esl_size_t maxlen); 82 | ESL_DECLARE(esl_size_t) esl_buffer_packet_count(esl_buffer_t *buffer); 83 | 84 | /*! \brief Read data endlessly from a esl_buffer_t 85 | * \param buffer any buffer of type esl_buffer_t 86 | * \param data pointer to the read data to be returned 87 | * \param datalen amount of data to be returned 88 | * \return int ammount of data actually read 89 | * \note Once you have read all the data from the buffer it will loop around. 90 | */ 91 | ESL_DECLARE(esl_size_t) esl_buffer_read_loop(esl_buffer_t *buffer, void *data, esl_size_t datalen); 92 | 93 | /*! \brief Assign a number of loops to read 94 | * \param buffer any buffer of type esl_buffer_t 95 | * \param loops the number of loops (-1 for infinite) 96 | */ 97 | ESL_DECLARE(void) esl_buffer_set_loops(esl_buffer_t *buffer, int32_t loops); 98 | 99 | /*! \brief Write data into a esl_buffer_t up to the length of datalen 100 | * \param buffer any buffer of type esl_buffer_t 101 | * \param data pointer to the data to be written 102 | * \param datalen amount of data to be written 103 | * \return int amount of buffer used after the write, or 0 if no space available 104 | */ 105 | ESL_DECLARE(esl_size_t) esl_buffer_write(esl_buffer_t *buffer, const void *data, esl_size_t datalen); 106 | 107 | /*! \brief Remove data from the buffer 108 | * \param buffer any buffer of type esl_buffer_t 109 | * \param datalen amount of data to be removed 110 | * \return int size of buffer, or 0 if unable to toss that much data 111 | */ 112 | ESL_DECLARE(esl_size_t) esl_buffer_toss(esl_buffer_t *buffer, esl_size_t datalen); 113 | 114 | /*! \brief Remove all data from the buffer 115 | * \param buffer any buffer of type esl_buffer_t 116 | */ 117 | ESL_DECLARE(void) esl_buffer_zero(esl_buffer_t *buffer); 118 | 119 | /*! \brief Destroy the buffer 120 | * \param buffer buffer to destroy 121 | * \note only neccessary on dynamic buffers (noop on pooled ones) 122 | */ 123 | ESL_DECLARE(void) esl_buffer_destroy(esl_buffer_t **buffer); 124 | 125 | /*! \brief Seek to offset from the beginning of the buffer 126 | * \param buffer buffer to seek 127 | * \param datalen offset in bytes 128 | * \return new position 129 | */ 130 | ESL_DECLARE(esl_size_t) esl_buffer_seek(esl_buffer_t *buffer, esl_size_t datalen); 131 | 132 | /** @} */ 133 | 134 | ESL_DECLARE(esl_size_t) esl_buffer_zwrite(esl_buffer_t *buffer, const void *data, esl_size_t datalen); 135 | 136 | #endif 137 | /* For Emacs: 138 | * Local Variables: 139 | * mode:c 140 | * indent-tabs-mode:t 141 | * tab-width:4 142 | * c-basic-offset:4 143 | * End: 144 | * For VIM: 145 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 146 | */ 147 | -------------------------------------------------------------------------------- /src/esl_threadmutex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Cross Platform Thread/Mutex abstraction 3 | * Copyright(C) 2007 Michael Jerris 4 | * 5 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 6 | * copies of the Software, and permit persons to whom the Software is 7 | * furnished to do so. 8 | * 9 | * This work is provided under this license on an "as is" basis, without warranty of any kind, 10 | * either expressed or implied, including, without limitation, warranties that the covered code 11 | * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire 12 | * risk as to the quality and performance of the covered code is with you. Should any covered 13 | * code prove defective in any respect, you (not the initial developer or any other contributor) 14 | * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty 15 | * constitutes an essential part of this license. No use of any covered code is authorized hereunder 16 | * except under this disclaimer. 17 | * 18 | */ 19 | 20 | #ifdef WIN32 21 | /* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ 22 | #define _WIN32_WINNT 0x0400 23 | #endif 24 | 25 | #include "esl.h" 26 | #include "esl_threadmutex.h" 27 | 28 | #ifdef WIN32 29 | #include 30 | 31 | #define ESL_THREAD_CALLING_CONVENTION __stdcall 32 | 33 | struct esl_mutex { 34 | CRITICAL_SECTION mutex; 35 | }; 36 | 37 | #else 38 | 39 | #include 40 | 41 | #define ESL_THREAD_CALLING_CONVENTION 42 | 43 | struct esl_mutex { 44 | pthread_mutex_t mutex; 45 | }; 46 | 47 | #endif 48 | 49 | struct esl_thread { 50 | #ifdef WIN32 51 | void *handle; 52 | #else 53 | pthread_t handle; 54 | #endif 55 | void *private_data; 56 | esl_thread_function_t function; 57 | size_t stack_size; 58 | #ifndef WIN32 59 | pthread_attr_t attribute; 60 | #endif 61 | }; 62 | 63 | size_t thread_default_stacksize = 240 * 1024; 64 | 65 | void esl_thread_override_default_stacksize(size_t size) 66 | { 67 | thread_default_stacksize = size; 68 | } 69 | 70 | static void * ESL_THREAD_CALLING_CONVENTION thread_launch(void *args) 71 | { 72 | void *exit_val; 73 | esl_thread_t *thread = (esl_thread_t *)args; 74 | exit_val = thread->function(thread, thread->private_data); 75 | #ifndef WIN32 76 | pthread_attr_destroy(&thread->attribute); 77 | #endif 78 | free(thread); 79 | 80 | return exit_val; 81 | } 82 | 83 | ESL_DECLARE(esl_status_t) esl_thread_create_detached(esl_thread_function_t func, void *data) 84 | { 85 | return esl_thread_create_detached_ex(func, data, thread_default_stacksize); 86 | } 87 | 88 | esl_status_t esl_thread_create_detached_ex(esl_thread_function_t func, void *data, size_t stack_size) 89 | { 90 | esl_thread_t *thread = NULL; 91 | esl_status_t status = ESL_FAIL; 92 | 93 | if (!func || !(thread = (esl_thread_t *)malloc(sizeof(esl_thread_t)))) { 94 | goto done; 95 | } 96 | 97 | thread->private_data = data; 98 | thread->function = func; 99 | thread->stack_size = stack_size; 100 | 101 | #if defined(WIN32) 102 | thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL); 103 | if (!thread->handle) { 104 | goto fail; 105 | } 106 | CloseHandle(thread->handle); 107 | 108 | status = ESL_SUCCESS; 109 | goto done; 110 | #else 111 | 112 | if (pthread_attr_init(&thread->attribute) != 0) goto fail; 113 | 114 | if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; 115 | 116 | if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; 117 | 118 | if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; 119 | 120 | status = ESL_SUCCESS; 121 | goto done; 122 | 123 | failpthread: 124 | 125 | pthread_attr_destroy(&thread->attribute); 126 | #endif 127 | 128 | fail: 129 | if (thread) { 130 | free(thread); 131 | } 132 | done: 133 | return status; 134 | } 135 | 136 | 137 | ESL_DECLARE(esl_status_t) esl_mutex_create(esl_mutex_t **mutex) 138 | { 139 | esl_status_t status = ESL_FAIL; 140 | #ifndef WIN32 141 | pthread_mutexattr_t attr; 142 | #endif 143 | esl_mutex_t *check = NULL; 144 | 145 | check = (esl_mutex_t *)malloc(sizeof(**mutex)); 146 | if (!check) 147 | goto done; 148 | #ifdef WIN32 149 | InitializeCriticalSection(&check->mutex); 150 | #else 151 | if (pthread_mutexattr_init(&attr)) { 152 | free(check); 153 | goto done; 154 | } 155 | 156 | if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) 157 | goto fail; 158 | 159 | if (pthread_mutex_init(&check->mutex, &attr)) 160 | goto fail; 161 | 162 | goto success; 163 | 164 | fail: 165 | pthread_mutexattr_destroy(&attr); 166 | free(check); 167 | goto done; 168 | 169 | success: 170 | #endif 171 | *mutex = check; 172 | status = ESL_SUCCESS; 173 | 174 | done: 175 | return status; 176 | } 177 | 178 | ESL_DECLARE(esl_status_t) esl_mutex_destroy(esl_mutex_t **mutex) 179 | { 180 | esl_mutex_t *mp = *mutex; 181 | *mutex = NULL; 182 | if (!mp) { 183 | return ESL_FAIL; 184 | } 185 | #ifdef WIN32 186 | DeleteCriticalSection(&mp->mutex); 187 | #else 188 | if (pthread_mutex_destroy(&mp->mutex)) 189 | return ESL_FAIL; 190 | #endif 191 | free(mp); 192 | return ESL_SUCCESS; 193 | } 194 | 195 | ESL_DECLARE(esl_status_t) esl_mutex_lock(esl_mutex_t *mutex) 196 | { 197 | #ifdef WIN32 198 | EnterCriticalSection(&mutex->mutex); 199 | #else 200 | if (pthread_mutex_lock(&mutex->mutex)) 201 | return ESL_FAIL; 202 | #endif 203 | return ESL_SUCCESS; 204 | } 205 | 206 | ESL_DECLARE(esl_status_t) esl_mutex_trylock(esl_mutex_t *mutex) 207 | { 208 | #ifdef WIN32 209 | if (!TryEnterCriticalSection(&mutex->mutex)) 210 | return ESL_FAIL; 211 | #else 212 | if (pthread_mutex_trylock(&mutex->mutex)) 213 | return ESL_FAIL; 214 | #endif 215 | return ESL_SUCCESS; 216 | } 217 | 218 | ESL_DECLARE(esl_status_t) esl_mutex_unlock(esl_mutex_t *mutex) 219 | { 220 | #ifdef WIN32 221 | LeaveCriticalSection(&mutex->mutex); 222 | #else 223 | if (pthread_mutex_unlock(&mutex->mutex)) 224 | return ESL_FAIL; 225 | #endif 226 | return ESL_SUCCESS; 227 | } 228 | 229 | 230 | 231 | 232 | 233 | /* For Emacs: 234 | * Local Variables: 235 | * mode:c 236 | * indent-tabs-mode:t 237 | * tab-width:4 238 | * c-basic-offset:4 239 | * End: 240 | * For VIM: 241 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 242 | */ 243 | -------------------------------------------------------------------------------- /include/esl_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "esl.h" 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | /* cJSON Types: */ 32 | #define cJSON_False 0 33 | #define cJSON_True 1 34 | #define cJSON_NULL 2 35 | #define cJSON_Number 3 36 | #define cJSON_String 4 37 | #define cJSON_Array 5 38 | #define cJSON_Object 6 39 | 40 | #define cJSON_IsReference 256 41 | 42 | /* The cJSON structure: */ 43 | typedef struct cJSON { 44 | struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 45 | struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 46 | 47 | int type; /* The type of the item, as above. */ 48 | 49 | char *valuestring; /* The item's string, if type==cJSON_String */ 50 | int valueint; /* The item's number, if type==cJSON_Number */ 51 | double valuedouble; /* The item's number, if type==cJSON_Number */ 52 | 53 | char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 54 | } cJSON; 55 | 56 | typedef struct cJSON_Hooks { 57 | void *(*malloc_fn)(size_t sz); 58 | void (*free_fn)(void *ptr); 59 | } cJSON_Hooks; 60 | 61 | /* Supply malloc, realloc and free functions to cJSON */ 62 | ESL_DECLARE(void) cJSON_InitHooks(cJSON_Hooks* hooks); 63 | 64 | 65 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ 66 | ESL_DECLARE(cJSON *)cJSON_Parse(const char *value); 67 | /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ 68 | ESL_DECLARE(char *)cJSON_Print(cJSON *item); 69 | /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ 70 | ESL_DECLARE(char *)cJSON_PrintUnformatted(cJSON *item); 71 | /* Delete a cJSON entity and all subentities. */ 72 | ESL_DECLARE(void) cJSON_Delete(cJSON *c); 73 | 74 | /* Returns the number of items in an array (or object). */ 75 | ESL_DECLARE(int) cJSON_GetArraySize(cJSON *array); 76 | /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ 77 | ESL_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item); 78 | /* Get item "string" from object. Case insensitive. */ 79 | ESL_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string); 80 | 81 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 82 | ESL_DECLARE(const char *)cJSON_GetErrorPtr(void); 83 | 84 | /* These calls create a cJSON item of the appropriate type. */ 85 | ESL_DECLARE(cJSON *)cJSON_CreateNull(void); 86 | ESL_DECLARE(cJSON *)cJSON_CreateTrue(void); 87 | ESL_DECLARE(cJSON *)cJSON_CreateFalse(void); 88 | ESL_DECLARE(cJSON *)cJSON_CreateBool(int b); 89 | ESL_DECLARE(cJSON *)cJSON_CreateNumber(double num); 90 | ESL_DECLARE(cJSON *)cJSON_CreateString(const char *string); 91 | ESL_DECLARE(cJSON *)cJSON_CreateArray(void); 92 | ESL_DECLARE(cJSON *)cJSON_CreateObject(void); 93 | 94 | /* These utilities create an Array of count items. */ 95 | ESL_DECLARE(cJSON *)cJSON_CreateIntArray(int *numbers,int count); 96 | ESL_DECLARE(cJSON *)cJSON_CreateFloatArray(float *numbers,int count); 97 | ESL_DECLARE(cJSON *)cJSON_CreateDoubleArray(double *numbers,int count); 98 | ESL_DECLARE(cJSON *)cJSON_CreateStringArray(const char **strings,int count); 99 | 100 | /* Append item to the specified array/object. */ 101 | ESL_DECLARE(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); 102 | ESL_DECLARE(void) cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); 103 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 104 | ESL_DECLARE(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 105 | ESL_DECLARE(void) cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); 106 | 107 | /* Remove/Detatch items from Arrays/Objects. */ 108 | ESL_DECLARE(cJSON *)cJSON_DetachItemFromArray(cJSON *array,int which); 109 | ESL_DECLARE(void) cJSON_DeleteItemFromArray(cJSON *array,int which); 110 | ESL_DECLARE(cJSON *)cJSON_DetachItemFromObject(cJSON *object,const char *string); 111 | ESL_DECLARE(void) cJSON_DeleteItemFromObject(cJSON *object,const char *string); 112 | 113 | /* Update array items. */ 114 | ESL_DECLARE(void) cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); 115 | ESL_DECLARE(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 116 | 117 | #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) 118 | #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) 119 | #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) 120 | #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) 121 | #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif 128 | 129 | /* For Emacs: 130 | * Local Variables: 131 | * mode:c 132 | * indent-tabs-mode:t 133 | * tab-width:4 134 | * c-basic-offset:4 135 | * End: 136 | * For VIM: 137 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 138 | */ 139 | -------------------------------------------------------------------------------- /src/esl_config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "esl.h" 35 | #include "esl_config.h" 36 | 37 | ESL_DECLARE(int) esl_config_open_file(esl_config_t *cfg, const char *file_path) 38 | { 39 | FILE *f; 40 | const char *path = NULL; 41 | char path_buf[1024]; 42 | 43 | if (file_path[0] == '/') { 44 | path = file_path; 45 | } else { 46 | esl_snprintf(path_buf, sizeof(path_buf), "%s%s%s", ESL_CONFIG_DIR, ESL_PATH_SEPARATOR, file_path); 47 | path = path_buf; 48 | } 49 | 50 | if (!path) { 51 | return 0; 52 | } 53 | 54 | memset(cfg, 0, sizeof(*cfg)); 55 | cfg->lockto = -1; 56 | esl_log(ESL_LOG_DEBUG, "Configuration file is %s.\n", path); 57 | f = fopen(path, "r"); 58 | 59 | if (!f) { 60 | if (file_path[0] != '/') { 61 | int last = -1; 62 | char *var, *val; 63 | 64 | esl_snprintf(path_buf, sizeof(path_buf), "%s%sopenesl.conf", ESL_CONFIG_DIR, ESL_PATH_SEPARATOR); 65 | path = path_buf; 66 | 67 | if ((f = fopen(path, "r")) == 0) { 68 | return 0; 69 | } 70 | 71 | cfg->file = f; 72 | esl_set_string(cfg->path, path); 73 | 74 | while (esl_config_next_pair(cfg, &var, &val)) { 75 | if ((cfg->sectno != last) && !strcmp(cfg->section, file_path)) { 76 | cfg->lockto = cfg->sectno; 77 | return 1; 78 | } 79 | } 80 | 81 | esl_config_close_file(cfg); 82 | memset(cfg, 0, sizeof(*cfg)); 83 | return 0; 84 | } 85 | 86 | return 0; 87 | } else { 88 | cfg->file = f; 89 | esl_set_string(cfg->path, path); 90 | return 1; 91 | } 92 | } 93 | 94 | ESL_DECLARE(void) esl_config_close_file(esl_config_t *cfg) 95 | { 96 | 97 | if (cfg->file) { 98 | fclose(cfg->file); 99 | } 100 | 101 | memset(cfg, 0, sizeof(*cfg)); 102 | } 103 | 104 | 105 | 106 | ESL_DECLARE(int) esl_config_next_pair(esl_config_t *cfg, char **var, char **val) 107 | { 108 | int ret = 0; 109 | char *p, *end; 110 | 111 | *var = *val = NULL; 112 | 113 | if (!cfg || !cfg->file) { 114 | return 0; 115 | } 116 | 117 | for (;;) { 118 | cfg->lineno++; 119 | 120 | if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { 121 | ret = 0; 122 | break; 123 | } 124 | *var = cfg->buf; 125 | 126 | if (**var == '[' && (end = strchr(*var, ']')) != 0) { 127 | *end = '\0'; 128 | (*var)++; 129 | if (**var == '+') { 130 | (*var)++; 131 | esl_copy_string(cfg->section, *var, sizeof(cfg->section)); 132 | cfg->sectno++; 133 | 134 | if (cfg->lockto > -1 && cfg->sectno != cfg->lockto) { 135 | break; 136 | } 137 | cfg->catno = 0; 138 | cfg->lineno = 0; 139 | *var = (char *) ""; 140 | *val = (char *) ""; 141 | return 1; 142 | } else { 143 | esl_copy_string(cfg->category, *var, sizeof(cfg->category)); 144 | cfg->catno++; 145 | } 146 | continue; 147 | } 148 | 149 | 150 | 151 | if (**var == '#' || **var == ';' || **var == '\n' || **var == '\r') { 152 | continue; 153 | } 154 | 155 | if (!strncmp(*var, "__END__", 7)) { 156 | break; 157 | } 158 | 159 | 160 | if ((end = strchr(*var, ';')) && *(end+1) == *end) { 161 | *end = '\0'; 162 | end--; 163 | } else if ((end = strchr(*var, '\n')) != 0) { 164 | if (*(end - 1) == '\r') { 165 | end--; 166 | } 167 | *end = '\0'; 168 | } 169 | 170 | p = *var; 171 | while ((*p == ' ' || *p == '\t') && p != end) { 172 | *p = '\0'; 173 | p++; 174 | } 175 | *var = p; 176 | 177 | 178 | if ((*val = strchr(*var, '=')) == 0) { 179 | ret = -1; 180 | /* log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno); */ 181 | continue; 182 | } else { 183 | p = *val - 1; 184 | *(*val) = '\0'; 185 | (*val)++; 186 | if (*(*val) == '>') { 187 | *(*val) = '\0'; 188 | (*val)++; 189 | } 190 | 191 | while ((*p == ' ' || *p == '\t') && p != *var) { 192 | *p = '\0'; 193 | p--; 194 | } 195 | 196 | p = *val; 197 | while ((*p == ' ' || *p == '\t') && p != end) { 198 | *p = '\0'; 199 | p++; 200 | } 201 | *val = p; 202 | ret = 1; 203 | break; 204 | } 205 | } 206 | 207 | 208 | return ret; 209 | 210 | } 211 | 212 | ESL_DECLARE(int) esl_config_get_cas_bits(char *strvalue, unsigned char *outbits) 213 | { 214 | char cas_bits[5]; 215 | unsigned char bit = 0x8; 216 | char *double_colon = strchr(strvalue, ':'); 217 | int x = 0; 218 | 219 | if (!double_colon) { 220 | esl_log(ESL_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); 221 | return -1; 222 | } 223 | 224 | double_colon++; 225 | *outbits = 0; 226 | cas_bits[4] = 0; 227 | 228 | if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) { 229 | esl_log(ESL_LOG_ERROR, "Invalid CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); 230 | return -1; 231 | } 232 | 233 | esl_log(ESL_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits); 234 | 235 | for (; cas_bits[x]; x++) { 236 | if ('1' == cas_bits[x]) { 237 | *outbits |= bit; 238 | } else if ('0' != cas_bits[x]) { 239 | esl_log(ESL_LOG_ERROR, "Invalid CAS pattern specified: %s, just 0 or 1 allowed for each bit\n"); 240 | return -1; 241 | } 242 | bit >>= 1; 243 | } 244 | return 0; 245 | } 246 | 247 | /* For Emacs: 248 | * Local Variables: 249 | * mode:c 250 | * indent-tabs-mode:t 251 | * tab-width:4 252 | * c-basic-offset:4 253 | * End: 254 | * For VIM: 255 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 256 | */ 257 | -------------------------------------------------------------------------------- /freeswitchESL/ESL.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (http://www.swig.org). 2 | # Version 3.0.5 3 | # 4 | # Do not make changes to this file unless you know what you are doing--modify 5 | # the SWIG interface file instead. 6 | 7 | 8 | 9 | 10 | 11 | from sys import version_info 12 | if version_info >= (2, 6, 0): 13 | def swig_import_helper(): 14 | from os.path import dirname 15 | import imp 16 | fp = None 17 | try: 18 | fp, pathname, description = imp.find_module('_ESL', [dirname(__file__)]) 19 | except ImportError: 20 | import _ESL 21 | return _ESL 22 | if fp is not None: 23 | try: 24 | _mod = imp.load_module('_ESL', fp, pathname, description) 25 | finally: 26 | fp.close() 27 | return _mod 28 | _ESL = swig_import_helper() 29 | del swig_import_helper 30 | else: 31 | import _ESL 32 | del version_info 33 | 34 | def _swig_setattr_nondynamic(self, class_type, name, value, static=1): 35 | if (name == "thisown"): 36 | return self.this.own(value) 37 | if (name == "this"): 38 | if type(value).__name__ == 'SwigPyObject': 39 | self.__dict__[name] = value 40 | return 41 | method = class_type.__swig_setmethods__.get(name, None) 42 | if method: 43 | return method(self, value) 44 | if (not static): 45 | self.__dict__[name] = value 46 | else: 47 | raise AttributeError("You cannot add attributes to %s" % self) 48 | 49 | 50 | def _swig_setattr(self, class_type, name, value): 51 | return _swig_setattr_nondynamic(self, class_type, name, value, 0) 52 | 53 | 54 | def _swig_getattr_nondynamic(self, class_type, name, static=1): 55 | if (name == "thisown"): 56 | return self.this.own() 57 | method = class_type.__swig_getmethods__.get(name, None) 58 | if method: 59 | return method(self) 60 | if (not static): 61 | return object.__getattr__(self, name) 62 | else: 63 | raise AttributeError(name) 64 | 65 | def _swig_getattr(self, class_type, name): 66 | return _swig_getattr_nondynamic(self, class_type, name, 0) 67 | 68 | 69 | def _swig_repr(self): 70 | try: 71 | strthis = "proxy of " + self.this.__repr__() 72 | except: 73 | strthis = "" 74 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 75 | 76 | class ESLevent: 77 | __swig_setmethods__ = {} 78 | __setattr__ = lambda self, name, value: _swig_setattr(self, ESLevent, name, value) 79 | __swig_getmethods__ = {} 80 | __getattr__ = lambda self, name: _swig_getattr(self, ESLevent, name) 81 | __repr__ = _swig_repr 82 | __swig_setmethods__["event"] = _ESL.ESLevent_event_set 83 | __swig_getmethods__["event"] = _ESL.ESLevent_event_get 84 | __swig_setmethods__["serialized_string"] = _ESL.ESLevent_serialized_string_set 85 | __swig_getmethods__["serialized_string"] = _ESL.ESLevent_serialized_string_get 86 | __swig_setmethods__["mine"] = _ESL.ESLevent_mine_set 87 | __swig_getmethods__["mine"] = _ESL.ESLevent_mine_get 88 | 89 | def __init__(self, *args): 90 | this = _ESL.new_ESLevent(*args) 91 | try: 92 | self.this.append(this) 93 | except: 94 | self.this = this 95 | __swig_destroy__ = _ESL.delete_ESLevent 96 | __del__ = lambda self: None 97 | 98 | def serialize(self, format=None): 99 | return _ESL.ESLevent_serialize(self, format) 100 | 101 | def setPriority(self, *args): 102 | return _ESL.ESLevent_setPriority(self, *args) 103 | 104 | def getHeader(self, header_name, idx=-1): 105 | return _ESL.ESLevent_getHeader(self, header_name, idx) 106 | 107 | def getBody(self): 108 | return _ESL.ESLevent_getBody(self) 109 | 110 | def getType(self): 111 | return _ESL.ESLevent_getType(self) 112 | 113 | def addBody(self, value): 114 | return _ESL.ESLevent_addBody(self, value) 115 | 116 | def addHeader(self, header_name, value): 117 | return _ESL.ESLevent_addHeader(self, header_name, value) 118 | 119 | def pushHeader(self, header_name, value): 120 | return _ESL.ESLevent_pushHeader(self, header_name, value) 121 | 122 | def unshiftHeader(self, header_name, value): 123 | return _ESL.ESLevent_unshiftHeader(self, header_name, value) 124 | 125 | def delHeader(self, header_name): 126 | return _ESL.ESLevent_delHeader(self, header_name) 127 | 128 | def firstHeader(self): 129 | return _ESL.ESLevent_firstHeader(self) 130 | 131 | def nextHeader(self): 132 | return _ESL.ESLevent_nextHeader(self) 133 | ESLevent_swigregister = _ESL.ESLevent_swigregister 134 | ESLevent_swigregister(ESLevent) 135 | 136 | class ESLconnection: 137 | __swig_setmethods__ = {} 138 | __setattr__ = lambda self, name, value: _swig_setattr(self, ESLconnection, name, value) 139 | __swig_getmethods__ = {} 140 | __getattr__ = lambda self, name: _swig_getattr(self, ESLconnection, name) 141 | __repr__ = _swig_repr 142 | 143 | def __init__(self, *args): 144 | this = _ESL.new_ESLconnection(*args) 145 | try: 146 | self.this.append(this) 147 | except: 148 | self.this = this 149 | __swig_destroy__ = _ESL.delete_ESLconnection 150 | __del__ = lambda self: None 151 | 152 | def socketDescriptor(self): 153 | return _ESL.ESLconnection_socketDescriptor(self) 154 | 155 | def connected(self): 156 | return _ESL.ESLconnection_connected(self) 157 | 158 | def getInfo(self): 159 | return _ESL.ESLconnection_getInfo(self) 160 | 161 | def send(self, cmd): 162 | return _ESL.ESLconnection_send(self, cmd) 163 | 164 | def sendRecv(self, cmd): 165 | return _ESL.ESLconnection_sendRecv(self, cmd) 166 | 167 | def api(self, cmd, arg=None): 168 | return _ESL.ESLconnection_api(self, cmd, arg) 169 | 170 | def bgapi(self, cmd, arg=None, job_uuid=None): 171 | return _ESL.ESLconnection_bgapi(self, cmd, arg, job_uuid) 172 | 173 | def sendEvent(self, send_me): 174 | return _ESL.ESLconnection_sendEvent(self, send_me) 175 | 176 | def sendMSG(self, send_me, uuid=None): 177 | return _ESL.ESLconnection_sendMSG(self, send_me, uuid) 178 | 179 | def recvEvent(self): 180 | return _ESL.ESLconnection_recvEvent(self) 181 | 182 | def recvEventTimed(self, ms): 183 | return _ESL.ESLconnection_recvEventTimed(self, ms) 184 | 185 | def filter(self, header, value): 186 | return _ESL.ESLconnection_filter(self, header, value) 187 | 188 | def events(self, etype, value): 189 | return _ESL.ESLconnection_events(self, etype, value) 190 | 191 | def execute(self, app, arg=None, uuid=None): 192 | return _ESL.ESLconnection_execute(self, app, arg, uuid) 193 | 194 | def executeAsync(self, app, arg=None, uuid=None): 195 | return _ESL.ESLconnection_executeAsync(self, app, arg, uuid) 196 | 197 | def setAsyncExecute(self, val): 198 | return _ESL.ESLconnection_setAsyncExecute(self, val) 199 | 200 | def setEventLock(self, val): 201 | return _ESL.ESLconnection_setEventLock(self, val) 202 | 203 | def disconnect(self): 204 | return _ESL.ESLconnection_disconnect(self) 205 | ESLconnection_swigregister = _ESL.ESLconnection_swigregister 206 | ESLconnection_swigregister(ESLconnection) 207 | 208 | 209 | def eslSetLogLevel(level): 210 | return _ESL.eslSetLogLevel(level) 211 | eslSetLogLevel = _ESL.eslSetLogLevel 212 | # This file is compatible with both classic and new-style classes. 213 | 214 | 215 | -------------------------------------------------------------------------------- /src/esl_buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | 35 | #include "esl_buffer.h" 36 | 37 | static unsigned buffer_id = 0; 38 | 39 | struct esl_buffer { 40 | unsigned char *data; 41 | unsigned char *head; 42 | esl_size_t used; 43 | esl_size_t actually_used; 44 | esl_size_t datalen; 45 | esl_size_t max_len; 46 | esl_size_t blocksize; 47 | unsigned id; 48 | int loops; 49 | }; 50 | 51 | 52 | ESL_DECLARE(esl_status_t) esl_buffer_create(esl_buffer_t **buffer, esl_size_t blocksize, esl_size_t start_len, esl_size_t max_len) 53 | { 54 | esl_buffer_t *new_buffer; 55 | 56 | new_buffer = malloc(sizeof(*new_buffer)); 57 | 58 | if (new_buffer) { 59 | memset(new_buffer, 0, sizeof(*new_buffer)); 60 | 61 | if (start_len) { 62 | new_buffer->data = malloc(start_len); 63 | if (!new_buffer->data) { 64 | free(new_buffer); 65 | return ESL_FAIL; 66 | } 67 | memset(new_buffer->data, 0, start_len); 68 | } 69 | 70 | new_buffer->max_len = max_len; 71 | new_buffer->datalen = start_len; 72 | new_buffer->id = buffer_id++; 73 | new_buffer->blocksize = blocksize; 74 | new_buffer->head = new_buffer->data; 75 | 76 | *buffer = new_buffer; 77 | return ESL_SUCCESS; 78 | } 79 | 80 | return ESL_FAIL; 81 | } 82 | 83 | ESL_DECLARE(esl_size_t) esl_buffer_len(esl_buffer_t *buffer) 84 | { 85 | 86 | esl_assert(buffer != NULL); 87 | 88 | return buffer->datalen; 89 | 90 | } 91 | 92 | 93 | ESL_DECLARE(esl_size_t) esl_buffer_freespace(esl_buffer_t *buffer) 94 | { 95 | esl_assert(buffer != NULL); 96 | 97 | if (buffer->max_len) { 98 | return (esl_size_t) (buffer->max_len - buffer->used); 99 | } 100 | return 1000000; 101 | 102 | } 103 | 104 | ESL_DECLARE(esl_size_t) esl_buffer_inuse(esl_buffer_t *buffer) 105 | { 106 | esl_assert(buffer != NULL); 107 | 108 | return buffer->used; 109 | } 110 | 111 | ESL_DECLARE(esl_size_t) esl_buffer_seek(esl_buffer_t *buffer, esl_size_t datalen) 112 | { 113 | esl_size_t reading = 0; 114 | 115 | esl_assert(buffer != NULL); 116 | 117 | if (buffer->used < 1) { 118 | buffer->used = 0; 119 | return 0; 120 | } else if (buffer->used >= datalen) { 121 | reading = datalen; 122 | } else { 123 | reading = buffer->used; 124 | } 125 | 126 | buffer->used = buffer->actually_used - reading; 127 | buffer->head = buffer->data + reading; 128 | 129 | return reading; 130 | } 131 | 132 | ESL_DECLARE(esl_size_t) esl_buffer_toss(esl_buffer_t *buffer, esl_size_t datalen) 133 | { 134 | esl_size_t reading = 0; 135 | 136 | esl_assert(buffer != NULL); 137 | 138 | if (buffer->used < 1) { 139 | buffer->used = 0; 140 | return 0; 141 | } else if (buffer->used >= datalen) { 142 | reading = datalen; 143 | } else { 144 | reading = buffer->used; 145 | } 146 | 147 | buffer->used -= reading; 148 | buffer->head += reading; 149 | 150 | return buffer->used; 151 | } 152 | 153 | ESL_DECLARE(void) esl_buffer_set_loops(esl_buffer_t *buffer, int loops) 154 | { 155 | buffer->loops = loops; 156 | } 157 | 158 | ESL_DECLARE(esl_size_t) esl_buffer_read_loop(esl_buffer_t *buffer, void *data, esl_size_t datalen) 159 | { 160 | esl_size_t len; 161 | if ((len = esl_buffer_read(buffer, data, datalen)) < datalen) { 162 | if (buffer->loops == 0) { 163 | return len; 164 | } 165 | buffer->head = buffer->data; 166 | buffer->used = buffer->actually_used; 167 | len = esl_buffer_read(buffer, (char*)data + len, datalen - len); 168 | buffer->loops--; 169 | } 170 | return len; 171 | } 172 | 173 | ESL_DECLARE(esl_size_t) esl_buffer_read(esl_buffer_t *buffer, void *data, esl_size_t datalen) 174 | { 175 | esl_size_t reading = 0; 176 | 177 | esl_assert(buffer != NULL); 178 | esl_assert(data != NULL); 179 | 180 | 181 | if (buffer->used < 1) { 182 | buffer->used = 0; 183 | return 0; 184 | } else if (buffer->used >= datalen) { 185 | reading = datalen; 186 | } else { 187 | reading = buffer->used; 188 | } 189 | 190 | memcpy(data, buffer->head, reading); 191 | buffer->used -= reading; 192 | buffer->head += reading; 193 | 194 | /* if (buffer->id == 4) printf("%u o %d = %d\n", buffer->id, (unsigned)reading, (unsigned)buffer->used); */ 195 | return reading; 196 | } 197 | 198 | 199 | ESL_DECLARE(esl_size_t) esl_buffer_packet_count(esl_buffer_t *buffer) 200 | { 201 | char *pe, *p, *e, *head = (char *) buffer->head; 202 | esl_size_t x = 0; 203 | 204 | esl_assert(buffer != NULL); 205 | 206 | e = (head + buffer->used); 207 | 208 | for (p = head; p && *p && p < e; p++) { 209 | if (*p == '\n') { 210 | pe = p+1; 211 | if (*pe == '\r') pe++; 212 | if (pe <= e && *pe == '\n') { 213 | p = pe++; 214 | x++; 215 | } 216 | } 217 | } 218 | 219 | return x; 220 | } 221 | 222 | ESL_DECLARE(esl_size_t) esl_buffer_read_packet(esl_buffer_t *buffer, void *data, esl_size_t maxlen) 223 | { 224 | char *pe, *p, *e, *head = (char *) buffer->head; 225 | esl_size_t datalen = 0; 226 | 227 | esl_assert(buffer != NULL); 228 | esl_assert(data != NULL); 229 | 230 | e = (head + buffer->used); 231 | 232 | for (p = head; p && *p && p < e; p++) { 233 | if (*p == '\n') { 234 | pe = p+1; 235 | if (*pe == '\r') pe++; 236 | if (pe <= e && *pe == '\n') { 237 | pe++; 238 | datalen = pe - head; 239 | if (datalen > maxlen) { 240 | datalen = maxlen; 241 | } 242 | break; 243 | } 244 | } 245 | } 246 | 247 | return esl_buffer_read(buffer, data, datalen); 248 | } 249 | 250 | ESL_DECLARE(esl_size_t) esl_buffer_write(esl_buffer_t *buffer, const void *data, esl_size_t datalen) 251 | { 252 | esl_size_t freespace, actual_freespace; 253 | 254 | esl_assert(buffer != NULL); 255 | esl_assert(data != NULL); 256 | esl_assert(buffer->data != NULL); 257 | 258 | if (!datalen) { 259 | return buffer->used; 260 | } 261 | 262 | actual_freespace = buffer->datalen - buffer->actually_used; 263 | if (actual_freespace < datalen && (!buffer->max_len || (buffer->used + datalen <= buffer->max_len))) { 264 | memmove(buffer->data, buffer->head, buffer->used); 265 | buffer->head = buffer->data; 266 | buffer->actually_used = buffer->used; 267 | } 268 | 269 | freespace = buffer->datalen - buffer->used; 270 | 271 | /* 272 | if (buffer->data != buffer->head) { 273 | memmove(buffer->data, buffer->head, buffer->used); 274 | buffer->head = buffer->data; 275 | } 276 | */ 277 | 278 | if (freespace < datalen) { 279 | esl_size_t new_size, new_block_size; 280 | void *data1; 281 | 282 | new_size = buffer->datalen + datalen; 283 | new_block_size = buffer->datalen + buffer->blocksize; 284 | 285 | if (new_block_size > new_size) { 286 | new_size = new_block_size; 287 | } 288 | buffer->head = buffer->data; 289 | data1 = realloc(buffer->data, new_size); 290 | if (!data1) { 291 | return 0; 292 | } 293 | buffer->data = data1; 294 | buffer->head = buffer->data; 295 | buffer->datalen = new_size; 296 | } 297 | 298 | 299 | freespace = buffer->datalen - buffer->used; 300 | 301 | if (freespace < datalen) { 302 | return 0; 303 | } else { 304 | memcpy(buffer->head + buffer->used, data, datalen); 305 | buffer->used += datalen; 306 | buffer->actually_used += datalen; 307 | } 308 | /* if (buffer->id == 4) printf("%u i %d = %d\n", buffer->id, (unsigned)datalen, (unsigned)buffer->used); */ 309 | 310 | return buffer->used; 311 | } 312 | 313 | ESL_DECLARE(void) esl_buffer_zero(esl_buffer_t *buffer) 314 | { 315 | esl_assert(buffer != NULL); 316 | esl_assert(buffer->data != NULL); 317 | 318 | buffer->used = 0; 319 | buffer->actually_used = 0; 320 | buffer->head = buffer->data; 321 | } 322 | 323 | ESL_DECLARE(esl_size_t) esl_buffer_zwrite(esl_buffer_t *buffer, const void *data, esl_size_t datalen) 324 | { 325 | esl_size_t w; 326 | 327 | if (!(w = esl_buffer_write(buffer, data, datalen))) { 328 | esl_buffer_zero(buffer); 329 | return esl_buffer_write(buffer, data, datalen); 330 | } 331 | 332 | return w; 333 | } 334 | 335 | ESL_DECLARE(void) esl_buffer_destroy(esl_buffer_t **buffer) 336 | { 337 | if (*buffer) { 338 | free((*buffer)->data); 339 | (*buffer)->data = NULL; 340 | free(*buffer); 341 | } 342 | 343 | *buffer = NULL; 344 | } 345 | 346 | /* For Emacs: 347 | * Local Variables: 348 | * mode:c 349 | * indent-tabs-mode:t 350 | * tab-width:4 351 | * c-basic-offset:4 352 | * End: 353 | * For VIM: 354 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 355 | */ 356 | -------------------------------------------------------------------------------- /include/esl_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef ESL_EVENT_H 35 | #define ESL_EVENT_H 36 | 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif /* defined(__cplusplus) */ 42 | 43 | typedef enum { 44 | ESL_STACK_BOTTOM, 45 | ESL_STACK_TOP, 46 | ESL_STACK_PUSH, 47 | ESL_STACK_UNSHIFT 48 | } esl_stack_t; 49 | 50 | typedef enum { 51 | ESL_EVENT_CUSTOM, 52 | ESL_EVENT_CLONE, 53 | ESL_EVENT_CHANNEL_CREATE, 54 | ESL_EVENT_CHANNEL_DESTROY, 55 | ESL_EVENT_CHANNEL_STATE, 56 | ESL_EVENT_CHANNEL_CALLSTATE, 57 | ESL_EVENT_CHANNEL_ANSWER, 58 | ESL_EVENT_CHANNEL_HANGUP, 59 | ESL_EVENT_CHANNEL_HANGUP_COMPLETE, 60 | ESL_EVENT_CHANNEL_EXECUTE, 61 | ESL_EVENT_CHANNEL_EXECUTE_COMPLETE, 62 | ESL_EVENT_CHANNEL_HOLD, 63 | ESL_EVENT_CHANNEL_UNHOLD, 64 | ESL_EVENT_CHANNEL_BRIDGE, 65 | ESL_EVENT_CHANNEL_UNBRIDGE, 66 | ESL_EVENT_CHANNEL_PROGRESS, 67 | ESL_EVENT_CHANNEL_PROGRESS_MEDIA, 68 | ESL_EVENT_CHANNEL_OUTGOING, 69 | ESL_EVENT_CHANNEL_PARK, 70 | ESL_EVENT_CHANNEL_UNPARK, 71 | ESL_EVENT_CHANNEL_APPLICATION, 72 | ESL_EVENT_CHANNEL_ORIGINATE, 73 | ESL_EVENT_CHANNEL_UUID, 74 | ESL_EVENT_API, 75 | ESL_EVENT_LOG, 76 | ESL_EVENT_INBOUND_CHAN, 77 | ESL_EVENT_OUTBOUND_CHAN, 78 | ESL_EVENT_STARTUP, 79 | ESL_EVENT_SHUTDOWN, 80 | ESL_EVENT_PUBLISH, 81 | ESL_EVENT_UNPUBLISH, 82 | ESL_EVENT_TALK, 83 | ESL_EVENT_NOTALK, 84 | ESL_EVENT_SESSION_CRASH, 85 | ESL_EVENT_MODULE_LOAD, 86 | ESL_EVENT_MODULE_UNLOAD, 87 | ESL_EVENT_DTMF, 88 | ESL_EVENT_MESSAGE, 89 | ESL_EVENT_PRESENCE_IN, 90 | ESL_EVENT_NOTIFY_IN, 91 | ESL_EVENT_PRESENCE_OUT, 92 | ESL_EVENT_PRESENCE_PROBE, 93 | ESL_EVENT_MESSAGE_WAITING, 94 | ESL_EVENT_MESSAGE_QUERY, 95 | ESL_EVENT_ROSTER, 96 | ESL_EVENT_CODEC, 97 | ESL_EVENT_BACKGROUND_JOB, 98 | ESL_EVENT_DETECTED_SPEECH, 99 | ESL_EVENT_DETECTED_TONE, 100 | ESL_EVENT_PRIVATE_COMMAND, 101 | ESL_EVENT_HEARTBEAT, 102 | ESL_EVENT_TRAP, 103 | ESL_EVENT_ADD_SCHEDULE, 104 | ESL_EVENT_DEL_SCHEDULE, 105 | ESL_EVENT_EXE_SCHEDULE, 106 | ESL_EVENT_RE_SCHEDULE, 107 | ESL_EVENT_RELOADXML, 108 | ESL_EVENT_NOTIFY, 109 | ESL_EVENT_PHONE_FEATURE, 110 | ESL_EVENT_PHONE_FEATURE_SUBSCRIBE, 111 | ESL_EVENT_SEND_MESSAGE, 112 | ESL_EVENT_RECV_MESSAGE, 113 | ESL_EVENT_REQUEST_PARAMS, 114 | ESL_EVENT_CHANNEL_DATA, 115 | ESL_EVENT_GENERAL, 116 | ESL_EVENT_COMMAND, 117 | ESL_EVENT_SESSION_HEARTBEAT, 118 | ESL_EVENT_CLIENT_DISCONNECTED, 119 | ESL_EVENT_SERVER_DISCONNECTED, 120 | ESL_EVENT_SEND_INFO, 121 | ESL_EVENT_RECV_INFO, 122 | ESL_EVENT_RECV_RTCP_MESSAGE, 123 | ESL_EVENT_CALL_SECURE, 124 | ESL_EVENT_NAT, 125 | ESL_EVENT_RECORD_START, 126 | ESL_EVENT_RECORD_STOP, 127 | ESL_EVENT_PLAYBACK_START, 128 | ESL_EVENT_PLAYBACK_STOP, 129 | ESL_EVENT_CALL_UPDATE, 130 | ESL_EVENT_FAILURE, 131 | ESL_EVENT_SOCKET_DATA, 132 | ESL_EVENT_MEDIA_BUG_START, 133 | ESL_EVENT_MEDIA_BUG_STOP, 134 | ESL_EVENT_CONFERENCE_DATA_QUERY, 135 | ESL_EVENT_CONFERENCE_DATA, 136 | ESL_EVENT_CALL_SETUP_REQ, 137 | ESL_EVENT_CALL_SETUP_RESULT, 138 | ESL_EVENT_CALL_DETAIL, 139 | ESL_EVENT_DEVICE_STATE, 140 | ESL_EVENT_ALL 141 | } esl_event_types_t; 142 | 143 | typedef enum { 144 | ESL_PRIORITY_NORMAL, 145 | ESL_PRIORITY_LOW, 146 | ESL_PRIORITY_HIGH 147 | } esl_priority_t; 148 | 149 | /*! \brief An event Header */ 150 | struct esl_event_header { 151 | /*! the header name */ 152 | char *name; 153 | /*! the header value */ 154 | char *value; 155 | /*! array space */ 156 | char **array; 157 | /*! array index */ 158 | int idx; 159 | /*! hash of the header name */ 160 | unsigned long hash; 161 | struct esl_event_header *next; 162 | }; 163 | 164 | 165 | /*! \brief Representation of an event */ 166 | struct esl_event { 167 | /*! the event id (descriptor) */ 168 | esl_event_types_t event_id; 169 | /*! the priority of the event */ 170 | esl_priority_t priority; 171 | /*! the owner of the event */ 172 | char *owner; 173 | /*! the subclass of the event */ 174 | char *subclass_name; 175 | /*! the event headers */ 176 | esl_event_header_t *headers; 177 | /*! the event headers tail pointer */ 178 | esl_event_header_t *last_header; 179 | /*! the body of the event */ 180 | char *body; 181 | /*! user data from the subclass provider */ 182 | void *bind_user_data; 183 | /*! user data from the event sender */ 184 | void *event_user_data; 185 | /*! unique key */ 186 | unsigned long key; 187 | struct esl_event *next; 188 | int flags; 189 | }; 190 | 191 | typedef enum { 192 | ESL_EF_UNIQ_HEADERS = (1 << 0) 193 | } esl_event_flag_t; 194 | 195 | 196 | #define ESL_EVENT_SUBCLASS_ANY NULL 197 | 198 | /*! 199 | \brief Create an event 200 | \param event a NULL pointer on which to create the event 201 | \param event_id the event id enumeration of the desired event 202 | \param subclass_name the subclass name for custom event (only valid when event_id is ESL_EVENT_CUSTOM) 203 | \return ESL_SUCCESS on success 204 | */ 205 | ESL_DECLARE(esl_status_t) esl_event_create_subclass(esl_event_t **event, esl_event_types_t event_id, const char *subclass_name); 206 | 207 | /*! 208 | \brief Set the priority of an event 209 | \param event the event to set the priority on 210 | \param priority the event priority 211 | \return ESL_SUCCESS 212 | */ 213 | ESL_DECLARE(esl_status_t) esl_event_set_priority(esl_event_t *event, esl_priority_t priority); 214 | 215 | /*! 216 | \brief Retrieve a header value from an event 217 | \param event the event to read the header from 218 | \param header_name the name of the header to read 219 | \return the value of the requested header 220 | */ 221 | 222 | 223 | ESL_DECLARE(esl_event_header_t *) esl_event_get_header_ptr(esl_event_t *event, const char *header_name); 224 | ESL_DECLARE(char *) esl_event_get_header_idx(esl_event_t *event, const char *header_name, int idx); 225 | #define esl_event_get_header(_e, _h) esl_event_get_header_idx(_e, _h, -1) 226 | 227 | /*! 228 | \brief Retrieve the body value from an event 229 | \param event the event to read the body from 230 | \return the value of the body or NULL 231 | */ 232 | ESL_DECLARE(char *)esl_event_get_body(esl_event_t *event); 233 | 234 | /*! 235 | \brief Add a header to an event 236 | \param event the event to add the header to 237 | \param stack the stack sense (stack it on the top or on the bottom) 238 | \param header_name the name of the header to add 239 | \param fmt the value of the header (varargs see standard sprintf family) 240 | \return ESL_SUCCESS if the header was added 241 | */ 242 | ESL_DECLARE(esl_status_t) esl_event_add_header(esl_event_t *event, esl_stack_t stack, 243 | const char *header_name, const char *fmt, ...); //PRINTF_FUNCTION(4, 5); 244 | 245 | ESL_DECLARE(int) esl_event_add_array(esl_event_t *event, const char *var, const char *val); 246 | 247 | /*! 248 | \brief Add a string header to an event 249 | \param event the event to add the header to 250 | \param stack the stack sense (stack it on the top or on the bottom) 251 | \param header_name the name of the header to add 252 | \param data the value of the header 253 | \return ESL_SUCCESS if the header was added 254 | */ 255 | ESL_DECLARE(esl_status_t) esl_event_add_header_string(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *data); 256 | 257 | ESL_DECLARE(esl_status_t) esl_event_del_header_val(esl_event_t *event, const char *header_name, const char *var); 258 | #define esl_event_del_header(_e, _h) esl_event_del_header_val(_e, _h, NULL) 259 | 260 | /*! 261 | \brief Destroy an event 262 | \param event pointer to the pointer to event to destroy 263 | */ 264 | ESL_DECLARE(void) esl_event_destroy(esl_event_t **event); 265 | #define esl_event_safe_destroy(_event) if (_event) esl_event_destroy(_event) 266 | 267 | /*! 268 | \brief Duplicate an event 269 | \param event a NULL pointer on which to duplicate the event 270 | \param todup an event to duplicate 271 | \return ESL_SUCCESS if the event was duplicated 272 | */ 273 | ESL_DECLARE(esl_status_t) esl_event_dup(esl_event_t **event, esl_event_t *todup); 274 | ESL_DECLARE(void) esl_event_merge(esl_event_t *event, esl_event_t *tomerge); 275 | 276 | /*! 277 | \brief Render the name of an event id enumeration 278 | \param event the event id to render the name of 279 | \return the rendered name 280 | */ 281 | ESL_DECLARE(const char *)esl_event_name(esl_event_types_t event); 282 | 283 | /*! 284 | \brief return the event id that matches a given event name 285 | \param name the name of the event 286 | \param type the event id to return 287 | \return ESL_SUCCESS if there was a match 288 | */ 289 | ESL_DECLARE(esl_status_t) esl_name_event(const char *name, esl_event_types_t *type); 290 | 291 | /*! 292 | \brief Render a string representation of an event sutable for printing or network transport 293 | \param event the event to render 294 | \param str a string pointer to point at the allocated data 295 | \param encode url encode the headers 296 | \return ESL_SUCCESS if the operation was successful 297 | \note you must free the resulting string when you are finished with it 298 | */ 299 | ESL_DECLARE(esl_status_t) esl_event_serialize(esl_event_t *event, char **str, esl_bool_t encode); 300 | ESL_DECLARE(esl_status_t) esl_event_serialize_json(esl_event_t *event, char **str); 301 | ESL_DECLARE(esl_status_t) esl_event_create_json(esl_event_t **event, const char *json); 302 | /*! 303 | \brief Add a body to an event 304 | \param event the event to add to body to 305 | \param fmt optional body of the event (varargs see standard sprintf family) 306 | \return ESL_SUCCESS if the body was added to the event 307 | \note the body parameter can be shadowed by the esl_event_reserve_subclass_detailed function 308 | */ 309 | ESL_DECLARE(esl_status_t) esl_event_add_body(esl_event_t *event, const char *fmt, ...); 310 | ESL_DECLARE(esl_status_t) esl_event_set_body(esl_event_t *event, const char *body); 311 | 312 | /*! 313 | \brief Create a new event assuming it will not be custom event and therefore hiding the unused parameters 314 | \param event a NULL pointer on which to create the event 315 | \param id the event id enumeration of the desired event 316 | \return ESL_SUCCESS on success 317 | */ 318 | #define esl_event_create(event, id) esl_event_create_subclass(event, id, ESL_EVENT_SUBCLASS_ANY) 319 | 320 | ESL_DECLARE(const char *)esl_priority_name(esl_priority_t priority); 321 | 322 | ///\} 323 | 324 | #ifdef __cplusplus 325 | } 326 | #endif /* defined(__cplusplus) */ 327 | 328 | #endif /* defined(ESL_EVENT_H) */ 329 | 330 | /* For Emacs: 331 | * Local Variables: 332 | * mode:c 333 | * indent-tabs-mode:t 334 | * tab-width:4 335 | * c-basic-offset:4 336 | * End: 337 | * For VIM: 338 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 339 | */ 340 | -------------------------------------------------------------------------------- /src/esl_oop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define connection_construct_common() memset(&handle, 0, sizeof(handle)) 5 | #define event_construct_common() event = NULL; serialized_string = NULL; mine = 0; hp = NULL 6 | 7 | void eslSetLogLevel(int level) 8 | { 9 | esl_global_set_default_logger(level); 10 | } 11 | 12 | ESLconnection::ESLconnection(const char *host, const int port, const char *password) 13 | { 14 | connection_construct_common(); 15 | 16 | esl_connect(&handle, host, port, NULL, password); 17 | } 18 | 19 | ESLconnection::ESLconnection(const char *host, const int port, const char *user, const char *password) 20 | { 21 | connection_construct_common(); 22 | 23 | esl_connect(&handle, host, port, user, password); 24 | } 25 | 26 | ESLconnection::ESLconnection(const char *host, const char *port, const char *password) 27 | { 28 | connection_construct_common(); 29 | if (port == NULL) return; 30 | int x_port = atoi(port); 31 | 32 | esl_connect(&handle, host, x_port, NULL, password); 33 | } 34 | 35 | ESLconnection::ESLconnection(const char *host, const char *port, const char *user, const char *password) 36 | { 37 | connection_construct_common(); 38 | if (port == NULL) return; 39 | int x_port = atoi(port); 40 | 41 | esl_connect(&handle, host, x_port, user, password); 42 | } 43 | 44 | 45 | ESLconnection::ESLconnection(int socket) 46 | { 47 | connection_construct_common(); 48 | esl_attach_handle(&handle, (esl_socket_t)socket, NULL); 49 | } 50 | 51 | ESLconnection::~ESLconnection() 52 | { 53 | if (!handle.destroyed) { 54 | esl_disconnect(&handle); 55 | } 56 | } 57 | 58 | int ESLconnection::socketDescriptor() 59 | { 60 | if (handle.connected) { 61 | return (int) handle.sock; 62 | } 63 | 64 | return -1; 65 | } 66 | 67 | 68 | int ESLconnection::disconnect() 69 | { 70 | if (!handle.destroyed) { 71 | return esl_disconnect(&handle); 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | int ESLconnection::connected() 78 | { 79 | return handle.connected; 80 | } 81 | 82 | int ESLconnection::send(const char *cmd) 83 | { 84 | return esl_send(&handle, cmd); 85 | } 86 | 87 | ESLevent *ESLconnection::sendRecv(const char *cmd) 88 | { 89 | if (esl_send_recv(&handle, cmd) == ESL_SUCCESS) { 90 | esl_event_t *event; 91 | esl_event_dup(&event, handle.last_sr_event); 92 | return new ESLevent(event, 1); 93 | } 94 | 95 | return NULL; 96 | } 97 | 98 | ESLevent *ESLconnection::api(const char *cmd, const char *arg) 99 | { 100 | size_t len; 101 | char *cmd_buf; 102 | ESLevent *event; 103 | 104 | if (!cmd) { 105 | return NULL; 106 | } 107 | 108 | len = strlen(cmd) + (arg ? strlen(arg) : 0) + 10; 109 | 110 | cmd_buf = (char *) malloc(len + 1); 111 | assert(cmd_buf); 112 | 113 | snprintf(cmd_buf, len, "api %s %s", cmd, arg ? arg : ""); 114 | *(cmd_buf + (len)) = '\0'; 115 | 116 | 117 | event = sendRecv(cmd_buf); 118 | free(cmd_buf); 119 | 120 | return event; 121 | } 122 | 123 | ESLevent *ESLconnection::bgapi(const char *cmd, const char *arg, const char *job_uuid) 124 | { 125 | size_t len; 126 | char *cmd_buf; 127 | ESLevent *event; 128 | 129 | if (!cmd) { 130 | return NULL; 131 | } 132 | 133 | len = strlen(cmd) + (arg ? strlen(arg) : 0) + (job_uuid ? strlen(job_uuid) + 12 : 0) + 10; 134 | 135 | cmd_buf = (char *) malloc(len + 1); 136 | assert(cmd_buf); 137 | 138 | if (job_uuid) { 139 | snprintf(cmd_buf, len, "bgapi %s%s%s\nJob-UUID: %s", cmd, arg ? " " : "", arg ? arg : "", job_uuid); 140 | } else { 141 | snprintf(cmd_buf, len, "bgapi %s%s%s", cmd, arg ? " " : "", arg ? arg : ""); 142 | } 143 | 144 | *(cmd_buf + (len)) = '\0'; 145 | 146 | event = sendRecv(cmd_buf); 147 | free(cmd_buf); 148 | 149 | return event; 150 | } 151 | 152 | ESLevent *ESLconnection::getInfo() 153 | { 154 | if (handle.connected && handle.info_event) { 155 | esl_event_t *event; 156 | esl_event_dup(&event, handle.info_event); 157 | return new ESLevent(event, 1); 158 | } 159 | 160 | return NULL; 161 | } 162 | 163 | int ESLconnection::setAsyncExecute(const char *val) 164 | { 165 | if (val) { 166 | handle.async_execute = esl_true(val); 167 | } 168 | return handle.async_execute; 169 | } 170 | 171 | int ESLconnection::setEventLock(const char *val) 172 | { 173 | if (val) { 174 | handle.event_lock = esl_true(val); 175 | } 176 | return handle.event_lock; 177 | } 178 | 179 | ESLevent *ESLconnection::execute(const char *app, const char *arg, const char *uuid) 180 | { 181 | if (esl_execute(&handle, app, arg, uuid) == ESL_SUCCESS) { 182 | esl_event_t *event; 183 | esl_event_dup(&event, handle.last_sr_event); 184 | return new ESLevent(event, 1); 185 | } 186 | 187 | return NULL; 188 | } 189 | 190 | 191 | ESLevent *ESLconnection::executeAsync(const char *app, const char *arg, const char *uuid) 192 | { 193 | int async = handle.async_execute; 194 | int r; 195 | 196 | handle.async_execute = 1; 197 | r = esl_execute(&handle, app, arg, uuid); 198 | handle.async_execute = async; 199 | 200 | if (r == ESL_SUCCESS) { 201 | esl_event_t *event; 202 | esl_event_dup(&event, handle.last_sr_event); 203 | return new ESLevent(event, 1); 204 | } 205 | 206 | return NULL; 207 | } 208 | 209 | ESLevent *ESLconnection::sendEvent(ESLevent *send_me) 210 | { 211 | if (esl_sendevent(&handle, send_me->event) == ESL_SUCCESS) { 212 | esl_event_t *e = handle.last_ievent ? handle.last_ievent : handle.last_event; 213 | if (e) { 214 | esl_event_t *event; 215 | esl_event_dup(&event, e); 216 | return new ESLevent(event, 1); 217 | } 218 | } 219 | 220 | return new ESLevent("server_disconnected"); 221 | } 222 | 223 | int ESLconnection::sendMSG(ESLevent *send_me, const char *uuid) 224 | { 225 | if (esl_sendmsg(&handle, send_me->event, uuid) == ESL_SUCCESS) { 226 | return 0; 227 | } 228 | 229 | return 1; 230 | } 231 | 232 | ESLevent *ESLconnection::recvEvent() 233 | { 234 | if (esl_recv_event(&handle, 1, NULL) == ESL_SUCCESS) { 235 | esl_event_t *e = handle.last_ievent ? handle.last_ievent : handle.last_event; 236 | if (e) { 237 | esl_event_t *event; 238 | esl_event_dup(&event, e); 239 | return new ESLevent(event, 1); 240 | } 241 | } 242 | 243 | return new ESLevent("server_disconnected"); 244 | } 245 | 246 | ESLevent *ESLconnection::recvEventTimed(int ms) 247 | { 248 | 249 | if (esl_recv_event_timed(&handle, ms, 1, NULL) == ESL_SUCCESS) { 250 | esl_event_t *e = handle.last_ievent ? handle.last_ievent : handle.last_event; 251 | if (e) { 252 | esl_event_t *event; 253 | esl_event_dup(&event, e); 254 | return new ESLevent(event, 1); 255 | } 256 | } 257 | 258 | return NULL; 259 | } 260 | 261 | ESLevent *ESLconnection::filter(const char *header, const char *value) 262 | { 263 | esl_status_t status = esl_filter(&handle, header, value); 264 | 265 | if (status == ESL_SUCCESS && handle.last_sr_event) { 266 | esl_event_t *event; 267 | esl_event_dup(&event, handle.last_sr_event); 268 | return new ESLevent(event, 1); 269 | } 270 | 271 | return NULL; 272 | 273 | } 274 | 275 | int ESLconnection::events(const char *etype, const char *value) 276 | { 277 | esl_event_type_t type_id = ESL_EVENT_TYPE_PLAIN; 278 | 279 | if (!strcmp(etype, "xml")) { 280 | type_id = ESL_EVENT_TYPE_XML; 281 | } else if (!strcmp(etype, "json")) { 282 | type_id = ESL_EVENT_TYPE_JSON; 283 | } 284 | 285 | return esl_events(&handle, type_id, value); 286 | } 287 | 288 | // ESLevent 289 | /////////////////////////////////////////////////////////////////////// 290 | 291 | ESLevent::ESLevent(const char *type, const char *subclass_name) 292 | { 293 | esl_event_types_t event_id; 294 | 295 | event_construct_common(); 296 | 297 | if (!strcasecmp(type, "json") && !esl_strlen_zero(subclass_name)) { 298 | if (esl_event_create_json(&event, subclass_name) != ESL_SUCCESS) { 299 | return; 300 | 301 | } 302 | event_id = event->event_id; 303 | } else { 304 | 305 | if (esl_name_event(type, &event_id) != ESL_SUCCESS) { 306 | event_id = ESL_EVENT_MESSAGE; 307 | } 308 | 309 | if (!esl_strlen_zero(subclass_name) && event_id != ESL_EVENT_CUSTOM) { 310 | esl_log(ESL_LOG_WARNING, "Changing event type to custom because you specified a subclass name!\n"); 311 | event_id = ESL_EVENT_CUSTOM; 312 | } 313 | 314 | if (esl_event_create_subclass(&event, event_id, subclass_name) != ESL_SUCCESS) { 315 | esl_log(ESL_LOG_ERROR, "Failed to create event!\n"); 316 | event = NULL; 317 | } 318 | } 319 | 320 | serialized_string = NULL; 321 | mine = 1; 322 | 323 | } 324 | 325 | ESLevent::ESLevent(esl_event_t *wrap_me, int free_me) 326 | { 327 | event_construct_common(); 328 | event = wrap_me; 329 | mine = free_me; 330 | serialized_string = NULL; 331 | } 332 | 333 | ESLevent::ESLevent(ESLevent *me) 334 | { 335 | /* workaround for silly php thing */ 336 | event = me->event; 337 | mine = me->mine; 338 | serialized_string = NULL; 339 | me->event = NULL; 340 | me->mine = 0; 341 | esl_safe_free(me->serialized_string); 342 | } 343 | 344 | ESLevent::~ESLevent() 345 | { 346 | 347 | if (serialized_string) { 348 | free(serialized_string); 349 | } 350 | 351 | if (event && mine) { 352 | esl_event_destroy(&event); 353 | } 354 | } 355 | 356 | const char *ESLevent::nextHeader(void) 357 | { 358 | const char *name = NULL; 359 | 360 | if (hp) { 361 | name = hp->name; 362 | hp = hp->next; 363 | } 364 | 365 | return name; 366 | } 367 | 368 | const char *ESLevent::firstHeader(void) 369 | { 370 | if (event) { 371 | hp = event->headers; 372 | } 373 | 374 | return nextHeader(); 375 | } 376 | 377 | const char *ESLevent::serialize(const char *format) 378 | { 379 | this_check(""); 380 | 381 | esl_safe_free(serialized_string); 382 | 383 | if (format == NULL) { 384 | format = "text"; 385 | } 386 | 387 | if (!event) { 388 | return ""; 389 | } 390 | 391 | if (format && !strcasecmp(format, "json")) { 392 | esl_event_serialize_json(event, &serialized_string); 393 | return serialized_string; 394 | } 395 | 396 | if (esl_event_serialize(event, &serialized_string, ESL_TRUE) == ESL_SUCCESS) { 397 | return serialized_string; 398 | } 399 | 400 | return ""; 401 | 402 | } 403 | 404 | bool ESLevent::setPriority(esl_priority_t priority) 405 | { 406 | this_check(false); 407 | 408 | if (event) { 409 | esl_event_set_priority(event, priority); 410 | return true; 411 | } else { 412 | esl_log(ESL_LOG_ERROR, "Trying to setPriority an event that does not exist!\n"); 413 | } 414 | return false; 415 | } 416 | 417 | const char *ESLevent::getHeader(const char *header_name, int idx) 418 | { 419 | this_check(""); 420 | 421 | if (event) { 422 | return esl_event_get_header_idx(event, header_name, idx); 423 | } else { 424 | esl_log(ESL_LOG_ERROR, "Trying to getHeader an event that does not exist!\n"); 425 | } 426 | return NULL; 427 | } 428 | 429 | bool ESLevent::addHeader(const char *header_name, const char *value) 430 | { 431 | this_check(false); 432 | 433 | if (event) { 434 | return esl_event_add_header_string(event, ESL_STACK_BOTTOM, header_name, value) == ESL_SUCCESS ? true : false; 435 | } else { 436 | esl_log(ESL_LOG_ERROR, "Trying to addHeader an event that does not exist!\n"); 437 | } 438 | 439 | return false; 440 | } 441 | 442 | bool ESLevent::pushHeader(const char *header_name, const char *value) 443 | { 444 | this_check(false); 445 | 446 | if (event) { 447 | return esl_event_add_header_string(event, ESL_STACK_PUSH, header_name, value) == ESL_SUCCESS ? true : false; 448 | } else { 449 | esl_log(ESL_LOG_ERROR, "Trying to addHeader an event that does not exist!\n"); 450 | } 451 | 452 | return false; 453 | } 454 | 455 | bool ESLevent::unshiftHeader(const char *header_name, const char *value) 456 | { 457 | this_check(false); 458 | 459 | if (event) { 460 | return esl_event_add_header_string(event, ESL_STACK_UNSHIFT, header_name, value) == ESL_SUCCESS ? true : false; 461 | } else { 462 | esl_log(ESL_LOG_ERROR, "Trying to addHeader an event that does not exist!\n"); 463 | } 464 | 465 | return false; 466 | } 467 | 468 | bool ESLevent::delHeader(const char *header_name) 469 | { 470 | this_check(false); 471 | 472 | if (event) { 473 | return esl_event_del_header(event, header_name) == ESL_SUCCESS ? true : false; 474 | } else { 475 | esl_log(ESL_LOG_ERROR, "Trying to delHeader an event that does not exist!\n"); 476 | } 477 | 478 | return false; 479 | } 480 | 481 | 482 | bool ESLevent::addBody(const char *value) 483 | { 484 | this_check(false); 485 | 486 | if (event) { 487 | return esl_event_add_body(event, "%s", value) == ESL_SUCCESS ? true : false; 488 | } else { 489 | esl_log(ESL_LOG_ERROR, "Trying to addBody an event that does not exist!\n"); 490 | } 491 | 492 | return false; 493 | } 494 | 495 | char *ESLevent::getBody(void) 496 | { 497 | 498 | this_check((char *)""); 499 | 500 | if (event) { 501 | return esl_event_get_body(event); 502 | } else { 503 | esl_log(ESL_LOG_ERROR, "Trying to getBody an event that does not exist!\n"); 504 | } 505 | 506 | return NULL; 507 | } 508 | 509 | const char *ESLevent::getType(void) 510 | { 511 | this_check(""); 512 | 513 | if (event) { 514 | return esl_event_name(event->event_id); 515 | } else { 516 | esl_log(ESL_LOG_ERROR, "Trying to getType an event that does not exist!\n"); 517 | } 518 | 519 | return (char *) "invalid"; 520 | } 521 | 522 | /* For Emacs: 523 | * Local Variables: 524 | * mode:c++ 525 | * indent-tabs-mode:t 526 | * tab-width:4 527 | * c-basic-offset:4 528 | * End: 529 | * For VIM: 530 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 531 | */ 532 | -------------------------------------------------------------------------------- /include/esl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _ESL_H_ 35 | #define _ESL_H_ 36 | 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif /* defined(__cplusplus) */ 42 | 43 | #define esl_copy_string(_x, _y, _z) strncpy(_x, _y, _z - 1) 44 | #define esl_set_string(_x, _y) esl_copy_string(_x, _y, sizeof(_x)) 45 | #define ESL_VA_NONE "%s", "" 46 | 47 | typedef struct esl_event_header esl_event_header_t; 48 | typedef struct esl_event esl_event_t; 49 | 50 | typedef enum { 51 | ESL_POLL_READ = (1 << 0), 52 | ESL_POLL_WRITE = (1 << 1), 53 | ESL_POLL_ERROR = (1 << 2) 54 | } esl_poll_t; 55 | 56 | typedef enum { 57 | ESL_EVENT_TYPE_PLAIN, 58 | ESL_EVENT_TYPE_XML, 59 | ESL_EVENT_TYPE_JSON 60 | } esl_event_type_t; 61 | 62 | #ifdef WIN32 63 | #define ESL_SEQ_FWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY 64 | #define ESL_SEQ_BWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE 65 | #define ESL_SEQ_FRED FOREGROUND_RED | FOREGROUND_INTENSITY 66 | #define ESL_SEQ_BRED FOREGROUND_RED 67 | #define ESL_SEQ_FMAGEN FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY 68 | #define ESL_SEQ_BMAGEN FOREGROUND_BLUE | FOREGROUND_RED 69 | #define ESL_SEQ_FCYAN FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY 70 | #define ESL_SEQ_BCYAN FOREGROUND_GREEN | FOREGROUND_BLUE 71 | #define ESL_SEQ_FGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY 72 | #define ESL_SEQ_BGREEN FOREGROUND_GREEN 73 | #define ESL_SEQ_FYELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY 74 | #define ESL_SEQ_BYELLOW FOREGROUND_RED | FOREGROUND_GREEN 75 | #define ESL_SEQ_DEFAULT_COLOR ESL_SEQ_FWHITE 76 | #define ESL_SEQ_FBLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY 77 | #define ESL_SEQ_BBLUE FOREGROUND_BLUE 78 | #define ESL_SEQ_FBLACK 0 | FOREGROUND_INTENSITY 79 | #define ESL_SEQ_BBLACK 0 80 | #else 81 | #define ESL_SEQ_ESC "\033[" 82 | /* Ansi Control character suffixes */ 83 | #define ESL_SEQ_HOME_CHAR 'H' 84 | #define ESL_SEQ_HOME_CHAR_STR "H" 85 | #define ESL_SEQ_CLEARLINE_CHAR '1' 86 | #define ESL_SEQ_CLEARLINE_CHAR_STR "1" 87 | #define ESL_SEQ_CLEARLINEEND_CHAR "K" 88 | #define ESL_SEQ_CLEARSCR_CHAR0 '2' 89 | #define ESL_SEQ_CLEARSCR_CHAR1 'J' 90 | #define ESL_SEQ_CLEARSCR_CHAR "2J" 91 | #define ESL_SEQ_DEFAULT_COLOR ESL_SEQ_ESC ESL_SEQ_END_COLOR /* Reset to Default fg/bg color */ 92 | #define ESL_SEQ_AND_COLOR ";" /* To add multiple color definitions */ 93 | #define ESL_SEQ_END_COLOR "m" /* To end color definitions */ 94 | /* Foreground colors values */ 95 | #define ESL_SEQ_F_BLACK "30" 96 | #define ESL_SEQ_F_RED "31" 97 | #define ESL_SEQ_F_GREEN "32" 98 | #define ESL_SEQ_F_YELLOW "33" 99 | #define ESL_SEQ_F_BLUE "34" 100 | #define ESL_SEQ_F_MAGEN "35" 101 | #define ESL_SEQ_F_CYAN "36" 102 | #define ESL_SEQ_F_WHITE "37" 103 | /* Background colors values */ 104 | #define ESL_SEQ_B_BLACK "40" 105 | #define ESL_SEQ_B_RED "41" 106 | #define ESL_SEQ_B_GREEN "42" 107 | #define ESL_SEQ_B_YELLOW "43" 108 | #define ESL_SEQ_B_BLUE "44" 109 | #define ESL_SEQ_B_MAGEN "45" 110 | #define ESL_SEQ_B_CYAN "46" 111 | #define ESL_SEQ_B_WHITE "47" 112 | /* Preset escape sequences - Change foreground colors only */ 113 | #define ESL_SEQ_FBLACK ESL_SEQ_ESC ESL_SEQ_F_BLACK ESL_SEQ_END_COLOR 114 | #define ESL_SEQ_FRED ESL_SEQ_ESC ESL_SEQ_F_RED ESL_SEQ_END_COLOR 115 | #define ESL_SEQ_FGREEN ESL_SEQ_ESC ESL_SEQ_F_GREEN ESL_SEQ_END_COLOR 116 | #define ESL_SEQ_FYELLOW ESL_SEQ_ESC ESL_SEQ_F_YELLOW ESL_SEQ_END_COLOR 117 | #define ESL_SEQ_FBLUE ESL_SEQ_ESC ESL_SEQ_F_BLUE ESL_SEQ_END_COLOR 118 | #define ESL_SEQ_FMAGEN ESL_SEQ_ESC ESL_SEQ_F_MAGEN ESL_SEQ_END_COLOR 119 | #define ESL_SEQ_FCYAN ESL_SEQ_ESC ESL_SEQ_F_CYAN ESL_SEQ_END_COLOR 120 | #define ESL_SEQ_FWHITE ESL_SEQ_ESC ESL_SEQ_F_WHITE ESL_SEQ_END_COLOR 121 | #define ESL_SEQ_BBLACK ESL_SEQ_ESC ESL_SEQ_B_BLACK ESL_SEQ_END_COLOR 122 | #define ESL_SEQ_BRED ESL_SEQ_ESC ESL_SEQ_B_RED ESL_SEQ_END_COLOR 123 | #define ESL_SEQ_BGREEN ESL_SEQ_ESC ESL_SEQ_B_GREEN ESL_SEQ_END_COLOR 124 | #define ESL_SEQ_BYELLOW ESL_SEQ_ESC ESL_SEQ_B_YELLOW ESL_SEQ_END_COLOR 125 | #define ESL_SEQ_BBLUE ESL_SEQ_ESC ESL_SEQ_B_BLUE ESL_SEQ_END_COLOR 126 | #define ESL_SEQ_BMAGEN ESL_SEQ_ESC ESL_SEQ_B_MAGEN ESL_SEQ_END_COLOR 127 | #define ESL_SEQ_BCYAN ESL_SEQ_ESC ESL_SEQ_B_CYAN ESL_SEQ_END_COLOR 128 | #define ESL_SEQ_BWHITE ESL_SEQ_ESC ESL_SEQ_B_WHITE ESL_SEQ_END_COLOR 129 | /* Preset escape sequences */ 130 | #define ESL_SEQ_HOME ESL_SEQ_ESC ESL_SEQ_HOME_CHAR_STR 131 | #define ESL_SEQ_CLEARLINE ESL_SEQ_ESC ESL_SEQ_CLEARLINE_CHAR_STR 132 | #define ESL_SEQ_CLEARLINEEND ESL_SEQ_ESC ESL_SEQ_CLEARLINEEND_CHAR 133 | #define ESL_SEQ_CLEARSCR ESL_SEQ_ESC ESL_SEQ_CLEARSCR_CHAR ESL_SEQ_HOME 134 | #endif 135 | 136 | #if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) 137 | #define _XOPEN_SOURCE 600 138 | #endif 139 | 140 | #ifndef HAVE_STRINGS_H 141 | #define HAVE_STRINGS_H 1 142 | #endif 143 | #ifndef HAVE_SYS_SOCKET_H 144 | #define HAVE_SYS_SOCKET_H 1 145 | #endif 146 | 147 | #ifndef __WINDOWS__ 148 | #if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) 149 | #define __WINDOWS__ 150 | #endif 151 | #endif 152 | 153 | #ifdef _MSC_VER 154 | #ifndef __inline__ 155 | #define __inline__ __inline 156 | #endif 157 | #if (_MSC_VER >= 1400) /* VC8+ */ 158 | #ifndef _CRT_SECURE_NO_DEPRECATE 159 | #define _CRT_SECURE_NO_DEPRECATE 160 | #endif 161 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 162 | #define _CRT_NONSTDC_NO_DEPRECATE 163 | #endif 164 | #endif 165 | #ifndef strcasecmp 166 | #define strcasecmp(s1, s2) _stricmp(s1, s2) 167 | #endif 168 | #ifndef strncasecmp 169 | #define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) 170 | #endif 171 | #ifndef snprintf 172 | #define snprintf _snprintf 173 | #endif 174 | #ifndef S_IRUSR 175 | #define S_IRUSR _S_IREAD 176 | #endif 177 | #ifndef S_IWUSR 178 | #define S_IWUSR _S_IWRITE 179 | #endif 180 | #undef HAVE_STRINGS_H 181 | #undef HAVE_SYS_SOCKET_H 182 | #endif 183 | 184 | #include 185 | #ifndef WIN32 186 | #include 187 | #endif 188 | 189 | #include 190 | #include 191 | #include 192 | #include 193 | #ifndef WIN32 194 | #include 195 | #include 196 | #include 197 | #include 198 | #include 199 | #include 200 | #endif 201 | 202 | #ifdef HAVE_STRINGS_H 203 | #include 204 | #endif 205 | #include 206 | 207 | #if (_MSC_VER >= 1400) // VC8+ 208 | #define esl_assert(expr) assert(expr);__analysis_assume( expr ) 209 | #endif 210 | 211 | #ifndef esl_assert 212 | #define esl_assert(_x) assert(_x) 213 | #endif 214 | 215 | #define esl_safe_free(_x) if (_x) free(_x); _x = NULL 216 | #define esl_strlen_zero(s) (!s || *(s) == '\0') 217 | #define esl_strlen_zero_buf(s) (*(s) == '\0') 218 | #define end_of(_s) *(*_s == '\0' ? _s : _s + strlen(_s) - 1) 219 | 220 | #ifdef WIN32 221 | #include 222 | #include 223 | typedef SOCKET esl_socket_t; 224 | #if !defined(_STDINT) && !defined(uint32_t) 225 | typedef unsigned __int64 uint64_t; 226 | typedef unsigned __int32 uint32_t; 227 | typedef unsigned __int16 uint16_t; 228 | typedef unsigned __int8 uint8_t; 229 | typedef __int64 int64_t; 230 | typedef __int32 int32_t; 231 | typedef __int16 int16_t; 232 | typedef __int8 int8_t; 233 | #endif 234 | typedef intptr_t esl_ssize_t; 235 | typedef int esl_filehandle_t; 236 | #define ESL_SOCK_INVALID INVALID_SOCKET 237 | #define strerror_r(num, buf, size) strerror_s(buf, size, num) 238 | #if defined(ESL_DECLARE_STATIC) 239 | #define ESL_DECLARE(type) type __stdcall 240 | #define ESL_DECLARE_NONSTD(type) type __cdecl 241 | #define ESL_DECLARE_DATA 242 | #elif defined(ESL_EXPORTS) 243 | #define ESL_DECLARE(type) __declspec(dllexport) type __stdcall 244 | #define ESL_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl 245 | #define ESL_DECLARE_DATA __declspec(dllexport) 246 | #else 247 | #define ESL_DECLARE(type) __declspec(dllimport) type __stdcall 248 | #define ESL_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl 249 | #define ESL_DECLARE_DATA __declspec(dllimport) 250 | #endif 251 | #else 252 | #define ESL_DECLARE(type) type 253 | #define ESL_DECLARE_NONSTD(type) type 254 | #define ESL_DECLARE_DATA 255 | #include 256 | #include 257 | #include 258 | #include 259 | #include 260 | #include 261 | #include 262 | #define ESL_SOCK_INVALID -1 263 | typedef int esl_socket_t; 264 | typedef ssize_t esl_ssize_t; 265 | typedef int esl_filehandle_t; 266 | #endif 267 | 268 | #include "math.h" 269 | #include "esl_json.h" 270 | 271 | typedef int16_t esl_port_t; 272 | typedef size_t esl_size_t; 273 | 274 | typedef enum { 275 | ESL_SUCCESS, 276 | ESL_FAIL, 277 | ESL_BREAK, 278 | ESL_DISCONNECTED, 279 | ESL_GENERR 280 | } esl_status_t; 281 | 282 | #define BUF_CHUNK 65536 * 50 283 | #define BUF_START 65536 * 100 284 | 285 | #include 286 | #include 287 | 288 | /*! \brief A handle that will hold the socket information and 289 | different events received. */ 290 | typedef struct { 291 | struct sockaddr_storage sockaddr; 292 | struct hostent hostent; 293 | char hostbuf[256]; 294 | esl_socket_t sock; 295 | /*! In case of socket error, this will hold the error description as reported by the OS */ 296 | char err[256]; 297 | /*! The error number reported by the OS */ 298 | int errnum; 299 | /*! The inner contents received by the socket. Used only internally. */ 300 | esl_buffer_t *packet_buf; 301 | char socket_buf[65536]; 302 | /*! Last command reply */ 303 | char last_reply[1024]; 304 | /*! Last command reply when called with esl_send_recv */ 305 | char last_sr_reply[1024]; 306 | /*! Last event received. Only populated when **save_event is NULL */ 307 | esl_event_t *last_event; 308 | /*! Last event received when called by esl_send_recv */ 309 | esl_event_t *last_sr_event; 310 | /*! This will hold already processed events queued by esl_recv_event */ 311 | esl_event_t *race_event; 312 | /*! Events that have content-type == text/plain and a body */ 313 | esl_event_t *last_ievent; 314 | /*! For outbound socket. Will hold reply information when connect\n\n is sent */ 315 | esl_event_t *info_event; 316 | /*! Socket is connected or not */ 317 | int connected; 318 | struct sockaddr_in addr; 319 | /*! Internal mutex */ 320 | esl_mutex_t *mutex; 321 | int async_execute; 322 | int event_lock; 323 | int destroyed; 324 | } esl_handle_t; 325 | 326 | #define esl_test_flag(obj, flag) ((obj)->flags & flag) 327 | #define esl_set_flag(obj, flag) (obj)->flags |= (flag) 328 | #define esl_clear_flag(obj, flag) (obj)->flags &= ~(flag) 329 | 330 | /*! \brief Used internally for truth test */ 331 | typedef enum { 332 | ESL_TRUE = 1, 333 | ESL_FALSE = 0 334 | } esl_bool_t; 335 | 336 | #ifndef __FUNCTION__ 337 | #define __FUNCTION__ (const char *)__func__ 338 | #endif 339 | 340 | #define ESL_PRE __FILE__, __FUNCTION__, __LINE__ 341 | #define ESL_LOG_LEVEL_DEBUG 7 342 | #define ESL_LOG_LEVEL_INFO 6 343 | #define ESL_LOG_LEVEL_NOTICE 5 344 | #define ESL_LOG_LEVEL_WARNING 4 345 | #define ESL_LOG_LEVEL_ERROR 3 346 | #define ESL_LOG_LEVEL_CRIT 2 347 | #define ESL_LOG_LEVEL_ALERT 1 348 | #define ESL_LOG_LEVEL_EMERG 0 349 | 350 | #define ESL_LOG_DEBUG ESL_PRE, ESL_LOG_LEVEL_DEBUG 351 | #define ESL_LOG_INFO ESL_PRE, ESL_LOG_LEVEL_INFO 352 | #define ESL_LOG_NOTICE ESL_PRE, ESL_LOG_LEVEL_NOTICE 353 | #define ESL_LOG_WARNING ESL_PRE, ESL_LOG_LEVEL_WARNING 354 | #define ESL_LOG_ERROR ESL_PRE, ESL_LOG_LEVEL_ERROR 355 | #define ESL_LOG_CRIT ESL_PRE, ESL_LOG_LEVEL_CRIT 356 | #define ESL_LOG_ALERT ESL_PRE, ESL_LOG_LEVEL_ALERT 357 | #define ESL_LOG_EMERG ESL_PRE, ESL_LOG_LEVEL_EMERG 358 | typedef void (*esl_logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...); 359 | 360 | 361 | ESL_DECLARE(int) esl_vasprintf(char **ret, const char *fmt, va_list ap); 362 | 363 | ESL_DECLARE_DATA extern esl_logger_t esl_log; 364 | 365 | /*! Sets the logger for libesl. Default is the null_logger */ 366 | ESL_DECLARE(void) esl_global_set_logger(esl_logger_t logger); 367 | /*! Sets the default log level for libesl */ 368 | ESL_DECLARE(void) esl_global_set_default_logger(int level); 369 | 370 | #include "esl_event.h" 371 | #include "esl_threadmutex.h" 372 | #include "esl_config.h" 373 | 374 | ESL_DECLARE(size_t) esl_url_encode(const char *url, char *buf, size_t len); 375 | ESL_DECLARE(char *)esl_url_decode(char *s); 376 | ESL_DECLARE(const char *)esl_stristr(const char *instr, const char *str); 377 | ESL_DECLARE(int) esl_toupper(int c); 378 | ESL_DECLARE(int) esl_tolower(int c); 379 | ESL_DECLARE(int) esl_snprintf(char *buffer, size_t count, const char *fmt, ...); 380 | 381 | 382 | typedef void (*esl_listen_callback_t)(esl_socket_t server_sock, esl_socket_t client_sock, struct sockaddr_in *addr, void *user_data); 383 | /*! 384 | \brief Attach a handle to an established socket connection 385 | \param handle Handle to be attached 386 | \param socket Socket to which the handle will be attached 387 | \param addr Structure that will contain the connection descritption (look up your os info) 388 | */ 389 | ESL_DECLARE(esl_status_t) esl_attach_handle(esl_handle_t *handle, esl_socket_t socket, struct sockaddr_in *addr); 390 | /*! 391 | \brief Will bind to host and callback when event is received. Used for outbound socket. 392 | \param host Host to bind to 393 | \param port Port to bind to 394 | \param callback Callback that will be called upon data received 395 | */ 396 | 397 | ESL_DECLARE(esl_status_t) esl_listen(const char *host, esl_port_t port, esl_listen_callback_t callback, void *user_data, esl_socket_t *server_sockP); 398 | ESL_DECLARE(esl_status_t) esl_listen_threaded(const char *host, esl_port_t port, esl_listen_callback_t callback, void *user_data, int max); 399 | /*! 400 | \brief Executes application with sendmsg to a specific UUID. Used for outbound socket. 401 | \param handle Handle that the msg will be sent 402 | \param app Application to execute 403 | \param arg Application arguments 404 | \param uuid Target UUID for the application 405 | */ 406 | ESL_DECLARE(esl_status_t) esl_execute(esl_handle_t *handle, const char *app, const char *arg, const char *uuid); 407 | /*! 408 | \brief Send an event 409 | \param handle Handle to which the event should be sent 410 | \param event Event to be sent 411 | */ 412 | ESL_DECLARE(esl_status_t) esl_sendevent(esl_handle_t *handle, esl_event_t *event); 413 | 414 | /*! 415 | \brief Send an event as a message to be parsed 416 | \param handle Handle to which the event should be sent 417 | \param event Event to be sent 418 | \param uuid a specific uuid if not the default 419 | */ 420 | ESL_DECLARE(esl_status_t) esl_sendmsg(esl_handle_t *handle, esl_event_t *event, const char *uuid); 421 | 422 | /*! 423 | \brief Connect a handle to a host/port with a specific password. This will also authenticate against the server 424 | \param handle Handle to connect 425 | \param host Host to be connected 426 | \param port Port to be connected 427 | \param password FreeSWITCH server username (optional) 428 | \param password FreeSWITCH server password 429 | \param timeout Connection timeout, in miliseconds 430 | */ 431 | ESL_DECLARE(esl_status_t) esl_connect_timeout(esl_handle_t *handle, const char *host, esl_port_t port, const char *user, const char *password, uint32_t timeout); 432 | #define esl_connect(_handle, _host, _port, _user, _password) esl_connect_timeout(_handle, _host, _port, _user, _password, 0) 433 | 434 | /*! 435 | \brief Disconnect a handle 436 | \param handle Handle to be disconnected 437 | */ 438 | ESL_DECLARE(esl_status_t) esl_disconnect(esl_handle_t *handle); 439 | /*! 440 | \brief Send a raw command using specific handle 441 | \param handle Handle to send the command to 442 | \param cmd Command to send 443 | */ 444 | ESL_DECLARE(esl_status_t) esl_send(esl_handle_t *handle, const char *cmd); 445 | /*! 446 | \brief Poll the handle's socket until an event is received or a connection error occurs 447 | \param handle Handle to poll 448 | \param check_q If set to 1, will check the handle queue (handle->race_event) and return the last event from it 449 | \param[out] save_event If this is not NULL, will return the event received 450 | */ 451 | ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_event_t **save_event); 452 | /*! 453 | \brief Poll the handle's socket until an event is received, a connection error occurs or ms expires 454 | \param handle Handle to poll 455 | \param ms Maximum time to poll 456 | \param check_q If set to 1, will check the handle queue (handle->race_event) and return the last event from it 457 | \param[out] save_event If this is not NULL, will return the event received 458 | */ 459 | ESL_DECLARE(esl_status_t) esl_recv_event_timed(esl_handle_t *handle, uint32_t ms, int check_q, esl_event_t **save_event); 460 | /*! 461 | \brief This will send a command and place its response event on handle->last_sr_event and handle->last_sr_reply 462 | \param handle Handle to be used 463 | \param cmd Raw command to send 464 | */ 465 | ESL_DECLARE(esl_status_t) esl_send_recv_timed(esl_handle_t *handle, const char *cmd, uint32_t ms); 466 | #define esl_send_recv(_handle, _cmd) esl_send_recv_timed(_handle, _cmd, 0) 467 | /*! 468 | \brief Applies a filter to received events 469 | \param handle Handle to apply the filter to 470 | \param header Header that the filter will be based on 471 | \param value The value of the header to filter 472 | */ 473 | ESL_DECLARE(esl_status_t) esl_filter(esl_handle_t *handle, const char *header, const char *value); 474 | /*! 475 | \brief Will subscribe to events on the server 476 | \param handle Handle to which we will subscribe to events 477 | \param etype Event type to subscribe 478 | \param value Which event to subscribe to 479 | */ 480 | ESL_DECLARE(esl_status_t) esl_events(esl_handle_t *handle, esl_event_type_t etype, const char *value); 481 | 482 | ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags); 483 | 484 | ESL_DECLARE(unsigned int) esl_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen); 485 | 486 | #define esl_recv(_h) esl_recv_event(_h, 0, NULL) 487 | #define esl_recv_timed(_h, _ms) esl_recv_event_timed(_h, _ms, 0, NULL) 488 | 489 | static __inline__ int esl_safe_strcasecmp(const char *s1, const char *s2) 490 | { 491 | if (!(s1 && s2)) { 492 | return 1; 493 | } 494 | 495 | return strcasecmp(s1, s2); 496 | } 497 | 498 | #ifdef __cplusplus 499 | } 500 | #endif /* defined(__cplusplus) */ 501 | 502 | 503 | #endif /* defined(_ESL_H_) */ 504 | 505 | /* For Emacs: 506 | * Local Variables: 507 | * mode:c 508 | * indent-tabs-mode:t 509 | * tab-width:4 510 | * c-basic-offset:4 511 | * End: 512 | * For VIM: 513 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 514 | */ 515 | -------------------------------------------------------------------------------- /src/esl_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "esl_json.h" 34 | #include "esl.h" 35 | 36 | static const char *ep; 37 | 38 | ESL_DECLARE(const char *)cJSON_GetErrorPtr(void) {return ep;} 39 | 40 | static int cJSON_strcasecmp(const char *s1,const char *s2) 41 | { 42 | if (!s1) return (s1==s2)?0:1;if (!s2) return 1; 43 | for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0; 44 | return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); 45 | } 46 | 47 | static void *glue_malloc(size_t theSize) 48 | { 49 | return(malloc(theSize)); 50 | } 51 | 52 | static void glue_free(void *thePtr) 53 | { 54 | free(thePtr); 55 | } 56 | 57 | static void *(*cJSON_malloc)(size_t sz) = glue_malloc; 58 | static void (*cJSON_free)(void *ptr) = glue_free; 59 | 60 | static char* cJSON_strdup(const char* str) 61 | { 62 | size_t len; 63 | char* copy; 64 | const char *s = str ? str : ""; 65 | 66 | len = strlen(s) + 1; 67 | if (!(copy = (char*)cJSON_malloc(len))) return 0; 68 | memcpy(copy,s,len); 69 | return copy; 70 | } 71 | 72 | ESL_DECLARE(void)cJSON_InitHooks(cJSON_Hooks* hooks) 73 | { 74 | if (!hooks) { /* Reset hooks */ 75 | cJSON_malloc = malloc; 76 | cJSON_free = free; 77 | return; 78 | } 79 | 80 | cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; 81 | cJSON_free = (hooks->free_fn)?hooks->free_fn:free; 82 | } 83 | 84 | /* Internal constructor. */ 85 | static cJSON *cJSON_New_Item(void) 86 | { 87 | cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); 88 | if (node) memset(node,0,sizeof(cJSON)); 89 | return node; 90 | } 91 | 92 | /* Delete a cJSON structure. */ 93 | ESL_DECLARE(void)cJSON_Delete(cJSON *c) 94 | { 95 | cJSON *next; 96 | while (c) 97 | { 98 | next=c->next; 99 | if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); 100 | if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); 101 | if (c->string) cJSON_free(c->string); 102 | cJSON_free(c); 103 | c=next; 104 | } 105 | } 106 | 107 | /* Parse the input text to generate a number, and populate the result into item. */ 108 | static const char *parse_number(cJSON *item,const char *num) 109 | { 110 | double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; 111 | 112 | /* Could use sscanf for this? */ 113 | if (*num=='-') sign=-1,num++; /* Has sign? */ 114 | if (*num=='0') num++; /* is zero */ 115 | if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ 116 | if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ 117 | if (*num=='e' || *num=='E') /* Exponent? */ 118 | { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ 119 | while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ 120 | } 121 | 122 | n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ 123 | 124 | item->valuedouble=n; 125 | item->valueint=(int)n; 126 | item->type=cJSON_Number; 127 | return num; 128 | } 129 | 130 | /* Render the number nicely from the given item into a string. */ 131 | static char *print_number(cJSON *item) 132 | { 133 | char *str; 134 | double d=item->valuedouble; 135 | if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) 136 | { 137 | str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ 138 | if (str) sprintf(str,"%d",item->valueint); 139 | } 140 | else 141 | { 142 | str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ 143 | if (str) 144 | { 145 | if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); 146 | else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); 147 | else sprintf(str,"%f",d); 148 | } 149 | } 150 | return str; 151 | } 152 | 153 | /* Parse the input text into an unescaped cstring, and populate item. */ 154 | static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 155 | static const char *parse_string(cJSON *item,const char *str) 156 | { 157 | const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; 158 | if (*str!='\"') {ep=str;return 0;} /* not a string! */ 159 | 160 | while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ 161 | 162 | out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ 163 | if (!out) return 0; 164 | 165 | ptr=str+1;ptr2=out; 166 | while (*ptr!='\"' && *ptr) 167 | { 168 | if (*ptr!='\\') *ptr2++=*ptr++; 169 | else 170 | { 171 | ptr++; 172 | switch (*ptr) 173 | { 174 | case 'b': *ptr2++='\b'; break; 175 | case 'f': *ptr2++='\f'; break; 176 | case 'n': *ptr2++='\n'; break; 177 | case 'r': *ptr2++='\r'; break; 178 | case 't': *ptr2++='\t'; break; 179 | case 'u': /* transcode utf16 to utf8. */ 180 | if (sscanf(ptr+1,"%4x",&uc) < 1) break; 181 | 182 | ptr+=4; /* get the unicode char. */ 183 | 184 | if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid. 185 | 186 | if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs. 187 | { 188 | if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate. 189 | if (sscanf(ptr+3,"%4x",&uc2) < 1) break; 190 | ptr+=6; 191 | if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate. 192 | uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF); 193 | } 194 | 195 | len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; 196 | 197 | switch (len) { 198 | case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 199 | case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 200 | case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 201 | case 1: *--ptr2 =(char)(uc | firstByteMark[len]); 202 | } 203 | ptr2+=len; 204 | break; 205 | default: *ptr2++=*ptr; break; 206 | } 207 | ptr++; 208 | } 209 | } 210 | *ptr2=0; 211 | if (*ptr=='\"') ptr++; 212 | item->valuestring=out; 213 | item->type=cJSON_String; 214 | return ptr; 215 | } 216 | 217 | /* Render the cstring provided to an escaped version that can be printed. */ 218 | static char *print_string_ptr(const char *str) 219 | { 220 | const char *ptr;char *ptr2,*out;int len=0;unsigned char token; 221 | 222 | if (!str) return cJSON_strdup(""); 223 | ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} 224 | 225 | out=(char*)cJSON_malloc(len+3); 226 | if (!out) return 0; 227 | 228 | ptr2=out;ptr=str; 229 | *ptr2++='\"'; 230 | while (*ptr) 231 | { 232 | if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; 233 | else 234 | { 235 | *ptr2++='\\'; 236 | switch (token=*ptr++) 237 | { 238 | case '\\': *ptr2++='\\'; break; 239 | case '\"': *ptr2++='\"'; break; 240 | case '\b': *ptr2++='b'; break; 241 | case '\f': *ptr2++='f'; break; 242 | case '\n': *ptr2++='n'; break; 243 | case '\r': *ptr2++='r'; break; 244 | case '\t': *ptr2++='t'; break; 245 | default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ 246 | } 247 | } 248 | } 249 | *ptr2++='\"';*ptr2++=0; 250 | return out; 251 | } 252 | /* Invote print_string_ptr (which is useful) on an item. */ 253 | static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} 254 | 255 | /* Predeclare these prototypes. */ 256 | static const char *parse_value(cJSON *item,const char *value); 257 | static char *print_value(cJSON *item,int depth,int fmt); 258 | static const char *parse_array(cJSON *item,const char *value); 259 | static char *print_array(cJSON *item,int depth,int fmt); 260 | static const char *parse_object(cJSON *item,const char *value); 261 | static char *print_object(cJSON *item,int depth,int fmt); 262 | 263 | /* Utility to jump whitespace and cr/lf */ 264 | static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} 265 | 266 | /* Parse an object - create a new root, and populate. */ 267 | ESL_DECLARE(cJSON *)cJSON_Parse(const char *value) 268 | { 269 | cJSON *c=cJSON_New_Item(); 270 | ep=0; 271 | if (!c) return 0; /* memory fail */ 272 | 273 | if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} 274 | return c; 275 | } 276 | 277 | /* Render a cJSON item/entity/structure to text. */ 278 | ESL_DECLARE(char *) cJSON_Print(cJSON *item) {return print_value(item,0,1);} 279 | ESL_DECLARE(char *) cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} 280 | 281 | /* Parser core - when encountering text, process appropriately. */ 282 | static const char *parse_value(cJSON *item,const char *value) 283 | { 284 | if (!value) return 0; /* Fail on null. */ 285 | if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } 286 | if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } 287 | if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } 288 | if (*value=='\"') { return parse_string(item,value); } 289 | if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } 290 | if (*value=='[') { return parse_array(item,value); } 291 | if (*value=='{') { return parse_object(item,value); } 292 | 293 | ep=value;return 0; /* failure. */ 294 | } 295 | 296 | /* Render a value to text. */ 297 | static char *print_value(cJSON *item,int depth,int fmt) 298 | { 299 | char *out=0; 300 | if (!item) return 0; 301 | switch ((item->type)&255) 302 | { 303 | case cJSON_NULL: out=cJSON_strdup("null"); break; 304 | case cJSON_False: out=cJSON_strdup("false");break; 305 | case cJSON_True: out=cJSON_strdup("true"); break; 306 | case cJSON_Number: out=print_number(item);break; 307 | case cJSON_String: out=print_string(item);break; 308 | case cJSON_Array: out=print_array(item,depth,fmt);break; 309 | case cJSON_Object: out=print_object(item,depth,fmt);break; 310 | } 311 | return out; 312 | } 313 | 314 | /* Build an array from input text. */ 315 | static const char *parse_array(cJSON *item,const char *value) 316 | { 317 | cJSON *child; 318 | if (*value!='[') {ep=value;return 0;} /* not an array! */ 319 | 320 | item->type=cJSON_Array; 321 | value=skip(value+1); 322 | if (*value==']') return value+1; /* empty array. */ 323 | 324 | item->child=child=cJSON_New_Item(); 325 | if (!item->child) return 0; /* memory fail */ 326 | value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ 327 | if (!value) return 0; 328 | 329 | while (*value==',') 330 | { 331 | cJSON *new_item; 332 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 333 | child->next=new_item;new_item->prev=child;child=new_item; 334 | value=skip(parse_value(child,skip(value+1))); 335 | if (!value) return 0; /* memory fail */ 336 | } 337 | 338 | if (*value==']') return value+1; /* end of array */ 339 | ep=value;return 0; /* malformed. */ 340 | } 341 | 342 | /* Render an array to text */ 343 | static char *print_array(cJSON *item,int depth,int fmt) 344 | { 345 | char **entries; 346 | char *out=0,*ptr,*ret;int len=5; 347 | cJSON *child=item->child; 348 | int numentries=0,i=0,fail=0; 349 | 350 | /* How many entries in the array? */ 351 | while (child) numentries++,child=child->next; 352 | /* Allocate an array to hold the values for each */ 353 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 354 | if (!entries) return 0; 355 | memset(entries,0,numentries*sizeof(char*)); 356 | /* Retrieve all the results: */ 357 | child=item->child; 358 | while (child && !fail) 359 | { 360 | ret=print_value(child,depth+1,fmt); 361 | entries[i++]=ret; 362 | if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; 363 | child=child->next; 364 | } 365 | 366 | /* If we didn't fail, try to malloc the output string */ 367 | if (!fail) out=(char*)cJSON_malloc(len); 368 | /* If that fails, we fail. */ 369 | if (!out) fail=1; 370 | 371 | /* Handle failure. */ 372 | if (fail) 373 | { 374 | for (i=0;itype=cJSON_Object; 400 | value=skip(value+1); 401 | if (*value=='}') return value+1; /* empty array. */ 402 | 403 | item->child=child=cJSON_New_Item(); 404 | if (!item->child) return 0; 405 | value=skip(parse_string(child,skip(value))); 406 | if (!value) return 0; 407 | child->string=child->valuestring;child->valuestring=0; 408 | if (*value!=':') {ep=value;return 0;} /* fail! */ 409 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 410 | if (!value) return 0; 411 | 412 | while (*value==',') 413 | { 414 | cJSON *new_item; 415 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 416 | child->next=new_item;new_item->prev=child;child=new_item; 417 | value=skip(parse_string(child,skip(value+1))); 418 | if (!value) return 0; 419 | child->string=child->valuestring;child->valuestring=0; 420 | if (*value!=':') {ep=value;return 0;} /* fail! */ 421 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 422 | if (!value) return 0; 423 | } 424 | 425 | if (*value=='}') return value+1; /* end of array */ 426 | ep=value;return 0; /* malformed. */ 427 | } 428 | 429 | /* Render an object to text. */ 430 | static char *print_object(cJSON *item,int depth,int fmt) 431 | { 432 | char **entries=0,**names=0; 433 | char *out=0,*ptr,*ret,*str;int len=7,i=0,j; 434 | cJSON *child=item->child; 435 | int numentries=0,fail=0; 436 | /* Count the number of entries. */ 437 | while (child) numentries++,child=child->next; 438 | /* Allocate space for the names and the objects */ 439 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 440 | if (!entries) return 0; 441 | names=(char**)cJSON_malloc(numentries*sizeof(char*)); 442 | if (!names) {cJSON_free(entries);return 0;} 443 | memset(entries,0,sizeof(char*)*numentries); 444 | memset(names,0,sizeof(char*)*numentries); 445 | 446 | /* Collect all the results into our arrays: */ 447 | child=item->child;depth++;if (fmt) len+=depth; 448 | while (child) 449 | { 450 | names[i]=str=print_string_ptr(child->string); 451 | entries[i++]=ret=print_value(child,depth,fmt); 452 | if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; 453 | child=child->next; 454 | } 455 | 456 | /* Try to allocate the output string */ 457 | if (!fail) out=(char*)cJSON_malloc(len); 458 | if (!out) fail=1; 459 | 460 | /* Handle failure */ 461 | if (fail) 462 | { 463 | for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} 489 | ESL_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} 490 | ESL_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} 491 | 492 | /* Utility for array list handling. */ 493 | static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} 494 | /* Utility for handling references. */ 495 | static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} 496 | 497 | /* Add item to array/object. */ 498 | ESL_DECLARE(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} 499 | ESL_DECLARE(void) cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} 500 | ESL_DECLARE(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} 501 | ESL_DECLARE(void) cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} 502 | 503 | ESL_DECLARE(cJSON *)cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; 504 | if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} 505 | ESL_DECLARE(void) cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} 506 | ESL_DECLARE(cJSON *)cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} 507 | ESL_DECLARE(void) cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} 508 | 509 | /* Replace array/object items with new ones. */ 510 | ESL_DECLARE(void) cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; 511 | newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; 512 | if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} 513 | ESL_DECLARE(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} 514 | 515 | /* Create basic types: */ 516 | ESL_DECLARE(cJSON *)cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} 517 | ESL_DECLARE(cJSON *)cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} 518 | ESL_DECLARE(cJSON *)cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} 519 | ESL_DECLARE(cJSON *)cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} 520 | ESL_DECLARE(cJSON *)cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} 521 | ESL_DECLARE(cJSON *)cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} 522 | ESL_DECLARE(cJSON *)cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} 523 | ESL_DECLARE(cJSON *)cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} 524 | 525 | /* Create Arrays: */ 526 | ESL_DECLARE(cJSON *)cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} 527 | ESL_DECLARE(cJSON *)cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} 528 | ESL_DECLARE(cJSON *)cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} 529 | ESL_DECLARE(cJSON *)cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} 530 | 531 | /* For Emacs: 532 | * Local Variables: 533 | * mode:c 534 | * indent-tabs-mode:t 535 | * tab-width:4 536 | * c-basic-offset:4 537 | * End: 538 | * For VIM: 539 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 540 | */ 541 | -------------------------------------------------------------------------------- /src/esl_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | 35 | #include 36 | #include 37 | 38 | static char *my_dup(const char *s) 39 | { 40 | size_t len = strlen(s) + 1; 41 | void *new = malloc(len); 42 | esl_assert(new); 43 | 44 | return (char *) memcpy(new, s, len); 45 | } 46 | 47 | #ifndef ALLOC 48 | #define ALLOC(size) malloc(size) 49 | #endif 50 | #ifndef DUP 51 | #define DUP(str) my_dup(str) 52 | #endif 53 | #ifndef FREE 54 | #define FREE(ptr) esl_safe_free(ptr) 55 | #endif 56 | 57 | /* make sure this is synced with the esl_event_types_t enum in esl_types.h 58 | also never put any new ones before EVENT_ALL 59 | */ 60 | static const char *EVENT_NAMES[] = { 61 | "CUSTOM", 62 | "CLONE", 63 | "CHANNEL_CREATE", 64 | "CHANNEL_DESTROY", 65 | "CHANNEL_STATE", 66 | "CHANNEL_CALLSTATE", 67 | "CHANNEL_ANSWER", 68 | "CHANNEL_HANGUP", 69 | "CHANNEL_HANGUP_COMPLETE", 70 | "CHANNEL_EXECUTE", 71 | "CHANNEL_EXECUTE_COMPLETE", 72 | "CHANNEL_HOLD", 73 | "CHANNEL_UNHOLD", 74 | "CHANNEL_BRIDGE", 75 | "CHANNEL_UNBRIDGE", 76 | "CHANNEL_PROGRESS", 77 | "CHANNEL_PROGRESS_MEDIA", 78 | "CHANNEL_OUTGOING", 79 | "CHANNEL_PARK", 80 | "CHANNEL_UNPARK", 81 | "CHANNEL_APPLICATION", 82 | "CHANNEL_ORIGINATE", 83 | "CHANNEL_UUID", 84 | "API", 85 | "LOG", 86 | "INBOUND_CHAN", 87 | "OUTBOUND_CHAN", 88 | "STARTUP", 89 | "SHUTDOWN", 90 | "PUBLISH", 91 | "UNPUBLISH", 92 | "TALK", 93 | "NOTALK", 94 | "SESSION_CRASH", 95 | "MODULE_LOAD", 96 | "MODULE_UNLOAD", 97 | "DTMF", 98 | "MESSAGE", 99 | "PRESENCE_IN", 100 | "NOTIFY_IN", 101 | "PRESENCE_OUT", 102 | "PRESENCE_PROBE", 103 | "MESSAGE_WAITING", 104 | "MESSAGE_QUERY", 105 | "ROSTER", 106 | "CODEC", 107 | "BACKGROUND_JOB", 108 | "DETECTED_SPEECH", 109 | "DETECTED_TONE", 110 | "PRIVATE_COMMAND", 111 | "HEARTBEAT", 112 | "TRAP", 113 | "ADD_SCHEDULE", 114 | "DEL_SCHEDULE", 115 | "EXE_SCHEDULE", 116 | "RE_SCHEDULE", 117 | "RELOADXML", 118 | "NOTIFY", 119 | "PHONE_FEATURE", 120 | "PHONE_FEATURE_SUBSCRIBE", 121 | "SEND_MESSAGE", 122 | "RECV_MESSAGE", 123 | "REQUEST_PARAMS", 124 | "CHANNEL_DATA", 125 | "GENERAL", 126 | "COMMAND", 127 | "SESSION_HEARTBEAT", 128 | "CLIENT_DISCONNECTED", 129 | "SERVER_DISCONNECTED", 130 | "SEND_INFO", 131 | "RECV_INFO", 132 | "RECV_RTCP_MESSAGE", 133 | "CALL_SECURE", 134 | "NAT", 135 | "RECORD_START", 136 | "RECORD_STOP", 137 | "PLAYBACK_START", 138 | "PLAYBACK_STOP", 139 | "CALL_UPDATE", 140 | "FAILURE", 141 | "SOCKET_DATA", 142 | "MEDIA_BUG_START", 143 | "MEDIA_BUG_STOP", 144 | "CONFERENCE_DATA_QUERY", 145 | "CONFERENCE_DATA", 146 | "CALL_SETUP_REQ", 147 | "CALL_SETUP_RESULT", 148 | "CALL_DETAIL", 149 | "DEVICE_STATE", 150 | "ALL" 151 | }; 152 | 153 | ESL_DECLARE(const char *)esl_event_name(esl_event_types_t event) 154 | { 155 | return EVENT_NAMES[event]; 156 | } 157 | 158 | ESL_DECLARE(esl_status_t) esl_name_event(const char *name, esl_event_types_t *type) 159 | { 160 | esl_event_types_t x; 161 | 162 | for (x = 0; x <= ESL_EVENT_ALL; x++) { 163 | if ((strlen(name) > 13 && !strcasecmp(name + 13, EVENT_NAMES[x])) || !strcasecmp(name, EVENT_NAMES[x])) { 164 | *type = x; 165 | return ESL_SUCCESS; 166 | } 167 | } 168 | 169 | return ESL_FAIL; 170 | } 171 | 172 | 173 | ESL_DECLARE(esl_status_t) esl_event_create_subclass(esl_event_t **event, esl_event_types_t event_id, const char *subclass_name) 174 | { 175 | *event = NULL; 176 | 177 | if ((event_id != ESL_EVENT_CLONE && event_id != ESL_EVENT_CUSTOM) && subclass_name) { 178 | return ESL_FAIL; 179 | } 180 | 181 | *event = ALLOC(sizeof(esl_event_t)); 182 | esl_assert(*event); 183 | 184 | 185 | memset(*event, 0, sizeof(esl_event_t)); 186 | 187 | if (event_id != ESL_EVENT_CLONE) { 188 | (*event)->event_id = event_id; 189 | esl_event_add_header_string(*event, ESL_STACK_BOTTOM, "Event-Name", esl_event_name((*event)->event_id)); 190 | } 191 | 192 | if (subclass_name) { 193 | (*event)->subclass_name = DUP(subclass_name); 194 | esl_event_add_header_string(*event, ESL_STACK_BOTTOM, "Event-Subclass", subclass_name); 195 | } 196 | 197 | return ESL_SUCCESS; 198 | } 199 | 200 | 201 | ESL_DECLARE(const char *)esl_priority_name(esl_priority_t priority) 202 | { 203 | switch (priority) { /*lol */ 204 | case ESL_PRIORITY_NORMAL: 205 | return "NORMAL"; 206 | case ESL_PRIORITY_LOW: 207 | return "LOW"; 208 | case ESL_PRIORITY_HIGH: 209 | return "HIGH"; 210 | default: 211 | return "INVALID"; 212 | } 213 | } 214 | 215 | ESL_DECLARE(esl_status_t) esl_event_set_priority(esl_event_t *event, esl_priority_t priority) 216 | { 217 | event->priority = priority; 218 | esl_event_add_header_string(event, ESL_STACK_TOP, "priority", esl_priority_name(priority)); 219 | return ESL_SUCCESS; 220 | } 221 | 222 | #define ESL_HASH_KEY_STRING -1 223 | 224 | static unsigned int esl_ci_hashfunc_default(const char *char_key, esl_ssize_t *klen) 225 | 226 | { 227 | unsigned int hash = 0; 228 | const unsigned char *key = (const unsigned char *)char_key; 229 | const unsigned char *p; 230 | esl_ssize_t i; 231 | 232 | if (*klen == ESL_HASH_KEY_STRING) { 233 | for (p = key; *p; p++) { 234 | hash = hash * 33 + tolower(*p); 235 | } 236 | *klen = p - key; 237 | } 238 | else { 239 | for (p = key, i = *klen; i; i--, p++) { 240 | hash = hash * 33 + tolower(*p); 241 | } 242 | } 243 | 244 | return hash; 245 | } 246 | 247 | ESL_DECLARE(esl_event_header_t *) esl_event_get_header_ptr(esl_event_t *event, const char *header_name) 248 | { 249 | esl_event_header_t *hp; 250 | esl_ssize_t hlen = -1; 251 | unsigned long hash = 0; 252 | 253 | esl_assert(event); 254 | 255 | if (!header_name) 256 | return NULL; 257 | 258 | hash = esl_ci_hashfunc_default(header_name, &hlen); 259 | 260 | for (hp = event->headers; hp; hp = hp->next) { 261 | if ((!hp->hash || hash == hp->hash) && !strcasecmp(hp->name, header_name)) { 262 | return hp; 263 | } 264 | } 265 | return NULL; 266 | } 267 | 268 | ESL_DECLARE(char *) esl_event_get_header_idx(esl_event_t *event, const char *header_name, int idx) 269 | { 270 | esl_event_header_t *hp; 271 | 272 | if ((hp = esl_event_get_header_ptr(event, header_name))) { 273 | if (idx > -1) { 274 | if (idx < hp->idx) { 275 | return hp->array[idx]; 276 | } else { 277 | return NULL; 278 | } 279 | } 280 | 281 | return hp->value; 282 | } else if (header_name && !strcmp(header_name, "_body")) { 283 | return event->body; 284 | } 285 | 286 | return NULL; 287 | } 288 | 289 | ESL_DECLARE(char *)esl_event_get_body(esl_event_t *event) 290 | { 291 | return (event ? event->body : NULL); 292 | } 293 | 294 | ESL_DECLARE(esl_status_t) esl_event_del_header_val(esl_event_t *event, const char *header_name, const char *val) 295 | { 296 | esl_event_header_t *hp, *lp = NULL, *tp; 297 | esl_status_t status = (esl_status_t) ESL_FALSE; 298 | int x = 0; 299 | esl_ssize_t hlen = -1; 300 | unsigned long hash = 0; 301 | 302 | tp = event->headers; 303 | while (tp) { 304 | hp = tp; 305 | tp = tp->next; 306 | 307 | x++; 308 | esl_assert(x < 1000000); 309 | hash = esl_ci_hashfunc_default(header_name, &hlen); 310 | 311 | if ((!hp->hash || hash == hp->hash) && (hp->name && !strcasecmp(header_name, hp->name)) && (esl_strlen_zero(val) || (hp->value && !strcmp(hp->value, val)))) { 312 | if (lp) { 313 | lp->next = hp->next; 314 | } else { 315 | event->headers = hp->next; 316 | } 317 | if (hp == event->last_header || !hp->next) { 318 | event->last_header = lp; 319 | } 320 | FREE(hp->name); 321 | 322 | if (hp->idx) { 323 | int i = 0; 324 | 325 | for (i = 0; i < hp->idx; i++) { 326 | FREE(hp->array[i]); 327 | } 328 | FREE(hp->array); 329 | } 330 | 331 | FREE(hp->value); 332 | 333 | memset(hp, 0, sizeof(*hp)); 334 | #ifdef ESL_EVENT_RECYCLE 335 | if (esl_queue_trypush(EVENT_HEADER_RECYCLE_QUEUE, hp) != ESL_SUCCESS) { 336 | FREE(hp); 337 | } 338 | #else 339 | FREE(hp); 340 | #endif 341 | status = ESL_SUCCESS; 342 | } else { 343 | lp = hp; 344 | } 345 | } 346 | 347 | return status; 348 | } 349 | 350 | static esl_event_header_t *new_header(const char *header_name) 351 | { 352 | esl_event_header_t *header; 353 | 354 | #ifdef ESL_EVENT_RECYCLE 355 | void *pop; 356 | if (esl_queue_trypop(EVENT_HEADER_RECYCLE_QUEUE, &pop) == ESL_SUCCESS) { 357 | header = (esl_event_header_t *) pop; 358 | } else { 359 | #endif 360 | header = ALLOC(sizeof(*header)); 361 | esl_assert(header); 362 | #ifdef ESL_EVENT_RECYCLE 363 | } 364 | #endif 365 | 366 | memset(header, 0, sizeof(*header)); 367 | header->name = DUP(header_name); 368 | 369 | return header; 370 | 371 | } 372 | 373 | ESL_DECLARE(int) esl_event_add_array(esl_event_t *event, const char *var, const char *val) 374 | { 375 | char *data; 376 | char **array; 377 | int max = 0; 378 | int len; 379 | const char *p; 380 | int i; 381 | 382 | if (strlen(val) < 8) { 383 | return -1; 384 | } 385 | 386 | p = val + 7; 387 | 388 | max = 1; 389 | 390 | while((p = strstr(p, "|:"))) { 391 | max++; 392 | p += 2; 393 | } 394 | 395 | data = strdup(val + 7); 396 | 397 | len = (sizeof(char *) * max) + 1; 398 | array = malloc(len); 399 | esl_assert(array); 400 | memset(array, 0, len); 401 | 402 | esl_separate_string_string(data, "|:", array, max); 403 | 404 | for(i = 0; i < max; i++) { 405 | esl_event_add_header_string(event, ESL_STACK_PUSH, var, array[i]); 406 | } 407 | 408 | free(array); 409 | free(data); 410 | 411 | return 0; 412 | } 413 | 414 | static esl_status_t esl_event_base_add_header(esl_event_t *event, esl_stack_t stack, const char *header_name, char *data) 415 | { 416 | esl_event_header_t *header = NULL; 417 | esl_ssize_t hlen = -1; 418 | int exists = 0, fly = 0; 419 | char *index_ptr; 420 | int index = 0; 421 | char *real_header_name = NULL; 422 | 423 | if (!strcmp(header_name, "_body")) { 424 | esl_event_set_body(event, data); 425 | } 426 | 427 | if ((index_ptr = strchr(header_name, '['))) { 428 | index_ptr++; 429 | index = atoi(index_ptr); 430 | real_header_name = DUP(header_name); 431 | if ((index_ptr = strchr(real_header_name, '['))) { 432 | *index_ptr++ = '\0'; 433 | } 434 | header_name = real_header_name; 435 | } 436 | 437 | if (index_ptr || (stack & ESL_STACK_PUSH) || (stack & ESL_STACK_UNSHIFT)) { 438 | 439 | if (!(header = esl_event_get_header_ptr(event, header_name)) && index_ptr) { 440 | 441 | header = new_header(header_name); 442 | 443 | if (esl_test_flag(event, ESL_EF_UNIQ_HEADERS)) { 444 | esl_event_del_header(event, header_name); 445 | } 446 | 447 | fly++; 448 | } 449 | 450 | if (header || (header = esl_event_get_header_ptr(event, header_name))) { 451 | 452 | if (index_ptr) { 453 | if (index > -1 && index <= 4000) { 454 | if (index < header->idx) { 455 | FREE(header->array[index]); 456 | header->array[index] = DUP(data); 457 | } else { 458 | int i; 459 | char **m; 460 | 461 | m = realloc(header->array, sizeof(char *) * (index + 1)); 462 | esl_assert(m); 463 | header->array = m; 464 | for (i = header->idx; i < index; i++) { 465 | m[i] = DUP(""); 466 | } 467 | m[index] = DUP(data); 468 | header->idx = index + 1; 469 | if (!fly) { 470 | exists = 1; 471 | } 472 | 473 | goto redraw; 474 | } 475 | } 476 | goto end; 477 | } else { 478 | if ((stack & ESL_STACK_PUSH) || (stack & ESL_STACK_UNSHIFT)) { 479 | exists++; 480 | stack &= ~(ESL_STACK_TOP | ESL_STACK_BOTTOM); 481 | } else { 482 | header = NULL; 483 | } 484 | } 485 | } 486 | } 487 | 488 | 489 | if (!header) { 490 | 491 | if (esl_strlen_zero(data)) { 492 | esl_event_del_header(event, header_name); 493 | FREE(data); 494 | goto end; 495 | } 496 | 497 | if (esl_test_flag(event, ESL_EF_UNIQ_HEADERS)) { 498 | esl_event_del_header(event, header_name); 499 | } 500 | 501 | if (strstr(data, "ARRAY::")) { 502 | esl_event_add_array(event, header_name, data); 503 | FREE(data); 504 | goto end; 505 | } 506 | 507 | 508 | header = new_header(header_name); 509 | } 510 | 511 | if ((stack & ESL_STACK_PUSH) || (stack & ESL_STACK_UNSHIFT)) { 512 | char **m = NULL; 513 | esl_size_t len = 0; 514 | char *hv; 515 | int i = 0, j = 0; 516 | 517 | if (header->value && !header->idx) { 518 | m = malloc(sizeof(char *)); 519 | esl_assert(m); 520 | m[0] = header->value; 521 | header->value = NULL; 522 | header->array = m; 523 | header->idx++; 524 | m = NULL; 525 | } 526 | 527 | i = header->idx + 1; 528 | m = realloc(header->array, sizeof(char *) * i); 529 | esl_assert(m); 530 | 531 | if ((stack & ESL_STACK_PUSH)) { 532 | m[header->idx] = data; 533 | } else if ((stack & ESL_STACK_UNSHIFT)) { 534 | for (j = header->idx; j > 0; j--) { 535 | m[j] = m[j-1]; 536 | } 537 | m[0] = data; 538 | } 539 | 540 | header->idx++; 541 | header->array = m; 542 | 543 | redraw: 544 | len = 0; 545 | for(j = 0; j < header->idx; j++) { 546 | len += strlen(header->array[j]) + 2; 547 | } 548 | 549 | if (len) { 550 | len += 8; 551 | hv = realloc(header->value, len); 552 | esl_assert(hv); 553 | header->value = hv; 554 | 555 | if (header->idx > 1) { 556 | esl_snprintf(header->value, len, "ARRAY::"); 557 | } else { 558 | *header->value = '\0'; 559 | } 560 | 561 | for(j = 0; j < header->idx; j++) { 562 | esl_snprintf(header->value + strlen(header->value), len - strlen(header->value), "%s%s", j == 0 ? "" : "|:", header->array[j]); 563 | } 564 | } 565 | 566 | } else { 567 | header->value = data; 568 | } 569 | 570 | if (!exists) { 571 | header->hash = esl_ci_hashfunc_default(header->name, &hlen); 572 | 573 | if ((stack & ESL_STACK_TOP)) { 574 | header->next = event->headers; 575 | event->headers = header; 576 | if (!event->last_header) { 577 | event->last_header = header; 578 | } 579 | } else { 580 | if (event->last_header) { 581 | event->last_header->next = header; 582 | } else { 583 | event->headers = header; 584 | header->next = NULL; 585 | } 586 | event->last_header = header; 587 | } 588 | } 589 | 590 | end: 591 | 592 | esl_safe_free(real_header_name); 593 | 594 | return ESL_SUCCESS; 595 | } 596 | 597 | ESL_DECLARE(esl_status_t) esl_event_add_header(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *fmt, ...) 598 | { 599 | int ret = 0; 600 | char *data; 601 | va_list ap; 602 | 603 | va_start(ap, fmt); 604 | ret = esl_vasprintf(&data, fmt, ap); 605 | va_end(ap); 606 | 607 | if (ret == -1) { 608 | return ESL_FAIL; 609 | } 610 | 611 | return esl_event_base_add_header(event, stack, header_name, data); 612 | } 613 | 614 | ESL_DECLARE(esl_status_t) esl_event_add_header_string(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *data) 615 | { 616 | if (data) { 617 | return esl_event_base_add_header(event, stack, header_name, DUP(data)); 618 | } 619 | return ESL_FAIL; 620 | } 621 | 622 | ESL_DECLARE(esl_status_t) esl_event_set_body(esl_event_t *event, const char *body) 623 | { 624 | esl_safe_free(event->body); 625 | 626 | if (body) { 627 | event->body = DUP(body); 628 | } 629 | 630 | return ESL_SUCCESS; 631 | } 632 | 633 | ESL_DECLARE(esl_status_t) esl_event_add_body(esl_event_t *event, const char *fmt, ...) 634 | { 635 | int ret = 0; 636 | char *data; 637 | 638 | va_list ap; 639 | if (fmt) { 640 | va_start(ap, fmt); 641 | ret = esl_vasprintf(&data, fmt, ap); 642 | va_end(ap); 643 | 644 | if (ret == -1) { 645 | return ESL_FAIL; 646 | } else { 647 | esl_safe_free(event->body); 648 | event->body = data; 649 | return ESL_SUCCESS; 650 | } 651 | } else { 652 | return ESL_FAIL; 653 | } 654 | } 655 | 656 | 657 | ESL_DECLARE(void) esl_event_destroy(esl_event_t **event) 658 | { 659 | esl_event_t *ep = *event; 660 | esl_event_header_t *hp, *this; 661 | 662 | if (ep) { 663 | for (hp = ep->headers; hp;) { 664 | this = hp; 665 | hp = hp->next; 666 | FREE(this->name); 667 | 668 | if (this->idx) { 669 | int i = 0; 670 | 671 | for (i = 0; i < this->idx; i++) { 672 | FREE(this->array[i]); 673 | } 674 | FREE(this->array); 675 | } 676 | 677 | FREE(this->value); 678 | 679 | 680 | #ifdef ESL_EVENT_RECYCLE 681 | if (esl_queue_trypush(EVENT_HEADER_RECYCLE_QUEUE, this) != ESL_SUCCESS) { 682 | FREE(this); 683 | } 684 | #else 685 | FREE(this); 686 | #endif 687 | 688 | 689 | } 690 | FREE(ep->body); 691 | FREE(ep->subclass_name); 692 | #ifdef ESL_EVENT_RECYCLE 693 | if (esl_queue_trypush(EVENT_RECYCLE_QUEUE, ep) != ESL_SUCCESS) { 694 | FREE(ep); 695 | } 696 | #else 697 | FREE(ep); 698 | #endif 699 | 700 | } 701 | *event = NULL; 702 | } 703 | 704 | ESL_DECLARE(void) esl_event_merge(esl_event_t *event, esl_event_t *tomerge) 705 | { 706 | esl_event_header_t *hp; 707 | 708 | esl_assert(tomerge && event); 709 | 710 | for (hp = tomerge->headers; hp; hp = hp->next) { 711 | if (hp->idx) { 712 | int i; 713 | 714 | for(i = 0; i < hp->idx; i++) { 715 | esl_event_add_header_string(event, ESL_STACK_PUSH, hp->name, hp->array[i]); 716 | } 717 | } else { 718 | esl_event_add_header_string(event, ESL_STACK_BOTTOM, hp->name, hp->value); 719 | } 720 | } 721 | } 722 | 723 | ESL_DECLARE(esl_status_t) esl_event_dup(esl_event_t **event, esl_event_t *todup) 724 | { 725 | esl_event_header_t *hp; 726 | 727 | if (esl_event_create_subclass(event, ESL_EVENT_CLONE, todup->subclass_name) != ESL_SUCCESS) { 728 | return ESL_GENERR; 729 | } 730 | 731 | (*event)->event_id = todup->event_id; 732 | (*event)->event_user_data = todup->event_user_data; 733 | (*event)->bind_user_data = todup->bind_user_data; 734 | (*event)->flags = todup->flags; 735 | for (hp = todup->headers; hp; hp = hp->next) { 736 | if (todup->subclass_name && !strcmp(hp->name, "Event-Subclass")) { 737 | continue; 738 | } 739 | 740 | if (hp->idx) { 741 | int i; 742 | for (i = 0; i < hp->idx; i++) { 743 | esl_event_add_header_string(*event, ESL_STACK_PUSH, hp->name, hp->array[i]); 744 | } 745 | } else { 746 | esl_event_add_header_string(*event, ESL_STACK_BOTTOM, hp->name, hp->value); 747 | } 748 | } 749 | 750 | if (todup->body) { 751 | (*event)->body = DUP(todup->body); 752 | } 753 | 754 | (*event)->key = todup->key; 755 | 756 | return ESL_SUCCESS; 757 | } 758 | 759 | 760 | ESL_DECLARE(esl_status_t) esl_event_serialize(esl_event_t *event, char **str, esl_bool_t encode) 761 | { 762 | esl_size_t len = 0; 763 | esl_event_header_t *hp; 764 | esl_size_t llen = 0, dlen = 0, blocksize = 512, encode_len = 1536, new_len = 0; 765 | char *buf; 766 | char *encode_buf = NULL; /* used for url encoding of variables to make sure unsafe things stay out of the serialized copy */ 767 | 768 | *str = NULL; 769 | 770 | dlen = blocksize * 2; 771 | 772 | if (!(buf = malloc(dlen))) { 773 | abort(); 774 | } 775 | 776 | /* go ahead and give ourselves some space to work with, should save a few reallocs */ 777 | if (!(encode_buf = malloc(encode_len))) { 778 | abort(); 779 | } 780 | 781 | /* esl_log_printf(ESL_CHANNEL_LOG, ESL_LOG_INFO, "hit serialized!.\n"); */ 782 | for (hp = event->headers; hp; hp = hp->next) { 783 | /* 784 | * grab enough memory to store 3x the string (url encode takes one char and turns it into %XX) 785 | * so we could end up with a string that is 3 times the originals length, unlikely but rather 786 | * be safe than destroy the string, also add one for the null. And try to be smart about using 787 | * the memory, allocate and only reallocate if we need more. This avoids an alloc, free CPU 788 | * destroying loop. 789 | */ 790 | 791 | if (hp->idx) { 792 | int i; 793 | new_len = 0; 794 | for(i = 0; i < hp->idx; i++) { 795 | new_len += (strlen(hp->array[i]) * 3) + 1; 796 | } 797 | } else { 798 | new_len = (strlen(hp->value) * 3) + 1; 799 | } 800 | 801 | if (encode_len < new_len) { 802 | char *tmp; 803 | 804 | /* keep track of the size of our allocation */ 805 | encode_len = new_len; 806 | 807 | if (!(tmp = realloc(encode_buf, encode_len))) { 808 | abort(); 809 | } 810 | 811 | encode_buf = tmp; 812 | } 813 | 814 | /* handle any bad things in the string like newlines : etc that screw up the serialized format */ 815 | 816 | 817 | if (encode) { 818 | esl_url_encode(hp->value, encode_buf, encode_len); 819 | } else { 820 | esl_snprintf(encode_buf, encode_len, "%s", hp->value); 821 | } 822 | 823 | 824 | llen = strlen(hp->name) + strlen(encode_buf) + 8; 825 | 826 | if ((len + llen) > dlen) { 827 | char *m; 828 | char *old = buf; 829 | dlen += (blocksize + (len + llen)); 830 | if ((m = realloc(buf, dlen))) { 831 | buf = m; 832 | } else { 833 | buf = old; 834 | abort(); 835 | } 836 | } 837 | 838 | esl_snprintf(buf + len, dlen - len, "%s: %s\n", hp->name, *encode_buf == '\0' ? "_undef_" : encode_buf); 839 | len = strlen(buf); 840 | } 841 | 842 | /* we are done with the memory we used for encoding, give it back */ 843 | esl_safe_free(encode_buf); 844 | 845 | if (event->body) { 846 | int blen = (int) strlen(event->body); 847 | llen = blen; 848 | 849 | if (blen) { 850 | llen += 25; 851 | } else { 852 | llen += 5; 853 | } 854 | 855 | if ((len + llen) > dlen) { 856 | char *m; 857 | char *old = buf; 858 | dlen += (blocksize + (len + llen)); 859 | if ((m = realloc(buf, dlen))) { 860 | buf = m; 861 | } else { 862 | buf = old; 863 | abort(); 864 | } 865 | } 866 | 867 | if (blen) { 868 | esl_snprintf(buf + len, dlen - len, "Content-Length: %d\n\n%s", blen, event->body); 869 | } else { 870 | esl_snprintf(buf + len, dlen - len, "\n"); 871 | } 872 | } else { 873 | esl_snprintf(buf + len, dlen - len, "\n"); 874 | } 875 | 876 | *str = buf; 877 | 878 | return ESL_SUCCESS; 879 | } 880 | 881 | ESL_DECLARE(esl_status_t) esl_event_create_json(esl_event_t **event, const char *json) 882 | { 883 | esl_event_t *new_event; 884 | cJSON *cj, *cjp; 885 | 886 | 887 | if (!(cj = cJSON_Parse(json))) { 888 | return (esl_status_t) ESL_FALSE; 889 | } 890 | 891 | if (esl_event_create(&new_event, ESL_EVENT_CLONE) != ESL_SUCCESS) { 892 | cJSON_Delete(cj); 893 | return (esl_status_t) ESL_FALSE; 894 | } 895 | 896 | for (cjp = cj->child; cjp; cjp = cjp->next) { 897 | char *name = cjp->string; 898 | char *value = cjp->valuestring; 899 | 900 | if (name && value) { 901 | if (!strcasecmp(name, "_body")) { 902 | esl_event_add_body(new_event, value, ESL_VA_NONE); 903 | } else { 904 | if (!strcasecmp(name, "event-name")) { 905 | esl_event_del_header(new_event, "event-name"); 906 | esl_name_event(value, &new_event->event_id); 907 | } 908 | 909 | esl_event_add_header_string(new_event, ESL_STACK_BOTTOM, name, value); 910 | } 911 | 912 | } else if (name) { 913 | if (cjp->type == cJSON_Array) { 914 | int i, x = cJSON_GetArraySize(cjp); 915 | 916 | for (i = 0; i < x; i++) { 917 | cJSON *item = cJSON_GetArrayItem(cjp, i); 918 | 919 | if (item != NULL && item->type == cJSON_String && item->valuestring) { 920 | esl_event_add_header_string(new_event, ESL_STACK_PUSH, name, item->valuestring); 921 | } 922 | } 923 | } 924 | } 925 | } 926 | 927 | cJSON_Delete(cj); 928 | *event = new_event; 929 | return ESL_SUCCESS; 930 | } 931 | 932 | ESL_DECLARE(esl_status_t) esl_event_serialize_json(esl_event_t *event, char **str) 933 | { 934 | esl_event_header_t *hp; 935 | cJSON *cj; 936 | 937 | *str = NULL; 938 | 939 | cj = cJSON_CreateObject(); 940 | 941 | for (hp = event->headers; hp; hp = hp->next) { 942 | if (hp->idx) { 943 | cJSON *a = cJSON_CreateArray(); 944 | int i; 945 | 946 | for(i = 0; i < hp->idx; i++) { 947 | cJSON_AddItemToArray(a, cJSON_CreateString(hp->array[i])); 948 | } 949 | 950 | cJSON_AddItemToObject(cj, hp->name, a); 951 | 952 | } else { 953 | cJSON_AddItemToObject(cj, hp->name, cJSON_CreateString(hp->value)); 954 | } 955 | } 956 | 957 | if (event->body) { 958 | int blen = (int) strlen(event->body); 959 | char tmp[25]; 960 | 961 | esl_snprintf(tmp, sizeof(tmp), "%d", blen); 962 | 963 | cJSON_AddItemToObject(cj, "Content-Length", cJSON_CreateString(tmp)); 964 | cJSON_AddItemToObject(cj, "_body", cJSON_CreateString(event->body)); 965 | } 966 | 967 | *str = cJSON_Print(cj); 968 | cJSON_Delete(cj); 969 | 970 | return ESL_SUCCESS; 971 | } 972 | 973 | /* For Emacs: 974 | * Local Variables: 975 | * mode:c 976 | * indent-tabs-mode:t 977 | * tab-width:4 978 | * c-basic-offset:4 979 | * End: 980 | * For VIM: 981 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 982 | */ 983 | -------------------------------------------------------------------------------- /src/esl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Anthony Minessale II 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the original author; nor the names of any contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | 35 | /* Use select on windows and poll everywhere else. 36 | Select is the devil. Especially if you are doing a lot of small socket connections. 37 | If your FD number is bigger than 1024 you will silently create memory corruption. 38 | 39 | If you have build errors on your platform because you don't have poll find a way to detect it and #define ESL_USE_SELECT and #undef ESL_USE_POLL 40 | All of this will be upgraded to autoheadache eventually. 41 | */ 42 | 43 | /* TBD for win32 figure out how to tell if you have WSAPoll (vista or higher) and use it when available by #defining ESL_USE_WSAPOLL (see below) */ 44 | 45 | #ifdef _MSC_VER 46 | #define FD_SETSIZE 8192 47 | #define ESL_USE_SELECT 48 | #else 49 | #define ESL_USE_POLL 50 | #endif 51 | 52 | #include 53 | #ifndef WIN32 54 | #define closesocket(x) shutdown(x, 2); close(x) 55 | #include 56 | #include 57 | #else 58 | #pragma warning (disable:6386) 59 | /* These warnings need to be ignored warning in sdk header */ 60 | #include 61 | #include 62 | #include 63 | #ifndef errno 64 | #define errno WSAGetLastError() 65 | #endif 66 | #ifndef EINTR 67 | #define EINTR WSAEINTR 68 | #endif 69 | #pragma warning (default:6386) 70 | #endif 71 | 72 | #ifdef ESL_USE_POLL 73 | #include 74 | #endif 75 | 76 | #ifndef ESL_MIN 77 | #define ESL_MIN(x,y) ((x) < (y) ? (x) : (y)) 78 | #endif 79 | #ifndef ESL_MAX 80 | #define ESL_MAX(x,y) ((x) > (y) ? (x) : (y)) 81 | #endif 82 | #ifndef ESL_CLAMP 83 | #define ESL_CLAMP(min,max,val) (ESL_MIN(max,ESL_MAX(val,min))) 84 | #endif 85 | 86 | 87 | /* Written by Marc Espie, public domain */ 88 | #define ESL_CTYPE_NUM_CHARS 256 89 | 90 | const short _esl_C_toupper_[1 + ESL_CTYPE_NUM_CHARS] = { 91 | EOF, 92 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 93 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 94 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 95 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 96 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 97 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 98 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 99 | 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 100 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 101 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 102 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 103 | 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 104 | 0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 105 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 106 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 107 | 'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 108 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 109 | 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 110 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 111 | 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 112 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 113 | 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 114 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 115 | 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 116 | 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 117 | 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 118 | 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 119 | 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 120 | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 121 | 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 122 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 123 | 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 124 | }; 125 | 126 | const short *_esl_toupper_tab_ = _esl_C_toupper_; 127 | 128 | ESL_DECLARE(int) esl_toupper(int c) 129 | { 130 | if ((unsigned int)c > 255) 131 | return(c); 132 | if (c < -1) 133 | return EOF; 134 | return((_esl_toupper_tab_ + 1)[c]); 135 | } 136 | 137 | const short _esl_C_tolower_[1 + ESL_CTYPE_NUM_CHARS] = { 138 | EOF, 139 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 140 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 141 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 142 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 143 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 144 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 145 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 146 | 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 147 | 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 148 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 149 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 150 | 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 151 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 152 | 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 153 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 154 | 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 155 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 156 | 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 157 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 158 | 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 159 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 160 | 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 161 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 162 | 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 163 | 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 164 | 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 165 | 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 166 | 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 167 | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 168 | 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 169 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 170 | 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 171 | }; 172 | 173 | const short *_esl_tolower_tab_ = _esl_C_tolower_; 174 | 175 | ESL_DECLARE(int) esl_tolower(int c) 176 | { 177 | if ((unsigned int)c > 255) 178 | return(c); 179 | if (c < -1) 180 | return EOF; 181 | return((_esl_tolower_tab_ + 1)[c]); 182 | } 183 | 184 | ESL_DECLARE(const char *)esl_stristr(const char *instr, const char *str) 185 | { 186 | /* 187 | ** Rev History: 16/07/97 Greg Thayer Optimized 188 | ** 07/04/95 Bob Stout ANSI-fy 189 | ** 02/03/94 Fred Cole Original 190 | ** 09/01/03 Bob Stout Bug fix (lines 40-41) per Fred Bulback 191 | ** 192 | ** Hereby donated to public domain. 193 | */ 194 | const char *pptr, *sptr, *start; 195 | 196 | if (!str || !instr) 197 | return NULL; 198 | 199 | for (start = str; *start; start++) { 200 | /* find start of pattern in string */ 201 | for (; ((*start) && (esl_toupper(*start) != esl_toupper(*instr))); start++); 202 | 203 | if (!*start) 204 | return NULL; 205 | 206 | pptr = instr; 207 | sptr = start; 208 | 209 | while (esl_toupper(*sptr) == esl_toupper(*pptr)) { 210 | sptr++; 211 | pptr++; 212 | 213 | /* if end of pattern then pattern was found */ 214 | if (!*pptr) 215 | return (start); 216 | 217 | if (!*sptr) 218 | return NULL; 219 | } 220 | } 221 | return NULL; 222 | } 223 | 224 | #ifdef WIN32 225 | #ifndef vsnprintf 226 | #define vsnprintf _vsnprintf 227 | #endif 228 | #endif 229 | 230 | 231 | int vasprintf(char **ret, const char *format, va_list ap); 232 | 233 | ESL_DECLARE(int) esl_vasprintf(char **ret, const char *fmt, va_list ap) 234 | { 235 | #if !defined(WIN32) && !defined(__sun) 236 | return vasprintf(ret, fmt, ap); 237 | #else 238 | char *buf; 239 | int len; 240 | size_t buflen; 241 | va_list ap2; 242 | char *tmp = NULL; 243 | 244 | #ifdef _MSC_VER 245 | #if _MSC_VER >= 1500 246 | /* hack for incorrect assumption in msvc header files for code analysis */ 247 | __analysis_assume(tmp); 248 | #endif 249 | ap2 = ap; 250 | #else 251 | va_copy(ap2, ap); 252 | #endif 253 | 254 | len = vsnprintf(tmp, 0, fmt, ap2); 255 | 256 | if (len > 0 && (buf = malloc((buflen = (size_t) (len + 1)))) != NULL) { 257 | len = vsnprintf(buf, buflen, fmt, ap); 258 | *ret = buf; 259 | } else { 260 | *ret = NULL; 261 | len = -1; 262 | } 263 | 264 | va_end(ap2); 265 | return len; 266 | #endif 267 | } 268 | 269 | 270 | 271 | 272 | ESL_DECLARE(int) esl_snprintf(char *buffer, size_t count, const char *fmt, ...) 273 | { 274 | va_list ap; 275 | int ret; 276 | 277 | va_start(ap, fmt); 278 | ret = vsnprintf(buffer, count-1, fmt, ap); 279 | if (ret < 0) 280 | buffer[count-1] = '\0'; 281 | va_end(ap); 282 | return ret; 283 | } 284 | 285 | static void null_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) 286 | { 287 | if (file && func && line && level && fmt) { 288 | return; 289 | } 290 | return; 291 | } 292 | 293 | 294 | static const char *LEVEL_NAMES[] = { 295 | "EMERG", 296 | "ALERT", 297 | "CRIT", 298 | "ERROR", 299 | "WARNING", 300 | "NOTICE", 301 | "INFO", 302 | "DEBUG", 303 | NULL 304 | }; 305 | 306 | static int esl_log_level = 7; 307 | 308 | static const char *cut_path(const char *in) 309 | { 310 | const char *p, *ret = in; 311 | char delims[] = "/\\"; 312 | char *i; 313 | 314 | for (i = delims; *i; i++) { 315 | p = in; 316 | while ((p = strchr(p, *i)) != 0) { 317 | ret = ++p; 318 | } 319 | } 320 | return ret; 321 | } 322 | 323 | 324 | static void default_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) 325 | { 326 | const char *fp; 327 | char *data; 328 | va_list ap; 329 | int ret; 330 | 331 | if (level < 0 || level > 7) { 332 | level = 7; 333 | } 334 | if (level > esl_log_level) { 335 | return; 336 | } 337 | 338 | fp = cut_path(file); 339 | 340 | va_start(ap, fmt); 341 | 342 | ret = esl_vasprintf(&data, fmt, ap); 343 | 344 | if (ret != -1) { 345 | fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], fp, line, func, data); 346 | free(data); 347 | } 348 | 349 | va_end(ap); 350 | 351 | } 352 | 353 | esl_logger_t esl_log = null_logger; 354 | 355 | ESL_DECLARE(void) esl_global_set_logger(esl_logger_t logger) 356 | { 357 | if (logger) { 358 | esl_log = logger; 359 | } else { 360 | esl_log = null_logger; 361 | } 362 | } 363 | 364 | ESL_DECLARE(void) esl_global_set_default_logger(int level) 365 | { 366 | if (level < 0 || level > 7) { 367 | level = 7; 368 | } 369 | 370 | esl_log = default_logger; 371 | esl_log_level = level; 372 | } 373 | 374 | ESL_DECLARE(size_t) esl_url_encode(const char *url, char *buf, size_t len) 375 | { 376 | const char *p; 377 | size_t x = 0; 378 | const char urlunsafe[] = "\r\n \"#%&+:;<=>?@[\\]^`{|}"; 379 | const char hex[] = "0123456789ABCDEF"; 380 | 381 | if (!buf) { 382 | return 0; 383 | } 384 | 385 | if (!url) { 386 | return 0; 387 | } 388 | 389 | len--; 390 | 391 | for (p = url; *p; p++) { 392 | if (x >= len) { 393 | break; 394 | } 395 | if (*p < ' ' || *p > '~' || strchr(urlunsafe, *p)) { 396 | if ((x + 3) >= len) { 397 | break; 398 | } 399 | buf[x++] = '%'; 400 | buf[x++] = hex[*p >> 4]; 401 | buf[x++] = hex[*p & 0x0f]; 402 | } else { 403 | buf[x++] = *p; 404 | } 405 | } 406 | buf[x] = '\0'; 407 | 408 | return x; 409 | } 410 | 411 | ESL_DECLARE(char *)esl_url_decode(char *s) 412 | { 413 | char *o; 414 | unsigned int tmp; 415 | 416 | for (o = s; *s; s++, o++) { 417 | if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) { 418 | *o = (char) tmp; 419 | s += 2; 420 | } else { 421 | *o = *s; 422 | } 423 | } 424 | *o = '\0'; 425 | return s; 426 | } 427 | 428 | static int sock_setup(esl_handle_t *handle) 429 | { 430 | 431 | if (handle->sock == ESL_SOCK_INVALID) { 432 | return ESL_FAIL; 433 | } 434 | 435 | #ifdef WIN32 436 | { 437 | BOOL bOptVal = TRUE; 438 | int bOptLen = sizeof(BOOL); 439 | setsockopt(handle->sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&bOptVal, bOptLen); 440 | } 441 | #else 442 | { 443 | int x = 1; 444 | setsockopt(handle->sock, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x)); 445 | } 446 | #endif 447 | 448 | return ESL_SUCCESS; 449 | } 450 | 451 | ESL_DECLARE(esl_status_t) esl_attach_handle(esl_handle_t *handle, esl_socket_t socket, struct sockaddr_in *addr) 452 | { 453 | 454 | if (!handle || socket == ESL_SOCK_INVALID) { 455 | return ESL_FAIL; 456 | } 457 | 458 | handle->sock = socket; 459 | 460 | if (addr) { 461 | handle->addr = *addr; 462 | } 463 | 464 | if (sock_setup(handle) != ESL_SUCCESS) { 465 | return ESL_FAIL; 466 | } 467 | 468 | if (!handle->mutex) { 469 | esl_mutex_create(&handle->mutex); 470 | } 471 | 472 | if (!handle->packet_buf) { 473 | esl_buffer_create(&handle->packet_buf, BUF_CHUNK, BUF_START, 0); 474 | } 475 | 476 | handle->connected = 1; 477 | 478 | esl_send_recv(handle, "connect\n\n"); 479 | 480 | if (handle->last_sr_event) { 481 | handle->info_event = handle->last_sr_event; 482 | handle->last_sr_event = NULL; 483 | return ESL_SUCCESS; 484 | } 485 | 486 | esl_disconnect(handle); 487 | 488 | return ESL_FAIL; 489 | } 490 | 491 | ESL_DECLARE(esl_status_t) esl_sendevent(esl_handle_t *handle, esl_event_t *event) 492 | { 493 | char *txt; 494 | char *event_buf = NULL; 495 | esl_status_t status = ESL_FAIL; 496 | size_t len = 0; 497 | 498 | if (!handle->connected || !event) { 499 | return ESL_FAIL; 500 | } 501 | 502 | esl_event_serialize(event, &txt, ESL_FALSE); 503 | 504 | esl_log(ESL_LOG_DEBUG, "SEND EVENT\n%s\n", txt); 505 | 506 | len = strlen(txt) + 100; 507 | event_buf = malloc(len); 508 | assert(event_buf); 509 | 510 | if (!event_buf) { 511 | return ESL_FAIL; 512 | } 513 | 514 | memset(event_buf, 0, len); 515 | 516 | snprintf(event_buf, len, "sendevent %s\n%s", esl_event_name(event->event_id), txt); 517 | 518 | status = esl_send_recv(handle, event_buf); 519 | 520 | free(txt); 521 | free(event_buf); 522 | 523 | return status; 524 | 525 | } 526 | 527 | ESL_DECLARE(esl_status_t) esl_execute(esl_handle_t *handle, const char *app, const char *arg, const char *uuid) 528 | { 529 | char cmd_buf[128] = "sendmsg"; 530 | char app_buf[512] = ""; 531 | char arg_buf[4096] = ""; 532 | const char *el_buf = "event-lock: true\n"; 533 | const char *bl_buf = "async: true\n"; 534 | char send_buf[5120] = ""; 535 | 536 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 537 | return ESL_FAIL; 538 | } 539 | 540 | if (uuid) { 541 | snprintf(cmd_buf, sizeof(cmd_buf), "sendmsg %s", uuid); 542 | } 543 | 544 | if (app) { 545 | snprintf(app_buf, sizeof(app_buf), "execute-app-name: %s\n", app); 546 | } 547 | 548 | if (arg) { 549 | snprintf(arg_buf, sizeof(arg_buf), "execute-app-arg: %s\n", arg); 550 | } 551 | 552 | snprintf(send_buf, sizeof(send_buf), "%s\ncall-command: execute\n%s%s%s%s\n", 553 | cmd_buf, app_buf, arg_buf, handle->event_lock ? el_buf : "", handle->async_execute ? bl_buf : ""); 554 | 555 | return esl_send_recv(handle, send_buf); 556 | } 557 | 558 | 559 | ESL_DECLARE(esl_status_t) esl_sendmsg(esl_handle_t *handle, esl_event_t *event, const char *uuid) 560 | { 561 | char *cmd_buf = NULL; 562 | char *txt; 563 | size_t len = 0; 564 | esl_status_t status = ESL_FAIL; 565 | 566 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 567 | return ESL_FAIL; 568 | } 569 | 570 | esl_event_serialize(event, &txt, ESL_FALSE); 571 | len = strlen(txt) + 100; 572 | cmd_buf = malloc(len); 573 | assert(cmd_buf); 574 | 575 | if (!cmd_buf) { 576 | return ESL_FAIL; 577 | } 578 | 579 | memset(cmd_buf, 0, len); 580 | 581 | if (uuid) { 582 | snprintf(cmd_buf, len, "sendmsg %s\n%s", uuid, txt); 583 | } else { 584 | snprintf(cmd_buf, len, "sendmsg\n%s", txt); 585 | } 586 | 587 | esl_log(ESL_LOG_DEBUG, "%s%s\n", cmd_buf, txt); 588 | 589 | status = esl_send_recv(handle, cmd_buf); 590 | 591 | free(txt); 592 | free(cmd_buf); 593 | 594 | return status; 595 | } 596 | 597 | 598 | ESL_DECLARE(esl_status_t) esl_filter(esl_handle_t *handle, const char *header, const char *value) 599 | { 600 | char send_buf[1024] = ""; 601 | 602 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 603 | return ESL_FAIL; 604 | } 605 | 606 | snprintf(send_buf, sizeof(send_buf), "filter %s %s\n\n", header, value); 607 | 608 | return esl_send_recv(handle, send_buf); 609 | } 610 | 611 | 612 | ESL_DECLARE(esl_status_t) esl_events(esl_handle_t *handle, esl_event_type_t etype, const char *value) 613 | { 614 | char send_buf[1024] = ""; 615 | const char *type = "plain"; 616 | 617 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 618 | return ESL_FAIL; 619 | } 620 | 621 | if (etype == ESL_EVENT_TYPE_XML) { 622 | type = "xml"; 623 | } else if (etype == ESL_EVENT_TYPE_JSON) { 624 | type = "json"; 625 | } 626 | 627 | snprintf(send_buf, sizeof(send_buf), "event %s %s\n\n", type, value); 628 | 629 | return esl_send_recv(handle, send_buf); 630 | } 631 | 632 | static int esl_socket_reuseaddr(esl_socket_t socket) 633 | { 634 | #ifdef WIN32 635 | BOOL reuse_addr = TRUE; 636 | return setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse_addr, sizeof(reuse_addr)); 637 | #else 638 | int reuse_addr = 1; 639 | return setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); 640 | #endif 641 | } 642 | 643 | struct thread_handler { 644 | esl_listen_callback_t callback; 645 | esl_socket_t server_sock; 646 | esl_socket_t client_sock; 647 | struct sockaddr_in addr; 648 | void *user_data; 649 | }; 650 | 651 | static void *client_thread(esl_thread_t *me, void *obj) 652 | { 653 | struct thread_handler *handler = (struct thread_handler *) obj; 654 | 655 | handler->callback(handler->server_sock, handler->client_sock, &handler->addr, handler->user_data); 656 | free(handler); 657 | 658 | return NULL; 659 | 660 | } 661 | 662 | static int prepare_sock(esl_socket_t sock) 663 | { 664 | int r = 0; 665 | 666 | #ifdef WIN32 667 | u_long arg = 1; 668 | if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) { 669 | r = -1; 670 | } 671 | #else 672 | int fd_flags = fcntl(sock, F_GETFL, 0); 673 | if (fcntl(sock, F_SETFL, fd_flags | O_NONBLOCK)) { 674 | r = -1; 675 | } 676 | #endif 677 | 678 | return r; 679 | 680 | } 681 | 682 | 683 | ESL_DECLARE(esl_status_t) esl_listen(const char *host, esl_port_t port, esl_listen_callback_t callback, void *user_data, esl_socket_t *server_sockP) 684 | { 685 | esl_socket_t server_sock = ESL_SOCK_INVALID; 686 | struct sockaddr_in addr; 687 | esl_status_t status = ESL_SUCCESS; 688 | 689 | 690 | if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 691 | return ESL_FAIL; 692 | } 693 | 694 | if (server_sockP) { 695 | *server_sockP = server_sock; 696 | } 697 | 698 | 699 | esl_socket_reuseaddr(server_sock); 700 | 701 | memset(&addr, 0, sizeof(addr)); 702 | addr.sin_family = AF_INET; 703 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 704 | addr.sin_port = htons(port); 705 | 706 | if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 707 | status = ESL_FAIL; 708 | goto end; 709 | } 710 | 711 | if (listen(server_sock, 10000) < 0) { 712 | status = ESL_FAIL; 713 | goto end; 714 | } 715 | 716 | for (;;) { 717 | int client_sock; 718 | struct sockaddr_in echoClntAddr; 719 | #ifdef WIN32 720 | int clntLen; 721 | #else 722 | unsigned int clntLen; 723 | #endif 724 | 725 | clntLen = sizeof(echoClntAddr); 726 | 727 | if ((client_sock = accept(server_sock, (struct sockaddr *) &echoClntAddr, &clntLen)) == ESL_SOCK_INVALID && errno != EINTR) { 728 | status = ESL_FAIL; 729 | goto end; 730 | } 731 | prepare_sock(client_sock); 732 | callback(server_sock, client_sock, &echoClntAddr, user_data); 733 | } 734 | 735 | end: 736 | 737 | if (server_sock != ESL_SOCK_INVALID) { 738 | closesocket(server_sock); 739 | } 740 | 741 | return status; 742 | 743 | } 744 | 745 | ESL_DECLARE(esl_status_t) esl_listen_threaded(const char *host, esl_port_t port, esl_listen_callback_t callback, void *user_data, int max) 746 | { 747 | esl_socket_t server_sock = ESL_SOCK_INVALID; 748 | struct sockaddr_in addr; 749 | esl_status_t status = ESL_SUCCESS; 750 | struct thread_handler *handler = NULL; 751 | 752 | if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 753 | return ESL_FAIL; 754 | } 755 | 756 | esl_socket_reuseaddr(server_sock); 757 | 758 | memset(&addr, 0, sizeof(addr)); 759 | addr.sin_family = AF_INET; 760 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 761 | addr.sin_port = htons(port); 762 | 763 | if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 764 | status = ESL_FAIL; 765 | goto end; 766 | } 767 | 768 | if (listen(server_sock, max) < 0) { 769 | status = ESL_FAIL; 770 | goto end; 771 | } 772 | 773 | for (;;) { 774 | int client_sock; 775 | struct sockaddr_in echoClntAddr; 776 | #ifdef WIN32 777 | int clntLen; 778 | #else 779 | unsigned int clntLen; 780 | #endif 781 | 782 | clntLen = sizeof(echoClntAddr); 783 | 784 | if ((client_sock = accept(server_sock, (struct sockaddr *) &echoClntAddr, &clntLen)) == ESL_SOCK_INVALID && errno != EINTR) { 785 | status = ESL_FAIL; 786 | goto end; 787 | } 788 | 789 | prepare_sock(client_sock); 790 | 791 | handler = malloc(sizeof(*handler)); 792 | esl_assert(handler); 793 | 794 | memset(handler, 0, sizeof(*handler)); 795 | handler->callback = callback; 796 | handler->server_sock = server_sock; 797 | handler->client_sock = client_sock; 798 | handler->addr = echoClntAddr; 799 | handler->user_data = user_data; 800 | esl_thread_create_detached(client_thread, handler); 801 | } 802 | 803 | end: 804 | 805 | if (server_sock != ESL_SOCK_INVALID) { 806 | closesocket(server_sock); 807 | } 808 | 809 | return status; 810 | 811 | } 812 | 813 | 814 | /* USE WSAPoll on vista or higher */ 815 | #ifdef ESL_USE_WSAPOLL 816 | ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) 817 | { 818 | } 819 | #endif 820 | 821 | 822 | #ifdef ESL_USE_SELECT 823 | #ifdef WIN32 824 | #pragma warning( push ) 825 | #pragma warning( disable : 6262 ) /* warning C6262: Function uses '98348' bytes of stack: exceeds /analyze:stacksize'16384'. Consider moving some data to heap */ 826 | #endif 827 | ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) 828 | { 829 | int s = 0, r = 0; 830 | fd_set rfds; 831 | fd_set wfds; 832 | fd_set efds; 833 | struct timeval tv; 834 | 835 | if (sock == ESL_SOCK_INVALID) { 836 | return ESL_SOCK_INVALID; 837 | } 838 | 839 | FD_ZERO(&rfds); 840 | FD_ZERO(&wfds); 841 | FD_ZERO(&efds); 842 | 843 | #ifndef WIN32 844 | /* Wouldn't you rather know?? */ 845 | assert(sock <= FD_SETSIZE); 846 | #endif 847 | 848 | if ((flags & ESL_POLL_READ)) { 849 | 850 | #ifdef WIN32 851 | #pragma warning( push ) 852 | #pragma warning( disable : 4127 ) 853 | FD_SET(sock, &rfds); 854 | #pragma warning( pop ) 855 | #else 856 | FD_SET(sock, &rfds); 857 | #endif 858 | } 859 | 860 | if ((flags & ESL_POLL_WRITE)) { 861 | 862 | #ifdef WIN32 863 | #pragma warning( push ) 864 | #pragma warning( disable : 4127 ) 865 | FD_SET(sock, &wfds); 866 | #pragma warning( pop ) 867 | #else 868 | FD_SET(sock, &wfds); 869 | #endif 870 | } 871 | 872 | if ((flags & ESL_POLL_ERROR)) { 873 | 874 | #ifdef WIN32 875 | #pragma warning( push ) 876 | #pragma warning( disable : 4127 ) 877 | FD_SET(sock, &efds); 878 | #pragma warning( pop ) 879 | #else 880 | FD_SET(sock, &efds); 881 | #endif 882 | } 883 | 884 | tv.tv_sec = ms / 1000; 885 | tv.tv_usec = (ms % 1000) * ms; 886 | 887 | s = select(sock + 1, (flags & ESL_POLL_READ) ? &rfds : NULL, (flags & ESL_POLL_WRITE) ? &wfds : NULL, (flags & ESL_POLL_ERROR) ? &efds : NULL, &tv); 888 | 889 | if (s < 0) { 890 | r = s; 891 | } else if (s > 0) { 892 | if ((flags & ESL_POLL_READ) && FD_ISSET(sock, &rfds)) { 893 | r |= ESL_POLL_READ; 894 | } 895 | 896 | if ((flags & ESL_POLL_WRITE) && FD_ISSET(sock, &wfds)) { 897 | r |= ESL_POLL_WRITE; 898 | } 899 | 900 | if ((flags & ESL_POLL_ERROR) && FD_ISSET(sock, &efds)) { 901 | r |= ESL_POLL_ERROR; 902 | } 903 | } 904 | 905 | return r; 906 | 907 | } 908 | #ifdef WIN32 909 | #pragma warning( pop ) 910 | #endif 911 | #endif 912 | 913 | #ifdef ESL_USE_POLL 914 | ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) 915 | { 916 | struct pollfd pfds[2] = { { 0 } }; 917 | int s = 0, r = 0; 918 | 919 | if (sock == ESL_SOCK_INVALID) { 920 | return ESL_SOCK_INVALID; 921 | } 922 | 923 | pfds[0].fd = sock; 924 | 925 | if ((flags & ESL_POLL_READ)) { 926 | pfds[0].events |= POLLIN; 927 | } 928 | 929 | if ((flags & ESL_POLL_WRITE)) { 930 | pfds[0].events |= POLLOUT; 931 | } 932 | 933 | if ((flags & ESL_POLL_ERROR)) { 934 | pfds[0].events |= POLLERR; 935 | } 936 | 937 | s = poll(pfds, 1, ms); 938 | 939 | if (s < 0) { 940 | r = s; 941 | } else if (s > 0) { 942 | if ((pfds[0].revents & POLLIN)) { 943 | r |= ESL_POLL_READ; 944 | } 945 | if ((pfds[0].revents & POLLOUT)) { 946 | r |= ESL_POLL_WRITE; 947 | } 948 | if ((pfds[0].revents & POLLERR)) { 949 | r |= ESL_POLL_ERROR; 950 | } 951 | } 952 | 953 | return r; 954 | 955 | } 956 | #endif 957 | 958 | 959 | ESL_DECLARE(esl_status_t) esl_connect_timeout(esl_handle_t *handle, const char *host, esl_port_t port, const char *user, const char *password, uint32_t timeout) 960 | { 961 | char sendbuf[256]; 962 | int rval = 0; 963 | const char *hval; 964 | struct addrinfo hints = { 0 }, *result; 965 | struct sockaddr_in *sockaddr_in; 966 | struct sockaddr_in6 *sockaddr_in6; 967 | socklen_t socklen; 968 | #ifndef WIN32 969 | int fd_flags = 0; 970 | #else 971 | WORD wVersionRequested = MAKEWORD(2, 0); 972 | WSADATA wsaData; 973 | int err = WSAStartup(wVersionRequested, &wsaData); 974 | if (err != 0) { 975 | snprintf(handle->err, sizeof(handle->err), "WSAStartup Error"); 976 | goto fail; 977 | } 978 | 979 | #endif 980 | 981 | if (!handle->mutex) { 982 | esl_mutex_create(&handle->mutex); 983 | } 984 | 985 | if (!handle->packet_buf) { 986 | esl_buffer_create(&handle->packet_buf, BUF_CHUNK, BUF_START, 0); 987 | } 988 | 989 | hints.ai_socktype = SOCK_STREAM; 990 | 991 | if (getaddrinfo(host, NULL, &hints, &result)) { 992 | strncpy(handle->err, "Cannot resolve host", sizeof(handle->err)); 993 | goto fail; 994 | } 995 | 996 | memcpy(&handle->sockaddr, result->ai_addr, result->ai_addrlen); 997 | switch(handle->sockaddr.ss_family) { 998 | case AF_INET: 999 | sockaddr_in = (struct sockaddr_in*)&(handle->sockaddr); 1000 | sockaddr_in->sin_port = htons(port); 1001 | socklen = sizeof(struct sockaddr_in); 1002 | break; 1003 | case AF_INET6: 1004 | sockaddr_in6 = (struct sockaddr_in6*)&(handle->sockaddr); 1005 | sockaddr_in6->sin6_port = htons(port); 1006 | socklen = sizeof(struct sockaddr_in6); 1007 | break; 1008 | default: 1009 | strncpy(handle->err, "Host resolves to unsupported address family", sizeof(handle->err)); 1010 | goto fail; 1011 | } 1012 | freeaddrinfo(result); 1013 | 1014 | handle->sock = socket(handle->sockaddr.ss_family, SOCK_STREAM, IPPROTO_TCP); 1015 | 1016 | if (handle->sock == ESL_SOCK_INVALID) { 1017 | snprintf(handle->err, sizeof(handle->err), "Socket Error"); 1018 | goto fail; 1019 | } 1020 | 1021 | if (timeout) { 1022 | #ifdef WIN32 1023 | u_long arg = 1; 1024 | if (ioctlsocket(handle->sock, FIONBIO, &arg) == SOCKET_ERROR) { 1025 | snprintf(handle->err, sizeof(handle->err), "Socket Connection Error"); 1026 | goto fail; 1027 | } 1028 | #else 1029 | fd_flags = fcntl(handle->sock, F_GETFL, 0); 1030 | if (fcntl(handle->sock, F_SETFL, fd_flags | O_NONBLOCK)) { 1031 | snprintf(handle->err, sizeof(handle->err), "Socket Connection Error"); 1032 | goto fail; 1033 | } 1034 | #endif 1035 | } 1036 | 1037 | rval = connect(handle->sock, (struct sockaddr*)&handle->sockaddr, socklen); 1038 | 1039 | if (timeout) { 1040 | int r; 1041 | 1042 | 1043 | r = esl_wait_sock(handle->sock, timeout, ESL_POLL_WRITE); 1044 | 1045 | if (r <= 0) { 1046 | snprintf(handle->err, sizeof(handle->err), "Connection timed out"); 1047 | goto fail; 1048 | } 1049 | 1050 | if (!(r & ESL_POLL_WRITE)) { 1051 | snprintf(handle->err, sizeof(handle->err), "Connection timed out"); 1052 | goto fail; 1053 | } 1054 | 1055 | #ifdef WIN32 1056 | { 1057 | u_long arg = 0; 1058 | if (ioctlsocket(handle->sock, FIONBIO, &arg) == SOCKET_ERROR) { 1059 | snprintf(handle->err, sizeof(handle->err), "Socket Connection Error"); 1060 | goto fail; 1061 | } 1062 | } 1063 | #else 1064 | fcntl(handle->sock, F_SETFL, fd_flags); 1065 | #endif 1066 | rval = 0; 1067 | } 1068 | 1069 | result = NULL; 1070 | 1071 | if (rval) { 1072 | snprintf(handle->err, sizeof(handle->err), "Socket Connection Error"); 1073 | goto fail; 1074 | } 1075 | 1076 | sock_setup(handle); 1077 | 1078 | handle->connected = 1; 1079 | 1080 | if (esl_recv_timed(handle, timeout)) { 1081 | snprintf(handle->err, sizeof(handle->err), "Connection Error"); 1082 | goto fail; 1083 | } 1084 | 1085 | hval = esl_event_get_header(handle->last_event, "content-type"); 1086 | 1087 | if (esl_safe_strcasecmp(hval, "auth/request")) { 1088 | snprintf(handle->err, sizeof(handle->err), "Connection Error"); 1089 | goto fail; 1090 | } 1091 | 1092 | if (esl_strlen_zero(user)) { 1093 | snprintf(sendbuf, sizeof(sendbuf), "auth %s\n\n", password); 1094 | } else { 1095 | snprintf(sendbuf, sizeof(sendbuf), "userauth %s:%s\n\n", user, password); 1096 | } 1097 | 1098 | esl_send(handle, sendbuf); 1099 | 1100 | 1101 | if (esl_recv_timed(handle, timeout)) { 1102 | snprintf(handle->err, sizeof(handle->err), "Authentication Error"); 1103 | goto fail; 1104 | } 1105 | 1106 | 1107 | hval = esl_event_get_header(handle->last_event, "reply-text"); 1108 | 1109 | if (esl_safe_strcasecmp(hval, "+OK accepted")) { 1110 | snprintf(handle->err, sizeof(handle->err), "Authentication Error"); 1111 | goto fail; 1112 | } 1113 | 1114 | return ESL_SUCCESS; 1115 | 1116 | fail: 1117 | 1118 | handle->connected = 0; 1119 | 1120 | return ESL_FAIL; 1121 | } 1122 | 1123 | ESL_DECLARE(esl_status_t) esl_disconnect(esl_handle_t *handle) 1124 | { 1125 | esl_mutex_t *mutex = handle->mutex; 1126 | esl_status_t status = ESL_FAIL; 1127 | esl_event_t *ep; 1128 | 1129 | if (handle->destroyed) { 1130 | return ESL_FAIL; 1131 | } 1132 | 1133 | if (handle->sock != ESL_SOCK_INVALID) { 1134 | closesocket(handle->sock); 1135 | handle->sock = ESL_SOCK_INVALID; 1136 | status = ESL_SUCCESS; 1137 | } 1138 | 1139 | if (mutex) { 1140 | esl_mutex_lock(mutex); 1141 | } 1142 | 1143 | 1144 | handle->connected = 0; 1145 | 1146 | ep = handle->race_event; 1147 | 1148 | while(ep) { 1149 | esl_event_t *e = ep; 1150 | ep = ep->next; 1151 | if (e) { 1152 | esl_event_destroy(&e); 1153 | } 1154 | } 1155 | 1156 | esl_event_safe_destroy(&handle->last_event); 1157 | esl_event_safe_destroy(&handle->last_sr_event); 1158 | esl_event_safe_destroy(&handle->last_ievent); 1159 | esl_event_safe_destroy(&handle->info_event); 1160 | 1161 | if (mutex) { 1162 | esl_mutex_unlock(mutex); 1163 | esl_mutex_lock(mutex); 1164 | esl_mutex_unlock(mutex); 1165 | esl_mutex_destroy(&mutex); 1166 | } 1167 | 1168 | if (handle->packet_buf) { 1169 | esl_buffer_destroy(&handle->packet_buf); 1170 | } 1171 | 1172 | memset(handle, 0, sizeof(*handle)); 1173 | handle->destroyed = 1; 1174 | 1175 | return status; 1176 | } 1177 | 1178 | ESL_DECLARE(esl_status_t) esl_recv_event_timed(esl_handle_t *handle, uint32_t ms, int check_q, esl_event_t **save_event) 1179 | { 1180 | int activity; 1181 | esl_status_t status = ESL_SUCCESS; 1182 | 1183 | if (!ms) { 1184 | return esl_recv_event(handle, check_q, save_event); 1185 | } 1186 | 1187 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 1188 | return ESL_FAIL; 1189 | } 1190 | 1191 | if (check_q) { 1192 | esl_mutex_lock(handle->mutex); 1193 | if (handle->race_event || esl_buffer_packet_count(handle->packet_buf)) { 1194 | esl_mutex_unlock(handle->mutex); 1195 | return esl_recv_event(handle, check_q, save_event); 1196 | } 1197 | esl_mutex_unlock(handle->mutex); 1198 | } 1199 | 1200 | if (handle->packet_buf && esl_buffer_inuse(handle->packet_buf)) { 1201 | activity = ESL_POLL_READ; 1202 | } else { 1203 | activity = esl_wait_sock(handle->sock, ms, ESL_POLL_READ|ESL_POLL_ERROR); 1204 | } 1205 | 1206 | if (activity < 0) { 1207 | handle->connected = 0; 1208 | return ESL_FAIL; 1209 | } 1210 | 1211 | if (activity == 0 || !(activity & ESL_POLL_READ) || (esl_mutex_trylock(handle->mutex) != ESL_SUCCESS)) { 1212 | return ESL_BREAK; 1213 | } 1214 | 1215 | if (activity < 0) { 1216 | handle->connected = 0; 1217 | status = ESL_FAIL; 1218 | } else if (activity > 0 && (activity & ESL_POLL_READ)) { 1219 | if (esl_recv_event(handle, check_q, save_event)) { 1220 | status = ESL_FAIL; 1221 | } 1222 | } else { 1223 | status = ESL_BREAK; 1224 | } 1225 | 1226 | if (handle->mutex) esl_mutex_unlock(handle->mutex); 1227 | 1228 | return status; 1229 | 1230 | } 1231 | 1232 | static esl_ssize_t handle_recv(esl_handle_t *handle, void *data, esl_size_t datalen) 1233 | { 1234 | esl_ssize_t activity = -1; 1235 | 1236 | if (handle->connected) { 1237 | if ((activity = esl_wait_sock(handle->sock, 1000, ESL_POLL_READ|ESL_POLL_ERROR)) > 0) { 1238 | if (activity < 0) { 1239 | activity = -1; 1240 | } else if ((activity & ESL_POLL_ERROR)) { 1241 | activity = -1; 1242 | } else if ((activity & ESL_POLL_READ)) { 1243 | if (!(activity = recv(handle->sock, data, datalen, 0))) { 1244 | activity = -1; 1245 | } else if (activity < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) { 1246 | activity = 0; 1247 | } 1248 | } 1249 | } 1250 | 1251 | } 1252 | 1253 | return activity; 1254 | } 1255 | 1256 | ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_event_t **save_event) 1257 | { 1258 | char *c; 1259 | esl_ssize_t rrval; 1260 | esl_event_t *revent = NULL; 1261 | char *beg; 1262 | char *hname, *hval; 1263 | char *col; 1264 | char *cl; 1265 | esl_ssize_t len; 1266 | 1267 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 1268 | return ESL_FAIL; 1269 | } 1270 | 1271 | esl_mutex_lock(handle->mutex); 1272 | 1273 | if (!handle->connected || handle->sock == ESL_SOCK_INVALID) { 1274 | goto fail; 1275 | } 1276 | 1277 | esl_event_safe_destroy(&handle->last_ievent); 1278 | 1279 | if (check_q && handle->race_event) { 1280 | revent = handle->race_event; 1281 | handle->race_event = handle->race_event->next; 1282 | revent->next = NULL; 1283 | 1284 | goto parse_event; 1285 | } 1286 | 1287 | while(!revent && handle->connected) { 1288 | esl_size_t len1; 1289 | 1290 | if ((len1 = esl_buffer_read_packet(handle->packet_buf, handle->socket_buf, sizeof(handle->socket_buf) - 1))) { 1291 | char *data = (char *) handle->socket_buf; 1292 | char *p, *e; 1293 | 1294 | *(data + len1) = '\0'; 1295 | 1296 | esl_event_create(&revent, ESL_EVENT_CLONE); 1297 | revent->event_id = ESL_EVENT_SOCKET_DATA; 1298 | esl_event_add_header_string(revent, ESL_STACK_BOTTOM, "Event-Name", "SOCKET_DATA"); 1299 | 1300 | p = data; 1301 | 1302 | while(p) { 1303 | hname = p; 1304 | p = NULL; 1305 | 1306 | if ((hval = strchr(hname, ':'))) { 1307 | *hval++ = '\0'; 1308 | while(*hval == ' ' || *hval == '\t') hval++; 1309 | 1310 | if ((e = strchr(hval, '\n'))) { 1311 | *e++ = '\0'; 1312 | while(*e == '\n' || *e == '\r') e++; 1313 | 1314 | if (hname && hval) { 1315 | esl_url_decode(hval); 1316 | esl_log(ESL_LOG_DEBUG, "RECV HEADER [%s] = [%s]\n", hname, hval); 1317 | if (!strncmp(hval, "ARRAY::", 7)) { 1318 | esl_event_add_array(revent, hname, hval); 1319 | } else { 1320 | esl_event_add_header_string(revent, ESL_STACK_BOTTOM, hname, hval); 1321 | } 1322 | } 1323 | 1324 | p = e; 1325 | } 1326 | } 1327 | } 1328 | 1329 | break; 1330 | } 1331 | 1332 | rrval = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf) - 1); 1333 | 1334 | if (rrval == 0) { 1335 | continue; 1336 | } else if (rrval < 0) { 1337 | if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err)))) 1338 | *(handle->err)=0; 1339 | goto fail; 1340 | } 1341 | 1342 | *((char *)handle->socket_buf + ESL_CLAMP(0, sizeof(handle->socket_buf) - 1, rrval)) = '\0'; 1343 | 1344 | esl_buffer_write(handle->packet_buf, handle->socket_buf, rrval); 1345 | } 1346 | 1347 | if (!revent) { 1348 | goto fail; 1349 | } 1350 | 1351 | if ((cl = esl_event_get_header(revent, "content-length"))) { 1352 | char *body; 1353 | esl_ssize_t sofar = 0; 1354 | 1355 | len = atol(cl); 1356 | body = malloc(len+1); 1357 | esl_assert(body); 1358 | *(body + len) = '\0'; 1359 | 1360 | do { 1361 | esl_ssize_t r,s = esl_buffer_inuse(handle->packet_buf); 1362 | 1363 | if (s >= len) { 1364 | sofar = esl_buffer_read(handle->packet_buf, body, len); 1365 | } else { 1366 | r = handle_recv(handle, handle->socket_buf, sizeof(handle->socket_buf) - 1); 1367 | 1368 | if (r < 0) { 1369 | if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err)))) 1370 | *(handle->err)=0; 1371 | free(body); 1372 | goto fail; 1373 | } else if (r == 0) { 1374 | continue; 1375 | } 1376 | 1377 | *((char *)handle->socket_buf + ESL_CLAMP(0, sizeof(handle->socket_buf) - 1, r)) = '\0'; 1378 | esl_buffer_write(handle->packet_buf, handle->socket_buf, r); 1379 | } 1380 | 1381 | } while (sofar < len); 1382 | 1383 | revent->body = body; 1384 | } 1385 | 1386 | parse_event: 1387 | 1388 | if (save_event) { 1389 | *save_event = revent; 1390 | revent = NULL; 1391 | } else { 1392 | esl_event_safe_destroy(&handle->last_event); 1393 | handle->last_event = revent; 1394 | } 1395 | 1396 | if (revent) { 1397 | hval = esl_event_get_header(revent, "reply-text"); 1398 | 1399 | if (!esl_strlen_zero(hval)) { 1400 | strncpy(handle->last_reply, hval, sizeof(handle->last_reply)); 1401 | } 1402 | 1403 | hval = esl_event_get_header(revent, "content-type"); 1404 | 1405 | if (!esl_safe_strcasecmp(hval, "text/disconnect-notice") && revent->body) { 1406 | const char *dval = esl_event_get_header(revent, "content-disposition"); 1407 | if (esl_strlen_zero(dval) || strcasecmp(dval, "linger")) { 1408 | goto fail; 1409 | } 1410 | } 1411 | 1412 | if (revent->body) { 1413 | if (!esl_safe_strcasecmp(hval, "text/event-plain")) { 1414 | esl_event_types_t et = ESL_EVENT_CLONE; 1415 | char *body = strdup(revent->body); 1416 | 1417 | esl_event_create(&handle->last_ievent, et); 1418 | 1419 | beg = body; 1420 | 1421 | while(beg) { 1422 | if (!(c = strchr(beg, '\n'))) { 1423 | break; 1424 | } 1425 | 1426 | hname = beg; 1427 | hval = col = NULL; 1428 | 1429 | if (hname && (col = strchr(hname, ':'))) { 1430 | hval = col + 1; 1431 | *col = '\0'; 1432 | while(*hval == ' ') hval++; 1433 | } 1434 | 1435 | *c = '\0'; 1436 | 1437 | if (hname && hval) { 1438 | esl_url_decode(hval); 1439 | esl_log(ESL_LOG_DEBUG, "RECV INNER HEADER [%s] = [%s]\n", hname, hval); 1440 | if (!strcasecmp(hname, "event-name")) { 1441 | esl_event_del_header(handle->last_ievent, "event-name"); 1442 | esl_name_event(hval, &handle->last_ievent->event_id); 1443 | } 1444 | 1445 | if (!strncmp(hval, "ARRAY::", 7)) { 1446 | esl_event_add_array(handle->last_ievent, hname, hval); 1447 | } else { 1448 | esl_event_add_header_string(handle->last_ievent, ESL_STACK_BOTTOM, hname, hval); 1449 | } 1450 | } 1451 | 1452 | beg = c + 1; 1453 | 1454 | if (*beg == '\n') { 1455 | beg++; 1456 | break; 1457 | } 1458 | } 1459 | 1460 | if ((cl = esl_event_get_header(handle->last_ievent, "content-length"))) { 1461 | handle->last_ievent->body = strdup(beg); 1462 | } 1463 | 1464 | free(body); 1465 | 1466 | if (esl_log_level >= 7) { 1467 | char *foo; 1468 | esl_event_serialize(handle->last_ievent, &foo, ESL_FALSE); 1469 | esl_log(ESL_LOG_DEBUG, "RECV EVENT\n%s\n", foo); 1470 | free(foo); 1471 | } 1472 | } else if (!esl_safe_strcasecmp(hval, "text/event-json")) { 1473 | esl_event_create_json(&handle->last_ievent, revent->body); 1474 | } 1475 | } 1476 | 1477 | if (esl_log_level >= 7) { 1478 | char *foo; 1479 | esl_event_serialize(revent, &foo, ESL_FALSE); 1480 | esl_log(ESL_LOG_DEBUG, "RECV MESSAGE\n%s\n", foo); 1481 | free(foo); 1482 | } 1483 | } 1484 | 1485 | esl_mutex_unlock(handle->mutex); 1486 | 1487 | return ESL_SUCCESS; 1488 | 1489 | fail: 1490 | 1491 | esl_mutex_unlock(handle->mutex); 1492 | 1493 | handle->connected = 0; 1494 | 1495 | return ESL_FAIL; 1496 | 1497 | } 1498 | 1499 | ESL_DECLARE(esl_status_t) esl_send(esl_handle_t *handle, const char *cmd) 1500 | { 1501 | const char *e = cmd + strlen(cmd) -1; 1502 | 1503 | 1504 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 1505 | return ESL_FAIL; 1506 | } 1507 | 1508 | esl_log(ESL_LOG_DEBUG, "SEND\n%s\n", cmd); 1509 | 1510 | if (send(handle->sock, cmd, strlen(cmd), 0) != (int)strlen(cmd)) { 1511 | handle->connected = 0; 1512 | if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err)))) 1513 | *(handle->err)=0; 1514 | return ESL_FAIL; 1515 | } 1516 | 1517 | if (!(*e == '\n' && *(e-1) == '\n')) { 1518 | if (send(handle->sock, "\n\n", 2, 0) != 2) { 1519 | handle->connected = 0; 1520 | if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err)))) 1521 | *(handle->err)=0; 1522 | return ESL_FAIL; 1523 | } 1524 | } 1525 | 1526 | return ESL_SUCCESS; 1527 | 1528 | } 1529 | 1530 | 1531 | ESL_DECLARE(esl_status_t) esl_send_recv_timed(esl_handle_t *handle, const char *cmd, uint32_t ms) 1532 | { 1533 | const char *hval; 1534 | esl_status_t status; 1535 | 1536 | if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { 1537 | return ESL_FAIL; 1538 | } 1539 | 1540 | esl_mutex_lock(handle->mutex); 1541 | 1542 | 1543 | if (!handle->connected || handle->sock == ESL_SOCK_INVALID) { 1544 | handle->connected = 0; 1545 | esl_mutex_unlock(handle->mutex); 1546 | return ESL_FAIL; 1547 | } 1548 | 1549 | esl_event_safe_destroy(&handle->last_sr_event); 1550 | 1551 | *handle->last_sr_reply = '\0'; 1552 | 1553 | if ((status = esl_send(handle, cmd))) { 1554 | esl_mutex_unlock(handle->mutex); 1555 | return status; 1556 | } 1557 | 1558 | recv: 1559 | 1560 | esl_event_safe_destroy(&handle->last_sr_event); 1561 | 1562 | *handle->last_sr_reply = '\0'; 1563 | 1564 | status = esl_recv_event_timed(handle, ms, 0, &handle->last_sr_event); 1565 | 1566 | if (handle->last_sr_event) { 1567 | char *ct = esl_event_get_header(handle->last_sr_event,"content-type"); 1568 | 1569 | if (ct && strcasecmp(ct, "api/response") && strcasecmp(ct, "command/reply")) { 1570 | esl_event_t *ep; 1571 | 1572 | for(ep = handle->race_event; ep && ep->next; ep = ep->next); 1573 | 1574 | if (ep) { 1575 | ep->next = handle->last_sr_event; 1576 | } else { 1577 | handle->race_event = handle->last_sr_event; 1578 | } 1579 | 1580 | handle->last_sr_event = NULL; 1581 | 1582 | esl_mutex_unlock(handle->mutex); 1583 | esl_mutex_lock(handle->mutex); 1584 | 1585 | if (!handle->connected || handle->sock == ESL_SOCK_INVALID) { 1586 | handle->connected = 0; 1587 | esl_mutex_unlock(handle->mutex); 1588 | return ESL_FAIL; 1589 | } 1590 | 1591 | goto recv; 1592 | } 1593 | 1594 | if (handle->last_sr_event) { 1595 | hval = esl_event_get_header(handle->last_sr_event, "reply-text"); 1596 | 1597 | if (!esl_strlen_zero(hval)) { 1598 | strncpy(handle->last_sr_reply, hval, sizeof(handle->last_sr_reply)); 1599 | } 1600 | } 1601 | } 1602 | 1603 | esl_mutex_unlock(handle->mutex); 1604 | 1605 | return status; 1606 | } 1607 | 1608 | 1609 | ESL_DECLARE(unsigned int) esl_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen) 1610 | { 1611 | unsigned int count = 0; 1612 | char *d; 1613 | size_t dlen = strlen(delim); 1614 | 1615 | array[count++] = buf; 1616 | 1617 | while (count < arraylen && array[count - 1]) { 1618 | if ((d = strstr(array[count - 1], delim))) { 1619 | *d = '\0'; 1620 | d += dlen; 1621 | array[count++] = d; 1622 | } else 1623 | break; 1624 | } 1625 | 1626 | return count; 1627 | } 1628 | 1629 | /* For Emacs: 1630 | * Local Variables: 1631 | * mode:c 1632 | * indent-tabs-mode:t 1633 | * tab-width:4 1634 | * c-basic-offset:4 1635 | * End: 1636 | * For VIM: 1637 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 1638 | */ 1639 | --------------------------------------------------------------------------------