[asterisk-commits] seanbright: branch seanbright/issue13827-1.4 r168534 - in /team/seanbright/is...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jan 12 18:52:37 CST 2009


Author: seanbright
Date: Mon Jan 12 18:52:37 2009
New Revision: 168534

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=168534
Log:
OK.  I think this is where we want to be.  Now to test.

Modified:
    team/seanbright/issue13827-1.4/channels/chan_sip.c
    team/seanbright/issue13827-1.4/configs/sip.conf.sample
    team/seanbright/issue13827-1.4/include/asterisk/channel.h
    team/seanbright/issue13827-1.4/main/channel.c

Modified: team/seanbright/issue13827-1.4/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/team/seanbright/issue13827-1.4/channels/chan_sip.c?view=diff&rev=168534&r1=168533&r2=168534
==============================================================================
--- team/seanbright/issue13827-1.4/channels/chan_sip.c (original)
+++ team/seanbright/issue13827-1.4/channels/chan_sip.c Mon Jan 12 18:52:37 2009
@@ -330,6 +330,13 @@
 	SIP_PING,		/* Not supported at all, no standard but still implemented out there */
 };
 
+/*! \brief Settings for the 'notifycid' option, see sip.conf.sample for details. */
+enum notifycid_setting {
+	DISABLED       = 0,
+	ENABLED        = 1,
+	IGNORE_CONTEXT = 2,
+};
+
 /*! \brief Authentication types - proxy or www authentication 
 	\note Endpoints, like Asterisk, should always use WWW authentication to
 	allow multiple authentications in the same call - to the proxy and
@@ -508,6 +515,7 @@
 #define DEFAULT_ALLOW_EXT_DOM	TRUE
 #define DEFAULT_REALM		"asterisk"
 #define DEFAULT_NOTIFYRINGING	TRUE
+#define DEFAULT_NOTIFYCID		DISABLED
 #define DEFAULT_PEDANTIC	FALSE
 #define DEFAULT_AUTOCREATEPEER	FALSE
 #define DEFAULT_QUALIFY		FALSE
@@ -544,6 +552,9 @@
 static int srvlookup;			/*!< SRV Lookup on or off. Default is on */
 static int pedanticsipchecking;		/*!< Extra checking ?  Default off */
 static int autocreatepeer;		/*!< Auto creation of peers at registration? Default off. */
+
+static enum notifycid_setting global_notifycid; /*!< Send CID with ringing notifications */
+
 static int global_relaxdtmf;			/*!< Relax DTMF */
 static int global_rtptimeout;		/*!< Time out call if no RTP */
 static int global_rtpholdtimeout;
@@ -1358,6 +1369,7 @@
 static int sip_refer_allocate(struct sip_pvt *p);
 static void ast_quiet_chan(struct ast_channel *chan);
 static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
+static int do_magic_pickup(struct ast_channel *channel, const char *exten, const char *context);
 
 /*--- Device monitoring and Device/extension state handling */
 static int cb_extensionstate(char *context, char* exten, int state, void *data);
@@ -7285,6 +7297,14 @@
 	return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
 }
 
