[libpri-commits] rmudgett: branch rmudgett/deflection r1163 - /team/rmudgett/deflection/

SVN commits to the libpri project libpri-commits at lists.digium.com
Thu Oct 8 12:53:43 CDT 2009


Author: rmudgett
Date: Thu Oct  8 12:53:39 2009
New Revision: 1163

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1163
Log:
Initial call deflection/rerouting support.

Modified:
    team/rmudgett/deflection/libpri.h
    team/rmudgett/deflection/pri.c
    team/rmudgett/deflection/pri_facility.c
    team/rmudgett/deflection/pri_facility.h
    team/rmudgett/deflection/pri_internal.h
    team/rmudgett/deflection/pri_q931.h
    team/rmudgett/deflection/q931.c

Modified: team/rmudgett/deflection/libpri.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/deflection/libpri.h?view=diff&rev=1163&r1=1162&r2=1163
==============================================================================
--- team/rmudgett/deflection/libpri.h (original)
+++ team/rmudgett/deflection/libpri.h Thu Oct  8 12:53:39 2009
@@ -476,9 +476,37 @@
 	int reason;
 };
 
+/*!
+ * \brief Information for rerouting/deflecting the call.
+ */
+struct pri_rerouting_data {
+	/*!
+	 * \brief Updated caller-id information.
+	 * \note The information may have been altered by procedure in the private network.
+	 */
+	struct pri_party_id caller;
+	/*!
+	 * \note
+	 * deflection.to is the new called number and must always be present.
+	 */
+	struct pri_party_redirecting deflection;
+	/*!
+	 * \brief Diverting user subscription option to specify if caller is notified.
+	 * \details
+	 * noNotification(0),
+	 * notificationWithoutDivertedToNr(1),
+	 * notificationWithDivertedToNr(2),
+	 * notApplicable(3) (Status only.)
+	 */
+	int subscription_option;
+	/*! Invocation ID to use when sending a reply to the call rerouting/deflection request. */
+	int invoke_id;
+};
+
 /* Subcommands derived from supplementary services. */
 #define PRI_SUBCMD_REDIRECTING		1
 #define PRI_SUBCMD_CONNECTED_LINE	2
+#define PRI_SUBCMD_REROUTING		3
 
 
 struct pri_subcommand {
@@ -489,6 +517,7 @@
 		char reserve_space[512];
 		struct pri_party_connected_line connected_line;
 		struct pri_party_redirecting redirecting;
+		struct pri_rerouting_data rerouting;
 	} u;
 };
 
@@ -576,10 +605,16 @@
 	char callingnum[256];		/*!< Deprecated, preserved for struct pri_event_facname compatibility */
 	int channel;
 	int cref;
+	/*!
+	 * \brief Master call or normal call.
+	 * \note Call pointer known about by upper layer.
+	 * \note NULL if dummy call reference.
+	 */
 	q931_call *call;
 	int callingpres;			/*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */
 	int callingplan;			/*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */
 	struct pri_subcommands *subcmds;
+	q931_call *subcall;			/*!< Subcall to send any reply toward. */
 };
 
 #define PRI_CALLINGPLANANI
