]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
sync changes in branch rupa_limit
authorRupa Schomaker <rupa@rupa.com>
Fri, 2 Apr 2010 02:31:14 +0000 (21:31 -0500)
committerRupa Schomaker <rupa@rupa.com>
Sat, 19 Jun 2010 12:49:34 +0000 (07:49 -0500)
19 files changed:
Makefile.am
build/modules.conf.in
src/include/switch.h
src/include/switch_limit.h [new file with mode: 0644]
src/include/switch_loadable_module.h
src/include/switch_module_interfaces.h
src/include/switch_types.h
src/mod/applications/mod_commands/mod_commands.c
src/mod/applications/mod_db/mod_db.2008.vcproj [moved from src/mod/applications/mod_limit/mod_limit.2008.vcproj with 93% similarity]
src/mod/applications/mod_db/mod_db.c [new file with mode: 0644]
src/mod/applications/mod_dptools/mod_dptools.c
src/mod/applications/mod_hash/mod_hash.2008.vcproj [new file with mode: 0644]
src/mod/applications/mod_hash/mod_hash.c [new file with mode: 0644]
src/mod/applications/mod_lcr/mod_lcr.c [changed mode: 0755->0644]
src/mod/applications/mod_limit/mod_limit.c [deleted file]
src/mod/applications/mod_limit/mod_limit.vcproj [deleted file]
src/switch_ivr_originate.c
src/switch_limit.c [new file with mode: 0755]
src/switch_loadable_module.c

index a760bb721b9951c5a217d86ea310173cb9a15bca..2ef6175cacce1dc3876165fecfe389fb289fa85c 100644 (file)
@@ -173,6 +173,7 @@ library_include_HEADERS = \
        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 = \
@@ -228,6 +229,7 @@ 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\
index 5a914bbc7d09f9d1e463f84860796c7983717913..8cb6d2a333a81b839d5d0d7ac455a280744c646c 100644 (file)
@@ -9,10 +9,11 @@ applications/mod_enum
 #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
index 275e51fdac551fbaad427c746f8cc58d02df43ac..7143c61d9192670c298d606f06903a05ee5c4e39 100644 (file)
 #include "switch_nat.h"
 #include "switch_odbc.h"
 #include "switch_json.h"
+#include "switch_limit.h"
 
 #include <libteletone.h>
 
diff --git a/src/include/switch_limit.h b/src/include/switch_limit.h
new file mode 100644 (file)
index 0000000..c63ac7f
--- /dev/null
@@ -0,0 +1,127 @@
+/* 
+ * 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:
+ */
index 7b9457ea2b0a7b0ce22268e96fdba3e0474c9597..946bd97f8650b7be52cb8e88cc4e6ccc5e5338f3 100644 (file)
@@ -81,6 +81,8 @@ SWITCH_BEGIN_EXTERN_C
        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;
@@ -204,6 +206,13 @@ SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interfac
  */
 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
@@ -315,6 +324,18 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void);
        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) \
index 642d1639f9bce4e40d4256749f3d5f828ba05aef..64131378de3a47a9fb9a3d70c5acbc307608ba5f 100644 (file)
@@ -512,6 +512,28 @@ struct switch_management_interface {
        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 */
index 74d5920b24d8b6c678b9fadfbbe9218a3289d610..e65b3b78ce9bc6b1075d4cc6522acbac60480626 100644 (file)
@@ -267,7 +267,8 @@ typedef enum {
        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 {
@@ -1593,6 +1594,7 @@ typedef struct switch_chat_interface switch_chat_interface_t;
 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;
index 00f2ec6eabb1bd0e784919066090277e6084ddeb..ce2fe0c72b231c78c079604ac38665a5c9993490 100644 (file)
@@ -3358,7 +3358,7 @@ SWITCH_STANDARD_API(alias_function)
        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];
@@ -3421,6 +3421,7 @@ SWITCH_STANDARD_API(show_function)
                           !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';
@@ -4222,6 +4223,107 @@ SWITCH_STANDARD_API(sql_escape)
        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;
@@ -4310,6 +4412,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        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>");
similarity index 93%
rename from src/mod/applications/mod_limit/mod_limit.2008.vcproj
rename to src/mod/applications/mod_db/mod_db.2008.vcproj
index 3273279e1be4b40eee1f0f967173b14333808b55..ae523ea1241b1f54ab1053beb1ecd3da428304e7 100644 (file)
@@ -2,9 +2,9 @@
 <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
diff --git a/src/mod/applications/mod_db/mod_db.c b/src/mod/applications/mod_db/mod_db.c
new file mode 100644 (file)
index 0000000..c5dfc11
--- /dev/null
@@ -0,0 +1,663 @@
+/* 
+ * 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:
+ */
index 187fa446ab1daf0f1e57b7aaced6df65668314ff..70b58f508c2e9259f8db8592eb07103e8ed06bfe 100755 (executable)
@@ -3007,6 +3007,140 @@ SWITCH_STANDARD_APP(session_loglevel_function)
        }
 }
 
