From 6ac7c18bb90f1e97e315e173fb7079741f4443cc Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 20 Dec 2008 05:24:51 +0000 Subject: [PATCH] work in progress git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10892 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/esl/Makefile | 2 +- libs/esl/fs_cli.c | 97 ++++++--- libs/esl/src/esl.c | 339 ++++++++++++++++++++++++++++-- libs/esl/src/esl_config.c | 251 ++++++++++++++++++++++ libs/esl/src/include/esl.h | 38 +++- libs/esl/src/include/esl_config.h | 138 ++++++++++++ 6 files changed, 819 insertions(+), 46 deletions(-) create mode 100644 libs/esl/src/esl_config.c create mode 100644 libs/esl/src/include/esl_config.h diff --git a/libs/esl/Makefile b/libs/esl/Makefile index e094058d55..ea4d9e2877 100644 --- a/libs/esl/Makefile +++ b/libs/esl/Makefile @@ -4,7 +4,7 @@ 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 +OBJS=src/esl.o src/esl_event.o src/esl_threadmutex.o src/esl_config.o all: $(MYLIB) fs_cli diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index aeddc58b1f..bf2ba11348 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -61,14 +61,18 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) esl_mutex_unlock(global_mutex); goto done; } - - if (activity && FD_ISSET(handle->sock, &rfds)) { - esl_recv(handle); + if (esl_recv(handle)) { + running = thread_running = 0; + esl_mutex_unlock(global_mutex); + esl_log(ESL_LOG_WARNING, "Disconnected.\n"); + goto done; + } + if (handle->last_event) { const char *type = esl_event_get_header(handle->last_event, "content-type"); if (!strcasecmp(type, "log/data")) { - int level; + int level = 0; if (strstr(handle->last_event->body, "[CONSOLE]")) { level = 0; } else if (strstr(handle->last_event->body, "[ALERT]")) { @@ -88,6 +92,10 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) } printf("%s%s%s", COLORS[level], handle->last_event->body, ESL_SEQ_DEFAULT_COLOR); + } else if (0 && !strcasecmp(type, "text/disconnect-notice")) { + running = thread_running = 0; + } else { + printf("INCOMING DATA [%s]\n%s", type, handle->last_event->body); } } @@ -106,28 +114,29 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) static int process_command(esl_handle_t *handle, const char *cmd) { - if (!strcasecmp(cmd, "exit")) { + if ( + !strcasecmp(cmd, "exit") || + !strcasecmp(cmd, "quit") || + !strcasecmp(cmd, "bye") + ) { 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); - } - + if ( + !strncasecmp(cmd, "event", 5) || + !strncasecmp(cmd, "noevent", 7) || + !strncasecmp(cmd, "nixevent", 8) || + !strncasecmp(cmd, "log", 3) || + !strncasecmp(cmd, "nolog", 5) || + !strncasecmp(cmd, "filter", 6) + ) { + esl_mutex_lock(global_mutex); + esl_send_recv(handle, cmd); + printf("%s\n", handle->last_reply); + esl_mutex_unlock(global_mutex); goto end; } - - + printf("Unknown command [%s]\n", cmd); end: @@ -136,17 +145,35 @@ static int process_command(esl_handle_t *handle, const char *cmd) } -int main(void) +typedef struct { + char host[128]; + char pass[128]; + esl_port_t port; +} cli_profile_t; + +static cli_profile_t profiles[128] = { 0 }; +static int pcount; + +int main(int argc, char *argv[]) { esl_handle_t handle = {0}; int count; const char *line; char cmd_str[1024] = ""; char hfile[512] = "/tmp/fs_cli_history"; + char cfile[512] = "/tmp/fs_cli_config"; char *home = getenv("HOME"); - + esl_config_t cfg; + cli_profile_t *profile = &profiles[0]; + + strncpy(profiles[0].host, "localhost", sizeof(profiles[0].host)); + strncpy(profiles[0].pass, "ClueCon", sizeof(profiles[0].pass)); + profiles[0].port = 8021; + pcount = 1; + if (home) { snprintf(hfile, sizeof(hfile), "%s/.fs_cli_history", home); + snprintf(cfile, sizeof(cfile), "%s/.fs_cli_config", home); } esl_mutex_create(&global_mutex); @@ -155,15 +182,27 @@ int main(void) 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; + if (esl_config_open_file(&cfg, cfile)) { + char *var, *val; + while (esl_config_next_pair(&cfg, &var, &val)) { + + } + esl_config_close_file(&cfg); } + + if (esl_connect(&handle, profile->host, profile->port, profile->pass)) { + printf("Error Connecting [%s]\n", handle.err); + return -1; + } + + + if (handle.debug) { + esl_global_set_default_logger(7); + } + esl_thread_create_detached(msg_thread_run, &handle); el = el_init(__FILE__, stdout, stdout, stdout); diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c index 703955cac7..60e7ad66c2 100644 --- a/libs/esl/src/esl.c +++ b/libs/esl/src/esl.c @@ -32,6 +32,7 @@ */ #include +#include #ifndef HAVE_GETHOSTBYNAME_R extern int gethostbyname_r (const char *__name, @@ -43,6 +44,230 @@ extern int gethostbyname_r (const char *__name, +/* Written by Marc Espie, public domain */ +#define ESL_CTYPE_NUM_CHARS 256 + +const short _esl_C_toupper_[1 + ESL_CTYPE_NUM_CHARS] = { + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const short *_esl_toupper_tab_ = _esl_C_toupper_; + +int esl_toupper(int c) +{ + if ((unsigned int)c > 255) + return(c); + if (c < -1) + return EOF; + return((_esl_toupper_tab_ + 1)[c]); +} + +const short _esl_C_tolower_[1 + ESL_CTYPE_NUM_CHARS] = { + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const short *_esl_tolower_tab_ = _esl_C_tolower_; + +int esl_tolower(int c) +{ + if ((unsigned int)c > 255) + return(c); + if (c < -1) + return EOF; + return((_esl_tolower_tab_ + 1)[c]); +} + +const char *esl_stristr(const char *instr, const char *str) +{ +/* +** Rev History: 16/07/97 Greg Thayer Optimized +** 07/04/95 Bob Stout ANSI-fy +** 02/03/94 Fred Cole Original +** 09/01/03 Bob Stout Bug fix (lines 40-41) per Fred Bulback +** +** Hereby donated to public domain. +*/ + const char *pptr, *sptr, *start; + + if (!str || !instr) + return NULL; + + for (start = str; *start; start++) { + /* find start of pattern in string */ + for (; ((*start) && (esl_toupper(*start) != esl_toupper(*instr))); start++); + + if (!*start) + return NULL; + + pptr = instr; + sptr = start; + + while (esl_toupper(*sptr) == esl_toupper(*pptr)) { + sptr++; + pptr++; + + /* if end of pattern then pattern was found */ + if (!*pptr) + return (start); + + if (!*sptr) + return NULL; + } + } + return NULL; +} + + +static void null_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + if (file && func && line && level && fmt) { + return; + } + return; +} + + +static const char *LEVEL_NAMES[] = { + "EMERG", + "ALERT", + "CRIT", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + NULL +}; + +static int esl_log_level = 7; + +static const char *cut_path(const char *in) +{ + const char *p, *ret = in; + char delims[] = "/\\"; + char *i; + + for (i = delims; *i; i++) { + p = in; + while ((p = strchr(p, *i)) != 0) { + ret = ++p; + } + } + return ret; +} + + +static void default_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + const char *fp; + char data[1024]; + va_list ap; + + if (level < 0 || level > 7) { + level = 7; + } + if (level > esl_log_level) { + return; + } + + fp = cut_path(file); + + va_start(ap, fmt); + + vsnprintf(data, sizeof(data), fmt, ap); + + + fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], file, line, func, data); + + va_end(ap); + +} + +esl_logger_t esl_log = null_logger; + +void esl_global_set_logger(esl_logger_t logger) +{ + if (logger) { + esl_log = logger; + } else { + esl_log = null_logger; + } +} + +void esl_global_set_default_logger(int level) +{ + if (level < 0 || level > 7) { + level = 7; + } + + esl_log = default_logger; + esl_log_level = level; +} + size_t esl_url_encode(const char *url, char *buf, size_t len) { const char *p; @@ -138,10 +363,12 @@ esl_status_t esl_connect(esl_handle_t *handle, const char *host, esl_port_t port rval = connect(handle->sock, (struct sockaddr *) &handle->sockaddr, sizeof(handle->sockaddr)); if (rval) { - strerror_r(handle->errno, handle->err, sizeof(handle->err)); + snprintf(handle->err, sizeof(handle->err), "Socket Connection Error"); goto fail; } - + + handle->connected = 1; + if (esl_recv(handle)) { snprintf(handle->err, sizeof(handle->err), "Connection Error"); goto fail; @@ -171,8 +398,6 @@ esl_status_t esl_connect(esl_handle_t *handle, const char *host, esl_port_t port goto fail; } - handle->connected = 1; - return ESL_SUCCESS; fail: @@ -184,7 +409,9 @@ esl_status_t esl_connect(esl_handle_t *handle, const char *host, esl_port_t port esl_status_t esl_disconnect(esl_handle_t *handle) { esl_event_safe_destroy(&handle->last_event); - + esl_event_safe_destroy(&handle->last_ievent); + esl_event_safe_destroy(&handle->info_event); + if (handle->sock != ESL_SOCK_INVALID) { close(handle->sock); handle->sock = ESL_SOCK_INVALID; @@ -207,20 +434,29 @@ esl_status_t esl_recv(esl_handle_t *handle) char *col; char *cl; ssize_t len; - + int zc = 0; + esl_event_safe_destroy(&handle->last_event); memset(handle->header_buf, 0, sizeof(handle->header_buf)); c = handle->header_buf; beg = c; - for(;;) { + while(handle->connected) { rrval = recv(handle->sock, c, 1, 0); - if (rrval < 0) { + if (rrval == 0) { + + if (++zc >= 100) { + esl_disconnect(handle); + return ESL_FAIL; + } + } else if (rrval < 0) { strerror_r(handle->errno, handle->err, sizeof(handle->err)); goto fail; - } else if (rrval > 0) { + } else { + zc = 0; + if (*c == '\n') { if (++crc == 2) { break; @@ -243,7 +479,7 @@ esl_status_t esl_recv(esl_handle_t *handle) if (hname && hval) { if (handle->debug > 1) { - printf("RECV HEADER [%s] = [%s]\n", hname, hval); + esl_log(ESL_LOG_DEBUG, "RECV HEADER [%s] = [%s]\n", hname, hval); } esl_event_add_header_string(revent, ESL_STACK_BOTTOM, hname, hval); } @@ -289,17 +525,84 @@ esl_status_t esl_recv(esl_handle_t *handle) 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)); + const char *hval = esl_event_get_header(handle->last_event, "reply-text"); + + if (!esl_strlen_zero(hval)) { + strncpy(handle->last_reply, hval, sizeof(handle->last_reply)); } + + hval = esl_event_get_header(handle->last_event, "content-type"); + + if (!esl_strlen_zero(hval) && !strcasecmp(hval, "text/event-plain") && handle->last_event->body) { + const char *en; + esl_event_types_t et = ESL_EVENT_COMMAND; + char *body = strdup(handle->last_event->body); + char *beg; + char *hname, *hval; + char *col; + char *cl; + ssize_t len; + char *c; + + esl_event_safe_destroy(&handle->last_ievent); + + if ((en = esl_stristr("event-name:", body))) { + en++; + while(*en == ' ') en++; + if (en) { + esl_name_event(en, &et); + } + } + + esl_event_create(&handle->last_ievent, et); + + beg = body; + + while(beg) { + if (!(c = strchr(beg, '\n'))) { + break; + } + + hname = beg; + hval = col = NULL; + + if (hname && (col = strchr(hname, ':'))) { + hval = col + 1; + *col = '\0'; + while(*hval == ' ') hval++; + } + + *c = '\0'; + + if (hname && hval) { + esl_url_decode(hval); + + if (handle->debug > 1) { + esl_log(ESL_LOG_DEBUG, "RECV INNER HEADER [%s] = [%s]\n", hname, hval); + } + esl_event_add_header_string(handle->last_ievent, ESL_STACK_BOTTOM, hname, hval); + } + + beg = c + 1; + } + + free(body); + + if (handle->debug) { + char *foo; + esl_event_serialize(handle->last_ievent, &foo, ESL_FALSE); + esl_log(ESL_LOG_DEBUG, "RECV EVENT\n%s\n", foo); + free(foo); + } + } + } if (handle->debug) { char *foo; esl_event_serialize(handle->last_event, &foo, ESL_FALSE); - printf("RECV MESSAGE\n%s\n", foo); + esl_log(ESL_LOG_DEBUG, "RECV MESSAGE\n%s\n", foo); free(foo); } @@ -314,11 +617,17 @@ esl_status_t esl_recv(esl_handle_t *handle) esl_status_t esl_send(esl_handle_t *handle, const char *cmd) { + const char *e = cmd + strlen(cmd) -1; + if (handle->debug) { - printf("SEND\n%s\n", cmd); + esl_log(ESL_LOG_DEBUG, "SEND\n%s\n", cmd); } send(handle->sock, cmd, strlen(cmd), 0); + + if (!(*e == '\n' && *(e-1) == '\n')) { + send(handle->sock, "\n\n", 2, 0); + } } diff --git a/libs/esl/src/esl_config.c b/libs/esl/src/esl_config.c new file mode 100644 index 0000000000..05702ff3f2 --- /dev/null +++ b/libs/esl/src/esl_config.c @@ -0,0 +1,251 @@ +/* + * 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 "esl.h" +#include "esl_config.h" + +int esl_config_open_file(esl_config_t *cfg, const char *file_path) +{ + FILE *f; + const char *path = NULL; + char path_buf[1024]; + + if (file_path[0] == '/') { + path = file_path; + } else { + snprintf(path_buf, sizeof(path_buf), "%s%s%s", ESL_CONFIG_DIR, ESL_PATH_SEPARATOR, file_path); + path = path_buf; + } + + if (!path) { + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->lockto = -1; + esl_log(ESL_LOG_DEBUG, "Configuration file is %s.\n", path); + f = fopen(path, "r"); + + if (!f) { + if (file_path[0] != '/') { + int last = -1; + char *var, *val; + + snprintf(path_buf, sizeof(path_buf), "%s%sopenesl.conf", ESL_CONFIG_DIR, ESL_PATH_SEPARATOR); + path = path_buf; + + if ((f = fopen(path, "r")) == 0) { + return 0; + } + + cfg->file = f; + esl_set_string(cfg->path, path); + + while (esl_config_next_pair(cfg, &var, &val)) { + if ((cfg->sectno != last) && !strcmp(cfg->section, file_path)) { + cfg->lockto = cfg->sectno; + return 1; + } + } + + esl_config_close_file(cfg); + memset(cfg, 0, sizeof(*cfg)); + return 0; + } + + return 0; + } else { + cfg->file = f; + esl_set_string(cfg->path, path); + return 1; + } +} + +void esl_config_close_file(esl_config_t *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + + +int esl_config_next_pair(esl_config_t *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + if (!cfg->path) { + return 0; + } + + for (;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']')) != 0) { + *end = '\0'; + (*var)++; + if (**var == '+') { + (*var)++; + esl_copy_string(cfg->section, *var, sizeof(cfg->section)); + cfg->sectno++; + + if (cfg->lockto > -1 && cfg->sectno != cfg->lockto) { + break; + } + cfg->catno = 0; + cfg->lineno = 0; + *var = (char *) ""; + *val = (char *) ""; + return 1; + } else { + esl_copy_string(cfg->category, *var, sizeof(cfg->category)); + cfg->catno++; + } + continue; + } + + + + if (**var == '#' || **var == ';' || **var == '\n' || **var == '\r') { + continue; + } + + if (!strncmp(*var, "__END__", 7)) { + break; + } + + + if ((end = strchr(*var, ';')) && *(end+1) == *end) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n')) != 0) { + if (*(end - 1) == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + + if ((*val = strchr(*var, '=')) == 0) { + ret = -1; + /* log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno); */ + continue; + } else { + p = *val - 1; + *(*val) = '\0'; + (*val)++; + if (*(*val) == '>') { + *(*val) = '\0'; + (*val)++; + } + + while ((*p == ' ' || *p == '\t') && p != *var) { + *p = '\0'; + p--; + } + + p = *val; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *val = p; + ret = 1; + break; + } + } + + + return ret; + +} + +int esl_config_get_cas_bits(char *strvalue, unsigned char *outbits) +{ + char cas_bits[5]; + unsigned char bit = 0x8; + char *double_colon = strchr(strvalue, ':'); + if (!double_colon) { + esl_log(ESL_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + double_colon++; + *outbits = 0; + cas_bits[4] = 0; + if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) { + esl_log(ESL_LOG_ERROR, "Invalid CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + esl_log(ESL_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits); + int x = 0; + for (; cas_bits[x]; x++) { + if ('1' == cas_bits[x]) { + *outbits |= bit; + } else if ('0' != cas_bits[x]) { + esl_log(ESL_LOG_ERROR, "Invalid CAS pattern specified: %s, just 0 or 1 allowed for each bit\n"); + return -1; + } + bit >>= 1; + } + return 0; +} + +/* 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 index 4b71364b87..412c4c322c 100644 --- a/libs/esl/src/include/esl.h +++ b/libs/esl/src/include/esl.h @@ -34,6 +34,9 @@ #ifndef _ESL_H_ #define _ESL_H_ +#define esl_copy_string(_x, _y, _z) strncpy(_x, _y, _z - 1) +#define esl_set_string(_x, _y) esl_copy_string(_x, _y, sizeof(_x)) + typedef struct esl_event_header esl_event_header_t; typedef struct esl_event esl_event_t; @@ -204,6 +207,8 @@ typedef struct { char header_buf[4196]; char last_reply[1024]; esl_event_t *last_event; + esl_event_t *last_ievent; + esl_event_t *info_event; int debug; int connected; } esl_handle_t; @@ -213,12 +218,44 @@ typedef enum { ESL_FALSE = 0 } esl_bool_t; +#ifndef __FUNCTION__ +#define __FUNCTION__ (const char *)__func__ +#endif + +#define ESL_PRE __FILE__, __FUNCTION__, __LINE__ +#define ESL_LOG_LEVEL_DEBUG 7 +#define ESL_LOG_LEVEL_INFO 6 +#define ESL_LOG_LEVEL_NOTICE 5 +#define ESL_LOG_LEVEL_WARNING 4 +#define ESL_LOG_LEVEL_ERROR 3 +#define ESL_LOG_LEVEL_CRIT 2 +#define ESL_LOG_LEVEL_ALERT 1 +#define ESL_LOG_LEVEL_EMERG 0 + +#define ESL_LOG_DEBUG ESL_PRE, ESL_LOG_LEVEL_DEBUG +#define ESL_LOG_INFO ESL_PRE, ESL_LOG_LEVEL_INFO +#define ESL_LOG_NOTICE ESL_PRE, ESL_LOG_LEVEL_NOTICE +#define ESL_LOG_WARNING ESL_PRE, ESL_LOG_LEVEL_WARNING +#define ESL_LOG_ERROR ESL_PRE, ESL_LOG_LEVEL_ERROR +#define ESL_LOG_CRIT ESL_PRE, ESL_LOG_LEVEL_CRIT +#define ESL_LOG_ALERT ESL_PRE, ESL_LOG_LEVEL_ALERT +#define ESL_LOG_EMERG ESL_PRE, ESL_LOG_LEVEL_EMERG +typedef void (*esl_logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...); + +extern esl_logger_t esl_log; + +void esl_global_set_logger(esl_logger_t logger); +void esl_global_set_default_logger(int level); #include "esl_event.h" #include "esl_threadmutex.h" +#include "esl_config.h" size_t esl_url_encode(const char *url, char *buf, size_t len); char *esl_url_decode(char *s); +int esl_toupper(int c); +int esl_tolower(int c); + 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); @@ -226,7 +263,6 @@ 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_config.h b/libs/esl/src/include/esl_config.h new file mode 100644 index 0000000000..934db157e3 --- /dev/null +++ b/libs/esl/src/include/esl_config.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/** + * @defgroup config Config File Parser + * @ingroup config + * This module implements a basic interface and file format parser + * + *
+ *
+ * EXAMPLE 
+ * 
+ * [category1]
+ * var1 => val1
+ * var2 => val2
+ * \# lines that begin with \# are comments
+ * \#var3 => val3
+ * 
+ * @{ + */ + +#ifndef ESL_CONFIG_H +#define ESL_CONFIG_H + +#include "esl.h" +#define ESL_URL_SEPARATOR "://" + + +#ifdef WIN32 +#define ESL_PATH_SEPARATOR "\\" +#ifndef ESL_CONFIG_DIR +#define ESL_CONFIG_DIR "c:\\openesl" +#endif +#define esl_is_file_path(file) (*(file +1) == ':' || *file == '/' || strstr(file, SWITCH_URL_SEPARATOR)) +#else +#define ESL_PATH_SEPARATOR "/" +#ifndef ESL_CONFIG_DIR +#define ESL_CONFIG_DIR "/etc/openesl" +#endif +#define esl_is_file_path(file) ((*file == '/') || strstr(file, SWITCH_URL_SEPARATOR)) +#endif + +typedef struct esl_config esl_config_t; + +/*! \brief A simple file handle representing an open configuration file **/ +struct esl_config { + /*! FILE stream buffer to the opened file */ + FILE *file; + /*! path to the file */ + char path[512]; + /*! current category */ + char category[256]; + /*! current section */ + char section[256]; + /*! buffer of current line being read */ + char buf[1024]; + /*! current line number in file */ + int lineno; + /*! current category number in file */ + int catno; + /*! current section number in file */ + int sectno; + + int lockto; +}; + +/*! + \brief Open a configuration file + \param cfg (esl_config_t *) config handle to use + \param file_path path to the file + \return 1 (true) on success 0 (false) on failure +*/ +int esl_config_open_file(esl_config_t * cfg, const char *file_path); + +/*! + \brief Close a previously opened configuration file + \param cfg (esl_config_t *) config handle to use +*/ +void esl_config_close_file(esl_config_t * cfg); + +/*! + \brief Retrieve next name/value pair from configuration file + \param cfg (esl_config_t *) config handle to use + \param var pointer to aim at the new variable name + \param val pointer to aim at the new value +*/ +int esl_config_next_pair(esl_config_t * cfg, char **var, char **val); + +/*! + \brief Retrieve the CAS bits from a configuration string value + \param strvalue pointer to the configuration string value (expected to be in format whatever:xxxx) + \param outbits pointer to aim at the CAS bits +*/ +int esl_config_get_cas_bits(char *strvalue, unsigned char *outbits); + + +/** @} */ +#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: + */