+static int find_calling_channel(struct ast_channel *c, void *data) {
+	struct sip_pvt *p = data;
+
+	return (c->pbx &&
+			(!strcasecmp(c->macroexten, p->exten) || !strcasecmp(c->exten, p->exten)) &&
+			(global_notifycid == IGNORE_CONTEXT || !strcasecmp(c->context, p->context)));
+}
+
 /*! \brief Used in the SUBSCRIBE notification subsystem */
 static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout)
 {
@@ -7440,11 +7460,42 @@
 		break;
 	case DIALOG_INFO_XML: /* SNOM subscribes in this format */
 		ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
-		ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
-		if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
-			ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
-		else
+		ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full" : "partial", mto);
+		if ((state & AST_EXTENSION_RINGING) && global_notifyringing) {
+			const char *local_display = p->exten, *local_target = mto;
+
+			/* There are some limitations to how this works.  The primary one is that the
+			   callee must be dialing the same extension that is being monitored.  Simply dialing
+			   the hint'd device is not sufficient. */
+			if (global_notifycid) {
+				struct ast_channel *caller = ast_channel_search_locked(find_calling_channel, p);
+
+				if (caller) {
+					local_display = ast_strdupa(caller->cid.cid_name);
+					local_target = ast_strdupa(caller->cid.cid_num);
+					ast_channel_unlock(caller);
+					caller = NULL;
+				}
+			}
+
+			/* We create a fake call-id which the phone will send back in an INVITE
+			   Replaces header which we can grab and do some magic with. */
+			ast_build_string(&t, &maxbytes,
+							 "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n"
+							 "<remote>\n"
+							 /* See the limitations of this above.  Luckily the phone seems to still be
+								happy when these values are not correct. */
+							 "<identity display=\"%s\">%s</identity>\n"
+							 "<target uri=\"%s\"/>\n"
+							 "</remote>\n"
+							 "<local>\n"
+							 "<identity>%s</identity>\n"
+							 "<target uri=\"%s\"/>\n"
+							 "</local>\n",
+							 p->exten, p->callid, local_display, local_target, local_target, mto, mto);
+		} else {
 			ast_build_string(&t, &maxbytes, "<dialog id=\"%s\">\n", p->exten);
+		}
 		ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
 		if (state == AST_EXTENSION_ONHOLD) {
 			ast_build_string(&t, &maxbytes, "<local>\n<target uri=\"%s\">\n"
@@ -10921,6 +10972,11 @@
 	ast_cli(fd, "  Outbound reg. timeout:  %d secs\n", global_reg_timeout);
 	ast_cli(fd, "  Outbound reg. attempts: %d\n", global_regattempts_max);
 	ast_cli(fd, "  Notify ringing state:   %s\n", global_notifyringing ? "Yes" : "No");
+	if (global_notifyringing) {
+		ast_cli(fd, "    Include CID:          %s%s\n",
+				global_notifycid ? "Yes" : "No",
+				global_notifycid == IGNORE_CONTEXT ? " (Ignoring context)" : "");
+	}
 	ast_cli(fd, "  Notify hold state:      %s\n", global_notifyhold ? "Yes" : "No");
 	ast_cli(fd, "  SIP Transfer mode:      %s\n", transfermode2str(global_allowtransfer));
 	ast_cli(fd, "  Max Call Bitrate:       %d kbps\r\n", default_maxcallbitrate);
@@ -14156,6 +14212,26 @@
 	return sip_uri_params_cmp(params1, params2);
 }
 
+static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context)
+{
+	int length = AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2;
+	char *argument = alloca(length);
+	struct ast_app *pickup = pbx_findapp("Pickup");
+
+	if (!pickup) {
+		ast_log(LOG_ERROR, "Unable to perform pickup: Application 'Pickup' not loaded (app_directed_pickup.so).\n");
+		return -1;
+	}
+
+	snprintf(argument, length, "%s@%s", extension, context);
+
+	/* There is no point in capturing the return value since pickup_exec
+	   doesn't return anything meaningful unless the passed data is an empty
+	   string (which in our case it will not be) */
+	pbx_exec(channel, pickup, argument);
+
+	return 0;	
+}
 
 /*! \brief Handle incoming INVITE request
 \note 	If the INVITE has a Replaces header, it is part of an
@@ -14173,6 +14249,12 @@
 	unsigned int required_profile = 0;
 	struct ast_channel *c = NULL;		/* New channel */
 	int reinvite = 0;
+	struct {
+		char exten[AST_MAX_EXTENSION];
+		char context[AST_MAX_CONTEXT];
+	} pickup = {
+		.exten = "",
+	};
 
 	/* Find out what they support */
 	if (!p->sipoptions) {
@@ -14306,10 +14388,30 @@
 			ast_log(LOG_DEBUG,"Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n", replace_id, fromtag ? fromtag : "<no from tag>", totag ? totag : "<no to tag>");
 
 
-		/* Try to find call that we are replacing 
-			If we have a Replaces  header, we need to cancel that call if we succeed with this call 
-		*/
-		if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
+		/* Try to find the call that we are replacing.
+		   If we have a Replaces header, we need to cancel that call if we succeed with this call.
+		   First we cheat a little and look for a magic call-id from phones that support
+		   dialog-info+xml so we can do technology independent pickup... */
+		if (strncmp(replace_id, "pickup-", 7) == 0) {
+			struct sip_pvt *subscription = NULL;
+			replace_id += 7; /* Worst case we are looking at \0 */
+
+			if ((subscription = get_sip_pvt_byid_locked(replace_id, NULL, NULL)) == NULL) {
+				ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id);
+				transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
+				error = 1;
+			} else {
+				ast_log(LOG_NOTICE, "Trying to pick up %s@%s\n", subscription->exten, subscription->context);
+				ast_copy_string(pickup.exten, subscription->exten, sizeof(pickup.exten));
+				ast_copy_string(pickup.context, subscription->context, sizeof(pickup.context));
+				ast_mutex_unlock(&subscription->lock);
+				if (subscription->owner) {
+					ast_channel_unlock(subscription->owner);
+				}
+			}
+		}
+
+		if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
 			ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
 			transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
 			error = 1;
@@ -14328,7 +14430,7 @@
 			error = 1;
 		}
 