+/* 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"
@@ -3176,6 +3310,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
                                   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);
 
diff --git a/src/mod/applications/mod_hash/mod_hash.2008.vcproj b/src/mod/applications/mod_hash/mod_hash.2008.vcproj
new file mode 100644 (file)
index 0000000..32da23d
--- /dev/null
@@ -0,0 +1,283 @@
+<?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
diff --git a/src/mod/applications/mod_hash/mod_hash.c b/src/mod/applications/mod_hash/mod_hash.c
new file mode 100644 (file)
index 0000000..6035b0b
--- /dev/null
@@ -0,0 +1,506 @@
+/* 
+ * 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:
+ */
old mode 100755 (executable)
new mode 100644 (file)
index aa16426..07bb67c
@@ -126,7 +126,6 @@ struct profile_obj {
        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;
 
@@ -295,18 +294,9 @@ static char *get_bridge_data(switch_memory_pool_t *pool, char *dialed_number, ch
                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;
@@ -415,7 +405,7 @@ static switch_bool_t db_check(char *sql)
        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;
                }
        }
@@ -545,7 +535,8 @@ static switch_bool_t lcr_execute_sql_callback(char *sql, switch_core_db_callback
        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;
@@ -912,7 +903,6 @@ static switch_status_t lcr_load_config()
                        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;
@@ -965,8 +955,6 @@ static switch_status_t lcr_load_config()
                                        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;
                                }
                        }
 
@@ -1057,10 +1045,6 @@ static switch_status_t lcr_load_config()
                                        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);
                                }
@@ -1268,11 +1252,7 @@ SWITCH_STANDARD_APP(lcr_app_function)
                                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);
                                }
@@ -1519,7 +1499,6 @@ SWITCH_STANDARD_API(dialplan_lcr_admin_function)
                                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");
                        }
diff --git a/src/mod/applications/mod_limit/mod_limit.c b/src/mod/applications/mod_limit/mod_limit.c
deleted file mode 100644 (file)
index b32cb12..0000000
+++ /dev/null
@@ -1,1350 +0,0 @@
-/* 
- * 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:
- */
diff --git a/src/mod/applications/mod_limit/mod_limit.vcproj b/src/mod/applications/mod_limit/mod_limit.vcproj
deleted file mode 100644 (file)
index a15f5aa..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-<?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
index 11e5bc5d019b726ed17135d8aec1b5c33d976165..c89013eb84ee6a9e6a377545bf1264104e0d2273 100644 (file)
@@ -1392,6 +1392,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_enterprise_originate(switch_core_sess
                }
        }
 
+       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);
diff --git a/src/switch_limit.c b/src/switch_limit.c
new file mode 100755 (executable)
index 0000000..55648ee
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * 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:
+ */
index 90f49ae58ea08c319c94402f0086c7ea7024ec23..97cb5e8d701bac925844655f97ae5f49c33d9c80 100644 (file)
@@ -71,6 +71,7 @@ struct switch_loadable_module_container {
        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;
 };
@@ -433,6 +434,32 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
                        }
                }
        }
+       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;
@@ -768,6 +795,23 @@ static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t
                }
        }
 
+       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;
@@ -1164,6 +1208,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init()
        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);
 
@@ -1354,6 +1399,7 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
        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);
@@ -1411,17 +1457,18 @@ SWITCH_DECLARE(switch_codec_interface_t *) switch_loadable_module_get_codec_inte
        }
 
 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);
 }
@@ -1691,6 +1738,9 @@ SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_m
        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;