[asterisk-commits] mjordan: trunk r393130 - in /trunk: include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jun 28 10:51:08 CDT 2013


Author: mjordan
Date: Fri Jun 28 10:50:56 2013
New Revision: 393130

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393130
Log:
Better handle parking in CDRs

Parking typically occurs when a channel is transferred to a parking extension.
When this occurs, the channel never actually hits the dialplan if the extension
it was transferred to was a "parking extension", that is, the extension in
the first priority calls the Park application. Instead, the channel is
immediately sent into the holding bridge acting as the parking bridge.

This is problematic.

Because we never go out to the dialplan, the CDRs won't transition properly
and the application field will not be set to "Park". CDRs typically swallow
holding bridges, so the CDR itself won't even be generated.

This patch handles this by pulling out the holding bridge handling into its
own CDR state. CDRs now have an explicit parking state that accounts for this
specific subclass of the holding bridge. In addition, we handle the parking
stasis message to set application specific data on the CDR such that the
last known application for the CDR properly reflects "Park".

This is a bit sad since we're working around the odd internal implementation
of parking that exists in Asterisk (and that we had to maintain in order to
continue to meet some odd use cases of parking), but at least the code to
handle that is where it belongs: in CDRs as opposed to sprinkled liberally
throughout the codebase.

This patch also properly clears the OUTBOUND channel flag from a channel when
it leaves a bridge, and tweaks up dialing handling to properly compare the
correct CDR with the channel calling/being dialed.



Modified:
    trunk/include/asterisk/cdr.h
    trunk/include/asterisk/parking.h
    trunk/main/asterisk.c
    trunk/main/bridging.c
    trunk/main/cdr.c

Modified: trunk/include/asterisk/cdr.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/cdr.h?view=diff&rev=393130&r1=393129&r2=393130
==============================================================================
--- trunk/include/asterisk/cdr.h (original)
+++ trunk/include/asterisk/cdr.h Fri Jun 28 10:50:56 2013
@@ -99,6 +99,9 @@
  * has hung up, the state transitions to Finalized
  * \li If a \ref ast_bridge_blob_type is received indicating a Bridge Enter, the
  * state transitions to Bridge
+ * \li If a \ref ast_bridge_blob_type message indicating an entrance to a
+ * holding bridge with a subclass type of "parking" is received, the CDR is
+ * transitioned to the Parked state.
  *
  * \par Dial
  *
@@ -138,6 +141,9 @@
  * if we have a Party B. Otherwise, we transition to the Single state.
  * \li If a \ref ast_bridge_blob_type is received indicating a Bridge Enter, the
  * state transitions to Bridge (through the Dial state)
+ * \li If a \ref ast_bridge_blob_type message indicating an entrance to a
+ * holding bridge with a subclass type of "parking" is received, the CDR is
+ * transitioned to the Parked state.
  *
  * \par Bridge
  *
@@ -174,6 +180,31 @@
  * \li If a \ref ast_bridge_blob_type message indicating a leave is received,
  * the state transitions to the Pending state
  *
+ * \par Parked
+ *
+ * Parking is technically just another bridge in the Asterisk bridging
+ * framework. Unlike other bridges, however there are several key distinctions:
+ * \li With normal bridges, you want to show paths of communication between
+ * the participants. In parking, however, each participant is independent.
+ * From the perspective of a CDR, a call in parking should look like a dialplan
+ * application just executed.
+ * \li Holding bridges are typically items using in more complex applications,
+ * and so we usually don't want to show them. However, with Park, there is no
+ * application execution - often, a channel will be put directly into the
+ * holding bridge, bypassing the dialplan. This occurs when a call is blind
+ * transferred to a parking extension.
+ *
+ * As such, if a channel enters a bridge and that happens to be a holding bridge
+ * with a subclass type of "parking", we transition the CDR into the Parked
+ * state. The parking Stasis message updates the application name and data to
+ * reflect that the channel is in parking. When this occurs, a special flag is
+ * set on the CDR that prevents the application name from being updates by
+ * subsequent channel snapshot updates.
+ *
+ * The following transitions can occur while in the Parked state:
+ * \li If a \ref ast_bridge_blob_type message indicating a leave is received,
+ * the state transitions to the Pending state
+ *
  * \par Pending
  *
  * After a channel leaves a bridge, we often don't know what's going to happen