@@ -1025,6 +1060,63 @@
 int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason);
 
 /*!
+ * \brief Set the call deflection/rerouting feature enable flag.
+ *
+ * \param ctrl D channel controller.
+ * \param enable TRUE to enable call deflection/rerouting feature.
+ *
+ * \return Nothing
+ */
+void pri_reroute_enable(struct pri *ctrl, int enable);
+
+/*!
+ * \brief Send the CallRerouting/CallDeflection message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.)
+ * \param deflection Call rerouting/deflecting redirection data.
+ * \param subscription_option Diverting user subscription option to specify if caller is notified.
+ *
+ * \note
+ * deflection->to is the new called number and must always be present.
+ * \note
+ * subscription option:
+ * noNotification(0),
+ * notificationWithoutDivertedToNr(1),
+ * notificationWithDivertedToNr(2)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option);
+
+enum PRI_REROUTING_RSP_CODE {
+	PRI_REROUTING_RSP_OK,
+	PRI_REROUTING_RSP_NOT_SUBSCRIBED,
+	PRI_REROUTING_RSP_NOT_AVAILABLE,
+	PRI_REROUTING_RSP_NOT_ALLOWED,/* Supplementary service interaction not allowed. */
+	PRI_REROUTING_RSP_INVALID_NUMBER,
+	PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER,/* Deflection to prohibited number (e.g., operator, police, emergency). */
+	PRI_REROUTING_RSP_DIVERSION_TO_SELF,/* Deflection to served user number. */
+	PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED,
+	PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE,
+};
+
+/*!
+ * \brief Send the CallRerouteing/CallDeflection response message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param invoke_id Value given by the initiating request.
+ * \param code The result to send.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code);
+
+/*!
  * \brief Set the call hold feature enable flag.
  *
  * \param ctrl D channel controller.

Modified: team/rmudgett/deflection/pri.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/deflection/pri.c?view=diff&rev=1163&r1=1162&r2=1163
==============================================================================
--- team/rmudgett/deflection/pri.c (original)
+++ team/rmudgett/deflection/pri.c Thu Oct  8 12:53:39 2009
@@ -551,15 +551,6 @@
 	return q931_keypad_facility(pri, call, digits);
 }
 
-
-int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason)
-{
-	if (!pri || !call)
-		return -1;
-
-	return qsig_cf_callrerouting(pri, call, dest, original, reason);
-}
-
 int pri_notify(struct pri *pri, q931_call *call, int channel, int info)
 {
 	if (!pri || !call)
@@ -883,7 +874,7 @@
 		return -1;
 	if (cause == -1)
 		/* normal clear cause */
-		cause = 16;
+		cause = PRI_CAUSE_NORMAL_CLEARING;
 	return q931_hangup(pri, call, cause);
 }
 
@@ -1369,6 +1360,66 @@
 	return q931_send_retrieve_rej(ctrl, call, cause);
 }
 
+int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason)
+{
+	if (!pri || !call || !dest)
+		return -1;
+
+	return qsig_cf_callrerouting(pri, call, dest, original, reason);
+}
+
+void pri_reroute_enable(struct pri *ctrl, int enable)
+{
+	ctrl = PRI_MASTER(ctrl);
+	if (ctrl) {
+		ctrl->deflection_support = enable ? 1 : 0;
+	}
+}
+
+int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option)
+{
+	const struct q931_party_id *caller_id;
+	struct q931_party_id local_caller;
+	struct q931_party_redirecting reroute;
+
+	if (!ctrl || !call || !deflection) {
+		return -1;
+	}
+
+	if (caller) {
+		/* Convert the caller update information. */
+		pri_copy_party_id_to_q931(&local_caller, caller);
+		q931_party_id_fixup(ctrl, &local_caller);
+		caller_id = &local_caller;
+	} else {
+		caller_id = NULL;
+	}
+
+	/* Convert the deflection information. */
+	q931_party_redirecting_init(&reroute);
+	pri_copy_party_id_to_q931(&reroute.from, &deflection->from);
+	q931_party_id_fixup(ctrl, &reroute.from);
+	pri_copy_party_id_to_q931(&reroute.to, &deflection->to);
+	q931_party_id_fixup(ctrl, &reroute.to);
+	pri_copy_party_id_to_q931(&reroute.orig_called, &deflection->orig_called);
+	q931_party_id_fixup(ctrl, &reroute.orig_called);
+	reroute.reason = deflection->reason;
+	reroute.orig_reason = deflection->orig_reason;
+	if (deflection->count <= 0) {
+		/*
+		 * We are deflecting with an unknown count
+		 * so assume the count is one.
+		 */
+		reroute.count = 1;
+	} else if (deflection->count < PRI_MAX_REDIRECTS) {
+		reroute.count = deflection->count;
+	} else {
+		reroute.count = PRI_MAX_REDIRECTS;
+	}
+
+	return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option);
+}
+
 void pri_sr_set_reversecharge(struct pri_sr *sr, int requested)
 {
 	sr->reversecharge = requested;

Modified: team/rmudgett/deflection/pri_facility.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/deflection/pri_facility.c?view=diff&rev=1163&r1=1162&r2=1163
==============================================================================
--- team/rmudgett/deflection/pri_facility.c (original)
+++ team/rmudgett/deflection/pri_facility.c Thu Oct  8 12:53:39 2009
@@ -514,6 +514,25 @@
 		sizeof(q931_number->str));
 	q931_number->plan = numbering_plan_for_q931(ctrl, rose_number->plan)
 		| typeofnumber_for_q931(ctrl, rose_number->ton);
