Fix recalled party B feature flags for a failed DTMF atxfer.
[asterisk/asterisk.git] / main / features.c
index 9a09058..d75b7b9 100644 (file)
@@ -681,9 +681,10 @@ 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;
+       /*! Channel's feature flags. */
+       struct ast_flags my_features;
+       /*! Bridge peer's feature flags. */
+       struct ast_flags peer_features;
 };
 
 #if defined(ATXFER_NULL_TECH)
@@ -788,6 +789,51 @@ static const struct ast_datastore_info dial_features_info = {
        .duplicate = dial_features_duplicate,
 };
 
+/*!
+ * \internal
+ * \brief Set the features datastore if it doesn't exist.
+ *
+ * \param chan Channel to add features datastore
+ * \param my_features The channel's feature flags
+ * \param peer_features The channel's bridge peer feature flags
+ *
+ * \retval TRUE if features datastore already existed.
+ */
+static int add_features_datastore(struct ast_channel *chan, const struct ast_flags *my_features, const struct ast_flags *peer_features)
+{
+       struct ast_datastore *datastore;
+       struct ast_dial_features *dialfeatures;
+
+       ast_channel_lock(chan);
+       datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL);
+       ast_channel_unlock(chan);
+       if (datastore) {
+               /* Already exists. */
+               return 1;
+       }
+
+       /* Create a new datastore with specified feature flags. */
+       datastore = ast_datastore_alloc(&dial_features_info, NULL);
+       if (!datastore) {
+               ast_log(LOG_WARNING, "Unable to create channel features datastore.\n");
+               return 0;
+       }
+       dialfeatures = ast_calloc(1, sizeof(*dialfeatures));
+       if (!dialfeatures) {
+               ast_log(LOG_WARNING, "Unable to allocate memory for feature flags.\n");
+               ast_datastore_free(datastore);
+               return 0;
+       }
+       ast_copy_flags(&dialfeatures->my_features, my_features, AST_FLAGS_ALL);
+       ast_copy_flags(&dialfeatures->peer_features, peer_features, AST_FLAGS_ALL);
+       datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+       datastore->data = dialfeatures;
+       ast_channel_lock(chan);
+       ast_channel_datastore_add(chan, datastore);
+       ast_channel_unlock(chan);
+       return 0;
+}
+
 /* Forward declarations */
 static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot);
 static void parkinglot_unref(struct ast_parkinglot *parkinglot);
@@ -2497,7 +2543,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
        int l;
        struct ast_party_connected_line connected_line;
        struct ast_datastore *features_datastore;
-       struct ast_dial_features *dialfeatures = NULL;
+       struct ast_dial_features *dialfeatures;
        char *transferer_tech;
        char *transferer_name;
        char *transferer_name_orig;
@@ -2547,7 +2593,13 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
                return xfer_park_call_helper(transferee, transferer, park_exten);
        }
 
-       /* Append context to dialed transfer number. */
+       /*
+        * Append context to dialed transfer number.
+        *
+        * NOTE: The local channel needs the /n flag so party C will use
+        * the feature flags set by the dialplan when calling that
+        * party.
+        */
        snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);
 
        /* If we are performing an attended transfer and we have two channels involved then
@@ -2700,7 +2752,31 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
                                        atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
                                ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n",
                                        !!newchan, outstate);
-                               if (newchan || ast_check_hangup(transferee)) {
+                               if (newchan) {
+                                       /*
+                                        * We have recalled party B (newchan).  We need to give this
+                                        * call leg the same feature flags as the original party B call
+                                        * leg.
+                                        */
+                                       ast_channel_lock(transferer);
+                                       features_datastore = ast_channel_datastore_find(transferer,
+                                               &dial_features_info, NULL);
+                                       if (features_datastore && (dialfeatures = features_datastore->data)) {
+                                               struct ast_flags my_features = { 0 };
+                                               struct ast_flags peer_features = { 0 };
+
+                                               ast_copy_flags(&my_features, &dialfeatures->my_features,
+                                                       AST_FLAGS_ALL);
+                                               ast_copy_flags(&peer_features, &dialfeatures->peer_features,
+                                                       AST_FLAGS_ALL);
+                                               ast_channel_unlock(transferer);
+                                               add_features_datastore(newchan, &my_features, &peer_features);
+                                       } else {
+                                               ast_channel_unlock(transferer);
+                                       }
+                                       break;
+                               }
+                               if (ast_check_hangup(transferee)) {
                                        break;
                                }
 
