Add transfer support to CEL

This adds CEL support for blind and attended transfers and call pickup.
During the course of adding this functionality I noticed that
CONF_ENTER, CONF_EXIT, and BRIDGE_TO_CONF events are particularly
useless without a bridge identifier, so I added that as well.

This adds tests for blind transfers, several types of attended
transfers, and call pickup.

The extra field in CEL records now consists of a JSON blob whose fields
are defined on a per-event basis.

Review: https://reviewboard.asterisk.org/r/2658/
(closes issue ASTERISK-21565)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394858 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Kinsey Moore 2013-07-20 13:10:22 +00:00
parent 4e15046cf6
commit 684c83b29b
14 changed files with 858 additions and 277 deletions

17
CHANGES
View File

@ -320,6 +320,23 @@ CDR (Call Detail Records)
included in the resulting CDR. If both parties have the same variable, only
the Party A value is provided.
CEL (Channel Event Logging)
------------------
* The 'extra' field of all CEL events that use it now consists of a JSON blob
with key/value pairs which are defined in the Asterisk 12 CEL documentation.
* AST_CEL_BLINDTRANSFER events now report the transferee bridge unique
identifier, extension, and context in a JSON blob as the extra string
instead of the transferee channel name as the peer.
* AST_CEL_ATTENDEDTRANSFER events now report the peer as NULL and additional
information in the 'extra' string as a JSON blob. For transfers that occur
between two bridged channels, the 'extra' JSON blob contains the primary
bridge unique identifier, the secondary channel name, and the secondary
bridge unique identifier. For transfers that occur between a bridged channel
and a channel running an app, the 'extra' JSON blob contains the primary
bridge unique identifier, the secondary channel name, and the app name.
Features
-------------------
* The BRIDGE_FEATURES channel variable would previously only set features for

View File

@ -75,9 +75,9 @@ static int celgenuserevent_exec(struct ast_channel *chan, const char *data)
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
blob = ast_json_pack("{s: s, s: s}",
blob = ast_json_pack("{s: s, s: {s: s}}",
"event", args.event,
"extra", args.extra);
"extra", "extra", args.extra);
if (!blob) {
return res;
}

View File

@ -60,7 +60,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
#include "asterisk/cel.h"
#include "asterisk/aoc.h"
#include "asterisk/ccss.h"
#include "asterisk/indications.h"

View File

@ -46,7 +46,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/features.h"
#include "asterisk/manager.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#define PICKUPMARK "PICKUPMARK"

View File

@ -103,7 +103,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/taskprocessor.h"
#include "asterisk/aoc.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"
#include "asterisk/term.h"
#include "asterisk/dial.h"

View File

@ -107,7 +107,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/callerid.h"
#include "asterisk/adsi.h"
#include "asterisk/cli.h"
#include "asterisk/cel.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/say.h"

View File

@ -276,7 +276,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/translate.h"
#include "asterisk/ast_version.h"
#include "asterisk/event.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"
#include "asterisk/aoc.h"
#include "asterisk/message.h"

View File

@ -41,7 +41,6 @@
#include "asterisk/manager.h"
#include "asterisk/astdb.h"
#include "asterisk/features.h"
#include "asterisk/cel.h"
#include "asterisk/causes.h"
#include "asterisk/features_config.h"
#include "asterisk/bridging.h"

View File

@ -67,8 +67,6 @@ enum ast_cel_event_type {
AST_CEL_BLINDTRANSFER = 13,
/*! \brief a transfer occurs */
AST_CEL_ATTENDEDTRANSFER = 14,
/*! \brief a transfer occurs */
AST_CEL_TRANSFER = 15,
/*! \brief a 3-way conference, usually part of a transfer */
AST_CEL_HOOKFLASH = 16,
/*! \brief a 3-way conference, usually part of a transfer */
@ -167,31 +165,6 @@ enum ast_cel_event_type ast_cel_str_to_event_type(const char *name);
*/
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event);
/*!
* \brief Report a channel event
*
* \param chan This argument is required. This is the primary channel associated with
* this channel event.
* \param event_type This is the type of call event being reported.
* \param userdefevname This is an optional custom name for the call event.
* \param extra This is an optional opaque field that will go into the "CEL_EXTRA"
* information element of the call event.
* \param peer2 All CEL events contain a "peer name" information element. The first
* place the code will look to get a peer name is from the bridged channel to
* chan. If chan has no bridged channel and peer2 is specified, then the name
* of peer2 will go into the "peer name" field. If neither are available, the
* peer name field will be blank.
*
* \since 1.8
*
* \pre chan and peer2 are both unlocked
*
* \retval 0 success
* \retval non-zero failure
*/
int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type,
const char *userdefevname, const char *extra, struct ast_channel *peer2);
/*!
* \brief Helper struct for getting the fields out of a CEL event
*/
@ -312,8 +285,8 @@ struct ast_channel_snapshot;
* with this channel event.
* \param event_type The type of call event being reported.
* \param userdefevname Custom name for the call event. (optional)
* \param extra An opaque field that will go into the "CEL_EXTRA" information
* element of the call event. (optional)
* \param extra An event-specific opaque JSON blob to be rendered and placed
* in the "CEL_EXTRA" information element of the call event. (optional)
* \param peer_name The peer name to be placed into the event. (optional)
*
* \since 12
@ -323,7 +296,7 @@ struct ast_channel_snapshot;
*/
struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
enum ast_cel_event_type event_type, const char *userdefevname,
const char *extra, const char *peer_name);
struct ast_json *extra, const char *peer_name);
#if defined(__cplusplus) || defined(c_plusplus)
}

View File

