[asterisk-commits] mjordan: branch mjordan/cdrs-of-doom r386791 - in /team/mjordan/cdrs-of-doom:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Apr 28 21:47:37 CDT 2013


Author: mjordan
Date: Sun Apr 28 21:47:32 2013
New Revision: 386791

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386791
Log:
Fix merge conflicts, update with more bridging stuff

Dial into a multi-party conference is the last thing to work
through. At least for the 'basic' scenarios.

Modified:
    team/mjordan/cdrs-of-doom/include/asterisk/config_options.h
    team/mjordan/cdrs-of-doom/main/bridging.c
    team/mjordan/cdrs-of-doom/main/cdr.c
    team/mjordan/cdrs-of-doom/main/config_options.c
    team/mjordan/cdrs-of-doom/res/res_stasis.c
    team/mjordan/cdrs-of-doom/tests/test_cdr.c

Modified: team/mjordan/cdrs-of-doom/include/asterisk/config_options.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/config_options.h?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/config_options.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/config_options.h Sun Apr 28 21:47:32 2013
@@ -146,10 +146,11 @@
 
 /*! \brief The representation of a single configuration file to be processed */
 struct aco_file {
-	const char *filename; /*!< \brief The filename to be processed */
-	const char *alias;    /*!< \brief An alias filename to be tried if 'filename' cannot be found */
-	const char **preload; /*!< \brief A null-terminated oredered array of categories to be loaded first */
-	struct aco_type *types[]; /*!< The list of types for this config. Required. Use a sentinel! */
+	const char *filename;		/*!< The filename to be processed */
+	const char *alias;			/*!< An alias filename to be tried if 'filename' cannot be found */
+	const char **preload;		/*!< A null-terminated ordered array of categories to be loaded first */
+	const char *skip_category;	/*!< A regular expression of categories to skip in the file. Use when a file is processed by multiple modules */
+	struct aco_type *types[];	/*!< The list of types for this config. Required. Use a sentinel! */
 };
 
 struct aco_info {

Modified: team/mjordan/cdrs-of-doom/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/bridging.c?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/main/bridging.c (original)
+++ team/mjordan/cdrs-of-doom/main/bridging.c Sun Apr 28 21:47:32 2013
@@ -3625,7 +3625,7 @@
 	struct ast_bridge_channel *peer_bridge_channel)
 {
 	struct ast_bridge *dst_bridge = NULL;
-	struct ast_bridge *src_bridge = NULL;
+	/* struct ast_bridge *src_bridge = NULL; */
 	struct ast_bridge_channel *dst_bridge_channel = NULL;
 	struct ast_bridge_channel *src_bridge_channel = NULL;
 	int peer_priority;
@@ -3645,13 +3645,13 @@
 		if (chan_bridge->num_channels == 2
 			&& chan_priority <= peer_priority) {
 			dst_bridge = peer_bridge;
-			src_bridge = chan_bridge;
+			/* src_bridge = chan_bridge; */
 			dst_bridge_channel = peer_bridge_channel;
 			src_bridge_channel = chan_bridge_channel;
 		} else if (peer_bridge->num_channels == 2
 			&& peer_priority <= chan_priority) {
 			dst_bridge = chan_bridge;
-			src_bridge = peer_bridge;
+			/* src_bridge = peer_bridge; */
 			dst_bridge_channel = chan_bridge_channel;
 			src_bridge_channel = peer_bridge_channel;
 		}
@@ -3660,7 +3660,7 @@
 		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
 		/* Can swap optimize only one way. */
 		dst_bridge = peer_bridge;
-		src_bridge = chan_bridge;
+		/* src_bridge = chan_bridge; */
 		dst_bridge_channel = peer_bridge_channel;
 		src_bridge_channel = chan_bridge_channel;
 	} else if (peer_bridge->num_channels == 2
@@ -3668,7 +3668,7 @@
 		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
 		/* Can swap optimize only one way. */
 		dst_bridge = chan_bridge;
-		src_bridge = peer_bridge;
+		/* src_bridge = peer_bridge; */
 		dst_bridge_channel = chan_bridge_channel;
 		src_bridge_channel = peer_bridge_channel;
 	}

Modified: team/mjordan/cdrs-of-doom/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/cdr.c?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/main/cdr.c (original)
+++ team/mjordan/cdrs-of-doom/main/cdr.c Sun Apr 28 21:47:32 2013
@@ -221,6 +221,7 @@
 /*! \brief The file definition */
 static struct aco_file module_file_conf = {
 	.filename = "cdr.conf",					/*< The name of the config file */
+	.skip_category = "(^csv$|^custom$|^manager$|^odbc$|^pgsql$|^radius$|^sqlite$|^tds$|^mysql$)",
 	.types = ACO_TYPES(&general_option),	/*< The type to process */
 };
 
