aboutsummaryrefslogtreecommitdiffstats
path: root/main/features.c
diff options
context:
space:
mode:
authortwilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-30 23:58:31 +0000
committertwilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-30 23:58:31 +0000
commitef01421fd35ea4d27915475f3160762c5b01ffd5 (patch)
tree8debdb4a4b7a30e73dab57373d7a3f555facc848 /main/features.c
parent3429e6951c2ff9c5c4c67c68b39978a3f0785077 (diff)
Merged revisions 172580 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r172580 | twilson | 2009-01-30 15:29:12 -0600 (Fri, 30 Jan 2009) | 44 lines Merged revisions 172517 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r172517 | twilson | 2009-01-30 11:47:41 -0600 (Fri, 30 Jan 2009) | 37 lines Fix feature inheritance with builtin features When using builtin features like parking and transfers, the AST_FEATURE_* flags would not be set correctly for all instances when either performing a builtin attended transfer, or parking a call and getting the timeout callback. Also, there was no way on a per-call basis to specify what features someone should have on picking up a parked call (since that doesn't involve the Dial() command). There was a global option for setting whether or not all users who pickup a parked call should have AST_FEATURE_REDIRECT set, but nothing for DISCONNECT, AUTOMON, or PARKCALL. This patch: 1) adds the BRIDGE_FEATURES dialplan variable which can be set either in the dialplan or with setvar in channels that support it. This variable can be set to any combination of 't', 'k', 'w', and 'h' (case insensitive matching of the equivalent dial options), to set what features should be activated on this channel. The patch moves the setting of the features datastores into the bridging code instead of app_dial to help facilitate this. 2) adds global options parkedcallparking, parkedcallhangup, and parkedcallrecording to be similar to the parkedcalltransfers option for globally setting features. 3) has builtin_atxfer call builtin_parkcall if being transfered to the parking extension since tracking everything through multiple masquerades, etc. is difficult and error-prone 4) attempts to fix all cases of return calls from parking and completed builtin transfers not having the correct permissions (closes issue #14274) Reported by: aragon Patches: fix_feature_inheritence.diff.txt uploaded by otherwiseguy (license 396) Tested by: aragon, otherwiseguy Review http://reviewboard.digium.com/r/138/ ........ ................ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.0@172635 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/features.c')
-rw-r--r--main/features.c280
1 files changed, 265 insertions, 15 deletions
diff --git a/main/features.c b/main/features.c
index 056692671..c25d924dc 100644
--- a/main/features.c
+++ b/main/features.c
@@ -64,6 +64,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_ATXFER_CALLBACK_RETRIES 2
#define AST_MAX_WATCHERS 256
+#define MAX_DIAL_FEATURE_OPTIONS 30
enum {
AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
@@ -97,6 +98,8 @@ static char *parkedcall = "ParkedCall";
static int parkaddhints = 0; /*!< Add parking hints automatically */
static int parkedcalltransfers = 0; /*!< Enable DTMF based transfers on bridge when picking up parked calls */
static int parkedcallreparking = 0; /*!< Enable DTMF based parking on bridge when picking up parked calls */
+static int parkedcallhangup = 0; /*!< Enable DTMF based disconnect on bridge when picking up parked calls */
+static int parkedcallrecording = 0; /*!< Enable DTMF based recording on bridge when picking up parked calls */
static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */
static char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */
static char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */
@@ -175,9 +178,41 @@ struct parkeduser {
unsigned char moh_trys;
AST_LIST_ENTRY(parkeduser) list;
};
+struct ast_dial_features {
+ struct ast_flags features_caller;
+ struct ast_flags features_callee;
+ int is_caller;
+};
static AST_LIST_HEAD_STATIC(parkinglot, parkeduser);
+static void *dial_features_duplicate(void *data)
+{
+ struct ast_dial_features *df = data, *df_copy;
+
+ if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
+ return NULL;
+ }
+
+ memcpy(df_copy, df, sizeof(*df));
+
+ return df_copy;
+ }
+
+ static void dial_features_destroy(void *data)
+ {
+ struct ast_dial_features *df = data;
+ if (df) {
+ ast_free(df);
+ }
+ }
+
+ const struct ast_datastore_info dial_features_info = {
+ .type = "dial-features",
+ .destroy = dial_features_destroy,
+ .duplicate = dial_features_duplicate,
+ };
+
static pthread_t parking_thread;
const char *ast_parking_ext(void)
@@ -265,7 +300,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
* bridge call, check if we're going back to dialplan
* if not hangup both legs of the call
*/
-static void *ast_bridge_call_thread(void *data)
+static void *ast_bridge_call_thread(void *data)
{
struct ast_bridge_thread_obj *tobj = data;
int res;
@@ -941,7 +976,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
ast_indicate(transferee, AST_CONTROL_HOLD);
memset(xferto, 0, sizeof(xferto));
-
+
/* Transfer */
res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
if (res < 0) {
@@ -1071,6 +1106,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
struct ast_bridge_config bconfig;
struct ast_frame *f;
int l;
+ struct ast_datastore *features_datastore;
+ struct ast_dial_features *dialfeatures = NULL;
ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
set_peers(&transferer, &transferee, peer, chan, sense);
@@ -1111,6 +1148,13 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
return FEATURE_RETURN_SUCCESS;
}
+ /* If we are attended transfering to parking, just use builtin_parkcall instead of trying to track all of
+ * the different variables for handling this properly with a builtin_atxfer */
+ if (!strcmp(xferto, ast_parking_ext())) {
+ finishup(transferee);
+ return builtin_parkcall(chan, peer, config, code, sense, data);
+ }
+
l = strlen(xferto);
snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */
newchan = ast_feature_request_and_dial(transferer, transferee, "Local", ast_best_codec(transferer->nativeformats),
@@ -1185,6 +1229,30 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_hangup(newchan);
return -1;
}
+
+ ast_channel_lock(newchan);
+ if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) {
+ dialfeatures = features_datastore->data;
+ }
+ ast_channel_unlock(newchan);
+
+ if (dialfeatures) {
+ /* newchan should always be the callee and shows up as callee in dialfeatures, but for some reason
+ I don't currently understand, the abilities of newchan seem to be stored on the caller side */
+ ast_copy_flags(&(config->features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL);
+ dialfeatures = NULL;
+ }
+
+ ast_channel_lock(xferchan);
+ if ((features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL))) {
+ dialfeatures = features_datastore->data;
+ }
+ ast_channel_unlock(xferchan);
+
+ if (dialfeatures) {
+ ast_copy_flags(&(config->features_caller), &(dialfeatures->features_caller), AST_FLAGS_ALL);
+ }
+
tobj->chan = newchan;
tobj->peer = xferchan;
tobj->bconfig = *config;
@@ -1931,6 +1999,92 @@ static struct ast_cdr *pick_unlocked_cdr(struct ast_cdr *cdr)
return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
}
+static void set_bridge_features_on_config(struct ast_bridge_config *config, const char *features)
+{
+ const char *feature;
+
+ if (ast_strlen_zero(features)) {
+ return;
+ }
+
+ for (feature = features; *feature; feature++) {
+ switch (*feature) {
+ case 'T' :
+ case 't' :
+ ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
+ break;
+ case 'K' :
+ case 'k' :
+ ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL);
+ break;
+ case 'H' :
+ case 'h' :
+ ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
+ break;
+ case 'W' :
+ case 'w' :
+ ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON);
+ break;
+ default :
+ ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature);
+ }
+ }
+}
+
+static void add_features_datastores(struct ast_channel *caller, struct ast_channel *callee, struct ast_bridge_config *config)
+{
+ struct ast_datastore *ds_callee_features = NULL, *ds_caller_features = NULL;
+ struct ast_dial_features *callee_features = NULL, *caller_features = NULL;
+
+ ast_channel_lock(caller);
+ ds_caller_features = ast_channel_datastore_find(caller, &dial_features_info, NULL);
+ ast_channel_unlock(caller);
+ if (!ds_caller_features) {
+ if (!(ds_caller_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to create channel datastore for caller features. Aborting!\n");
+ return;
+ }
+ if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) {
+ ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
+ ast_channel_datastore_free(ds_caller_features);
+ return;
+ }
+ ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER;
+ caller_features->is_caller = 1;
+ ast_copy_flags(&(caller_features->features_callee), &(config->features_callee), AST_FLAGS_ALL);
+ ast_copy_flags(&(caller_features->features_caller), &(config->features_caller), AST_FLAGS_ALL);
+ ds_caller_features->data = caller_features;
+ ast_channel_lock(caller);
+ ast_channel_datastore_add(caller, ds_caller_features);
+ ast_channel_unlock(caller);
+ }
+
+ ast_channel_lock(callee);
+ ds_callee_features = ast_channel_datastore_find(callee, &dial_features_info, NULL);
+ ast_channel_unlock(callee);
+ if (!ds_callee_features) {
+ if (!(ds_callee_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to create channel datastore for callee features. Aborting!\n");
+ return;
+ }
+ if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) {
+ ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
+ ast_channel_datastore_free(ds_callee_features);
+ return;
+ }
+ ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER;
+ callee_features->is_caller = 0;
+ ast_copy_flags(&(callee_features->features_callee), &(config->features_caller), AST_FLAGS_ALL);
+ ast_copy_flags(&(callee_features->features_caller), &(config->features_callee), AST_FLAGS_ALL);
+ ds_callee_features->data = callee_features;
+ ast_channel_lock(callee);
+ ast_channel_datastore_add(callee, ds_callee_features);
+ ast_channel_unlock(callee);
+ }
+
+ return;
+}
+
/*!
* \brief bridge the call and set CDR
* \param chan,peer,config
@@ -1975,6 +2129,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
}
+ set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES"));
+ add_features_datastores(chan, peer, config);
+
/* This is an interesting case. One example is if a ringing channel gets redirected to
* an extension that picks up a parked call. This will make sure that the call taken
* out of parking gets told that the channel it just got bridged to is still ringing. */
@@ -1998,7 +2155,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
pbx_exec(src, monitor_app, tmp);
}
}
-
+
set_config_flags(chan, peer, config);
config->firstpass = 1;
@@ -2065,7 +2222,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
}
for (;;) {
struct ast_channel *other; /* used later */
-
+
res = ast_channel_bridge(chan, peer, config, &f, &who);
if (config->feature_timer) {
@@ -2399,6 +2556,50 @@ static void post_manager_event(const char *s, struct parkeduser *pu)
);
}
+static char *callback_dialoptions(struct ast_flags *features_callee, struct ast_flags *features_caller, char *options, size_t len)
+{
+ int i = 0;
+ enum {
+ OPT_CALLEE_REDIRECT = 't',
+ OPT_CALLER_REDIRECT = 'T',
+ OPT_CALLEE_AUTOMON = 'w',
+ OPT_CALLER_AUTOMON = 'W',
+ OPT_CALLEE_DISCONNECT = 'h',
+ OPT_CALLER_DISCONNECT = 'H',
+ OPT_CALLEE_PARKCALL = 'k',
+ OPT_CALLER_PARKCALL = 'K',
+ };
+
+ memset(options, 0, len);
+ if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
+ options[i++] = OPT_CALLER_REDIRECT;
+ }
+ if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
+ options[i++] = OPT_CALLER_AUTOMON;
+ }
+ if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
+ options[i++] = OPT_CALLER_DISCONNECT;
+ }
+ if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
+ options[i++] = OPT_CALLER_PARKCALL;
+ }
+
+ if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
+ options[i++] = OPT_CALLEE_REDIRECT;
+ }
+ if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
+ options[i++] = OPT_CALLEE_AUTOMON;
+ }
+ if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
+ options[i++] = OPT_CALLEE_DISCONNECT;
+ }
+ if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
+ options[i++] = OPT_CALLEE_PARKCALL;
+ }
+
+ return options;
+}
+
/*!
* \brief Take care of parked calls and unpark them if needed
* \param ignore unused var.
@@ -2468,11 +2669,12 @@ static void *do_parking_thread(void *ignore)
peername += 7;
}
- if (dialfeatures)
- snprintf(returnexten, sizeof(returnexten), "%s,,%s", peername, dialfeatures->options);
- else /* Existing default */
- snprintf(returnexten, sizeof(returnexten), "%s,,t", peername);
-
+ if (dialfeatures) {
+ char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};
+ snprintf(returnexten, sizeof(returnexten), "%s|30|%s", peername, callback_dialoptions(&(dialfeatures->features_callee), &(dialfeatures->features_caller), buf, sizeof(buf)));
+ } else { /* Existing default */
+ snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername);
+ }
ast_add_extension2(con, 1, peername_flat, 1, NULL, NULL, "Dial", ast_strdup(returnexten), ast_free_ptr, registrar);
}
if (comebacktoorigin) {
@@ -2677,8 +2879,11 @@ static int park_exec(struct ast_channel *chan, void *data)
ast_answer(chan);
if (peer) {
+ struct ast_datastore *features_datastore;
+ struct ast_dial_features *dialfeatures = NULL;
+
/* Play a courtesy to the source(s) configured to prefix the bridge connecting */
-
+
if (!ast_strlen_zero(courtesytone)) {
int error = 0;
ast_indicate(peer, AST_CONTROL_UNHOLD);
@@ -2703,7 +2908,7 @@ static int park_exec(struct ast_channel *chan, void *data)
return -1;
}
} else
- ast_indicate(peer, AST_CONTROL_UNHOLD);
+ ast_indicate(peer, AST_CONTROL_UNHOLD);
res = ast_channel_make_compatible(chan, peer);
if (res < 0) {
@@ -2718,14 +2923,43 @@ static int park_exec(struct ast_channel *chan, void *data)
pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
ast_cdr_setdestchan(chan->cdr, peer->name);
memset(&config, 0, sizeof(struct ast_bridge_config));
- if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+
+ /* Get datastore for peer and apply it's features to the callee side of the bridge config */
+ ast_channel_lock(peer);
+ if ((features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL))) {
+ dialfeatures = features_datastore->data;
+ }
+ ast_channel_unlock(peer);
+
+ if (dialfeatures) {
+ ast_copy_flags(&(config.features_callee), dialfeatures->is_caller ? &(dialfeatures->features_caller) : &(dialfeatures->features_callee), AST_FLAGS_ALL);
+ }
+
+ if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
- if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
- if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
- if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+ }
+ if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
+ }
+ if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
+ }
+ if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+ }
+ if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
+ }
+
res = ast_bridge_call(chan, peer, &config);
pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
@@ -2808,6 +3042,8 @@ static int load_config(void)
parkaddhints = 0;
parkedcalltransfers = 0;
parkedcallreparking = 0;
+ parkedcallrecording = 0;
+ parkedcallhangup = 0;
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
@@ -2857,6 +3093,20 @@ static int load_config(void)
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "parkedcallhangup")) {
+ if (!strcasecmp(var->value, "both"))
+ parkedcallhangup = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ parkedcallhangup = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ parkedcallhangup = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "parkedcallrecording")) {
+ if (!strcasecmp(var->value, "both"))
+ parkedcallrecording = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ parkedcallrecording = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ parkedcallrecording = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "adsipark")) {
adsipark = ast_true(var->value);
} else if (!strcasecmp(var->name, "transferdigittimeout")) {