sync changes in branch rupa_limit

This commit is contained in:
Rupa Schomaker 2010-04-01 21:31:14 -05:00
parent f6eda2c23b
commit 0e8a26f840
19 changed files with 2165 additions and 1546 deletions

View File

@ -173,6 +173,7 @@ library_include_HEADERS = \
libs/libteletone/src/libteletone_detect.h \ libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \ libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \ libs/libteletone/src/libteletone.h \
src/include/switch_limit.h \
src/include/switch_odbc.h src/include/switch_odbc.h
nodist_libfreeswitch_la_SOURCES = \ nodist_libfreeswitch_la_SOURCES = \
@ -228,6 +229,7 @@ libfreeswitch_la_SOURCES = \
src/switch_config.c \ src/switch_config.c \
src/switch_time.c \ src/switch_time.c \
src/switch_odbc.c \ src/switch_odbc.c \
src/switch_limit.c \
src/g711.c \ src/g711.c \
src/switch_pcm.c \ src/switch_pcm.c \
src/switch_profile.c\ src/switch_profile.c\

View File

@ -9,10 +9,11 @@ applications/mod_enum
#applications/mod_osp #applications/mod_osp
applications/mod_fifo applications/mod_fifo
#applications/mod_curl #applications/mod_curl
applications/mod_db
applications/mod_hash
applications/mod_voicemail applications/mod_voicemail
#applications/mod_directory #applications/mod_directory
#applications/mod_lcr #applications/mod_lcr
applications/mod_limit
applications/mod_expr applications/mod_expr
applications/mod_esf applications/mod_esf
#applications/mod_easyroute #applications/mod_easyroute

View File

@ -133,6 +133,7 @@
#include "switch_nat.h" #include "switch_nat.h"
#include "switch_odbc.h" #include "switch_odbc.h"
#include "switch_json.h" #include "switch_json.h"
#include "switch_limit.h"
#include <libteletone.h> #include <libteletone.h>

127
src/include/switch_limit.h Normal file
View File

@ -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:
*/

View File

@ -81,6 +81,8 @@ SWITCH_BEGIN_EXTERN_C
switch_asr_interface_t *asr_interface; switch_asr_interface_t *asr_interface;
/*! the table of management interfaces the module has implmented */ /*! the table of management interfaces the module has implmented */
switch_management_interface_t *management_interface; 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; switch_thread_rwlock_t *rwlock;
int refs; int refs;
switch_memory_pool_t *pool; 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); 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 \brief Retrieve the list of loaded codecs into an array
\param array the array to populate \param array the array to populate
@ -315,6 +324,18 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void);
break; \ 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); SWITCH_DECLARE(uint32_t) switch_core_codec_next_id(void);
#define SWITCH_ADD_CODEC(codec_int, int_name) \ #define SWITCH_ADD_CODEC(codec_int, int_name) \

View File