+	q931_number->valid = 1;
+}
+
+/*!
+ * \internal
+ * \brief Copy the given rose address to the q931_party_id address.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param q931_address Q.931 party id structure to fill address
+ * \param rose_address ROSE address structure
+ *
+ * \note It is assumed that the q931_address has been initialized before calling.
+ *
+ * \return Nothing
+ */
+static void rose_copy_address_to_q931(struct pri *ctrl,
+	struct q931_party_id *q931_address, const struct roseAddress *rose_address)
+{
+	rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_address->number);
 }
 
 /*!
@@ -580,28 +599,29 @@
  * \brief Copy the given rose presented screened party address to the q931_party_number
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
- * \param q931_number Q.931 party number structure
+ * \param q931_address Q.931 party id structure to fill the address
  * \param rose_presented ROSE presented screened party address structure
  *
  * \return Nothing
  */
 static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl,
-	struct q931_party_number *q931_number,
+	struct q931_party_id *q931_address,
 	const struct rosePresentedAddressScreened *rose_presented)
 {
-	q931_party_number_init(q931_number);
-	q931_number->valid = 1;
-	q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation);
+	q931_party_number_init(&q931_address->number);
+	q931_address->number.valid = 1;
+	q931_address->number.presentation = presentation_for_q931(ctrl,
+		rose_presented->presentation);
 	switch (rose_presented->presentation) {
 	case 0:	/* presentationAllowedAddress */
 	case 3:	/* presentationRestrictedAddress */
-		q931_number->presentation |=
+		q931_address->number.presentation |=
 			(rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE);
-		rose_copy_number_to_q931(ctrl, q931_number,
+		rose_copy_number_to_q931(ctrl, &q931_address->number,
 			&rose_presented->screened.number);
 		break;
 	default:
-		q931_number->presentation |= PRI_PRES_USER_NUMBER_UNSCREENED;
+		q931_address->number.presentation |= PRI_PRES_USER_NUMBER_UNSCREENED;
 		break;
 	}
 }
@@ -646,6 +666,22 @@
 	libpri_copy_string((char *) rose_number->str, q931_number->str,
 		sizeof(rose_number->str));
 	rose_number->length = strlen((char *) rose_number->str);
+}
+
+/*!
+ * \internal
+ * \brief Copy the given q931_party_id address to the rose address.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param rose_address ROSE address structure
+ * \param q931_address Q.931 party id structure to give address
+ *
+ * \return Nothing
+ */
+static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address,
+	const struct q931_party_id *q931_address)
+{
+	q931_copy_number_to_rose(ctrl, &rose_address->number, &q931_address->number);
 }
 
 /*!
@@ -703,20 +739,22 @@
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param rose_presented ROSE presented screened party address structure
- * \param q931_number Q.931 party number structure
+ * \param q931_address Q.931 party id structure to get the address
  *
  * \return Nothing
  */
 static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl,
 	struct rosePresentedAddressScreened *rose_presented,
