[svn-commits] rmudgett: branch group/issue14068 r189770 - in /team/group/issue14068: ./ cha...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Apr 21 13:31:02 CDT 2009


Author: rmudgett
Date: Tue Apr 21 13:30:47 2009
New Revision: 189770

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=189770
Log:
Merged revisions 189735 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

........
  r189735 | rmudgett | 2009-04-21 12:44:01 -0500 (Tue, 21 Apr 2009) | 18 lines
  
  Added CCBS/CCNR Party A support and enhanced COLP support.
  
  This change adds the following features to chan_misdn:
  * CCBS/CCNR Party A support for PTMP and PTP modes.
  * Enhances COLP support for call diversion and explicit call transfer.
  
  These enhanced features require a modified version of mISDN.
  
  The latest modified mISDN v1.1.x based version is available at:
  http://svn.digium.com/svn/thirdparty/mISDN/trunk
  http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
  
  Taged versions of the modified mISDN code are available under:
  http://svn.digium.com/svn/thirdparty/mISDN/tags
  http://svn.digium.com/svn/thirdparty/mISDNuser/tags
  
  Merged from team/rmudgett/misdn_facility branch.
........

Modified:
    team/group/issue14068/   (props changed)
    team/group/issue14068/CHANGES
    team/group/issue14068/channels/chan_misdn.c
    team/group/issue14068/channels/misdn/chan_misdn_config.h
    team/group/issue14068/channels/misdn/ie.c
    team/group/issue14068/channels/misdn/isdn_lib.c
    team/group/issue14068/channels/misdn/isdn_lib.h
    team/group/issue14068/channels/misdn/isdn_lib_intern.h
    team/group/issue14068/channels/misdn/isdn_msg_parser.c
    team/group/issue14068/channels/misdn_config.c
    team/group/issue14068/configs/misdn.conf.sample

Propchange: team/group/issue14068/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/group/issue14068/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Tue Apr 21 13:30:47 2009
@@ -1,1 +1,1 @@
-/trunk:1-189683
+/trunk:1-189758

Modified: team/group/issue14068/CHANGES
URL: http://svn.digium.com/svn-view/asterisk/team/group/issue14068/CHANGES?view=diff&rev=189770&r1=189769&r2=189770
==============================================================================
--- team/group/issue14068/CHANGES (original)
+++ team/group/issue14068/CHANGES Tue Apr 21 13:30:47 2009
@@ -61,6 +61,25 @@
     subscriberprefix, and abbreviatedprefix in misdn.conf to prefix any
     received number from the ISDN link if that number has the corresponding
     Type-Of-Number.
+  * Added new dialplan application misdn_command which permits controlling
+    the CCBS/CCNR functionality.
+  * Added new dialplan function mISDN_CC which permits retrieval of various
+    values from an active call completion record.
+
+thirdparty mISDN enhancements
+-----------------------------
+mISDN has been modified by Digium, Inc. to greatly expand facility message
+support to allow:
+  * Enhanced COLP support for call diversion and transfer.
+  * CCBS/CCNR support.
+
+The latest modified mISDN v1.1.x based version is available at:
+http://svn.digium.com/svn/thirdparty/mISDN/trunk
+http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
+
+Taged versions of the modified mISDN code are available under:
+http://svn.digium.com/svn/thirdparty/mISDN/tags
+http://svn.digium.com/svn/thirdparty/mISDNuser/tags
 
 libpri channel driver (chan_dahdi) changes
 -------------------------------------------

Modified: team/group/issue14068/channels/chan_misdn.c
URL: http://svn.digium.com/svn-view/asterisk/team/group/issue14068/channels/chan_misdn.c?view=diff&rev=189770&r1=189769&r2=189770
==============================================================================
--- team/group/issue14068/channels/chan_misdn.c (original)
+++ team/group/issue14068/channels/chan_misdn.c Tue Apr 21 13:30:47 2009
@@ -28,6 +28,26 @@
  *
  * \ingroup channel_drivers
  */
+
+/*!
+ * \note
+ * To use the CCBS/CCNR supplementary service feature and other
+ * supplementary services using FACILITY messages requires a
+ * modified version of mISDN.
+ *
+ * \note
+ * The latest modified mISDN v1.1.x based version is available at:
+ * http://svn.digium.com/svn/thirdparty/mISDN/trunk
+ * http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
+ *
+ * \note
+ * Taged versions of the modified mISDN code are available under:
+ * http://svn.digium.com/svn/thirdparty/mISDN/tags
+ * http://svn.digium.com/svn/thirdparty/mISDNuser/tags
+ */
+
+/* Define to enable cli commands to generate canned CCBS messages. */
+// #define CCBS_TEST_MESSAGES	1
 
 /*** MODULEINFO
 	<depend>isdnnet</depend>
@@ -48,6 +68,7 @@
 #include <sys/file.h>
 #include <semaphore.h>
 #include <ctype.h>
+#include <time.h>
 
 #include "asterisk/channel.h"
 #include "asterisk/config.h"
@@ -109,6 +130,165 @@
 static char *complete_show_config(struct ast_cli_args *a);
 
 /* BEGIN: chan_misdn.h */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*