@@ -307,10 +308,14 @@
 static struct ao2_container *active_cdrs_by_bridge;
 
 /*! \brief Message router for stasis messages regarding channel state */
-static struct stasis_message_router *stasis_channel_router;
-
-/*! \brief Message router for stasis messages regarding bridge state */
-static struct stasis_message_router *stasis_bridge_router;
+static struct stasis_message_router *stasis_router;
+
+static struct stasis_subscription *bridge_subscription;
+
+static struct stasis_subscription *channel_subscription;
+
+/*! \brief The parent topic for all topics we want to aggregate for CDRs */
+static struct stasis_topic *cdr_topic;
 
 struct cdr_object;
 
@@ -321,17 +326,33 @@
 struct cdr_object_fn_table {
 	/*! Name of the subclass */
 	const char *name;
+
 	/*! An initialization function. This will be called automatically when a
 	 * \ref cdr_object is switched to this type in
 	 * \ref cdr_object_transition_state
+	 *
+	 * \param cdr The \ref cdr_object that was just transitioned
 	 */
 	void (* const init_function)(struct cdr_object *cdr);
-	/*! Process a Party A update for the \ref cdr_object */
-	void (* const process_party_a_update)(struct cdr_object *cdr,
+
+	/*! Process a Party A update for the \ref cdr_object
+	 *
+	 * \param cdr The \ref cdr_object to process the update
+	 * \param snapshot The snapshot for the CDR's Party A
+	 * \retval 0 the CDR handled the update or ignored it
+	 * \retval 1 the CDR is finalized and a new one should be made to handle it
+	 */
+	int (* const process_party_a_update)(struct cdr_object *cdr,
 			struct ast_channel_snapshot *snapshot);
-	/*! Process a Party B update for the \ref cdr_object */
+
+	/*! Process a Party B update for the \ref cdr_object
+	 *
+	 * \param cdr The \ref cdr_object to process the update
+	 * \param snapshot The snapshot for the CDR's Party B
+	 */
 	void (* const process_party_b_update)(struct cdr_object *cdr,
 			struct ast_channel_snapshot *snapshot);
+
 	/*! Process the beginning of a dial. A dial message implies one of two
 	 * things:
 	 * The \ref cdr_object's Party A has been originated
@@ -341,8 +362,7 @@
 	 */
 	int (* const process_dial_begin)(struct cdr_object *cdr,
 			struct ast_channel_snapshot *caller,
-			struct ast_channel_snapshot *peer,
-			const char *dial_status);
+			struct ast_channel_snapshot *peer);
 	int (* const process_dial_end)(struct cdr_object *cdr,
 			struct ast_channel_snapshot *caller,
 			struct ast_channel_snapshot *peer,
@@ -356,56 +376,86 @@
 };
 AST_MUTEX_DEFINE_STATIC(cdr_sched_lock);
 
-static void base_process_party_a_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
-static int base_process_bridge_leave_message(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+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 void single_state_init_function(struct cdr_object *cdr);
-static void single_state_process_party_b_update(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, const char *dial_status);
+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);
 
 struct cdr_object_fn_table single_state_fn_table = {
 	.name = "Single",
 	.init_function = single_state_init_function,
-	.process_party_a_update = base_process_party_a_update,
-	.process_party_b_update = single_state_process_party_b_update,
+	.process_party_a_update = base_process_party_a,
+	.process_party_b_update = single_state_process_party_b,
 	.process_dial_begin = single_state_process_dial_begin,
 	.process_dial_end = base_process_dial_end,
 	.process_bridge_enter = single_state_process_bridge_enter,
-	.process_bridge_leave = base_process_bridge_leave_message,
+	.process_bridge_leave = base_process_bridge_leave,
 };
 
-static void dial_state_process_party_b_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
-static int dial_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
+static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+static int dial_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
 static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
+static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 struct cdr_object_fn_table dial_state_fn_table = {
 	.name = "Dial",
-	.process_party_a_update = base_process_party_a_update,
-	.process_party_b_update = dial_state_process_party_b_update,
+	.process_party_a_update = base_process_party_a,
+	.process_party_b_update = dial_state_process_party_b,
 	.process_dial_begin = dial_state_process_dial_begin,
 	.process_dial_end = dial_state_process_dial_end,
-	.process_bridge_leave = base_process_bridge_leave_message,
+	.process_bridge_enter = dial_state_process_bridge_enter,
+	.process_bridge_leave = base_process_bridge_leave,
 };
 
-static void bridge_state_init_function(struct cdr_object *cdr);
-static void bridge_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 struct cdr_object_fn_table bridge_state_fn_table = {
 	.name = "Bridged",
-	.init_function = bridge_state_init_function,
-	.process_party_a_update = base_process_party_a_update,
-	.process_bridge_leave = bridge_process_bridge_leave,
+	.process_party_a_update = base_process_party_a,
+	.process_party_b_update = bridge_state_process_party_b,
+	.process_bridge_leave = bridge_state_process_bridge_leave,
 };
 
+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);
+
+struct cdr_object_fn_table bridged_pending_state_fn_table = {
+	.name = "Pending",
+	.init_function = pending_state_init_function,
+	.process_party_a_update = pending_state_process_party_a,
+	.process_dial_begin = pending_state_process_dial_begin,
+	.process_dial_end = base_process_dial_end,
+	.process_bridge_enter = pending_state_process_bridge_enter,
+	.process_bridge_leave = base_process_bridge_leave,
+};
+
+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);
+
+struct cdr_object_fn_table dialed_pending_state_fn_table = {
+	.name = "DialedPending",
+	.process_party_a_update = dialed_pending_state_process_party_a,
+	.process_dial_begin = dialed_pending_state_process_dial_begin,
+	.process_dial_end = base_process_dial_end,
+	.process_bridge_enter = dialed_pending_state_process_bridge_enter,
+	.process_bridge_leave = base_process_bridge_leave,
+};
+
 static void finalized_state_init_function(struct cdr_object *cdr);