-	const struct q931_party_number *q931_number)
-{
-	if (q931_number->valid) {
+	const struct q931_party_id *q931_address)
+{
+	if (q931_address->number.valid) {
 		rose_presented->presentation =
-			presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]);
+			presentation_from_q931(ctrl, q931_address->number.presentation,
+				q931_address->number.str[0]);
 		rose_presented->screened.screening_indicator =
-			q931_number->presentation & PRI_PRES_NUMBER_TYPE;
-		q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, q931_number);
+			q931_address->number.presentation & PRI_PRES_NUMBER_TYPE;
+		q931_copy_number_to_rose(ctrl, &rose_presented->screened.number,
+			&q931_address->number);
 		rose_presented->screened.subaddress.length = 0;
 	} else {
 		rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */
@@ -1654,17 +1692,24 @@
  * \param pos Starting position to encode the facility ie contents.
  * \param end End of facility ie contents encoding data buffer.
  * \param call Q.931 call leg.
- * \param calling Calling party info.
- * \param dest Destination number.
- * \param original Original called number.
- * \param reason Rerouting reason: cfu, cfb, cfnr
+ * \param calling Call rerouting/deflecting updated caller data.
+ * \param deflection Call rerouting/deflecting redirection data.
+ * \param subscription_option Diverting user subscription option to specify if caller is notified.
+ *
+ * \note
+ * deflection->to is the new called number and must always be present.
+ * \note
+ * subscription option:
+ * noNotification(0),
+ * notificationWithoutDivertedToNr(1),
+ * notificationWithDivertedToNr(2)
  *
  * \retval Start of the next ASN.1 component to encode on success.
  * \retval NULL on error.
  */
 static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *pos,
 	unsigned char *end, q931_call *call, const struct q931_party_id *calling,
-	const char *dest, const char *original, const char *reason)
+	const struct q931_party_redirecting *deflection, int subscription_option)
 {
 	struct fac_extension_header header;
 	struct rose_msg_invoke msg;
@@ -1685,26 +1730,13 @@
 	msg.operation = ROSE_QSIG_CallRerouting;
 	msg.invoke_id = get_invokeid(ctrl);
 
-	/* The rerouting_reason defaults to unknown */
-	if (reason) {
-		if (!strcasecmp(reason, "cfu")) {
-			msg.args.qsig.CallRerouting.rerouting_reason = 1;	/* cfu */
-		} else if (!strcasecmp(reason, "cfb")) {
-			msg.args.qsig.CallRerouting.rerouting_reason = 2;	/* cfb */
-		} else if (!strcasecmp(reason, "cfnr")) {
-			msg.args.qsig.CallRerouting.rerouting_reason = 3;	/* cfnr */
-		}
-	}
-
-	/* calledAddress is the passed in dest number */
-	msg.args.qsig.CallRerouting.called.number.plan = 1;	/* public */
-	msg.args.qsig.CallRerouting.called.number.ton = 0;	/* unknown */
-	libpri_copy_string((char *) msg.args.qsig.CallRerouting.called.number.str, dest,
-		sizeof(msg.args.qsig.CallRerouting.called.number.str));
-	msg.args.qsig.CallRerouting.called.number.length = strlen((char *)
-		msg.args.qsig.CallRerouting.called.number.str);
-
-	msg.args.qsig.CallRerouting.diversion_counter = call->redirecting.count + 1;
+	msg.args.qsig.CallRerouting.rerouting_reason =
+		redirectingreason_from_q931(ctrl, deflection->reason);
+
+	/* calledAddress is the passed in deflection->to address */
+	q931_copy_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to);
+
+	msg.args.qsig.CallRerouting.diversion_counter = deflection->count;
 
 	/* pSS1InfoElement */
 	q931ie_pos = msg.args.qsig.CallRerouting.q931ie_contents;
@@ -1720,30 +1752,276 @@
 	msg.args.qsig.CallRerouting.q931ie.length = q931ie_pos
 		- msg.args.qsig.CallRerouting.q931ie_contents;
 
-	/* lastReroutingNr is the passed in original number */
-	msg.args.qsig.CallRerouting.last_rerouting.presentation = 0;	/* presentationAllowedNumber */
-	msg.args.qsig.CallRerouting.last_rerouting.number.plan = 1;	/* public */
-	msg.args.qsig.CallRerouting.last_rerouting.number.ton = 0;	/* unknown */
-	libpri_copy_string((char *) msg.args.qsig.CallRerouting.last_rerouting.number.str,
-		original, sizeof(msg.args.qsig.CallRerouting.last_rerouting.number.str));
-	msg.args.qsig.CallRerouting.last_rerouting.number.length = strlen((char *)
-		msg.args.qsig.CallRerouting.last_rerouting.number.str);
-
-	msg.args.qsig.CallRerouting.subscription_option = 0;	/* noNotification */
+	/* lastReroutingNr is the passed in deflection->from.number */
+	q931_copy_presented_number_unscreened_to_rose(ctrl,
+		&msg.args.qsig.CallRerouting.last_rerouting, &deflection->from.number);
+
+	msg.args.qsig.CallRerouting.subscription_option = subscription_option;
 
 	/* callingNumber is the passed in calling->number */
 	q931_copy_presented_number_screened_to_rose(ctrl,
 		&msg.args.qsig.CallRerouting.calling, &calling->number);
 
