res_stasis: Expose event for call forwarding and follow forwarded channel.

This change adds an event for when an originated call is redirected to
another target. This event contains the original channel and the newly
created channel. If a stasis subscription exists on the original originated
channel for a stasis application then a new subscription will also be
created on the stasis application to the redirected channel. This allows
the application to follow the call path completely.

(closes issue ASTERISK-22719)
Reported by: Joshua Colp

Review: https://reviewboard.asterisk.org/r/3054/
........

Merged revisions 403808 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403810 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/97/197/1
Joshua Colp 12 years ago
parent 661ac14911
commit 3a5cc054ed

@ -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);

@ -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);

@ -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);

@ -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;
}

@ -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,

@ -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

@ -790,6 +790,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.
*
@ -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)

@ -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) {

@ -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.",

Loading…
Cancel
Save