-		if (!error && !p->refer->refer_call->owner) {
+		if (!error && ast_strlen_zero(pickup.exten) && !p->refer->refer_call->owner) {
 			/* Oops, someting wrong anyway, no owner, no call */
 			ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
 			/* Check for better return code */
@@ -14336,7 +14438,7 @@
 			error = 1;
 		}
 
-		if (!error && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) {
+		if (!error && ast_strlen_zero(pickup.exten) && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) {
 			ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
 			transmit_response_reliable(p, "603 Declined (Replaces)", req);
 			error = 1;
@@ -14528,10 +14630,28 @@
 		p->lastinvite = seqno;
 
 	if (replace_id) { 	/* Attended transfer or call pickup - we're the target */
-		/* Go and take over the target call */
-		if (sipdebug && option_debug > 3)
-			ast_log(LOG_DEBUG, "Sending this call to the invite/replcaes handler %s\n", p->callid);
-		return handle_invite_replaces(p, req, debug, ast_test_flag(req, SIP_PKT_IGNORE), seqno, sin);
+		if (!ast_strlen_zero(pickup.exten)) {
+			append_history(p, "Xfer", "INVITE/Replace received");
+
+			/* Let the caller know we're giving it a shot */
+			transmit_response(p, "100 Trying", req);
+			ast_setstate(c, AST_STATE_RING);
+
+			/* Do the pickup itself */
+			ast_channel_unlock(c);
+			*nounlock = 1;
+			do_magic_pickup(c, pickup.exten, pickup.context);
+
+			/* Now we're either masqueraded or we failed to pickup, in either case we... */
+			ast_hangup(c);
+
+			return 0;
+		} else {
+			/* Go and take over the target call */
+			if (sipdebug && option_debug > 3)
+				ast_log(LOG_DEBUG, "Sending this call to the invite/replcaes handler %s\n", p->callid);
+			return handle_invite_replaces(p, req, debug, ast_test_flag(req, SIP_PKT_IGNORE), seqno, sin);
+		}
 	}
 
 
@@ -17594,6 +17714,7 @@
 	global_regcontext[0] = '\0';
 	expiry = DEFAULT_EXPIRY;
 	global_notifyringing = DEFAULT_NOTIFYRINGING;
+	global_notifycid = DEFAULT_NOTIFYCID;
 	global_limitonpeers = FALSE;
 	global_directrtpsetup = FALSE;		/* Experimental feature, disabled by default */
 	global_notifyhold = FALSE;
@@ -17736,6 +17857,12 @@
 			global_notifyringing = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "notifyhold")) {
 			global_notifyhold = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "notifycid")) {
+			if (!strcasecmp(v->value, "ignore-context")) {
+				global_notifycid = IGNORE_CONTEXT;
+			} else {
+				global_notifycid = ast_true(v->value);
+			}
 		} else if (!strcasecmp(v->name, "alwaysauthreject")) {
 			global_alwaysauthreject = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "mohinterpret") 

