[svn-commits] rmudgett: branch rmudgett/mcid r256600 - in /team/rmudgett/mcid: ./ apps/ cha...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Apr 9 12:59:11 CDT 2010


Author: rmudgett
Date: Fri Apr  9 12:59:06 2010
New Revision: 256600

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=256600
Log:
Merged revisions 256528-256530,256569 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

........
  r256528 | mmichelson | 2010-04-09 10:31:32 -0500 (Fri, 09 Apr 2010) | 54 lines
  
  Merge Call completion support into trunk.
  
  From Reviewboard:
  CCSS stands for Call Completion Supplementary Services. An admittedly out-of-date
  overview of the architecture can be found in the file doc/CCSS_architecture.pdf
  in the CCSS branch. Off the top of my head, the big differences between what is
  implemented and what is in the document are as follows:
  
  1. We did not end up modifying the Hangup application at all.
  2. The document states that a single call completion monitor may be used across
     multiple calls to the same device. This proved to not be such a good idea
     when implementing protocol-specific monitors, and so we ended up using one
     monitor per-device per-call.
  3. There are some configuration options which were conceived after the document
     was written. These are documented in the ccss.conf.sample that is on this
     review request.
  		      
  For some basic understanding of terminology used throughout this code, see the
  ccss.tex document that is on this review.
  
  This implements CCBS and CCNR in several flavors.
  
  First up is a "generic" implementation, which can work over any channel technology
  provided that the channel technology can accurately report device state. Call
  completion is requested using the dialplan application CallCompletionRequest and can
  be canceled using CallCompletionCancel. Device state subscriptions are used in order
  to monitor the state of called parties.
  
  Next, there is a SIP-specific implementation of call completion. This method uses the
  methods outlined in draft-ietf-bliss-call-completion-06 to implement call completion
  using SIP signaling. There are a few things to note here:
  
  * The agent/monitor terminology used throughout Asterisk sometimes is the reverse of
    what is defined in the referenced draft.
  
  * Implementation of the draft required support for SIP PUBLISH. I attempted to write
    this in a generic-enough fashion such that if someone were to want to write PUBLISH
    support for other event packages, such as dialog-state or presence, most of the effort
    would be in writing callbacks specific to the event package.
  
  * A subportion of supporting PUBLISH reception was that we had to implement a PIDF
    parser. The PIDF support added is a bit minimal. I first wrote a validation
    routine to ensure that the PIDF document is formatted properly. The rest of the
    PIDF reading is done in-line in the call-completion-specific PUBLISH-handling
    code. In other words, while there is PIDF support here, it is not in any state
    where it could easily be applied to other event packages as is.
  
  Finally, there are a variety of ISDN-related call completion protocols supported. These
  were written by Richard Mudgett, and as such I can't really say much about their
  implementation. There are notes in the CHANGES file that indicate the ISDN protocols
  over which call completion is supported.
  
  Review: https://reviewboard.asterisk.org/r/523
........
  r256529 | mmichelson | 2010-04-09 10:56:55 -0500 (Fri, 09 Apr 2010) | 3 lines
  
  Fix some compiler errors that popped up after the CCSS merge.