@@ -190,6 +221,9 @@
  * Context/Extension/Priority, a new CDR is created and transitioned to the
  * Single state. If the update indicates that the party has been hung up, the
  * CDR is transitioned to the Finalized state.
+ * \li If a \ref ast_bridge_blob_type message indicating an entrance to a
+ * holding bridge with a subclass type of "parking" is received, the CDR is
+ * transitioned to the Parked state.
  *
  * \par Finalized
  *
@@ -203,19 +237,19 @@
 
 /*! \brief CDR engine settings */
 enum ast_cdr_settings {
-	CDR_ENABLED = 1 << 0,				/*< Enable CDRs */
-	CDR_BATCHMODE = 1 << 1,				/*< Whether or not we should dispatch CDRs in batches */
-	CDR_UNANSWERED = 1 << 2,			/*< Log unanswered CDRs */
-	CDR_CONGESTION = 1 << 3,			/*< Treat congestion as if it were a failed call */
-	CDR_END_BEFORE_H_EXTEN = 1 << 4,	/*< End the CDR before the 'h' extension runs */
-	CDR_INITIATED_SECONDS = 1 << 5,		/*< Include microseconds into the billing time */
-	CDR_DEBUG = 1 << 6,					/*< Enables extra debug statements */
+	CDR_ENABLED = 1 << 0,               /*< Enable CDRs */
+	CDR_BATCHMODE = 1 << 1,             /*< Whether or not we should dispatch CDRs in batches */
+	CDR_UNANSWERED = 1 << 2,            /*< Log unanswered CDRs */
+	CDR_CONGESTION = 1 << 3,            /*< Treat congestion as if it were a failed call */
+	CDR_END_BEFORE_H_EXTEN = 1 << 4,    /*< End the CDR before the 'h' extension runs */
+	CDR_INITIATED_SECONDS = 1 << 5,     /*< Include microseconds into the billing time */
+	CDR_DEBUG = 1 << 6,                 /*< Enables extra debug statements */
 };
 
 /*! \brief CDR Batch Mode settings */
 enum ast_cdr_batch_mode_settings {
-	BATCH_MODE_SCHEDULER_ONLY = 1 << 0,	/*< Don't spawn a thread to handle the batches - do it on the scheduler */
-	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,	/*< During safe shutdown, submit the batched CDRs */
+	BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*< Don't spawn a thread to handle the batches - do it on the scheduler */
+	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,  /*< During safe shutdown, submit the batched CDRs */
 };
 
 /*!
@@ -223,24 +257,25 @@
  * state of a CDR object based on these flags.
  */
 enum ast_cdr_options {
-	AST_CDR_FLAG_KEEP_VARS = (1 << 0),			/*< Copy variables during the operation */
-	AST_CDR_FLAG_DISABLE = (1 << 1),			/*< Disable the current CDR */
-	AST_CDR_FLAG_DISABLE_ALL = (3 << 1),		/*< Disable the CDR and all future CDRs */
-	AST_CDR_FLAG_PARTY_A = (1 << 3),			/*< Set the channel as party A */
-	AST_CDR_FLAG_FINALIZE = (1 << 4),			/*< Finalize the current CDRs */
-	AST_CDR_FLAG_SET_ANSWER = (1 << 5),			/*< If the channel is answered, set the answer time to now */
-	AST_CDR_FLAG_RESET = (1 << 6),				/*< If set, set the start and answer time to now */
+	AST_CDR_FLAG_KEEP_VARS = (1 << 0),   /*< Copy variables during the operation */
+	AST_CDR_FLAG_DISABLE = (1 << 1),     /*< Disable the current CDR */
+	AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*< Disable the CDR and all future CDRs */
+	AST_CDR_FLAG_PARTY_A = (1 << 3),     /*< Set the channel as party A */
+	AST_CDR_FLAG_FINALIZE = (1 << 4),    /*< Finalize the current CDRs */
+	AST_CDR_FLAG_SET_ANSWER = (1 << 5),  /*< If the channel is answered, set the answer time to now */
+	AST_CDR_FLAG_RESET = (1 << 6),       /*< If set, set the start and answer time to now */
+	AST_CDR_LOCK_APP = (1 << 7),         /*< Prevent any further changes to the application field/data field for this CDR */
 };
 
 /*!
  * \brief CDR Flags - Disposition
  */
 enum ast_cdr_disposition {
-	AST_CDR_NOANSWER = 0,
-	AST_CDR_NULL     = (1 << 0),
-	AST_CDR_FAILED   = (1 << 1),
-	AST_CDR_BUSY     = (1 << 2),
-	AST_CDR_ANSWERED = (1 << 3),
+	AST_CDR_NOANSWER   = 0,
+	AST_CDR_NULL       = (1 << 0),
+	AST_CDR_FAILED     = (1 << 1),
+	AST_CDR_BUSY       = (1 << 2),
+	AST_CDR_ANSWERED   = (1 << 3),
 	AST_CDR_CONGESTION = (1 << 4),
 };
 