Modified: team/seanbright/issue13827-1.4/configs/sip.conf.sample
URL: http://svn.digium.com/svn-view/asterisk/team/seanbright/issue13827-1.4/configs/sip.conf.sample?view=diff&rev=168534&r1=168533&r2=168534
==============================================================================
--- team/seanbright/issue13827-1.4/configs/sip.conf.sample (original)
+++ team/seanbright/issue13827-1.4/configs/sip.conf.sample Mon Jan 12 18:52:37 2009
@@ -208,6 +208,18 @@
 ;notifyhold = yes               ; Notify subscriptions on HOLD state (default: no)
                                 ; Turning on notifyringing and notifyhold will add a lot
                                 ; more database transactions if you are using realtime.
+;notifycid = yes                ; Control whether caller ID information is sent along with
+                                ; dialog-info+xml notifications (supported by snom phones).
+                                ; Note that this feature will only work properly when the
+                                ; incoming call is using the same extension and context that
+                                ; is being used as the hint for the called extension.  This means
+                                ; that it won't work when using subscribecontext for your sip
+                                ; user or peer (if subscribecontext is different than context).
+                                ; This is also limited to a single caller, meaning that if an
+                                ; extension is ringing because multiple calls are incoming,
+                                ; only one will be used as the source of caller ID.  Specify
+                                ; 'ignore-context' to ignore the called context when looking
+                                ; for the caller's channel.  The default is 'no.'
 ;limitonpeers = yes             ; Apply call limits on peers only. This will improve 
                                 ; status notification when you are using type=friend
                                 ; Inbound calls, that really apply to the user part

Modified: team/seanbright/issue13827-1.4/include/asterisk/channel.h
URL: http://svn.digium.com/svn-view/asterisk/team/seanbright/issue13827-1.4/include/asterisk/channel.h?view=diff&rev=168534&r1=168533&r2=168534
==============================================================================
--- team/seanbright/issue13827-1.4/include/asterisk/channel.h (original)
+++ team/seanbright/issue13827-1.4/include/asterisk/channel.h Mon Jan 12 18:52:37 2009
@@ -985,6 +985,18 @@
 struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
 						     const char *context);
 
+/*! \brief Search for a channel based on the passed channel matching callback
+ * Search for a channel based on the specified is_match callback, and return the
+ * first channel that we match.  When returned, the channel will be locked.  Note
+ * that the is_match callback is called with the passed channel locked, and should
+ * return 0 if there is no match, and non-zero if there is.
+ * \param is_match callback executed on each channel until non-zero is returned, or we
+ *        run out of channels to search.
+ * \param data data passed to the is_match callback during each invocation.
+ * \return Returns the matched channel, or NULL if no channel was matched.
+ */
+struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data);
+
 /*! ! \brief Waits for a digit
  * \param c channel to wait for a digit on
  * \param ms how many milliseconds to wait

Modified: team/seanbright/issue13827-1.4/main/channel.c
URL: http://svn.digium.com/svn-view/asterisk/team/seanbright/issue13827-1.4/main/channel.c?view=diff&rev=168534&r1=168533&r2=168534
==============================================================================
--- team/seanbright/issue13827-1.4/main/channel.c (original)
+++ team/seanbright/issue13827-1.4/main/channel.c Mon Jan 12 18:52:37 2009
@@ -1168,6 +1168,24 @@
 						     const char *context)
 {
 	return channel_find_locked(chan, NULL, 0, context, exten);
+}
+
+/*! \brief Search for a channel based on the passed channel matching callback (first match) and return it, locked */
+struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data)
+{
+	struct ast_channel *c = NULL;
+
+	AST_LIST_LOCK(&channels);
+	AST_LIST_TRAVERSE(&channels, c, chan_list) {
+		ast_channel_lock(c);
+		if (is_match(c, data)) {
+			break;
+		}
+		ast_channel_unlock(c);
+	}
+	AST_LIST_UNLOCK(&channels);
+
+	return c;
 }
 
 /*! \brief Wait, look for hangups and condition arg */




More information about the asterisk-commits mailing list