@@ -2799,33 +2875,26 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
                return -1;
        }
 
+       tobj->chan = newchan;
+       tobj->peer = xferchan;
+       tobj->bconfig = *config;
+
        ast_channel_lock(newchan);
-       if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) {
-               dialfeatures = features_datastore->data;
+       features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL);
+       if (features_datastore && (dialfeatures = features_datastore->data)) {
+               ast_copy_flags(&tobj->bconfig.features_callee, &dialfeatures->my_features,
+                       AST_FLAGS_ALL);
        }
        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;
+       features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL);
+       if (features_datastore && (dialfeatures = features_datastore->data)) {
+               ast_copy_flags(&tobj->bconfig.features_caller, &dialfeatures->my_features,
+                       AST_FLAGS_ALL);
        }
        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;
-
        if (tobj->bconfig.end_bridge_callback_data_fixup) {
                tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
        }
@@ -3825,60 +3894,16 @@ static void set_bridge_features_on_config(struct ast_bridge_config *config, cons
 
 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);
-       } else {
-               /* If we don't return here, then when we do a builtin_atxfer we will copy the disconnect
-                * flags over from the atxfer to the caller */
+       if (add_features_datastore(caller, &config->features_caller, &config->features_callee)) {
+               /*
+                * If we don't return here, then when we do a builtin_atxfer we
+                * will copy the disconnect flags over from the atxfer to the
+                * callee (Party C).
+                */
                return;
        }
 
-       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;
+       add_features_datastore(callee, &config->features_callee, &config->features_caller);
 }
 
 static void clear_dialed_interfaces(struct ast_channel *chan)
@@ -4710,8 +4735,8 @@ static int manage_parked_call(struct parkeduser *pu, const struct pollfd *pfds,
 
                                        snprintf(returnexten, sizeof(returnexten), "%s,%u,%s", peername,
                                                pu->parkinglot->cfg.comebackdialtime,
-                                               callback_dialoptions(&(dialfeatures->features_callee),
-                                                       &(dialfeatures->features_caller), buf, sizeof(buf)));
+                                               callback_dialoptions(&dialfeatures->peer_features,
+                                                       &dialfeatures->my_features, buf, sizeof(buf)));
                                } else { /* Existing default */
                                        ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n",
                                                ast_channel_name(chan));
@@ -5261,7 +5286,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
 
        if (peer) {
                struct ast_datastore *features_datastore;
-               struct ast_dial_features *dialfeatures = NULL;
+               struct ast_dial_features *dialfeatures;
 
                /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
                if (!ast_strlen_zero(courtesytone)) {
@@ -5305,20 +5330,10 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
 
                /* 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;
-               }
-
-               /*
-                * When the datastores for both caller and callee are created,
-                * both the callee and caller channels use the features_caller
-                * flag variable to represent themselves.  With that said, the
-                * config.features_callee flags should be copied from the
-                * datastore's caller feature flags regardless if peer was a
-                * callee or caller.
-                */
-               if (dialfeatures) {
-                       ast_copy_flags(&(config.features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL);
+               features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL);
+               if (features_datastore && (dialfeatures = features_datastore->data)) {
+                       ast_copy_flags(&config.features_callee, &dialfeatures->my_features,
+                               AST_FLAGS_ALL);
                }
                ast_channel_unlock(peer);