-static void finalized_state_process_party_a_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+static int finalized_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 
 struct cdr_object_fn_table finalized_state_fn_table = {
 	.name = "Finalized",
 	.init_function = finalized_state_init_function,
-	.process_party_a_update = finalized_state_process_party_a_update,
+	.process_party_a_update = finalized_state_process_party_a,
 };
 
 /*! \brief A wrapper object around a snapshot.
@@ -514,8 +564,14 @@
 {
 	struct cdr_object *left = obj;
 	struct cdr_object *right = arg;
+	struct cdr_object *it_cdr;
 	const char *match = (flags & OBJ_KEY) ? arg : right->bridge;
-	return strcasecmp(left->bridge, match) ? 0 : (CMP_MATCH);
+	for (it_cdr = left; it_cdr; it_cdr = it_cdr->next) {
+		if (!strcasecmp(it_cdr->bridge, match)) {
+			return CMP_MATCH;
+		}
+	}
+	return 0;
 }
 
 static void cdr_object_dtor(void *obj)
@@ -851,7 +907,7 @@
 
 /* BASE METHOD IMPLEMENTATIONS */
 
-static void base_process_party_a_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 
@@ -862,13 +918,15 @@
 
 	cdr_object_check_party_a_answer(cdr);
 	cdr_object_check_party_a_hangup(cdr);
-}
-
-static int base_process_bridge_leave_message(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+
+	return 0;
+}
+
+static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
 	/* Assume we shouldn't get a bridge leave message for ourselves */
 	ast_assert(strcmp(channel->name, cdr->party_a.snapshot->name) != 0);
-	return 0;
+	return 1;
 }
 
 static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
@@ -887,7 +945,7 @@
 	cdr_object_check_party_a_answer(cdr);
 }
 
-static void single_state_process_party_b_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+static void single_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	/* This should never happen! */
 	ast_assert(cdr->party_b.snapshot == NULL);
@@ -895,7 +953,7 @@
 	return;
 }
 
-static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
+static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
 {
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 
@@ -1026,22 +1084,25 @@
 	cdr_object_finalize(cdr);
 }
 
-static void finalized_state_process_party_a_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+static int finalized_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	if (ast_test_flag(&cdr->party_a.snapshot->flags, AST_FLAG_ZOMBIE)) {
 		cdr_object_finalize(cdr);
 	}
+
+	return 1;
 }
 
 /* DIAL STATE */
 
-static void dial_state_process_party_b_update(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	ast_assert(snapshot != NULL);
 
-	if (cdr->party_b.snapshot && !strcmp(cdr->party_b.snapshot->name, snapshot->name)) {
-		cdr_object_swap_snapshot(&cdr->party_b, snapshot);
-	}
+	if (!cdr->party_b.snapshot || strcmp(cdr->party_b.snapshot->name, snapshot->name)) {
+		return;
+	}
+	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
 	/* If party B hangs up, finalize this CDR */
 	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_ZOMBIE)) {
@@ -1049,9 +1110,18 @@
 	}
 }
 
