mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-10 06:49:40 +00:00
add ability to map feature sequences to applications (issue #3764)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6374 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -25,3 +25,8 @@ context => parkedcalls ; Which context parked calls are in
|
|||||||
;disconnect => *0 ; Disconnect
|
;disconnect => *0 ; Disconnect
|
||||||
;automon => *1 ; One Touch Record
|
;automon => *1 ; One Touch Record
|
||||||
;atxfer => *2 ; Attended transfer
|
;atxfer => *2 ; Attended transfer
|
||||||
|
|
||||||
|
[applicationmap]
|
||||||
|
;testfeature => #9,callee,Playback,tt-monkeys ;Play tt-monkes to
|
||||||
|
;callee if #9 was pressed
|
||||||
|
|
||||||
|
@@ -373,6 +373,8 @@ struct ast_channel {
|
|||||||
#define AST_FEATURE_AUTOMON (1 << 4)
|
#define AST_FEATURE_AUTOMON (1 << 4)
|
||||||
|
|
||||||
#define AST_FEATURE_FLAG_NEEDSDTMF (1 << 0)
|
#define AST_FEATURE_FLAG_NEEDSDTMF (1 << 0)
|
||||||
|
#define AST_FEATURE_FLAG_CALLEE (1 << 1)
|
||||||
|
#define AST_FEATURE_FLAG_CALLER (1 << 2)
|
||||||
|
|
||||||
struct ast_bridge_config {
|
struct ast_bridge_config {
|
||||||
struct ast_flags features_caller;
|
struct ast_flags features_caller;
|
||||||
|
@@ -17,6 +17,28 @@
|
|||||||
#ifndef _AST_FEATURES_H
|
#ifndef _AST_FEATURES_H
|
||||||
#define _AST_FEATURES_H
|
#define _AST_FEATURES_H
|
||||||
|
|
||||||
|
#define FEATURE_MAX_LEN 11
|
||||||
|
#define FEATURE_APP_LEN 64
|
||||||
|
#define FEATURE_APP_ARGS_LEN 256
|
||||||
|
#define FEATURE_SNAME_LEN 32
|
||||||
|
#define FEATURE_EXTEN_LEN 32
|
||||||
|
|
||||||
|
/* main call feature structure */
|
||||||
|
struct ast_call_feature {
|
||||||
|
int feature_mask;
|
||||||
|
char *fname;
|
||||||
|
char sname[FEATURE_SNAME_LEN];
|
||||||
|
char exten[FEATURE_MAX_LEN];
|
||||||
|
char default_exten[FEATURE_MAX_LEN];
|
||||||
|
int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
|
||||||
|
unsigned int flags;
|
||||||
|
char app[FEATURE_APP_LEN];
|
||||||
|
char app_args[FEATURE_APP_ARGS_LEN];
|
||||||
|
AST_LIST_ENTRY(ast_call_feature) feature_entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! Park a call and read back parked location */
|
/*! Park a call and read back parked location */
|
||||||
/*! \param chan the channel to actually be parked
|
/*! \param chan the channel to actually be parked
|
||||||
\param host the channel which will have the parked location read to
|
\param host the channel which will have the parked location read to
|
||||||
@@ -50,5 +72,15 @@ extern int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,st
|
|||||||
|
|
||||||
extern int ast_pickup_call(struct ast_channel *chan);
|
extern int ast_pickup_call(struct ast_channel *chan);
|
||||||
|
|
||||||
|
/*! register new feature into feature_set
|
||||||
|
\param feature an ast_call_feature object which contains a keysequence
|
||||||
|
and a callback function which is called when this keysequence is pressed
|
||||||
|
during a call. */
|
||||||
|
extern void ast_register_feature(struct ast_call_feature *feature);
|
||||||
|
|
||||||
|
/*! unregister feature from feature_set
|
||||||
|
\param feature the ast_call_feature object which was registered before*/
|
||||||
|
extern void ast_unregister_feature(struct ast_call_feature *feature);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _AST_FEATURES_H */
|
#endif /* _AST_FEATURES_H */
|
||||||
|
@@ -429,7 +429,7 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int
|
|||||||
|
|
||||||
#define FEATURE_SENSE_CHAN (1 << 0)
|
#define FEATURE_SENSE_CHAN (1 << 0)
|
||||||
#define FEATURE_SENSE_PEER (1 << 1)
|
#define FEATURE_SENSE_PEER (1 << 1)
|
||||||
#define FEATURE_MAX_LEN 11
|
|
||||||
|
|
||||||
static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
||||||
{
|
{
|
||||||
@@ -841,15 +841,6 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
|
|||||||
return FEATURE_RETURN_SUCCESS;
|
return FEATURE_RETURN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ast_call_feature {
|
|
||||||
int feature_mask;
|
|
||||||
char *fname;
|
|
||||||
char *sname;
|
|
||||||
char exten[FEATURE_MAX_LEN];
|
|
||||||
char default_exten[FEATURE_MAX_LEN];
|
|
||||||
int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
|
|
||||||
unsigned int flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* add atxfer and automon as undefined so you can only use em if you configure them */
|
/* add atxfer and automon as undefined so you can only use em if you configure them */
|
||||||
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
|
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
|
||||||
@@ -861,6 +852,83 @@ struct ast_call_feature builtin_features[] =
|
|||||||
{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
|
{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static AST_LIST_HEAD(feature_list,ast_call_feature) feature_list;
|
||||||
|
|
||||||
|
/* register new feature into feature_list*/
|
||||||
|
void ast_register_feature(struct ast_call_feature *feature)
|
||||||
|
{
|
||||||
|
if (!feature) {
|
||||||
|
ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
|
||||||
|
if (option_verbose >= 2)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unregister feature from feature_list */
|
||||||
|
void ast_unregister_feature(struct ast_call_feature *feature)
|
||||||
|
{
|
||||||
|
if (!feature) return;
|
||||||
|
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_REMOVE(&feature_list,feature,feature_entry);
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
free(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* find a feature by name */
|
||||||
|
static struct ast_call_feature *find_feature(char *name)
|
||||||
|
{
|
||||||
|
struct ast_call_feature *tmp;
|
||||||
|
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_TRAVERSE(&feature_list,tmp,feature_entry) {
|
||||||
|
if (!strcasecmp(tmp->sname,name)) break;
|
||||||
|
}
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exec an app by feature */
|
||||||
|
static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
||||||
|
{
|
||||||
|
struct ast_app *app;
|
||||||
|
struct ast_call_feature *feature;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_TRAVERSE(&feature_list,feature,feature_entry) {
|
||||||
|
if (!strcasecmp(feature->exten,code)) break;
|
||||||
|
}
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
|
||||||
|
if (!feature) { /* shouldn't ever happen! */
|
||||||
|
ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
app = pbx_findapp(feature->app);
|
||||||
|
if (app) {
|
||||||
|
struct ast_channel *work=chan;
|
||||||
|
if (ast_test_flag(feature,AST_FEATURE_FLAG_CALLEE)) work=peer;
|
||||||
|
res = pbx_exec(work, app, feature->app_args, 1);
|
||||||
|
if (res<0) return res;
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
|
||||||
|
res = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FEATURE_RETURN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static void unmap_features(void)
|
static void unmap_features(void)
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
@@ -889,6 +957,8 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
|
|||||||
int x;
|
int x;
|
||||||
struct ast_flags features;
|
struct ast_flags features;
|
||||||
int res = FEATURE_RETURN_PASSDIGITS;
|
int res = FEATURE_RETURN_PASSDIGITS;
|
||||||
|
struct ast_call_feature *feature;
|
||||||
|
char *dynamic_features=pbx_builtin_getvar_helper(chan,"DYNAMIC_FEATURES");
|
||||||
|
|
||||||
if (sense == FEATURE_SENSE_CHAN)
|
if (sense == FEATURE_SENSE_CHAN)
|
||||||
ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
|
ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
|
||||||
@@ -908,6 +978,42 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (dynamic_features) {
|
||||||
|
char *tmp=strdup(dynamic_features);
|
||||||
|
char *tok;
|
||||||
|
char *begin=tmp;
|
||||||
|
|
||||||
|
if (!tmp) {
|
||||||
|
ast_log(LOG_ERROR,"strdup failed");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( (tok=strsep(&tmp,"#")) != NULL) {
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
|
||||||
|
if ( ! strcasecmp(tok,feature->sname))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
|
||||||
|
if ( feature ) {
|
||||||
|
/* Feature is up for consideration */
|
||||||
|
if (!strcmp(feature->exten, code)) {
|
||||||
|
if (option_verbose > 2)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
|
||||||
|
res = feature->operation(chan, peer, config, code, sense);
|
||||||
|
break;
|
||||||
|
} else if (!strncmp(feature->exten, code, strlen(code))) {
|
||||||
|
res = FEATURE_RETURN_STOREDIGITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(begin);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1643,10 +1749,11 @@ static int handle_showfeatures(int fd, int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int fcount;
|
int fcount;
|
||||||
|
struct ast_call_feature *feature;
|
||||||
char format[] = "%-25s %-7s %-7s\n";
|
char format[] = "%-25s %-7s %-7s\n";
|
||||||
|
|
||||||
ast_cli(fd, format, "Feature", "Default", "Current");
|
ast_cli(fd, format, "Builtin Feature", "Default", "Current");
|
||||||
ast_cli(fd, format, "-------", "-------", "-------");
|
ast_cli(fd, format, "---------------", "-------", "-------");
|
||||||
|
|
||||||
ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
|
ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
|
||||||
|
|
||||||
@@ -1656,6 +1763,14 @@ static int handle_showfeatures(int fd, int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
|
ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
|
||||||
}
|
}
|
||||||
|
ast_cli(fd, "\n");
|
||||||
|
ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
|
||||||
|
ast_cli(fd, format, "---------------", "-------", "-------");
|
||||||
|
AST_LIST_LOCK(&feature_list);
|
||||||
|
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
|
||||||
|
ast_cli(fd, format, feature->sname, "no def", feature->exten);
|
||||||
|
}
|
||||||
|
AST_LIST_UNLOCK(&feature_list);
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -1843,6 +1958,7 @@ static int load_config(void)
|
|||||||
}
|
}
|
||||||
var = var->next;
|
var = var->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
unmap_features();
|
unmap_features();
|
||||||
var = ast_variable_browse(cfg, "featuremap");
|
var = ast_variable_browse(cfg, "featuremap");
|
||||||
while(var) {
|
while(var) {
|
||||||
@@ -1850,9 +1966,75 @@ static int load_config(void)
|
|||||||
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
|
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
|
||||||
var = var->next;
|
var = var->next;
|
||||||
}
|
}
|
||||||
ast_config_destroy(cfg);
|
|
||||||
|
/* Map a key combination to an application*/
|
||||||
|
var = ast_variable_browse(cfg, "applicationmap");
|
||||||
|
while(var) {
|
||||||
|
char *tmp_val=strdup(var->value);
|
||||||
|
|
||||||
|
if (!tmp_val) {
|
||||||
|
ast_log(LOG_ERROR, "res_features: strdup failed");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *exten, *party=NULL, *app=NULL, *app_args=NULL;
|
||||||
|
|
||||||
|
exten=strsep(&tmp_val,",");
|
||||||
|
if (exten) party=strsep(&tmp_val,",");
|
||||||
|
if (party) app=strsep(&tmp_val,",");
|
||||||
|
|
||||||
|
if (app) app_args=strsep(&tmp_val,",");
|
||||||
|
|
||||||
|
if (!(app && strlen(app)) || !(exten && strlen(exten)) || !(party && strlen(party)) || !(var->name && strlen(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,party,var->name);
|
||||||
|
free(tmp_val);
|
||||||
|
var = var->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct ast_call_feature *feature=find_feature(var->name);
|
||||||
|
int mallocd=0;
|
||||||
|
|
||||||
|
if (!feature) {
|
||||||
|
feature=malloc(sizeof(struct ast_call_feature));
|
||||||
|
mallocd=1;
|
||||||
|
}
|
||||||
|
if (!feature) {
|
||||||
|
ast_log(LOG_NOTICE, "Malloc failed at feature mapping\n");
|
||||||
|
free(tmp_val);
|
||||||
|
var = var->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(feature,0,sizeof(struct ast_call_feature));
|
||||||
|
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);
|
||||||
|
free(tmp_val);
|
||||||
|
|
||||||
|
if (app_args)
|
||||||
|
ast_copy_string(feature->app_args,app_args,FEATURE_APP_ARGS_LEN);
|
||||||
|
|
||||||
|
ast_copy_string(feature->exten, exten,sizeof(feature->exten));
|
||||||
|
feature->operation=feature_exec_app;
|
||||||
|
ast_set_flag(feature,AST_FEATURE_FLAG_NEEDSDTMF);
|
||||||
|
|
||||||
|
if (!strcasecmp(party,"caller"))
|
||||||
|
ast_set_flag(feature,AST_FEATURE_FLAG_CALLER);
|
||||||
|
else
|
||||||
|
ast_set_flag(feature,AST_FEATURE_FLAG_CALLEE);
|
||||||
|
|
||||||
|
ast_register_feature(feature);
|
||||||
|
|
||||||
|
if (option_verbose >=1) ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s' with code '%s'\n", var->name, app, exten);
|
||||||
|
}
|
||||||
|
var = var->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
|
||||||
|
|
||||||
if (con)
|
if (con)
|
||||||
ast_context_remove_extension2(con, ast_parking_ext(), 1, registrar);
|
ast_context_remove_extension2(con, ast_parking_ext(), 1, registrar);
|
||||||
|
|
||||||
@@ -1872,6 +2054,9 @@ int reload(void) {
|
|||||||
int load_module(void)
|
int load_module(void)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
AST_LIST_HEAD_INIT(&feature_list);
|
||||||
|
|
||||||
if ((res = load_config()))
|
if ((res = load_config()))
|
||||||
return res;
|
return res;
|
||||||
ast_cli_register(&showparked);
|
ast_cli_register(&showparked);
|
||||||
|
Reference in New Issue
Block a user