aboutsummaryrefslogtreecommitdiffstats
path: root/main/features.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/features.c')
-rw-r--r--main/features.c281
1 files changed, 266 insertions, 15 deletions
diff --git a/main/features.c b/main/features.c
index b4861162e..6ad1c12a9 100644
--- a/main/features.c
+++ b/main/features.c
@@ -166,6 +166,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_ATXFER_CALLBACK_RETRIES 2
#define AST_MAX_WATCHERS 256
+#define MAX_DIAL_FEATURE_OPTIONS 30
struct feature_group_exten {
AST_LIST_ENTRY(feature_group_exten) entry;
@@ -222,6 +223,8 @@ struct ast_parkinglot {
int parkaddhints; /*!< Add parking hints automatically */
int parkedcalltransfers; /*!< Enable DTMF based transfers on bridge when picking up parked calls */
int parkedcallreparking; /*!< Enable DTMF based parking on bridge when picking up parked calls */
+ int parkedcallhangup; /*!< Enable DTMF based hangup on a bridge when pickup up parked calls */
+ int parkedcallrecording; /*!< Enable DTMF based recording on a bridge when picking up parked calls */
AST_LIST_HEAD(parkinglot_parklist, parkeduser) parkings; /*!< List of active parkings in this parkinglot */
};
@@ -262,7 +265,39 @@ static struct ast_app *stopmixmonitor_app = NULL;
static int stopmixmonitor_ok = 1;
static pthread_t parking_thread;
+struct ast_dial_features {
+ struct ast_flags features_caller;
+ struct ast_flags features_callee;
+ int is_caller;
+};
+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,
+ };
+
/* Forward declarations */
static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot);
static void parkinglot_unref(struct ast_parkinglot *parkinglot);
@@ -368,7 +403,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;
@@ -880,7 +915,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
}
return res;
-
}
/*! \brief Play message to both caller and callee in bridged call, plays synchronously, autoservicing the
@@ -1193,7 +1227,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) {
@@ -1323,6 +1357,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);
@@ -1363,6 +1399,13 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
return AST_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 */
@@ -1452,6 +1495,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;
@@ -2201,6 +2268,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_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_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_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_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
@@ -2245,6 +2398,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. */
@@ -2268,7 +2424,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;
@@ -2335,7 +2491,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) {
@@ -2677,6 +2833,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 Run management on parkinglots, called once per parkinglot */
int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, fd_set *nrfds, fd_set *nefds, int *ms, int *max)
{
@@ -2734,10 +2934,13 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds,
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 */
+ ast_log(LOG_WARNING, "Dialfeatures not found on %s, using default!\n", chan->name);
+ 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);
}
@@ -3057,8 +3260,11 @@ static int park_exec_full(struct ast_channel *chan, void *data, struct ast_parki
//ASTOBJ_UNLOCK(parkinglot);
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);
@@ -3083,7 +3289,7 @@ static int park_exec_full(struct ast_channel *chan, void *data, struct ast_parki
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) {
@@ -3098,14 +3304,43 @@ static int park_exec_full(struct ast_channel *chan, void *data, struct ast_parki
pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
ast_cdr_setdestchan(chan->cdr, peer->name);
memset(&config, 0, sizeof(struct ast_bridge_config));
- if ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->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 ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
- if ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
- if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
- if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ }
+ if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+ }
+ if ((parkinglot->parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
+ }
+ if ((parkinglot->parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
+ }
+ if ((parkinglot->parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+ }
+ if ((parkinglot->parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->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);
@@ -3352,6 +3587,8 @@ static int load_config(void)
default_parkinglot->parkaddhints = 0;
default_parkinglot->parkedcalltransfers = 0;
default_parkinglot->parkedcallreparking = 0;
+ default_parkinglot->parkedcallrecording = 0;
+ default_parkinglot->parkedcallhangup = 0;
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
@@ -3403,6 +3640,20 @@ static int load_config(void)
default_parkinglot->parkedcallreparking = AST_FEATURE_FLAG_BYCALLER;
else if (!strcasecmp(var->value, "callee"))
default_parkinglot->parkedcallreparking = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "parkedcallhangup")) {
+ if (!strcasecmp(var->value, "both"))
+ default_parkinglot->parkedcallhangup = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ default_parkinglot->parkedcallhangup = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ default_parkinglot->parkedcallhangup = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "parkedcallrecording")) {
+ if (!strcasecmp(var->value, "both"))
+ default_parkinglot->parkedcallrecording = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ default_parkinglot->parkedcallrecording = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ default_parkinglot->parkedcallrecording = AST_FEATURE_FLAG_BYCALLEE;
} else if (!strcasecmp(var->name, "adsipark")) {
adsipark = ast_true(var->value);
} else if (!strcasecmp(var->name, "transferdigittimeout")) {