summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2014-09-19 15:18:01 +0000
committerJonathan Rose <jrose@digium.com>2014-09-19 15:18:01 +0000
commitd1b1e911bf7d11c5f2991bf5f625d84418ccdff2 (patch)
tree8d41112611df926fbb506943579029f44f3a69d9 /main
parentc95b53e21a70d49da172fb983df088893d932191 (diff)
Stasis_channels: Resolve unfinished Dials when doing masquerades
Masquerades into channels that are in the dialing state don't end their dial and this goes against the model for things like CDRs and generating Dial end manager actions and such. ASTERISK-24237 #close Reported by: Richard Mudgett Review: https://reviewboard.asterisk.org/r/3990/ ........ Merged revisions 423525 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@423530 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/stasis_channels.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index d40b5701c..79c453cdd 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -40,6 +40,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis.h"
#include "asterisk/stasis_cache_pattern.h"
#include "asterisk/stasis_channels.h"
+#include "asterisk/dial.h"
+#include "asterisk/linkedlists.h"
/*** DOCUMENTATION
<managerEvent language="en_US" name="VarSet">
@@ -360,10 +362,29 @@ void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_cha
publish_message_for_channel_topics(msg, caller);
}
+static void remove_dial_masquerade(struct ast_channel *caller,
+ struct ast_channel *peer);
+static int set_dial_masquerade(struct ast_channel *caller,
+ struct ast_channel *peer, const char *dialstring);
+
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer,
const char *dialstring, const char *dialstatus)
{
+ if (caller) {
+ ast_channel_lock_both(caller, peer);
+ if (!ast_strlen_zero(dialstatus)) {
+ remove_dial_masquerade(caller, peer);
+ } else {
+ set_dial_masquerade(caller, peer, dialstring);
+ }
+ }
+
ast_channel_publish_dial_forward(caller, peer, NULL, dialstring, dialstatus, NULL);
+
+ if (caller) {
+ ast_channel_unlock(peer);
+ ast_channel_unlock(caller);
+ }
}
static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot,
@@ -1198,3 +1219,201 @@ int ast_stasis_channels_init(void)
return res;
}
+/*!
+ * \internal
+ * \brief A list element for the dial_masquerade_datastore -- stores data about a dialed peer
+ */
+struct dial_target {
+ struct ast_channel *peer;
+ char *dialstring;
+ AST_LIST_ENTRY(dial_target) list;
+};
+
+/*!
+ * \internal
+ * \brief Datastore used for advancing dial state in the case of a masquerade
+ * against a channel in the process of dialing.
+ */
+struct dial_masquerade_datastore {
+ AST_LIST_HEAD(,dial_target) dialed_peers;
+};
+
+/*!
+ * \internal
+ * \brief Destructor for dial_masquerade_datastore
+ */
+static void dial_masquerade_datastore_destroy(void *data)
+{
+ struct dial_masquerade_datastore *ds = data;
+ struct dial_target *cur;
+
+ while ((cur = AST_LIST_REMOVE_HEAD(&ds->dialed_peers, list))) {
+ ast_channel_cleanup(cur->peer);
+ ast_free(cur->dialstring);
+ ast_free(cur);
+ }
+}
+
+static void disable_dial_masquerade(struct ast_channel *caller);
+
+/*!
+ * \internal
+ * \brief Primary purpose for dial_masquerade_datastore, publishes
+ * the channel dial event needed to set the incoming channel into the
+ * dial state during a masquerade.
+ * \param data pointer to the dial_masquerade_datastore
+ * \param old_chan Channel being replaced (not used)
+ * \param new_chan Channel being pushed to dial mode
+ */
+static void dial_masquerade(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+ struct dial_masquerade_datastore *masq_data = data;
+ struct dial_target *cur;
+
+ AST_LIST_TRAVERSE(&masq_data->dialed_peers, cur, list) {
+ ast_channel_publish_dial_forward(old_chan, cur->peer, NULL, cur->dialstring, "NOANSWER", NULL);
+ ast_channel_publish_dial_forward(new_chan, cur->peer, NULL, cur->dialstring, NULL, NULL);
+ }
+
+ disable_dial_masquerade(old_chan);
+}
+
+static const struct ast_datastore_info dial_masquerade_info = {
+ .type = "stasis-chan-dial-masq",
+ .destroy = dial_masquerade_datastore_destroy,
+ .chan_breakdown = dial_masquerade,
+};
+
+/*!
+ * \internal
+ * \brief Retrieves the dial_masquerade_datastore from a channel if
+ * it has one.
+ * \param chan Channel a datastore data is wanted from
+ *
+ * \returns the datastore data if it exists
+ * \returns NULL if no dial_masquerade_datastore has been set for
+ * chan
+ */
+static struct dial_masquerade_datastore *fetch_dial_masquerade_datastore(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore = NULL;
+
+ datastore = ast_channel_datastore_find(chan, &dial_masquerade_info, NULL);
+ if (!datastore) {
+ return NULL;
+ }
+
+ return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Create a fresh dial_masquerade_datastore on a channel
+ * \param chan Channel we want to create a dial_masquerade_datastore
+ * for.
+ * \returns pointer to the datastore data if successful
+ * \returns NULL on allocation error
+ */
+static struct dial_masquerade_datastore *setup_dial_masquerade_datastore(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore = NULL;
+ struct dial_masquerade_datastore *masq_datastore;
+
+ datastore = ast_datastore_alloc(&dial_masquerade_info, NULL);
+ if (!datastore) {
+ return NULL;
+ }
+
+ masq_datastore = ast_calloc(1, sizeof(*masq_datastore));
+ if (!masq_datastore) {
+ ast_datastore_free(datastore);
+ return NULL;
+ }
+
+ datastore->data = masq_datastore;
+ ast_channel_datastore_add(chan, datastore);
+ return masq_datastore;
+}
+
+/*!
+ * \internal
+ * \brief Get existing dial_masquerade_datastore if it exists. If
+ * not, create a new one and return a pointer to that data.
+ * \param chan Which channel the datastore is wanted for.
+ * \returns pointer to the datastore if successful
+ * \returns NULL on allocation failure.
+ */
+static struct dial_masquerade_datastore *fetch_or_create_dial_masquerade_datastore(struct ast_channel *chan)
+{
+ struct dial_masquerade_datastore *ds;
+
+ ds = fetch_dial_masquerade_datastore(chan);
+ if (!ds) {
+ ds = setup_dial_masquerade_datastore(chan);
+ }
+
+ return ds;
+}
+
+static int set_dial_masquerade(struct ast_channel *chan, struct ast_channel *peer_chan, const char *dialstring)
+{
+ struct dial_masquerade_datastore *ds = fetch_or_create_dial_masquerade_datastore(chan);
+ struct dial_target *target = ast_calloc(1, sizeof(*target));
+
+ if (!target) {
+ return -1;
+ }
+
+ if (!ds) {
+ ast_free(target);
+ return -1;
+ }
+
+ if (dialstring) {
+ target->dialstring = ast_strdup(dialstring);
+ if (!target->dialstring) {
+ ast_free(target);
+ return -1;
+ }
+ }
+
+ ast_channel_ref(peer_chan);
+ target->peer = peer_chan;
+
+ AST_LIST_INSERT_HEAD(&ds->dialed_peers, target, list);
+
+ return 0;
+}
+
+static void remove_dial_masquerade(struct ast_channel *chan, struct ast_channel *peer_chan)
+{
+ struct dial_masquerade_datastore *ds = fetch_dial_masquerade_datastore(chan);
+ struct dial_target *cur;
+
+ if (!ds) {
+ return;
+ }
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&ds->dialed_peers, cur, list) {
+ if (cur->peer == peer_chan) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_channel_cleanup(cur->peer);
+ ast_free(cur->dialstring);
+ ast_free(cur);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+static void disable_dial_masquerade(struct ast_channel *chan)
+{
+ struct ast_datastore *ds = ast_channel_datastore_find(chan,
+ &dial_masquerade_info, NULL);
+
+ if (!ds) {
+ return;
+ }
+
+ ast_channel_datastore_remove(chan, ds);
+}