Modified: trunk/include/asterisk/parking.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/parking.h?view=diff&rev=393130&r1=393129&r2=393130
==============================================================================
--- trunk/include/asterisk/parking.h (original)
+++ trunk/include/asterisk/parking.h Fri Jun 28 10:50:56 2013
@@ -55,6 +55,8 @@
 		AST_STRING_FIELD(parkinglot);                /*!< Name of the parking lot used to park the parkee */
 	);
 };
+
+struct ast_exten;
 
 /*!
  * \brief Constructor for parked_call_payload objects

Modified: trunk/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/asterisk.c?view=diff&rev=393130&r1=393129&r2=393130
==============================================================================
--- trunk/main/asterisk.c (original)
+++ trunk/main/asterisk.c Fri Jun 28 10:50:56 2013
@@ -4333,6 +4333,11 @@
 		exit(1);
 	}
 
+	if (ast_parking_stasis_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
 	if (ast_cdr_engine_init()) {
 		printf("%s", term_quit());
 		exit(1);
@@ -4362,11 +4367,6 @@
 	}
 
 	if (load_pbx()) {
-		printf("%s", term_quit());
-		exit(1);
-	}
-
-	if (ast_parking_stasis_init()) {
 		printf("%s", term_quit());
 		exit(1);
 	}

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=393130&r1=393129&r2=393130
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Fri Jun 28 10:50:56 2013
@@ -654,7 +654,7 @@
 	 * outgoing channel, clear the outgoing flag.
 	 */
 	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING)
-			&& (ast_channel_softhangup_internal_flag(bridge_channel->chan) & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) {
+			&& (!ast_check_hangup(bridge_channel->chan))) {
 		ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING);
 	}
 

Modified: trunk/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/cdr.c?view=diff&rev=393130&r1=393129&r2=393130
==============================================================================
--- trunk/main/cdr.c (original)
+++ trunk/main/cdr.c Fri Jun 28 10:50:56 2013
@@ -65,6 +65,7 @@
 #include "asterisk/data.h"
 #include "asterisk/config_options.h"
 #include "asterisk/json.h"
+#include "asterisk/parking.h"
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/stasis_bridging.h"
@@ -329,6 +330,9 @@
 /*! \brief Our subscription for channels */
 static struct stasis_subscription *channel_subscription;
 
+/*! \brief Our subscription for parking */
+static struct stasis_subscription *parking_subscription;
+
 /*! \brief The parent topic for all topics we want to aggregate for CDRs */
 static struct stasis_topic *cdr_topic;
 
@@ -416,15 +420,34 @@
 	 * of this channel into the bridge is handled by the higher level message
 	 * handler.
 	 *
+	 * Note that this handler is for when a channel enters into a "normal"
+	 * bridge, where people actually talk to each other. Parking is its own
+	 * thing.
+	 *
 	 * \param cdr The \ref cdr_object
 	 * \param bridge The bridge that the Party A just entered into
 	 * \param channel The \ref ast_channel_snapshot for this CDR's Party A
 	 *
 	 * \retval 0 This CDR found a Party B for itself and updated it, or there
 	 * was no Party B to find (we're all alone)
-	 * \retval 1 This CDR couldn't find a Party B, and there were options
+	 * \retval 1 This CDR couldn't find a Party B and channels were in the bridge
 	 */
 	int (* const process_bridge_enter)(struct cdr_object *cdr,
+			struct ast_bridge_snapshot *bridge,
+			struct ast_channel_snapshot *channel);
+
+	/*!
+	 * \brief Process entering into a parking bridge.
+	 *
+	 * \param cdr The \ref cdr_object
+	 * \param bridge The parking bridge that Party A just entered into
+	 * \param channel The \ref ast_channel_snapshot for this CDR's Party A
+	 *
+	 * \retval 0 This CDR successfully transitioned itself into the parked state
+	 * \retval 1 This CDR couldn't handle the parking transition and we need a
+	 *  new CDR.
+	 */
+	int (* const process_parking_bridge_enter)(struct cdr_object *cdr,
 			struct ast_bridge_snapshot *bridge,
 			struct ast_channel_snapshot *channel);
 
@@ -441,16 +464,30 @@
 	int (* const process_bridge_leave)(struct cdr_object *cdr,
 			struct ast_bridge_snapshot *bridge,
 			struct ast_channel_snapshot *channel);
+
+	/*!
+	 * \brief Process an update informing us that the channel got itself parked
+	 *
+	 * \param cdr The \ref cdr_object
+	 * \param channel The parking information for this CDR's party A
+	 *
+	 * \retval 0 This CDR successfully parked itself
+	 * \retval 1 This CDR couldn't handle the park
+	 */
+	int (* const process_parked_channel)(struct cdr_object *cdr,
+			struct ast_parked_call_payload *parking_info);
 };
 
 static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