-static int dial_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
-{
-	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+static int dial_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
+{
+	/* Don't process a begin dial here. A party A already in the dial state will
+	 * who receives a dial begin for something else will be handled by the
+	 * message router callback and will add a new CDR for the party A */
+	return 1;
+}
+
+static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
+{
+	RAII_VAR(struct module_config *, mod_cfg,
+			ao2_global_obj_ref(module_configs), ao2_cleanup);
 	struct ast_channel_snapshot *party_a;
 
 	if (caller) {
@@ -1062,12 +1132,6 @@
 	ast_assert(!strcmp(cdr->party_a.snapshot->name, party_a->name));
 	cdr_object_swap_snapshot(&cdr->party_a, party_a);
 
-	if (ast_strlen_zero(dial_status)) {
-		/* If this isn't a dial end, don't process it here. A begin dial for
-		 * a party A already in the dial state will be handled by the message
-		 * router callback and add a new CDR for the party A */
-		return 1;
-	}
 	if (cdr->party_b.snapshot) {
 		if (strcmp(cdr->party_b.snapshot->name, peer->name)) {
 			/* Not the status for this CDR - defer back to the message router */
@@ -1078,7 +1142,9 @@
 
 	/* Set the disposition based on the dial string. */
 	if (!strcmp(dial_status, "ANSWER")) {
-		/* TODO: if ANSWER, do NOTHING? */
+		cdr->disposition = AST_CDR_ANSWERED;
+		/* Switch to dial pending to wait and see what the caller does */
+		cdr_object_transition_state(cdr, &dialed_pending_state_fn_table);
 		return 0;
 	} else if (!strcmp(dial_status, "BUSY")) {
 		cdr->disposition = AST_CDR_BUSY;
@@ -1093,28 +1159,164 @@
 	return 0;
 }
 
-static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
-{
-
+static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	struct ao2_iterator *it_cdrs;
+	char *bridge_id = ast_strdupa(bridge->uniqueid);
+	struct cdr_object *cand_cdr_master;
+	int success = 1;
+
+	ast_string_field_set(cdr, bridge, bridge->uniqueid);
+
+	/* Get parties in the bridge */
+	it_cdrs = ao2_callback(active_cdrs_by_bridge, OBJ_MULTIPLE | OBJ_KEY,
+			cdr_object_bridge_cmp_fn, bridge_id);
+	if (!it_cdrs) {
+		/* No one in the bridge yet! */
+		cdr_object_transition_state(cdr, &bridge_state_fn_table);
+		return 0;
+	}
+
+	while ((cand_cdr_master = ao2_iterator_next(it_cdrs))) {
+		struct cdr_object *cand_cdr;
+
+		ao2_lock(cand_cdr_master);
+		for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
+			/* Skip any records that are not in a bridge or in this bridge.
+			 * I'm not sure how that would happen, but it pays to be careful. */
+			if (cand_cdr->fn_table != &bridge_state_fn_table ||
+					strcmp(cdr->bridge, cand_cdr->bridge)) {
+				continue;
+			}
+
+			/* Skip any records that aren't our Party B */
+			if (strcmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
+				continue;
+			}
+
+			cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
+			/* If they have a Party B, they joined up with someone else as their
+			 * Party A. Don't finalize them as they're active. Otherwise, we
+			 * have stolen them so they need to be finalized.
+			 */
+			if (!cand_cdr->party_b.snapshot) {
+				cdr_object_finalize(cand_cdr);
+			}
+			success = 0;
+			break;
+		}
+		ao2_unlock(cand_cdr_master);
+		ao2_t_ref(cand_cdr_master, -1, "Drop iterator reference");
+	}
+	ao2_iterator_destroy(it_cdrs);
+
+	/* We always transition state, even if we didn't get a peer */
+	cdr_object_transition_state(cdr, &bridge_state_fn_table);
+
+	/* Success implies that we have a Party B */
+	return success;
 }
 
 
 /* BRIDGE STATE */
 
-static void bridge_state_init_function(struct cdr_object *cdr)
-{
-
-}
-
-static int base_process_bridge_leave_message(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+	if (!cdr->party_b.snapshot || strcmp(cdr->party_b.snapshot->name, snapshot->name)) {
+		return;
+	}
+	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
+
+	/* If party B hangs up, finalize this CDR */
+	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_ZOMBIE)) {
+		cdr_object_transition_state(cdr, &finalized_state_fn_table);
+	}
+}
+
+static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
 	if (strcmp(cdr->bridge, bridge->uniqueid)) {
+		return 1;
+	}
+	cdr_object_transition_state(cdr, &finalized_state_fn_table);
+
+	return 0;
+}
+
+/* PENDING STATE */
+
+static void pending_state_init_function(struct cdr_object *cdr)
+{
+	ast_cdr_set_property(cdr->name, AST_CDR_FLAG_DISABLE);
+}
+
+static int pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+	if (ast_test_flag(&snapshot->flags, AST_FLAG_ZOMBIE)) {
 		return 0;
 	}
 
+	/* If we get a CEP change, we're executing dialplan. Switch to single.
+	 * Otherwise, ignore.
+	 */
+	if (!strcmp(snapshot->context, cdr->party_a.snapshot->context)
+		&& !strcmp(snapshot->exten, cdr->party_a.snapshot->exten)
+		&& snapshot->priority == cdr->party_a.snapshot->priority) {
+		return 0;
+	}
+
+	cdr_object_transition_state(cdr, &single_state_fn_table);
+	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
+	cdr->fn_table->process_party_a_update(cdr, snapshot);
+	return 0;
+}
+
+static int pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
+{
+	cdr_object_transition_state(cdr, &single_state_fn_table);
+	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
+	return cdr->fn_table->process_dial_begin(cdr, caller, peer);
+}
+
+static int pending_state_process_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_bridge_enter(cdr, bridge, channel);
+}
+
+
+/* DIALED PENDING STATE */
+
+static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+	/* If we get a CEP change, we're executing dialplan. Finalize ourselves
+	 * and ask the handler to make a new CDR if needed.
+	 */
+	if (strcmp(snapshot->context, cdr->party_a.snapshot->context)
+		|| strcmp(snapshot->exten, cdr->party_a.snapshot->exten)
+		|| snapshot->priority != cdr->party_a.snapshot->priority) {
+		cdr_object_transition_state(cdr, &finalized_state_fn_table);
+		return 1;
+	}
+	base_process_party_a(cdr, snapshot);
+	return 0;
+}
+
+static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	cdr_object_transition_state(cdr, &dial_state_fn_table);
+	return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
+}
+
+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);
-
-	return 0;
+	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);
 }
 
 