........
  r256530 | mmichelson | 2010-04-09 11:04:16 -0500 (Fri, 09 Apr 2010) | 21 lines
  
  Add routines for parsing SIP URIs consistently.
  
  From the original issue report opened by Nick Lewis:
  Many sip headers in many sip methods contain the ABNF structure
   name-andor-addr = name-addr / addr-spec
   Examples include the to-header, from-header, contact-header, replyto-header
  
   At the moment chan_sip.c makes various different attempts to parse this name-andor-addr structure for each header type and for each sip method with sometimes limited degrees of success.
  
   I recommend that this name-andor-addr structure be parsed by a dedicated function and that it be used irrespective of the specific method or header that contains the name-andor-addr structure
  
  Nick has also included unit tests for verifying these routines as well, so...heck yeah.
  
  (closes issue #16708)
  Reported by: Nick_Lewis
  Patches:
        reqresp_parser-nameandoraddr2.patch uploaded by Nick Lewis (license 657
  
  Review: https://reviewboard.asterisk.org/r/549
........
  r256569 | rmudgett | 2010-04-09 11:43:30 -0500 (Fri, 09 Apr 2010) | 1 line
  
  Remove PRI CCSS BUGBUG message and update configure script.
........

Added:
    team/rmudgett/mcid/configs/ccss.conf.sample
      - copied unchanged from r256569, trunk/configs/ccss.conf.sample
    team/rmudgett/mcid/doc/tex/ccss.tex
      - copied unchanged from r256569, trunk/doc/tex/ccss.tex
    team/rmudgett/mcid/funcs/func_callcompletion.c
      - copied unchanged from r256569, trunk/funcs/func_callcompletion.c
    team/rmudgett/mcid/include/asterisk/ccss.h
      - copied unchanged from r256569, trunk/include/asterisk/ccss.h
    team/rmudgett/mcid/include/asterisk/channelstate.h
      - copied unchanged from r256569, trunk/include/asterisk/channelstate.h
    team/rmudgett/mcid/main/ccss.c
      - copied unchanged from r256569, trunk/main/ccss.c
Modified:
    team/rmudgett/mcid/   (props changed)
    team/rmudgett/mcid/CHANGES
    team/rmudgett/mcid/apps/app_dial.c
    team/rmudgett/mcid/channels/chan_dahdi.c
    team/rmudgett/mcid/channels/chan_local.c
    team/rmudgett/mcid/channels/chan_sip.c
    team/rmudgett/mcid/channels/sig_analog.c
    team/rmudgett/mcid/channels/sig_analog.h
    team/rmudgett/mcid/channels/sig_pri.c
    team/rmudgett/mcid/channels/sig_pri.h
    team/rmudgett/mcid/channels/sip/include/reqresp_parser.h
    team/rmudgett/mcid/channels/sip/include/sip.h
    team/rmudgett/mcid/channels/sip/reqresp_parser.c
    team/rmudgett/mcid/configs/chan_dahdi.conf.sample
    team/rmudgett/mcid/configs/manager.conf.sample
    team/rmudgett/mcid/configure
    team/rmudgett/mcid/configure.ac
    team/rmudgett/mcid/doc/tex/asterisk.tex
    team/rmudgett/mcid/funcs/func_srv.c
    team/rmudgett/mcid/include/asterisk/autoconfig.h.in
    team/rmudgett/mcid/include/asterisk/channel.h
    team/rmudgett/mcid/include/asterisk/devicestate.h
    team/rmudgett/mcid/include/asterisk/frame.h
    team/rmudgett/mcid/include/asterisk/manager.h
    team/rmudgett/mcid/include/asterisk/rtp_engine.h
    team/rmudgett/mcid/include/asterisk/xml.h
    team/rmudgett/mcid/main/asterisk.c
    team/rmudgett/mcid/main/channel.c
    team/rmudgett/mcid/main/manager.c
    team/rmudgett/mcid/main/xml.c
    team/rmudgett/mcid/tests/test_gosub.c

Propchange: team/rmudgett/mcid/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/rmudgett/mcid/
------------------------------------------------------------------------------
--- mcid-integrated (original)
+++ mcid-integrated Fri Apr  9 12:59:06 2010
@@ -1,1 +1,1 @@
-/trunk:1-256514
+/trunk:1-256599

Modified: team/rmudgett/mcid/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/mcid/CHANGES?view=diff&rev=256600&r1=256599&r2=256600
==============================================================================
--- team/rmudgett/mcid/CHANGES (original)
+++ team/rmudgett/mcid/CHANGES Fri Apr  9 12:59:06 2010
@@ -388,6 +388,13 @@
    iCalendar, CalDAV, and Exchange Server calendars are supported (Exchange support
    only tested on Exchange Server 2003 with no support for forms-based authentication).
 
+Call Completion Supplementary Services for Asterisk
+---------------------------------------------------
+ * Call completion support has been added for SIP, DAHDI/ISDN, and DAHDI/analog.
+   DAHDI/ISDN supports call completion for the following switch types:
+   EuroIsdn(ETSI) for PTP and PTMP modes, and Qsig.
+   See doc/CCSS_architecture.pdf and doc/tex/ccss.tex(asterisk.pdf) for details.
+
 Multicast RTP Support
 ---------------------
  * A new RTP engine and channel driver have been added which supports Multicast RTP.

Modified: team/rmudgett/mcid/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/mcid/apps/app_dial.c?view=diff&rev=256600&r1=256599&r2=256600
==============================================================================
--- team/rmudgett/mcid/apps/app_dial.c (original)
+++ team/rmudgett/mcid/apps/app_dial.c Fri Apr  9 12:59:06 2010
@@ -62,6 +62,7 @@
 #include "asterisk/global_datastores.h"
 #include "asterisk/dsp.h"
 #include "asterisk/cel.h"
+#include "asterisk/ccss.h"
 #include "asterisk/indications.h"
 
 /*** DOCUMENTATION
@@ -810,6 +811,12 @@
 				ast_channel_make_compatible(o->chan, in);
 			ast_channel_inherit_variables(in, o->chan);
 			ast_channel_datastore_inherit(in, o->chan);
+			/* When a call is forwarded, we don't want to track new interfaces
+			 * dialed for CC purposes. Setting the done flag will ensure that
+			 * any Dial operations that happen later won't record CC interfaces.
+			 */
+			ast_ignore_cc(o->chan);
+			ast_log(LOG_NOTICE, "Not accepting call completion offers from call-forward recipient %s\n", o->chan->name);
 		} else
 			ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
 	}
@@ -904,7 +911,8 @@
 	struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
 	char *opt_args[],
 	struct privacy_args *pa,
-	const struct cause_args *num_in, int *result, char *dtmf_progress)
+	const struct cause_args *num_in, int *result, char *dtmf_progress,
+	const int ignore_cc)
 {
 	struct cause_args num = *num_in;
 	int prestart = num.busy + num.congestion + num.nochan;
@@ -917,6 +925,10 @@
 #endif
 	struct ast_party_connected_line connected_caller;
 	struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
+	int cc_recall_core_id;
+	int is_cc_recall;
+	int cc_frame_received = 0;
+	int num_ringing = 0;
 
 	ast_party_connected_line_init(&connected_caller);
 	if (single) {
@@ -937,6 +949,8 @@
 			ast_party_connected_line_free(&connected_caller);
 		}
 	}
+
+	is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
 
 #ifdef HAVE_EPOLL
 	for (epollo = outgoing; epollo; epollo = epollo->next)
@@ -970,6 +984,9 @@
 				ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
 			}
 			*to = 0;