+static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info);
 
 static void single_state_init_function(struct cdr_object *cdr);
 static void single_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
 static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
  * \brief The virtual table for the Single state.
@@ -471,7 +508,9 @@
 	.process_dial_begin = single_state_process_dial_begin,
 	.process_dial_end = base_process_dial_end,
 	.process_bridge_enter = single_state_process_bridge_enter,
+	.process_parking_bridge_enter = single_state_process_parking_bridge_enter,
 	.process_bridge_leave = base_process_bridge_leave,
+	.process_parked_channel = base_process_parked_channel,
 };
 
 static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
@@ -505,6 +544,7 @@
 static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
 static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int dialed_pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
  * \brief The virtual table for the Dialed Pending state.
@@ -529,7 +569,9 @@
 	.process_party_a = dialed_pending_state_process_party_a,
 	.process_dial_begin = dialed_pending_state_process_dial_begin,
 	.process_bridge_enter = dialed_pending_state_process_bridge_enter,
+	.process_parking_bridge_enter = dialed_pending_state_process_parking_bridge_enter,
 	.process_bridge_leave = base_process_bridge_leave,
+	.process_parked_channel = base_process_parked_channel,
 };
 
 static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
@@ -550,12 +592,14 @@
 	.process_party_a = base_process_party_a,
 	.process_party_b = bridge_state_process_party_b,
 	.process_bridge_leave = bridge_state_process_bridge_leave,
+	.process_parked_channel = base_process_parked_channel,
 };
 
 static void pending_state_init_function(struct cdr_object *cdr);
 static int pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
 static int pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
  * \brief The virtual table for the Pending state
@@ -576,7 +620,27 @@
 	.process_dial_begin = pending_state_process_dial_begin,
 	.process_dial_end = base_process_dial_end,
 	.process_bridge_enter = pending_state_process_bridge_enter,
+	.process_parking_bridge_enter = pending_state_process_parking_bridge_enter,
 	.process_bridge_leave = base_process_bridge_leave,
+	.process_parked_channel = base_process_parked_channel,
+};
+
+static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+
+/*!
+ * \brief The virtual table for the Parked state
+ *
+ * Parking is weird. Unlike typical bridges, it has to be treated somewhat
+ * uniquely - a channel in a parking bridge (which is a subclass of a holding
+ * bridge) has to be handled as if the channel went into an application.
+ * However, when the channel comes out, we need a new CDR - unlike the Single
+ * state.
+ */
+struct cdr_object_fn_table parked_state_fn_table = {
+	.name = "Parked",
+	.process_party_a = base_process_party_a,
+	.process_bridge_leave = parked_state_process_bridge_leave,
+	.process_parked_channel = base_process_parked_channel,
 };
 
 static void finalized_state_init_function(struct cdr_object *cdr);
@@ -1239,7 +1303,9 @@
 	 * of "AppDialX". Prevent that, and any other application changes we might not want
 	 * here.
 	 */
-	if (!ast_strlen_zero(snapshot->appl) && (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))) {
+	if (!ast_strlen_zero(snapshot->appl)
+			&& (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))
+			&& !ast_test_flag(&cdr->flags, AST_CDR_LOCK_APP)) {
 		ast_string_field_set(cdr, appl, snapshot->appl);
 		ast_string_field_set(cdr, data, snapshot->data);
 	}
