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:
Kevin P. Fleming
2005-08-23 02:22:33 +00:00
parent eb805b409f
commit 9e9f3ace21
4 changed files with 240 additions and 16 deletions

View File

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

View File

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

View File

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

View File

@@ -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,26 +841,94 @@ 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]))
struct ast_call_feature builtin_features[] = struct ast_call_feature builtin_features[] =
{ {
{ AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF }, { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
{ AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF }, { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
{ AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF }, { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
{ 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);
@@ -904,10 +974,46 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
break; break;
} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) { } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
if (res == FEATURE_RETURN_PASSDIGITS) if (res == FEATURE_RETURN_PASSDIGITS)
res = FEATURE_RETURN_STOREDIGITS; res = FEATURE_RETURN_STOREDIGITS;
} }
} }
} }
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,7 +1763,15 @@ 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,8 +1966,74 @@ 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);