add ability to map feature sequences to applications (issue #3764)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@6374 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
3f7567fe15
commit
89bf07cab3
|
@ -25,3 +25,8 @@ context => parkedcalls ; Which context parked calls are in
|
|||
;disconnect => *0 ; Disconnect
|
||||
;automon => *1 ; One Touch Record
|
||||
;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_FLAG_NEEDSDTMF (1 << 0)
|
||||
#define AST_FEATURE_FLAG_CALLEE (1 << 1)
|
||||
#define AST_FEATURE_FLAG_CALLER (1 << 2)
|
||||
|
||||
struct ast_bridge_config {
|
||||
struct ast_flags features_caller;
|
||||
|
|
|
@ -17,6 +17,28 @@
|
|||
#ifndef _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 */
|
||||
/*! \param chan the channel to actually be parked
|
||||
\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);
|
||||
|
||||
/*! 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 */
|
||||
|
|
|
@ -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_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)
|
||||
{
|
||||
|
@ -841,26 +841,94 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
|
|||
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 */
|
||||
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
|
||||
struct ast_call_feature builtin_features[] =
|
||||
{
|
||||
{
|
||||
{ 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_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, 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)
|
||||
{
|
||||
int x;
|
||||
|
@ -889,6 +957,8 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
|
|||
int x;
|
||||
struct ast_flags features;
|
||||
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)
|
||||
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;
|
||||
} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1643,10 +1749,11 @@ static int handle_showfeatures(int fd, int argc, char *argv[])
|
|||
{
|
||||
int i;
|
||||
int fcount;
|
||||
struct ast_call_feature *feature;
|
||||
char format[] = "%-25s %-7s %-7s\n";
|
||||
|
||||
ast_cli(fd, format, "Feature", "Default", "Current");
|
||||
ast_cli(fd, format, "-------", "-------", "-------");
|
||||
ast_cli(fd, format, "Builtin Feature", "Default", "Current");
|
||||
ast_cli(fd, format, "---------------", "-------", "-------");
|
||||
|
||||
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, "\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;
|
||||
}
|
||||
|
||||
|
@ -1843,6 +1958,7 @@ static int load_config(void)
|
|||
}
|
||||
var = var->next;
|
||||
}
|
||||
|
||||
unmap_features();
|
||||
var = ast_variable_browse(cfg, "featuremap");
|
||||
while(var) {
|
||||
|
@ -1850,8 +1966,74 @@ static int load_config(void)
|
|||
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
|
||||
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)
|
||||
ast_context_remove_extension2(con, ast_parking_ext(), 1, registrar);
|
||||
|
@ -1872,6 +2054,9 @@ int reload(void) {
|
|||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
AST_LIST_HEAD_INIT(&feature_list);
|
||||
|
||||
if ((res = load_config()))
|
||||
return res;
|
||||
ast_cli_register(&showparked);
|
||||
|
|
Reference in New Issue