@@ -1262,6 +1328,26 @@
 {
 	/* In general, most things shouldn't get a dial end. */
 	ast_assert(0);
+	return 0;
+}
+
+static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info)
+{
+	char park_info[128];
+
+	ast_assert(!strcmp(parking_info->parkee->name, cdr->party_a.snapshot->name));
+
+	/* Update Party A information regardless */
+	cdr->fn_table->process_party_a(cdr, parking_info->parkee);
+
+	/* Fake out where we're parked */
+	ast_string_field_set(cdr, appl, "Park");
+	snprintf(park_info, sizeof(park_info), "%s:%u", parking_info->parkinglot, parking_info->parkingspace);
+	ast_string_field_set(cdr, data, park_info);
+
+	/* Prevent any further changes to the App/Data fields for this record */
+	ast_set_flag(&cdr->flags, AST_CDR_LOCK_APP);
+
 	return 0;
 }
 
@@ -1394,6 +1480,13 @@
 	/* Success implies that we have a Party B */
 	return success;
 }
+
+static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	cdr_object_transition_state(cdr, &parked_state_fn_table);
+	return 0;
+}
+
 
 /* DIAL STATE */
 
@@ -1567,14 +1660,18 @@
 	return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
 }
 
+static int dialed_pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	/* We can't handle this as we have a Party B - ask for a new one */
+	return 1;
+}
+
 static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
 {
-	struct cdr_object *new_cdr;
-
 	cdr_object_transition_state(cdr, &finalized_state_fn_table);
-	new_cdr = cdr_object_create_and_append(cdr);
-	cdr_object_transition_state(cdr, &single_state_fn_table);
-	return new_cdr->fn_table->process_dial_begin(cdr, caller, peer);
+
+	/* Ask for a new CDR */
+	return 1;
 }
 
 /* BRIDGE STATE */
@@ -1645,6 +1742,25 @@
 	cdr_object_transition_state(cdr, &single_state_fn_table);
 	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
 	return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
+}
+
+static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	cdr_object_transition_state(cdr, &single_state_fn_table);
+	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
+	return cdr->fn_table->process_parking_bridge_enter(cdr, bridge, channel);
+}
+
+/* PARKED STATE */
+
+static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	if (strcmp(cdr->party_a.snapshot->name, channel->name)) {
+		return 1;
+	}
+	cdr_object_transition_state(cdr, &finalized_state_fn_table);
+
+	return 0;
 }
 
 /* FINALIZED STATE */
@@ -1688,16 +1804,14 @@
 	struct ast_multi_channel_blob *payload = stasis_message_data(message);
 	struct ast_channel_snapshot *caller;
 	struct ast_channel_snapshot *peer;
+	struct ast_channel_snapshot *party_a_snapshot;
+	struct ast_channel_snapshot *party_b_snapshot;
 	struct cdr_object_snapshot *party_a;
-	struct cdr_object_snapshot *party_b;
 	struct cdr_object *it_cdr;
 	struct ast_json *dial_status_blob;
 	const char *dial_status = NULL;
 	int res = 1;
 
