Revision
16915
Author
rupa
Date
2010-03-05 10:51:03 -0600 (Fri, 05 Mar 2010)

Log Message

mod_limit -> switch_limit*,mod_hash,mod_db
branch: rupa_limit

Modified Paths

Added Paths

Removed Paths

Diff

Modified: freeswitch/branches/rupa_limit/Makefile.am (16914 => 16915)


--- freeswitch/branches/rupa_limit/Makefile.am	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/Makefile.am	2010-03-05 16:51:03 UTC (rev 16915)
@@ -171,6 +171,7 @@
 	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 = \
@@ -225,6 +226,7 @@
 	src/switch_config.c \
 	src/switch_time.c \
 	src/switch_odbc.c \
+	src/switch_limit.c \
 	src/g711.c \
 	src/switch_pcm.c \
 	libs/stfu/stfu.c \

Modified: freeswitch/branches/rupa_limit/build/modules.conf.in (16914 => 16915)


--- freeswitch/branches/rupa_limit/build/modules.conf.in	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/build/modules.conf.in	2010-03-05 16:51:03 UTC (rev 16915)
@@ -9,10 +9,11 @@
 applications/mod_fifo
 #applications/mod_fax
 #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

Modified: freeswitch/branches/rupa_limit/src/include/switch.h (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/include/switch.h	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch.h	2010-03-05 16:51:03 UTC (rev 16915)
@@ -131,6 +131,7 @@
 #include "switch_config.h"
 #include "switch_nat.h"
 #include "switch_odbc.h"
+#include "switch_limit.h"
 
 #include <libteletone.h>
 

Modified: freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h	2010-03-05 16:51:03 UTC (rev 16915)
@@ -81,6 +81,8 @@
 	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;
@@ -205,6 +207,13 @@
 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
   \param arraylen the max size in elements of the array
@@ -315,6 +324,18 @@
 	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) \

Modified: freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h	2010-03-05 16:51:03 UTC (rev 16915)
@@ -512,6 +512,28 @@
 	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);
+	/*! 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 */

Modified: freeswitch/branches/rupa_limit/src/include/switch_types.h (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/include/switch_types.h	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_types.h	2010-03-05 16:51:03 UTC (rev 16915)
@@ -264,7 +264,8 @@
 	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 {
@@ -1503,6 +1504,7 @@
 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;

Modified: freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -3201,7 +3201,7 @@
 	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];
@@ -3259,6 +3259,7 @@
 			   !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';
@@ -4048,6 +4049,95 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
+/* LIMIT Stuff */
+#define LIMIT_USAGE_USAGE "<backend> <realm> <id>"
+SWITCH_STANDARD_API(limit_usage_function)
+{
+	int argc = 0;
+	char *argv[5] = { 0 };
+	char *mydata = NULL;
+	uint32_t count = 0;
+
+	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;
+	}
+	
+	count = switch_limit_usage(argv[0], argv[1], argv[2]);
+
+	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;
@@ -4108,6 +4198,9 @@
 	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, "md5", "md5", md5_function, "<data>");
 	SWITCH_ADD_API(commands_api_interface, "module_exists", "check if module exists", module_exists_function, "<module>");

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,2 @@
+BASE=../../../..
+include $(BASE)/build/modmake.rules

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mod_db"
+	ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
+	RootNamespace="mod_db"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="131072"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath=".\mod_db.c"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,657 @@
+/* 
+ * 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);
+
+	/* 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:
+ */

Modified: freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -2977,6 +2977,140 @@
 	}
 }
 
+/* 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[2], '/'))) {
+				*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"
@@ -3144,6 +3278,8 @@
 				   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);
 

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,2 @@
+BASE=../../../..
+include $(BASE)/build/modmake.rules

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mod_hash"
+	ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
+	RootNamespace="mod_hash"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="131072"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			ConfigurationType="2"
+			InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				UsePrecompiledHeader="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath=".\mod_hash.c"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c (0 => 16915)


--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c	                        (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,457 @@
+/* 
+ * 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>
+
+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_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;
+} 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_mutex_lock(globals.limit_hash_mutex);
+	/* 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 */
+		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 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) {
+		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_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_mutex_unlock(globals.limit_hash_mutex);
+	return status;
+}
+
+/* !\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_mutex_lock(globals.limit_hash_mutex);
+
+	/* 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) {
+				/* 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) {
+				/* 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);
+	
+	return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_LIMIT_USAGE(limit_usage_hash)
+{
+	char *hash_key = NULL;
+	limit_hash_item_t *item = NULL;
+	int count = 0;
+
+	switch_mutex_lock(globals.limit_hash_mutex);
+
+	hash_key = switch_mprintf("%s_%s", realm, resource);
+
+	if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) {
+		count = item->total_usage;
+	}
+
+ 	switch_safe_free(hash_key);
+	switch_mutex_unlock(globals.limit_hash_mutex);
+
+	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_mutex_lock(globals.limit_hash_mutex);
+	
+	while ((hi = switch_hash_first(NULL, globals.limit_hash))) {
+		count++;
+	}
+	switch_mutex_unlock(globals.limit_hash_mutex);
+	
+	ret = switch_mprintf("There are %d elements being tracked.", count);
+	return ret;
+}
+
+/* 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_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;
+}
+
+/* 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;
+
+	memset(&globals, 0, sizeof(&globals));
+	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;
+	}
+
+	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);
+
+	/* 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_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_mutex_destroy(globals.mutex);
+	switch_mutex_destroy(globals.limit_hash_mutex);
+
+	switch_core_hash_destroy(&globals.limit_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:
+ */

Modified: freeswitch/branches/rupa_limit/src/switch_core.c (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/switch_core.c	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/switch_core.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -1290,7 +1290,6 @@
 	runtime.timer_affinity = -1;
 	switch_load_core_config("switch.conf");
 
-
 	switch_core_state_machine_init(runtime.memory_pool);
 
 	if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
@@ -1301,6 +1300,8 @@
 
 	switch_rtp_init(runtime.memory_pool);
 
+	switch_nat_init(runtime.memory_pool);
+
 	runtime.running = 1;
 	runtime.initiated = switch_time_now();
 	

Modified: freeswitch/branches/rupa_limit/src/switch_loadable_module.c (16914 => 16915)


--- freeswitch/branches/rupa_limit/src/switch_loadable_module.c	2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/switch_loadable_module.c	2010-03-05 16:51:03 UTC (rev 16915)
@@ -71,6 +71,7 @@
 	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,7 +434,33 @@
 			}
 		}
 	}
+	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 @@
 		}
 	}
 
+	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_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);
 
@@ -1347,6 +1392,7 @@
 	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);
 
 }
@@ -1403,17 +1449,18 @@
 	}
 
 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)
+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)
+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);
 }
@@ -1683,6 +1730,9 @@
 	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;