+	/* callingPartySubaddress is the passed in calling->subaddress if valid */
+
 	/* callingName is the passed in calling->name if valid */
 	if (calling->name.valid) {
 		msg.args.qsig.CallRerouting.calling_name_present = 1;
-		q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name, &calling->name);
+		q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name,
+			&calling->name);
+	}
+
+	if (1 < deflection->count) {
+		/* originalCalledNr is the deflection->orig_called.number */
+		msg.args.qsig.CallRerouting.original_called_present = 1;
+		q931_copy_presented_number_unscreened_to_rose(ctrl,
+			&msg.args.qsig.CallRerouting.original_called,
+			&deflection->orig_called.number);
+
+		msg.args.qsig.CallRerouting.original_rerouting_reason_present = 1;
+		if (deflection->orig_called.number.valid) {
+			msg.args.qsig.CallRerouting.original_rerouting_reason =
+				redirectingreason_from_q931(ctrl, deflection->orig_reason);
+		} else {
+			msg.args.qsig.CallRerouting.original_rerouting_reason =
+				QSIG_DIVERT_REASON_UNKNOWN;
+		}
+
+		/* originalCalledName is the deflection->orig_called.name */
+		if (deflection->orig_called.name.valid) {
+			msg.args.qsig.CallRerouting.original_called_name_present = 1;
+			q931_copy_name_to_rose(ctrl,
+				&msg.args.qsig.CallRerouting.original_called_name,
+				&deflection->orig_called.name);
+		}
 	}
 
 	pos = rose_encode_invoke(ctrl, pos, end, &msg);
 
 	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI CallRerouting invoke message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Q.931 call leg.