-	CDR_DEBUG(mod_cfg, "Dial message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
-	ast_assert(payload != NULL);
-
 	caller = ast_multi_channel_blob_get_channel(payload, "caller");
 	peer = ast_multi_channel_blob_get_channel(payload, "peer");
 	if (!peer && !caller) {
@@ -1708,6 +1822,13 @@
 		dial_status = ast_json_string_get(dial_status_blob);
 	}
 
+	CDR_DEBUG(mod_cfg, "Dial %s message for %s, %s: %u.%08u\n",
+			ast_strlen_zero(dial_status) ? "Begin" : "End",
+			caller ? caller->name : "(none)",
+			peer ? peer->name : "(none)",
+			(unsigned int)stasis_message_timestamp(message)->tv_sec,
+			(unsigned int)stasis_message_timestamp(message)->tv_usec);
+
 	/* Figure out who is running this show */
 	if (caller) {
 		cdr_caller = ao2_find(active_cdrs_by_channel, caller->name, OBJ_KEY);
@@ -1716,22 +1837,24 @@
 		cdr_peer = ao2_find(active_cdrs_by_channel, peer->name, OBJ_KEY);
 	}
 	if (cdr_caller && cdr_peer) {
-		party_a = cdr_object_pick_party_a(&cdr_caller->party_a, &cdr_peer->party_a);
-		if (!strcmp(party_a->snapshot->name, cdr_caller->party_a.snapshot->name)) {
+		party_a = cdr_object_pick_party_a(&cdr_caller->last->party_a, &cdr_peer->last->party_a);
+		if (!strcmp(party_a->snapshot->name, cdr_caller->last->party_a.snapshot->name)) {
 			cdr = cdr_caller;
-			party_b = &cdr_peer->party_a;
+			party_a_snapshot = caller;
+			party_b_snapshot = peer;
 		} else {
 			cdr = cdr_peer;
-			party_b = &cdr_caller->party_a;
+			party_a_snapshot = peer;
+			party_b_snapshot = caller;
 		}
 	} else if (cdr_caller) {
 		cdr = cdr_caller;
-		party_a = &cdr_caller->party_a;
-		party_b = NULL;
+		party_a_snapshot = caller;
+		party_b_snapshot = NULL;
 	} else if (cdr_peer) {
 		cdr = cdr_peer;
-		party_a = NULL;
-		party_b = &cdr_peer->party_a;
+		party_a_snapshot = NULL;
+		party_b_snapshot = peer;
 	} else {
 		return;
 	}
@@ -1744,22 +1867,22 @@
 			}
 			CDR_DEBUG(mod_cfg, "%p - Processing Dial Begin message for channel %s, peer %s\n",
 					cdr,
-					party_a ? party_a->snapshot->name : "(none)",
-					party_b ? party_b->snapshot->name : "(none)");
+					party_a_snapshot ? party_a_snapshot->name : "(none)",
+					party_b_snapshot ? party_b_snapshot->name : "(none)");
 			res &= it_cdr->fn_table->process_dial_begin(it_cdr,
-					party_a ? party_a->snapshot : NULL,
-					party_b ? party_b->snapshot : NULL);
+					party_a_snapshot,
+					party_b_snapshot);
 		} else {
 			if (!it_cdr->fn_table->process_dial_end) {
 				continue;
 			}
 			CDR_DEBUG(mod_cfg, "%p - Processing Dial End message for channel %s, peer %s\n",
 					cdr,
-					party_a ? party_a->snapshot->name : "(none)",
-					party_b ? party_b->snapshot->name : "(none)");
+					party_a_snapshot ? party_a_snapshot->name : "(none)",
+					party_b_snapshot ? party_b_snapshot->name : "(none)");
 			it_cdr->fn_table->process_dial_end(it_cdr,
-					party_a ? party_a->snapshot : NULL,
-					party_b ? party_b->snapshot : NULL,
+					party_a_snapshot,
+					party_b_snapshot,
 					dial_status);
 		}
 	}
@@ -1773,8 +1896,8 @@
 			return;
 		}
 		new_cdr->fn_table->process_dial_begin(new_cdr,
-				party_a ? party_a->snapshot : NULL,
-				party_b ? party_b->snapshot : NULL);
+				party_a_snapshot,
+				party_b_snapshot);
 	}
 	ao2_unlock(cdr);
 }
@@ -1932,7 +2055,9 @@
 				/* We're not hung up and we have a new snapshot - we need a new CDR */
 				struct cdr_object *new_cdr;
 				new_cdr = cdr_object_create_and_append(cdr);
