[svn-commits] file: branch 12 r407153 - in /branches/12: main/ res/ res/ari/ res/stasis/ re...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Feb 1 10:24:07 CST 2014


Author: file
Date: Sat Feb  1 10:23:56 2014
New Revision: 407153

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=407153
Log:
res_stasis: Enable transfers and provide events when they occur.

This change enables transfers within ARI created bridges and adds events
for when they occur. Unlike other events these will be received if *any*
subscribed object is involved in the transfer.

(closes issue ASTERISK-22984)
Reported by: David M. Lee

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

Modified:
    branches/12/main/stasis_bridges.c
    branches/12/res/ari/ari_model_validators.c
    branches/12/res/ari/ari_model_validators.h
    branches/12/res/res_stasis.c
    branches/12/res/stasis/app.c
    branches/12/rest-api/api-docs/events.json

Modified: branches/12/main/stasis_bridges.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/main/stasis_bridges.c?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/main/stasis_bridges.c (original)
+++ branches/12/main/stasis_bridges.c Sat Feb  1 10:23:56 2014
@@ -133,7 +133,11 @@
 	</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 @@
 	.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)
@@ -613,6 +621,43 @@
 	[AST_BRIDGE_TRANSFER_NOT_PERMITTED] = "Not Permitted",
 	[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)
 {
@@ -683,6 +728,110 @@
 	}
 
 	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)

Modified: branches/12/res/ari/ari_model_validators.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/ari/ari_model_validators.c?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/res/ari/ari_model_validators.c (original)
+++ branches/12/res/ari/ari_model_validators.c Sat Feb  1 10:23:56 2014
@@ -1552,6 +1552,373 @@
 	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 @@
 	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
@@ -3363,6 +3736,12 @@
 	} else
 	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);

Modified: branches/12/res/ari/ari_model_validators.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/ari/ari_model_validators.h?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/res/ari/ari_model_validators.h (original)
+++ branches/12/res/ari/ari_model_validators.h Sat Feb  1 10:23:56 2014
@@ -535,6 +535,42 @@
  * See \ref ast_ari_model_validators.h for more details.
  */
 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.
@@ -1137,6 +1173,33 @@
  * - 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)

Modified: branches/12/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/res_stasis.c?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/res/res_stasis.c (original)
+++ branches/12/res/res_stasis.c Sat Feb  1 10:23:56 2014
@@ -591,7 +591,7 @@
 	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 |

Modified: branches/12/res/stasis/app.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/stasis/app.c?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/res/stasis/app.c (original)
+++ branches/12/res/stasis/app.c Sat Feb  1 10:23:56 2014
@@ -41,8 +41,8 @@
 	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 @@
 	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 @@
 	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);
+
+	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);
+	}
+}
+
+/*! \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;
+	}
+
+	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);
+
+	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);
+	}
+}
+
+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);
+	}
+}
+
+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);
 	}
-
-	if (stasis_message_type(message) != ast_bridge_merge_message_type()) {
-		return;
-	}
-
-	merge = stasis_message_data(message);
-
-	/* 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);
-	}
-
-	if (!forwards) {
-		return;
-	}
-
-	/* Forward the message to the app */
-	stasis_publish(app->topic, message);
 }
 
 struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data)
@@ -652,12 +742,27 @@
 		return NULL;
 	}
 
-	app->bridge_merge_sub = stasis_subscribe(ast_bridge_topic_all(),
-		bridge_merge_handler, app);
-	if (!app->bridge_merge_sub) {
-		return NULL;
-	}
-	/* Subscription holds a reference */
+	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;
+	}
+	/* Bridge router holds a reference */
 	ao2_ref(app, +1);
 
 	app->router = stasis_message_router_create(app->topic);
@@ -739,8 +844,8 @@
 
 	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)

Modified: branches/12/rest-api/api-docs/events.json
URL: http://svnview.digium.com/svn/asterisk/branches/12/rest-api/api-docs/events.json?view=diff&rev=407153&r1=407152&r2=407153
==============================================================================
--- branches/12/rest-api/api-docs/events.json (original)
+++ branches/12/rest-api/api-docs/events.json Sat Feb  1 10:23:56 2014
@@ -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.",




More information about the svn-commits mailing list