summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_dial.c2
-rw-r--r--apps/app_queue.c2
-rw-r--r--include/asterisk/stasis_channels.h2
-rw-r--r--main/dial.c7
-rw-r--r--main/stasis_channels.c73
-rw-r--r--res/ari/ari_model_validators.c137
-rw-r--r--res/ari/ari_model_validators.h28
-rw-r--r--res/stasis/app.c23
-rw-r--r--rest-api/api-docs/events.json37
9 files changed, 303 insertions, 8 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index c9bee19b0..fdbe05692 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1007,7 +1007,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
ast_channel_unlock(c);
ast_channel_lock_both(original, in);
- ast_channel_publish_dial_forward(in, original, NULL, "CANCEL",
+ ast_channel_publish_dial_forward(in, original, c, NULL, "CANCEL",
ast_channel_call_forward(c));
ast_channel_unlock(in);
ast_channel_unlock(original);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 906dff15f..d9f0f85d3 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -4666,7 +4666,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_channel_unlock(qe->chan);
ast_channel_lock_both(qe->chan, original);
- ast_channel_publish_dial_forward(qe->chan, original, NULL, "CANCEL",
+ ast_channel_publish_dial_forward(qe->chan, original, o->chan, NULL, "CANCEL",
ast_channel_call_forward(original));
ast_channel_unlock(original);
ast_channel_unlock(qe->chan);
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 519a4b676..8c27803df 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -518,12 +518,14 @@ void ast_channel_publish_dial(struct ast_channel *caller,
*
* \param caller The channel performing the dial operation
* \param peer The channel being dialed
+ * \param forwarded The channel created as a result of the call forwarding
* \param dialstring The information passed to the dialing application when beginning a dial
* \param dialstatus The current status of the dial operation
* \param forward The call forward string provided by the dialed channel
*/
void ast_channel_publish_dial_forward(struct ast_channel *caller,
struct ast_channel *peer,
+ struct ast_channel *forwarded,
const char *dialstring,
const char *dialstatus,
const char *forward);
diff --git a/main/dial.c b/main/dial.c
index 134386735..ca0b9c8d1 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -465,14 +465,17 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
channel->device = ast_strdup(device);
AST_LIST_UNLOCK(&dial->channels);
-
/* Drop the original channel */
- ast_hangup(original);
channel->owner = NULL;
/* Finally give it a go... send it out into the world */
begin_dial_channel(channel, chan, chan ? 0 : 1, predial_string);
+ ast_channel_publish_dial_forward(chan, original, channel->owner, NULL, "CANCEL",
+ ast_channel_call_forward(original));
+
+ ast_hangup(original);
+
return 0;
}
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index 38aac982e..8a39bdfcb 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -287,14 +287,21 @@ static void channel_blob_dtor(void *obj)
ast_json_unref(event->blob);
}
+/*! \brief Dummy callback for receiving events */
+static void dummy_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+}
+
void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer,
- const char *dialstring, const char *dialstatus, const char *forward)
+ struct ast_channel *forwarded, const char *dialstring, const char *dialstatus,
+ const char *forward)
{
RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel_snapshot *, peer_snapshot, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_channel_snapshot *, forwarded_snapshot, NULL, ao2_cleanup);
ast_assert(peer != NULL);
blob = ast_json_pack("{s: s, s: s, s: s}",
@@ -323,18 +330,33 @@ void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_cha
}
ast_multi_channel_blob_add_channel(payload, "peer", peer_snapshot);
+ if (forwarded) {
+ forwarded_snapshot = ast_channel_snapshot_create(forwarded);
+ if (!forwarded_snapshot) {
+ return;
+ }
+ ast_multi_channel_blob_add_channel(payload, "forwarded", forwarded_snapshot);
+ }
+
msg = stasis_message_create(ast_channel_dial_type(), payload);
if (!msg) {
return;
}
- publish_message_for_channel_topics(msg, caller);
+ if (forwarded) {
+ struct stasis_subscription *subscription = stasis_subscribe(ast_channel_topic(peer), dummy_event_cb, NULL);
+
+ stasis_publish(ast_channel_topic(peer), msg);
+ stasis_unsubscribe_and_join(subscription);
+ } else {
+ publish_message_for_channel_topics(msg, caller);
+ }
}
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer,
const char *dialstring, const char *dialstatus)
{
- ast_channel_publish_dial_forward(caller, peer, dialstring, dialstatus, NULL);
+ ast_channel_publish_dial_forward(caller, peer, NULL, dialstring, dialstatus, NULL);
}
static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot,
@@ -931,11 +953,54 @@ static struct ast_json *hangup_request_to_json(
return channel_blob_to_json(message, "ChannelHangupRequest", sanitize);
}
+static struct ast_json *dial_to_json(
+ struct stasis_message *message,
+ const struct stasis_message_sanitizer *sanitize)
+{
+ struct ast_multi_channel_blob *payload = stasis_message_data(message);
+ struct ast_json *blob = ast_multi_channel_blob_get_json(payload);
+ struct ast_json *caller_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "caller"), sanitize);
+ struct ast_json *peer_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "peer"), sanitize);
+ struct ast_json *forwarded_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "forwarded"), sanitize);
+ struct ast_json *json;
+ const struct timeval *tv = stasis_message_timestamp(message);
+ int res = 0;
+
+ json = ast_json_pack("{s: s, s: o, s: O, s: O, s: O}",
+ "type", "Dial",
+ "timestamp", ast_json_timeval(*tv, NULL),
+ "dialstatus", ast_json_object_get(blob, "dialstatus"),
+ "forward", ast_json_object_get(blob, "forward"),
+ "dialstring", ast_json_object_get(blob, "dialstring"));
+ if (!json) {
+ return NULL;
+ }
+
+ if (caller_json) {
+ res |= ast_json_object_set(json, "caller", caller_json);
+ }
+ if (peer_json) {
+ res |= ast_json_object_set(json, "peer", peer_json);
+ }
+ if (forwarded_json) {
+ res |= ast_json_object_set(json, "forwarded", forwarded_json);
+ }
+
+ if (res) {
+ ast_json_unref(json);
+ return NULL;
+ }
+
+ return json;
+}
+
/*!
* @{ \brief Define channel message types.
*/
STASIS_MESSAGE_TYPE_DEFN(ast_channel_snapshot_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_dial_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_dial_type,
+ .to_json = dial_to_json,
+ );
STASIS_MESSAGE_TYPE_DEFN(ast_channel_varset_type,
.to_ami = varset_to_ami,
.to_json = varset_to_json,
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 7ddef278d..d99240bd8 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -2879,6 +2879,137 @@ ari_validator ast_ari_validate_device_state_changed_fn(void)
return ast_ari_validate_device_state_changed;
}
+int ast_ari_validate_dial(struct ast_json *json)
+{
+ int res = 1;
+ struct ast_json_iter *iter;
+ int has_type = 0;
+ int has_application = 0;
+ int has_dialstatus = 0;
+ int has_peer = 0;
+
+ for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+ if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_type = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field type failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_application = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field application failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_date(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field timestamp failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("caller", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_channel(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field caller failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("dialstatus", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_dialstatus = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field dialstatus failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("dialstring", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field dialstring failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("forward", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field forward failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("forwarded", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_channel(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field forwarded failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("peer", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_peer = 1;
+ prop_is_valid = ast_ari_validate_channel(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Dial field peer failed validation\n");
+ res = 0;
+ }
+ } else
+ {
+ ast_log(LOG_ERROR,
+ "ARI Dial has undocumented field %s\n",
+ ast_json_object_iter_key(iter));
+ res = 0;
+ }
+ }
+
+ if (!has_type) {
+ ast_log(LOG_ERROR, "ARI Dial missing required field type\n");
+ res = 0;
+ }
+
+ if (!has_application) {
+ ast_log(LOG_ERROR, "ARI Dial missing required field application\n");
+ res = 0;
+ }
+
+ if (!has_dialstatus) {
+ ast_log(LOG_ERROR, "ARI Dial missing required field dialstatus\n");
+ res = 0;
+ }
+
+ if (!has_peer) {
+ ast_log(LOG_ERROR, "ARI Dial missing required field peer\n");
+ res = 0;
+ }
+
+ return res;
+}
+
+ari_validator ast_ari_validate_dial_fn(void)
+{
+ return ast_ari_validate_dial;
+}
+
int ast_ari_validate_endpoint_state_change(struct ast_json *json)
{
int res = 1;
@@ -3023,6 +3154,9 @@ int ast_ari_validate_event(struct ast_json *json)
if (strcmp("DeviceStateChanged", discriminator) == 0) {
return ast_ari_validate_device_state_changed(json);
} else
+ if (strcmp("Dial", discriminator) == 0) {
+ return ast_ari_validate_dial(json);
+ } else
if (strcmp("EndpointStateChange", discriminator) == 0) {
return ast_ari_validate_endpoint_state_change(json);
} else
@@ -3173,6 +3307,9 @@ int ast_ari_validate_message(struct ast_json *json)
if (strcmp("DeviceStateChanged", discriminator) == 0) {
return ast_ari_validate_device_state_changed(json);
} else
+ if (strcmp("Dial", discriminator) == 0) {
+ return ast_ari_validate_dial(json);
+ } else
if (strcmp("EndpointStateChange", discriminator) == 0) {
return ast_ari_validate_endpoint_state_change(json);
} else
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 1f9420cb0..22ab43be8 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -791,6 +791,24 @@ int ast_ari_validate_device_state_changed(struct ast_json *json);
ari_validator ast_ari_validate_device_state_changed_fn(void);
/*!
+ * \brief Validator for Dial.
+ *
+ * Dialing state has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_dial(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_dial().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_dial_fn(void);
+
+/*!
* \brief Validator for EndpointStateChange.
*
* Endpoint state changed.
@@ -1187,6 +1205,16 @@ ari_validator ast_ari_validate_application_fn(void);
* - application: string (required)
* - timestamp: Date
* - device_state: DeviceState (required)
+ * Dial
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - caller: Channel
+ * - dialstatus: string (required)
+ * - dialstring: string
+ * - forward: string
+ * - forwarded: Channel
+ * - peer: Channel (required)
* EndpointStateChange
* - type: string (required)
* - application: string (required)
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 8ad41e565..8e9872aec 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -265,6 +265,25 @@ static void app_dtor(void *obj)
app->data = NULL;
}
+static void call_forwarded_handler(struct stasis_app *app, struct stasis_message *message)
+{
+ struct ast_multi_channel_blob *payload = stasis_message_data(message);
+ struct ast_channel_snapshot *snapshot = ast_multi_channel_blob_get_channel(payload, "forwarded");
+ struct ast_channel *chan;
+
+ if (!snapshot) {
+ return;
+ }
+
+ chan = ast_channel_get_by_name(snapshot->uniqueid);
+ if (!chan) {
+ return;
+ }
+
+ app_subscribe_channel(app, chan);
+ ast_channel_unref(chan);
+}
+
static void sub_default_handler(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
@@ -275,6 +294,10 @@ static void sub_default_handler(void *data, struct stasis_subscription *sub,
ao2_cleanup(app);
}
+ if (stasis_message_type(message) == ast_channel_dial_type()) {
+ call_forwarded_handler(app, message);
+ }
+
/* By default, send any message that has a JSON representation */
json = stasis_message_to_json(message, stasis_app_get_sanitizer());
if (!json) {
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index 5195a5bbb..a0c5408fc 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -98,6 +98,7 @@
"ChannelHangupRequest",
"ChannelVarset",
"EndpointStateChange",
+ "Dial",
"StasisEnd",
"StasisStart"
]
@@ -411,6 +412,42 @@
}
}
},
+ "Dial": {
+ "id": "Dial",
+ "description": "Dialing state has changed.",
+ "properties": {
+ "caller": {
+ "required": false,
+ "type": "Channel",
+ "description": "The calling channel."
+ },
+ "peer": {
+ "required": true,
+ "type": "Channel",
+ "description": "The dialed channel."
+ },
+ "forward": {
+ "required": false,
+ "type": "string",
+ "description": "Forwarding target requested by the original dialed channel."
+ },
+ "forwarded": {
+ "required": false,
+ "type": "Channel",
+ "description": "Channel that the caller has been forwarded to."
+ },
+ "dialstring": {
+ "required": false,
+ "type": "string",
+ "description": "The dial string for calling the peer channel."
+ },
+ "dialstatus": {
+ "required": true,
+ "type": "string",
+ "description": "Current status of the dialing attempt to the peer."
+ }
+ }
+ },
"StasisEnd": {
"id": "StasisEnd",
"description": "Notification that a channel has left a Stasis application.",