Move code from res_features into (new file) main/features.c

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@100039 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.0
Jason Parker 18 years ago
parent ef690e01eb
commit 3bd33214b9

@ -409,6 +409,8 @@ Call Features (res_features) Changes
* Updated the ParkedCall application to allow you to not specify a parking
extension. If you don't specify a parking space to pick up, it will grab
the first one available.
* Added cli command 'features reload' to reload call features from features.conf
* Moved into core asterisk binary.
Language Support Changes
------------------------
@ -498,4 +500,3 @@ Miscellaneous
specifying which socket to use to connect to the running Asterisk daemon
(-s)
* Added logging to 'make update' command. See update.log

@ -32,6 +32,7 @@ void ast_event_init(void); /*!< Provided by event.c */
int ast_device_state_engine_init(void); /*!< Provided by devicestate.c */
int astobj2_init(void); /*!< Provided by astobj2.c */
int ast_file_init(void); /*!< Provided by file.c */
int ast_features_init(void); /*!< Provided by features.c */
/*!
* \brief Reload asterisk modules.

@ -109,4 +109,7 @@ struct ast_call_feature *ast_find_call_feature(const char *name);
void ast_rdlock_call_features(void);
void ast_unlock_call_features(void);
/*! \brief Reload call features from features.conf */
int ast_features_reload(void);
#endif /* _AST_FEATURES_H */

@ -29,7 +29,8 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o
astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o \
features.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static