@@ -1185,20 +1387,26 @@
 			cdr, party_a->name, party_b ? party_b->name : "(none)");
 	ao2_lock(cdr);
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (it_cdr->fn_table->process_dial_begin) {
-			res &= it_cdr->fn_table->process_dial_begin(it_cdr, party_a, party_b, dial_status);
-		}
-	}
-
-	/* If no CDR handled the dial message, make a new one */
-	if (res) {
+		if (ast_strlen_zero(dial_status)) {
+			if (it_cdr->fn_table->process_dial_begin) {
+				res &= it_cdr->fn_table->process_dial_begin(it_cdr, party_a, party_b);
+			}
+		} else {
+			if (it_cdr->fn_table->process_dial_end) {
+				it_cdr->fn_table->process_dial_end(it_cdr, party_a, party_b, dial_status);
+			}
+		}
+	}
+
+	/* If no CDR handled a dial begin message, make a new one */
+	if (res && ast_strlen_zero(dial_status)) {
 		struct cdr_object *new_cdr;
 
 		new_cdr = cdr_object_create_and_append(cdr);
 		if (!new_cdr) {
 			return;
 		}
-		new_cdr->fn_table->process_dial_begin(new_cdr, party_a, party_b, dial_status);
+		new_cdr->fn_table->process_dial_begin(new_cdr, party_a, party_b);
 	}
 	ao2_unlock(cdr);
 }
@@ -1267,13 +1475,20 @@
 		if (cdr) {
 			ao2_lock(cdr);
 			if (new_snapshot) {
+				int all_reject = 1;
 				for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 					if (it_cdr->fn_table->process_party_a_update) {
 						CDR_DEBUG(mod_cfg, "%p - processing new channel snapshot %s\n", it_cdr, new_snapshot->name);
-						it_cdr->fn_table->process_party_a_update(it_cdr, new_snapshot);
+						all_reject &= it_cdr->fn_table->process_party_a_update(it_cdr, new_snapshot);
 					}
 				}
+				if (all_reject && !ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE)) {
+					struct cdr_object *new_cdr;
+					new_cdr = cdr_object_create_and_append(cdr);
+					new_cdr->fn_table->process_party_a_update(new_cdr, new_snapshot);
+				}
 			} else {
+				CDR_DEBUG(mod_cfg, "%p - beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
 				for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 					cdr_object_finalize(it_cdr);
 				}
@@ -1297,20 +1512,44 @@
 
 static void handle_bridge_leave_message(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);
 	RAII_VAR(struct cdr_object *, cdr,
 			ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
 			ao2_cleanup);
 	struct cdr_object *it_cdr;
-
-	ast_assert(cdr != NULL);
+	struct cdr_object *pending_cdr;
+	int left_bridge = 0;
+
+	if (!cdr) {
+		return;
+	}
 
 	ao2_lock(cdr);
 
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (it_cdr->fn_table->process_bridge_leave) {
-			it_cdr->fn_table->process_bridge_leave(it_cdr, bridge, channel);
-		}
-	}
+		if (!it_cdr->fn_table->process_bridge_leave) {
+			continue;
+		}
+		CDR_DEBUG(mod_cfg, "%p - Processing Bridge Leave for %s\n",
+				it_cdr, channel->name);
+		if (!it_cdr->fn_table->process_bridge_leave(it_cdr, bridge, channel)) {
+			ast_string_field_set(it_cdr, bridge, "");
+			left_bridge = 1;
+		}
+	}
+	if (!left_bridge) {
+		ao2_unlock(cdr);
+		return;
+	}
+
+	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);
 
 	ao2_unlock(cdr);
 }
