libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \
+ src/include/switch_limit.h \
src/include/switch_odbc.h
nodist_libfreeswitch_la_SOURCES = \
src/switch_config.c \
src/switch_time.c \
src/switch_odbc.c \
+ src/switch_limit.c \
src/g711.c \
src/switch_pcm.c \
src/switch_profile.c\
#applications/mod_osp
applications/mod_fifo
#applications/mod_curl
+applications/mod_db
+applications/mod_hash
applications/mod_voicemail
#applications/mod_directory
#applications/mod_lcr
-applications/mod_limit
applications/mod_expr
applications/mod_esf
#applications/mod_easyroute
#include "switch_nat.h"
#include "switch_odbc.h"
#include "switch_json.h"
+#include "switch_limit.h"
#include <libteletone.h>
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Rupa Schomaker <rupa@rupa.com>
+ *
+ * switch_limit.h - Limit generic implementations
+ *
+ */
+
+
+ /*!
+ \defgroup limit1 LIMIT code
+ \ingroup core1
+ \{
+*/
+#ifndef _SWITCH_LIMIT_H
+#define _SWITCH_LIMIT_H
+
+SWITCH_BEGIN_EXTERN_C
+
+/*!
+ \brief Initilize the LIMIT Core System
+ \param pool the memory pool to use for long term allocations
+ \note Generally called by the core_init
+*/
+SWITCH_DECLARE(void) switch_limit_init(switch_memory_pool_t *pool);
+
+/*!
+ \brief Increment resource.
+ \param backend to use
+ \param realm
+ \param resource
+ \param max - 0 means no limit, just count
+ \param interval - 0 means no interval
+ \return true/false - true ok, false over limit
+*/
+SWITCH_DECLARE(switch_status_t) switch_limit_incr(const char *backend, switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval);
+
+/*!
+ \brief Release resource.
+ \param backend to use
+ \param realm
+ \param resource
+ \return true/false - true ok, false over limit
+*/
+SWITCH_DECLARE(switch_status_t) switch_limit_release(const char *backend, switch_core_session_t *session, const char *realm, const char *resource);
+
+/*!
+ \brief get usage count for resource
+ \param backend to use
+ \param realm
+ \param resource
+ \param rcount - output paramter, rate counter
+*/
+SWITCH_DECLARE(int) switch_limit_usage(const char *backend, const char *realm, const char *resource, uint32_t *rcount);
+
+/*!
+ \brief reset all usage counters
+ \param backend to use
+*/
+SWITCH_DECLARE(switch_status_t) switch_limit_reset(const char *backend);
+
+/*!
+ \brief fire event for limit usage
+ \param backend to use
+ \param realm
+ \param resource
+ \param usage
+ \param rate
+ \param max
+ \param ratemax
+*/
+SWITCH_DECLARE(void) switch_limit_fire_event(const char *backend, const char *realm, const char *resource, uint32_t usage, uint32_t rate, uint32_t max, uint32_t ratemax);
+
+/*!
+ \brief retrieve arbitrary status information
+ \param backend to use
+ \note caller must free returned value
+*/
+SWITCH_DECLARE(char *) switch_limit_status(const char *backend);
+
+/*! callback to init a backend */
+#define SWITCH_LIMIT_INCR(name) static switch_status_t name (switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval)
+#define SWITCH_LIMIT_RELEASE(name) static switch_status_t name (switch_core_session_t *session, const char *realm, const char *resource)
+#define SWITCH_LIMIT_USAGE(name) static int name (const char *realm, const char *resource, uint32_t *rcount)
+#define SWITCH_LIMIT_RESET(name) static switch_status_t name (void)
+#define SWITCH_LIMIT_STATUS(name) static char * name (void)
+
+#define LIMIT_IGNORE_TRANSFER_VARIABLE "limit_ignore_transfer"
+#define LIMIT_BACKEND_VARIABLE "limit_backend"
+#define LIMIT_EVENT_USAGE "limit::usage"
+#define LIMIT_DEF_XFER_EXTEN "limit_exceeded"
+
+SWITCH_END_EXTERN_C
+#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:
+ */
switch_asr_interface_t *asr_interface;
/*! the table of management interfaces the module has implmented */
switch_management_interface_t *management_interface;
+ /*! the table of limit interfaces the module has implmented */
+ switch_limit_interface_t *limit_interface;
switch_thread_rwlock_t *rwlock;
int refs;
switch_memory_pool_t *pool;
*/
SWITCH_DECLARE(switch_management_interface_t *) switch_loadable_module_get_management_interface(const char *relative_oid);
+/*!
+ \brief Retrieve the limit interface by it's registered name
+ \param name the name of the limit interface
+ \return the desired limit interface
+ */
+SWITCH_DECLARE(switch_limit_interface_t *) switch_loadable_module_get_limit_interface(const char *name);
+
/*!
\brief Retrieve the list of loaded codecs into an array
\param array the array to populate
break; \
}
+#define SWITCH_ADD_LIMIT(limit_int, int_name, incrptr, releaseptr, usageptr, resetptr, statusptr) \
+ for (;;) { \
+ limit_int = (switch_limit_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_LIMIT_INTERFACE); \
+ limit_int->incr = incrptr; \
+ limit_int->release = releaseptr; \
+ limit_int->usage = usageptr; \
+ limit_int->reset = resetptr; \
+ limit_int->status = statusptr; \
+ limit_int->interface_name = int_name; \
+ break; \
+ }
+
SWITCH_DECLARE(uint32_t) switch_core_codec_next_id(void);
#define SWITCH_ADD_CODEC(codec_int, int_name) \
struct switch_management_interface *next;
};
+/*! \brief Abstract interface to a limit module */
+struct switch_limit_interface {
+ /*! name of the interface */
+ const char *interface_name;
+ /*! increment */
+ switch_status_t (*incr) (switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval);
+ /*! release */
+ switch_status_t (*release) (switch_core_session_t *session, const char *realm, const char *resource);
+ /*! usage for resource */
+ int (*usage) (const char *realm, const char *resource, uint32_t *rcount);
+ /*! reset counters */
+ switch_status_t (*reset) (void);
+ /*! freform status */
+ char * (*status) (void);
+ /* internal */
+ switch_thread_rwlock_t *rwlock;
+ int refs;
+ switch_mutex_t *reflock;
+ switch_loadable_module_interface_t *parent;
+ struct switch_limit_interface *next;
+};
+
/*! \brief Abstract interface to a directory module */
struct switch_directory_interface {
/*! the name of the interface */
SWITCH_CHAT_INTERFACE,
SWITCH_SAY_INTERFACE,
SWITCH_ASR_INTERFACE,
- SWITCH_MANAGEMENT_INTERFACE
+ SWITCH_MANAGEMENT_INTERFACE,
+ SWITCH_LIMIT_INTERFACE
} switch_module_interface_name_t;
typedef enum {
typedef struct switch_management_interface switch_management_interface_t;
typedef struct switch_core_port_allocator switch_core_port_allocator_t;
typedef struct switch_media_bug switch_media_bug_t;
+typedef struct switch_limit_interface switch_limit_interface_t;
struct switch_console_callback_match_node {
char *val;
return SWITCH_STATUS_SUCCESS;
}
-#define SHOW_SYNTAX "codec|endpoint|application|api|dialplan|file|timer|calls [count]|channels [count|like <match string>]|distinct_channels|aliases|complete|chat|management|modules|nat_map|say|interfaces|interface_types|tasks"
+#define SHOW_SYNTAX "codec|endpoint|application|api|dialplan|file|timer|calls [count]|channels [count|like <match string>]|distinct_channels|aliases|complete|chat|management|modules|nat_map|say|interfaces|interface_types|tasks|limits"
SWITCH_STANDARD_API(show_function)
{
char sql[1024];
!strncasecmp(command, "file", 4) ||
!strncasecmp(command, "timer", 5) ||
!strncasecmp(command, "chat", 4) ||
+ !strncasecmp(command, "limit", 5) ||
!strncasecmp(command, "say", 3) || !strncasecmp(command, "management", 10) || !strncasecmp(command, "endpoint", 8)) {
if (end_of(command) == 's') {
end_of(command) = '\0';
return SWITCH_STATUS_SUCCESS;
}
+/* LIMIT Stuff */
+#define LIMIT_USAGE_USAGE "<backend> <realm> <id> [rate]"
+SWITCH_STANDARD_API(limit_usage_function)
+{
+ int argc = 0;
+ char *argv[5] = { 0 };
+ char *mydata = NULL;
+ uint32_t count = 0;
+ uint32_t rcount = 0;
+ switch_bool_t dorate = SWITCH_FALSE;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3) {
+ stream->write_function(stream, "USAGE: limit_usage %s\n", LIMIT_USAGE_USAGE);
+ goto end;
+ }
+
+ if (argc > 3) {
+ if (!strcasecmp("rate", argv[3])) {
+ dorate = SWITCH_TRUE;
+ }
+ }
+
+ count = switch_limit_usage(argv[0], argv[1], argv[2], &rcount);
+
+ if (dorate == SWITCH_TRUE) {
+ stream->write_function(stream, "%d/%d", count, rcount);
+ } else {
+ stream->write_function(stream, "%d", count);
+ }
+
+end:
+ switch_safe_free(mydata);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+#define LIMIT_STATUS_USAGE "<backend>"
+SWITCH_STANDARD_API(limit_status_function)
+{
+ int argc = 0;
+ char *argv[2] = { 0 };
+ char *mydata = NULL;
+ char *ret = NULL;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 1) {
+ stream->write_function(stream, "USAGE: limit_status %s\n", LIMIT_STATUS_USAGE);
+ goto end;
+ }
+
+ ret = switch_limit_status(argv[0]);
+
+ stream->write_function(stream, "%s", ret);
+
+end:
+ switch_safe_free(mydata);
+ switch_safe_free(ret);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+#define LIMIT_RESET_USAGE "<backend>"
+SWITCH_STANDARD_API(limit_reset_function)
+{
+ int argc = 0;
+ char *argv[2] = { 0 };
+ char *mydata = NULL;
+ switch_status_t ret = SWITCH_STATUS_SUCCESS;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 1) {
+ stream->write_function(stream, "USAGE: limit_reset %s\n", LIMIT_RESET_USAGE);
+ goto end;
+ }
+
+ ret = switch_limit_reset(argv[0]);
+
+ stream->write_function(stream, "%s", (ret == SWITCH_STATUS_SUCCESS) ? "+OK" : "-ERR");
+
+end:
+ switch_safe_free(mydata);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_commands_shutdown)
{
int x;
SWITCH_ADD_API(commands_api_interface, "hupall", "hupall", hupall_api_function, "<cause> [<var> <value>]");
SWITCH_ADD_API(commands_api_interface, "in_group", "determine if a user is in a group", in_group_function, "<user>[@<domain>] <group_name>");
SWITCH_ADD_API(commands_api_interface, "is_lan_addr", "see if an ip is a lan addr", lan_addr_function, "<ip>");
+ SWITCH_ADD_API(commands_api_interface, "limit_usage", "Gets the usage count of a limited resource", limit_usage_function, "<backend> <realm> <id>");
+ SWITCH_ADD_API(commands_api_interface, "limit_status", "Gets the status of a limit backend", limit_status_function, "<backend>");
+ SWITCH_ADD_API(commands_api_interface, "limit_reset", "Reset the counters of a limit backend", limit_reset_function, "<backend>");
SWITCH_ADD_API(commands_api_interface, "load", "Load Module", load_function, LOAD_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "log", "Log", log_function, LOG_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "md5", "md5", md5_function, "<data>");
<VisualStudioProject\r
ProjectType="Visual C++"\r
Version="9.00"\r
- Name="mod_limit"\r
+ Name="mod_db"\r
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"\r
- RootNamespace="mod_limit"\r
+ RootNamespace="mod_db"\r
Keyword="Win32Proj"\r
TargetFrameworkVersion="131072"\r
>\r
</References>\r
<Files>\r
<File\r
- RelativePath=".\mod_limit.c"\r
+ RelativePath=".\mod_db.c"\r
>\r
</File>\r
</Files>\r
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Ken Rice <krice at suspicious dot org
+ * Mathieu Rene <mathieu.rene@gmail.com>
+ * Bret McDanel <trixter AT 0xdecafbad.com>
+ * Rupa Schomaker <rupa@rupa.com>
+ *
+ * mod_db.c -- Implements simple db API, group support, and limit db backend
+ *
+ */
+
+#include <switch.h>
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_db_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_db_shutdown);
+SWITCH_MODULE_DEFINITION(mod_db, mod_db_load, mod_db_shutdown, NULL);
+
+static struct {
+ switch_memory_pool_t *pool;
+ char hostname[256];
+ char *dbname;
+ char *odbc_dsn;
+ char *odbc_user;
+ char *odbc_pass;
+ switch_mutex_t *mutex;
+ switch_mutex_t *db_hash_mutex;
+ switch_hash_t *db_hash;
+} globals;
+
+typedef struct {
+ uint32_t total_usage;
+ uint32_t rate_usage;
+ time_t last_check;
+} limit_hash_item_t;
+
+struct callback {
+ char *buf;
+ size_t len;
+ int matches;
+};
+
+typedef struct callback callback_t;
+
+static int sql2str_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+ callback_t *cbt = (callback_t *) pArg;
+
+ switch_copy_string(cbt->buf, argv[0], cbt->len);
+ cbt->matches++;
+ return 0;
+}
+
+static char limit_sql[] =
+ "CREATE TABLE limit_data (\n"
+ " hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " id VARCHAR(255),\n" " uuid VARCHAR(255)\n" ");\n";
+
+static char db_sql[] =
+ "CREATE TABLE db_data (\n"
+ " hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " data_key VARCHAR(255),\n" " data VARCHAR(255)\n" ");\n";
+
+static char group_sql[] =
+ "CREATE TABLE group_data (\n" " hostname VARCHAR(255),\n" " groupname VARCHAR(255),\n" " url VARCHAR(255)\n" ");\n";
+
+
+switch_cache_db_handle_t *limit_get_db_handle(void)
+{
+ switch_cache_db_connection_options_t options = { {0} };
+ switch_cache_db_handle_t *dbh = NULL;
+
+ if (!zstr(globals.odbc_dsn)) {
+ options.odbc_options.dsn = globals.odbc_dsn;
+ options.odbc_options.user = globals.odbc_user;
+ options.odbc_options.pass = globals.odbc_pass;
+
+ if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS)
+ dbh = NULL;
+ return dbh;
+ } else {
+ options.core_db_options.db_path = globals.dbname;
+ if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_CORE_DB, &options) != SWITCH_STATUS_SUCCESS)
+ dbh = NULL;
+ return dbh;
+ }
+}
+
+
+static switch_status_t limit_execute_sql(char *sql)
+{
+ switch_cache_db_handle_t *dbh = NULL;
+ switch_status_t status = SWITCH_STATUS_FALSE;
+
+ if (!(dbh = limit_get_db_handle())) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
+ goto end;
+ }
+
+ status = switch_cache_db_execute_sql(dbh, sql, NULL);
+
+ end:
+
+ switch_cache_db_release_db_handle(&dbh);
+
+ return status;
+}
+
+
+static switch_bool_t limit_execute_sql_callback(char *sql, switch_core_db_callback_func_t callback, void *pdata)
+{
+ switch_bool_t ret = SWITCH_FALSE;
+ char *errmsg = NULL;
+ switch_cache_db_handle_t *dbh = NULL;
+
+ if (!(dbh = limit_get_db_handle())) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
+ goto end;
+ }
+
+ switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, &errmsg);
+
+ if (errmsg) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
+ free(errmsg);
+ }
+
+ end:
+
+ switch_cache_db_release_db_handle(&dbh);
+
+ return ret;
+}
+
+static char * limit_execute_sql2str(char *sql, char *str, size_t len)
+{
+ callback_t cbt = { 0 };
+
+ cbt.buf = str;
+ cbt.len = len;
+
+ limit_execute_sql_callback(sql, sql2str_callback, &cbt);
+
+ return cbt.buf;
+}
+
+/* \brief Enforces limit restrictions
+ * \param session current session
+ * \param realm limit realm
+ * \param id limit id
+ * \param max maximum count
+ * \return SWITCH_STATUS_SUCCESS if the access is allowed
+ */
+SWITCH_LIMIT_INCR(limit_incr_sql)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ int got = 0;
+ char *sql = NULL;
+ char gotstr[128];
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ // check max! WTF
+
+ switch_mutex_lock(globals.mutex);
+
+ switch_channel_set_variable(channel, "limit_realm", realm);
+ switch_channel_set_variable(channel, "limit_id", resource);
+ switch_channel_set_variable(channel, "limit_max", switch_core_session_sprintf(session, "%d", max));
+
+ sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id='%q';", realm, resource);
+ limit_execute_sql2str(sql, gotstr, 128);
+ switch_safe_free(sql);
+ got = atoi(gotstr);
+
+ if (max < 0) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d\n", realm, resource, got + 1);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d/%d\n", realm, resource, got + 1, max);
+ }
+
+ if (max >= 0 && got + 1 > max) {
+ status = SWITCH_STATUS_GENERR;
+ goto done;
+ }
+
+ sql =
+ switch_mprintf("insert into limit_data (hostname, realm, id, uuid) values('%q','%q','%q','%q');", globals.hostname, realm, resource,
+ switch_core_session_get_uuid(session));
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+
+ {
+ const char *susage = switch_core_session_sprintf(session, "%d", ++got);
+
+ switch_channel_set_variable(channel, "limit_usage", susage);
+ switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s_%s", realm, resource), susage);
+ }
+ switch_limit_fire_event("sql", realm, resource, got, 0, max, 0);
+
+ done:
+ switch_mutex_unlock(globals.mutex);
+ return status;
+}
+
+SWITCH_LIMIT_RELEASE(limit_release_sql)
+{
+ char *sql = NULL;
+
+ if (realm == NULL && resource == NULL) {
+ sql = switch_mprintf("delete from limit_data where uuid='%q'", switch_core_session_get_uuid(session));
+ } else {
+ sql = switch_mprintf("delete from limit_data where uuid='%q' and realm='%q' and id = '%q'", switch_core_session_get_uuid(session), realm, resource);
+ }
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_LIMIT_USAGE(limit_usage_sql)
+{
+ char usagestr[128] = "";
+ int usage = 0;
+ char *sql = NULL;
+
+ sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id='%q'", realm, resource);
+ limit_execute_sql2str(sql, usagestr, sizeof(usagestr));
+ switch_safe_free(sql);
+ usage = atoi(usagestr);
+
+ return usage;
+}
+
+SWITCH_LIMIT_RESET(limit_reset_sql)
+{
+ char *sql = NULL;
+ sql = switch_mprintf("delete from limit_data where hostname='%q';", globals.hostname);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_LIMIT_STATUS(limit_status_sql)
+{
+ char count[128] = "";
+ char *ret = NULL;
+ char *sql = NULL;
+
+ sql = switch_mprintf("select count(hostname) from limit_data where hostname='%q'", globals.hostname);
+ limit_execute_sql2str(sql, count, sizeof(count));
+ switch_safe_free(sql);
+ ret = switch_mprintf("Tracking %s resources for hostname %s.", count, globals.hostname);
+ return ret;
+}
+
+/* INIT / Config */
+
+static switch_xml_config_string_options_t limit_config_dsn = { NULL, 0, "[^:]+:[^:]+:.+" };
+
+static switch_xml_config_item_t config_settings[] = {
+ SWITCH_CONFIG_ITEM("odbc-dsn", SWITCH_CONFIG_STRING, 0, &globals.odbc_dsn, NULL, &limit_config_dsn,
+ "dsn:username:password", "If set, the ODBC DSN used by the limit and db applications"),
+ SWITCH_CONFIG_ITEM_END()
+};
+
+static switch_status_t do_config()
+{
+ switch_cache_db_handle_t *dbh = NULL;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ char *sql = NULL;
+
+ limit_config_dsn.pool = globals.pool;
+
+ if (switch_xml_config_parse_module_settings("db.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_TERM;
+ }
+
+ if (globals.odbc_dsn) {
+ if ((globals.odbc_user = strchr(globals.odbc_dsn, ':'))) {
+ *globals.odbc_user++ = '\0';
+ if ((globals.odbc_pass = strchr(globals.odbc_user, ':'))) {
+ *globals.odbc_pass++ = '\0';
+ }
+ }
+
+ if (!(dbh = limit_get_db_handle())) {
+ globals.odbc_dsn = globals.odbc_user = globals.odbc_pass;
+ }
+ }
+
+
+ if (zstr(globals.odbc_dsn)) {
+ globals.dbname = "call_limit";
+ dbh = limit_get_db_handle();
+ }
+
+
+ if (dbh) {
+ int x = 0;
+ char *indexes[] = {
+ "create index ld_hostname on limit_data (hostname)",
+ "create index ld_uuid on limit_data (uuid)",
+ "create index ld_realm on limit_data (realm)",
+ "create index ld_id on limit_data (id)",
+ "create index dd_realm on db_data (realm)",
+ "create index dd_data_key on db_data (data_key)",
+ "create index gd_groupname on group_data (groupname)",
+ "create index gd_url on group_data (url)",
+ NULL
+ };
+
+
+
+ switch_cache_db_test_reactive(dbh, "select * from limit_data", NULL, limit_sql);
+ switch_cache_db_test_reactive(dbh, "select * from db_data", NULL, db_sql);
+ switch_cache_db_test_reactive(dbh, "select * from group_data", NULL, group_sql);
+
+ for (x = 0; indexes[x]; x++) {
+ switch_cache_db_execute_sql(dbh, indexes[x], NULL);
+ }
+
+ switch_cache_db_release_db_handle(&dbh);
+
+ sql = switch_mprintf("delete from limit_data where hostname='%q';", globals.hostname);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ }
+
+ return status;
+}
+
+/* APP/API STUFF */
+
+/* CORE DB STUFF */
+
+SWITCH_STANDARD_API(db_api_function)
+{
+ int argc = 0;
+ char *argv[4] = { 0 };
+ char *mydata = NULL;
+ char *sql;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 1 || !argv[0]) {
+ goto error;
+ }
+
+ if (!strcasecmp(argv[0], "insert")) {
+ if (argc < 4) {
+ goto error;
+ }
+ sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ sql =
+ switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
+ argv[3]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ stream->write_function(stream, "+OK");
+ goto done;
+ } else if (!strcasecmp(argv[0], "delete")) {
+ if (argc < 2) {
+ goto error;
+ }
+ sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ stream->write_function(stream, "+OK");
+ goto done;
+ } else if (!strcasecmp(argv[0], "select")) {
+ char buf[256] = "";
+ if (argc < 3) {
+ goto error;
+ }
+ sql = switch_mprintf("select data from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
+ limit_execute_sql2str(sql, buf, sizeof(buf));
+ switch_safe_free(sql);
+ stream->write_function(stream, "%s", buf);
+ goto done;
+ }
+
+ error:
+ stream->write_function(stream, "!err!");
+
+ done:
+
+ switch_safe_free(mydata);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+#define DB_USAGE "[insert|delete]/<realm>/<key>/<val>"
+#define DB_DESC "save data"
+
+SWITCH_STANDARD_APP(db_function)
+{
+ int argc = 0;
+ char *argv[4] = { 0 };
+ char *mydata = NULL;
+ char *sql = NULL;
+
+ if (!zstr(data)) {
+ mydata = switch_core_session_strdup(session, data);
+ argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3 || !argv[0]) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
+ return;
+ }
+
+ if (!strcasecmp(argv[0], "insert")) {
+ if (argc < 4) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
+ return;
+ }
+ sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+
+ sql =
+ switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
+ argv[3]);
+ } else if (!strcasecmp(argv[0], "delete")) {
+ sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
+ return;
+ }
+
+ if (sql) {
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ }
+}
+
+/* GROUP STUFF */
+
+static int group_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+ callback_t *cbt = (callback_t *) pArg;
+ switch_snprintf(cbt->buf + strlen(cbt->buf), cbt->len - strlen(cbt->buf), "%s%c", argv[0], *argv[1]);
+ cbt->matches++;
+ return 0;
+}
+
+SWITCH_STANDARD_API(group_api_function)
+{
+ int argc = 0;
+ char *argv[4] = { 0 };
+ char *mydata = NULL;
+ char *sql;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 2 || !argv[0]) {
+ goto error;
+ }
+
+ if (!strcasecmp(argv[0], "insert")) {
+ if (argc < 3) {
+ goto error;
+ }
+ sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
+ switch_assert(sql);
+
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ stream->write_function(stream, "+OK");
+ goto done;
+ } else if (!strcasecmp(argv[0], "delete")) {
+ if (argc < 3) {
+ goto error;
+ }
+ if (!strcmp(argv[2], "*")) {
+ sql = switch_mprintf("delete from group_data where groupname='%q';", argv[1]);
+ } else {
+ sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
+ }
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ stream->write_function(stream, "+OK");
+ goto done;
+ } else if (!strcasecmp(argv[0], "call")) {
+ char buf[4096] = "";
+ char *how = ",";
+ callback_t cbt = { 0 };
+ cbt.buf = buf;
+ cbt.len = sizeof(buf);
+
+ if (argc > 2) {
+ if (!strcasecmp(argv[2], "order")) {
+ how = "|";
+ }
+ }
+
+ sql = switch_mprintf("select url,'%q' from group_data where groupname='%q'", how, argv[1]);
+ switch_assert(sql);
+
+ limit_execute_sql_callback(sql, group_callback, &cbt);
+ switch_safe_free(sql);
+
+ *(buf + (strlen(buf) - 1)) = '\0';
+ stream->write_function(stream, "%s", buf);
+
+ goto done;
+ }
+
+ error:
+ stream->write_function(stream, "!err!");
+
+ done:
+
+ switch_safe_free(mydata);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+#define GROUP_USAGE "[insert|delete]:<group name>:<val>"
+#define GROUP_DESC "save data"
+
+SWITCH_STANDARD_APP(group_function)
+{
+ int argc = 0;
+ char *argv[3] = { 0 };
+ char *mydata = NULL;
+ char *sql;
+
+ if (!zstr(data)) {
+ mydata = switch_core_session_strdup(session, data);
+ argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3 || !argv[0]) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: group %s\n", DB_USAGE);
+ return;
+ }
+
+ if (!strcasecmp(argv[0], "insert")) {
+ sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ } else if (!strcasecmp(argv[0], "delete")) {
+ sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
+ switch_assert(sql);
+ limit_execute_sql(sql);
+ switch_safe_free(sql);
+ }
+}
+
+/* INIT/DEINIT STUFF */
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_db_load)
+{
+ switch_status_t status;
+ switch_application_interface_t *app_interface;
+ switch_api_interface_t *commands_api_interface;
+ switch_limit_interface_t *limit_interface;
+
+ memset(&globals, 0, sizeof(&globals));
+ gethostname(globals.hostname, sizeof(globals.hostname));
+ globals.pool = pool;
+
+
+ if ((status = do_config() != SWITCH_STATUS_SUCCESS)) {
+ return status;
+ }
+
+ switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_mutex_init(&globals.db_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_core_hash_init(&globals.db_hash, pool);
+
+ status = switch_event_reserve_subclass(LIMIT_EVENT_USAGE);
+ if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_INUSE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register event subclass \"%s\" (%d)\n", LIMIT_EVENT_USAGE, status);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ /* register limit interfaces */
+ SWITCH_ADD_LIMIT(limit_interface, "sql", limit_incr_sql, limit_release_sql, limit_usage_sql, limit_reset_sql, limit_status_sql);
+
+ SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
+ switch_console_set_complete("add db insert");
+ switch_console_set_complete("add db delete");
+ switch_console_set_complete("add db select");
+ SWITCH_ADD_API(commands_api_interface, "group", "group [insert|delete|call]", group_api_function, "[insert|delete|call]:<group name>:<url>");
+ switch_console_set_complete("add group insert");
+ switch_console_set_complete("add group delete");
+ switch_console_set_complete("add group call");
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_db_shutdown)
+{
+
+ switch_xml_config_cleanup(config_settings);
+
+ switch_mutex_destroy(globals.mutex);
+ switch_mutex_destroy(globals.db_hash_mutex);
+
+ switch_core_hash_destroy(&globals.db_hash);
+
+ return SWITCH_STATUS_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:
+ */
}
}
+/* LIMIT STUFF */
+#define LIMIT_USAGE "<backend> <realm> <id> [<max>[/interval]] [number [dialplan [context]]]"
+#define LIMIT_DESC "limit access to a resource and transfer to an extension if the limit is exceeded"
+SWITCH_STANDARD_APP(limit_function)
+{
+ int argc = 0;
+ char *argv[7] = { 0 };
+ char *mydata = NULL;
+ char *backend = NULL;
+ char *realm = NULL;
+ char *id = NULL;
+ char *xfer_exten = NULL;
+ int max = -1;
+ int interval = 0;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+ /* Parse application data */
+ if (!zstr(data)) {
+ mydata = switch_core_session_strdup(session, data);
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit %s\n", LIMIT_USAGE);
+ return;
+ }
+
+ backend = argv[0];
+ realm = argv[1];
+ id = argv[2];
+
+ /* If max is omitted or negative, only act as a counter and skip maximum checks */
+ if (argc > 3) {
+ if (argv[3][0] == '-') {
+ max = -1;
+ } else {
+ char *szinterval = NULL;
+ if ((szinterval = strchr(argv[3], '/'))) {
+ *szinterval++ = '\0';
+ interval = atoi(szinterval);
+ }
+
+ max = atoi(argv[3]);
+
+ if (max < 0) {
+ max = 0;
+ }
+ }
+ }
+
+ if (argc > 4) {
+ xfer_exten = argv[4];
+ } else {
+ xfer_exten = LIMIT_DEF_XFER_EXTEN;
+ }
+
+ if (switch_limit_incr(backend, session, realm, id, max, interval) != SWITCH_STATUS_SUCCESS) {
+ /* Limit exceeded */
+ if (*xfer_exten == '!') {
+ switch_channel_hangup(channel, switch_channel_str2cause(xfer_exten + 1));
+ } else {
+ switch_ivr_session_transfer(session, xfer_exten, argv[5], argv[6]);
+ }
+ }
+}
+
+
+#define LIMITEXECUTE_USAGE "<backend> <realm> <id> [<max>[/interval]] [application] [application arguments]"
+#define LIMITEXECUTE_DESC "limit access to a resource. the specified application will only be executed if the resource is available"
+SWITCH_STANDARD_APP(limit_execute_function)
+{
+ int argc = 0;
+ char *argv[6] = { 0 };
+ char *mydata = NULL;
+ char *backend = NULL;
+ char *realm = NULL;
+ char *id = NULL;
+ char *app = NULL;
+ char *app_arg = NULL;
+ int max = -1;
+ int interval = 0;
+
+ /* Parse application data */
+ if (!zstr(data)) {
+ mydata = switch_core_session_strdup(session, data);
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 6) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit_execute %s\n", LIMITEXECUTE_USAGE);
+ return;
+ }
+
+ backend = argv[0];
+ realm = argv[1];
+ id = argv[2];
+
+ /* Accept '-' as unlimited (act as counter) */
+ if (argv[3][0] == '-') {
+ max = -1;
+ } else {
+ char *szinterval = NULL;
+
+ if ((szinterval = strchr(argv[3], '/'))) {
+ *szinterval++ = '\0';
+ interval = atoi(szinterval);
+ }
+
+ max = atoi(argv[3]);
+
+ if (max < 0) {
+ max = 0;
+ }
+ }
+
+ app = argv[4];
+ app_arg = argv[5];
+
+ if (zstr(app)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing application\n");
+ return;
+ }
+
+ if (switch_limit_incr(backend, session, realm, id, max, interval) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Executing\n");
+ switch_core_session_execute_application(session, app, app_arg);
+ /* Only release the resource if we are still in CS_EXECUTE */
+ if (switch_channel_get_state(switch_core_session_get_channel(session)) == CS_EXECUTE) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "immediately releasing\n");
+ switch_limit_release(backend, session, realm, id);
+ }
+ }
+}
+
#define SPEAK_DESC "Speak text to a channel via the tts interface"
#define DISPLACE_DESC "Displace audio from a file to the channels input"
#define SESS_REC_DESC "Starts a background recording of the entire session"
SAF_NONE);
SWITCH_ADD_APP(app_interface, "session_loglevel", "session_loglevel", "session_loglevel", session_loglevel_function, SESSION_LOGLEVEL_SYNTAX,
SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_APP(app_interface, "limit", "Limit", LIMIT_DESC, limit_function, LIMIT_USAGE, SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_APP(app_interface, "limit_execute", "Limit", LIMITEXECUTE_USAGE, limit_execute_function, LIMITEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_DIALPLAN(dp_interface, "inline", inline_dialplan_hunt);
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="9.00"\r
+ Name="mod_hash"\r
+ ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"\r
+ RootNamespace="mod_hash"\r
+ Keyword="Win32Proj"\r
+ TargetFrameworkVersion="131072"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ <Platform\r
+ Name="x64"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ RandomizedBaseAddress="1"\r
+ DataExecutionPrevention="0"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Debug|x64"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ TargetEnvironment="3"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+ RandomizedBaseAddress="1"\r
+ DataExecutionPrevention="0"\r
+ TargetMachine="17"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ RandomizedBaseAddress="1"\r
+ DataExecutionPrevention="0"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|x64"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ TargetEnvironment="3"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+ RandomizedBaseAddress="1"\r
+ DataExecutionPrevention="0"\r
+ TargetMachine="17"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <File\r
+ RelativePath=".\mod_hash.c"\r
+ >\r
+ </File>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Ken Rice <krice at suspicious dot org
+ * Mathieu Rene <mathieu.rene@gmail.com>
+ * Bret McDanel <trixter AT 0xdecafbad.com>
+ * Rupa Schomaker <rupa@rupa.com>
+ *
+ * mod_hash.c -- Hash api, hash backend for limit
+ *
+ */
+
+#include <switch.h>
+
+#define LIMIT_HASH_CLEANUP_INTERVAL 900
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown);
+SWITCH_MODULE_DEFINITION(mod_hash, mod_hash_load, mod_hash_shutdown, NULL);
+
+/* CORE STUFF */
+static struct {
+ switch_memory_pool_t *pool;
+ switch_thread_rwlock_t *limit_hash_rwlock;
+ switch_hash_t *limit_hash;
+ switch_thread_rwlock_t *db_hash_rwlock;
+ switch_hash_t *db_hash;
+} globals;
+
+typedef struct {
+ uint32_t total_usage;
+ uint32_t rate_usage;
+ time_t last_check;
+ uint32_t interval;
+} limit_hash_item_t;
+
+struct callback {
+ char *buf;
+ size_t len;
+ int matches;
+};
+
+typedef struct callback callback_t;
+
+/* HASH STUFF */
+typedef struct {
+ switch_hash_t *hash;
+} limit_hash_private_t;
+
+/* \brief Enforces limit_hash restrictions
+ * \param session current session
+ * \param realm limit realm
+ * \param id limit id
+ * \param max maximum count
+ * \param interval interval for rate limiting
+ * \return SWITCH_TRUE if the access is allowed, SWITCH_FALSE if it isnt
+ */
+SWITCH_LIMIT_INCR(limit_incr_hash)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ char *hashkey = NULL;
+ switch_bool_t status = SWITCH_STATUS_SUCCESS;
+ limit_hash_item_t *item = NULL;
+ time_t now = switch_epoch_time_now(NULL);
+ limit_hash_private_t *pvt = NULL;
+ uint8_t increment = 1;
+
+ hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
+
+ switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
+ /* Check if that realm+resource has ever been checked */
+ if (!(item = (limit_hash_item_t *) switch_core_hash_find(globals.limit_hash, hashkey))) {
+ /* No, create an empty structure and add it, then continue like as if it existed */
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Creating new limit structure: key: %s\n", hashkey);
+ item = (limit_hash_item_t *) malloc(sizeof(limit_hash_item_t));
+ switch_assert(item);
+ memset(item, 0, sizeof(limit_hash_item_t));
+ switch_core_hash_insert(globals.limit_hash, hashkey, item);
+ }
+
+ /* Did we already run on this channel before? */
+ if ((pvt = switch_channel_get_private(channel, "limit_hash"))) {
+ /* Yes, but check if we did that realm+resource
+ If we didnt, allow incrementing the counter.
+ If we did, dont touch it but do the validation anyways
+ */
+ increment = !switch_core_hash_find(pvt->hash, hashkey);
+ } else {
+ /* This is the first limit check on this channel, create a hashtable, set our prviate data */
+ pvt = (limit_hash_private_t *) switch_core_session_alloc(session, sizeof(limit_hash_private_t));
+ memset(pvt, 0, sizeof(limit_hash_private_t));
+ switch_core_hash_init(&pvt->hash, switch_core_session_get_pool(session));
+ switch_channel_set_private(channel, "limit_hash", pvt);
+ }
+
+ if (interval > 0) {
+ item->interval = interval;
+ if (item->last_check <= (now - interval)) {
+ item->rate_usage = 1;
+ item->last_check = now;
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Usage for %s reset to 1\n",
+ hashkey);
+ } else {
+ /* Always increment rate when its checked as it doesnt depend on the channel */
+ item->rate_usage++;
+
+ if ((max >= 0) && (item->rate_usage > (uint32_t) max)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s exceeds maximum rate of %d/%ds, now at %d\n",
+ hashkey, max, interval, item->rate_usage);
+ status = SWITCH_STATUS_GENERR;
+ goto end;
+ }
+ }
+ } else if ((max >= 0) && (item->total_usage + increment > (uint32_t) max)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is already at max value (%d)\n", hashkey, item->total_usage);
+ status = SWITCH_STATUS_GENERR;
+ goto end;
+ }
+
+ if (increment) {
+ item->total_usage++;
+
+ switch_core_hash_insert(pvt->hash, hashkey, item);
+
+ if (max == -1) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", hashkey, item->total_usage);
+ } else if (interval == 0) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d\n", hashkey, item->total_usage, max);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey,
+ item->rate_usage, max, interval);
+ }
+
+ switch_limit_fire_event("hash", realm, resource, item->total_usage, item->rate_usage, max, max >= 0 ? (uint32_t) max : 0);
+ }
+
+ /* Save current usage & rate into channel variables so it can be used later in the dialplan, or added to CDR records */
+ {
+ const char *susage = switch_core_session_sprintf(session, "%d", item->total_usage);
+ const char *srate = switch_core_session_sprintf(session, "%d", item->rate_usage);
+
+ switch_channel_set_variable(channel, "limit_usage", susage);
+ switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s", hashkey), susage);
+
+ switch_channel_set_variable(channel, "limit_rate", srate);
+ switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_rate_%s", hashkey), srate);
+ }
+
+ end:
+ switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
+ return status;
+}
+
+/* !\brief Determines whether a given entry is ready to be removed. */
+SWITCH_HASH_DELETE_FUNC(limit_hash_cleanup_delete_callback) {
+ limit_hash_item_t *item = (limit_hash_item_t *) val;
+ time_t now = switch_epoch_time_now(NULL);
+
+ /* reset to 0 if window has passed so we can clean it up */
+ if (item->rate_usage > 0 && (item->last_check <= (now - item->interval))) {
+ item->rate_usage = 0;
+ }
+
+ if (item->total_usage == 0 && item->rate_usage == 0) {
+ /* Noone is using this item anymore */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Freeing limit item: %s\n", (const char *) key);
+
+ free(item);
+ return SWITCH_TRUE;
+ }
+
+ return SWITCH_FALSE;
+}
+
+/* !\brief Periodically checks for unused limit entries and frees them */
+SWITCH_STANDARD_SCHED_FUNC(limit_hash_cleanup_callback)
+{
+ switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
+ switch_core_hash_delete_multi(globals.limit_hash, limit_hash_cleanup_delete_callback, NULL);
+ switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
+
+ task->runtime = switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL;
+}
+
+/* !\brief Releases usage of a limit_hash-controlled ressource */
+SWITCH_LIMIT_RELEASE(limit_release_hash)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ limit_hash_private_t *pvt = switch_channel_get_private(channel, "limit_hash");
+ limit_hash_item_t *item = NULL;
+ switch_hash_index_t *hi;
+ char *hashkey = NULL;
+
+ if (!pvt || !pvt->hash) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
+
+ /* clear for uuid */
+ if (realm == NULL && resource == NULL) {
+ /* Loop through the channel's hashtable which contains mapping to all the limit_hash_item_t referenced by that channel */
+ while ((hi = switch_hash_first(NULL, pvt->hash))) {
+ void *val = NULL;
+ const void *key;
+ switch_ssize_t keylen;
+ limit_hash_item_t *item = NULL;
+
+ switch_hash_this(hi, &key, &keylen, &val);
+
+ item = (limit_hash_item_t *) val;
+ item->total_usage--;
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) key, item->total_usage);
+
+ if (item->total_usage == 0 && item->rate_usage == 0) {
+ /* Noone is using this item anymore */
+ switch_core_hash_delete(globals.limit_hash, (const char *) key);
+ free(item);
+ }
+
+ switch_core_hash_delete(pvt->hash, (const char *) key);
+ }
+ } else {
+ hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
+
+ if ((item = (limit_hash_item_t *) switch_core_hash_find(pvt->hash, hashkey))) {
+ item->total_usage--;
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) hashkey, item->total_usage);
+
+ switch_core_hash_delete(pvt->hash, hashkey);
+
+ if (item->total_usage == 0 && item->rate_usage == 0) {
+ /* Noone is using this item anymore */
+ switch_core_hash_delete(globals.limit_hash, (const char *) hashkey);
+ free(item);
+ }
+ }
+ }
+
+ switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_LIMIT_USAGE(limit_usage_hash)
+{
+ char *hash_key = NULL;
+ limit_hash_item_t *item = NULL;
+ int count = 0;
+
+ switch_thread_rwlock_rdlock(globals.limit_hash_rwlock);
+
+ hash_key = switch_mprintf("%s_%s", realm, resource);
+
+ if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) {
+ count = item->total_usage;
+ *rcount = item->rate_usage;
+ }
+
+ switch_safe_free(hash_key);
+ switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
+
+ return count;
+}
+
+SWITCH_LIMIT_RESET(limit_reset_hash)
+{
+ return SWITCH_STATUS_GENERR;
+}
+
+SWITCH_LIMIT_STATUS(limit_status_hash)
+{
+ /*
+ switch_hash_index_t *hi = NULL;
+ int count = 0;
+ char *ret = NULL;
+
+ switch_thread_rwlock_rdlock(globals.limit_hash_rwlock);
+
+ for (hi = switch_hash_first(NULL, globals.limit_hash); hi; switch_hash_next(hi)) {
+ count++;
+ }
+
+ switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
+
+ ret = switch_mprintf("There are %d elements being tracked.", count);
+ return ret;
+ */
+ return strdup("-ERR not supported yet (locking problems).");
+}
+
+/* APP/API STUFF */
+
+/* CORE HASH STUFF */
+
+#define HASH_USAGE "[insert|delete]/<realm>/<key>/<val>"
+#define HASH_DESC "save data"
+
+SWITCH_STANDARD_APP(hash_function)
+{
+ int argc = 0;
+ char *argv[4] = { 0 };
+ char *mydata = NULL;
+ char *hash_key = NULL;
+ char *value = NULL;
+
+ switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
+
+ if (!zstr(data)) {
+ mydata = strdup(data);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3 || !argv[0]) {
+ goto usage;
+ }
+
+ hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
+
+ if (!strcasecmp(argv[0], "insert")) {
+ if (argc < 4) {
+ goto usage;
+ }
+ if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
+ free(value);
+ switch_core_hash_delete(globals.db_hash, hash_key);
+ }
+ value = strdup(argv[3]);
+ switch_assert(value);
+ switch_core_hash_insert(globals.db_hash, hash_key, value);
+ } else if (!strcasecmp(argv[0], "delete")) {
+ if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
+ switch_safe_free(value);
+ switch_core_hash_delete(globals.db_hash, hash_key);
+ }
+ } else {
+ goto usage;
+ }
+
+ goto done;
+
+ usage:
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: hash %s\n", HASH_USAGE);
+
+ done:
+ switch_thread_rwlock_unlock(globals.db_hash_rwlock);
+ switch_safe_free(mydata);
+ switch_safe_free(hash_key);
+}
+
+#define HASH_API_USAGE "insert|select|delete/realm/key[/value]"
+SWITCH_STANDARD_API(hash_api_function)
+{
+ int argc = 0;
+ char *argv[4] = { 0 };
+ char *mydata = NULL;
+ char *value = NULL;
+ char *hash_key = NULL;
+
+ if (!zstr(cmd)) {
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+ argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
+ }
+
+ if (argc < 3 || !argv[0]) {
+ goto usage;
+ }
+
+ hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
+
+ if (!strcasecmp(argv[0], "insert")) {
+ if (argc < 4) {
+ goto usage;
+ }
+ switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
+ if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
+ switch_safe_free(value);
+ switch_core_hash_delete(globals.db_hash, hash_key);
+ }
+ value = strdup(argv[3]);
+ switch_assert(value);
+ switch_core_hash_insert(globals.db_hash, hash_key, value);
+ stream->write_function(stream, "+OK\n");
+ } else if (!strcasecmp(argv[0], "delete")) {
+ switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
+ if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
+ switch_safe_free(value);
+ switch_core_hash_delete(globals.db_hash, hash_key);
+ stream->write_function(stream, "+OK\n");
+ } else {
+ stream->write_function(stream, "-ERR Not found\n");
+ }
+ } else if (!strcasecmp(argv[0], "select")) {
+ switch_thread_rwlock_rdlock(globals.db_hash_rwlock);
+ if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
+ stream->write_function(stream, "%s", value);
+ }
+ } else {
+ goto usage;
+ }
+
+ goto done;
+
+ usage:
+ stream->write_function(stream, "-ERR Usage: hash %s\n", HASH_API_USAGE);
+
+ done:
+ switch_thread_rwlock_unlock(globals.db_hash_rwlock);
+ switch_safe_free(mydata);
+ switch_safe_free(hash_key);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* INIT/DEINIT STUFF */
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load)
+{
+ switch_application_interface_t *app_interface;
+ switch_api_interface_t *commands_api_interface;
+ switch_limit_interface_t *limit_interface;
+ switch_status_t status;
+
+ memset(&globals, 0, sizeof(&globals));
+ globals.pool = pool;
+
+ status = switch_event_reserve_subclass(LIMIT_EVENT_USAGE);
+ if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_INUSE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register event subclass \"%s\" (%d)\n", LIMIT_EVENT_USAGE, status);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_thread_rwlock_create(&globals.limit_hash_rwlock, globals.pool);
+ switch_thread_rwlock_create(&globals.db_hash_rwlock, globals.pool);
+ switch_core_hash_init(&globals.limit_hash, pool);
+ switch_core_hash_init(&globals.db_hash, pool);
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ /* register limit interfaces */
+ SWITCH_ADD_LIMIT(limit_interface, "hash", limit_incr_hash, limit_release_hash, limit_usage_hash, limit_reset_hash, limit_status_hash);
+
+ switch_scheduler_add_task(switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_hash", 0, NULL,
+ SSHF_NONE);
+
+ SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA)
+ SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
+ switch_console_set_complete("add hash insert");
+ switch_console_set_complete("add hash delete");
+ switch_console_set_complete("add hash select");
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown)
+{
+ switch_scheduler_del_task_group("mod_hash");
+
+ switch_thread_rwlock_destroy(globals.db_hash_rwlock);
+ switch_thread_rwlock_destroy(globals.limit_hash_rwlock);
+
+ switch_core_hash_destroy(&globals.limit_hash);
+ switch_core_hash_destroy(&globals.db_hash);
+
+ return SWITCH_STATUS_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:
+ */
switch_bool_t reorder_by_rate;
switch_bool_t quote_in_list;
switch_bool_t info_in_headers;
- switch_bool_t enable_sip_redir;
};
typedef struct profile_obj profile_t;
user_rate = switch_core_sprintf(pool, ",lcr_user_rate=%s", cur_route->user_rate_str);
}
- if (profile->enable_sip_redir) {
- data =
- switch_core_sprintf(pool, "%s%s%s%s%s", cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
- } else {
- data =
- switch_core_sprintf(pool, "[lcr_carrier=%s,lcr_rate=%s%s%s%s%s]%s%s%s%s%s", cur_route->carrier_name, cur_route->rate_str, user_rate, codec, cid,
- header, cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
- }
-
- if (session && (switch_string_var_check_const(data) || switch_string_has_escaped_data(data))) {
- data = switch_channel_expand_variables(switch_core_session_get_channel(session), data);
- }
+ data =
+ switch_core_sprintf(pool, "[lcr_carrier=%s,lcr_rate=%s%s%s%s%s]%s%s%s%s%s", cur_route->carrier_name, cur_route->rate_str, user_rate, codec, cid,
+ header, cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Returning Dialstring %s\n", data);
return data;
switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) {
- if (switch_cache_db_execute_sql(dbh, sql, NULL) == SWITCH_STATUS_SUCCESS) {
+ if (switch_cache_db_execute_sql(dbh, sql, NULL) == SWITCH_ODBC_SUCCESS) {
ret = SWITCH_TRUE;
}
}
switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) {
- if (switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, NULL) != SWITCH_STATUS_SUCCESS) {
+ if (switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, NULL)
+ == SWITCH_ODBC_FAIL) {
retval = SWITCH_FALSE;
} else {
retval = SWITCH_TRUE;
char *reorder_by_rate = NULL;
char *quote_in_list = NULL;
char *info_in_headers = NULL;
- char *enable_sip_redir = NULL;
char *id_s = NULL;
char *custom_sql = NULL;
int argc, x = 0;
info_in_headers = val;
} else if (!strcasecmp(var, "quote_in_list") && !zstr(val)) {
quote_in_list = val;
- } else if (!strcasecmp(var, "enable_sip_redir") && !zstr(val)) {
- enable_sip_redir = val;
}
}
profile->info_in_headers = switch_true(info_in_headers);
}
- if (!zstr(enable_sip_redir)) {
- profile->enable_sip_redir = switch_true(enable_sip_redir);
- }
-
if (!zstr(quote_in_list)) {
profile->quote_in_list = switch_true(quote_in_list);
}
switch_channel_set_variable(channel, vbuf, cur_route->codec);
cnt++;
if (cur_route->next) {
- if (routes.profile->enable_sip_redir) {
- dig_stream.write_function(&dig_stream, "%s,", cur_route->dialstring);
- } else {
- dig_stream.write_function(&dig_stream, "%s|", cur_route->dialstring);
- }
+ dig_stream.write_function(&dig_stream, "%s|", cur_route->dialstring);
} else {
dig_stream.write_function(&dig_stream, "%s", cur_route->dialstring);
}
stream->write_function(stream, " has npanxx:\t%s\n", profile->profile_has_npanxx ? "true" : "false");
stream->write_function(stream, " Reorder rate:\t%s\n", profile->reorder_by_rate ? "enabled" : "disabled");
stream->write_function(stream, " Info in headers:\t%s\n", profile->info_in_headers ? "enabled" : "disabled");
- stream->write_function(stream, " Sip Redirection Mode:\t%s\n", profile->enable_sip_redir ? "enabled" : "disabled");
stream->write_function(stream, " Quote IN() List:\t%s\n", profile->quote_in_list ? "enabled" : "disabled");
stream->write_function(stream, "\n");
}
+++ /dev/null
-/*
- * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- * Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
- *
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- *
- * The Initial Developer of the Original Code is
- * Anthony Minessale II <anthm@freeswitch.org>
- * Portions created by the Initial Developer are Copyright (C)
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Anthony Minessale II <anthm@freeswitch.org>
- * Ken Rice <krice at suspicious dot org
- * Mathieu Rene <mathieu.rene@gmail.com>
- * Bret McDanel <trixter AT 0xdecafbad.com>
- * Rupa Schomaker <rupa@rupa.com>
- *
- * mod_limit.c -- Resource Limit Module
- *
- */
-
-#include <switch.h>
-
-#define LIMIT_EVENT_USAGE "limit::usage"
-#define LIMIT_IGNORE_TRANSFER_VARIABLE "limit_ignore_transfer"
-#define LIMIT_HASH_CLEANUP_INTERVAL 900
-
-SWITCH_MODULE_LOAD_FUNCTION(mod_limit_load);
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_limit_shutdown);
-SWITCH_MODULE_DEFINITION(mod_limit, mod_limit_load, mod_limit_shutdown, NULL);
-
-static struct {
- switch_memory_pool_t *pool;
- char hostname[256];
- char *dbname;
- char *odbc_dsn;
- char *odbc_user;
- char *odbc_pass;
- switch_mutex_t *mutex;
- switch_mutex_t *limit_hash_mutex;
- switch_hash_t *limit_hash;
- switch_mutex_t *db_hash_mutex;
- switch_hash_t *db_hash;
-} globals;
-
-typedef struct {
- uint32_t total_usage;
- uint32_t rate_usage;
- time_t last_check;
- uint32_t interval;
-} limit_hash_item_t;
-
-typedef struct {
- switch_hash_t *hash;
-} limit_hash_private_t;
-
-static char limit_sql[] =
- "CREATE TABLE limit_data (\n"
- " hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " id VARCHAR(255),\n" " uuid VARCHAR(255)\n" ");\n";
-
-static char db_sql[] =
- "CREATE TABLE db_data (\n"
- " hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " data_key VARCHAR(255),\n" " data VARCHAR(255)\n" ");\n";
-
-static char group_sql[] =
- "CREATE TABLE group_data (\n" " hostname VARCHAR(255),\n" " groupname VARCHAR(255),\n" " url VARCHAR(255)\n" ");\n";
-
-
-switch_cache_db_handle_t *limit_get_db_handle(void)
-{
- switch_cache_db_connection_options_t options = { {0} };
- switch_cache_db_handle_t *dbh = NULL;
-
- if (!zstr(globals.odbc_dsn)) {
- options.odbc_options.dsn = globals.odbc_dsn;
- options.odbc_options.user = globals.odbc_user;
- options.odbc_options.pass = globals.odbc_pass;
-
- if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS)
- dbh = NULL;
- return dbh;
- } else {
- options.core_db_options.db_path = globals.dbname;
- if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_CORE_DB, &options) != SWITCH_STATUS_SUCCESS)
- dbh = NULL;
- return dbh;
- }
-}
-
-
-static switch_status_t limit_execute_sql(char *sql, switch_mutex_t *mutex)
-{
- switch_cache_db_handle_t *dbh = NULL;
- switch_status_t status = SWITCH_STATUS_FALSE;
-
- if (mutex) {
- switch_mutex_lock(mutex);
- }
-
- if (!(dbh = limit_get_db_handle())) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
- goto end;
- }
-
- status = switch_cache_db_execute_sql(dbh, sql, NULL);
-
- end:
-
- switch_cache_db_release_db_handle(&dbh);
-
- if (mutex) {
- switch_mutex_unlock(mutex);
- }
-
- return status;
-}
-
-static switch_bool_t limit_execute_sql_callback(switch_mutex_t *mutex, char *sql, switch_core_db_callback_func_t callback, void *pdata)
-{
- switch_bool_t ret = SWITCH_FALSE;
- char *errmsg = NULL;
- switch_cache_db_handle_t *dbh = NULL;
-
- if (mutex) {
- switch_mutex_lock(mutex);
- }
-
- if (!(dbh = limit_get_db_handle())) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
- goto end;
- }
-
- switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, &errmsg);
-
- if (errmsg) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
- free(errmsg);
- }
-
- end:
-
- switch_cache_db_release_db_handle(&dbh);
-
- if (mutex) {
- switch_mutex_unlock(mutex);
- }
-
- return ret;
-}
-
-
-static switch_xml_config_string_options_t limit_config_dsn = { NULL, 0, "[^:]+:[^:]+:.+" };
-
-static switch_xml_config_item_t config_settings[] = {
- SWITCH_CONFIG_ITEM("odbc-dsn", SWITCH_CONFIG_STRING, 0, &globals.odbc_dsn, NULL, &limit_config_dsn,
- "dsn:username:password", "If set, the ODBC DSN used by the limit and db applications"),
- SWITCH_CONFIG_ITEM_END()
-};
-
-static switch_status_t do_config()
-{
- switch_cache_db_handle_t *dbh = NULL;
- switch_status_t status = SWITCH_STATUS_SUCCESS;
- char *sql = NULL;
-
- limit_config_dsn.pool = globals.pool;
-
- if (switch_xml_config_parse_module_settings("limit.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
- return SWITCH_STATUS_TERM;
- }
-
- if (globals.odbc_dsn) {
- if ((globals.odbc_user = strchr(globals.odbc_dsn, ':'))) {
- *globals.odbc_user++ = '\0';
- if ((globals.odbc_pass = strchr(globals.odbc_user, ':'))) {
- *globals.odbc_pass++ = '\0';
- }
- }
-
- if (!(dbh = limit_get_db_handle())) {
- globals.odbc_dsn = globals.odbc_user = globals.odbc_pass;
- }
- }
-
-
- if (zstr(globals.odbc_dsn)) {
- globals.dbname = "call_limit";
- dbh = limit_get_db_handle();
- }
-
-
- if (dbh) {
- int x = 0;
- char *indexes[] = {
- "create index ld_hostname on limit_data (hostname)",
- "create index ld_uuid on limit_data (uuid)",
- "create index ld_realm on limit_data (realm)",
- "create index ld_id on limit_data (id)",
- "create index dd_realm on db_data (realm)",
- "create index dd_data_key on db_data (data_key)",
- "create index gd_groupname on group_data (groupname)",
- "create index gd_url on group_data (url)",
- NULL
- };
-
-
-
- switch_cache_db_test_reactive(dbh, "select * from limit_data", NULL, limit_sql);
- switch_cache_db_test_reactive(dbh, "select * from db_data", NULL, db_sql);
- switch_cache_db_test_reactive(dbh, "select * from group_data", NULL, group_sql);
-
- for (x = 0; indexes[x]; x++) {
- switch_cache_db_execute_sql(dbh, indexes[x], NULL);
- }
-
- switch_cache_db_release_db_handle(&dbh);
-
- sql = switch_mprintf("delete from limit_data where hostname='%q';", globals.hostname);
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
- }
-
- return status;
-}
-
-static void limit_fire_event(const char *realm, const char *key, uint32_t usage, uint32_t rate, uint32_t max, uint32_t ratemax)
-{
- switch_event_t *event;
-
- if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, LIMIT_EVENT_USAGE) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "realm", realm);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", key);
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "usage", "%d", usage);
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rate", "%d", rate);
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "max", "%d", max);
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "ratemax", "%d", ratemax);
- switch_event_fire(&event);
- }
-}
-
-static switch_status_t db_state_handler(switch_core_session_t *session)
-{
- switch_channel_t *channel = switch_core_session_get_channel(session);
- switch_channel_state_t state = switch_channel_get_state(channel);
- char *sql = NULL;
- const char *vval = switch_channel_get_variable(channel, LIMIT_IGNORE_TRANSFER_VARIABLE);
-
- if (state >= CS_HANGUP || (state == CS_ROUTING && !switch_true(vval))) {
- sql = switch_mprintf("delete from limit_data where uuid='%q';", switch_core_session_get_uuid(session));
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
- switch_core_event_hook_remove_state_change(session, db_state_handler);
- /* Remove limit_realm variable so we register another hook if limit is called again */
- switch_channel_set_variable(channel, "limit_realm", NULL);
- }
- return SWITCH_STATUS_SUCCESS;
-}
-
-static switch_status_t hash_state_handler(switch_core_session_t *session)
-{
- switch_channel_t *channel = switch_core_session_get_channel(session);
- switch_channel_state_t state = switch_channel_get_state(channel);
- limit_hash_private_t *pvt = switch_channel_get_private(channel, "limit_hash");
- const char *vval = switch_channel_get_variable(channel, LIMIT_IGNORE_TRANSFER_VARIABLE);
-
- /* The call is either hung up, or is going back into the dialplan, decrement appropriate couters */
- if (state >= CS_HANGUP || (state == CS_ROUTING && !switch_true(vval))) {
- switch_hash_index_t *hi;
- switch_mutex_lock(globals.limit_hash_mutex);
-
- /* Loop through the channel's hashtable which contains mapping to all the limit_hash_item_t referenced by that channel
- while() idiom used -- 'cause pvt->hash is being completely removed
- */
- while ((hi = switch_hash_first(NULL, pvt->hash))) {
- void *val = NULL;
- const void *key;
- switch_ssize_t keylen;
- limit_hash_item_t *item = NULL;
-
- switch_hash_this(hi, &key, &keylen, &val);
-
- item = (limit_hash_item_t *) val;
- item->total_usage--;
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) key, item->total_usage);
-
- if (item->total_usage == 0 && item->rate_usage == 0) {
- /* Noone is using this item anymore */
- switch_core_hash_delete(globals.limit_hash, (const char *) key);
- free(item);
- }
-
- switch_core_hash_delete(pvt->hash, (const char *) key);
- }
-
- /* Remove handler */
- switch_core_event_hook_remove_state_change(session, hash_state_handler);
-
- switch_mutex_unlock(globals.limit_hash_mutex);
- }
-
- return SWITCH_STATUS_SUCCESS;
-}
-
-struct callback {
- char *buf;
- size_t len;
- int matches;
-};
-
-typedef struct callback callback_t;
-
-static int sql2str_callback(void *pArg, int argc, char **argv, char **columnNames)
-{
- callback_t *cbt = (callback_t *) pArg;
-
- switch_copy_string(cbt->buf, argv[0], cbt->len);
- cbt->matches++;
- return 0;
-}
-
-static int group_callback(void *pArg, int argc, char **argv, char **columnNames)
-{
- callback_t *cbt = (callback_t *) pArg;
- switch_snprintf(cbt->buf + strlen(cbt->buf), cbt->len - strlen(cbt->buf), "%s%c", argv[0], *argv[1]);
- cbt->matches++;
- return 0;
-}
-
-SWITCH_STANDARD_API(db_api_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *sql;
-
- switch_mutex_lock(globals.mutex);
-
- if (!zstr(cmd)) {
- mydata = strdup(cmd);
- switch_assert(mydata);
- argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 1 || !argv[0]) {
- goto error;
- }
-
- if (!strcasecmp(argv[0], "insert")) {
- if (argc < 4) {
- goto error;
- }
- sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- sql =
- switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
- argv[3]);
- switch_assert(sql);
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- stream->write_function(stream, "+OK");
- goto done;
- } else if (!strcasecmp(argv[0], "delete")) {
- if (argc < 2) {
- goto error;
- }
- sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- stream->write_function(stream, "+OK");
- goto done;
- } else if (!strcasecmp(argv[0], "select")) {
- char buf[256] = "";
- callback_t cbt = { 0 };
- if (argc < 3) {
- goto error;
- }
- cbt.buf = buf;
- cbt.len = sizeof(buf);
- sql = switch_mprintf("select data from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
- limit_execute_sql_callback(NULL, sql, sql2str_callback, &cbt);
- switch_safe_free(sql);
- stream->write_function(stream, "%s", buf);
- goto done;
- }
-
- error:
- stream->write_function(stream, "!err!");
-
- done:
-
- switch_mutex_unlock(globals.mutex);
- switch_safe_free(mydata);
- return SWITCH_STATUS_SUCCESS;
-}
-
-#define DB_USAGE "[insert|delete]/<realm>/<key>/<val>"
-#define DB_DESC "save data"
-
-SWITCH_STANDARD_APP(db_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *sql = NULL;
-
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 3 || !argv[0]) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
- return;
- }
-
- if (!strcasecmp(argv[0], "insert")) {
- if (argc < 4) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
- return;
- }
- sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
-
- sql =
- switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
- argv[3]);
- } else if (!strcasecmp(argv[0], "delete")) {
- sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
- } else {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
- return;
- }
-
- if (sql) {
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
- }
-}
-
-#define HASH_USAGE "[insert|delete]/<realm>/<key>/<val>"
-#define HASH_DESC "save data"
-
-SWITCH_STANDARD_APP(hash_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *hash_key = NULL;
- char *value = NULL;
-
- switch_mutex_lock(globals.db_hash_mutex);
-
- if (!zstr(data)) {
- mydata = strdup(data);
- switch_assert(mydata);
- argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 3 || !argv[0]) {
- goto usage;
- }
-
- hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
-
- if (!strcasecmp(argv[0], "insert")) {
- if (argc < 4) {
- goto usage;
- }
- if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
- free(value);
- switch_core_hash_delete(globals.db_hash, hash_key);
- }
- value = strdup(argv[3]);
- switch_assert(value);
- switch_core_hash_insert(globals.db_hash, hash_key, value);
- } else if (!strcasecmp(argv[0], "delete")) {
- if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
- switch_safe_free(value);
- switch_core_hash_delete(globals.db_hash, hash_key);
- }
- } else {
- goto usage;
- }
-
- goto done;
-
- usage:
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: hash %s\n", HASH_USAGE);
-
- done:
- switch_mutex_unlock(globals.db_hash_mutex);
- switch_safe_free(mydata);
- switch_safe_free(hash_key);
-}
-
-#define HASH_API_USAGE "insert|select|delete/realm/key[/value]"
-SWITCH_STANDARD_API(hash_api_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *value = NULL;
- char *hash_key = NULL;
-
- switch_mutex_lock(globals.db_hash_mutex);
-
- if (!zstr(cmd)) {
- mydata = strdup(cmd);
- switch_assert(mydata);
- argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 3 || !argv[0]) {
- goto usage;
- }
-
- hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
-
- if (!strcasecmp(argv[0], "insert")) {
- if (argc < 4) {
- goto usage;
- }
- if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
- switch_safe_free(value);
- switch_core_hash_delete(globals.db_hash, hash_key);
- }
- value = strdup(argv[3]);
- switch_assert(value);
- switch_core_hash_insert(globals.db_hash, hash_key, value);
- stream->write_function(stream, "+OK\n");
- } else if (!strcasecmp(argv[0], "delete")) {
- if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
- switch_safe_free(value);
- switch_core_hash_delete(globals.db_hash, hash_key);
- stream->write_function(stream, "+OK\n");
- } else {
- stream->write_function(stream, "-ERR Not found\n");
- }
- } else if (!strcasecmp(argv[0], "select")) {
- if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
- stream->write_function(stream, "%s", value);
- }
- } else {
- goto usage;
- }
-
- goto done;
-
- usage:
- stream->write_function(stream, "-ERR Usage: hash %s\n", HASH_API_USAGE);
-
- done:
- switch_mutex_unlock(globals.db_hash_mutex);
- switch_safe_free(mydata);
- switch_safe_free(hash_key);
-
- return SWITCH_STATUS_SUCCESS;
-}
-
-SWITCH_STANDARD_API(group_api_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *sql;
-
- switch_mutex_lock(globals.mutex);
-
- if (!zstr(cmd)) {
- mydata = strdup(cmd);
- argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 2 || !argv[0]) {
- goto error;
- }
-
- if (!strcasecmp(argv[0], "insert")) {
- if (argc < 3) {
- goto error;
- }
- sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
- switch_assert(sql);
-
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- stream->write_function(stream, "+OK");
- goto done;
- } else if (!strcasecmp(argv[0], "delete")) {
- if (argc < 3) {
- goto error;
- }
- if (!strcmp(argv[2], "*")) {
- sql = switch_mprintf("delete from group_data where groupname='%q';", argv[1]);
- } else {
- sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
- }
- switch_assert(sql);
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
- stream->write_function(stream, "+OK");
- goto done;
- } else if (!strcasecmp(argv[0], "call")) {
- char buf[4096] = "";
- char *how = ",";
- callback_t cbt = { 0 };
- cbt.buf = buf;
- cbt.len = sizeof(buf);
-
- if (argc > 2) {
- if (!strcasecmp(argv[2], "order")) {
- how = "|";
- }
- }
-
- sql = switch_mprintf("select url,'%q' from group_data where groupname='%q'", how, argv[1]);
- switch_assert(sql);
-
- limit_execute_sql_callback(NULL, sql, group_callback, &cbt);
- switch_safe_free(sql);
-
- if (!zstr(buf)) {
- *(buf + (strlen(buf) - 1)) = '\0';
- }
-
- stream->write_function(stream, "%s", buf);
-
- goto done;
- }
-
- error:
- stream->write_function(stream, "!err!");
-
- done:
-
- switch_mutex_unlock(globals.mutex);
- switch_safe_free(mydata);
- return SWITCH_STATUS_SUCCESS;
-}
-
-#define GROUP_USAGE "[insert|delete]:<group name>:<val>"
-#define GROUP_DESC "save data"
-
-SWITCH_STANDARD_APP(group_function)
-{
- int argc = 0;
- char *argv[3] = { 0 };
- char *mydata = NULL;
- char *sql;
-
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 3 || !argv[0]) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: group %s\n", DB_USAGE);
- return;
- }
-
- if (!strcasecmp(argv[0], "insert")) {
- sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
- } else if (!strcasecmp(argv[0], "delete")) {
- sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
- switch_assert(sql);
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
- }
-}
-
-/* \brief Enforces limit restrictions
- * \param session current session
- * \param realm limit realm
- * \param id limit id
- * \param max maximum count
- * \return SWITCH_TRUE if the access is allowed, SWITCH_FALSE if it isnt
- */
-static switch_bool_t do_limit(switch_core_session_t *session, const char *realm, const char *id, int max)
-{
- switch_channel_t *channel = switch_core_session_get_channel(session);
- int got = 0;
- char *sql = NULL;
- char buf[80] = "";
- callback_t cbt = { 0 };
- switch_status_t status = SWITCH_TRUE;
-
- switch_mutex_lock(globals.mutex);
-
- switch_channel_set_variable(channel, "limit_realm", realm);
- switch_channel_set_variable(channel, "limit_id", id);
- switch_channel_set_variable(channel, "limit_max", switch_core_session_sprintf(session, "%d", max));
-
- cbt.buf = buf;
- cbt.len = sizeof(buf);
- sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id='%q'", realm, id);
- limit_execute_sql_callback(NULL, sql, sql2str_callback, &cbt);
- switch_safe_free(sql);
- got = atoi(buf);
-
- if (max < 0) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d\n", realm, id, got + 1);
- } else {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d/%d\n", realm, id, got + 1, max);
- }
-
- if (max >= 0 && got + 1 > max) {
- status = SWITCH_FALSE;
- goto done;
- }
-
- switch_core_event_hook_add_state_change(session, db_state_handler);
-
- sql =
- switch_mprintf("insert into limit_data (hostname, realm, id, uuid) values('%q','%q','%q','%q');", globals.hostname, realm, id,
- switch_core_session_get_uuid(session));
- limit_execute_sql(sql, NULL);
- switch_safe_free(sql);
-
- {
- const char *susage = switch_core_session_sprintf(session, "%d", ++got);
-
- switch_channel_set_variable(channel, "limit_usage", susage);
- switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s_%s", realm, id), susage);
- }
- limit_fire_event(realm, id, got, 0, max, 0);
-
- done:
- switch_mutex_unlock(globals.mutex);
- return status;
-}
-
-#define LIMIT_USAGE "<realm> <id> [<max> [number [dialplan [context]]]]"
-#define LIMIT_DESC "limit access to a resource and transfer to an extension if the limit is exceeded"
-static char *limit_def_xfer_exten = "limit_exceeded";
-
-SWITCH_STANDARD_APP(limit_function)
-{
- int argc = 0;
- char *argv[6] = { 0 };
- char *mydata = NULL;
- char *realm = NULL;
- char *id = NULL;
- char *xfer_exten = NULL;
- int max = 0;
- switch_channel_t *channel = switch_core_session_get_channel(session);
-
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 3) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit %s\n", LIMIT_USAGE);
- return;
- }
-
- realm = argv[0];
- id = argv[1];
-
- /* Accept '-' as unlimited (act as counter) */
- if (argv[2][0] == '-') {
- max = -1;
- } else {
- max = atoi(argv[2]);
-
- if (max < 0) {
- max = 0;
- }
- }
-
- if (argc >= 4) {
- xfer_exten = argv[3];
- } else {
- xfer_exten = limit_def_xfer_exten;
- }
-
- if (!do_limit(session, realm, id, max)) {
- /* Limit exceeded */
- if (*xfer_exten == '!') {
- switch_channel_hangup(channel, switch_channel_str2cause(xfer_exten + 1));
- } else {
- switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]);
- }
- }
-
-}
-
-/* !\brief Releases usage of a limit_-controlled ressource */
-static void limit_release(switch_core_session_t *session, const char *realm, const char *id)
-{
- char *sql = NULL;
-
- sql = switch_mprintf("delete from limit_data where uuid='%q' and realm='%q' and id like '%q'", switch_core_session_get_uuid(session), realm, id);
- limit_execute_sql(sql, globals.mutex);
- switch_safe_free(sql);
-}
-
-#define LIMITEXECUTE_USAGE "<realm> <id> [<max>] [application] [application arguments]"
-#define LIMITEXECUTE_DESC "limit access to a resource. the specified application will only be executed if the resource is available"
-SWITCH_STANDARD_APP(limit_execute_function)
-{
- int argc = 0;
- char *argv[5] = { 0 };
- char *mydata = NULL;
- char *realm = NULL;
- char *id = NULL;
- char *app = NULL;
- char *app_arg = NULL;
- int max = -1;
-
- /* Parse application data */
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 2) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit_execute %s\n", LIMITEXECUTE_USAGE);
- return;
- }
-
- realm = argv[0];
- id = argv[1];
-
- /* Accept '-' as unlimited (act as counter) */
- if (argv[2][0] == '-') {
- max = -1;
- } else {
- max = atoi(argv[2]);
-
- if (max < 0) {
- max = 0;
- }
- }
-
- app = argv[3];
- app_arg = argv[4];
-
- if (zstr(app)) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing application\n");
- return;
- }
-
- if (do_limit(session, realm, id, max)) {
- switch_core_session_execute_application(session, app, app_arg);
- limit_release(session, realm, id);
- }
-}
-
-#define LIMIT_USAGE_USAGE "<realm> <id>"
-SWITCH_STANDARD_API(limit_usage_function)
-{
- int argc = 0;
- char *argv[6] = { 0 };
- char *mydata = NULL;
- char *sql = NULL;
- char *realm = NULL;
- char *id = NULL;
- char buf[80] = "";
- callback_t cbt = { 0 };
-
-
- if (!zstr(cmd)) {
- mydata = strdup(cmd);
- switch_assert(mydata);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 2) {
- stream->write_function(stream, "USAGE: limit_usage %s\n", LIMIT_USAGE_USAGE);
- goto end;
- }
-
-
- realm = argv[0];
- id = argv[1];
-
- cbt.buf = buf;
- cbt.len = sizeof(buf);
- sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id like '%q'", realm, id);
- limit_execute_sql_callback(NULL, sql, sql2str_callback, &cbt);
- switch_safe_free(sql);
-
- stream->write_function(stream, "%s", buf);
-
- end:
- switch_safe_free(mydata);
- return SWITCH_STATUS_SUCCESS;
-}
-
-/* \brief Enforces limit_hash restrictions
- * \param session current session
- * \param realm limit realm
- * \param id limit id
- * \param max maximum count
- * \param interval interval for rate limiting
- * \return SWITCH_TRUE if the access is allowed, SWITCH_FALSE if it isnt
- */
-static switch_bool_t do_limit_hash(switch_core_session_t *session, const char *realm, const char *id, int max, int interval)
-{
- switch_channel_t *channel = switch_core_session_get_channel(session);
- char *hashkey = NULL;
- switch_bool_t status = SWITCH_TRUE;
- limit_hash_item_t *item = NULL;
- time_t now = switch_epoch_time_now(NULL);
- limit_hash_private_t *pvt = NULL;
- uint8_t increment = 1;
-
- hashkey = switch_core_session_sprintf(session, "%s_%s", realm, id);
-
- switch_mutex_lock(globals.limit_hash_mutex);
- /* Check if that realm+id has ever been checked */
- if (!(item = (limit_hash_item_t *) switch_core_hash_find(globals.limit_hash, hashkey))) {
- /* No, create an empty structure and add it, then continue like as if it existed */
- item = (limit_hash_item_t *) malloc(sizeof(limit_hash_item_t));
- switch_assert(item);
- memset(item, 0, sizeof(limit_hash_item_t));
- switch_core_hash_insert(globals.limit_hash, hashkey, item);
- }
-
- /* Did we already run on this channel before? */
- if ((pvt = switch_channel_get_private(channel, "limit_hash"))) {
- /* Yes, but check if we did that realm+id
- If we didnt, allow incrementing the counter.
- If we did, dont touch it but do the validation anyways
- */
- increment = !switch_core_hash_find(pvt->hash, hashkey);
- } else {
- /* This is the first limit check on this channel, create a hashtable, set our prviate data and add a state handler */
- pvt = (limit_hash_private_t *) switch_core_session_alloc(session, sizeof(limit_hash_private_t));
- memset(pvt, 0, sizeof(limit_hash_private_t));
- switch_core_hash_init(&pvt->hash, switch_core_session_get_pool(session));
- switch_channel_set_private(channel, "limit_hash", pvt);
- }
-
- if (interval > 0) {
- /* interval is always the last used interval setting? */
- item->interval = interval;
- if (item->last_check <= (now - interval)) {
- item->rate_usage = 1;
- item->last_check = now;
- } else {
- /* Always increment rate when its checked as it doesnt depend on the channel */
- item->rate_usage++;
-
- if ((max >= 0) && (item->rate_usage > (uint32_t) max)) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s exceeds maximum rate of %d/%ds, now at %d\n",
- hashkey, max, interval, item->rate_usage);
- status = SWITCH_FALSE;
- goto end;
- }
- }
- } else if ((max >= 0) && (item->total_usage + increment > (uint32_t) max)) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is already at max value (%d)\n", hashkey, item->total_usage);
- status = SWITCH_FALSE;
- goto end;
- }
-
- if (increment) {
- item->total_usage++;
-
- switch_core_hash_insert(pvt->hash, hashkey, item);
-
- if (max == -1) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", hashkey, item->total_usage);
- } else if (interval == 0) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d\n", hashkey, item->total_usage, max);
- } else {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey,
- item->rate_usage, max, interval);
- }
-
- limit_fire_event(realm, id, item->total_usage, item->rate_usage, max, max >= 0 ? (uint32_t) max : 0);
- }
-
- /* Save current usage & rate into channel variables so it can be used later in the dialplan, or added to CDR records */
- {
- const char *susage = switch_core_session_sprintf(session, "%d", item->total_usage);
- const char *srate = switch_core_session_sprintf(session, "%d", item->rate_usage);
-
- switch_channel_set_variable(channel, "limit_usage", susage);
- switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s", hashkey), susage);
-
- switch_channel_set_variable(channel, "limit_rate", srate);
- switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_rate_%s", hashkey), srate);
- }
-
- switch_core_event_hook_add_state_change(session, hash_state_handler);
-
- end:
- switch_mutex_unlock(globals.limit_hash_mutex);
- return status;
-}
-
-SWITCH_HASH_DELETE_FUNC(limit_hash_cleanup_delete_callback) {
- limit_hash_item_t *item = (limit_hash_item_t *) val;
- time_t now = switch_epoch_time_now(NULL);
-
- /* reset to 0 if window has passed so we can clean it up */
- if (item->rate_usage > 0 && (item->last_check <= (now - item->interval))) {
- item->rate_usage = 0;
- }
-
- if (item->total_usage == 0 && item->rate_usage == 0) {
- /* Noone is using this item anymore */
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Freeing limit item: %s\n", (const char *) key);
-
- free(item);
- return SWITCH_TRUE;
- }
-
- return SWITCH_FALSE;
-}
-
-/* !\brief Periodically checks for unused limit entries and frees them */
-SWITCH_STANDARD_SCHED_FUNC(limit_hash_cleanup_callback)
-{
- switch_mutex_lock(globals.limit_hash_mutex);
- switch_core_hash_delete_multi(globals.limit_hash, limit_hash_cleanup_delete_callback, NULL);
- switch_mutex_unlock(globals.limit_hash_mutex);
-
- task->runtime = switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL;
-}
-
-/* !\brief Releases usage of a limit_hash-controlled ressource */
-static void limit_hash_release(switch_core_session_t *session, const char *realm, const char *id)
-{
- switch_channel_t *channel = switch_core_session_get_channel(session);
- limit_hash_private_t *pvt = switch_channel_get_private(channel, "limit_hash");
- limit_hash_item_t *item = NULL;
- char *hashkey = NULL;
-
- if (!pvt || !pvt->hash) {
- return;
- }
-
- hashkey = switch_core_session_sprintf(session, "%s_%s", realm, id);
-
- switch_mutex_lock(globals.limit_hash_mutex);
-
- if ((item = (limit_hash_item_t *) switch_core_hash_find(pvt->hash, hashkey))) {
- item->total_usage--;
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) hashkey, item->total_usage);
-
- switch_core_hash_delete(pvt->hash, hashkey);
-
- if (item->total_usage == 0 && item->rate_usage == 0) {
- /* Noone is using this item anymore */
- switch_core_hash_delete(globals.limit_hash, (const char *) hashkey);
- free(item);
- }
- }
-
- switch_mutex_unlock(globals.limit_hash_mutex);
-}
-
-#define LIMITHASH_USAGE "<realm> <id> [<max>[/interval]] [number [dialplan [context]]]"
-#define LIMITHASH_DESC "limit access to a resource and transfer to an extension if the limit is exceeded"
-SWITCH_STANDARD_APP(limit_hash_function)
-{
- int argc = 0;
- char *argv[6] = { 0 };
- char *mydata = NULL;
- char *realm = NULL;
- char *id = NULL;
- char *xfer_exten = NULL;
- int max = -1;
- int interval = 0;
- switch_channel_t *channel = switch_core_session_get_channel(session);
-
- /* Parse application data */
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 2) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit_hash %s\n", LIMITHASH_USAGE);
- return;
- }
-
- realm = argv[0];
- id = argv[1];
-
- /* If max is omitted or negative, only act as a counter and skip maximum checks */
- if (argc > 2) {
- if (argv[2][0] == '-') {
- max = -1;
- } else {
- char *szinterval = NULL;
- if ((szinterval = strchr(argv[2], '/'))) {
- *szinterval++ = '\0';
- interval = atoi(szinterval);
- }
-
- max = atoi(argv[2]);
-
- if (max < 0) {
- max = 0;
- }
- }
- }
-
- if (argc > 3) {
- xfer_exten = argv[3];
- } else {
- xfer_exten = limit_def_xfer_exten;
- }
-
- if (!do_limit_hash(session, realm, id, max, interval)) {
- /* Limit exceeded */
- if (*xfer_exten == '!') {
- switch_channel_hangup(channel, switch_channel_str2cause(xfer_exten + 1));
- } else {
- switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]);
- }
- }
-}
-
-
-#define LIMITHASHEXECUTE_USAGE "<realm> <id> [<max>[/interval]] [application] [application arguments]"
-#define LIMITHASHEXECUTE_DESC "limit access to a resource. the specified application will only be executed if the resource is available"
-SWITCH_STANDARD_APP(limit_hash_execute_function)
-{
- int argc = 0;
- char *argv[5] = { 0 };
- char *mydata = NULL;
- char *realm = NULL;
- char *id = NULL;
- char *app = NULL;
- char *app_arg = NULL;
- int max = -1;
- int interval = 0;
-
- /* Parse application data */
- if (!zstr(data)) {
- mydata = switch_core_session_strdup(session, data);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 5) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit_hash_execute %s\n", LIMITHASHEXECUTE_USAGE);
- return;
- }
-
- realm = argv[0];
- id = argv[1];
-
- /* Accept '-' as unlimited (act as counter) */
- if (argv[2][0] == '-') {
- max = -1;
- } else {
- char *szinterval = NULL;
-
- if ((szinterval = strchr(argv[2], '/'))) {
- *szinterval++ = '\0';
- interval = atoi(szinterval);
- }
-
- max = atoi(argv[2]);
-
- if (max < 0) {
- max = 0;
- }
- }
-
- app = argv[3];
- app_arg = argv[4];
-
- if (zstr(app)) {
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing application\n");
- return;
- }
-
- if (do_limit_hash(session, realm, id, max, interval)) {
- switch_core_session_execute_application(session, app, app_arg);
- /* Only release the resource if we are still in CS_EXECUTE */
- if (switch_channel_get_state(switch_core_session_get_channel(session)) == CS_EXECUTE) {
- limit_hash_release(session, realm, id);
- }
- }
-}
-
-
-#define LIMIT_HASH_USAGE_USAGE "<realm> <id> [rate]"
-SWITCH_STANDARD_API(limit_hash_usage_function)
-{
- int argc = 0;
- char *argv[4] = { 0 };
- char *mydata = NULL;
- char *hash_key = NULL;
- limit_hash_item_t *item = NULL;
- uint32_t count = 0, rcount = 0;
- switch_bool_t dorate = SWITCH_FALSE;
-
- switch_mutex_lock(globals.limit_hash_mutex);
-
- if (!zstr(cmd)) {
- mydata = strdup(cmd);
- switch_assert(mydata);
- argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
- }
-
- if (argc < 2) {
- stream->write_function(stream, "USAGE: limit_hash_usage %s\n", LIMIT_HASH_USAGE_USAGE);
- goto end;
- }
-
- if (argc > 2) {
- if (!strcasecmp(argv[2], "rate")) {
- dorate = SWITCH_TRUE;
- }
- }
-
- hash_key = switch_mprintf("%s_%s", argv[0], argv[1]);
-
- if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) {
- count = item->total_usage;
- rcount = item->rate_usage;
- }
-
- if (dorate == SWITCH_TRUE) {
- stream->write_function(stream, "%d/%d", count, rcount);
- } else {
- stream->write_function(stream, "%d", count);
- }
-
- end:
- switch_safe_free(mydata);
- switch_safe_free(hash_key);
- switch_mutex_unlock(globals.limit_hash_mutex);
-
- return SWITCH_STATUS_SUCCESS;
-}
-
-SWITCH_MODULE_LOAD_FUNCTION(mod_limit_load)
-{
- switch_status_t status;
- switch_application_interface_t *app_interface;
- switch_api_interface_t *commands_api_interface;
-
- memset(&globals, 0, sizeof(&globals));
- gethostname(globals.hostname, sizeof(globals.hostname));
- globals.pool = pool;
-
-
- if (switch_event_reserve_subclass(LIMIT_EVENT_USAGE) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldnt register event subclass \"%s\"", LIMIT_EVENT_USAGE);
- return SWITCH_STATUS_FALSE;
- }
-
- if ((status = do_config() != SWITCH_STATUS_SUCCESS)) {
- return status;
- }
-
- switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
- switch_mutex_init(&globals.limit_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
- switch_mutex_init(&globals.db_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
- switch_core_hash_init(&globals.limit_hash, pool);
- switch_core_hash_init(&globals.db_hash, pool);
-
- /* connect my internal structure to the blank pointer passed to me */
- *module_interface = switch_loadable_module_create_module_interface(pool, modname);
-
- switch_scheduler_add_task(switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_limit", 0, NULL,
- SSHF_NONE);
-
- SWITCH_ADD_APP(app_interface, "limit", "Limit", LIMIT_DESC, limit_function, LIMIT_USAGE, SAF_SUPPORT_NOMEDIA);
- SWITCH_ADD_APP(app_interface, "limit_execute", "Limit", LIMITEXECUTE_USAGE, limit_execute_function, LIMITEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA);
- SWITCH_ADD_APP(app_interface, "limit_hash", "Limit (hash)", LIMITHASH_DESC, limit_hash_function, LIMITHASH_USAGE, SAF_SUPPORT_NOMEDIA);
- SWITCH_ADD_APP(app_interface, "limit_hash_execute", "Limit (hash)", LIMITHASHEXECUTE_USAGE, limit_hash_execute_function, LIMITHASHEXECUTE_USAGE,
- SAF_SUPPORT_NOMEDIA);
- SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA);
- SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA)
- SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA);
-
- SWITCH_ADD_API(commands_api_interface, "limit_hash_usage", "Gets the usage count of a limited resource", limit_hash_usage_function,
- LIMIT_HASH_USAGE_USAGE);
- SWITCH_ADD_API(commands_api_interface, "limit_usage", "Gets the usage count of a limited resource", limit_usage_function, "<realm> <id>");
- SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
- switch_console_set_complete("add db insert");
- switch_console_set_complete("add db delete");
- switch_console_set_complete("add db select");
- SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
- switch_console_set_complete("add hash insert");
- switch_console_set_complete("add hash delete");
- switch_console_set_complete("add hash select");
- SWITCH_ADD_API(commands_api_interface, "group", "group [insert|delete|call]", group_api_function, "[insert|delete|call]:<group name>:<url>");
- switch_console_set_complete("add group insert");
- switch_console_set_complete("add group delete");
- switch_console_set_complete("add group call");
-
- /* indicate that the module should continue to be loaded */
- return SWITCH_STATUS_SUCCESS;
-}
-
-
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_limit_shutdown)
-{
- switch_scheduler_del_task_group("mod_limit");
- switch_event_free_subclass(LIMIT_EVENT_USAGE);
-
- switch_xml_config_cleanup(config_settings);
-
- switch_mutex_destroy(globals.mutex);
- switch_mutex_destroy(globals.limit_hash_mutex);
- switch_mutex_destroy(globals.db_hash_mutex);
-
- switch_core_hash_destroy(&globals.limit_hash);
- switch_core_hash_destroy(&globals.db_hash);
-
- return SWITCH_STATUS_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:
- */
+++ /dev/null
-<?xml version="1.0" encoding="Windows-1252"?>\r
-<VisualStudioProject\r
- ProjectType="Visual C++"\r
- Version="8.00"\r
- Name="mod_limit"\r
- ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"\r
- RootNamespace="mod_limit"\r
- Keyword="Win32Proj"\r
- >\r
- <Platforms>\r
- <Platform\r
- Name="Win32"\r
- />\r
- </Platforms>\r
- <ToolFiles>\r
- </ToolFiles>\r
- <Configurations>\r
- <Configuration\r
- Name="Debug|Win32"\r
- ConfigurationType="2"\r
- InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
- CharacterSet="2"\r
- >\r
- <Tool\r
- Name="VCPreBuildEventTool"\r
- />\r
- <Tool\r
- Name="VCCustomBuildTool"\r
- />\r
- <Tool\r
- Name="VCXMLDataGeneratorTool"\r
- />\r
- <Tool\r
- Name="VCWebServiceProxyGeneratorTool"\r
- />\r
- <Tool\r
- Name="VCMIDLTool"\r
- />\r
- <Tool\r
- Name="VCCLCompilerTool"\r
- UsePrecompiledHeader="0"\r
- />\r
- <Tool\r
- Name="VCManagedResourceCompilerTool"\r
- />\r
- <Tool\r
- Name="VCResourceCompilerTool"\r
- />\r
- <Tool\r
- Name="VCPreLinkEventTool"\r
- />\r
- <Tool\r
- Name="VCLinkerTool"\r
- />\r
- <Tool\r
- Name="VCALinkTool"\r
- />\r
- <Tool\r
- Name="VCManifestTool"\r
- />\r
- <Tool\r
- Name="VCXDCMakeTool"\r
- />\r
- <Tool\r
- Name="VCBscMakeTool"\r
- />\r
- <Tool\r
- Name="VCFxCopTool"\r
- />\r
- <Tool\r
- Name="VCAppVerifierTool"\r
- />\r
- <Tool\r
- Name="VCWebDeploymentTool"\r
- />\r
- <Tool\r
- Name="VCPostBuildEventTool"\r
- />\r
- </Configuration>\r
- <Configuration\r
- Name="Release|Win32"\r
- ConfigurationType="2"\r
- InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
- CharacterSet="2"\r
- >\r
- <Tool\r
- Name="VCPreBuildEventTool"\r
- />\r
- <Tool\r
- Name="VCCustomBuildTool"\r
- />\r
- <Tool\r
- Name="VCXMLDataGeneratorTool"\r
- />\r
- <Tool\r
- Name="VCWebServiceProxyGeneratorTool"\r
- />\r
- <Tool\r
- Name="VCMIDLTool"\r
- />\r
- <Tool\r
- Name="VCCLCompilerTool"\r
- UsePrecompiledHeader="0"\r
- />\r
- <Tool\r
- Name="VCManagedResourceCompilerTool"\r
- />\r
- <Tool\r
- Name="VCResourceCompilerTool"\r
- />\r
- <Tool\r
- Name="VCPreLinkEventTool"\r
- />\r
- <Tool\r
- Name="VCLinkerTool"\r
- />\r
- <Tool\r
- Name="VCALinkTool"\r
- />\r
- <Tool\r
- Name="VCManifestTool"\r
- />\r
- <Tool\r
- Name="VCXDCMakeTool"\r
- />\r
- <Tool\r
- Name="VCBscMakeTool"\r
- />\r
- <Tool\r
- Name="VCFxCopTool"\r
- />\r
- <Tool\r
- Name="VCAppVerifierTool"\r
- />\r
- <Tool\r
- Name="VCWebDeploymentTool"\r
- />\r
- <Tool\r
- Name="VCPostBuildEventTool"\r
- />\r
- </Configuration>\r
- </Configurations>\r
- <References>\r
- </References>\r
- <Files>\r
- <File\r
- RelativePath=".\mod_limit.c"\r
- >\r
- </File>\r
- </Files>\r
- <Globals>\r
- </Globals>\r
-</VisualStudioProject>\r
}
}
+ switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "ent_originate_aleg_uuid", switch_core_session_get_uuid(session));
+
/* A comma (,) separated list of variable names that should ne propagated from originator to originatee */
if (channel && (export_vars = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE))) {
char *cptmp = switch_core_session_strdup(session, export_vars);
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Rupa Schomaker <rupa@rupa.com>
+ *
+ * switch_limit.c Limit support
+ *
+ */
+
+#include <switch.h>
+
+static switch_limit_interface_t *get_backend(const char *backend) {
+ switch_limit_interface_t *limit = NULL;
+
+ if (!backend) {
+ return NULL;
+ }
+
+ if (!(limit = switch_loadable_module_get_limit_interface(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate limit backend: %s\n", backend);
+ }
+
+ return limit;
+}
+
+static void release_backend(switch_limit_interface_t *limit) {
+ if (limit) {
+ UNPROTECT_INTERFACE(limit);
+ }
+}
+
+SWITCH_DECLARE(void) switch_limit_init(switch_memory_pool_t *pool) {
+ if (switch_event_reserve_subclass(LIMIT_EVENT_USAGE) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldnt register event subclass \"%s\"", LIMIT_EVENT_USAGE);
+ }
+}
+
+
+SWITCH_DECLARE(void) switch_limit_fire_event(const char *backend, const char *realm, const char *key, uint32_t usage, uint32_t rate, uint32_t max, uint32_t ratemax)
+{
+ switch_event_t *event;
+
+ if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, LIMIT_EVENT_USAGE) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "backend", backend);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "realm", realm);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", key);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "usage", "%d", usage);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rate", "%d", rate);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "max", "%d", max);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "ratemax", "%d", ratemax);
+ switch_event_fire(&event);
+ }
+}
+
+static switch_status_t limit_state_handler(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_channel_state_t state = switch_channel_get_state(channel);
+ const char *vval = switch_channel_get_variable(channel, LIMIT_IGNORE_TRANSFER_VARIABLE);
+ const char *backendlist = switch_channel_get_variable(channel, LIMIT_BACKEND_VARIABLE);
+ int argc = 0;
+ char *argv[6] = { 0 };
+ char *mydata = NULL;
+
+ if (zstr(backendlist)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unset limit backendlist!\n");
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ mydata = strdup(backendlist);
+
+ if (state >= CS_HANGUP || (state == CS_ROUTING && !switch_true(vval))) {
+ int x;
+ argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0])));
+ for (x = 0; x < argc; x++) {
+ switch_limit_release(argv[x], session, NULL, NULL);
+ }
+ switch_core_event_hook_remove_state_change(session, limit_state_handler);
+ /* Remove limit_realm variable so we register another hook if limit is called again */
+ switch_channel_set_variable(channel, "limit_realm", NULL);
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_limit_incr(const char *backend, switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval) {
+ switch_limit_interface_t *limit = NULL;
+ switch_channel_t *channel = NULL;
+ int status = SWITCH_STATUS_SUCCESS;
+
+ assert(session);
+
+ channel = switch_core_session_get_channel(session);
+
+ /* locate impl, call appropriate func */
+ if (!(limit = get_backend(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
+ switch_goto_status(SWITCH_STATUS_GENERR, end);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "incr called: %s_%s max:%d, interval:%d\n",
+ realm, resource, max, interval);
+
+ if ((status = limit->incr(session, realm, resource, max, interval)) == SWITCH_STATUS_SUCCESS) {
+ /* race condition? what if another leg is doing the same thing? */
+ const char *existing = switch_channel_get_variable(channel, LIMIT_BACKEND_VARIABLE);
+ if (existing) {
+ if (!strstr(existing, backend)) {
+ switch_channel_set_variable_printf(channel, LIMIT_BACKEND_VARIABLE, "%s,%s", existing, backend);
+ }
+ } else {
+ switch_channel_set_variable(channel, LIMIT_BACKEND_VARIABLE, backend);
+ switch_core_event_hook_add_state_change(session, limit_state_handler);
+ }
+ }
+
+ release_backend(limit);
+
+end:
+ return status;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_limit_release(const char *backend, switch_core_session_t *session, const char *realm, const char *resource) {
+ switch_limit_interface_t *limit = NULL;
+ int status = SWITCH_STATUS_SUCCESS;
+
+ /* locate impl, call appropriate func */
+ if (!(limit = get_backend(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
+ switch_goto_status(SWITCH_STATUS_GENERR, end);
+ }
+
+ status = limit->release(session, realm, resource);
+
+end:
+ release_backend(limit);
+ return status;
+}
+
+SWITCH_DECLARE(int) switch_limit_usage(const char *backend, const char *realm, const char *resource, uint32_t *rcount) {
+ switch_limit_interface_t *limit = NULL;
+ int usage = 0;
+
+ /* locate impl, call appropriate func */
+ if (!(limit = get_backend(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
+ goto end;
+ }
+
+ usage = limit->usage(realm, resource, rcount);
+
+end:
+ release_backend(limit);
+ return usage;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_limit_reset(const char *backend) {
+ switch_limit_interface_t *limit = NULL;
+ int status = SWITCH_STATUS_SUCCESS;
+
+ /* locate impl, call appropriate func */
+ if (!(limit = get_backend(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
+ switch_goto_status(SWITCH_STATUS_GENERR, end);
+ }
+
+ status = limit->reset();
+
+end:
+ release_backend(limit);
+ return status;
+}
+
+SWITCH_DECLARE(char *) switch_limit_status(const char *backend) {
+ switch_limit_interface_t *limit = NULL;
+ char *status = NULL;
+
+ /* locate impl, call appropriate func */
+ if (!(limit = get_backend(backend))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
+ switch_goto_status("-ERR", end);
+ }
+
+ status = limit->status();
+
+end:
+ release_backend(limit);
+ return status;
+}
+
+/* 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:
+ */
switch_hash_t *chat_hash;
switch_hash_t *say_hash;
switch_hash_t *management_hash;
+ switch_hash_t *limit_hash;
switch_mutex_t *mutex;
switch_memory_pool_t *pool;
};
}
}
}
+ if (new_module->module_interface->limit_interface) {
+ const switch_limit_interface_t *ptr;
+
+ for (ptr = new_module->module_interface->limit_interface; ptr; ptr = ptr->next) {
+ if (!ptr->interface_name) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load limit interface from %s due to no interface name.\n", key);
+ } else {
+ if (switch_core_hash_find(loadable_modules.limit_hash, ptr->interface_name)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
+ "Failed to load limit interface %s. Name %s already exists\n", key, ptr->interface_name);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
+ "Adding Limit interface '%s'\n", ptr->interface_name);
+ switch_core_hash_insert(loadable_modules.limit_hash, ptr->interface_name, (const void *) ptr);
+ if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
+ switch_event_fire(&event);
+ }
+ }
+
+ }
+ }
+ }
switch_mutex_unlock(loadable_modules.mutex);
return SWITCH_STATUS_SUCCESS;
}
}
+ if (old_module->module_interface->limit_interface) {
+ const switch_limit_interface_t *ptr;
+
+ for (ptr = old_module->module_interface->limit_interface; ptr; ptr = ptr->next) {
+ if (ptr->interface_name) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
+ "Deleting Limit interface '%s'\n", ptr->interface_name);
+ switch_core_hash_delete(loadable_modules.limit_hash, ptr->interface_name);
+ if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
+ switch_event_fire(&event);
+ }
+ }
+ }
+ }
+
switch_mutex_unlock(loadable_modules.mutex);
return SWITCH_STATUS_SUCCESS;
switch_core_hash_init_nocase(&loadable_modules.chat_hash, loadable_modules.pool);
switch_core_hash_init_nocase(&loadable_modules.say_hash, loadable_modules.pool);
switch_core_hash_init_nocase(&loadable_modules.management_hash, loadable_modules.pool);
+ switch_core_hash_init_nocase(&loadable_modules.limit_hash, loadable_modules.pool);
switch_core_hash_init_nocase(&loadable_modules.dialplan_hash, loadable_modules.pool);
switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, loadable_modules.pool);
switch_core_hash_destroy(&loadable_modules.chat_hash);
switch_core_hash_destroy(&loadable_modules.say_hash);
switch_core_hash_destroy(&loadable_modules.management_hash);
+ switch_core_hash_destroy(&loadable_modules.limit_hash);
switch_core_hash_destroy(&loadable_modules.dialplan_hash);
switch_core_destroy_memory_pool(&loadable_modules.pool);
}
HASH_FUNC(dialplan)
- HASH_FUNC(timer)
- HASH_FUNC(application)
- HASH_FUNC(api)
- HASH_FUNC(file)
- HASH_FUNC(speech)
- HASH_FUNC(asr)
- HASH_FUNC(directory)
- HASH_FUNC(chat)
-
-
- SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(const char *name)
+HASH_FUNC(timer)
+HASH_FUNC(application)
+HASH_FUNC(api)
+HASH_FUNC(file)
+HASH_FUNC(speech)
+HASH_FUNC(asr)
+HASH_FUNC(directory)
+HASH_FUNC(chat)
+HASH_FUNC(limit)
+
+
+SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(const char *name)
{
return switch_core_hash_find_locked(loadable_modules.say_hash, name, loadable_modules.mutex);
}
case SWITCH_MANAGEMENT_INTERFACE:
ALLOC_INTERFACE(management)
+ case SWITCH_LIMIT_INTERFACE:
+ ALLOC_INTERFACE(limit)
+
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n");
return NULL;