@ -96,6 +96,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/network.h"
#include "asterisk/cli.h"
#include "asterisk/channel.h"
#include "asterisk/features.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
@ -3165,6 +3166,8 @@ int main(int argc, char *argv[])
exit(1);
}
ast_features_init();
if (init_framer()) {
printf(term_quit());
exit(1);

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2008, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -31,6 +31,8 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
#include <pthread.h>
#include <sys/time.h>
#include <sys/signal.h>
@ -126,7 +128,7 @@ static unsigned int atxferdropcall;
static unsigned int atxferloopdelay;
static unsigned int atxfercallbackretries;
static char *registrar = "res_features"; /*!< Registrar for operations */
static char *registrar = "features"; /*!< Registrar for operations */
/* module and CLI command definitions */
static char *synopsis = "Answer a parked call";
@ -599,9 +601,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
struct ast_channel *parker;
struct ast_channel *parkee;
int res = 0;
struct ast_module_user *u;
u = ast_module_user_add(chan);
set_peers(&parker, &parkee, peer, chan, sense);
/* Setup the exten/priority to be s/1 since we don't know
@ -615,8 +614,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
if (!res)
res = ast_park_call(parkee, parker, 0, NULL);
ast_module_user_remove(u);
if (!res) {
if (sense == FEATURE_SENSE_CHAN)
res = AST_PBX_NO_HANGUP_PEER;
@ -675,7 +672,7 @@ static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *pee
if (callee_chan->monitor) {
ast_verb(4, "User hit '%s' to stop recording call.\n", code);
ast_monitor_stop(callee_chan, 1);
callee_chan->monitor->stop(callee_chan, 1);
return FEATURE_RETURN_SUCCESS;
}
@ -2512,72 +2509,406 @@ static int park_exec(struct ast_channel *chan, void *data)
}
/*!
* \brief CLI command to list configured features
* \param e
* \param cmd
* \param a
*
* \retval CLI_SUCCESS on success.
* \retval NULL when tab completion is used.
*/
static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
* \brief Add parking hints for all defined parking lots
* \param context
* \param start starting parkinglot number
* \param stop ending parkinglot number
*/
static void park_add_hints(char *context, int start, int stop)
{
int numext;
char device[AST_MAX_EXTENSION];
char exten[10];
for (numext = start; numext <= stop; numext++) {
snprintf(exten, sizeof(exten), "%d", numext);
snprintf(device, sizeof(device), "park:%s@%s", exten, context);
ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
}
}
static int load_config(void)
{
int start = 0, end = 0;
int res;
int i;
struct ast_call_feature *feature;
char format[] = "%-25s %-7s %-7s\n";
struct ast_context *con = NULL;
struct ast_config *cfg = NULL;
struct ast_variable *var = NULL;
struct feature_group *fg = NULL;
struct ast_flags config_flags = { 0 };
char old_parking_ext[AST_MAX_EXTENSION];
char old_parking_con[AST_MAX_EXTENSION] = "";
char *ctg;
static const char *categories[] = {
/* Categories in features.conf that are not
* to be parsed as group categories
*/
"general",
"featuremap",
"applicationmap"
};
switch (cmd) {
if (!ast_strlen_zero(parking_con)) {
strcpy(old_parking_ext, parking_ext);
strcpy(old_parking_con, parking_con);
}
case CLI_INIT:
e->command = "features show";
e->usage =
"Usage: features show\n"
" Lists configured features\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
/* Reset to defaults */
strcpy(parking_con, "parkedcalls");
strcpy(parking_con_dial, "park-dial");
strcpy(parking_ext, "700");
strcpy(pickup_ext, "*8");
strcpy(parkmohclass, "default");
courtesytone[0] = '\0';
strcpy(xfersound, "beep");
strcpy(xferfailsound, "pbx-invalid");
parking_start = 701;
parking_stop = 750;
parkfindnext = 0;
adsipark = 0;
comebacktoorigin = 1;
parkaddhints = 0;
parkedcalltransfers = 0;
parkedcallreparking = 0;
ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
cfg = ast_config_load("features.conf", config_flags);
if (!cfg) {
ast_log(LOG_WARNING,"Could not load features.conf\n");
return 0;
}
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
if (!strcasecmp(var->name, "parkext")) {
ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
} else if (!strcasecmp(var->name, "context")) {
ast_copy_string(parking_con, var->value, sizeof(parking_con));
} else if (!strcasecmp(var->name, "parkingtime")) {
if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
parkingtime = DEFAULT_PARK_TIME;
} else
parkingtime = parkingtime * 1000;
} else if (!strcasecmp(var->name, "parkpos")) {
if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
} else {
parking_start = start;
parking_stop = end;
}
} else if (!strcasecmp(var->name, "findslot")) {
parkfindnext = (!strcasecmp(var->value, "next"));
} else if (!strcasecmp(var->name, "parkinghints")) {
parkaddhints = ast_true(var->value);
} else if (!strcasecmp(var->name, "parkedcalltransfers")) {
if (!strcasecmp(var->value, "both"))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
else if (!strcasecmp(var->value, "caller"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "parkedcallreparking")) {
if (!strcasecmp(var->value, "both"))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
else if (!strcasecmp(var->value, "caller"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "adsipark")) {
adsipark = ast_true(var->value);
} else if (!strcasecmp(var->name, "transferdigittimeout")) {
if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
} else
transferdigittimeout = transferdigittimeout * 1000;
} else if (!strcasecmp(var->name, "featuredigittimeout")) {
if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
}
} else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
} else
atxfernoanswertimeout = atxfernoanswertimeout * 1000;
} else if (!strcasecmp(var->name, "atxferloopdelay")) {
if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
} else
atxferloopdelay *= 1000;
} else if (!strcasecmp(var->name, "atxferdropcall")) {
atxferdropcall = ast_true(var->value);
} else if (!strcasecmp(var->name, "atxfercallbackretries")) {
if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
}
} else if (!strcasecmp(var->name, "courtesytone")) {
ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
} else if (!strcasecmp(var->name, "parkedplay")) {
if (!strcasecmp(var->value, "both"))
parkedplay = 2;
else if (!strcasecmp(var->value, "parked"))
parkedplay = 1;
else
parkedplay = 0;
} else if (!strcasecmp(var->name, "xfersound")) {
ast_copy_string(xfersound, var->value, sizeof(xfersound));
} else if (!strcasecmp(var->name, "xferfailsound")) {
ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
} else if (!strcasecmp(var->name, "pickupexten")) {
ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
} else if (!strcasecmp(var->name, "comebacktoorigin")) {
comebacktoorigin = ast_true(var->value);
} else if (!strcasecmp(var->name, "parkedmusicclass")) {
ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
}
}
ast_rwlock_rdlock(&features_lock);
for (i = 0; i < FEATURES_COUNT; i++)
ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
ast_rwlock_unlock(&features_lock);
unmap_features();
for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
if (remap_feature(var->name, var->value))
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
}
/* Map a key combination to an application*/
ast_unregister_features();
for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
char *tmp_val = ast_strdupa(var->value);
char *exten, *activateon, *activatedby, *app, *app_args, *moh_class;
struct ast_call_feature *feature;
/* strsep() sets the argument to NULL if match not found, and it
* is safe to use it with a NULL argument, so we don't check
* between calls.
*/
exten = strsep(&tmp_val,",");
activatedby = strsep(&tmp_val,",");
app = strsep(&tmp_val,",");
app_args = strsep(&tmp_val,",");
moh_class = strsep(&tmp_val,",");
activateon = strsep(&activatedby, "/");
/*! \todo XXX var_name or app_args ? */
if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
app, exten, activateon, var->name);
continue;
}
ast_cli(a->fd, "\n");
ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
if (AST_LIST_EMPTY(&feature_list))
ast_cli(a->fd, "(none)\n");
else {
AST_LIST_LOCK(&feature_list);
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
if ((feature = find_dynamic_feature(var->name))) {
AST_LIST_UNLOCK(&feature_list);
ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
continue;
}
AST_LIST_UNLOCK(&feature_list);
}
ast_cli(a->fd, "\nCall parking\n");
ast_cli(a->fd, "------------\n");
ast_cli(a->fd,"%-20s: %s\n", "Parking extension", parking_ext);
ast_cli(a->fd,"%-20s: %s\n", "Parking context", parking_con);
ast_cli(a->fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
ast_cli(a->fd,"\n");
return CLI_SUCCESS;
}
if (!(feature = ast_calloc(1, sizeof(*feature))))
continue;
static char mandescr_bridge[] =
"Description: Bridge together two channels already in the PBX\n"
"Variables: ( Headers marked with * are required )\n"
" *Channel1: Channel to Bridge to Channel2\n"
" *Channel2: Channel to Bridge to Channel1\n"
" Tone: (Yes|No) Play courtesy tone to Channel 2\n"
"\n";
ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
ast_copy_string(feature->app, app, FEATURE_APP_LEN);
ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
/*!
* \brief Actual bridge
if (app_args)
ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
if (moh_class)
ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
ast_copy_string(feature->exten, exten, sizeof(feature->exten));
feature->operation = feature_exec_app;
ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
/* Allow caller and calle to be specified for backwards compatability */
if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
else {
ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
" must be 'self', or 'peer'\n", var->name);
continue;
}
if (ast_strlen_zero(activatedby))
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
else if (!strcasecmp(activatedby, "caller"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
else if (!strcasecmp(activatedby, "callee"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
else if (!strcasecmp(activatedby, "both"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
else {
ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
" must be 'caller', or 'callee', or 'both'\n", var->name);
continue;
}
ast_register_feature(feature);
ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
}
ast_unregister_groups();
AST_RWLIST_WRLOCK(&feature_groups);
ctg = NULL;
while ((ctg = ast_category_browse(cfg, ctg))) {
for (i = 0; i < ARRAY_LEN(categories); i++) {
if (!strcasecmp(categories[i], ctg))
break;
}
if (i < ARRAY_LEN(categories))
continue;
if (!(fg = register_group(ctg)))
continue;
for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
struct ast_call_feature *feature;
AST_LIST_LOCK(&feature_list);
if(!(feature = find_dynamic_feature(var->name)) &&
!(feature = ast_find_call_feature(var->name))) {
AST_LIST_UNLOCK(&feature_list);
ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
continue;
}
AST_LIST_UNLOCK(&feature_list);
register_group_feature(fg, var->value, feature);
}
}
AST_RWLIST_UNLOCK(&feature_groups);
ast_config_destroy(cfg);
/* Remove the old parking extension */
if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
notify_metermaids(old_parking_ext, old_parking_con, AST_DEVICE_NOT_INUSE);
ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
}
if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
return -1;
}
res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
if (parkaddhints)
park_add_hints(parking_con, parking_start, parking_stop);
if (!res)
notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
return res;
}
/*!
* \brief CLI command to list configured features
* \param e
* \param cmd
* \param a
*
* \retval CLI_SUCCESS on success.
* \retval NULL when tab completion is used.
*/
static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int i;
struct ast_call_feature *feature;
char format[] = "%-25s %-7s %-7s\n";
switch (cmd) {
case CLI_INIT:
e->command = "features show";
e->usage =
"Usage: features show\n"
" Lists configured features\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
ast_rwlock_rdlock(&features_lock);
for (i = 0; i < FEATURES_COUNT; i++)
ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
ast_rwlock_unlock(&features_lock);
ast_cli(a->fd, "\n");
ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
if (AST_LIST_EMPTY(&feature_list))
ast_cli(a->fd, "(none)\n");
else {
AST_LIST_LOCK(&feature_list);
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
AST_LIST_UNLOCK(&feature_list);
}
ast_cli(a->fd, "\nCall parking\n");
ast_cli(a->fd, "------------\n");
ast_cli(a->fd,"%-20s: %s\n", "Parking extension", parking_ext);
ast_cli(a->fd,"%-20s: %s\n", "Parking context", parking_con);
ast_cli(a->fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
ast_cli(a->fd,"\n");
return CLI_SUCCESS;
}
int ast_features_reload(void)
{
load_config();
return RESULT_SUCCESS;
}
static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "features reload";
e->usage =
"Usage: features reload\n"
" Reloads configured call features from features.conf\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
load_config();
return CLI_SUCCESS;
}
static char mandescr_bridge[] =
"Description: Bridge together two channels already in the PBX\n"
"Variables: ( Headers marked with * are required )\n"
" *Channel1: Channel to Bridge to Channel2\n"
" *Channel2: Channel to Bridge to Channel1\n"
" Tone: (Yes|No) Play courtesy tone to Channel 2\n"
"\n";
/*!
* \brief Actual bridge
* \param chan
* \param tmpchan
*
@ -2769,6 +3100,7 @@ static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(han
static struct ast_cli_entry cli_features[] = {
AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
AST_CLI_DEFINE(handle_features_reload, "Reloads configured features"),
AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
};
@ -2930,316 +3262,6 @@ int ast_pickup_call(struct ast_channel *chan)
return res;
}
/*!
* \brief Add parking hints for all defined parking lots
* \param context
* \param start starting parkinglot number
* \param stop ending parkinglot number
*/
static void park_add_hints(char *context, int start, int stop)
{
int numext;
char device[AST_MAX_EXTENSION];
char exten[10];
for (numext = start; numext <= stop; numext++) {
snprintf(exten, sizeof(exten), "%d", numext);
snprintf(device, sizeof(device), "park:%s@%s", exten, context);
ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
}
}
static int load_config(void)
{
int start = 0, end = 0;
int res;
int i;
struct ast_context *con = NULL;
struct ast_config *cfg = NULL;
struct ast_variable *var = NULL;
struct feature_group *fg = NULL;
struct ast_flags config_flags = { 0 };
char old_parking_ext[AST_MAX_EXTENSION];
char old_parking_con[AST_MAX_EXTENSION] = "";
char *ctg;
static const char *categories[] = {
/* Categories in features.conf that are not
* to be parsed as group categories
*/
"general",
"featuremap",
"applicationmap"
};
if (!ast_strlen_zero(parking_con)) {
strcpy(old_parking_ext, parking_ext);
strcpy(old_parking_con, parking_con);
}
/* Reset to defaults */
strcpy(parking_con, "parkedcalls");
strcpy(parking_con_dial, "park-dial");
strcpy(parking_ext, "700");
strcpy(pickup_ext, "*8");
strcpy(parkmohclass, "default");
courtesytone[0] = '\0';
strcpy(xfersound, "beep");
strcpy(xferfailsound, "pbx-invalid");
parking_start = 701;
parking_stop = 750;
parkfindnext = 0;
adsipark = 0;
comebacktoorigin = 1;
parkaddhints = 0;
parkedcalltransfers = 0;
parkedcallreparking = 0;
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
cfg = ast_config_load("features.conf", config_flags);
if (!cfg) {
ast_log(LOG_WARNING,"Could not load features.conf\n");
return AST_MODULE_LOAD_DECLINE;
}
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
if (!strcasecmp(var->name, "parkext")) {
ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
} else if (!strcasecmp(var->name, "context")) {
ast_copy_string(parking_con, var->value, sizeof(parking_con));
} else if (!strcasecmp(var->name, "parkingtime")) {
if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
parkingtime = DEFAULT_PARK_TIME;
} else
parkingtime = parkingtime * 1000;
} else if (!strcasecmp(var->name, "parkpos")) {
if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
} else {
parking_start = start;
parking_stop = end;
}
} else if (!strcasecmp(var->name, "findslot")) {
parkfindnext = (!strcasecmp(var->value, "next"));
} else if (!strcasecmp(var->name, "parkinghints")) {
parkaddhints = ast_true(var->value);
} else if (!strcasecmp(var->name, "parkedcalltransfers")) {
if (!strcasecmp(var->value, "both"))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
else if (!strcasecmp(var->value, "caller"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "parkedcallreparking")) {
if (!strcasecmp(var->value, "both"))
parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
else if (!strcasecmp(var->value, "caller"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "adsipark")) {
adsipark = ast_true(var->value);
} else if (!strcasecmp(var->name, "transferdigittimeout")) {
if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
} else
transferdigittimeout = transferdigittimeout * 1000;
} else if (!strcasecmp(var->name, "featuredigittimeout")) {
if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
}
} else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
} else
atxfernoanswertimeout = atxfernoanswertimeout * 1000;
} else if (!strcasecmp(var->name, "atxferloopdelay")) {
if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
} else
atxferloopdelay *= 1000;
} else if (!strcasecmp(var->name, "atxferdropcall")) {
atxferdropcall = ast_true(var->value);
} else if (!strcasecmp(var->name, "atxfercallbackretries")) {
if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
}
} else if (!strcasecmp(var->name, "courtesytone")) {
ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
} else if (!strcasecmp(var->name, "parkedplay")) {
if (!strcasecmp(var->value, "both"))
parkedplay = 2;
else if (!strcasecmp(var->value, "parked"))
parkedplay = 1;
else
parkedplay = 0;
} else if (!strcasecmp(var->name, "xfersound")) {
ast_copy_string(xfersound, var->value, sizeof(xfersound));
} else if (!strcasecmp(var->name, "xferfailsound")) {
ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
} else if (!strcasecmp(var->name, "pickupexten")) {
ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
} else if (!strcasecmp(var->name, "comebacktoorigin")) {
comebacktoorigin = ast_true(var->value);
} else if (!strcasecmp(var->name, "parkedmusicclass")) {
ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
}
}
unmap_features();
for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
if (remap_feature(var->name, var->value))
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
}
/* Map a key combination to an application*/
ast_unregister_features();
for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
char *tmp_val = ast_strdupa(var->value);
char *exten, *activateon, *activatedby, *app, *app_args, *moh_class;
struct ast_call_feature *feature;
/* strsep() sets the argument to NULL if match not found, and it
* is safe to use it with a NULL argument, so we don't check
* between calls.
*/
exten = strsep(&tmp_val,",");
activatedby = strsep(&tmp_val,",");
app = strsep(&tmp_val,",");
app_args = strsep(&tmp_val,",");
moh_class = strsep(&tmp_val,",");
activateon = strsep(&activatedby, "/");
/*! \todo XXX var_name or app_args ? */
if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
app, exten, activateon, var->name);
continue;
}
AST_LIST_LOCK(&feature_list);
if ((feature = find_dynamic_feature(var->name))) {
AST_LIST_UNLOCK(&feature_list);
ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
continue;
}
AST_LIST_UNLOCK(&feature_list);
if (!(feature = ast_calloc(1, sizeof(*feature))))
continue;
ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
ast_copy_string(feature->app, app, FEATURE_APP_LEN);
ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
if (app_args)
ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
if (moh_class)
ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
ast_copy_string(feature->exten, exten, sizeof(feature->exten));
feature->operation = feature_exec_app;
ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
/* Allow caller and calle to be specified for backwards compatability */
if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
else {
ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
" must be 'self', or 'peer'\n", var->name);
continue;
}
if (ast_strlen_zero(activatedby))
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
else if (!strcasecmp(activatedby, "caller"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
else if (!strcasecmp(activatedby, "callee"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
else if (!strcasecmp(activatedby, "both"))
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
else {
ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
" must be 'caller', or 'callee', or 'both'\n", var->name);
continue;
}
ast_register_feature(feature);
ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
}
ast_unregister_groups();
AST_RWLIST_WRLOCK(&feature_groups);
ctg = NULL;
while ((ctg = ast_category_browse(cfg, ctg))) {
for (i = 0; i < ARRAY_LEN(categories); i++) {
if (!strcasecmp(categories[i], ctg))
break;
}
if (i < ARRAY_LEN(categories))
continue;
if (!(fg = register_group(ctg)))
continue;
for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
struct ast_call_feature *feature;
AST_LIST_LOCK(&feature_list);
if(!(feature = find_dynamic_feature(var->name)) &&
!(feature = ast_find_call_feature(var->name))) {
AST_LIST_UNLOCK(&feature_list);
ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
continue;
}
AST_LIST_UNLOCK(&feature_list);
register_group_feature(fg, var->value, feature);
}
}
AST_RWLIST_UNLOCK(&feature_groups);
ast_config_destroy(cfg);
/* Remove the old parking extension */
if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
notify_metermaids(old_parking_ext, old_parking_con, AST_DEVICE_NOT_INUSE);
ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
}
if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
return -1;
}
res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
if (parkaddhints)
park_add_hints(parking_con, parking_start, parking_stop);
if (!res)
notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
return res;
}
static char *app_bridge = "Bridge";
static char *bridge_synopsis = "Bridge two channels";
static char *bridge_descrip =
@ -3388,16 +3410,11 @@ static int bridge_exec(struct ast_channel *chan, void *data)
return 0;
}
static int reload(void)
{
return load_config();
}
static int load_module(void)
int ast_features_init(void)
{
int res;
ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip);
ast_register_application2(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip, NULL);
memset(parking_ext, 0, sizeof(parking_ext));
memset(parking_con, 0, sizeof(parking_con));
@ -3406,9 +3423,9 @@ static int load_module(void)
return res;
ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
res = ast_register_application2(parkedcall, park_exec, synopsis, descrip, NULL);
if (!res)
res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
res = ast_register_application2(parkcall, park_call_exec, synopsis2, descrip2, NULL);
if (!res) {
ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls");
ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
@ -3420,29 +3437,3 @@ static int load_module(void)
return res;
}
static int unload_module(void)
{
struct ast_context *con;
ast_manager_unregister("ParkedCalls");
ast_manager_unregister("Bridge");
ast_manager_unregister("Park");
ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
ast_unregister_application(parkcall);
ast_unregister_application(app_bridge);
ast_devstate_prov_del("Park");
con = ast_context_find(parking_con);
if (con)
ast_context_destroy(con, registrar);
con = ast_context_find(parking_con_dial);
if (con)
ast_context_destroy(con, registrar);
return ast_unregister_application(parkedcall);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/rtp.h"
#include "asterisk/http.h"
#include "asterisk/lock.h"
#include "asterisk/features.h"
#ifdef DLFCNCOMPAT
#include "asterisk/dlfcn-compat.h"
@ -247,6 +248,7 @@ static struct reload_classes {
{ "rtp", ast_rtp_reload },
{ "http", ast_http_reload },
{ "logger", logger_reload },
{ "features", ast_features_reload },
{ NULL, NULL }
};

Loading…
Cancel
Save