+			if (is_cc_recall) {
+				ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
+			}
 			return NULL;
 		}
 		winner = ast_waitfor_n(watchers, pos, to);
@@ -1014,6 +1031,15 @@
 			/* here, o->chan == c == winner */
 			if (!ast_strlen_zero(c->call_forward)) {
 				pa->sentringing = 0;
+				if (!ignore_cc && (f = ast_read(c))) {
+					if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_CC) {
+						/* This channel is forwarding the call, and is capable of CC, so
+						 * be sure to add the new device interface to the list
+						 */
+						ast_handle_cc_control_frame(in, c, f->data.ptr);
+					}
+					ast_frfree(f);
+				}
 				do_forward(o, &num, peerflags, single, to);
 				continue;
 			}
@@ -1088,13 +1114,41 @@
 					handle_cause(AST_CAUSE_CONGESTION, &num);
 					break;
 				case AST_CONTROL_RINGING:
-					ast_verb(3, "%s is ringing\n", c->name);
-					/* Setup early media if appropriate */
-					if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
-						ast_channel_early_bridge(in, c);
-					if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
-						ast_indicate(in, AST_CONTROL_RINGING);
-						pa->sentringing++;
+					/* This is a tricky area to get right when using a native
+					 * CC agent. The reason is that we do the best we can to send only a
+					 * single ringing notification to the caller.
+					 *
+					 * Call completion complicates the logic used here. CCNR is typically
+					 * offered during a ringing message. Let's say that party A calls
+					 * parties B, C, and D. B and C do not support CC requests, but D
+					 * does. If we were to receive a ringing notification from B before
+					 * the others, then we would end up sending a ringing message to
+					 * A with no CCNR offer present.
+					 *
+					 * The approach that we have taken is that if we receive a ringing
+					 * response from a party and no CCNR offer is present, we need to
+					 * wait. Specifically, we need to wait until either a) a called party
+					 * offers CCNR in its ringing response or b) all called parties have
+					 * responded in some way to our call and none offers CCNR.
+					 *
+					 * The drawback to this is that if one of the parties has a delayed
+					 * response or, god forbid, one just plain doesn't respond to our
+					 * outgoing call, then this will result in a significant delay between
+					 * when the caller places the call and hears ringback.
+					 *
+					 * Note also that if CC is disabled for this call, then it is perfectly
+					 * fine for ringing frames to get sent through.
+					 */
+					++num_ringing;
+					if (ignore_cc || cc_frame_received || num_ringing == numlines) {
+						ast_verb(3, "%s is ringing\n", c->name);
+						/* Setup early media if appropriate */
+						if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
+							ast_channel_early_bridge(in, c);
+						if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
+							ast_indicate(in, AST_CONTROL_RINGING);
+							pa->sentringing++;
+						}
 					}
 					break;
 				case AST_CONTROL_PROGRESS:
@@ -1163,6 +1217,12 @@
 				case AST_CONTROL_FLASH:
 					/* Ignore going off hook and flash */
 					break;
+				case AST_CONTROL_CC:
+					if (!ignore_cc) {
+						ast_handle_cc_control_frame(in, c, f->data.ptr);
+						cc_frame_received = 1;
+					}
+					break;
 				case -1:
 					if (!ast_test_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
 						ast_verb(3, "%s stopped sounds\n", c->name);
@@ -1212,6 +1272,9 @@
 					}
 					ast_frfree(f);
 				}
+				if (is_cc_recall) {
+					ast_cc_completed(in, "CC completed, although the caller hung up (cancelled)");
+				}
 				return NULL;
 			}
 
@@ -1229,6 +1292,9 @@
 						strcpy(pa->status, "CANCEL");
 						ast_frfree(f);
 						ast_channel_unlock(in);
+						if (is_cc_recall) {
+							ast_cc_completed(in, "CC completed, but the caller used DTMF to exit");
+						}
 						return NULL;
 					}
 					ast_channel_unlock(in);
@@ -1241,6 +1307,9 @@
 					strcpy(pa->status, "CANCEL");
 					ast_cdr_noanswer(in->cdr);
 					ast_frfree(f);
+					if (is_cc_recall) {
+						ast_cc_completed(in, "CC completed, but the caller hung up with DTMF");
+					}
 					return NULL;
 				}
 			}
@@ -1283,6 +1352,9 @@
 	}
 #endif
 
+	if (is_cc_recall) {
+		ast_cc_completed(in, "Recall completed!");
+	}
 	return peer;
 }
 
@@ -1656,6 +1728,8 @@
 	char *opt_args[OPT_ARG_ARRAY_SIZE];
 	struct ast_datastore *datastore = NULL;
 	int fulldial = 0, num_dialed = 0;
+	int ignore_cc = 0;
+	char device_name[AST_CHANNEL_NAME];
 
 	/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
 	pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
@@ -1683,6 +1757,10 @@
 	if (ast_strlen_zero(args.peers)) {
 		ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
 		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
+		goto done;
+	}
+
+	if (ast_cc_call_init(chan, &ignore_cc)) {
 		goto done;
 	}
 
@@ -1871,7 +1949,16 @@
 			if (!rest) /* we are on the last destination */
 				chan->hangupcause = cause;
 			chanlist_free(tmp);
+			if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) {
+				if (!ast_cc_callback(chan, tech, numsubst, ast_cc_busy_interface)) {
+					ast_cc_extension_monitor_add_dialstring(chan, interface, "");
+				}
+			}
 			continue;