+ * \param calling Call rerouting/deflecting updated caller data.
+ * \param deflection Call rerouting/deflecting redirection data.
+ * \param subscription_option Diverting user subscription option to specify if caller is notified.
+ *
+ * \note
+ * deflection->to is the new called number and must always be present.
+ * \note
+ * subscription option:
+ * noNotification(0),
+ * notificationWithoutDivertedToNr(1),
+ * notificationWithDivertedToNr(2)
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, const struct q931_party_id *calling,
+	const struct q931_party_redirecting *deflection, int subscription_option)
+{
+	struct rose_msg_invoke msg;
+	unsigned char *q931ie_pos;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.operation = ROSE_ETSI_CallRerouting;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	msg.args.etsi.CallRerouting.rerouting_reason =
+		redirectingreason_from_q931(ctrl, deflection->reason);
+
+	/* calledAddress is the passed in deflection->to address */
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address,
+		&deflection->to);
+
+	msg.args.etsi.CallRerouting.rerouting_counter = deflection->count;
+
+	/* q931InfoElement */
+	q931ie_pos = msg.args.etsi.CallRerouting.q931ie_contents;
+	*q931ie_pos++ = 0x04;	/* Bearer Capability IE */
+	*q931ie_pos++ = 0x03;	/* len */
+	*q931ie_pos++ = 0x80 | call->transcapability;	/* Rxed transfer capability. */
+	*q931ie_pos++ = 0x90;	/* circuit mode, 64kbit/s */
+	*q931ie_pos++ = 0xa3;	/* level1 protocol, a-law */
+	msg.args.etsi.CallRerouting.q931ie.length = q931ie_pos
+		- msg.args.etsi.CallRerouting.q931ie_contents;
+
+	/* lastReroutingNr is the passed in deflection->from.number */
+	q931_copy_presented_number_unscreened_to_rose(ctrl,
+		&msg.args.etsi.CallRerouting.last_rerouting, &deflection->from.number);
+
+	msg.args.etsi.CallRerouting.subscription_option = subscription_option;
+
+	/* callingPartySubaddress is the passed in calling->subaddress if valid */
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode the ETSI CallDeflection invoke message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Q.931 call leg.
+ * \param deflection Call deflection address.
+ *
+ * \note
+ * deflection is the new called number and must always be present.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_call_deflection(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, const struct q931_party_id *deflection)
+{
+	struct rose_msg_invoke msg;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.operation = ROSE_ETSI_CallDeflection;
+	msg.invoke_id = get_invokeid(ctrl);
+
+	/* deflectionAddress is the passed in deflection->to address */
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallDeflection.deflection,
+		deflection);
+
+	msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1;
+	switch (deflection->number.presentation & PRI_PRES_RESTRICTION) {
+	case PRI_PRES_ALLOWED:
+		msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1;
+		break;
+	default:
+	case PRI_PRES_UNAVAILABLE:
+	case PRI_PRES_RESTRICTED:
+		break;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue the CallRerouting/CallDeflection message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.)
+ * \param deflection Call rerouting/deflecting redirection data.
+ * \param subscription_option Diverting user subscription option to specify if caller is notified.
+ *
+ * \note
+ * deflection->to is the new called number and must always be present.
+ * \note
+ * subscription option:
+ * noNotification(0),
+ * notificationWithoutDivertedToNr(1),
+ * notificationWithDivertedToNr(2)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_reroute_request_encode(struct pri *ctrl, q931_call *call,
+	const struct q931_party_id *caller, const struct q931_party_redirecting *deflection,
+	int subscription_option)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	if (!caller) {
+		/*
+		 * We are deflecting an incoming call back to the network.
+		 * Therefore, the Caller-ID is the remote party.
+		 */
+		caller = &call->remote_id;
+	}
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		if (q931_is_ptmp(ctrl)) {
+			end =
+				enc_etsi_call_deflection(ctrl, buffer, buffer + sizeof(buffer), call,
+					&deflection->to);
+		} else {
+			end =
+				enc_etsi_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call,
+					caller, deflection, subscription_option);
+		}
+		break;
+	case PRI_SWITCH_QSIG:
+		end =
+			enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, caller,
+				deflection, subscription_option);
+		break;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL);
+}
+
+/*!
+ * \brief Send the CallRerouting/CallDeflection message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.)
+ * \param deflection Call rerouting/deflecting redirection data.
+ * \param subscription_option Diverting user subscription option to specify if caller is notified.
+ *
+ * \note
+ * deflection->to is the new called number and must always be present.
+ * \note
+ * subscription option:
+ * noNotification(0),
+ * notificationWithoutDivertedToNr(1),
+ * notificationWithDivertedToNr(2)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int send_reroute_request(struct pri *ctrl, q931_call *call,
+	const struct q931_party_id *caller, const struct q931_party_redirecting *deflection,
+	int subscription_option)
+{
+	if (!deflection->to.number.str[0]) {
+		/* Must have a deflect to number.  That is the point of deflection. */
+		return -1;
+	}
+	if (rose_reroute_request_encode(ctrl, call, caller, deflection, subscription_option)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CallRerouting/CallDeflection message.\n");
+		return -1;
+	}
+
+	return 0;
 }
 
 /*!
@@ -1761,38 +2039,57 @@
 int qsig_cf_callrerouting(struct pri *ctrl, q931_call *call, const char *dest,
 	const char *original, const char *reason)
 {
-	unsigned char buffer[255];
-	unsigned char *end;
-	int res;
-
-	/*
-	 * We are deflecting an incoming call back to the network.
-	 * Therefore, the Caller-ID is the remote party.
-	 */
-	end =
-		enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call,
-			&call->remote_id, dest, original ? original : call->called.number.str,
-			reason);
-	if (!end) {
-		return -1;
-	}
-
-	res = pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL);
-	if (res) {
-		pri_message(ctrl, "Could not queue ADPU in facility message\n");
-		return -1;
-	}
-
-	/* Remember that if we queue a facility IE for a facility message we
-	 * have to explicitly send the facility message ourselves */
-
-	res = q931_facility(call->pri, call);
-	if (res) {
-		pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr);
-		return -1;
-	}
-
-	return 0;
+	struct q931_party_redirecting reroute;
+
+	q931_party_redirecting_init(&reroute);
+
+	/* Rerouting to the dest number. */
+	reroute.to.number.valid = 1;
+	reroute.to.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
+	reroute.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
+	libpri_copy_string(reroute.to.number.str, dest, sizeof(reroute.to.number.str));
+
+	/* Rerouting from the original number. */
+	if (original) {
+		reroute.from.number.valid = 1;
+		reroute.from.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
+		libpri_copy_string(reroute.from.number.str, original, sizeof(reroute.from.number.str));
+	} else {
+		reroute.from.number = call->called.number;
+	}
+	reroute.from.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
+
+	/* Decode the rerouting reason. */
+	reroute.reason = PRI_REDIR_UNKNOWN;
+	if (!reason) {
+		/* No reason for rerouting given. */
+	} else if (!strcasecmp(reason, "cfu")) {
+		reroute.reason = PRI_REDIR_UNCONDITIONAL;
+	} else if (!strcasecmp(reason, "cfb")) {
+		reroute.reason = PRI_REDIR_FORWARD_ON_BUSY;
+	} else if (!strcasecmp(reason, "cfnr")) {
+		reroute.reason = PRI_REDIR_FORWARD_ON_NO_REPLY;
+	}
+
+	reroute.count = (call->redirecting.count < PRI_MAX_REDIRECTS)
+		? call->redirecting.count + 1 : PRI_MAX_REDIRECTS;
+
+	if (!call->redirecting.orig_called.number.valid) {
+		/*
+		 * Since we do not already have an originally called party, we
+		 * must either be the first redirected to party or this call
+		 * has not been redirected before.
+		 *
+		 * Preserve who redirected to us as the originally called party.
+		 */
+		reroute.orig_called = call->redirecting.from;
+		reroute.orig_reason = call->redirecting.reason;
+	} else {
+		reroute.orig_called = call->redirecting.orig_called;
+		reroute.orig_reason = call->redirecting.orig_reason;
+	}
+
+	return send_reroute_request(ctrl, call, NULL, &reroute, 0 /* noNotification */);
 }
 /* End QSIG CC-CallRerouting */
 