+ * This timeout duration is to clean up any call completion records that
+ * are forgotten about by the switch.
+ */
+#define MISDN_CC_RECORD_AGE_MAX		(6UL * 60 * 60)	/* seconds */
+
+#define MISDN_CC_REQUEST_WAIT_MAX	5	/* seconds */
+
+/*!
+ * \brief Caller that initialized call completion services
+ *
+ * \details
+ * This data is the payload for a datastore that is put on the channel that
+ * initializes call completion services.  This datastore is set to be inherited
+ * by the outbound mISDN channel.  When one of these channels hangs up, the
+ * channel pointer will be set to NULL.  That way, we can ensure that we do not
+ * touch this channel after it gets destroyed.
+ */
+struct misdn_cc_caller {
+	/*! \brief The channel that initialized call completion services */
+	struct ast_channel *chan;
+};
+
+struct misdn_cc_notify {
+	/*! \brief Dialplan: Notify extension priority */
+	int priority;
+
+	/*! \brief Dialplan: Notify extension context */
+	char context[AST_MAX_CONTEXT];
+
+	/*! \brief Dialplan: Notify extension number (User-A) */
+	char exten[AST_MAX_EXTENSION];
+};
+
+/*! \brief mISDN call completion record */
+struct misdn_cc_record {
+	/*! \brief Call completion record linked list */
+	AST_LIST_ENTRY(misdn_cc_record) list;
+
+	/*! \brief Time the record was created. */
+	time_t time_created;
+
+	/*! \brief MISDN_CC_RECORD_ID value */
+	long record_id;
+
+	/*!
+	 * \brief Logical Layer 1 port associated with this
+	 * call completion record
+	 */
+	int port;
+
+	/*! \brief TRUE if point-to-point mode (CCBS-T/CCNR-T mode) */
+	int ptp;
+
+	/*! \brief Mode specific parameters */
+	union {
+		/*! \brief point-to-point specific parameters. */
+		struct {
+			/*!
+			 * \brief Call-completion signaling link.
+			 * NULL if signaling link not established.
+			 */
+			struct misdn_bchannel *bc;
+
+			/*!
+			 * \brief TRUE if we requested the request retention option
+			 * to be enabled.
+			 */
+			int requested_retention;
+
+			/*!
+			 * \brief TRUE if the request retention option is enabled.
+			 */
+			int retention_enabled;
+		} ptp;
+
+		/*! \brief point-to-multi-point specific parameters. */
+		struct {
+			/*! \brief CallLinkageID (valid when port determined) */
+			int linkage_id;
+
+			/*! \breif CCBSReference (valid when activated is TRUE) */
+			int reference_id;
+
+			/*! \brief globalRecall(0),	specificRecall(1) */
+			int recall_mode;
+		} ptmp;
+	} mode;
+
+	/*! \brief TRUE if call completion activated */
+	int activated;
+
+	/*! \brief Outstanding message ID (valid when outstanding_message) */
+	int invoke_id;
+
+	/*! \brief TRUE if waiting for a response from a message (invoke_id is valid) */
+	int outstanding_message;
+
+	/*! \brief TRUE if activation has been requested */
+	int activation_requested;
+
+	/*!
+	 * \brief TRUE if User-A is free
+	 * \note PTMP - Used to answer CCBSStatusRequest.
+	 * PTP - Determines how to respond to CCBS_T_RemoteUserFree.
+	 */
+	int party_a_free;
+
+	/*! \brief Error code received from last outstanding message. */
+	enum FacErrorCode error_code;
+
+	/*! \brief Reject code received from last outstanding message. */
+	enum FacRejectCode reject_code;
+
+	/*!
+	 * \brief Saved struct misdn_bchannel call information when
+	 * attempted to call User-B
+	 */
+	struct {
+		/*! \brief User-A caller id information */
+		struct misdn_party_id caller;
+
+		/*! \brief User-B number information */
+		struct misdn_party_dialing dialed;
+
+		/*! \brief The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */
+		struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc;
+
+		/*! \brief SETUP message bearer capability field code value */
+		int capability;
+
+		/*! \brief TRUE if call made in digital HDLC mode */
+		int hdlc;
+	} redial;
+
+	/*! \brief Dialplan location to indicate User-B free and User-A is free */
+	struct misdn_cc_notify remote_user_free;
+
+	/*! \brief Dialplan location to indicate User-B free and User-A is busy */
+	struct misdn_cc_notify b_free;
+};
+
+/*! \brief mISDN call completion record database */
+static AST_LIST_HEAD_STATIC(misdn_cc_records_db, misdn_cc_record);
+/*! \brief Next call completion record ID to use */
+static __u16 misdn_cc_record_id;
+/*! \brief Next invoke ID to use */
+static __s16 misdn_invoke_id;
+
+static const char misdn_no_response_from_network[] = "No response from network";
+static const char misdn_cc_record_not_found[] = "Call completion record not found";
+
+/* mISDN channel variable names */
+#define MISDN_CC_RECORD_ID	"MISDN_CC_RECORD_ID"
+#define MISDN_CC_STATUS		"MISDN_CC_STATUS"
+#define MISDN_ERROR_MSG		"MISDN_ERROR_MSG"
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 
 ast_mutex_t release_lock;
 