+		}
+		ast_channel_get_device_name(tc, device_name, sizeof(device_name));
+		if (!ignore_cc) {
+			ast_cc_extension_monitor_add_dialstring(chan, interface, device_name);
 		}
 		pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
 
@@ -1965,6 +2052,7 @@
 				chan->hangupcause = tc->hangupcause;
 			}
 			ast_channel_unlock(chan);
+			ast_cc_call_failed(chan, tc, interface);
 			ast_hangup(tc);
 			tc = NULL;
 			chanlist_free(tmp);
@@ -2038,7 +2126,7 @@
 		}
 	}
 
-	peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress);
+	peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress, ignore_cc);
 
 	/* The ast_channel_datastore_remove() function could fail here if the
 	 * datastore was moved to another channel during a masquerade. If this is
@@ -2513,6 +2601,7 @@
 	if (config.start_sound) {
 		ast_free((char *)config.start_sound);
 	}
+	ast_ignore_cc(chan);
 	return res;
 }
 

Modified: team/rmudgett/mcid/channels/chan_dahdi.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/mcid/channels/chan_dahdi.c?view=diff&rev=256600&r1=256599&r2=256600
==============================================================================
--- team/rmudgett/mcid/channels/chan_dahdi.c (original)
+++ team/rmudgett/mcid/channels/chan_dahdi.c Fri Apr  9 12:59:06 2010
@@ -116,6 +116,7 @@
 #include "asterisk/event.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/paths.h"
+#include "asterisk/ccss.h"
 
 /*** DOCUMENTATION
 	<application name="DAHDISendKeypadFacility" language="en_US">
@@ -607,6 +608,11 @@
 };
 
 static struct dahdi_pri pris[NUM_SPANS];
+
+#if defined(HAVE_PRI_CCSS)
+/*! DAHDI PRI CCSS agent and monitor type name. */
+static const char dahdi_pri_cc_type[] = "DAHDI/PRI";
+#endif	/* defined(HAVE_PRI_CCSS) */
 
 #else
 /*! Shut up the compiler */
@@ -1252,6 +1258,14 @@
 	/*! \brief TRUE if confrence is muted. */
 	int muting;
 	void *sig_pvt;
+	struct ast_cc_config_params *cc_params;
+	/* DAHDI channel names may differ greatly from the
+	 * string that was provided to an app such as Dial. We
+	 * need to save the original string passed to dahdi_request
+	 * for call completion purposes. This way, we can replicate
+	 * the original dialed string later.
+	 */
+	char dialstring[AST_CHANNEL_NAME];
 };
 
 static struct dahdi_pvt *iflist = NULL;	/*!< Main interface list start */
@@ -1314,6 +1328,12 @@
 			.localdialplan = PRI_NATIONAL_ISDN + 1,
 			.nodetype = PRI_CPE,
 			.qsigchannelmapping = DAHDI_CHAN_MAPPING_PHYSICAL,
+
+#if defined(HAVE_PRI_CCSS)
+			.cc_ptmp_recall_mode = 1,/* specificRecall */
+			.cc_qsig_signaling_link_req = 1,/* retain */
+			.cc_qsig_signaling_link_rsp = 1,/* retain */
+#endif	/* defined(HAVE_PRI_CCSS) */
 
 			.minunused = 2,
 			.idleext = "",
@@ -1398,6 +1418,7 @@
 			.buf_policy = DAHDI_POLICY_IMMEDIATE,
 			.buf_no = numbufs,
 			.usefaxbuffers = 0,
+			.cc_params = ast_cc_config_params_init(),
 		},
 		.timing = {
 			.prewinktime = -1,
@@ -1433,6 +1454,8 @@
 static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
 static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
 static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value);
+static int dahdi_devicestate(void *data);
+static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback);
 
 static const struct ast_channel_tech dahdi_tech = {
 	.type = "DAHDI",
@@ -1455,6 +1478,8 @@
 	.queryoption = dahdi_queryoption,
 	.func_channel_read = dahdi_func_read,
 	.func_channel_write = dahdi_func_write,
+	.devicestate = dahdi_devicestate,
+	.cc_callback = dahdi_cc_callback,
 };
 
 #define GET_CHANNEL(p) ((p)->channel)
@@ -2150,6 +2175,13 @@
 {
 	struct dahdi_pvt *p = pvt;
 	p->pulsedial = flag;
+}
+
+static const char *my_get_orig_dialstring(void *pvt)
+{
+	struct dahdi_pvt *p = pvt;
+
+	return p->dialstring;
 }
 
 static void my_increase_ss_count(void)
@@ -2785,6 +2817,160 @@
 	ast_copy_string(p->rdnis, rdnis, sizeof(p->rdnis));
 }
 
