From 0f12bc31cc9d2dddf7726241b9804b25eab39d6d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 20 Dec 2008 00:35:09 +0000 Subject: [PATCH] add esl and fs_cli git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10890 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/esl/Makefile | 26 ++ libs/esl/fs_cli.c | 245 +++++++++++ libs/esl/src/esl.c | 333 +++++++++++++++ libs/esl/src/esl_event.c | 553 +++++++++++++++++++++++++ libs/esl/src/esl_threadmutex.c | 237 +++++++++++ libs/esl/src/include/esl.h | 233 +++++++++++ libs/esl/src/include/esl_event.h | 284 +++++++++++++ libs/esl/src/include/esl_threadmutex.h | 51 +++ libs/esl/testclient.c | 19 + 9 files changed, 1981 insertions(+) create mode 100644 libs/esl/Makefile create mode 100644 libs/esl/fs_cli.c create mode 100644 libs/esl/src/esl.c create mode 100644 libs/esl/src/esl_event.c create mode 100644 libs/esl/src/esl_threadmutex.c create mode 100644 libs/esl/src/include/esl.h create mode 100644 libs/esl/src/include/esl_event.h create mode 100644 libs/esl/src/include/esl_threadmutex.h create mode 100644 libs/esl/testclient.c diff --git a/libs/esl/Makefile b/libs/esl/Makefile new file mode 100644 index 0000000000..e094058d55 --- /dev/null +++ b/libs/esl/Makefile @@ -0,0 +1,26 @@ +PWD=$(shell pwd) +INCS=-I$(PWD)/src/include +LIBEDIT_DIR=../../libs/libedit +CFLAGS=$(INCS) -g -ggdb -I$(LIBEDIT_DIR)/src/ +MYLIB=libesl.a + +OBJS=src/esl.o src/esl_event.o src/esl_threadmutex.o + +all: $(MYLIB) fs_cli + +$(MYLIB): $(OBJS) $(HEADERS) + ar rcs $(MYLIB) $(OBJS) + ranlib $(MYLIB) + +%.o: %.c + $(CC) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@ + +test: $(MYLIB) + $(CC) $(CC_CFLAGS) $(CFLAGS) testclient.c -o testclient -L. -lesl + +fs_cli: $(MYLIB) fs_cli.c + $(CC) $(CC_CFLAGS) $(CFLAGS) fs_cli.c -o fs_cli -L. -L$(LIBEDIT_DIR)/src/.libs -lesl -ledit -lncurses -lpthread + +clean: + rm -f *.o src/*.o testclient fs_cli libesl.a *~ src/*~ src/include/*~ + diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c new file mode 100644 index 0000000000..aeddc58b1f --- /dev/null +++ b/libs/esl/fs_cli.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include + +#include +static char prompt_str[512] = ""; +static char hostname[512] = ""; + +char *prompt(EditLine * e) +{ + if (*prompt_str == '\0') { + gethostname(hostname, sizeof(hostname)); + snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s> ", hostname); + } + + return prompt_str; + +} + +static EditLine *el; +static History *myhistory; +static HistEvent ev; +static char *hfile = NULL; +static int running = 1; +static int thread_running = 0; +static esl_mutex_t *global_mutex; + +static void handle_SIGINT(int sig) +{ + if (sig); + return; +} + +static const char* COLORS[] = { ESL_SEQ_DEFAULT_COLOR, ESL_SEQ_FRED, ESL_SEQ_FRED, + ESL_SEQ_FRED, ESL_SEQ_FMAGEN, ESL_SEQ_FCYAN, ESL_SEQ_FGREEN, ESL_SEQ_FYELLOW }; + + +static void *msg_thread_run(esl_thread_t *me, void *obj) +{ + + esl_handle_t *handle = (esl_handle_t *) obj; + + thread_running = 1; + + while(thread_running && handle->connected) { + fd_set rfds, efds; + struct timeval tv = { 0, 50 * 1000 }; + int max, activity, i = 0; + + esl_mutex_lock(global_mutex); + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(handle->sock, &rfds); + FD_SET(handle->sock, &efds); + + max = handle->sock + 1; + + if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { + esl_mutex_unlock(global_mutex); + goto done; + } + + + if (activity && FD_ISSET(handle->sock, &rfds)) { + esl_recv(handle); + if (handle->last_event) { + const char *type = esl_event_get_header(handle->last_event, "content-type"); + if (!strcasecmp(type, "log/data")) { + int level; + if (strstr(handle->last_event->body, "[CONSOLE]")) { + level = 0; + } else if (strstr(handle->last_event->body, "[ALERT]")) { + level = 1; + } else if (strstr(handle->last_event->body, "[CRIT]")) { + level = 2; + } else if (strstr(handle->last_event->body, "[ERROR]")) { + level = 3; + } else if (strstr(handle->last_event->body, "[WARNING]")) { + level = 4; + } else if (strstr(handle->last_event->body, "[NOTICE]")) { + level = 5; + } else if (strstr(handle->last_event->body, "[INFO]")) { + level = 6; + } else if (strstr(handle->last_event->body, "[DEBUG]")) { + level = 7; + } + + printf("%s%s%s", COLORS[level], handle->last_event->body, ESL_SEQ_DEFAULT_COLOR); + } + } + + } + + esl_mutex_unlock(global_mutex); + usleep(1000); + } + + done: + + thread_running = 0; + + return NULL; +} + +static int process_command(esl_handle_t *handle, const char *cmd) +{ + if (!strcasecmp(cmd, "exit")) { + return -1; + } + + if (!strncasecmp(cmd, "loglevel", 8)) { + const char *level = cmd + 8; + + while(*level == ' ') level++; + if (!esl_strlen_zero(level)) { + char cb[128] = ""; + + snprintf(cb, sizeof(cb), "log %s\n\n", level); + esl_mutex_lock(global_mutex); + esl_send_recv(handle, cb); + printf("%s\n", handle->last_reply); + esl_mutex_unlock(global_mutex); + } + + goto end; + } + + + printf("Unknown command [%s]\n", cmd); + + end: + + return 0; + +} + +int main(void) +{ + esl_handle_t handle = {0}; + int count; + const char *line; + char cmd_str[1024] = ""; + char hfile[512] = "/tmp/fs_cli_history"; + char *home = getenv("HOME"); + + if (home) { + snprintf(hfile, sizeof(hfile), "%s/.fs_cli_history", home); + } + + esl_mutex_create(&global_mutex); + + signal(SIGINT, handle_SIGINT); + gethostname(hostname, sizeof(hostname)); + + handle.debug = 0; + + + // um ya add some command line parsing for host port and pass + + if (esl_connect(&handle, "localhost", 8021, "ClueCon")) { + printf("Error Connecting [%s]\n", handle.err); + goto done; + } + + esl_thread_create_detached(msg_thread_run, &handle); + + el = el_init(__FILE__, stdout, stdout, stdout); + el_set(el, EL_PROMPT, &prompt); + el_set(el, EL_EDITOR, "emacs"); + myhistory = history_init(); + + if (myhistory == 0) { + fprintf(stderr, "history could not be initialized\n"); + goto done; + } + + history(myhistory, &ev, H_SETSIZE, 800); + el_set(el, EL_HIST, history, myhistory); + history(myhistory, &ev, H_LOAD, hfile); + + + snprintf(cmd_str, sizeof(cmd_str), "log info\n\n"); + esl_mutex_lock(global_mutex); + esl_send_recv(&handle, cmd_str); + esl_mutex_unlock(global_mutex); + + while (running) { + + line = el_gets(el, &count); + + if (count > 1) { + if (!esl_strlen_zero(line)) { + char *cmd = strdup(line); + char *p; + const LineInfo *lf = el_line(el); + char *foo = (char *) lf->buffer; + if ((p = strrchr(cmd, '\r')) || (p = strrchr(cmd, '\n'))) { + *p = '\0'; + } + assert(cmd != NULL); + history(myhistory, &ev, H_ENTER, line); + + if (!strncasecmp(cmd, "...", 3)) { + goto done; + } else if (*cmd == '/') { + if (process_command(&handle, cmd + 1)) { + running = 0; + } + } else { + snprintf(cmd_str, sizeof(cmd_str), "api %s\n\n", cmd); + esl_mutex_lock(global_mutex); + esl_send_recv(&handle, cmd_str); + printf("%s\n", handle.last_event->body); + esl_mutex_unlock(global_mutex); + } + + el_deletestr(el, strlen(foo) + 1); + memset(foo, 0, strlen(foo)); + free(cmd); + } + } + + usleep(1000); + + } + + + done: + + history(myhistory, &ev, H_SAVE, hfile); + + /* Clean up our memory */ + history_end(myhistory); + el_end(el); + + esl_disconnect(&handle); + + thread_running = 0; + + esl_mutex_destroy(&global_mutex); + + return 0; +} diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c new file mode 100644 index 0000000000..703955cac7 --- /dev/null +++ b/libs/esl/src/esl.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef HAVE_GETHOSTBYNAME_R +extern int gethostbyname_r (const char *__name, + struct hostent *__result_buf, + char *__buf, size_t __buflen, + struct hostent **__result, + int *__h_errnop); +#endif + + + +size_t esl_url_encode(const char *url, char *buf, size_t len) +{ + const char *p; + size_t x = 0; + const char urlunsafe[] = "\r\n \"#%&+:;<=>?@[\\]^`{|}"; + const char hex[] = "0123456789ABCDEF"; + + if (!buf) { + return 0; + } + + if (!url) { + return 0; + } + + len--; + + for (p = url; *p; p++) { + if (x >= len) { + break; + } + if (*p < ' ' || *p > '~' || strchr(urlunsafe, *p)) { + if ((x + 3) >= len) { + break; + } + buf[x++] = '%'; + buf[x++] = hex[*p >> 4]; + buf[x++] = hex[*p & 0x0f]; + } else { + buf[x++] = *p; + } + } + buf[x] = '\0'; + + return x; +} + +char *esl_url_decode(char *s) +{ + char *o; + unsigned int tmp; + + for (o = s; *s; s++, o++) { + if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) { + *o = (char) tmp; + s += 2; + } else { + *o = *s; + } + } + *o = '\0'; + return s; +} + + + +esl_status_t esl_connect(esl_handle_t *handle, const char *host, esl_port_t port, const char *password) +{ + + struct hostent *result; + char sendbuf[256]; + char recvbuf[256]; + int rval; + const char *hval; + + handle->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (handle->sock == ESL_SOCK_INVALID) { + snprintf(handle->err, sizeof(handle->err), "Socket Error"); + return ESL_FAIL; + } + + memset(&handle->sockaddr, 0, sizeof(handle->sockaddr)); + handle->sockaddr.sin_family = AF_INET; + handle->sockaddr.sin_port = htons(port); + + memset(&handle->hostent, 0, sizeof(handle->hostent)); + +#ifdef HAVE_GETHOSTBYNAME_R_FIVE + rval = gethostbyname_r(host, &handle->hostent, handle->hostbuf, sizeof(handle->hostbuf), &handle->errno); + result = handle->hostent; +#else + rval = gethostbyname_r(host, &handle->hostent, handle->hostbuf, sizeof(handle->hostbuf), &result, &handle->errno); +#endif + + if (rval) { + strerror_r(handle->errno, handle->err, sizeof(handle->err)); + goto fail; + } + + memcpy(&handle->sockaddr.sin_addr, result->h_addr, result->h_length); + + rval = connect(handle->sock, (struct sockaddr *) &handle->sockaddr, sizeof(handle->sockaddr)); + + if (rval) { + strerror_r(handle->errno, handle->err, sizeof(handle->err)); + goto fail; + } + + if (esl_recv(handle)) { + snprintf(handle->err, sizeof(handle->err), "Connection Error"); + goto fail; + } + + hval = esl_event_get_header(handle->last_event, "content-type"); + + if (strcasecmp(hval, "auth/request")) { + snprintf(handle->err, sizeof(handle->err), "Connection Error"); + goto fail; + } + + snprintf(sendbuf, sizeof(sendbuf), "auth %s\n\n", password); + esl_send(handle, sendbuf); + + + if (esl_recv(handle)) { + snprintf(handle->err, sizeof(handle->err), "Connection Error"); + goto fail; + } + + + hval = esl_event_get_header(handle->last_event, "reply-text"); + + if (strcasecmp(hval, "+OK accepted")) { + snprintf(handle->err, sizeof(handle->err), "Connection Error"); + goto fail; + } + + handle->connected = 1; + + return ESL_SUCCESS; + + fail: + + esl_disconnect(handle); + return ESL_FAIL; +} + +esl_status_t esl_disconnect(esl_handle_t *handle) +{ + esl_event_safe_destroy(&handle->last_event); + + if (handle->sock != ESL_SOCK_INVALID) { + close(handle->sock); + handle->sock = ESL_SOCK_INVALID; + return ESL_SUCCESS; + } + + handle->connected = 0; + + return ESL_FAIL; +} + +esl_status_t esl_recv(esl_handle_t *handle) +{ + char *c; + esl_ssize_t rrval; + int crc = 0; + esl_event_t *revent = NULL; + char *beg; + char *hname, *hval; + char *col; + char *cl; + ssize_t len; + + esl_event_safe_destroy(&handle->last_event); + memset(handle->header_buf, 0, sizeof(handle->header_buf)); + + c = handle->header_buf; + beg = c; + + for(;;) { + rrval = recv(handle->sock, c, 1, 0); + + if (rrval < 0) { + strerror_r(handle->errno, handle->err, sizeof(handle->err)); + goto fail; + } else if (rrval > 0) { + if (*c == '\n') { + if (++crc == 2) { + break; + } + + if (!revent) { + esl_event_create(&revent, ESL_EVENT_COMMAND); + } + + hname = beg; + hval = col = NULL; + + if (hname && (col = strchr(hname, ':'))) { + hval = col + 1; + *col = '\0'; + while(*hval == ' ') hval++; + } + + *c = '\0'; + + if (hname && hval) { + if (handle->debug > 1) { + printf("RECV HEADER [%s] = [%s]\n", hname, hval); + } + esl_event_add_header_string(revent, ESL_STACK_BOTTOM, hname, hval); + } + + beg = c+1; + + + } else { + crc = 0; + } + + c++; + } + } + + if (!revent) { + goto fail; + } + + if ((cl = esl_event_get_header(revent, "content-length"))) { + char *body; + esl_ssize_t sofar = 0; + + len = atol(cl); + body = malloc(len+1); + esl_assert(body); + *(body + len) = '\0'; + + do { + esl_ssize_t r; + if ((r = recv(handle->sock, body + sofar, len - sofar, 0)) < 0) { + strerror_r(handle->errno, handle->err, sizeof(handle->err)); + goto fail; + } + sofar += r; + } while (sofar < len); + + revent->body = body; + } + + + + handle->last_event = revent; + + if (handle->last_event) { + const char *reply = esl_event_get_header(handle->last_event, "reply-text"); + if (!esl_strlen_zero(reply)) { + strncpy(handle->last_reply, reply, sizeof(handle->last_reply)); + } + } + + + if (handle->debug) { + char *foo; + esl_event_serialize(handle->last_event, &foo, ESL_FALSE); + printf("RECV MESSAGE\n%s\n", foo); + free(foo); + } + + return ESL_SUCCESS; + + fail: + + esl_disconnect(handle); + return ESL_FAIL; + +} + +esl_status_t esl_send(esl_handle_t *handle, const char *cmd) +{ + if (handle->debug) { + printf("SEND\n%s\n", cmd); + } + + send(handle->sock, cmd, strlen(cmd), 0); +} + + +esl_status_t esl_send_recv(esl_handle_t *handle, const char *cmd) +{ + esl_send(handle, cmd); + esl_recv(handle); +} + + + + diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c new file mode 100644 index 0000000000..90a2e1a0c3 --- /dev/null +++ b/libs/esl/src/esl_event.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +static char *my_dup(const char *s) +{ + size_t len = strlen(s) + 1; + void *new = malloc(len); + esl_assert(new); + + return (char *) memcpy(new, s, len); +} + +#ifndef ALLOC +#define ALLOC(size) malloc(size) +#endif +#ifndef DUP +#define DUP(str) my_dup(str) +#endif +#ifndef FREE +#define FREE(ptr) esl_safe_free(ptr) +#endif + +/* make sure this is synced with the esl_event_types_t enum in esl_types.h + also never put any new ones before EVENT_ALL +*/ +static char *EVENT_NAMES[] = { + "CUSTOM", + "CHANNEL_CREATE", + "CHANNEL_DESTROY", + "CHANNEL_STATE", + "CHANNEL_ANSWER", + "CHANNEL_HANGUP", + "CHANNEL_EXECUTE", + "CHANNEL_EXECUTE_COMPLETE", + "CHANNEL_BRIDGE", + "CHANNEL_UNBRIDGE", + "CHANNEL_PROGRESS", + "CHANNEL_PROGRESS_MEDIA", + "CHANNEL_OUTGOING", + "CHANNEL_PARK", + "CHANNEL_UNPARK", + "CHANNEL_APPLICATION", + "CHANNEL_ORIGINATE", + "CHANNEL_UUID", + "API", + "LOG", + "INBOUND_CHAN", + "OUTBOUND_CHAN", + "STARTUP", + "SHUTDOWN", + "PUBLISH", + "UNPUBLISH", + "TALK", + "NOTALK", + "SESSION_CRASH", + "MODULE_LOAD", + "MODULE_UNLOAD", + "DTMF", + "MESSAGE", + "PRESENCE_IN", + "NOTIFY_IN", + "PRESENCE_OUT", + "PRESENCE_PROBE", + "MESSAGE_WAITING", + "MESSAGE_QUERY", + "ROSTER", + "CODEC", + "BACKGROUND_JOB", + "DETECTED_SPEECH", + "DETECTED_TONE", + "PRIVATE_COMMAND", + "HEARTBEAT", + "TRAP", + "ADD_SCHEDULE", + "DEL_SCHEDULE", + "EXE_SCHEDULE", + "RE_SCHEDULE", + "RELOADXML", + "NOTIFY", + "SEND_MESSAGE", + "RECV_MESSAGE", + "REQUEST_PARAMS", + "CHANNEL_DATA", + "GENERAL", + "COMMAND", + "SESSION_HEARTBEAT", + "ALL" +}; + +const char *esl_event_name(esl_event_types_t event) +{ + return EVENT_NAMES[event]; +} + +esl_status_t esl_name_event(const char *name, esl_event_types_t *type) +{ + esl_event_types_t x; + + for (x = 0; x <= ESL_EVENT_ALL; x++) { + if ((strlen(name) > 13 && !strcasecmp(name + 13, EVENT_NAMES[x])) || !strcasecmp(name, EVENT_NAMES[x])) { + *type = x; + return ESL_SUCCESS; + } + } + + return ESL_FAIL; +} + + +esl_status_t esl_event_create_subclass(esl_event_t **event, esl_event_types_t event_id, const char *subclass_name) +{ + *event = NULL; + + if (event_id != ESL_EVENT_CUSTOM && subclass_name) { + return ESL_FAIL; + } + + *event = ALLOC(sizeof(esl_event_t)); + esl_assert(*event); + + + memset(*event, 0, sizeof(esl_event_t)); + + (*event)->event_id = event_id; + + if (subclass_name) { + (*event)->subclass_name = DUP(subclass_name); + esl_event_add_header_string(*event, ESL_STACK_BOTTOM, "Event-Subclass", subclass_name); + } + + return ESL_SUCCESS; +} + + +const char *esl_priority_name(esl_priority_t priority) +{ + switch (priority) { /*lol */ + case ESL_PRIORITY_NORMAL: + return "NORMAL"; + case ESL_PRIORITY_LOW: + return "LOW"; + case ESL_PRIORITY_HIGH: + return "HIGH"; + default: + return "INVALID"; + } +} + +esl_status_t esl_event_set_priority(esl_event_t *event, esl_priority_t priority) +{ + event->priority = priority; + esl_event_add_header_string(event, ESL_STACK_TOP, "priority", esl_priority_name(priority)); + return ESL_SUCCESS; +} + +#define ESL_HASH_KEY_STRING -1 + +static unsigned int esl_ci_hashfunc_default(const char *char_key, esl_ssize_t *klen) + +{ + unsigned int hash = 0; + const unsigned char *key = (const unsigned char *)char_key; + const unsigned char *p; + esl_ssize_t i; + + if (*klen == ESL_HASH_KEY_STRING) { + for (p = key; *p; p++) { + hash = hash * 33 + tolower(*p); + } + *klen = p - key; + } + else { + for (p = key, i = *klen; i; i--, p++) { + hash = hash * 33 + tolower(*p); + } + } + + return hash; +} + + +char *esl_event_get_header(esl_event_t *event, const char *header_name) +{ + esl_event_header_t *hp; + esl_ssize_t hlen = -1; + unsigned long hash = 0; + + esl_assert(event); + + if (!header_name) return NULL; + + hash = esl_ci_hashfunc_default(header_name, &hlen); + + for (hp = event->headers; hp; hp = hp->next) { + if ((!hp->hash || hash == hp->hash) && !strcasecmp(hp->name, header_name) ) { + return hp->value; + } + } + return NULL; +} + +char *esl_event_get_body(esl_event_t *event) +{ + return (event ? event->body : NULL); +} + +esl_status_t esl_event_del_header(esl_event_t *event, const char *header_name) +{ + esl_event_header_t *hp, *lp = NULL, *tp; + esl_status_t status = ESL_FAIL; + int x = 0; + esl_ssize_t hlen = -1; + unsigned long hash = 0; + + tp = event->headers; + while (tp) { + hp = tp; + tp = tp->next; + + x++; + esl_assert(x < 1000); + hash = esl_ci_hashfunc_default(header_name, &hlen); + + if ((!hp->hash || hash == hp->hash) && !strcasecmp(header_name, hp->name)) { + if (lp) { + lp->next = hp->next; + } else { + event->headers = hp->next; + } + if (hp == event->last_header || !hp->next) { + event->last_header = lp; + } + FREE(hp->name); + FREE(hp->value); + memset(hp, 0, sizeof(*hp)); + FREE(hp); + + status = ESL_SUCCESS; + } else { + lp = hp; + } + } + + return status; +} + +esl_status_t esl_event_base_add_header(esl_event_t *event, esl_stack_t stack, const char *header_name, char *data) +{ + esl_event_header_t *header; + esl_ssize_t hlen = -1; + + header = ALLOC(sizeof(*header)); + esl_assert(header); + + memset(header, 0, sizeof(*header)); + + header->name = DUP(header_name); + header->value = data; + header->hash = esl_ci_hashfunc_default(header->name, &hlen); + + if (stack == ESL_STACK_TOP) { + header->next = event->headers; + event->headers = header; + if (!event->last_header) { + event->last_header = header; + } + } else { + if (event->last_header) { + event->last_header->next = header; + } else { + event->headers = header; + header->next = NULL; + } + event->last_header = header; + } + + return ESL_SUCCESS; +} + +esl_status_t esl_event_add_header(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *fmt, ...) +{ + int ret = 0; + char *data; + va_list ap; + + va_start(ap, fmt); + ret = vasprintf(&data, fmt, ap); + va_end(ap); + + if (ret == -1) { + return ESL_FAIL; + } + + return esl_event_base_add_header(event, stack, header_name, data); +} + +esl_status_t esl_event_add_header_string(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *data) +{ + if (data) { + return esl_event_base_add_header(event, stack, header_name, DUP(data)); + } + return ESL_FAIL; +} + +esl_status_t esl_event_add_body(esl_event_t *event, const char *fmt, ...) +{ + int ret = 0; + char *data; + + va_list ap; + if (fmt) { + va_start(ap, fmt); + ret = vasprintf(&data, fmt, ap); + va_end(ap); + + if (ret == -1) { + return ESL_FAIL; + } else { + esl_safe_free(event->body); + event->body = data; + return ESL_SUCCESS; + } + } else { + return ESL_FAIL; + } +} + +void esl_event_destroy(esl_event_t **event) +{ + esl_event_t *ep = *event; + esl_event_header_t *hp, *this; + + if (ep) { + for (hp = ep->headers; hp;) { + this = hp; + hp = hp->next; + FREE(this->name); + FREE(this->value); + memset(this, 0, sizeof(*this)); + FREE(this); + } + FREE(ep->body); + FREE(ep->subclass_name); + memset(ep, 0, sizeof(*ep)); + FREE(ep); + } + *event = NULL; +} + + + +esl_status_t esl_event_dup(esl_event_t **event, esl_event_t *todup) +{ + esl_event_header_t *header, *hp, *hp2, *last = NULL; + + if (esl_event_create_subclass(event, todup->event_id, todup->subclass_name) != ESL_SUCCESS) { + return ESL_FAIL; + } + + if (todup->subclass_name) { + (*event)->subclass_name = DUP(todup->subclass_name); + } + (*event)->event_user_data = todup->event_user_data; + (*event)->bind_user_data = todup->bind_user_data; + + hp2 = (*event)->headers; + + for (hp = todup->headers; hp; hp = hp->next) { + header = ALLOC(sizeof(*header)); + esl_assert(header); + + memset(header, 0, sizeof(*header)); + + header->name = DUP(hp->name); + header->value = DUP(hp->value); + + if (last) { + last->next = header; + } else { + (*event)->headers = header; + } + + (*event)->last_header = last = header; + } + + if (todup->body) { + (*event)->body = DUP(todup->body); + } + + (*event)->key = todup->key; + + return ESL_SUCCESS; +} + +esl_status_t esl_event_serialize(esl_event_t *event, char **str, esl_bool_t encode) +{ + size_t len = 0; + esl_event_header_t *hp; + size_t llen = 0, dlen = 0, blocksize = 512, encode_len = 1536, new_len = 0; + char *buf; + char *encode_buf = NULL; /* used for url encoding of variables to make sure unsafe things stay out of the serialized copy */ + + *str = NULL; + + dlen = blocksize * 2; + + if (!(buf = malloc(dlen))) { + return ESL_FAIL; + } + + /* go ahead and give ourselves some space to work with, should save a few reallocs */ + if (!(encode_buf = malloc(encode_len))) { + esl_safe_free(buf); + return ESL_FAIL; + } + + /* esl_log_printf(ESL_CHANNEL_LOG, ESL_LOG_INFO, "hit serialized!.\n"); */ + for (hp = event->headers; hp; hp = hp->next) { + /* + * grab enough memory to store 3x the string (url encode takes one char and turns it into %XX) + * so we could end up with a string that is 3 times the originals length, unlikely but rather + * be safe than destroy the string, also add one for the null. And try to be smart about using + * the memory, allocate and only reallocate if we need more. This avoids an alloc, free CPU + * destroying loop. + */ + new_len = (strlen(hp->value) * 3) + 1; + + if (encode_len < new_len) { + char *tmp; + /* esl_log_printf(ESL_CHANNEL_LOG, ESL_LOG_INFO, "Allocing %d was %d.\n", ((strlen(hp->value) * 3) + 1), encode_len); */ + /* we can use realloc for initial alloc as well, if encode_buf is zero it treats it as a malloc */ + + /* keep track of the size of our allocation */ + encode_len = new_len; + + if (!(tmp = realloc(encode_buf, encode_len))) { + /* oh boy, ram's gone, give back what little we grabbed and bail */ + esl_safe_free(buf); + esl_safe_free(encode_buf); + return ESL_FAIL; + } + + encode_buf = tmp; + } + + /* handle any bad things in the string like newlines : etc that screw up the serialized format */ + if (encode) { + esl_url_encode(hp->value, encode_buf, encode_len); + } else { + snprintf(encode_buf, encode_len, "[%s]", hp->value); + } + + llen = strlen(hp->name) + strlen(encode_buf) + 8; + + if ((len + llen) > dlen) { + char *m; + dlen += (blocksize + (len + llen)); + if ((m = realloc(buf, dlen))) { + buf = m; + } else { + /* we seem to be out of memory trying to resize the serialize string, give back what we already have and give up */ + esl_safe_free(buf); + esl_safe_free(encode_buf); + return ESL_FAIL; + } + } + + snprintf(buf + len, dlen - len, "%s: %s\n", hp->name, *encode_buf == '\0' ? "_undef_" : encode_buf); + len = strlen(buf); + } + + /* we are done with the memory we used for encoding, give it back */ + esl_safe_free(encode_buf); + + if (event->body) { + int blen = (int) strlen(event->body); + llen = blen; + + if (blen) { + llen += 25; + } else { + llen += 5; + } + + if ((len + llen) > dlen) { + char *m; + dlen += (blocksize + (len + llen)); + if ((m = realloc(buf, dlen))) { + buf = m; + } else { + esl_safe_free(buf); + return ESL_FAIL; + } + } + + if (blen) { + snprintf(buf + len, dlen - len, "\n%s", event->body); + } else { + snprintf(buf + len, dlen - len, "\n"); + } + } else { + snprintf(buf + len, dlen - len, "\n"); + } + + *str = buf; + + return ESL_SUCCESS; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/esl/src/esl_threadmutex.c b/libs/esl/src/esl_threadmutex.c new file mode 100644 index 0000000000..a24684c521 --- /dev/null +++ b/libs/esl/src/esl_threadmutex.c @@ -0,0 +1,237 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + +#ifdef WIN32 +/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ +#define _WIN32_WINNT 0x0400 +#endif + +#include "esl.h" +#include "esl_threadmutex.h" + +#ifdef WIN32 +#include + +#define ESL_THREAD_CALLING_CONVENTION __stdcall + +struct esl_mutex { + CRITICAL_SECTION mutex; +}; + +#else + +#include + +#define ESL_THREAD_CALLING_CONVENTION + +struct esl_mutex { + pthread_mutex_t mutex; +}; + +#endif + +struct esl_thread { +#ifdef WIN32 + void *handle; +#else + pthread_t handle; +#endif + void *private_data; + esl_thread_function_t function; + size_t stack_size; +#ifndef WIN32 + pthread_attr_t attribute; +#endif +}; + +size_t thread_default_stacksize = 0; + +void esl_thread_override_default_stacksize(size_t size) +{ + thread_default_stacksize = size; +} + +static void * ESL_THREAD_CALLING_CONVENTION thread_launch(void *args) +{ + void *exit_val; + esl_thread_t *thread = (esl_thread_t *)args; + exit_val = thread->function(thread, thread->private_data); +#ifndef WIN32 + pthread_attr_destroy(&thread->attribute); +#endif + free(thread); + + return exit_val; +} + +esl_status_t esl_thread_create_detached(esl_thread_function_t func, void *data) +{ + return esl_thread_create_detached_ex(func, data, thread_default_stacksize); +} + +esl_status_t esl_thread_create_detached_ex(esl_thread_function_t func, void *data, size_t stack_size) +{ + esl_thread_t *thread = NULL; + esl_status_t status = ESL_FAIL; + + if (!func || !(thread = (esl_thread_t *)malloc(sizeof(esl_thread_t)))) { + goto done; + } + + thread->private_data = data; + thread->function = func; + thread->stack_size = stack_size; + +#if defined(WIN32) + thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL); + if (!thread->handle) { + goto fail; + } + CloseHandle(thread->handle); + + status = ESL_SUCCESS; + goto done; +#else + + if (pthread_attr_init(&thread->attribute) != 0) goto fail; + + if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; + + if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; + + if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; + + status = ESL_SUCCESS; + goto done; + failpthread: + pthread_attr_destroy(&thread->attribute); +#endif + + fail: + if (thread) { + free(thread); + } + done: + return status; +} + + +esl_status_t esl_mutex_create(esl_mutex_t **mutex) +{ + esl_status_t status = ESL_FAIL; +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + esl_mutex_t *check = NULL; + + check = (esl_mutex_t *)malloc(sizeof(**mutex)); + if (!check) + goto done; +#ifdef WIN32 + InitializeCriticalSection(&check->mutex); +#else + if (pthread_mutexattr_init(&attr)) + goto done; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + goto fail; + + if (pthread_mutex_init(&check->mutex, &attr)) + goto fail; + + goto success; + + fail: + pthread_mutexattr_destroy(&attr); + goto done; + + success: +#endif + *mutex = check; + status = ESL_SUCCESS; + + done: + return status; +} + +esl_status_t esl_mutex_destroy(esl_mutex_t **mutex) +{ + esl_mutex_t *mp = *mutex; + *mutex = NULL; + if (!mp) { + return ESL_FAIL; + } +#ifdef WIN32 + DeleteCriticalSection(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->mutex)) + return ESL_FAIL; +#endif + free(mp); + return ESL_SUCCESS; +} + +esl_status_t esl_mutex_lock(esl_mutex_t *mutex) +{ +#ifdef WIN32 + EnterCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_lock(&mutex->mutex)) + return ESL_FAIL; +#endif + return ESL_SUCCESS; +} + +esl_status_t esl_mutex_trylock(esl_mutex_t *mutex) +{ +#ifdef WIN32 + if (!TryEnterCriticalSection(&mutex->mutex)) + return ESL_FAIL; +#else + if (pthread_mutex_trylock(&mutex->mutex)) + return ESL_FAIL; +#endif + return ESL_SUCCESS; +} + +esl_status_t esl_mutex_unlock(esl_mutex_t *mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_unlock(&mutex->mutex)) + return ESL_FAIL; +#endif + return ESL_SUCCESS; +} + + + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ diff --git a/libs/esl/src/include/esl.h b/libs/esl/src/include/esl.h new file mode 100644 index 0000000000..4b71364b87 --- /dev/null +++ b/libs/esl/src/include/esl.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ESL_H_ +#define _ESL_H_ + +typedef struct esl_event_header esl_event_header_t; +typedef struct esl_event esl_event_t; + + +#define ESL_SEQ_ESC "\033[" +/* Ansi Control character suffixes */ +#define ESL_SEQ_HOME_CHAR 'H' +#define ESL_SEQ_HOME_CHAR_STR "H" +#define ESL_SEQ_CLEARLINE_CHAR '1' +#define ESL_SEQ_CLEARLINE_CHAR_STR "1" +#define ESL_SEQ_CLEARLINEEND_CHAR "K" +#define ESL_SEQ_CLEARSCR_CHAR0 '2' +#define ESL_SEQ_CLEARSCR_CHAR1 'J' +#define ESL_SEQ_CLEARSCR_CHAR "2J" +#define ESL_SEQ_DEFAULT_COLOR ESL_SEQ_ESC ESL_SEQ_END_COLOR /* Reset to Default fg/bg color */ +#define ESL_SEQ_AND_COLOR ";" /* To add multiple color definitions */ +#define ESL_SEQ_END_COLOR "m" /* To end color definitions */ +/* Foreground colors values */ +#define ESL_SEQ_F_BLACK "30" +#define ESL_SEQ_F_RED "31" +#define ESL_SEQ_F_GREEN "32" +#define ESL_SEQ_F_YELLOW "33" +#define ESL_SEQ_F_BLUE "34" +#define ESL_SEQ_F_MAGEN "35" +#define ESL_SEQ_F_CYAN "36" +#define ESL_SEQ_F_WHITE "37" +/* Background colors values */ +#define ESL_SEQ_B_BLACK "40" +#define ESL_SEQ_B_RED "41" +#define ESL_SEQ_B_GREEN "42" +#define ESL_SEQ_B_YELLOW "43" +#define ESL_SEQ_B_BLUE "44" +#define ESL_SEQ_B_MAGEN "45" +#define ESL_SEQ_B_CYAN "46" +#define ESL_SEQ_B_WHITE "47" +/* Preset escape sequences - Change foreground colors only */ +#define ESL_SEQ_FBLACK ESL_SEQ_ESC ESL_SEQ_F_BLACK ESL_SEQ_END_COLOR +#define ESL_SEQ_FRED ESL_SEQ_ESC ESL_SEQ_F_RED ESL_SEQ_END_COLOR +#define ESL_SEQ_FGREEN ESL_SEQ_ESC ESL_SEQ_F_GREEN ESL_SEQ_END_COLOR +#define ESL_SEQ_FYELLOW ESL_SEQ_ESC ESL_SEQ_F_YELLOW ESL_SEQ_END_COLOR +#define ESL_SEQ_FBLUE ESL_SEQ_ESC ESL_SEQ_F_BLUE ESL_SEQ_END_COLOR +#define ESL_SEQ_FMAGEN ESL_SEQ_ESC ESL_SEQ_F_MAGEN ESL_SEQ_END_COLOR +#define ESL_SEQ_FCYAN ESL_SEQ_ESC ESL_SEQ_F_CYAN ESL_SEQ_END_COLOR +#define ESL_SEQ_FWHITE ESL_SEQ_ESC ESL_SEQ_F_WHITE ESL_SEQ_END_COLOR +#define ESL_SEQ_BBLACK ESL_SEQ_ESC ESL_SEQ_B_BLACK ESL_SEQ_END_COLOR +#define ESL_SEQ_BRED ESL_SEQ_ESC ESL_SEQ_B_RED ESL_SEQ_END_COLOR +#define ESL_SEQ_BGREEN ESL_SEQ_ESC ESL_SEQ_B_GREEN ESL_SEQ_END_COLOR +#define ESL_SEQ_BYELLOW ESL_SEQ_ESC ESL_SEQ_B_YELLOW ESL_SEQ_END_COLOR +#define ESL_SEQ_BBLUE ESL_SEQ_ESC ESL_SEQ_B_BLUE ESL_SEQ_END_COLOR +#define ESL_SEQ_BMAGEN ESL_SEQ_ESC ESL_SEQ_B_MAGEN ESL_SEQ_END_COLOR +#define ESL_SEQ_BCYAN ESL_SEQ_ESC ESL_SEQ_B_CYAN ESL_SEQ_END_COLOR +#define ESL_SEQ_BWHITE ESL_SEQ_ESC ESL_SEQ_B_WHITE ESL_SEQ_END_COLOR +/* Preset escape sequences */ +#define ESL_SEQ_HOME ESL_SEQ_ESC ESL_SEQ_HOME_CHAR_STR +#define ESL_SEQ_CLEARLINE ESL_SEQ_ESC ESL_SEQ_CLEARLINE_CHAR_STR +#define ESL_SEQ_CLEARLINEEND ESL_SEQ_ESC ESL_SEQ_CLEARLINEEND_CHAR +#define ESL_SEQ_CLEARSCR ESL_SEQ_ESC ESL_SEQ_CLEARSCR_CHAR ESL_SEQ_HOME + +#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__NetBSD__) +#define _XOPEN_SOURCE 600 +#endif + +#ifndef HAVE_STRINGS_H +#define HAVE_STRINGS_H 1 +#endif +#ifndef HAVE_SYS_SOCKET_H +#define HAVE_SYS_SOCKET_H 1 +#endif + +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) +#define __WINDOWS__ +#endif +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#undef HAVE_STRINGS_H +#undef HAVE_SYS_SOCKET_H +#endif + +#include +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include + + +#define esl_assert(_x) assert(_x) +#define esl_safe_free(_x) if (_x) free(_x); _x = NULL +#define esl_strlen_zero(s) (!s || *(s) == '\0') +#define esl_strlen_zero_buf(s) (*(s) == '\0') + +#ifdef WIN32 +#include +typedef HANDLE esl_socket_t; +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef intptr_t esl_ssize_t; +typedef int esl_filehandle_t; +#else +#include +#include +#include +#include +#include +#include +#include +#define ESL_SOCK_INVALID -1 +typedef int esl_socket_t; +typedef ssize_t esl_ssize_t; +typedef int esl_filehandle_t; +#endif + +typedef int16_t esl_port_t; + +typedef enum { + ESL_SUCCESS, + ESL_FAIL +} esl_status_t; + + +typedef struct { + struct sockaddr_in sockaddr; + struct hostent hostent; + char hostbuf[256]; + esl_socket_t sock; + char err[256]; + int errno; + char header_buf[4196]; + char last_reply[1024]; + esl_event_t *last_event; + int debug; + int connected; +} esl_handle_t; + +typedef enum { + ESL_TRUE = 1, + ESL_FALSE = 0 +} esl_bool_t; + + +#include "esl_event.h" +#include "esl_threadmutex.h" + +size_t esl_url_encode(const char *url, char *buf, size_t len); +char *esl_url_decode(char *s); + +esl_status_t esl_connect(esl_handle_t *handle, const char *host, esl_port_t port, const char *password); +esl_status_t esl_disconnect(esl_handle_t *handle); +esl_status_t esl_send(esl_handle_t *handle, const char *cmd); +esl_status_t esl_recv(esl_handle_t *handle); +esl_status_t esl_send_recv(esl_handle_t *handle, const char *cmd); + + +#endif + + + diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h new file mode 100644 index 0000000000..56ca57c938 --- /dev/null +++ b/libs/esl/src/include/esl_event.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ESL_EVENT_H +#define ESL_EVENT_H + +#include + +typedef enum { + ESL_STACK_BOTTOM, + ESL_STACK_TOP +} esl_stack_t; + +typedef enum { + ESL_EVENT_CUSTOM, + ESL_EVENT_CHANNEL_CREATE, + ESL_EVENT_CHANNEL_DESTROY, + ESL_EVENT_CHANNEL_STATE, + ESL_EVENT_CHANNEL_ANSWER, + ESL_EVENT_CHANNEL_HANGUP, + ESL_EVENT_CHANNEL_EXECUTE, + ESL_EVENT_CHANNEL_EXECUTE_COMPLETE, + ESL_EVENT_CHANNEL_BRIDGE, + ESL_EVENT_CHANNEL_UNBRIDGE, + ESL_EVENT_CHANNEL_PROGRESS, + ESL_EVENT_CHANNEL_PROGRESS_MEDIA, + ESL_EVENT_CHANNEL_OUTGOING, + ESL_EVENT_CHANNEL_PARK, + ESL_EVENT_CHANNEL_UNPARK, + ESL_EVENT_CHANNEL_APPLICATION, + ESL_EVENT_CHANNEL_ORIGINATE, + ESL_EVENT_CHANNEL_UUID, + ESL_EVENT_API, + ESL_EVENT_LOG, + ESL_EVENT_INBOUND_CHAN, + ESL_EVENT_OUTBOUND_CHAN, + ESL_EVENT_STARTUP, + ESL_EVENT_SHUTDOWN, + ESL_EVENT_PUBLISH, + ESL_EVENT_UNPUBLISH, + ESL_EVENT_TALK, + ESL_EVENT_NOTALK, + ESL_EVENT_SESSION_CRASH, + ESL_EVENT_MODULE_LOAD, + ESL_EVENT_MODULE_UNLOAD, + ESL_EVENT_DTMF, + ESL_EVENT_MESSAGE, + ESL_EVENT_PRESENCE_IN, + ESL_EVENT_NOTIFY_IN, + ESL_EVENT_PRESENCE_OUT, + ESL_EVENT_PRESENCE_PROBE, + ESL_EVENT_MESSAGE_WAITING, + ESL_EVENT_MESSAGE_QUERY, + ESL_EVENT_ROSTER, + ESL_EVENT_CODEC, + ESL_EVENT_BACKGROUND_JOB, + ESL_EVENT_DETECTED_SPEECH, + ESL_EVENT_DETECTED_TONE, + ESL_EVENT_PRIVATE_COMMAND, + ESL_EVENT_HEARTBEAT, + ESL_EVENT_TRAP, + ESL_EVENT_ADD_SCHEDULE, + ESL_EVENT_DEL_SCHEDULE, + ESL_EVENT_EXE_SCHEDULE, + ESL_EVENT_RE_SCHEDULE, + ESL_EVENT_RELOADXML, + ESL_EVENT_NOTIFY, + ESL_EVENT_SEND_MESSAGE, + ESL_EVENT_RECV_MESSAGE, + ESL_EVENT_REQUEST_PARAMS, + ESL_EVENT_CHANNEL_DATA, + ESL_EVENT_GENERAL, + ESL_EVENT_COMMAND, + ESL_EVENT_SESSION_HEARTBEAT, + ESL_EVENT_ALL +} esl_event_types_t; + +typedef enum { + ESL_PRIORITY_NORMAL, + ESL_PRIORITY_LOW, + ESL_PRIORITY_HIGH +} esl_priority_t; + +/*! \brief An event Header */ + struct esl_event_header { + /*! the header name */ + char *name; + /*! the header value */ + char *value; + /*! hash of the header name */ + unsigned long hash; + struct esl_event_header *next; +}; + + +/*! \brief Representation of an event */ +struct esl_event { + /*! the event id (descriptor) */ + esl_event_types_t event_id; + /*! the priority of the event */ + esl_priority_t priority; + /*! the owner of the event */ + char *owner; + /*! the subclass of the event */ + char *subclass_name; + /*! the event headers */ + esl_event_header_t *headers; + /*! the event headers tail pointer */ + esl_event_header_t *last_header; + /*! the body of the event */ + char *body; + /*! user data from the subclass provider */ + void *bind_user_data; + /*! user data from the event sender */ + void *event_user_data; + /*! unique key */ + unsigned long key; + struct esl_event *next; +}; + + + +#define ESL_EVENT_SUBCLASS_ANY NULL + +/*! + \brief Create an event + \param event a NULL pointer on which to create the event + \param event_id the event id enumeration of the desired event + \param subclass_name the subclass name for custom event (only valid when event_id is ESL_EVENT_CUSTOM) + \return ESL_STATUS_SUCCESS on success +*/ +esl_status_t esl_event_create_subclass(esl_event_t **event, esl_event_types_t event_id, const char *subclass_name); + +/*! + \brief Set the priority of an event + \param event the event to set the priority on + \param priority the event priority + \return ESL_STATUS_SUCCESS +*/ +esl_status_t esl_event_set_priority(esl_event_t *event, esl_priority_t priority); + +/*! + \brief Retrieve a header value from an event + \param event the event to read the header from + \param header_name the name of the header to read + \return the value of the requested header +*/ +char *esl_event_get_header(esl_event_t *event, const char *header_name); + +/*! + \brief Retrieve the body value from an event + \param event the event to read the body from + \return the value of the body or NULL +*/ +char *esl_event_get_body(esl_event_t *event); + +/*! + \brief Add a header to an event + \param event the event to add the header to + \param stack the stack sense (stack it on the top or on the bottom) + \param header_name the name of the header to add + \param fmt the value of the header (varargs see standard sprintf family) + \return ESL_STATUS_SUCCESS if the header was added +*/ +esl_status_t esl_event_add_header(esl_event_t *event, esl_stack_t stack, + const char *header_name, const char *fmt, ...); //PRINTF_FUNCTION(4, 5); + +/*! + \brief Add a string header to an event + \param event the event to add the header to + \param stack the stack sense (stack it on the top or on the bottom) + \param header_name the name of the header to add + \param data the value of the header + \return ESL_STATUS_SUCCESS if the header was added +*/ +esl_status_t esl_event_add_header_string(esl_event_t *event, esl_stack_t stack, const char *header_name, const char *data); + +esl_status_t esl_event_del_header(esl_event_t *event, const char *header_name); + +/*! + \brief Destroy an event + \param event pointer to the pointer to event to destroy +*/ +void esl_event_destroy(esl_event_t **event); +#define esl_event_safe_destroy(_event) if (_event) esl_event_destroy(_event) + +/*! + \brief Duplicate an event + \param event a NULL pointer on which to duplicate the event + \param todup an event to duplicate + \return ESL_STATUS_SUCCESS if the event was duplicated +*/ +esl_status_t esl_event_dup(esl_event_t **event, esl_event_t *todup); + +/*! + \brief Render the name of an event id enumeration + \param event the event id to render the name of + \return the rendered name +*/ +const char *esl_event_name(esl_event_types_t event); + +/*! + \brief return the event id that matches a given event name + \param name the name of the event + \param type the event id to return + \return ESL_STATUS_SUCCESS if there was a match +*/ +esl_status_t esl_name_event(const char *name, esl_event_types_t *type); + +/*! + \brief Render a string representation of an event sutable for printing or network transport + \param event the event to render + \param str a string pointer to point at the allocated data + \param encode url encode the headers + \return ESL_STATUS_SUCCESS if the operation was successful + \note you must free the resulting string when you are finished with it +*/ +esl_status_t esl_event_serialize(esl_event_t *event, char **str, esl_bool_t encode); + +/*! + \brief Add a body to an event + \param event the event to add to body to + \param fmt optional body of the event (varargs see standard sprintf family) + \return ESL_STATUS_SUCCESS if the body was added to the event + \note the body parameter can be shadowed by the esl_event_reserve_subclass_detailed function +*/ +esl_status_t esl_event_add_body(esl_event_t *event, const char *fmt, ...); + +/*! + \brief Create a new event assuming it will not be custom event and therefore hiding the unused parameters + \param event a NULL pointer on which to create the event + \param id the event id enumeration of the desired event + \return ESL_STATUS_SUCCESS on success +*/ +#define esl_event_create(event, id) esl_event_create_subclass(event, id, ESL_EVENT_SUBCLASS_ANY) + +const char *esl_priority_name(esl_priority_t priority); + +///\} + + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ + diff --git a/libs/esl/src/include/esl_threadmutex.h b/libs/esl/src/include/esl_threadmutex.h new file mode 100644 index 0000000000..ab7d834829 --- /dev/null +++ b/libs/esl/src/include/esl_threadmutex.h @@ -0,0 +1,51 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _ESL_THREADMUTEX_H +#define _ESL_THREADMUTEX_H + +#include "esl.h" + +typedef struct esl_mutex esl_mutex_t; +typedef struct esl_thread esl_thread_t; +typedef void *(*esl_thread_function_t) (esl_thread_t *, void *); + +esl_status_t esl_thread_create_detached(esl_thread_function_t func, void *data); +esl_status_t esl_thread_create_detached_ex(esl_thread_function_t func, void *data, size_t stack_size); +void esl_thread_override_default_stacksize(size_t size); +esl_status_t esl_mutex_create(esl_mutex_t **mutex); +esl_status_t esl_mutex_destroy(esl_mutex_t **mutex); +esl_status_t esl_mutex_lock(esl_mutex_t *mutex); +esl_status_t esl_mutex_trylock(esl_mutex_t *mutex); +esl_status_t esl_mutex_unlock(esl_mutex_t *mutex); + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ + diff --git a/libs/esl/testclient.c b/libs/esl/testclient.c new file mode 100644 index 0000000000..383755cb79 --- /dev/null +++ b/libs/esl/testclient.c @@ -0,0 +1,19 @@ +#include +#include +#include + + +int main(void) +{ + esl_handle_t handle = {0}; + + handle.debug = 1; + + esl_connect(&handle, "localhost", 8021, "ClueCon"); + + esl_send_recv(&handle, "api status\n\n"); + + esl_disconnect(&handle); + + return 0; +}