@@ -308,6 +488,16 @@
 	 */
 	struct misdn_bchannel *bc;
 
+#if defined(AST_MISDN_ENHANCEMENTS)
+	/*!
+	 * \brief Peer channel for which call completion was initialized.
+	 */
+	struct misdn_cc_caller *peer;
+
+	/*! \brief Associated call completion record ID (-1 if not associated) */
+	long record_id;
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
 	/*!
 	 * \brief HOLDED channel information
 	 */
@@ -472,7 +662,6 @@
 static int pbx_start_chan(struct chan_list *ch);
 
 #define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt
-#define MISDN_ASTERISK_PVT(ast) 1
 
 #include "asterisk/strings.h"
 
@@ -518,6 +707,10 @@
 static int stop_bc_tones(struct chan_list *cl);
 static void release_chan(struct misdn_bchannel *bc);
 
+#if defined(AST_MISDN_ENHANCEMENTS)
+static const char misdn_command_name[] = "misdn_command";
+static int misdn_command_exec(struct ast_channel *chan, void *data);
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 static int misdn_check_l2l1(struct ast_channel *chan, void *data);
 static int misdn_set_opt_exec(struct ast_channel *chan, void *data);
 static int misdn_facility_exec(struct ast_channel *chan, void *data);
@@ -565,6 +758,1027 @@
 
 	return NULL;
 }
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Destroy the misdn_cc_ds_info datastore payload
+ *
+ * \param[in] data the datastore payload, a reference to an misdn_cc_caller
+ *
+ * \details
+ * Since the payload is a reference to an astobj2 object, we just decrement its
+ * reference count.  Before doing so, we NULL out the channel pointer inside of
+ * the misdn_cc_caller instance.  This function will be called in one of two
+ * cases.  In both cases, we no longer need the channel pointer:
+ *
+ *  - The original channel that initialized call completion services, the same
+ *    channel that is stored here, has been destroyed early.  This could happen
+ *    if it transferred the mISDN channel, for example.
+ *
+ *  - The mISDN channel that had this datastore inherited on to it is now being
+ *    destroyed.  If this is the case, then the call completion events have
+ *    already occurred and the appropriate channel variables have already been
+ *    set on the original channel that requested call completion services.
+ *
+ * \return Nothing
+ */
+static void misdn_cc_ds_destroy(void *data)
+{
+	struct misdn_cc_caller *cc_caller = data;
+
+	ao2_lock(cc_caller);
+	cc_caller->chan = NULL;
+	ao2_unlock(cc_caller);
+
+	ao2_ref(cc_caller, -1);
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Duplicate the misdn_cc_ds_info datastore payload
+ *
+ * \param[in] data the datastore payload, a reference to an misdn_cc_caller
+ *
+ * \details
+ * All we need to do is bump the reference count and return the same instance.
+ *
+ * \return A reference to an instance of a misdn_cc_caller
+ */
+static void *misdn_cc_ds_duplicate(void *data)
+{
+	struct misdn_cc_caller *cc_caller = data;
+
+	ao2_ref(cc_caller, +1);
+
+	return cc_caller;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+static const struct ast_datastore_info misdn_cc_ds_info = {
+	.type      = "misdn_cc",
+	.destroy   = misdn_cc_ds_destroy,
+	.duplicate = misdn_cc_ds_duplicate,
+};
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Set a channel var on the peer channel for call completion services
+ *
+ * \param[in] peer The peer that initialized call completion services
+ * \param[in] var The variable name to set
+ * \param[in] value The variable value to set
+ *
+ * This function may be called from outside of the channel thread.  It handles
+ * the fact that the peer channel may be hung up and destroyed at any time.
+ *
+ * \return nothing
+ */
+static void misdn_cc_set_peer_var(struct misdn_cc_caller *peer, const char *var,
+	const char *value)
+{
+	ao2_lock(peer);
+
+	/*! \todo XXX This nastiness can go away once ast_channel is ref counted! */
+	while (peer->chan && ast_channel_trylock(peer->chan)) {
+		ao2_unlock(peer);
+		sched_yield();
+		ao2_lock(peer);
+	}
+
+	if (peer->chan) {
+		pbx_builtin_setvar_helper(peer->chan, var, value);
+		ast_channel_unlock(peer->chan);
+	}
+
+	ao2_unlock(peer);
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Get a reference to the CC caller if it exists
+ */
+static struct misdn_cc_caller *misdn_cc_caller_get(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct misdn_cc_caller *cc_caller;
+
+	ast_channel_lock(chan);
+
+	if (!(datastore = ast_channel_datastore_find(chan, &misdn_cc_ds_info, NULL))) {
+		ast_channel_unlock(chan);
+		return NULL;
+	}
+
+	ao2_ref(datastore->data, +1);
+	cc_caller = datastore->data;
+
+	ast_channel_unlock(chan);
+
+	return cc_caller;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the record id.
+ *
+ * \param record_id
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_id(long record_id)
+{
+	struct misdn_cc_record *current;
+
+	AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+		if (current->record_id == record_id) {
+			/* Found the record */
+			break;
+		}
+	}
+
+	return current;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and call linkage id.
+ *
+ * \param port Logical port number
+ * \param linkage_id Call linkage ID number from switch.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_linkage(int port, int linkage_id)
+{
+	struct misdn_cc_record *current;
+
+	AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+		if (current->port == port
+			&& !current->ptp
+			&& current->mode.ptmp.linkage_id == linkage_id) {
+			/* Found the record */
+			break;
+		}
+	}
+
+	return current;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and outstanding invocation id.
+ *
+ * \param port Logical port number
+ * \param invoke_id Outstanding message invocation ID number.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_invoke(int port, int invoke_id)
+{
+	struct misdn_cc_record *current;
+
+	AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+		if (current->outstanding_message
+			&& current->invoke_id == invoke_id
+			&& current->port == port) {
+			/* Found the record */
+			break;
+		}
+	}
+
+	return current;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and CCBS reference id.
+ *
+ * \param port Logical port number
+ * \param reference_id CCBS reference ID number from switch.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_reference(int port, int reference_id)
+{
+	struct misdn_cc_record *current;
+
+	AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+		if (current->activated
+			&& current->port == port
+			&& !current->ptp
+			&& current->mode.ptmp.reference_id == reference_id) {
+			/* Found the record */
+			break;
+		}
+	}
+
+	return current;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the B channel pointer
+ *
+ * \param bc B channel control structure pointer.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_bc(const struct misdn_bchannel *bc)
+{
+	struct misdn_cc_record *current;
+
+	if (bc) {
+		AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+			if (current->ptp
+				&& current->mode.ptp.bc == bc) {
+				/* Found the record */
+				break;
+			}
+		}
+	} else {
+		current = NULL;
+	}
+
+	return current;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Delete the given call completion record
+ *
+ * \param doomed Call completion record to destroy
+ *
+ * \return Nothing
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static void misdn_cc_delete(struct misdn_cc_record *doomed)
+{
+	struct misdn_cc_record *current;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) {
+		if (current == doomed) {
+			AST_LIST_REMOVE_CURRENT(list);
+			ast_free(current);
+			return;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	/* The doomed node is not in the call completion database */
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Delete all old call completion records
+ *
+ * \return Nothing
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static void misdn_cc_remove_old(void)
+{
+	struct misdn_cc_record *current;
+	time_t now;
+
+	now = time(NULL);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) {
+		if (MISDN_CC_RECORD_AGE_MAX < now - current->time_created) {
+			if (current->ptp && current->mode.ptp.bc) {
+				/* Close the old call-completion signaling link */
+				current->mode.ptp.bc->fac_out.Function = Fac_None;
+				current->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING;
+				misdn_lib_send_event(current->mode.ptp.bc, EVENT_RELEASE_COMPLETE);
+			}
+
+			/* Remove the old call completion record */
+			AST_LIST_REMOVE_CURRENT(list);
+			ast_free(current);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Allocate the next record id.
+ *
+ * \retval New record id on success.
+ * \retval -1 on error.
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static long misdn_cc_record_id_new(void)
+{
+	long record_id;
+	long first_id;
+
+	record_id = ++misdn_cc_record_id;
+	first_id = record_id;
+	while (misdn_cc_find_by_id(record_id)) {
+		record_id = ++misdn_cc_record_id;
+		if (record_id == first_id) {
+			/*
+			 * We have a resource leak.
+			 * We should never need to allocate 64k records.
+			 */
+			chan_misdn_log(0, 0, " --> ERROR Too many call completion records!\n");
+			record_id = -1;
+			break;
+		}
+	}
+
+	return record_id;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Create a new call completion record
+ *
+ * \retval pointer to new call completion record
+ * \retval NULL if failed
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_new(void)
+{
+	struct misdn_cc_record *cc_record;
+	long record_id;
+
+	misdn_cc_remove_old();
+
+	cc_record = ast_calloc(1, sizeof(*cc_record));
+	if (cc_record) {
+		record_id = misdn_cc_record_id_new();
+		if (record_id < 0) {
+			ast_free(cc_record);
+			return NULL;
+		}
+
+		/* Initialize the new record */
+		cc_record->record_id = record_id;
+		cc_record->port = -1;/* Invalid port so it will never be found this way */
+		cc_record->invoke_id = ++misdn_invoke_id;
+		cc_record->party_a_free = 1;/* Default User-A as free */
+		cc_record->error_code = FacError_None;
+		cc_record->reject_code = FacReject_None;
+		cc_record->time_created = time(NULL);
+
+		/* Insert the new record into the database */
+		AST_LIST_INSERT_HEAD(&misdn_cc_records_db, cc_record, list);
+	}
+	return cc_record;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Destroy the call completion record database
+ *
+ * \return Nothing
+ */
+static void misdn_cc_destroy(void)
+{
+	struct misdn_cc_record *current;
+
+	while ((current = AST_LIST_REMOVE_HEAD(&misdn_cc_records_db, list))) {
+		/* Do a misdn_cc_delete(current) inline */
+		ast_free(current);
+	}
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Initialize the call completion record database
+ *
+ * \return Nothing
+ */
+static void misdn_cc_init(void)
+{
+	misdn_cc_record_id = 0;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Check the status of an outstanding invocation request.
+ *
+ * \param data Points to an integer containing the call completion record id.
+ *
+ * \retval 0 if got a response.
+ * \retval -1 if no response yet.
+ */
+static int misdn_cc_response_check(void *data)
+{
+	int not_responded;
+	struct misdn_cc_record *cc_record;
+
+	AST_LIST_LOCK(&misdn_cc_records_db);
+	cc_record = misdn_cc_find_by_id(*(long *) data);
+	if (cc_record) {
+		if (cc_record->outstanding_message) {
+			not_responded = -1;
+		} else {
+			not_responded = 0;
+		}
+	} else {
+		/* No record so there is no response to check. */
+		not_responded = 0;
+	}
+	AST_LIST_UNLOCK(&misdn_cc_records_db);
+
+	return not_responded;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Wait for a response from the switch for an outstanding
+ * invocation request.
+ *
+ * \param chan Asterisk channel to operate upon.
+ * \param wait_seconds Number of seconds to wait
+ * \param record_id Call completion record ID.
+ *
+ * \return Nothing
+ */
+static void misdn_cc_response_wait(struct ast_channel *chan, int wait_seconds, long record_id)
+{
+	unsigned count;
+
+	for (count = 2 * MISDN_CC_REQUEST_WAIT_MAX; count--;) {
+		/* Sleep in 500 ms increments */
+		if (ast_safe_sleep_conditional(chan, 500, misdn_cc_response_check, &record_id) != 0) {
+			/* We got hung up or our response came in. */
+			break;
+		}
+	}
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN reject code to a string
+ *
+ * \param code mISDN reject code.
+ *
+ * \return The mISDN reject code as a string
+ */
+static const char *misdn_to_str_reject_code(enum FacRejectCode code)
+{
+	static const struct {
+		enum FacRejectCode code;
+		char *name;
+	} arr[] = {
+/* *INDENT-OFF* */
+		{ FacReject_None,                           "No reject occurred" },
+		{ FacReject_Unknown,                        "Unknown reject code" },
+
+		{ FacReject_Gen_UnrecognizedComponent,      "General: Unrecognized Component" },
+		{ FacReject_Gen_MistypedComponent,          "General: Mistyped Component" },
+		{ FacReject_Gen_BadlyStructuredComponent,   "General: Badly Structured Component" },
+
+		{ FacReject_Inv_DuplicateInvocation,        "Invoke: Duplicate Invocation" },
+		{ FacReject_Inv_UnrecognizedOperation,      "Invoke: Unrecognized Operation" },
+		{ FacReject_Inv_MistypedArgument,           "Invoke: Mistyped Argument" },
+		{ FacReject_Inv_ResourceLimitation,         "Invoke: Resource Limitation" },
+		{ FacReject_Inv_InitiatorReleasing,         "Invoke: Initiator Releasing" },
+		{ FacReject_Inv_UnrecognizedLinkedID,       "Invoke: Unrecognized Linked ID" },
+		{ FacReject_Inv_LinkedResponseUnexpected,   "Invoke: Linked Response Unexpected" },
+		{ FacReject_Inv_UnexpectedChildOperation,   "Invoke: Unexpected Child Operation" },
+
+		{ FacReject_Res_UnrecognizedInvocation,     "Result: Unrecognized Invocation" },
+		{ FacReject_Res_ResultResponseUnexpected,   "Result: Result Response Unexpected" },
+		{ FacReject_Res_MistypedResult,             "Result: Mistyped Result" },
+
+		{ FacReject_Err_UnrecognizedInvocation,     "Error: Unrecognized Invocation" },
+		{ FacReject_Err_ErrorResponseUnexpected,    "Error: Error Response Unexpected" },
+		{ FacReject_Err_UnrecognizedError,          "Error: Unrecognized Error" },
+		{ FacReject_Err_UnexpectedError,            "Error: Unexpected Error" },
+		{ FacReject_Err_MistypedParameter,          "Error: Mistyped Parameter" },
+/* *INDENT-ON* */
+	};
+
+	unsigned index;
+
+	for (index = 0; index < ARRAY_LEN(arr); ++index) {
+		if (arr[index].code == code) {
+			return arr[index].name;
+		}
+	}
+
+	return "unknown";
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN error code to a string
+ *
+ * \param code mISDN error code.
+ *
+ * \return The mISDN error code as a string
+ */
+static const char *misdn_to_str_error_code(enum FacErrorCode code)
+{
+	static const struct {
+		enum FacErrorCode code;
+		char *name;
+	} arr[] = {
+/* *INDENT-OFF* */
+		{ FacError_None,                            "No error occurred" },
+		{ FacError_Unknown,                         "Unknown OID error code" },
+
+		{ FacError_Gen_NotSubscribed,               "General: Not Subscribed" },
+		{ FacError_Gen_NotAvailable,                "General: Not Available" },
+		{ FacError_Gen_NotImplemented,              "General: Not Implemented" },
+		{ FacError_Gen_InvalidServedUserNr,         "General: Invalid Served User Number" },
+		{ FacError_Gen_InvalidCallState,            "General: Invalid Call State" },
+		{ FacError_Gen_BasicServiceNotProvided,     "General: Basic Service Not Provided" },
+		{ FacError_Gen_NotIncomingCall,             "General: Not Incoming Call" },
+		{ FacError_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" },
+		{ FacError_Gen_ResourceUnavailable,         "General: Resource Unavailable" },
+
+		{ FacError_Div_InvalidDivertedToNr,         "Diversion: Invalid Diverted To Number" },
+		{ FacError_Div_SpecialServiceNr,            "Diversion: Special Service Number" },
+		{ FacError_Div_DiversionToServedUserNr,     "Diversion: Diversion To Served User Number" },
+		{ FacError_Div_IncomingCallAccepted,        "Diversion: Incoming Call Accepted" },
+		{ FacError_Div_NumberOfDiversionsExceeded,  "Diversion: Number Of Diversions Exceeded" },
+		{ FacError_Div_NotActivated,                "Diversion: Not Activated" },
+		{ FacError_Div_RequestAlreadyAccepted,      "Diversion: Request Already Accepted" },
+
+		{ FacError_AOC_NoChargingInfoAvailable,     "AOC: No Charging Info Available" },
+
+		{ FacError_CCBS_InvalidCallLinkageID,       "CCBS: Invalid Call Linkage ID" },
+		{ FacError_CCBS_InvalidCCBSReference,       "CCBS: Invalid CCBS Reference" },
+		{ FacError_CCBS_LongTermDenial,             "CCBS: Long Term Denial" },
+		{ FacError_CCBS_ShortTermDenial,            "CCBS: Short Term Denial" },
+		{ FacError_CCBS_IsAlreadyActivated,         "CCBS: Is Already Activated" },
+		{ FacError_CCBS_AlreadyAccepted,            "CCBS: Already Accepted" },
+		{ FacError_CCBS_OutgoingCCBSQueueFull,      "CCBS: Outgoing CCBS Queue Full" },
+		{ FacError_CCBS_CallFailureReasonNotBusy,   "CCBS: Call Failure Reason Not Busy" },
+		{ FacError_CCBS_NotReadyForCall,            "CCBS: Not Ready For Call" },
+
+		{ FacError_CCBS_T_LongTermDenial,           "CCBS-T: Long Term Denial" },
+		{ FacError_CCBS_T_ShortTermDenial,          "CCBS-T: Short Term Denial" },
+
+		{ FacError_ECT_LinkIdNotAssignedByNetwork,  "ECT: Link ID Not Assigned By Network" },
+/* *INDENT-ON* */
+	};
+
+	unsigned index;
+
+	for (index = 0; index < ARRAY_LEN(arr); ++index) {
+		if (arr[index].code == code) {
+			return arr[index].name;
+		}
+	}
+
+	return "unknown";
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN redirecting reason to diversion reason.
+ *
+ * \param reason mISDN redirecting reason code.
+ *
+ * \return Supported diversion reason code.
+ */
+static unsigned misdn_to_diversion_reason(enum mISDN_REDIRECTING_REASON reason)
+{
+	unsigned diversion_reason;
+
+	switch (reason) {
+	case mISDN_REDIRECTING_REASON_CALL_FWD:
+		diversion_reason = 1;/* cfu */
+		break;
+	case mISDN_REDIRECTING_REASON_CALL_FWD_BUSY:
+		diversion_reason = 2;/* cfb */
+		break;
+	case mISDN_REDIRECTING_REASON_NO_REPLY:
+		diversion_reason = 3;/* cfnr */
+		break;
+	default:
+		diversion_reason = 0;/* unknown */
+		break;
+	}
+
+	return diversion_reason;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert diversion reason to mISDN redirecting reason
+ *
+ * \param diversion_reason Diversion reason to convert
+ *
+ * \return Supported redirecting reason code.
+ */
+static enum mISDN_REDIRECTING_REASON diversion_reason_to_misdn(unsigned diversion_reason)
+{
+	enum mISDN_REDIRECTING_REASON reason;
+
+	switch (diversion_reason) {
+	case 1:/* cfu */
+		reason = mISDN_REDIRECTING_REASON_CALL_FWD;
+		break;
+	case 2:/* cfb */
+		reason = mISDN_REDIRECTING_REASON_CALL_FWD_BUSY;
+		break;
+	case 3:/* cfnr */
+		reason = mISDN_REDIRECTING_REASON_NO_REPLY;
+		break;
+	default:
+		reason = mISDN_REDIRECTING_REASON_UNKNOWN;
+		break;
+	}
+
+	return reason;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN presentation to PresentedNumberUnscreened type
+ *
+ * \param presentation mISDN presentation to convert
+ * \param number_present TRUE if the number is present
+ *
+ * \return PresentedNumberUnscreened type
+ */
+static unsigned misdn_to_PresentedNumberUnscreened_type(int presentation, int number_present)
+{
+	unsigned type;
+
+	switch (presentation) {
+	case 0:/* allowed */
+		if (number_present) {
+			type = 0;/* presentationAllowedNumber */
+		} else {
+			type = 2;/* numberNotAvailableDueToInterworking */
+		}
+		break;
+	case 1:/* restricted */
+		if (number_present) {
+			type = 3;/* presentationRestrictedNumber */
+		} else {
+			type = 1;/* presentationRestricted */
+		}
+		break;
+	default:
+		type = 2;/* numberNotAvailableDueToInterworking */
+		break;
+	}
+
+	return type;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PresentedNumberUnscreened type to mISDN presentation
+ *
+ * \param type PresentedNumberUnscreened type
+ *
+ * \return mISDN presentation
+ */
+static int PresentedNumberUnscreened_to_misdn_pres(unsigned type)
+{
+	int presentation;
+
+	switch (type) {
+	default:
+	case 0:/* presentationAllowedNumber */
+		presentation = 0;/* allowed */
+		break;
+
+	case 1:/* presentationRestricted */
+	case 3:/* presentationRestrictedNumber */
+		presentation = 1;/* restricted */
+		break;
+
+	case 2:/* numberNotAvailableDueToInterworking */
+		presentation = 2;/* unavailable */
+		break;
+	}
+
+	return presentation;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN numbering plan to PartyNumber numbering plan
+ *
+ * \param number_plan mISDN numbering plan
+ *
+ * \return PartyNumber numbering plan
+ */
+static unsigned misdn_to_PartyNumber_plan(enum mISDN_NUMBER_PLAN number_plan)
+{
+	unsigned party_plan;
+
+	switch (number_plan) {
+	default:
+	case NUMPLAN_UNKNOWN:
+		party_plan = 0;/* unknown */
+		break;
+
+	case NUMPLAN_ISDN:
+		party_plan = 1;/* public */
+		break;
+
+	case NUMPLAN_DATA:
+		party_plan = 3;/* data */
+		break;
+
+	case NUMPLAN_TELEX:
+		party_plan = 4;/* telex */
+		break;
+
+	case NUMPLAN_NATIONAL:
+		party_plan = 8;/* nationalStandard */
+		break;
+
+	case NUMPLAN_PRIVATE:
+		party_plan = 5;/* private */
+		break;
+	}
+
+	return party_plan;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert PartyNumber numbering plan to mISDN numbering plan
+ *
+ * \param party_plan PartyNumber numbering plan
+ *
+ * \return mISDN numbering plan
+ */
+static enum mISDN_NUMBER_PLAN PartyNumber_to_misdn_plan(unsigned party_plan)
+{
+	enum mISDN_NUMBER_PLAN number_plan;
+
+	switch (party_plan) {
+	default:
+	case 0:/* unknown */
+		number_plan = NUMPLAN_UNKNOWN;
+		break;
+	case 1:/* public */
+		number_plan = NUMPLAN_ISDN;
+		break;
+	case 3:/* data */
+		number_plan = NUMPLAN_DATA;
+		break;
+	case 4:/* telex */
+		number_plan = NUMPLAN_TELEX;
+		break;
+	case 8:/* nationalStandard */
+		number_plan = NUMPLAN_NATIONAL;
+		break;
+	case 5:/* private */
+		number_plan = NUMPLAN_PRIVATE;
+		break;
+	}
+
+	return number_plan;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN type-of-number to PartyNumber public type-of-number
+ *
+ * \param ton mISDN type-of-number
+ *
+ * \return PartyNumber public type-of-number
+ */
+static unsigned misdn_to_PartyNumber_ton_public(enum mISDN_NUMBER_TYPE ton)
+{
+	unsigned party_ton;
+
+	switch (ton) {
+	default:
+	case NUMTYPE_UNKNOWN:
+		party_ton = 0;/* unknown */
+		break;
+
+	case NUMTYPE_INTERNATIONAL:
+		party_ton = 1;/* internationalNumber */
+		break;
+
+	case NUMTYPE_NATIONAL:
+		party_ton = 2;/* nationalNumber */
+		break;
+
+	case NUMTYPE_NETWORK_SPECIFIC:
+		party_ton = 3;/* networkSpecificNumber */
+		break;
+
+	case NUMTYPE_SUBSCRIBER:
+		party_ton = 4;/* subscriberNumber */
+		break;
+
+	case NUMTYPE_ABBREVIATED:
+		party_ton = 6;/* abbreviatedNumber */
+		break;
+	}
+
+	return party_ton;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PartyNumber public type-of-number to mISDN type-of-number
+ *
+ * \param party_ton PartyNumber public type-of-number
+ *
+ * \return mISDN type-of-number
+ */
+static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_public(unsigned party_ton)
+{
+	enum mISDN_NUMBER_TYPE ton;
+
+	switch (party_ton) {
+	default:
+	case 0:/* unknown */
+		ton = NUMTYPE_UNKNOWN;
+		break;
+
+	case 1:/* internationalNumber */
+		ton = NUMTYPE_INTERNATIONAL;
+		break;
+
+	case 2:/* nationalNumber */
+		ton = NUMTYPE_NATIONAL;
+		break;
+
+	case 3:/* networkSpecificNumber */
+		ton = NUMTYPE_NETWORK_SPECIFIC;
+		break;
+
+	case 4:/* subscriberNumber */
+		ton = NUMTYPE_SUBSCRIBER;
+		break;
+
+	case 6:/* abbreviatedNumber */
+		ton = NUMTYPE_ABBREVIATED;
+		break;
+	}
+
+	return ton;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN type-of-number to PartyNumber private type-of-number
+ *
+ * \param ton mISDN type-of-number
+ *
+ * \return PartyNumber private type-of-number
+ */
+static unsigned misdn_to_PartyNumber_ton_private(enum mISDN_NUMBER_TYPE ton)
+{
+	unsigned party_ton;
+
+	switch (ton) {
+	default:
+	case NUMTYPE_UNKNOWN:
+		party_ton = 0;/* unknown */
+		break;
+
+	case NUMTYPE_INTERNATIONAL:
+		party_ton = 1;/* level2RegionalNumber */
+		break;
+
+	case NUMTYPE_NATIONAL:
+		party_ton = 2;/* level1RegionalNumber */
+		break;
+
+	case NUMTYPE_NETWORK_SPECIFIC:
+		party_ton = 3;/* pTNSpecificNumber */
+		break;
+
+	case NUMTYPE_SUBSCRIBER:
+		party_ton = 4;/* localNumber */
+		break;
+
+	case NUMTYPE_ABBREVIATED:
+		party_ton = 6;/* abbreviatedNumber */
+		break;
+	}
+
+	return party_ton;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PartyNumber private type-of-number to mISDN type-of-number
+ *
+ * \param party_ton PartyNumber private type-of-number
+ *
+ * \return mISDN type-of-number
+ */
+static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_private(unsigned party_ton)
+{
+	enum mISDN_NUMBER_TYPE ton;
+
+	switch (party_ton) {
+	default:
+	case 0:/* unknown */
+		ton = NUMTYPE_UNKNOWN;
+		break;
+
+	case 1:/* level2RegionalNumber */
+		ton = NUMTYPE_INTERNATIONAL;
+		break;
+
+	case 2:/* level1RegionalNumber */
+		ton = NUMTYPE_NATIONAL;
+		break;
+
+	case 3:/* pTNSpecificNumber */
+		ton = NUMTYPE_NETWORK_SPECIFIC;
+		break;
+
+	case 4:/* localNumber */
+		ton = NUMTYPE_SUBSCRIBER;
+		break;
+
+	case 6:/* abbreviatedNumber */
+		ton = NUMTYPE_ABBREVIATED;
+		break;
+	}
+
+	return ton;
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 
 /*!
  * \internal
@@ -1128,25 +2342,589 @@
 	return "Unknown Bearer";
 }
 
-
-static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc)
-{
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Fill in facility PartyNumber information
+ *
+ * \param party PartyNumber structure to fill in.
+ * \param id Information to put in PartyNumber structure.
+ *
+ * \return Nothing
+ */
+static void misdn_PartyNumber_fill(struct FacPartyNumber *party, const struct misdn_party_id *id)
+{
+	ast_copy_string((char *) party->Number, id->number, sizeof(party->Number));
+	party->LengthOfNumber = strlen((char *) party->Number);
+	party->Type = misdn_to_PartyNumber_plan(id->number_plan);
+	switch (party->Type) {
+	case 1:/* public */
+		party->TypeOfNumber = misdn_to_PartyNumber_ton_public(id->number_type);
+		break;
+	case 5:/* private */
+		party->TypeOfNumber = misdn_to_PartyNumber_ton_private(id->number_type);
+		break;
+	default:
+		party->TypeOfNumber = 0;/* Dont't care */
+		break;
+	}
+}
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Extract the information from PartyNumber
+ *
+ * \param id Where to put extracted PartyNumber information
+ * \param party PartyNumber information to extract
+ *
+ * \return Nothing
+ */
+static void misdn_PartyNumber_extract(struct misdn_party_id *id, const struct FacPartyNumber *party)
+{
+	if (party->LengthOfNumber) {
+		ast_copy_string(id->number, (char *) party->Number, sizeof(id->number));
+		id->number_plan = PartyNumber_to_misdn_plan(party->Type);
+		switch (party->Type) {
+		case 1:/* public */

[... 7931 lines stripped ...]



More information about the svn-commits mailing list