@ -512,6 +512,28 @@ struct switch_management_interface {
struct switch_management_interface *next; 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 */ /*! \brief Abstract interface to a directory module */
struct switch_directory_interface { struct switch_directory_interface {
/*! the name of the interface */ /*! the name of the interface */

View File

@ -267,7 +267,8 @@ typedef enum {
SWITCH_CHAT_INTERFACE, SWITCH_CHAT_INTERFACE,
SWITCH_SAY_INTERFACE, SWITCH_SAY_INTERFACE,
SWITCH_ASR_INTERFACE, SWITCH_ASR_INTERFACE,
SWITCH_MANAGEMENT_INTERFACE SWITCH_MANAGEMENT_INTERFACE,
SWITCH_LIMIT_INTERFACE
} switch_module_interface_name_t; } switch_module_interface_name_t;
typedef enum { 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_management_interface switch_management_interface_t;
typedef struct switch_core_port_allocator switch_core_port_allocator_t; typedef struct switch_core_port_allocator switch_core_port_allocator_t;
typedef struct switch_media_bug switch_media_bug_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 { struct switch_console_callback_match_node {
char *val; char *val;

View File

@ -3358,7 +3358,7 @@ SWITCH_STANDARD_API(alias_function)
return SWITCH_STATUS_SUCCESS; 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) SWITCH_STANDARD_API(show_function)
{ {
char sql[1024]; char sql[1024];
@ -3421,6 +3421,7 @@ SWITCH_STANDARD_API(show_function)
!strncasecmp(command, "file", 4) || !strncasecmp(command, "file", 4) ||
!strncasecmp(command, "timer", 5) || !strncasecmp(command, "timer", 5) ||
!strncasecmp(command, "chat", 4) || !strncasecmp(command, "chat", 4) ||
!strncasecmp(command, "limit", 5) ||
!strncasecmp(command, "say", 3) || !strncasecmp(command, "management", 10) || !strncasecmp(command, "endpoint", 8)) { !strncasecmp(command, "say", 3) || !strncasecmp(command, "management", 10) || !strncasecmp(command, "endpoint", 8)) {
if (end_of(command) == 's') { if (end_of(command) == 's') {
end_of(command) = '\0'; end_of(command) = '\0';
@ -4222,6 +4223,107 @@ SWITCH_STANDARD_API(sql_escape)
return SWITCH_STATUS_SUCCESS; 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) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_commands_shutdown)
{ {
int x; 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, "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, "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, "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, "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, "log", "Log", log_function, LOG_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "md5", "md5", md5_function, "<data>"); SWITCH_ADD_API(commands_api_interface, "md5", "md5", md5_function, "<data>");

View File

@ -2,9 +2,9 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="mod_limit" Name="mod_db"
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}" ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
RootNamespace="mod_limit" RootNamespace="mod_db"
Keyword="Win32Proj" Keyword="Win32Proj"
TargetFrameworkVersion="131072" TargetFrameworkVersion="131072"
> >
@ -274,7 +274,7 @@
</References> </References>
<Files> <Files>
<File <File
RelativePath=".\mod_limit.c" RelativePath=".\mod_db.c"
> >
</File> </File>
</Files> </Files>

View File

@ -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:
*/

View File

@ -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 SPEAK_DESC "Speak text to a channel via the tts interface"
#define DISPLACE_DESC "Displace audio from a file to the channels input" #define DISPLACE_DESC "Displace audio from a file to the channels input"
#define SESS_REC_DESC "Starts a background recording of the entire session" #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); SAF_NONE);
SWITCH_ADD_APP(app_interface, "session_loglevel", "session_loglevel", "session_loglevel", session_loglevel_function, SESSION_LOGLEVEL_SYNTAX, SWITCH_ADD_APP(app_interface, "session_loglevel", "session_loglevel", "session_loglevel", session_loglevel_function, SESSION_LOGLEVEL_SYNTAX,
SAF_SUPPORT_NOMEDIA); 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); SWITCH_ADD_DIALPLAN(dp_interface, "inline", inline_dialplan_hunt);

View File

@ -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>

View File

@ -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:
*/

35
src/mod/applications/mod_lcr/mod_lcr.c Executable file → Normal file
View File

@ -126,7 +126,6 @@ struct profile_obj {
switch_bool_t reorder_by_rate; switch_bool_t reorder_by_rate;
switch_bool_t quote_in_list; switch_bool_t quote_in_list;
switch_bool_t info_in_headers; switch_bool_t info_in_headers;
switch_bool_t enable_sip_redir;
}; };
typedef struct profile_obj profile_t; 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); user_rate = switch_core_sprintf(pool, ",lcr_user_rate=%s", cur_route->user_rate_str);
} }
if (profile->enable_sip_redir) { 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,
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); header, 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);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Returning Dialstring %s\n", data); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Returning Dialstring %s\n", data);
return data; return data;
@ -415,7 +405,7 @@ static switch_bool_t db_check(char *sql)
switch_cache_db_handle_t *dbh = NULL; switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) { 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; 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; switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) { 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; retval = SWITCH_FALSE;
} else { } else {
retval = SWITCH_TRUE; retval = SWITCH_TRUE;
@ -912,7 +903,6 @@ static switch_status_t lcr_load_config()
char *reorder_by_rate = NULL; char *reorder_by_rate = NULL;
char *quote_in_list = NULL; char *quote_in_list = NULL;
char *info_in_headers = NULL; char *info_in_headers = NULL;
char *enable_sip_redir = NULL;
char *id_s = NULL; char *id_s = NULL;
char *custom_sql = NULL; char *custom_sql = NULL;
int argc, x = 0; int argc, x = 0;
@ -965,8 +955,6 @@ static switch_status_t lcr_load_config()
info_in_headers = val; info_in_headers = val;
} else if (!strcasecmp(var, "quote_in_list") && !zstr(val)) { } else if (!strcasecmp(var, "quote_in_list") && !zstr(val)) {
quote_in_list = 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); 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)) { if (!zstr(quote_in_list)) {
profile->quote_in_list = switch_true(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); switch_channel_set_variable(channel, vbuf, cur_route->codec);
cnt++; cnt++;
if (cur_route->next) { if (cur_route->next) {
if (routes.profile->enable_sip_redir) { 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);
}
} else { } else {
dig_stream.write_function(&dig_stream, "%s", cur_route->dialstring); 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, " 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, " 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, " 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, " Quote IN() List:\t%s\n", profile->quote_in_list ? "enabled" : "disabled");
stream->write_function(stream, "\n"); stream->write_function(stream, "\n");
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="mod_limit"
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
RootNamespace="mod_limit"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</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"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<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"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=".\mod_limit.c"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View 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 */ /* 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))) { if (channel && (export_vars = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE))) {
char *cptmp = switch_core_session_strdup(session, export_vars); char *cptmp = switch_core_session_strdup(session, export_vars);

222
src/switch_limit.c Executable file
View File

@ -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:
*/

View File

@ -71,6 +71,7 @@ struct switch_loadable_module_container {
switch_hash_t *chat_hash; switch_hash_t *chat_hash;
switch_hash_t *say_hash; switch_hash_t *say_hash;
switch_hash_t *management_hash; switch_hash_t *management_hash;
switch_hash_t *limit_hash;
switch_mutex_t *mutex; switch_mutex_t *mutex;
switch_memory_pool_t *pool; 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); switch_mutex_unlock(loadable_modules.mutex);
return SWITCH_STATUS_SUCCESS; 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); switch_mutex_unlock(loadable_modules.mutex);
return SWITCH_STATUS_SUCCESS; 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.chat_hash, loadable_modules.pool);
switch_core_hash_init_nocase(&loadable_modules.say_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.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_core_hash_init_nocase(&loadable_modules.dialplan_hash, loadable_modules.pool);
switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, 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.chat_hash);
switch_core_hash_destroy(&loadable_modules.say_hash); switch_core_hash_destroy(&loadable_modules.say_hash);
switch_core_hash_destroy(&loadable_modules.management_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_hash_destroy(&loadable_modules.dialplan_hash);
switch_core_destroy_memory_pool(&loadable_modules.pool); 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(dialplan)
HASH_FUNC(timer) HASH_FUNC(timer)
HASH_FUNC(application) HASH_FUNC(application)
HASH_FUNC(api) HASH_FUNC(api)
HASH_FUNC(file) HASH_FUNC(file)
HASH_FUNC(speech) HASH_FUNC(speech)
HASH_FUNC(asr) HASH_FUNC(asr)
HASH_FUNC(directory) HASH_FUNC(directory)
HASH_FUNC(chat) 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); 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: case SWITCH_MANAGEMENT_INTERFACE:
ALLOC_INTERFACE(management) ALLOC_INTERFACE(management)
case SWITCH_LIMIT_INTERFACE:
ALLOC_INTERFACE(limit)
default: default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n");
return NULL; return NULL;