@@ -1408,6 +1647,7 @@
 			}
 			ao2_cleanup(bcand);
 		}
+		ao2_unlock(cand_cdr_master);
 	}
 	ao2_iterator_destroy(it_cdrs);
 
@@ -1478,7 +1718,7 @@
 		 * add ourselves directly as the Party B, or if we need a new CDR.
 		 */
 		if (bcand->cdr->party_a.snapshot == bcand->candidate) {
-			if (bcand->cdr->party_b.snapshot) {
+			if (bcand->cdr->party_b.snapshot && strcmp(bcand->cdr->party_b.snapshot->name, cdr->party_a.snapshot->name)) {
 				new_cdr = cdr_object_create_and_append(bcand->cdr);
 				cdr_object_snapshot_copy(&new_cdr->party_b, &cdr->party_a);
 				ast_string_field_set(new_cdr, bridge, cdr->bridge);
@@ -1548,19 +1788,24 @@
 	struct cdr_object *it_cdr;
 	struct cdr_object *handled_cdr = NULL;
 
-	ast_assert(cdr != NULL);
+	if (!cdr) {
+		ast_debug(1, "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_party_a_update) {
-			CDR_DEBUG(mod_cfg, "%p - updating Party A\n", it_cdr);
+			CDR_DEBUG(mod_cfg, "%p - updating Party A %s snapshot\n", it_cdr,
+					channel->name);
 			it_cdr->fn_table->process_party_a_update(it_cdr, channel);
 		}
 
 		/* Notify all states that they have entered a bridge */
 		if (it_cdr->fn_table->process_bridge_enter) {
-			CDR_DEBUG(mod_cfg, "%p - processing bridge enter\n", it_cdr);
+			CDR_DEBUG(mod_cfg, "%p - Processing bridge enter for %s\n", it_cdr,
+					channel->name);
 			res &= it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
 			if (!res && !handled_cdr) {
 				handled_cdr = it_cdr;
@@ -2776,7 +3021,12 @@
 	ast_free(batch);
 	batch = NULL;
 
+	/*	ao2_ref(stasis_router, -1);
+	ao2_ref(bridge_subscription, -1);
+	ao2_ref(channel_subscription, -1);
+	ao2_ref(cdr_topic, -1);*/
 	ao2_ref(active_cdrs_by_channel, -1);
+	ao2_ref(active_cdrs_by_bridge, -1);
 }
 
 static void cdr_enable_batch_mode(struct ast_cdr_config *config)
@@ -2820,18 +3070,26 @@
 		return -1;
 	}
 
-	stasis_channel_router = stasis_message_router_create(stasis_caching_get_topic(ast_channel_topic_all_cached()));
-	if (!stasis_channel_router) {
+	cdr_topic = stasis_topic_create("cdr_engine");
+	if (!cdr_topic) {
 		return -1;
 	}
-	stasis_message_router_add(stasis_channel_router, stasis_cache_update_type(), handle_channel_cache_message, NULL);
-	stasis_message_router_add(stasis_channel_router, ast_channel_dial_type(), handle_dial_message, NULL);
-
-	stasis_bridge_router = stasis_message_router_create(stasis_caching_get_topic(ast_bridge_topic_all_cached()));
-	if (!stasis_bridge_router) {
+
+	channel_subscription = stasis_forward_all(stasis_caching_get_topic(ast_channel_topic_all_cached()), cdr_topic);
+	if (!channel_subscription) {
 		return -1;
 	}
-	stasis_message_router_add(stasis_bridge_router, ast_bridge_blob_type(), handle_bridge_message, NULL);
+	bridge_subscription = stasis_forward_all(stasis_caching_get_topic(ast_bridge_topic_all_cached()), cdr_topic);
+	if (!bridge_subscription) {
+		return -1;
+	}
+	stasis_router = stasis_message_router_create(cdr_topic);
+	if (!stasis_router) {
+		return -1;
+	}
+	stasis_message_router_add(stasis_router, stasis_cache_update_type(), handle_channel_cache_message, NULL);
+	stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);
+	stasis_message_router_add(stasis_router, ast_bridge_blob_type(), handle_bridge_message, NULL);
 
 	sched = ast_sched_context_create();
 	if (!sched) {

Modified: team/mjordan/cdrs-of-doom/main/config_options.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/config_options.c?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/main/config_options.c (original)
+++ team/mjordan/cdrs-of-doom/main/config_options.c Sun Apr 28 21:47:32 2013
@@ -430,10 +430,21 @@
 	 * We do not grab a reference to these objects, as the info already holds references to them. This
 	 * pointer is just a convenience. Do not actually store it somewhere. */
 	void **field;
+	regex_t *regex_skip;
 
 	/* Skip preloaded categories if we aren't preloading */
 	if (!preload && is_preload(file, cat)) {
 		return 0;
+	}
+
+	/* Skip the category if we've been told to ignore it */
+	if (!ast_strlen_zero(file->skip_category)) {
+		regex_skip = build_regex(file->skip_category);
+		if (!regexec(regex_skip, cat, 0, NULL, 0)) {
+			ast_free(regex_skip);
+			return 0;
+		}
+		ast_free(regex_skip);
 	}
 
 	/* Find aco_type by category, if not found it is an error */

Modified: team/mjordan/cdrs-of-doom/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/res/res_stasis.c?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/res/res_stasis.c (original)
+++ team/mjordan/cdrs-of-doom/res/res_stasis.c Sun Apr 28 21:47:32 2013
@@ -323,7 +323,7 @@
 				  struct ast_channel *chan, void *data)
 {
 	ast_debug(3, "%s: Answering", control->channel_id);
-	return __ast_answer(chan, 0, 1) == 0 ? &OK : &FAIL;
+	return __ast_answer(chan, 0) == 0 ? &OK : &FAIL;
 }
 
 int stasis_app_control_answer(struct stasis_app_control *control)

Modified: team/mjordan/cdrs-of-doom/tests/test_cdr.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/tests/test_cdr.c?view=diff&rev=386791&r1=386790&r2=386791
==============================================================================
--- team/mjordan/cdrs-of-doom/tests/test_cdr.c (original)
+++ team/mjordan/cdrs-of-doom/tests/test_cdr.c Sun Apr 28 21:47:32 2013
@@ -216,6 +216,15 @@
 /*! \brief Alice's Caller ID */
 #define ALICE_CALLERID { .id.name.str = "Alice", .id.name.valid = 1, .id.number.str = "100", .id.number.valid = 1, }
 
+/*! \brief Bob's Caller ID */
+#define BOB_CALLERID { .id.name.str = "Bob", .id.name.valid = 1, .id.number.str = "200", .id.number.valid = 1, }
+
+/*! \brief Charlie's Caller ID */
+#define CHARLIE_CALLERID { .id.name.str = "Charlie", .id.name.valid = 1, .id.number.str = "300", .id.number.valid = 1, }
+
+/*! \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 Copy the linkedid and uniqueid from a channel to an expected CDR */
 #define COPY_IDS(channel_var, expected_record) do { \
 	ast_copy_string((expected_record)->uniqueid, ast_channel_uniqueid((channel_var)), sizeof((expected_record)->uniqueid)); \
@@ -223,7 +232,7 @@
 	} while (0)
 
 /*! \brief Create a \ref test_cdr_chan_tech for Alice, and set the expected
- * CDR records (if available) to her linkedid and uniqueid. */
+ * CDR records' linkedid and uniqueid. */
 #define CREATE_ALICE_CHANNEL(channel_var, caller_id, expected_record) do { \
 	(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, "100", "Alice", "100", "100", "default", NULL, 0, CHANNEL_TECH_NAME "/Alice"); \
 	ast_channel_set_caller((channel_var), (caller_id), NULL); \
@@ -231,8 +240,38 @@
 	ast_copy_string((expected_record)->linkedid, ast_channel_linkedid((channel_var)), sizeof((expected_record)->linkedid)); \
 	} while (0)
 
+/*! \brief Create a \ref test_cdr_chan_tech for Bob, and set the expected
+ * CDR records' linkedid and uniqueid. */
+#define CREATE_BOB_CHANNEL(channel_var, caller_id, expected_record) do { \
+	(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, "200", "Bob", "200", "200", "default", NULL, 0, CHANNEL_TECH_NAME "/Bob"); \
+	ast_channel_set_caller((channel_var), (caller_id), NULL); \
+	ast_copy_string((expected_record)->uniqueid, ast_channel_uniqueid((channel_var)), sizeof((expected_record)->uniqueid)); \
+	ast_copy_string((expected_record)->linkedid, ast_channel_linkedid((channel_var)), sizeof((expected_record)->linkedid)); \
+	} while (0)
+
+/*! \brief Create a \ref test_cdr_chan_tech for Charlie, and set the expected
+ * CDR records' linkedid and uniqueid. */
+#define CREATE_CHARLIE_CHANNEL(channel_var, caller_id, expected_record) do { \
+	(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, "300", "Charlie", "300", "300", "default", NULL, 0, CHANNEL_TECH_NAME "/Charlie"); \
+	ast_channel_set_caller((channel_var), (caller_id), NULL); \
+	ast_copy_string((expected_record)->uniqueid, ast_channel_uniqueid((channel_var)), sizeof((expected_record)->uniqueid)); \
+	ast_copy_string((expected_record)->linkedid, ast_channel_linkedid((channel_var)), sizeof((expected_record)->linkedid)); \
+	} while (0)
+
+/*! \brief Create a \ref test_cdr_chan_tech for Charlie, and set the expected
+ * CDR records' linkedid and uniqueid. */
+#define CREATE_DAVID_CHANNEL(channel_var, caller_id, expected_record) do { \
+	(channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, "400", "David", "400", "400", "default", NULL, 0, CHANNEL_TECH_NAME "/David"); \
+	ast_channel_set_caller((channel_var), (caller_id), NULL); \
+	ast_copy_string((expected_record)->uniqueid, ast_channel_uniqueid((channel_var)), sizeof((expected_record)->uniqueid)); \
+	ast_copy_string((expected_record)->linkedid, ast_channel_linkedid((channel_var)), sizeof((expected_record)->linkedid)); \
+	} while (0)
+
 /*! \brief Emulate a channel entering into an application */