-				new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
+				if (new_cdr) {
+					new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
+				}
 			}
 		} else {
 			CDR_DEBUG(mod_cfg, "%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
@@ -1998,7 +2123,7 @@
 	/* Ignore holding bridge technology messages. We treat this simply as an application
 	 * that a channel enters into.
 	 */
-	if (!strcmp(bridge->technology, "holding_bridge")) {
+	if (!strcmp(bridge->technology, "holding_bridge") && strcmp(bridge->subclass, "parking")) {
 		return 1;
 	}
 	return 0;
@@ -2006,8 +2131,10 @@
 
 /*!
  * \brief Handler for when a channel leaves a bridge
- * \param bridge The \ref ast_bridge_snapshot representing the bridge
- * \param channel The \ref ast_channel_snapshot representing the channel
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message - hopefully a bridge one!
  */
 static void handle_bridge_leave_message(void *data, struct stasis_subscription *sub,
 		struct stasis_topic *topic, struct stasis_message *message)
@@ -2032,7 +2159,10 @@
 		return;
 	}
 
-	CDR_DEBUG(mod_cfg, "Bridge Leave message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
+	CDR_DEBUG(mod_cfg, "Bridge Leave message for %s: %u.%08u\n",
+			channel->name,
+			(unsigned int)stasis_message_timestamp(message)->tv_sec,
+			(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
 	if (!cdr) {
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
@@ -2057,19 +2187,25 @@
 		return;
 	}
 
-	ao2_unlink(active_cdrs_by_bridge, cdr);
+	if (strcmp(bridge->subclass, "parking")) {
+		ao2_unlink(active_cdrs_by_bridge, cdr);
+	}
 
 	/* Create a new pending record. If the channel decides to do something else,
 	 * the pending record will handle it - otherwise, if gets dropped.
 	 */
 	pending_cdr = cdr_object_create_and_append(cdr);
-	cdr_object_transition_state(pending_cdr, &bridged_pending_state_fn_table);
+	if (pending_cdr) {
+		cdr_object_transition_state(pending_cdr, &bridged_pending_state_fn_table);
+	}
 	ao2_unlock(cdr);
 
-	/* Party B */
-	ao2_callback(active_cdrs_by_bridge, OBJ_NODATA,
-			cdr_object_party_b_left_bridge_cb,
-			&leave_data);
+	if (strcmp(bridge->subclass, "parking")) {
+		/* Party B */
+		ao2_callback(active_cdrs_by_bridge, OBJ_NODATA,
+				cdr_object_party_b_left_bridge_cb,
+				&leave_data);
+	}
 }
 
 struct bridge_candidate {
@@ -2239,6 +2375,9 @@
 	struct cdr_object *new_cdr;
 
 	new_cdr = cdr_object_create_and_append(cdr);
+	if (!new_cdr) {
+		return;
+	}
 	cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
 	cdr_object_check_party_a_answer(new_cdr);
 	ast_string_field_set(new_cdr, bridge, cdr->bridge);
@@ -2340,38 +2479,60 @@
 	return;
 }
 
-/*!
- * \brief Handler for Stasis-Core bridge enter messages
- * \param data Passed on
- * \param sub The stasis subscription for this message callback
- * \param topic The topic this message was published for
- * \param message The message - hopefully a bridge one!
- */
-static void handle_bridge_enter_message(void *data, struct stasis_subscription *sub,
-		struct stasis_topic *topic, struct stasis_message *message)
-{
-	struct ast_bridge_blob *update = stasis_message_data(message);
-	struct ast_bridge_snapshot *bridge = update->bridge;
-	struct ast_channel_snapshot *channel = update->channel;
-	RAII_VAR(struct cdr_object *, cdr,
-			ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
-			ao2_cleanup);
+/*! \brief Handle entering into a parking bridge
+ * \param cdr The CDR to operate on
+ * \param bridge The bridge the channel just entered
+ * \param channel The channel snapshot
+ */
+static void handle_parking_bridge_enter_message(struct cdr_object *cdr,
+		struct ast_bridge_snapshot *bridge,
+		struct ast_channel_snapshot *channel)
+{
+	RAII_VAR(struct module_config *, mod_cfg,
+			ao2_global_obj_ref(module_configs), ao2_cleanup);
+	int res = 1;
+	struct cdr_object *it_cdr;
+	struct cdr_object *new_cdr;
+
+	ao2_lock(cdr);
+
+	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+		if (it_cdr->fn_table->process_parking_bridge_enter) {
+			res &= it_cdr->fn_table->process_parking_bridge_enter(it_cdr, bridge, channel);
+		}
+		if (it_cdr->fn_table->process_party_a) {
+			CDR_DEBUG(mod_cfg, "%p - Updating Party A %s snapshot\n", it_cdr,
+					channel->name);
+			it_cdr->fn_table->process_party_a(it_cdr, channel);
+		}
+	}
+
+	if (res) {
+		/* No one handled it - we need a new one! */
+		new_cdr = cdr_object_create_and_append(cdr);
+		if (new_cdr) {
+			/* Let the single state transition us to Parked */
+			cdr_object_transition_state(new_cdr, &single_state_fn_table);
+			new_cdr->fn_table->process_parking_bridge_enter(new_cdr, bridge, channel);
+		}
+	}
+	ao2_unlock(cdr);
+}
+
+/*! \brief Handle a bridge enter message for a 'normal' bridge
+ * \param cdr The CDR to operate on
+ * \param bridge The bridge the channel just entered
+ * \param channel The channel snapshot
+ */
+static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
+		struct ast_bridge_snapshot *bridge,
+		struct ast_channel_snapshot *channel)
+{
 	RAII_VAR(struct module_config *, mod_cfg,
 			ao2_global_obj_ref(module_configs), ao2_cleanup);
 	int res = 1;
 	struct cdr_object *it_cdr;
 	struct cdr_object *handled_cdr = NULL;
-
-	if (filter_bridge_messages(bridge)) {
-		return;
-	}
-
-	CDR_DEBUG(mod_cfg, "Bridge Enter message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
-
-	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
-		return;
-	}
 
 	ao2_lock(cdr);
 
@@ -2414,6 +2575,96 @@
 
 	ao2_link(active_cdrs_by_bridge, cdr);
 	ao2_unlock(cdr);
+}
+
+/*!
+ * \brief Handler for Stasis-Core bridge enter messages
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message - hopefully a bridge one!
+ */
+static void handle_bridge_enter_message(void *data, struct stasis_subscription *sub,
+		struct stasis_topic *topic, struct stasis_message *message)
+{
+	struct ast_bridge_blob *update = stasis_message_data(message);
+	struct ast_bridge_snapshot *bridge = update->bridge;
+	struct ast_channel_snapshot *channel = update->channel;
+	RAII_VAR(struct cdr_object *, cdr,
+			ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
+			ao2_cleanup);
+	RAII_VAR(struct module_config *, mod_cfg,
+			ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	if (filter_bridge_messages(bridge)) {
+		return;
+	}
+
+	CDR_DEBUG(mod_cfg, "Bridge Enter message for channel %s: %u.%08u\n",
+			channel->name,
+			(unsigned int)stasis_message_timestamp(message)->tv_sec,
+			(unsigned int)stasis_message_timestamp(message)->tv_usec);
+
+	if (!cdr) {
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+		return;
+	}
+
+	if (!strcmp(bridge->subclass, "parking")) {
+		handle_parking_bridge_enter_message(cdr, bridge, channel);
+	} else {
+		handle_standard_bridge_enter_message(cdr, bridge, channel);
+	}
+}
+
+/*!
+ * \brief Handler for when a channel is parked
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message about who got parked
+ * */
+static void handle_parked_call_message(void *data, struct stasis_subscription *sub,
+		struct stasis_topic *topic, struct stasis_message *message)
+{
+	struct ast_parked_call_payload *payload = stasis_message_data(message);
+	struct ast_channel_snapshot *channel = payload->parkee;
+	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	RAII_VAR(struct module_config *, mod_cfg,
+			ao2_global_obj_ref(module_configs), ao2_cleanup);
+	struct cdr_object *it_cdr;
+
+	/* Anything other than getting parked will be handled by other updates */
+	if (payload->event_type != PARKED_CALL) {
+		return;
+	}
+
+	/* No one got parked? */
+	if (!channel) {
+		return;
+	}
+
+	CDR_DEBUG(mod_cfg, "Parked Call message for channel %s: %u.%08u\n",
+			channel->name,
+			(unsigned int)stasis_message_timestamp(message)->tv_sec,
+			(unsigned int)stasis_message_timestamp(message)->tv_usec);
+
+	cdr = ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY);
+	if (!cdr) {
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+		return;
+	}
+
+	ao2_lock(cdr);
+
+	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+		if (it_cdr->fn_table->process_parked_channel) {
+			it_cdr->fn_table->process_parked_channel(it_cdr, payload);
+		}
+	}
+
+	ao2_unlock(cdr);
+
 }
 
 struct ast_cdr_config *ast_cdr_get_config(void)
@@ -2953,6 +3204,7 @@
 		if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) &&
 				cdr->disposition < AST_CDR_ANSWERED &&
 				(ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
+			ast_log(AST_LOG_WARNING, "Skipping CDR since we weren't answered\n");
 			continue;
 		}
 
@@ -3584,6 +3836,11 @@
 	if (!bridge_subscription) {
 		return -1;
 	}
+	parking_subscription = stasis_forward_all(ast_parking_topic(), cdr_topic);
+	if (!parking_subscription) {
+		return -1;
+	}
+
 	stasis_router = stasis_message_router_create(cdr_topic);
 	if (!stasis_router) {
 		return -1;
@@ -3592,6 +3849,7 @@
 	stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);
 	stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
 	stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
+	stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
 
 	sched = ast_sched_context_create();
 	if (!sched) {




More information about the asterisk-commits mailing list