+/*!
+ * \internal
+ * \brief Make a dialstring for native ISDN CC to recall properly.
+ * \since 1.8
+ *
+ * \param priv Channel private control structure.
+ * \param buf Where to put the modified dialstring.
+ * \param buf_size Size of modified dialstring buffer.
+ *
+ * \details
+ * original dialstring:
+ * DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]]
+ * DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
+ *
+ * The modified dialstring will have prefixed the channel-group section
+ * with the ISDN channel restriction.
+ *
+ * buf:
+ * DAHDI/i<span>-<channel#>[c|r<cadance#>|d][/extension[/options]]
+ * DAHDI/i<span>-(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
+ *
+ * The routine will check to see if the ISDN channel restriction is already
+ * in the original dialstring.
+ *
+ * \return Nothing
+ */
+static void my_pri_make_cc_dialstring(void *priv, char *buf, size_t buf_size)
+{
+	char *dial;
+	struct dahdi_pvt *pvt;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(tech);	/* channel technology token */
+		AST_APP_ARG(group);	/* channel/group token */
+		//AST_APP_ARG(ext);	/* extension token */
+		//AST_APP_ARG(opts);	/* options token */
+		//AST_APP_ARG(other);	/* Any remining unused arguments */
+	);
+
+	pvt = priv;
+	dial = ast_strdupa(pvt->dialstring);
+	AST_NONSTANDARD_APP_ARGS(args, dial, '/');
+	if (!args.tech) {
+		ast_copy_string(buf, pvt->dialstring, buf_size);
+		return;
+	}
+	if (!args.group) {
+		/* Append the ISDN span channel restriction to the dialstring. */
+		snprintf(buf, buf_size, "%s/i%d-", args.tech, pvt->pri->span);
+		return;
+	}
+	if (args.group[0] == 'i') {
+		/* The ISDN span channel restriction is already in the dialstring. */
+		ast_copy_string(buf, pvt->dialstring, buf_size);
+		return;
+	}
+	/* Insert the ISDN span channel restriction into the dialstring. */
+	snprintf(buf, buf_size, "%s/i%d-%s", args.tech, pvt->pri->span, args.group);
+}
+
+/*!
+ * \internal
+ * \brief Reevaluate the PRI span device state.
+ * \since 1.8
+ *
+ * \param pri Asterisk D channel control structure.
+ *
+ * \return Nothing
+ *
+ * \note Assumes the pri->lock is already obtained.
+ */
+static void dahdi_pri_update_span_devstate(struct sig_pri_pri *pri)
+{
+	unsigned idx;
+	unsigned num_b_chans;	/* Number of B channels provisioned on the span. */
+	unsigned in_use;		/* Number of B channels in use on the span. */
+	unsigned in_alarm;		/* TRUE if the span is in alarm condition. */
+	enum ast_device_state new_state;
+
+	/* Count the number of B channels and the number of B channels in use. */
+	num_b_chans = 0;
+	in_use = 0;
+	in_alarm = 1;
+	for (idx = pri->numchans; idx--;) {
+		if (pri->pvts[idx] && !pri->pvts[idx]->no_b_channel) {
+			/* This is a B channel interface. */
+			++num_b_chans;
+			if (pri->pvts[idx]->owner
+#if defined(HAVE_PRI_SERVICE_MESSAGES)
+				/* Out-of-service B channels are "in-use". */
+				&& pri->pvts[idx]->service_status
+#endif	/* defined(HAVE_PRI_SERVICE_MESSAGES) */
+				) {
+				++in_use;
+			}
+			if (!pri->pvts[idx]->inalarm) {
+				/* There is a channel that is not in alarm. */
+				in_alarm = 0;
+			}
+		}
+	}
+
+	/* Update the span congestion device state and report any change. */
+	if (in_alarm) {
+		new_state = AST_DEVICE_UNAVAILABLE;
+	} else {
+		new_state = num_b_chans == in_use ? AST_DEVICE_BUSY : AST_DEVICE_NOT_INUSE;
+	}
+	if (pri->congestion_devstate != new_state) {
+		pri->congestion_devstate = new_state;
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span);
+	}
+#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
+	/* Update the span threshold device state and report any change. */
+	if (in_alarm) {
+		new_state = AST_DEVICE_UNAVAILABLE;
+	} else if (!in_use) {
+		new_state = AST_DEVICE_NOT_INUSE;
+	} else if (!pri->user_busy_threshold) {
+		new_state = in_use < num_b_chans ? AST_DEVICE_INUSE : AST_DEVICE_BUSY;
+	} else {
+		new_state = in_use < pri->user_busy_threshold ? AST_DEVICE_INUSE
+			: AST_DEVICE_BUSY;
+	}
+	if (pri->threshold_devstate != new_state) {
+		pri->threshold_devstate = new_state;
+		ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span);
+	}
+#endif	/* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
+}
+
+/*!
+ * \internal
+ * \brief Reference this module.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+static void my_module_ref(void)
+{
+	ast_module_ref(ast_module_info->self);
+}
+
+/*!
+ * \internal
+ * \brief Unreference this module.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+static void my_module_unref(void)
+{
+	ast_module_unref(ast_module_info->self);
+}
+
 static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri);
 
 static struct sig_pri_callback dahdi_pri_callbacks =
@@ -2803,6 +2989,11 @@
 	.set_dnid = my_pri_set_dnid,
 	.set_rdnis = my_pri_set_rdnis,
 	.new_nobch_intf = dahdi_new_pri_nobch_channel,
+	.get_orig_dialstring = my_get_orig_dialstring,
+	.make_cc_dialstring = my_pri_make_cc_dialstring,
+	.update_span_devstate = dahdi_pri_update_span_devstate,
+	.module_ref = my_module_ref,
+	.module_unref = my_module_unref,
 };
 #endif	/* defined(HAVE_PRI) */
 