@@ -2426,17 +2723,323 @@
 }
 
 /*!
+ * \internal
+ * \brief Encode a plain facility ETSI error code.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Call leg from which to encode error message response.
+ * \param invoke_id Invoke id to put in error message response.
+ * \param code Error code to put in error message response.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_error(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code)
+{
+	struct rose_msg_error msg;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.invoke_id = invoke_id;
+	msg.code = code;
+
+	pos = rose_encode_error(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode a plain facility Q.SIG error code.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Call leg from which to encode error message response.
+ * \param invoke_id Invoke id to put in error message response.
+ * \param code Error code to put in error message response.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_qsig_error(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code)
+{
+	struct fac_extension_header header;
+	struct rose_msg_error msg;
+
+	memset(&header, 0, sizeof(header));
+	header.nfe_present = 1;
+	header.nfe.source_entity = 0;	/* endPINX */
+	header.nfe.destination_entity = 0;	/* endPINX */
+	header.interpretation_present = 1;
+	header.interpretation = 0;	/* discardAnyUnrecognisedInvokePdu */
+	pos = facility_encode_header(ctrl, pos, end, &header);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.invoke_id = invoke_id;
+	msg.code = code;
+
+	pos = rose_encode_error(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a plain facility error code.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode error message response.
+ * \param invoke_id Invoke id to put in error message response.
+ * \param code Error code to put in error message response.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int invoke_id,
+	enum rose_error_code code)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		end =
+			enc_etsi_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code);
+		break;
+	case PRI_SWITCH_QSIG:
+		end =
+			enc_qsig_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code);
+		break;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL);
+}
+
+/*!
+ * \brief Encode and send a plain facility error code.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode error message response.
+ * \param invoke_id Invoke id to put in error message response.
+ * \param code Error code to put in error message response.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id,
+	enum rose_error_code code)
+{
+	if (rose_facility_error_encode(ctrl, call, invoke_id, code)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for error message.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode a plain facility ETSI result ok.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Call leg from which to encode result ok message response.
+ * \param invoke_id Invoke id to put in result ok message response.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_result_ok(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, int invoke_id)
+{
+	struct rose_msg_result msg;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.invoke_id = invoke_id;
+	msg.operation = ROSE_None;
+
+	pos = rose_encode_result(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode a plain facility Q.SIG result ok.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param call Call leg from which to encode result ok message response.
+ * \param invoke_id Invoke id to put in result ok message response.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos,
+	unsigned char *end, q931_call *call, int invoke_id)
+{
+	struct fac_extension_header header;
+	struct rose_msg_result msg;
+
+	memset(&header, 0, sizeof(header));
+	header.nfe_present = 1;
+	header.nfe.source_entity = 0;	/* endPINX */
+	header.nfe.destination_entity = 0;	/* endPINX */
+	header.interpretation_present = 1;
+	header.interpretation = 0;	/* discardAnyUnrecognisedInvokePdu */
+	pos = facility_encode_header(ctrl, pos, end, &header);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.invoke_id = invoke_id;
+	msg.operation = ROSE_None;
+
+	pos = rose_encode_result(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a plain facility result ok.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode result ok message response.
+ * \param invoke_id Invoke id to put in result ok message response.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_facility_result_ok_encode(struct pri *ctrl, q931_call *call, int invoke_id)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		end =
+			enc_etsi_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id);
+		break;
+	case PRI_SWITCH_QSIG:
+		end =
+			enc_qsig_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id);
+		break;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL);
+}
+
+/*!
+ * \brief Encode and send a plain facility result ok.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode result ok message response.
+ * \param invoke_id Invoke id to put in result ok message response.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id)
+{
+	if (rose_facility_result_ok_encode(ctrl, call, invoke_id)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for result OK message.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code)
+{
+	enum rose_error_code rose_err;
+
+	if (!ctrl || !call) {
+		return -1;
+	}
+
+	/* Convert the public rerouting response code to an error code or result ok. */
+	rose_err = ROSE_ERROR_Gen_ResourceUnavailable;
+	switch (code) {
+	case PRI_REROUTING_RSP_OK:
+		return send_facility_result_ok(ctrl, call, invoke_id);
+	case PRI_REROUTING_RSP_NOT_SUBSCRIBED:
+		rose_err = ROSE_ERROR_Gen_NotSubscribed;
+		break;
+	case PRI_REROUTING_RSP_NOT_AVAILABLE:
+		rose_err = ROSE_ERROR_Gen_NotAvailable;
+		break;
+	case PRI_REROUTING_RSP_NOT_ALLOWED:
+		rose_err = ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed;
+		break;
+	case PRI_REROUTING_RSP_INVALID_NUMBER:
+		rose_err = ROSE_ERROR_Div_InvalidDivertedToNr;
+		break;
+	case PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER:
+		rose_err = ROSE_ERROR_Div_SpecialServiceNr;
+		break;
+	case PRI_REROUTING_RSP_DIVERSION_TO_SELF:
+		rose_err = ROSE_ERROR_Div_DiversionToServedUserNr;
+		break;
+	case PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED:
+		rose_err = ROSE_ERROR_Div_NumberOfDiversionsExceeded;
+		break;
+	case PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE:
+		rose_err = ROSE_ERROR_Gen_ResourceUnavailable;
+		break;
+	}
+	return send_facility_error(ctrl, call, invoke_id, rose_err);
+}
+
+/*!
  * \brief Handle the ROSE reject message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param call Call leg from which the message came.
+ * \param msgtype Q.931 message type ie is in.
  * \param ie Raw ie contents.
  * \param header Decoded facility header before ROSE.
  * \param reject Decoded ROSE reject message contents.
  *
  * \return Nothing
  */