@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis_bridging.h"
#include "asterisk/bridging.h"
#include "asterisk/parking.h"
#include "asterisk/features.h"
/*** DOCUMENTATION
<configInfo name="cel" language="en_US">
@ -101,7 +102,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="CONF_END"/>
<enum name="PARK_START"/>
<enum name="PARK_END"/>
<enum name="TRANSFER"/>
<enum name="USER_DEFINED"/>
<enum name="CONF_ENTER"/>
<enum name="CONF_EXIT"/>
@ -308,7 +308,6 @@ static const char * const cel_event_types[CEL_MAX_EVENT_IDS] = {
[AST_CEL_CONF_END] = "CONF_END",
[AST_CEL_PARK_START] = "PARK_START",
[AST_CEL_PARK_END] = "PARK_END",
[AST_CEL_TRANSFER] = "TRANSFER",
[AST_CEL_USER_DEFINED] = "USER_DEFINED",
[AST_CEL_CONF_ENTER] = "CONF_ENTER",
[AST_CEL_CONF_EXIT] = "CONF_EXIT",
@ -634,9 +633,13 @@ static int cel_track_app(const char *const_app)
static int cel_linkedid_ref(const char *linkedid);
struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
enum ast_cel_event_type event_type, const char *userdefevname,
const char *extra, const char *peer_name)
struct ast_json *extra, const char *peer_name)
{
struct timeval eventtime = ast_tvnow();
RAII_VAR(char *, extra_txt, NULL, ast_free);
if (extra) {
extra_txt = ast_json_dump_string(extra);
}
return ast_event_new(AST_EVENT_CEL,
AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
@ -658,14 +661,14 @@ struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->uniqueid,
AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra, ""),
AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer_name, ""),
AST_EVENT_IE_END);
}
static int report_event_snapshot(struct ast_channel_snapshot *snapshot,
static int cel_report_event(struct ast_channel_snapshot *snapshot,
enum ast_cel_event_type event_type, const char *userdefevname,
const char *extra, const char *peer2_name)
struct ast_json *extra, const char *peer2_name)
{
struct ast_event *ev;
char *linkedid = ast_strdupa(snapshot->linkedid);
@ -735,7 +738,7 @@ static void check_retire_linkedid(struct ast_channel_snapshot *snapshot)
* before unreffing the channel we have a refcount of 3, we're done. Unlink and report. */
if (ao2_ref(lid, -1) == 3) {
ast_str_container_remove(linkedids, lid);
report_event_snapshot(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
}
ao2_ref(lid, -1);
}
@ -895,109 +898,6 @@ static int cel_linkedid_ref(const char *linkedid)
return 0;
}
int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type,
const char *userdefevname, const char *extra, struct ast_channel *peer2)
{
struct timeval eventtime;
struct ast_event *ev;
const char *peername = "";
struct ast_channel *peer;
char *linkedid = ast_strdupa(ast_channel_linkedid(chan));
if (!ast_cel_check_enabled()) {
return 0;
}
/* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
* reporting on CHANNEL_START so we can track when to send LINKEDID_END */
if (ast_cel_track_event(AST_CEL_LINKEDID_END) && event_type == AST_CEL_CHANNEL_START && linkedid) {
if (cel_linkedid_ref(linkedid)) {
return -1;
}
}
if (!ast_cel_track_event(event_type)) {
return 0;
}
if ((event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END)
&& !cel_track_app(ast_channel_appl(chan))) {
return 0;
}
ast_channel_lock(chan);
peer = ast_bridged_channel(chan);
if (peer) {
ast_channel_ref(peer);
}
ast_channel_unlock(chan);
if (peer) {
ast_channel_lock(peer);
peername = ast_strdupa(ast_channel_name(peer));
ast_channel_unlock(peer);
} else if (peer2) {
ast_channel_lock(peer2);
peername = ast_strdupa(ast_channel_name(peer2));
ast_channel_unlock(peer2);
}
if (!userdefevname) {
userdefevname = "";
}
if (!extra) {
extra = "";
}
eventtime = ast_tvnow();
ast_channel_lock(chan);
ev = ast_event_new(AST_EVENT_CEL,
AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, userdefevname,
AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR,
S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""),
AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR,
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""),
AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR,
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, ""),
AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR,
S_OR(ast_channel_dialed(chan)->number.str, ""),
AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, ast_channel_exten(chan),
AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, ast_channel_context(chan),
AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan),
AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_appl(chan), ""),
AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_data(chan), ""),
AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, ast_channel_amaflags(chan),
AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, ast_channel_accountcode(chan),
AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, ast_channel_peeraccount(chan),
AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan),
AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, ast_channel_linkedid(chan),
AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, ast_channel_userfield(chan),
AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, extra,
AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, peername,
AST_EVENT_IE_END);
ast_channel_unlock(chan);
if (peer) {
peer = ast_channel_unref(peer);
}
if (ev && ast_event_queue(ev)) {
ast_event_destroy(ev);
return -1;
}
return 0;
}
int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r)
{
if (r->version != AST_CEL_EVENT_RECORD_VERSION) {
@ -1074,13 +974,13 @@ static void cel_channel_state_change(
int is_hungup, was_hungup;
if (!new_snapshot) {
report_event_snapshot(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
check_retire_linkedid(old_snapshot);
return;
}
if (!old_snapshot) {
report_event_snapshot(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
return;
}
@ -1088,22 +988,22 @@ static void cel_channel_state_change(
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
if (!was_hungup && is_hungup) {
RAII_VAR(struct ast_str *, extra_str, ast_str_create(128), ast_free);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
RAII_VAR(struct ast_multi_channel_blob *, blob, get_dialstatus_blob(new_snapshot->uniqueid), ao2_cleanup);
const char *dialstatus = "";
if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
dialstatus = get_blob_variable(blob, "dialstatus");
}
ast_str_set(&extra_str, 0, "%d,%s,%s",
new_snapshot->hangupcause,
new_snapshot->hangupsource,
dialstatus);
report_event_snapshot(new_snapshot, AST_CEL_HANGUP, NULL, ast_str_buffer(extra_str), NULL);
extra = ast_json_pack("{s: i, s: s, s: s}",
"hangupcause", new_snapshot->hangupcause,
"hangupsource", new_snapshot->hangupsource,
"dialstatus", dialstatus);
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
return;
}
if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
report_event_snapshot(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
return;
}
}
@ -1136,12 +1036,12 @@ static void cel_channel_app_change(
/* old snapshot has an application, end it */
if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
report_event_snapshot(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
}
/* new snapshot has an application, start it */
if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
report_event_snapshot(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
}
}
@ -1260,7 +1160,11 @@ static void cel_bridge_enter_cb(
if (snapshot->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE)) {
if (assoc && assoc->track_as_conf) {
report_event_snapshot(chan_snapshot, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
extra = ast_json_pack("{s: s}", "bridge_id", snapshot->uniqueid);
if (extra) {
cel_report_event(chan_snapshot, AST_CEL_CONF_ENTER, NULL, extra, NULL);
}
return;
}
@ -1286,7 +1190,7 @@ static void cel_bridge_enter_cb(
}
add_bridge_primary(latest_primary, snapshot->uniqueid, chan_snapshot->name);
report_event_snapshot(latest_primary, AST_CEL_BRIDGE_START, NULL, NULL, chan_snapshot->name);
cel_report_event(latest_primary, AST_CEL_BRIDGE_START, NULL, NULL, chan_snapshot->name);
} else if (ao2_container_count(snapshot->channels) > 2) {
if (!assoc) {
ast_log(LOG_ERROR, "No association found for bridge %s\n", snapshot->uniqueid);
@ -1295,18 +1199,31 @@ static void cel_bridge_enter_cb(
/* this bridge will no longer be treated like a bridge, so mark the bridge_assoc as such */
if (!assoc->track_as_conf) {
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
assoc->track_as_conf = 1;
report_event_snapshot(assoc->primary_snapshot, AST_CEL_BRIDGE_TO_CONF, NULL,
chan_snapshot->name, assoc->secondary_name);
extra = ast_json_pack("{s: s, s: s}",
"channel_name", chan_snapshot->name,
"bridge_id", snapshot->uniqueid);
if (extra) {
cel_report_event(assoc->primary_snapshot, AST_CEL_BRIDGE_TO_CONF, NULL,
extra, assoc->secondary_name);
}
ast_string_field_set(assoc, secondary_name, "");
}
}
} else if (snapshot->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
if (!assoc) {
add_bridge_primary(chan_snapshot, snapshot->uniqueid, "");
return;
}
report_event_snapshot(chan_snapshot, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
extra = ast_json_pack("{s: s}", "bridge_id", snapshot->uniqueid);
if (extra) {
cel_report_event(chan_snapshot, AST_CEL_CONF_ENTER, NULL, extra, NULL);
}
}
}
@ -1333,17 +1250,25 @@ static void cel_bridge_leave_cb(
}
if (assoc->track_as_conf) {
report_event_snapshot(chan_snapshot, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
extra = ast_json_pack("{s: s}", "bridge_id", snapshot->uniqueid);
if (extra) {
cel_report_event(chan_snapshot, AST_CEL_CONF_EXIT, NULL, extra, NULL);
}
return;
}
if (ao2_container_count(snapshot->channels) == 1) {
report_event_snapshot(assoc->primary_snapshot, AST_CEL_BRIDGE_END, NULL, NULL, assoc->secondary_name);
cel_report_event(assoc->primary_snapshot, AST_CEL_BRIDGE_END, NULL, NULL, assoc->secondary_name);
remove_bridge_primary(assoc->primary_snapshot->uniqueid);
return;
}
} else if (snapshot->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
report_event_snapshot(chan_snapshot, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
extra = ast_json_pack("{s: s}", "bridge_id", snapshot->uniqueid);
if (extra) {
cel_report_event(chan_snapshot, AST_CEL_CONF_EXIT, NULL, extra, NULL);
}
}
}
@ -1353,26 +1278,36 @@ static void cel_parking_cb(
struct stasis_message *message)
{
struct ast_parked_call_payload *parked_payload = stasis_message_data(message);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
const char *reason = NULL;
switch (parked_payload->event_type) {
case PARKED_CALL:
report_event_snapshot(parked_payload->parkee, AST_CEL_PARK_START, NULL,
parked_payload->parkinglot,
parked_payload->parker_dial_string);
break;
extra = ast_json_pack("{s: s, s: s}",
"parker_dial_string", parked_payload->parker_dial_string,
"parking_lot", parked_payload->parkinglot);
if (extra) {
cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
}
return;
case PARKED_CALL_TIMEOUT:
report_event_snapshot(parked_payload->parkee, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL);
reason = "ParkedCallTimeOut";
break;
case PARKED_CALL_GIVEUP:
report_event_snapshot(parked_payload->parkee, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp", NULL);
reason = "ParkedCallGiveUp";
break;
case PARKED_CALL_UNPARKED:
report_event_snapshot(parked_payload->parkee, AST_CEL_PARK_END, NULL, "ParkedCallUnparked", NULL);
reason = "ParkedCallUnparked";
break;
case PARKED_CALL_FAILED:
report_event_snapshot(parked_payload->parkee, AST_CEL_PARK_END, NULL, "ParkedCallFailed", NULL);
reason = "ParkedCallFailed";
break;
}
extra = ast_json_pack("{s: s}", "reason", reason);
if (extra) {
cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
}
}
static void save_dialstatus(struct ast_multi_channel_blob *blob)
@ -1396,11 +1331,15 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub,
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
if (!caller) {
return;
}
report_event_snapshot(caller, AST_CEL_FORWARD, NULL, get_blob_variable(blob, "forward"), NULL);
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
if (extra) {
cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
}
}
if (ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
@ -1423,8 +1362,8 @@ static void cel_generic_cb(
case AST_CEL_USER_DEFINED:
{
const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
const char *extra = ast_json_string_get(ast_json_object_get(event_details, "extra"));
report_event_snapshot(obj->snapshot, event_type, event, extra, NULL);
struct ast_json *extra = ast_json_object_get(event_details, "extra");
cel_report_event(obj->snapshot, event_type, event, extra, NULL);
break;
}
default:
@ -1433,6 +1372,107 @@ static void cel_generic_cb(
}
}
static void cel_blind_transfer_cb(
void *data, struct stasis_subscription *sub,
struct stasis_topic *topic,
struct stasis_message *message)
{
struct ast_bridge_blob *obj = stasis_message_data(message);
struct ast_channel_snapshot *chan_snapshot = obj->channel;
struct ast_bridge_snapshot *bridge_snapshot = obj->bridge;
struct ast_json *blob = obj->blob;
struct ast_json *json_exten = ast_json_object_get(blob, "exten");
struct ast_json *json_context = ast_json_object_get(blob, "context");
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
const char *exten, *context;
if (!json_exten || !json_context) {
return;
}
exten = ast_json_string_get(json_exten);
context = ast_json_string_get(json_context);
if (!exten || !context) {
return;
}
extra = ast_json_pack("{s: s, s: s, s: s}",
"extension", exten,
"context", context,
"bridge_id", bridge_snapshot->uniqueid);
if (extra) {
cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
}
}
static void cel_attended_transfer_cb(
void *data, struct stasis_subscription *sub,
struct stasis_topic *topic,
struct stasis_message *message)
{
struct ast_attended_transfer_message *xfer = stasis_message_data(message);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
struct ast_bridge_snapshot *bridge1, *bridge2;
struct ast_channel_snapshot *channel1, *channel2;
/* Make sure bridge1 is always non-NULL */
if (!xfer->to_transferee.bridge_snapshot) {
bridge1 = xfer->to_transfer_target.bridge_snapshot;
bridge2 = xfer->to_transferee.bridge_snapshot;
channel1 = xfer->to_transfer_target.channel_snapshot;
channel2 = xfer->to_transferee.channel_snapshot;
} else {
bridge1 = xfer->to_transferee.bridge_snapshot;
bridge2 = xfer->to_transfer_target.bridge_snapshot;
channel1 = xfer->to_transferee.channel_snapshot;
channel2 = xfer->to_transfer_target.channel_snapshot;
}
switch (xfer->dest_type) {
case AST_ATTENDED_TRANSFER_DEST_FAIL:
return;
/* handle these two the same */
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
case AST_ATTENDED_TRANSFER_DEST_LINK:
extra = ast_json_pack("{s: s, s: s, s: s}",
"bridge1_id", bridge1->uniqueid,
"channel2_name", channel2->name,
"bridge2_id", bridge2->uniqueid);
if (!extra) {
return;
}
break;
case AST_ATTENDED_TRANSFER_DEST_APP:
extra = ast_json_pack("{s: s, s: s, s: s}",
"bridge1_id", bridge1->uniqueid,
"channel2_name", channel2->name,
"app", xfer->dest.app);
if (!extra) {
return;
}
break;
}
cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
}
static void cel_pickup_cb(
void *data, struct stasis_subscription *sub,
struct stasis_topic *topic,
struct stasis_message *message)
{
struct ast_multi_channel_blob *obj = stasis_message_data(message);
struct ast_channel_snapshot *channel = ast_multi_channel_blob_get_channel(obj, "channel");
struct ast_channel_snapshot *target = ast_multi_channel_blob_get_channel(obj, "target");
if (!channel || !target) {
return;
}
cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, channel->name);
}
static void ast_cel_engine_term(void)
{
aco_info_destroy(&cel_cfg_info);
@ -1554,6 +1594,21 @@ int ast_cel_engine_init(void)
cel_generic_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_blind_transfer_type(),
cel_blind_transfer_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_attended_transfer_type(),
cel_attended_transfer_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_call_pickup_type(),
cel_pickup_cb,
NULL);
/* If somehow we failed to add any routes, just shut down the whole
* thing and fail it.
*/

View File

@ -50,7 +50,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/translate.h"
#include "asterisk/manager.h"
#include "asterisk/cel.h"
#include "asterisk/chanvars.h"
#include "asterisk/linkedlists.h"
#include "asterisk/indications.h"

View File

@ -70,7 +70,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/audiohook.h"
#include "asterisk/global_datastores.h"
#include "asterisk/astobj2.h"
#include "asterisk/cel.h"
#include "asterisk/test.h"
#include "asterisk/bridging.h"
#include "asterisk/bridging_basic.h"
@ -2553,8 +2552,6 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
}
/* Initiate the channel transfer of party A to party C (or recalled party B). */
ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);
xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
if (!xferchan) {
ast_autoservice_chan_hangup_peer(transferee, newchan);
@ -4739,8 +4736,6 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
ast_channel_unlock(chan);
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
if (ast_answer(chan)) {
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
goto pickup_failed;

View File

@ -50,7 +50,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/callerid.h"
#include "asterisk/cdr.h"
#include "asterisk/cel.h"
#include "asterisk/config.h"
#include "asterisk/term.h"
#include "asterisk/time.h"

View File

@ -47,6 +47,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/bridging_basic.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_bridging.h"
#include "asterisk/json.h"
#include "asterisk/features.h"
#define TEST_CATEGORY "/main/cel/"
@ -78,6 +80,73 @@ static void do_sleep(void)
} \
} while (0)
#define APPEND_EVENT_SNAPSHOT(snapshot, ev_type, userevent, extra, peer) do { \
if (append_expected_event_snapshot(snapshot, ev_type, userevent, extra, peer)) { \
return AST_TEST_FAIL; \
} \
} while (0)
#define APPEND_DUMMY_EVENT() do { \
if (append_dummy_event()) { \
return AST_TEST_FAIL; \
} \
} while (0)
#define CONF_EXIT(channel, bridge) do { \
ast_test_validate(test, 0 == ast_bridge_depart(channel)); \
CONF_EXIT_EVENT(channel, bridge); \
} while (0)
#define CONF_EXIT_EVENT(channel, bridge) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s}", "bridge_id", bridge->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(channel, AST_CEL_CONF_EXIT, NULL, extra, NULL); \
} while (0)
#define CONF_EXIT_SNAPSHOT(channel, bridge) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s}", "bridge_id", bridge->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT_SNAPSHOT(channel, AST_CEL_CONF_EXIT, NULL, extra, NULL); \
} while (0)
#define CONF_ENTER_EVENT(channel, bridge) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s}", "bridge_id", bridge->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(channel, AST_CEL_CONF_ENTER, NULL, extra, NULL); \
} while (0)
#define BRIDGE_TO_CONF(first, second, third, bridge) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s, s: s}", \
"channel_name", ast_channel_name(third), \
"bridge_id", bridge->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(first, AST_CEL_BRIDGE_TO_CONF, NULL, extra, ast_channel_name(second)); \
} while (0)
#define BLINDTRANSFER_EVENT(channel, bridge, extension, context) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s, s: s, s: s}", \
"extension", extension, \
"context", context, \
"bridge_id", bridge->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(channel, AST_CEL_BLINDTRANSFER, NULL, extra, NULL); \
} while (0)
#define ATTENDEDTRANSFER_BRIDGE(channel1, bridge1, channel2, bridge2) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: s, s: s, s: s}", \
"bridge1_id", bridge1->uniqueid, \
"channel2_name", ast_channel_name(channel2), \
"bridge2_id", bridge2->uniqueid); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL); \
} while (0)
/*! \brief Alice's Caller ID */
#define ALICE_CALLERID { .id.name.str = "Alice", .id.name.valid = 1, .id.number.str = "100", .id.number.valid = 1, }
@ -90,31 +159,45 @@ static void do_sleep(void)
/*! \brief David's Caller ID */
#define DAVID_CALLERID { .id.name.str = "David", .id.name.valid = 1, .id.number.str = "400", .id.number.valid = 1, }
/*! \brief Eve's Caller ID */
#define EVE_CALLERID { .id.name.str = "Eve", .id.name.valid = 1, .id.number.str = "500", .id.number.valid = 1, }
/*! \brief Fred's Caller ID */
#define FRED_CALLERID { .id.name.str = "Fred", .id.name.valid = 1, .id.number.str = "600", .id.number.valid = 1, }
/*! \brief Create a \ref test_cel_chan_tech for Alice. */
#define CREATE_ALICE_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "100", "100", "default", NULL, 0, CHANNEL_TECH_NAME "/Alice"); \
/*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
/*! \brief Create a \ref test_cel_chan_tech for Bob. */
#define CREATE_BOB_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "200", "200", "default", NULL, 0, CHANNEL_TECH_NAME "/Bob"); \
/*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
/*! \brief Create a \ref test_cel_chan_tech for Charlie. */
#define CREATE_CHARLIE_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "300", "300", "default", NULL, 0, CHANNEL_TECH_NAME "/Charlie"); \
/*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
/*! \brief Create a \ref test_cel_chan_tech for Charlie. */
/*! \brief Create a \ref test_cel_chan_tech for David. */
#define CREATE_DAVID_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "400", "400", "default", NULL, 0, CHANNEL_TECH_NAME "/David"); \
/*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
/*! \brief Create a \ref test_cel_chan_tech for Eve. */
#define CREATE_EVE_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "500", "500", "default", NULL, 0, CHANNEL_TECH_NAME "/Eve"); \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
/*! \brief Create a \ref test_cel_chan_tech for Eve. */
#define CREATE_FRED_CHANNEL(channel_var, caller_id) do { \
(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "600", "600", "default", NULL, 0, CHANNEL_TECH_NAME "/Fred"); \
APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
} while (0)
@ -139,24 +222,41 @@ static void do_sleep(void)
} while (0)
/*! \brief Hang up a test channel safely */
#define HANGUP_CHANNEL(channel, cause, hangup_extra) \
do { \
ast_channel_hangupcause_set((channel), (cause)); \
ao2_ref(channel, +1); \
ast_hangup(channel); \
APPEND_EVENT(channel, AST_CEL_HANGUP, NULL, hangup_extra, NULL); \
APPEND_EVENT(channel, AST_CEL_CHANNEL_END, NULL, NULL, NULL); \
ao2_cleanup(stasis_cache_get_extended(ast_channel_topic_all_cached(), \
ast_channel_snapshot_type(), ast_channel_uniqueid(channel), 1)); \
ao2_cleanup(channel); \
channel = NULL; \
#define HANGUP_CHANNEL(channel, cause, dialstatus) do { \
ast_channel_hangupcause_set((channel), (cause)); \
ao2_ref(channel, +1); \
ast_hangup((channel)); \
HANGUP_EVENT(channel, cause, dialstatus); \
APPEND_EVENT(channel, AST_CEL_CHANNEL_END, NULL, NULL, NULL); \
ao2_cleanup(stasis_cache_get_extended(ast_channel_topic_all_cached(), \
ast_channel_snapshot_type(), ast_channel_uniqueid(channel), 1)); \
ao2_cleanup(channel); \
channel = NULL; \
} while (0)
#define HANGUP_EVENT(channel, cause, dialstatus) do { \
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); \
extra = ast_json_pack("{s: i, s: s, s: s}", \
"hangupcause", cause, \
"hangupsource", "", \
"dialstatus", dialstatus); \
ast_test_validate(test, extra != NULL); \
APPEND_EVENT(channel, AST_CEL_HANGUP, NULL, extra, NULL); \
} while (0)
static int append_expected_event(
struct ast_channel *chan,
enum ast_cel_event_type type,
const char *userdefevname,
const char *extra, const char *peer);
struct ast_json *extra, const char *peer);
static int append_expected_event_snapshot(
struct ast_channel_snapshot *snapshot,
enum ast_cel_event_type type,
const char *userdefevname,
struct ast_json *extra, const char *peer);
static int append_dummy_event(void);
static void safe_channel_release(struct ast_channel *chan)
{
@ -185,7 +285,7 @@ AST_TEST_DEFINE(test_cel_channel_creation)
CREATE_ALICE_CHANNEL(chan, (&caller));
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -213,7 +313,7 @@ AST_TEST_DEFINE(test_cel_unanswered_inbound_call)
EMULATE_APP_DATA(chan, 1, "Wait", "1");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -246,7 +346,7 @@ AST_TEST_DEFINE(test_cel_unanswered_outbound_call)
ast_channel_context_set(chan, "default");
ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
EMULATE_APP_DATA(chan, 0, "AppDial", "(Outgoing Line)");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -273,7 +373,7 @@ AST_TEST_DEFINE(test_cel_single_party)
ANSWER_CHANNEL(chan);
EMULATE_APP_DATA(chan, 2, "VoiceMailMain", "1");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -312,7 +412,7 @@ AST_TEST_DEFINE(test_cel_single_bridge)
ast_bridge_depart(chan);
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -353,7 +453,7 @@ AST_TEST_DEFINE(test_cel_single_bridge_continue)
EMULATE_APP_DATA(chan, 3, "Wait", "");
/* And then it hangs up */
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -403,8 +503,8 @@ AST_TEST_DEFINE(test_cel_single_twoparty_bridge_a)
ast_bridge_depart(chan_bob);
APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_bob));
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -455,8 +555,8 @@ AST_TEST_DEFINE(test_cel_single_twoparty_bridge_b)
ast_bridge_depart(chan_bob);
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_alice));
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -510,18 +610,15 @@ AST_TEST_DEFINE(test_cel_single_multiparty_bridge)
EMULATE_APP_DATA(chan_charlie, 2, "Bridge", "");
ast_bridge_impart(bridge, chan_charlie, NULL, NULL, 0);
do_sleep();
APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_TO_CONF, NULL, ast_channel_name(chan_charlie), ast_channel_name(chan_bob));
BRIDGE_TO_CONF(chan_alice, chan_bob, chan_charlie, bridge);
ast_bridge_depart(chan_alice);
APPEND_EVENT(chan_alice, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
ast_bridge_depart(chan_bob);
APPEND_EVENT(chan_bob, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
ast_bridge_depart(chan_charlie);
APPEND_EVENT(chan_charlie, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
CONF_EXIT(chan_alice, bridge);
CONF_EXIT(chan_bob, bridge);
CONF_EXIT(chan_charlie, bridge);
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -574,8 +671,8 @@ AST_TEST_DEFINE(test_cel_dial_unanswered)
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "NOANSWER");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ANSWER, "19,,NOANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ANSWER, "19,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ANSWER, "NOANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ANSWER, "");
return AST_TEST_PASS;
}
@ -609,8 +706,8 @@ AST_TEST_DEFINE(test_cel_dial_busy)
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "BUSY");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_BUSY, "17,,BUSY");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_BUSY, "17,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_BUSY, "BUSY");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_BUSY, "");
return AST_TEST_PASS;
}
@ -643,8 +740,8 @@ AST_TEST_DEFINE(test_cel_dial_congestion)
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CONGESTION");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_CONGESTION, "34,,CONGESTION");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_CONGESTION, "34,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_CONGESTION, "CONGESTION");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_CONGESTION, "");
return AST_TEST_PASS;
}
@ -677,8 +774,8 @@ AST_TEST_DEFINE(test_cel_dial_unavailable)
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CHANUNAVAIL");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ROUTE_DESTINATION, "3,,CHANUNAVAIL");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ROUTE_DESTINATION, "3,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ROUTE_DESTINATION, "CHANUNAVAIL");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ROUTE_DESTINATION, "");
return AST_TEST_PASS;
}
@ -712,8 +809,8 @@ AST_TEST_DEFINE(test_cel_dial_caller_cancel)
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CANCEL");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,CANCEL");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "CANCEL");
return AST_TEST_PASS;
}
@ -755,18 +852,18 @@ AST_TEST_DEFINE(test_cel_dial_parallel_failed)
/* Charlie is busy */
ast_channel_publish_dial(chan_caller, chan_charlie, NULL, "BUSY");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_BUSY, "17,,");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_BUSY, "");
/* David is congested */
ast_channel_publish_dial(chan_caller, chan_david, NULL, "CONGESTION");
HANGUP_CHANNEL(chan_david, AST_CAUSE_CONGESTION, "34,,");
HANGUP_CHANNEL(chan_david, AST_CAUSE_CONGESTION, "");
/* Bob is canceled */
ast_channel_publish_dial(chan_caller, chan_bob, NULL, "CANCEL");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
/* Alice hangs up */
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,BUSY");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "BUSY");
return AST_TEST_PASS;
}
@ -809,8 +906,8 @@ AST_TEST_DEFINE(test_cel_dial_answer_no_bridge)
EMULATE_APP_DATA(chan_caller, 2, "Wait", "1");
EMULATE_APP_DATA(chan_callee, 1, "Wait", "1");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -860,8 +957,8 @@ AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_a)
ast_bridge_depart(chan_callee);
APPEND_EVENT(chan_caller, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_callee));
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -910,8 +1007,8 @@ AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_b)
ast_bridge_depart(chan_callee);
APPEND_EVENT(chan_callee, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_caller));
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -970,28 +1067,431 @@ AST_TEST_DEFINE(test_cel_dial_answer_multiparty)
ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_TO_CONF, NULL, ast_channel_name(chan_bob), ast_channel_name(chan_david));
BRIDGE_TO_CONF(chan_charlie, chan_david, chan_bob, bridge);
ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_alice, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
CONF_ENTER_EVENT(chan_alice, bridge);
ast_test_validate(test, 0 == ast_bridge_depart(chan_alice));
APPEND_EVENT(chan_alice, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
CONF_EXIT(chan_alice, bridge);
CONF_EXIT(chan_bob, bridge);
CONF_EXIT(chan_charlie, bridge);
CONF_EXIT(chan_david, bridge);
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
AST_TEST_DEFINE(test_cel_blind_transfer)
{
RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
struct ast_party_caller alice_caller = ALICE_CALLERID;
struct ast_party_caller bob_caller = BOB_CALLERID;
switch (cmd) {
case TEST_INIT:
info->name = __func__;
info->category = TEST_CATEGORY;
info->summary = "Test blind transfers to an extension";
info->description =
"This test creates two channels, bridges them, and then"
" blind transfers the bridge to an extension.\n";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
bridge = ast_bridge_basic_new();
ast_test_validate(test, bridge != NULL);
CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
ANSWER_NO_APP(chan_alice);
ANSWER_NO_APP(chan_bob);
ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_alice));
BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_alice));
ast_bridge_transfer_blind(1, chan_alice, "transfer_extension", "transfer_context", NULL, NULL);
ast_test_validate(test, 0 == ast_bridge_depart(chan_bob));
APPEND_EVENT(chan_bob, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
ast_test_validate(test, 0 == ast_bridge_depart(chan_charlie));
APPEND_EVENT(chan_charlie, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
ast_test_validate(test, 0 == ast_bridge_depart(chan_david));
APPEND_EVENT(chan_david, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,ANSWER");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "16,,ANSWER");
HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "16,,");
return AST_TEST_PASS;
}
AST_TEST_DEFINE(test_cel_attended_transfer_bridges_swap)
{
RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_fred, NULL, safe_channel_release);
RAII_VAR(struct ast_bridge *, bridge1, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge *, bridge2, NULL, ao2_cleanup);
struct ast_party_caller alice_caller = ALICE_CALLERID;
struct ast_party_caller bob_caller = BOB_CALLERID;
struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
struct ast_party_caller fred_caller = ALICE_CALLERID;
switch (cmd) {
case TEST_INIT:
info->name = __func__;
info->category = TEST_CATEGORY;
info->summary = "Test attended transfers between two pairs of bridged parties";
info->description =
"This test creates four channels, places each pair in"
" a bridge, and then attended transfers the bridges"
" together.\n";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/* Create first set of bridged parties */
bridge1 = ast_bridge_basic_new();
ast_test_validate(test, bridge1 != NULL);
CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
ANSWER_NO_APP(chan_alice);
ANSWER_NO_APP(chan_bob);
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_bob, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_alice, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_alice));
/* Create second set of bridged parties */
bridge2 = ast_bridge_basic_new();
ast_test_validate(test, bridge2 != NULL);
CREATE_FRED_CHANNEL(chan_fred, &fred_caller);
CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
ANSWER_NO_APP(chan_fred);
ANSWER_NO_APP(chan_charlie);
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_charlie, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_fred, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_fred));
/* Perform attended transfer */
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_alice));
ast_bridge_transfer_attended(chan_alice, chan_fred);
do_sleep();
BRIDGE_TO_CONF(chan_charlie, chan_fred, chan_bob, bridge2);
CONF_EXIT_EVENT(chan_fred, bridge2);
ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_fred, bridge2);
CONF_EXIT(chan_bob, bridge2);
CONF_EXIT(chan_charlie, bridge2);
do_sleep();
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_fred, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
AST_TEST_DEFINE(test_cel_attended_transfer_bridges_merge)
{
RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_david, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_eve, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_fred, NULL, safe_channel_release);
RAII_VAR(struct ast_bridge *, bridge1, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge *, bridge2, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel_snapshot *, eve_tmp_snapshot, NULL, ao2_cleanup);
struct ast_party_caller alice_caller = ALICE_CALLERID;
struct ast_party_caller bob_caller = BOB_CALLERID;
struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
struct ast_party_caller david_caller = DAVID_CALLERID;
struct ast_party_caller eve_caller = EVE_CALLERID;
struct ast_party_caller fred_caller = EVE_CALLERID;
switch (cmd) {
case TEST_INIT:
info->name = __func__;
info->category = TEST_CATEGORY;
info->summary = "Test attended transfers between two pairs of"
" bridged parties that results in a bridge merge";
info->description =
"This test creates six channels, places each triplet"
" in a bridge, and then attended transfers the bridges"
" together causing a bridge merge.\n";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/* Create first set of bridged parties */
bridge1 = ast_bridge_basic_new();
ast_test_validate(test, bridge1 != NULL);
CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
CREATE_DAVID_CHANNEL(chan_david, &david_caller);
ANSWER_NO_APP(chan_alice);
ANSWER_NO_APP(chan_bob);
ANSWER_NO_APP(chan_david);
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_bob, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_alice, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_alice));
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_david, NULL, NULL, 0));
do_sleep();
BRIDGE_TO_CONF(chan_bob, chan_alice, chan_david, bridge1);
/* Create second set of bridged parties */
bridge2 = ast_bridge_basic_new();
ast_test_validate(test, bridge2 != NULL);
CREATE_FRED_CHANNEL(chan_fred, &fred_caller);
CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
CREATE_EVE_CHANNEL(chan_eve, &eve_caller);
ANSWER_NO_APP(chan_fred);
ANSWER_NO_APP(chan_charlie);
ANSWER_NO_APP(chan_eve);
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_charlie, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_fred, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_fred));
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_eve, NULL, NULL, 0));
do_sleep();
BRIDGE_TO_CONF(chan_charlie, chan_fred, chan_eve, bridge2);
/* Perform attended transfer */
CONF_EXIT_EVENT(chan_charlie, bridge2);
eve_tmp_snapshot = ast_channel_snapshot_create(chan_eve);
ast_bridge_transfer_attended(chan_alice, chan_fred);
do_sleep();
CONF_ENTER_EVENT(chan_charlie, bridge1);
/* Fred goes away */
CONF_EXIT_EVENT(chan_fred, bridge2);
CONF_EXIT_SNAPSHOT(eve_tmp_snapshot, bridge2);
CONF_ENTER_EVENT(chan_eve, bridge1);
/* Alice goes away */
CONF_EXIT_EVENT(chan_alice, bridge1);
ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_fred, bridge2);
CONF_EXIT(chan_bob, bridge1);
CONF_EXIT(chan_charlie, bridge1);
CONF_EXIT(chan_david, bridge1);
CONF_EXIT(chan_eve, bridge1);
do_sleep();
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_fred, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_eve, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
AST_TEST_DEFINE(test_cel_attended_transfer_bridges_link)
{
RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_david, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_eve, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_fred, NULL, safe_channel_release);
RAII_VAR(struct ast_bridge *, bridge1, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge *, bridge2, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel_snapshot *, eve_tmp_snapshot, NULL, ao2_cleanup);
struct ast_party_caller alice_caller = ALICE_CALLERID;
struct ast_party_caller bob_caller = BOB_CALLERID;
struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
struct ast_party_caller david_caller = DAVID_CALLERID;
struct ast_party_caller eve_caller = EVE_CALLERID;
struct ast_party_caller fred_caller = EVE_CALLERID;
switch (cmd) {
case TEST_INIT:
info->name = __func__;
info->category = TEST_CATEGORY;
info->summary = "Test attended transfers between two pairs of"
" bridged parties that results in a bridge merge";
info->description =
"This test creates six channels, places each triplet"
" in a bridge, and then attended transfers the bridges"
" together causing a bridge merge.\n";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/* Create first set of bridged parties */
bridge1 = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_MULTIMIX,
AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED | AST_BRIDGE_FLAG_SMART);
ast_test_validate(test, bridge1 != NULL);
CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
CREATE_DAVID_CHANNEL(chan_david, &david_caller);
ANSWER_NO_APP(chan_alice);
ANSWER_NO_APP(chan_bob);
ANSWER_NO_APP(chan_david);
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_bob, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_alice, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_alice));
ast_test_validate(test, 0 == ast_bridge_impart(bridge1, chan_david, NULL, NULL, 0));
do_sleep();
BRIDGE_TO_CONF(chan_bob, chan_alice, chan_david, bridge1);
/* Create second set of bridged parties */
bridge2 = ast_bridge_basic_new();
ast_test_validate(test, bridge2 != NULL);
CREATE_FRED_CHANNEL(chan_fred, &fred_caller);
CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
CREATE_EVE_CHANNEL(chan_eve, &eve_caller);
ANSWER_NO_APP(chan_fred);
ANSWER_NO_APP(chan_charlie);
ANSWER_NO_APP(chan_eve);
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_charlie, NULL, NULL, 0));
do_sleep();
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_fred, NULL, NULL, 0));
do_sleep();
APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_fred));
ast_test_validate(test, 0 == ast_bridge_impart(bridge2, chan_eve, NULL, NULL, 0));
do_sleep();
BRIDGE_TO_CONF(chan_charlie, chan_fred, chan_eve, bridge2);
/* Perform attended transfer */
ast_bridge_transfer_attended(chan_alice, chan_fred);
do_sleep();
/* Append dummy event for the link channel ;1 start */
APPEND_DUMMY_EVENT();
/* Append dummy event for the link channel ;2 start */
APPEND_DUMMY_EVENT();
/* Append dummy event for the link channel ;2 answer */
APPEND_DUMMY_EVENT();
ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_fred, bridge2);
/* Append dummy event for the link channel ;1 enter */
APPEND_DUMMY_EVENT();
/* Append dummy events for the link channel ;2 enter and Alice's exit,
* must both be dummies since they're racing */
APPEND_DUMMY_EVENT();
APPEND_DUMMY_EVENT();
/* Append dummy events for the link channel ;1 answer and Fred's exit,
* must both be dummies since they're racing */
APPEND_DUMMY_EVENT();
APPEND_DUMMY_EVENT();
CONF_EXIT(chan_bob, bridge1);
CONF_EXIT(chan_charlie, bridge2);
CONF_EXIT(chan_david, bridge1);
CONF_EXIT(chan_eve, bridge2);
do_sleep();
HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_fred, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
HANGUP_CHANNEL(chan_eve, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
AST_TEST_DEFINE(test_cel_dial_pickup)
{
RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
struct ast_party_caller caller = ALICE_CALLERID;
struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
switch (cmd) {
case TEST_INIT:
info->name = __func__;
info->category = TEST_CATEGORY;
info->summary = "Test call pickup";
info->description =
"Test CEL records for a call that is\n"
"inbound to Asterisk, executes some dialplan, and\n"
"is picked up.\n";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
CREATE_ALICE_CHANNEL(chan_caller, &caller);
EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
START_DIALED(chan_caller, chan_callee);
ast_channel_state_set(chan_caller, AST_STATE_RINGING);
CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
{
SCOPED_CHANNELLOCK(lock, chan_callee);
APPEND_EVENT(chan_callee, AST_CEL_PICKUP, NULL, NULL, ast_channel_name(chan_charlie));
ast_test_validate(test, 0 == ast_do_pickup(chan_charlie, chan_callee));
}
/* Hang up the masqueraded zombie */
HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
}
@ -1022,25 +1522,9 @@ static struct ast_event *ao2_dup_event(const struct ast_event *event)
return event_dup;
}
static int append_expected_event(
struct ast_channel *chan,
enum ast_cel_event_type type,
const char *userdefevname,
const char *extra, const char *peer)
static int append_event(struct ast_event *ev)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
RAII_VAR(struct ast_event *, ev, NULL, ast_free);
RAII_VAR(struct ast_event *, ao2_ev, NULL, ao2_cleanup);
snapshot = ast_channel_snapshot_create(chan);
if (!snapshot) {
return -1;
}
ev = ast_cel_create_event(snapshot, type, userdefevname, extra, peer);
if (!ev) {
return -1;
}
ao2_ev = ao2_dup_event(ev);
if (!ao2_ev) {
return -1;
@ -1050,6 +1534,49 @@ static int append_expected_event(
return 0;
}
static int append_dummy_event(void)
{
RAII_VAR(struct ast_event *, ev, NULL, ast_free);
RAII_VAR(struct ast_event *, ao2_ev, NULL, ao2_cleanup);
ev = ast_event_new(AST_EVENT_CUSTOM, AST_EVENT_IE_END);
if (!ev) {
return -1;
}
return append_event(ev);
}
static int append_expected_event_snapshot(
struct ast_channel_snapshot *snapshot,
enum ast_cel_event_type type,
const char *userdefevname,
struct ast_json *extra, const char *peer)
{
RAII_VAR(struct ast_event *, ev, NULL, ast_free);
ev = ast_cel_create_event(snapshot, type, userdefevname, extra, peer);
if (!ev) {
return -1;
}
return append_event(ev);
}
static int append_expected_event(
struct ast_channel *chan,
enum ast_cel_event_type type,
const char *userdefevname,
struct ast_json *extra, const char *peer)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
snapshot = ast_channel_snapshot_create(chan);
if (!snapshot) {
return -1;
}
return append_expected_event_snapshot(snapshot, type, userdefevname, extra, peer);
}
ast_mutex_t sync_lock;
ast_cond_t sync_out;
@ -1149,19 +1676,24 @@ static int match_ie_val(
return 0;
}
static int events_are_equal(struct ast_event *event1, struct ast_event *event2)
static int events_are_equal(struct ast_event *received, struct ast_event *expected)
{
struct ast_event_iterator iterator;
int res;
for (res = ast_event_iterator_init(&iterator, event1); !res; res = ast_event_iterator_next(&iterator)) {
if (ast_event_get_type(expected) == AST_EVENT_CUSTOM) {
/* this event is flagged as a wildcard match */
return 1;
}
for (res = ast_event_iterator_init(&iterator, received); !res; res = ast_event_iterator_next(&iterator)) {
/* XXX ignore sec/usec for now */
/* ignore EID */
int ie_type = ast_event_iterator_get_ie_type(&iterator);
if (ie_type != AST_EVENT_IE_CEL_EVENT_TIME_USEC
&& ie_type != AST_EVENT_IE_EID
&& ie_type != AST_EVENT_IE_CEL_EVENT_TIME
&& !match_ie_val(event1, event2, ie_type)) {
&& !match_ie_val(received, expected, ie_type)) {
ast_log(LOG_ERROR, "Failed matching on field %s\n", ast_event_get_ie_type_name(ie_type));
return 0;
}
@ -1244,8 +1776,8 @@ static int check_events(struct ao2_container *local_expected, struct ao2_contain
return -1;
}
if (debug) {
ast_log(LOG_ERROR, "Compared events successfully\n");
dump_event(ex_event);
ast_log(LOG_ERROR, "Compared events successfully%s\n", ast_event_get_type(ex_event) == AST_EVENT_CUSTOM ? " (wildcard match)" : "");
dump_event(rx_event);
}
ao2_cleanup(rx_event);
ao2_cleanup(ex_event);
@ -1370,6 +1902,13 @@ static int unload_module(void)
AST_TEST_UNREGISTER(test_cel_dial_answer_twoparty_bridge_b);
AST_TEST_UNREGISTER(test_cel_dial_answer_multiparty);
AST_TEST_UNREGISTER(test_cel_blind_transfer);
AST_TEST_UNREGISTER(test_cel_attended_transfer_bridges_swap);
AST_TEST_UNREGISTER(test_cel_attended_transfer_bridges_merge);
AST_TEST_UNREGISTER(test_cel_attended_transfer_bridges_link);
AST_TEST_UNREGISTER(test_cel_dial_pickup);
ast_channel_unregister(&test_cel_chan_tech);
ao2_cleanup(cel_test_config);
@ -1405,6 +1944,9 @@ static int load_module(void)
cel_test_config->events |= 1<<AST_CEL_BRIDGE_TO_CONF;
cel_test_config->events |= 1<<AST_CEL_CONF_ENTER;
cel_test_config->events |= 1<<AST_CEL_CONF_EXIT;
cel_test_config->events |= 1<<AST_CEL_BLINDTRANSFER;
cel_test_config->events |= 1<<AST_CEL_ATTENDEDTRANSFER;
cel_test_config->events |= 1<<AST_CEL_PICKUP;
ast_channel_register(&test_cel_chan_tech);
@ -1430,6 +1972,13 @@ static int load_module(void)
AST_TEST_REGISTER(test_cel_dial_answer_twoparty_bridge_b);
AST_TEST_REGISTER(test_cel_dial_answer_multiparty);
AST_TEST_REGISTER(test_cel_blind_transfer);
AST_TEST_REGISTER(test_cel_attended_transfer_bridges_swap);
AST_TEST_REGISTER(test_cel_attended_transfer_bridges_merge);
AST_TEST_REGISTER(test_cel_attended_transfer_bridges_link);
AST_TEST_REGISTER(test_cel_dial_pickup);
/* ast_test_register_* has to happen after AST_TEST_REGISTER */
/* Verify received vs expected events and clean things up after every test */
ast_test_register_init(TEST_CATEGORY, test_cel_init_cb);