@@ -2932,6 +3123,7 @@
 	.cancel_cidspill = my_cancel_cidspill,
 	.confmute = my_confmute,
 	.set_pulsedial = my_set_pulsedial,
+	.get_orig_dialstring = my_get_orig_dialstring,
 };
 
 static struct dahdi_pvt *round_robin[32];
@@ -5121,6 +5313,9 @@
 		ast_event_unsubscribe(p->mwi_event_sub);
 	if (p->vars) {
 		ast_variables_destroy(p->vars);
+	}
+	if (p->cc_params) {
+		ast_cc_config_params_destroy(p->cc_params);
 	}
 	ast_mutex_destroy(&p->lock);
 	dahdi_close_sub(p, SUB_REAL);
@@ -5957,6 +6152,18 @@
 		*cp = (p->callprogress & CALLPROGRESS_FAX) ? 0 : 1;
 		ast_debug(1, "Reporting fax tone detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
 		break;
+	case AST_OPTION_CC_AGENT_TYPE:
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CCSS)
+		if (dahdi_sig_pri_lib_handles(p->sig)) {
+			ast_copy_string((char *) data, dahdi_pri_cc_type, *datalen);
+			break;
+		}
+#endif	/* defined(HAVE_PRI_CCSS) */
+#endif	/* defined(HAVE_PRI) */
+		return -1;
+	default:
+		return -1;
 	}
 
 	errno = 0;
@@ -8582,37 +8789,28 @@
 	return res;
 }
 
-static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid)
-{
-	struct ast_channel *tmp;
-	format_t deflaw;
-	int res;
-	int x,y;
-	int features;
+#if defined(HAVE_PRI)
+static struct ast_str *create_channel_name(struct dahdi_pvt *i, int is_outgoing, char *address)
+#else
+static struct ast_str *create_channel_name(struct dahdi_pvt *i)
+#endif	/* defined(HAVE_PRI) */
+{
 	struct ast_str *chan_name;
-	struct ast_variable *v;
-	struct dahdi_params ps;
-
-	if (i->subs[idx].owner) {
-		ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
+	int x, y;
+
+	/* Create the new channel name tail. */
+	if (!(chan_name = ast_str_create(32))) {
 		return NULL;
 	}
-
-	/* Create the new channel name tail. */
-	chan_name = ast_str_alloca(32);
 	if (i->channel == CHAN_PSEUDO) {
 		ast_str_set(&chan_name, 0, "pseudo-%ld", ast_random());
 #if defined(HAVE_PRI)
 	} else if (i->pri) {
 		ast_mutex_lock(&i->pri->lock);
 		y = ++i->pri->new_chan_seq;
-		if (i->outgoing) {
-			/*
-			 * The dnid has been stuffed with the called-number[:subaddress]
-			 * by dahdi_request().
-			 */
-			ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->dnid, y);
-			i->dnid[0] = '\0';
+		if (is_outgoing) {
+			ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, address, y);
+			address[0] = '\0';
 		} else if (ast_strlen_zero(i->cid_subaddr)) {
 			/* Put in caller-id number only since there is no subaddress. */
 			ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->cid_num, y);
@@ -8636,11 +8834,49 @@
 			++y;
 		} while (x < 3);
 	}
+	return chan_name;
+}
+
+static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid)
+{
+	struct ast_channel *tmp;
+	format_t deflaw;
+	int res;
+	int x;
+	int features;
+	struct ast_str *chan_name;
+	struct ast_variable *v;
+	struct dahdi_params ps;
+
+	if (i->subs[idx].owner) {
+		ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
+		return NULL;
+	}
+
+#if defined(HAVE_PRI)
+	/*
+	 * The dnid has been stuffed with the called-number[:subaddress]
+	 * by dahdi_request() for outgoing calls.
+	 */
+	chan_name = create_channel_name(i, i->outgoing, i->dnid);
+#else
+	chan_name = create_channel_name(i);
+#endif	/* defined(HAVE_PRI) */
+	if (!chan_name) {
+		return NULL;
+	}
 
 	tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name));
+	ast_free(chan_name);
 	if (!tmp)
 		return NULL;
 	tmp->tech = &dahdi_tech;
+#if defined(HAVE_PRI)
+	if (i->pri) {
+		ast_cc_copy_config_params(i->cc_params, i->pri->cc_params);
+	}
+#endif	/* defined(HAVE_PRI) */
+	ast_channel_cc_params_init(tmp, i->cc_params);
 	memset(&ps, 0, sizeof(ps));
 	res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps);
 	if (res) {
@@ -11169,6 +11405,11 @@
 		if (!tmp) {
 			return NULL;
 		}
+		tmp->cc_params = ast_cc_config_params_init();
+		if (!tmp->cc_params) {
+			ast_free(tmp);
+			return NULL;
+		}
 		ast_mutex_init(&tmp->lock);
 		ifcount++;
 		for (x = 0; x < 3; x++)
@@ -11412,6 +11653,16 @@
 						tmp->sig_pvt = pchan;
 						tmp->pri = &pris[span].pri;
 
+						if (!tmp->pri->cc_params) {
+							tmp->pri->cc_params = ast_cc_config_params_init();
+							if (!tmp->pri->cc_params) {
+								destroy_dahdi_pvt(tmp);
+								return NULL;
+							}
+						}
+						ast_cc_copy_config_params(tmp->pri->cc_params,
+							conf->chan.cc_params);
+
 						pris[span].pri.sig = chan_sig;
 						pris[span].pri.nodetype = conf->pri.pri.nodetype;
 						pris[span].pri.switchtype = myswitchtype;
@@ -11434,6 +11685,14 @@
 						pris[span].pri.hold_disconnect_transfer =
 							conf->pri.pri.hold_disconnect_transfer;
 #endif	/* defined(HAVE_PRI_CALL_HOLD) */
+#if defined(HAVE_PRI_CCSS)
+						pris[span].pri.cc_ptmp_recall_mode =
+							conf->pri.pri.cc_ptmp_recall_mode;
+						pris[span].pri.cc_qsig_signaling_link_req =
+							conf->pri.pri.cc_qsig_signaling_link_req;
+						pris[span].pri.cc_qsig_signaling_link_rsp =
+							conf->pri.pri.cc_qsig_signaling_link_rsp;
+#endif	/* defined(HAVE_PRI_CCSS) */
 						pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
 						ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list));
 						ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial));