-void rose_handle_reject(struct pri *ctrl, q931_call *call, q931_ie *ie,
+void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie,
 	const struct fac_extension_header *header, const struct rose_msg_reject *reject)
 {
 	pri_error(ctrl, "ROSE REJECT:\n");
@@ -2451,13 +3054,14 @@
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param call Call leg from which the message came.
+ * \param msgtype Q.931 message type ie is in.
  * \param ie Raw ie contents.
  * \param header Decoded facility header before ROSE.
  * \param error Decoded ROSE error message contents.
  *
  * \return Nothing
  */
-void rose_handle_error(struct pri *ctrl, q931_call *call, q931_ie *ie,
+void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie,
 	const struct fac_extension_header *header, const struct rose_msg_error *error)
 {
 	const char *dms100_operation;
@@ -2493,13 +3097,14 @@
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param call Call leg from which the message came.
+ * \param msgtype Q.931 message type ie is in.
  * \param ie Raw ie contents.
  * \param header Decoded facility header before ROSE.
  * \param result Decoded ROSE result message contents.
  *
  * \return Nothing
  */
-void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie,
+void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie,
 	const struct fac_extension_header *header, const struct rose_msg_result *result)
 {
 	switch (ctrl->switchtype) {
@@ -2531,7 +3136,6 @@
 	}
 

[... 823 lines stripped ...]



More information about the libpri-commits mailing list