diff options
-rw-r--r-- | main/stasis_bridges.c | 153 | ||||
-rw-r--r-- | res/ari/ari_model_validators.c | 379 | ||||
-rw-r--r-- | res/ari/ari_model_validators.h | 63 | ||||
-rw-r--r-- | res/res_stasis.c | 2 | ||||
-rw-r--r-- | res/stasis/app.c | 155 | ||||
-rw-r--r-- | rest-api/api-docs/events.json | 100 |
6 files changed, 824 insertions, 28 deletions
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c index b8d4ae8e1..eb020163d 100644 --- a/main/stasis_bridges.c +++ b/main/stasis_bridges.c @@ -133,7 +133,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </managerEvent> ***/ +static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, + const struct stasis_message_sanitizer *sanitize); static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *message); +static struct ast_json *blind_transfer_to_json(struct stasis_message *msg, + const struct stasis_message_sanitizer *sanitize); static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *message); static struct ast_json *ast_channel_entered_bridge_to_json( struct stasis_message *msg, @@ -157,8 +161,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_entered_bridge_type, .to_json = ast_channel_entered_bridge_to_json); STASIS_MESSAGE_TYPE_DEFN(ast_channel_left_bridge_type, .to_json = ast_channel_left_bridge_to_json); -STASIS_MESSAGE_TYPE_DEFN(ast_blind_transfer_type, .to_ami = blind_transfer_to_ami); -STASIS_MESSAGE_TYPE_DEFN(ast_attended_transfer_type, .to_ami = attended_transfer_to_ami); +STASIS_MESSAGE_TYPE_DEFN(ast_blind_transfer_type, + .to_json = blind_transfer_to_json, + .to_ami = blind_transfer_to_ami); +STASIS_MESSAGE_TYPE_DEFN(ast_attended_transfer_type, + .to_json = attended_transfer_to_json, + .to_ami = attended_transfer_to_ami); /*! @} */ struct stasis_cache *ast_bridge_cache(void) @@ -614,6 +622,43 @@ static const char *result_strs[] = { [AST_BRIDGE_TRANSFER_SUCCESS] = "Success", }; +static struct ast_json *blind_transfer_to_json(struct stasis_message *msg, + const struct stasis_message_sanitizer *sanitize) +{ + struct ast_bridge_blob *blob = stasis_message_data(msg); + struct ast_json *json_channel, *out; + const struct timeval *tv = stasis_message_timestamp(msg); + + json_channel = ast_channel_snapshot_to_json(blob->channel, sanitize); + if (!json_channel) { + return NULL; + } + + out = ast_json_pack("{s: s, s: o, s: o, s: O, s: O, s: s, s: o}", + "type", "BridgeBlindTransfer", + "timestamp", ast_json_timeval(*tv, NULL), + "channel", json_channel, + "exten", ast_json_object_get(blob->blob, "exten"), + "context", ast_json_object_get(blob->blob, "context"), + "result", result_strs[ast_json_integer_get(ast_json_object_get(blob->blob, "result"))], + "is_external", ast_json_boolean(ast_json_integer_get(ast_json_object_get(blob->blob, "is_external")))); + + if (!out) { + return NULL; + } + + if (blob->bridge) { + struct ast_json *json_bridge = ast_bridge_snapshot_to_json(blob->bridge, sanitize); + + if (!json_bridge || ast_json_object_set(out, "bridge", json_bridge)) { + ast_json_unref(out); + return NULL; + } + } + + return out; +} + static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *msg) { RAII_VAR(struct ast_str *, channel_state, NULL, ast_free_ptr); @@ -685,6 +730,110 @@ void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result stasis_publish(ast_bridge_topic_all(), msg); } +static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, + const struct stasis_message_sanitizer *sanitize) +{ + struct ast_attended_transfer_message *transfer_msg = stasis_message_data(msg); + RAII_VAR(struct ast_json *, out, NULL, ast_json_unref); + struct ast_json *json_transferer1, *json_transferer2, *json_bridge, *json_channel; + const struct timeval *tv = stasis_message_timestamp(msg); + int res = 0; + + json_transferer1 = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize); + if (!json_transferer1) { + return NULL; + } + + json_transferer2 = ast_channel_snapshot_to_json(transfer_msg->to_transfer_target.channel_snapshot, sanitize); + if (!json_transferer2) { + ast_json_unref(json_transferer1); + return NULL; + } + + out = ast_json_pack("{s: s, s: o, s: o, s: o, s: s, s: o}", + "type", "BridgeAttendedTransfer", + "timestamp", ast_json_timeval(*tv, NULL), + "transferer_first_leg", json_transferer1, + "transferer_second_leg", json_transferer2, + "result", result_strs[transfer_msg->result], + "is_external", ast_json_boolean(transfer_msg->is_external)); + if (!out) { + return NULL; + } + + if (transfer_msg->to_transferee.bridge_snapshot) { + json_bridge = ast_bridge_snapshot_to_json(transfer_msg->to_transferee.bridge_snapshot, sanitize); + + if (!json_bridge) { + return NULL; + } + + res |= ast_json_object_set(out, "transferer_first_leg_bridge", json_bridge); + } + + if (transfer_msg->to_transfer_target.bridge_snapshot) { + json_bridge = ast_bridge_snapshot_to_json(transfer_msg->to_transfer_target.bridge_snapshot, sanitize); + + if (!json_bridge) { + return NULL; + } + + res |= ast_json_object_set(out, "transferer_second_leg_bridge", json_bridge); + } + + switch (transfer_msg->dest_type) { + case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE: + res |= ast_json_object_set(out, "destination_type", ast_json_string_create("bridge")); + res |= ast_json_object_set(out, "destination_bridge", ast_json_string_create(transfer_msg->dest.bridge)); + break; + case AST_ATTENDED_TRANSFER_DEST_APP: + res |= ast_json_object_set(out, "destination_type", ast_json_string_create("application")); + res |= ast_json_object_set(out, "destination_application", ast_json_string_create(transfer_msg->dest.app)); + break; + case AST_ATTENDED_TRANSFER_DEST_LINK: + res |= ast_json_object_set(out, "destination_type", ast_json_string_create("link")); + + json_channel = ast_channel_snapshot_to_json(transfer_msg->dest.links[0], sanitize); + if (!json_channel) { + return NULL; + } + res |= ast_json_object_set(out, "destination_link_first_leg", json_channel); + + json_channel = ast_channel_snapshot_to_json(transfer_msg->dest.links[1], sanitize); + if (!json_channel) { + return NULL; + } + res |= ast_json_object_set(out, "destination_link_second_leg", json_channel); + + break; + case AST_ATTENDED_TRANSFER_DEST_THREEWAY: + res |= ast_json_object_set(out, "destination_type", ast_json_string_create("threeway")); + + json_channel = ast_channel_snapshot_to_json(transfer_msg->dest.threeway.channel_snapshot, sanitize); + if (!json_channel) { + return NULL; + } + res |= ast_json_object_set(out, "destination_threeway_channel", json_channel); + + json_bridge = ast_bridge_snapshot_to_json(transfer_msg->dest.threeway.bridge_snapshot, sanitize); + if (!json_bridge) { + return NULL; + } + res |= ast_json_object_set(out, "destination_threeway_bridge", json_bridge); + + break; + case AST_ATTENDED_TRANSFER_DEST_FAIL: + res |= ast_json_object_set(out, "destination_type", ast_json_string_create("fail")); + break; + } + + if (res) { + return NULL; + } + + return ast_json_ref(out); +} + static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *msg) { RAII_VAR(struct ast_str *, variable_data, ast_str_create(64), ast_free_ptr); diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index b1482fa87..88cfc2697 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -1552,6 +1552,373 @@ ari_validator ast_ari_validate_application_replaced_fn(void) return ast_ari_validate_application_replaced; } +int ast_ari_validate_bridge_attended_transfer(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_destination_type = 0; + int has_is_external = 0; + int has_result = 0; + int has_transferer_first_leg = 0; + int has_transferer_second_leg = 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 BridgeAttendedTransfer 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 BridgeAttendedTransfer 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 BridgeAttendedTransfer field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_application", 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 BridgeAttendedTransfer field destination_application failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_bridge", 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 BridgeAttendedTransfer field destination_bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_link_first_leg", 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 BridgeAttendedTransfer field destination_link_first_leg failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_link_second_leg", 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 BridgeAttendedTransfer field destination_link_second_leg failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_threeway_bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field destination_threeway_bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_threeway_channel", 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 BridgeAttendedTransfer field destination_threeway_channel failed validation\n"); + res = 0; + } + } else + if (strcmp("destination_type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_destination_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field destination_type failed validation\n"); + res = 0; + } + } else + if (strcmp("is_external", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_is_external = 1; + prop_is_valid = ast_ari_validate_boolean( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field is_external failed validation\n"); + res = 0; + } + } else + if (strcmp("result", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_result = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field result failed validation\n"); + res = 0; + } + } else + if (strcmp("transferer_first_leg", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_transferer_first_leg = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field transferer_first_leg failed validation\n"); + res = 0; + } + } else + if (strcmp("transferer_first_leg_bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field transferer_first_leg_bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("transferer_second_leg", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_transferer_second_leg = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field transferer_second_leg failed validation\n"); + res = 0; + } + } else + if (strcmp("transferer_second_leg_bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer field transferer_second_leg_bridge failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI BridgeAttendedTransfer has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field application\n"); + res = 0; + } + + if (!has_destination_type) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field destination_type\n"); + res = 0; + } + + if (!has_is_external) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field is_external\n"); + res = 0; + } + + if (!has_result) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field result\n"); + res = 0; + } + + if (!has_transferer_first_leg) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field transferer_first_leg\n"); + res = 0; + } + + if (!has_transferer_second_leg) { + ast_log(LOG_ERROR, "ARI BridgeAttendedTransfer missing required field transferer_second_leg\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_attended_transfer_fn(void) +{ + return ast_ari_validate_bridge_attended_transfer; +} + +int ast_ari_validate_bridge_blind_transfer(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + int has_context = 0; + int has_exten = 0; + int has_is_external = 0; + int has_result = 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 BridgeBlindTransfer 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 BridgeBlindTransfer 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 BridgeBlindTransfer field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("context", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_context = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field context failed validation\n"); + res = 0; + } + } else + if (strcmp("exten", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_exten = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field exten failed validation\n"); + res = 0; + } + } else + if (strcmp("is_external", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_is_external = 1; + prop_is_valid = ast_ari_validate_boolean( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field is_external failed validation\n"); + res = 0; + } + } else + if (strcmp("result", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_result = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field result failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI BridgeBlindTransfer has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field channel\n"); + res = 0; + } + + if (!has_context) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field context\n"); + res = 0; + } + + if (!has_exten) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field exten\n"); + res = 0; + } + + if (!has_is_external) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field is_external\n"); + res = 0; + } + + if (!has_result) { + ast_log(LOG_ERROR, "ARI BridgeBlindTransfer missing required field result\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_blind_transfer_fn(void) +{ + return ast_ari_validate_bridge_blind_transfer; +} + int ast_ari_validate_bridge_created(struct ast_json *json) { int res = 1; @@ -3211,6 +3578,12 @@ int ast_ari_validate_event(struct ast_json *json) if (strcmp("ApplicationReplaced", discriminator) == 0) { return ast_ari_validate_application_replaced(json); } else + if (strcmp("BridgeAttendedTransfer", discriminator) == 0) { + return ast_ari_validate_bridge_attended_transfer(json); + } else + if (strcmp("BridgeBlindTransfer", discriminator) == 0) { + return ast_ari_validate_bridge_blind_transfer(json); + } else if (strcmp("BridgeCreated", discriminator) == 0) { return ast_ari_validate_bridge_created(json); } else @@ -3364,6 +3737,12 @@ int ast_ari_validate_message(struct ast_json *json) if (strcmp("ApplicationReplaced", discriminator) == 0) { return ast_ari_validate_application_replaced(json); } else + if (strcmp("BridgeAttendedTransfer", discriminator) == 0) { + return ast_ari_validate_bridge_attended_transfer(json); + } else + if (strcmp("BridgeBlindTransfer", discriminator) == 0) { + return ast_ari_validate_bridge_blind_transfer(json); + } else if (strcmp("BridgeCreated", discriminator) == 0) { return ast_ari_validate_bridge_created(json); } else diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index ffe003903..c299724eb 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -537,6 +537,42 @@ int ast_ari_validate_application_replaced(struct ast_json *json); ari_validator ast_ari_validate_application_replaced_fn(void); /*! + * \brief Validator for BridgeAttendedTransfer. + * + * Notification that an attended transfer has occurred. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge_attended_transfer(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge_attended_transfer(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_attended_transfer_fn(void); + +/*! + * \brief Validator for BridgeBlindTransfer. + * + * Notification that a blind transfer has occurred. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge_blind_transfer(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge_blind_transfer(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_blind_transfer_fn(void); + +/*! * \brief Validator for BridgeCreated. * * Notification that a bridge has been created. @@ -1137,6 +1173,33 @@ ari_validator ast_ari_validate_application_fn(void); * - type: string (required) * - application: string (required) * - timestamp: Date + * BridgeAttendedTransfer + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - destination_application: string + * - destination_bridge: string + * - destination_link_first_leg: Channel + * - destination_link_second_leg: Channel + * - destination_threeway_bridge: Bridge + * - destination_threeway_channel: Channel + * - destination_type: string (required) + * - is_external: boolean (required) + * - result: string (required) + * - transferer_first_leg: Channel (required) + * - transferer_first_leg_bridge: Bridge + * - transferer_second_leg: Channel (required) + * - transferer_second_leg_bridge: Bridge + * BridgeBlindTransfer + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge + * - channel: Channel (required) + * - context: string (required) + * - exten: string (required) + * - is_external: boolean (required) + * - result: string (required) * BridgeCreated * - type: string (required) * - application: string (required) diff --git a/res/res_stasis.c b/res/res_stasis.c index 32de9a041..f6fc0ac6e 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -591,7 +591,7 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name) int capabilities; int flags = AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM | AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO - | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED; + | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY; if (ast_strlen_zero(type) || !strcmp(type, "mixing")) { capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX | diff --git a/res/stasis/app.c b/res/stasis/app.c index 8e9872aec..dc322b6bf 100644 --- a/res/stasis/app.c +++ b/res/stasis/app.c @@ -41,8 +41,8 @@ struct stasis_app { struct stasis_topic *topic; /*! Router for handling messages forwarded to \a topic. */ struct stasis_message_router *router; - /*! Subscription to watch for bridge merge messages */ - struct stasis_subscription *bridge_merge_sub; + /*! Router for handling messages to the bridge all \a topic. */ + struct stasis_message_router *bridge_router; /*! Container of the channel forwards to this app's topic. */ struct ao2_container *forwards; /*! Callback function for this application. */ @@ -255,7 +255,7 @@ static void app_dtor(void *obj) ast_verb(1, "Destroying Stasis app %s\n", app->name); ast_assert(app->router == NULL); - ast_assert(app->bridge_merge_sub == NULL); + ast_assert(app->bridge_router == NULL); ao2_cleanup(app->topic); app->topic = NULL; @@ -589,37 +589,127 @@ static void sub_bridge_update_handler(void *data, app_send(app, json); } + +/*! \brief Helper function for determining if the application is subscribed to a given entity */ +static int bridge_app_subscribed(struct stasis_app *app, const char *uniqueid) +{ + struct app_forwards *forwards = NULL; + + forwards = ao2_find(app->forwards, uniqueid, OBJ_SEARCH_KEY); + if (!forwards) { + return 0; + } + + ao2_ref(forwards, -1); + return 1; +} + static void bridge_merge_handler(void *data, struct stasis_subscription *sub, struct stasis_message *message) { struct stasis_app *app = data; struct ast_bridge_merge_message *merge; - RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup); - if (stasis_subscription_final_message(sub, message)) { - ao2_cleanup(app); + merge = stasis_message_data(message); + + /* Find out if we're subscribed to either bridge */ + if (bridge_app_subscribed(app, merge->from->uniqueid) || + bridge_app_subscribed(app, merge->to->uniqueid)) { + /* Forward the message to the app */ + stasis_publish(app->topic, message); } +} - if (stasis_message_type(message) != ast_bridge_merge_message_type()) { - return; +/*! \brief Callback function for checking if channels in a bridge are subscribed to */ +static int bridge_app_subscribed_involved(struct stasis_app *app, struct ast_bridge_snapshot *snapshot) +{ + int subscribed = 0; + struct ao2_iterator iter; + char *uniqueid; + + if (bridge_app_subscribed(app, snapshot->uniqueid)) { + return 1; } - merge = stasis_message_data(message); + iter = ao2_iterator_init(snapshot->channels, 0); + for (; (uniqueid = ao2_iterator_next(&iter)); ao2_ref(uniqueid, -1)) { + if (bridge_app_subscribed(app, uniqueid)) { + subscribed = 1; + ao2_ref(uniqueid, -1); + break; + } + } + ao2_iterator_destroy(&iter); - /* Find out if we're subscribed to either bridge */ - forwards = ao2_find(app->forwards, merge->from->uniqueid, - OBJ_SEARCH_KEY); - if (!forwards) { - forwards = ao2_find(app->forwards, merge->to->uniqueid, - OBJ_SEARCH_KEY); + return subscribed; +} + +static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + struct stasis_app *app = data; + struct ast_bridge_blob *blob = stasis_message_data(message); + + if (bridge_app_subscribed(app, blob->channel->uniqueid) || + bridge_app_subscribed_involved(app, blob->bridge)) { + stasis_publish(app->topic, message); } +} - if (!forwards) { - return; +static void bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + struct stasis_app *app = data; + struct ast_attended_transfer_message *transfer_msg = stasis_message_data(message); + int subscribed = 0; + + subscribed = bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid); + if (!subscribed) { + subscribed = bridge_app_subscribed(app, transfer_msg->to_transfer_target.channel_snapshot->uniqueid); + } + if (!subscribed && transfer_msg->to_transferee.bridge_snapshot) { + subscribed = bridge_app_subscribed_involved(app, transfer_msg->to_transferee.bridge_snapshot); + } + if (!subscribed && transfer_msg->to_transfer_target.bridge_snapshot) { + subscribed = bridge_app_subscribed_involved(app, transfer_msg->to_transfer_target.bridge_snapshot); + } + + if (!subscribed) { + switch (transfer_msg->dest_type) { + case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE: + subscribed = bridge_app_subscribed(app, transfer_msg->dest.bridge); + break; + case AST_ATTENDED_TRANSFER_DEST_LINK: + subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[0]->uniqueid); + if (!subscribed) { + subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[1]->uniqueid); + } + break; + break; + case AST_ATTENDED_TRANSFER_DEST_THREEWAY: + subscribed = bridge_app_subscribed_involved(app, transfer_msg->dest.threeway.bridge_snapshot); + if (!subscribed) { + subscribed = bridge_app_subscribed(app, transfer_msg->dest.threeway.channel_snapshot->uniqueid); + } + break; + default: + break; + } + } + + if (subscribed) { + stasis_publish(app->topic, message); } +} - /* Forward the message to the app */ - stasis_publish(app->topic, message); +static void bridge_default_handler(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + struct stasis_app *app = data; + + if (stasis_subscription_final_message(sub, message)) { + ao2_cleanup(app); + } } struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data) @@ -652,12 +742,27 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat return NULL; } - app->bridge_merge_sub = stasis_subscribe(ast_bridge_topic_all(), - bridge_merge_handler, app); - if (!app->bridge_merge_sub) { + app->bridge_router = stasis_message_router_create(ast_bridge_topic_all()); + if (!app->bridge_router) { + return NULL; + } + + res |= stasis_message_router_add(app->bridge_router, + ast_bridge_merge_message_type(), bridge_merge_handler, app); + + res |= stasis_message_router_add(app->bridge_router, + ast_blind_transfer_type(), bridge_blind_transfer_handler, app); + + res |= stasis_message_router_add(app->bridge_router, + ast_attended_transfer_type(), bridge_attended_transfer_handler, app); + + res |= stasis_message_router_set_default(app->bridge_router, + bridge_default_handler, app); + + if (res != 0) { return NULL; } - /* Subscription holds a reference */ + /* Bridge router holds a reference */ ao2_ref(app, +1); app->router = stasis_message_router_create(app->topic); @@ -739,8 +844,8 @@ void app_shutdown(struct stasis_app *app) stasis_message_router_unsubscribe(app->router); app->router = NULL; - stasis_unsubscribe(app->bridge_merge_sub); - app->bridge_merge_sub = NULL; + stasis_message_router_unsubscribe(app->bridge_router); + app->bridge_router = NULL; } int app_is_active(struct stasis_app *app) diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json index b841485b3..e26eaa6c6 100644 --- a/rest-api/api-docs/events.json +++ b/rest-api/api-docs/events.json @@ -86,6 +86,8 @@ "BridgeCreated", "BridgeDestroyed", "BridgeMerged", + "BridgeBlindTransfer", + "BridgeAttendedTransfer", "ChannelCreated", "ChannelDestroyed", "ChannelEnteredBridge", @@ -211,6 +213,104 @@ } } }, + "BridgeBlindTransfer": { + "id": "BridgeBlindTransfer", + "description": "Notification that a blind transfer has occurred.", + "properties": { + "channel": { + "description": "The channel performing the blind transfer", + "required": true, + "type": "Channel" + }, + "exten": { + "description": "The extension transferred to", + "required": true, + "type": "string" + }, + "context": { + "description": "The context transferred to", + "required": true, + "type": "string" + }, + "result": { + "description": "The result of the transfer attempt", + "required": true, + "type": "string" + }, + "is_external": { + "description": "Whether the transfer was externally initiated or not", + "required": true, + "type": "boolean" + }, + "bridge": { + "description": "The bridge being transferred", + "type": "Bridge" + } + } + }, + "BridgeAttendedTransfer": { + "id": "BridgeAttendedTransfer", + "description": "Notification that an attended transfer has occurred.", + "properties": { + "transferer_first_leg": { + "description": "First leg of the transferer", + "required": true, + "type": "Channel" + }, + "transferer_second_leg": { + "description": "Second leg of the transferer", + "required": true, + "type": "Channel" + }, + "result": { + "description": "The result of the transfer attempt", + "required": true, + "type": "string" + }, + "is_external": { + "description": "Whether the transfer was externally initiated or not", + "required": true, + "type": "boolean" + }, + "transferer_first_leg_bridge": { + "description": "Bridge the transferer first leg is in", + "type": "Bridge" + }, + "transferer_second_leg_bridge": { + "description": "Bridge the transferer second leg is in", + "type": "Bridge" + }, + "destination_type": { + "description": "How the transfer was accomplished", + "required": true, + "type": "string" + }, + "destination_bridge": { + "description": "Bridge that survived the merge result", + "type": "string" + }, + "destination_application": { + "description": "Application that has been transferred into", + "type": "string" + }, + "destination_link_first_leg": { + "description": "First leg of a link transfer result", + "type": "Channel" + }, + "destination_link_second_leg": { + "description": "Second leg of a link transfer result", + "type": "Channel" + }, + "destination_threeway_channel": { + "description": "Transferer channel that survived the threeway result", + "type": "Channel" + }, + "destination_threeway_bridge": { + "description": "Bridge that survived the threeway result", + "type": "Bridge" + } + } + }, "ChannelCreated": { "id": "ChannelCreated", "description": "Notification that a channel has been created.", |