@@ -11742,6 +12001,7 @@
 		tmp->answeronpolarityswitch = conf->chan.answeronpolarityswitch;
 		tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
 		tmp->sendcalleridafter = conf->chan.sendcalleridafter;
+		ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params);
 
 		if (!here) {
 			tmp->locallyblocked = tmp->remotelyblocked = 0;
@@ -11881,21 +12141,36 @@
 	return tmp;
 }
 
-static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *channelmatched, int *groupmatched)
-{
-	/* First, check group matching */
+static int is_group_or_channel_match(struct dahdi_pvt *p, int span, ast_group_t groupmatch, int *groupmatched, int channelmatch, int *channelmatched)
+{
+#if defined(HAVE_PRI)
+	if (0 < span) {
+		/* The channel must be on the specified PRI span. */
+		if (!p->pri || p->pri->span != span) {
+			return 0;
+		}
+	}
+#endif	/* defined(HAVE_PRI) */
+	/* check group matching */
 	if (groupmatch) {
 		if ((p->group & groupmatch) != groupmatch)
+			/* Doesn't match the specified group, try the next one */
 			return 0;
 		*groupmatched = 1;
 	}
 	/* Check to see if we have a channel match */
 	if (channelmatch != -1) {
 		if (p->channel != channelmatch)
+			/* Doesn't match the specified channel, try the next one */
 			return 0;
 		*channelmatched = 1;
 	}
 
+	return 1;
+}
+
+static int available(struct dahdi_pvt *p)
+{
 	if (p->inalarm)
 		return 0;
 
@@ -11988,6 +12263,11 @@
 	if (!pvt) {
 		return -1;
 	}
+	pvt->cc_params = ast_cc_config_params_init();
+	if (!pvt->cc_params) {
+		ast_free(pvt);
+		return -1;
+	}
 	ast_mutex_init(&pvt->lock);
 	for (idx = 0; idx < ARRAY_LEN(pvt->subs); ++idx) {
 		pvt->subs[idx].dfd = -1;
@@ -12089,24 +12369,31 @@
 	return p;
 }
 
-static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
-{
-	ast_group_t groupmatch = 0;
-	int channelmatch = -1;
-	int roundrobin = 0;
-	int callwait = 0;
+struct dahdi_starting_point {
+	/*! Group matching mask.  Zero if not specified. */
+	ast_group_t groupmatch;
+	/*! DAHDI channel to match with.  -1 if not specified. */
+	int channelmatch;
+	/*! Round robin saved search location index. (Valid if roundrobin TRUE) */
+	int rr_starting_point;
+	/*! ISDN span where channels can be picked (Zero if not specified) */
+	int span;
+	/*! Analog channel distinctive ring cadance index. */
+	int cadance;
+	/*! Dialing option. c/r/d if present and valid. */
+	char opt;
+	/*! TRUE if to search the channel list backwards. */
+	char backwards;
+	/*! TRUE if search is done with round robin sequence. */
+	char roundrobin;
+};
+static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi_starting_point *param)
+{
+	char *dest;
+	char *s;
+	int x;
+	int res = 0;
 	struct dahdi_pvt *p;
-	struct ast_channel *tmp = NULL;
-	char *dest;
-	int x;
-	char *s;
-	char opt=0;
-	int res=0, y=0;
-	int backwards = 0;
-	struct dahdi_pvt *exitpvt;
-	int channelmatched = 0;
-	int groupmatched = 0;
-	int transcapdigital = 0;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(group);	/* channel/group token */
 		//AST_APP_ARG(ext);	/* extension token */
@@ -12117,8 +12404,11 @@
 	/*
 	 * data is ---v
 	 * Dial(DAHDI/pseudo[/extension[/options]])
-	 * Dial(DAHDI/<channel#>[c|r<cadance#>|d][/extension[/options]])
-	 * Dial(DAHDI/(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]])
+	 * Dial(DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]])
+	 * Dial(DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]])
+	 *
+	 * i - ISDN span channel restriction.
+	 *     Used by CC to ensure that the CC recall goes out the same span.
 	 *
 	 * g - channel group allocation search forward
 	 * G - channel group allocation search backward
@@ -12131,7 +12421,7 @@
 	 */
 
 	if (data) {
-		dest = ast_strdupa((char *)data);
+		dest = ast_strdupa(data);
 	} else {
 		ast_log(LOG_WARNING, "Channel requested with no data\n");
 		return NULL;
@@ -12142,27 +12432,47 @@
 		return NULL;
 	}
 
+	/* Initialize the output parameters */
+	memset(param, 0, sizeof(*param));
+	param->channelmatch = -1;
+
+	if (args.group[0] == 'i') {
+		/* Extract the ISDN span channel restriction specifier. */
+		res = sscanf(args.group + 1, "%30d", &x);
+		if (res < 1) {
+			ast_log(LOG_WARNING, "Unable to determine ISDN span for data %s\n", data);
+			return NULL;
+		}
+		param->span = x;
+
+		/* Remove the ISDN span channel restriction specifier. */
+		s = strchr(args.group, '-');
+		if (!s) {
+			ast_log(LOG_WARNING, "Bad ISDN span format for data %s\n", data);
+			return NULL;
+		}
+		args.group = s + 1;
+		res = 0;
+	}
 	if (toupper(args.group[0]) == 'G' || toupper(args.group[0])=='R') {
 		/* Retrieve the group number */
 		s = args.group + 1;
-		if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) {
-			ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data);
+		res = sscanf(s, "%30d%1c%30d", &x, &param->opt, &param->cadance);
+		if (res < 1) {
+			ast_log(LOG_WARNING, "Unable to determine group for data %s\n", data);
 			return NULL;
 		}
-		groupmatch = ((ast_group_t) 1 << x);
-
-		/* Lock the interface list */
-		ast_mutex_lock(&iflock);
+		param->groupmatch = ((ast_group_t) 1 << x);
 
 		if (toupper(args.group[0]) == 'G') {
 			if (args.group[0] == 'G') {
-				backwards = 1;
+				param->backwards = 1;
 				p = ifend;
 			} else
 				p = iflist;
 		} else {
 			if (args.group[0] == 'R') {
-				backwards = 1;
+				param->backwards = 1;
 				p = round_robin[x]?round_robin[x]->prev:ifend;
 				if (!p)
 					p = ifend;
@@ -12171,36 +12481,62 @@
 				if (!p)
 					p = iflist;
 			}
-			roundrobin = 1;
+			param->roundrobin = 1;
+			param->rr_starting_point = x;
 		}
 	} else {
 		s = args.group;
 		if (!strcasecmp(s, "pseudo")) {
 			/* Special case for pseudo */
 			x = CHAN_PSEUDO;
-			channelmatch = x;
-		} else if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) {
-			ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data);
-			return NULL;
+			param->channelmatch = x;
 		} else {
-			channelmatch = x;
-		}
-
-		/* Lock the interface list */
-		ast_mutex_lock(&iflock);
+			res = sscanf(s, "%30d%1c%30d", &x, &param->opt, &param->cadance);
+			if (res < 1) {
+				ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", data);
+				return NULL;
+			} else {
+				param->channelmatch = x;
+			}
+		}
 
 		p = iflist;
 	}