-#define EMULATE_APP_DATA(channel, application, data) do { \
+#define EMULATE_APP_DATA(channel, priority, application, data) do { \
+	if ((priority) > 0) { \
+		ast_channel_priority_set((channel), (priority)); \
+	} \
 	ast_channel_appl_set((channel), (application)); \
 	ast_channel_data_set((channel), (data)); \
 	publish_channel_snapshot((channel)); \
@@ -402,7 +441,7 @@
 	CREATE_ALICE_CHANNEL(chan, &caller, &expected);
 
 	/* Channel enters wait */
-	EMULATE_APP_DATA(chan, "Wait", "1");
+	EMULATE_APP_DATA(chan, 1, "Wait", "1");
 
 	/* And hangs up */
 	HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL);
@@ -418,6 +457,11 @@
 	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
 			ao2_cleanup);
 
+	struct ast_party_caller caller = {
+			.id.name.str = "",
+			.id.name.valid = 1,
+			.id.number.str = "",
+			.id.number.valid = 1, };
 	struct ast_cdr expected = {
 		.clid = "\"\" <>",
 		.dst = "s",
@@ -446,12 +490,12 @@
 
 	SWAP_CONFIG(config, unanswered_cdr_config);
 
-	CREATE_ALICE_CHANNEL(chan, NULL, &expected);
+	CREATE_ALICE_CHANNEL(chan, &caller, &expected);
 
 	ast_channel_exten_set(chan, "s");
 	ast_channel_context_set(chan, "default");
 	ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
-	EMULATE_APP_DATA(chan, "AppDial", "(Outgoing Line)");
+	EMULATE_APP_DATA(chan, 0, "AppDial", "(Outgoing Line)");
 	HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL);
 
 	result = verify_mock_cdr_record(test, &expected, 1);
@@ -497,14 +541,9 @@
 	SWAP_CONFIG(config, debug_cdr_config);
 	CREATE_ALICE_CHANNEL(chan, &caller, &expected);
 
-	/* Channel enters Answer app */
-	ast_channel_priority_set(chan, 1);
-	EMULATE_APP_DATA(chan, "Answer", "");
+	EMULATE_APP_DATA(chan, 1, "Answer", "");
 	ast_setstate(chan, AST_STATE_UP);
-
-	/* Channel enters the VoiceMailMain app */
-	ast_channel_priority_set(chan, 2);
-	EMULATE_APP_DATA(chan, "VoiceMailMain", "1");
+	EMULATE_APP_DATA(chan, 2, "VoiceMailMain", "1");
 
 	/* And then it hangs up */

[... 1009 lines stripped ...]



More information about the asterisk-commits mailing list