+
+	if (param->opt == 'r' && res < 3) {
+		ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", data);
+		param->opt = '\0';
+	}
+
+	return p;
+}
+
+static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
+{
+	int callwait = 0;
+	struct dahdi_pvt *p;
+	struct ast_channel *tmp = NULL;
+	struct dahdi_pvt *exitpvt;
+	int channelmatched = 0;
+	int groupmatched = 0;
+	int transcapdigital = 0;
+	struct dahdi_starting_point start;
+
+	p = determine_starting_point(data, &start);
+	if (!p) {
+		/* We couldn't determine a starting point, which likely means badly-formatted channel name. Abort! */
+		return NULL;
+	}
+
 	/* Search for an unowned channel */
 	exitpvt = p;
+	ast_mutex_lock(&iflock);
 	while (p && !tmp) {
-		if (roundrobin)
-			round_robin[x] = p;
-#if 0
-		ast_verbose("name = %s, %d, %d, %llu\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch);
-#endif
-
-		if (p && available(p, channelmatch, groupmatch, &channelmatched, &groupmatched)) {
+		if (start.roundrobin)
+			round_robin[start.rr_starting_point] = p;
+
+		if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)
+			&& available(p)) {
 			ast_debug(1, "Using channel %d\n", p->channel);
 
 			callwait = (p->owner != NULL);
@@ -12224,22 +12560,25 @@
 			}
 
 			/* Make special notes */
-			if (res > 1) {
-				if (opt == 'c') {
-					/* Confirm answer */
-					p->confirmanswer = 1;
-				} else if (opt == 'r') {
-					/* Distinctive ring */
-					if (res < 3)
-						ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", (char *)data);
-					else
-						p->distinctivering = y;
-				} else if (opt == 'd') {
-					/* If this is an ISDN call, make it digital */
-					transcapdigital = AST_TRANS_CAP_DIGITAL;
-				} else {
-					ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data);
-				}
+			switch (start.opt) {
+			case '\0':
+				/* No option present. */
+				break;
+			case 'c':
+				/* Confirm answer */
+				p->confirmanswer = 1;
+				break;
+			case 'r':
+				/* Distinctive ring */
+				p->distinctivering = start.cadance;
+				break;
+			case 'd':
+				/* If this is an ISDN call, make it digital */
+				transcapdigital = AST_TRANS_CAP_DIGITAL;
+				break;
+			default:
+				ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", start.opt, (char *)data);
+				break;
 			}
 
 			p->outgoing = 1;

[... 7526 lines stripped ...]



More information about the svn-commits mailing list