[asterisk-commits] mmichelson: branch mmichelson/more_transfer r388648 - in /team/mmichelson/mor...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon May 13 16:45:31 CDT 2013


Author: mmichelson
Date: Mon May 13 16:45:28 2013
New Revision: 388648

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=388648
Log:
Resolve conflict and reset automerge.


Added:
    team/mmichelson/more_transfer/configs/res_parking.conf.sample
      - copied unchanged from r388309, team/mmichelson/transfer/configs/res_parking.conf.sample
    team/mmichelson/more_transfer/include/asterisk/parking.h
      - copied unchanged from r388309, team/mmichelson/transfer/include/asterisk/parking.h
    team/mmichelson/more_transfer/main/parking.c
      - copied unchanged from r388309, team/mmichelson/transfer/main/parking.c
    team/mmichelson/more_transfer/res/parking/
      - copied from r388309, team/mmichelson/transfer/res/parking/
    team/mmichelson/more_transfer/res/parking/parking_applications.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_applications.c
    team/mmichelson/more_transfer/res/parking/parking_bridge.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_bridge.c
    team/mmichelson/more_transfer/res/parking/parking_bridge_features.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_bridge_features.c
    team/mmichelson/more_transfer/res/parking/parking_controller.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_controller.c
    team/mmichelson/more_transfer/res/parking/parking_manager.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_manager.c
    team/mmichelson/more_transfer/res/parking/parking_ui.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/parking_ui.c
    team/mmichelson/more_transfer/res/parking/res_parking.h
      - copied unchanged from r388309, team/mmichelson/transfer/res/parking/res_parking.h
    team/mmichelson/more_transfer/res/res_parking.c
      - copied unchanged from r388309, team/mmichelson/transfer/res/res_parking.c
    team/mmichelson/more_transfer/rest-api-templates/event_function_decl.mustache
      - copied unchanged from r388309, team/mmichelson/transfer/rest-api-templates/event_function_decl.mustache
Modified:
    team/mmichelson/more_transfer/   (props changed)
    team/mmichelson/more_transfer/CHANGES
    team/mmichelson/more_transfer/apps/app_userevent.c
    team/mmichelson/more_transfer/bridges/bridge_builtin_features.c
    team/mmichelson/more_transfer/channels/chan_sip.c
    team/mmichelson/more_transfer/include/asterisk/bridging.h
    team/mmichelson/more_transfer/include/asterisk/config_options.h
    team/mmichelson/more_transfer/include/asterisk/stasis_bridging.h
    team/mmichelson/more_transfer/include/asterisk/stasis_channels.h
    team/mmichelson/more_transfer/main/bridging.c
    team/mmichelson/more_transfer/main/bridging_roles.c
    team/mmichelson/more_transfer/main/config_options.c
    team/mmichelson/more_transfer/main/features.c
    team/mmichelson/more_transfer/main/manager_channels.c
    team/mmichelson/more_transfer/main/stasis_bridging.c
    team/mmichelson/more_transfer/main/stasis_channels.c
    team/mmichelson/more_transfer/res/Makefile
    team/mmichelson/more_transfer/res/res_stasis.c
    team/mmichelson/more_transfer/res/res_stasis_http_events.c
    team/mmichelson/more_transfer/res/res_stasis_websocket.c
    team/mmichelson/more_transfer/res/stasis_http/resource_endpoints.h
    team/mmichelson/more_transfer/res/stasis_http/resource_events.h
    team/mmichelson/more_transfer/res/stasis_http/resource_recordings.h
    team/mmichelson/more_transfer/res/stasis_http/resource_sounds.h
    team/mmichelson/more_transfer/rest-api-templates/asterisk_processor.py
    team/mmichelson/more_transfer/rest-api-templates/res_stasis_http_resource.c.mustache
    team/mmichelson/more_transfer/rest-api-templates/stasis_http_resource.h.mustache
    team/mmichelson/more_transfer/rest-api-templates/swagger_model.py
    team/mmichelson/more_transfer/rest-api/api-docs/events.json

Propchange: team/mmichelson/more_transfer/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/mmichelson/more_transfer/
------------------------------------------------------------------------------
Binary property 'branch-11-merged' - no diff available.

Propchange: team/mmichelson/more_transfer/
------------------------------------------------------------------------------
--- bridge_construction-integrated (original)
+++ bridge_construction-integrated Mon May 13 16:45:28 2013
@@ -1,1 +1,1 @@
-/trunk:1-388176
+/trunk:1-388275

Propchange: team/mmichelson/more_transfer/
------------------------------------------------------------------------------
--- more_transfer-integrated (original)
+++ more_transfer-integrated Mon May 13 16:45:28 2013
@@ -1,1 +1,1 @@
-/team/mmichelson/transfer:1-388229
+/team/mmichelson/transfer:1-388647

Propchange: team/mmichelson/more_transfer/
------------------------------------------------------------------------------
--- transfer-integrated (original)
+++ transfer-integrated Mon May 13 16:45:28 2013
@@ -1,1 +1,1 @@
-/team/group/bridge_construction:1-388218
+/team/group/bridge_construction:1-388303

Modified: team/mmichelson/more_transfer/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/CHANGES?view=diff&rev=388648&r1=388647&r2=388648
==============================================================================
--- team/mmichelson/more_transfer/CHANGES (original)
+++ team/mmichelson/more_transfer/CHANGES Mon May 13 16:45:28 2013
@@ -50,6 +50,19 @@
  * The AMI event 'UserEvent' from app_userevent now contains the channel state
    fields. The channel state fields will come before the body fields.
 
+ * The AMI events 'ParkedCall', 'ParkedCallTimeOut', 'ParkedCallGiveUp', and
+   'UnParkedCall' have changed significantly in the new res_parking module.
+   First, channel snapshot data is included for both the parker and the parkee
+   in lieu of the "From" and "Channel" fields. They follow standard channel
+   snapshot format but each field is suffixed with 'Parker' or 'Parkee'
+   depending on which side it applies to. The 'Exten' field is replaced with
+   'ParkingSpace' since the registration of extensions as for parking spaces
+   is no longer mandatory.
+
+ * The AMI event 'Parkinglot' (response to 'Parkinglots' command) in a similar
+   fashion has changed the field names 'StartExten' and 'StopExten' to
+   'StartSpace' and 'StopSpace' respectively.
+
  * The deprecated use of | (pipe) as a separator in the channelvars setting in
    manager.conf has been removed.
 
@@ -101,8 +114,8 @@
 
  * Add support for automixmonitor to the BRIDGE_FEATURES channel variable.
 
- * PARKINGSLOT and PARKEDLOT channel variables will now be set for a parked
-   channel even when comebactoorigin=yes
+ * Parking has been pulled from core and placed into a separate module called
+   res_parking. See Parking changes below for more details.
 
  * You can now have the settings for a channel updated using the FEATURE()
    and FEATUREMAP() functions inherited to child channels by setting
@@ -131,6 +144,43 @@
   if a denoiser is attached to the channel; this option gives them the ability
   to remove the denoiser without having to unload func_speex.
 
+Parking
+-------------------
+ * Parking is now implemented as a module instead of as core functionality.
+   The preferred way to configure parking is now through res_parking.conf while
+   configuration through features.conf is not currently supported.
+
+ * Parked calls are now placed in bridges. This is a largely architectural change,
+   but it could have some implications in allowing for new parked call retrieval
+   methods and the contents of parking lots will be visible though certain bridge
+   commands.
+
+ * The order of arguments for the new parking application are different from the
+   old one to be more intuitive. Timeout and return context/exten/priority are now
+   implemented as options. parking_lot_name is now the first parameter. See the
+   application documentation for Park for more in-depth information.
+
+ * Extensions are no longer automatically created in the dialplan to park calls,
+   pickup parked calls, etc by default.
+
+ * adsipark is no longer supported under the new parking model
+
+ * The PARKINGSLOT channel variable has been deprecated in favor of PARKING_SPACE
+   to match the naming scheme of the new system.
+
+ * PARKING_SPACE and PARKEDLOT channel variables will now be set for a parked
+   channel even when comebactoorigin=yes
+
+ * New CLI command 'parking show' allows you to inspect the currently in use
+   parking lots. 'parking show <parkinglot>' will also show the parked calls
+   in that specific parking lot.
+
+ * The CLI command 'parkedcalls' is now deprecated in favor of
+   'parking show <parkinglot>'.
+
+ * The AMI command 'ParkedCalls' will now accept a 'ParkingLot' argument which
+   can be used to get a list of parked calls only for a specific parking lot.
+
 Queue
 -------------------
  * Add queue available hint.  exten => 8501,hint,Queue:markq_avail
@@ -173,6 +223,12 @@
  * All future modules which utilize Sorcery for object persistence must have a
    column named "id" within their schema when using the Sorcery realtime module.
    This column must be able to contain a string of up to 128 characters in length.
+
+app_userevent
+------------------
+ * UserEvent will now handle duplicate keys by overwriting the previous value
+   assigned to the key. UserEvent invocations will also be distributed to any
+   interested res_stasis applications.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 10 to Asterisk 11 --------------------

Modified: team/mmichelson/more_transfer/apps/app_userevent.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/apps/app_userevent.c?view=diff&rev=388648&r1=388647&r2=388648
==============================================================================
--- team/mmichelson/more_transfer/apps/app_userevent.c (original)
+++ team/mmichelson/more_transfer/apps/app_userevent.c Mon May 13 16:45:28 2013
@@ -39,23 +39,28 @@
 /*** DOCUMENTATION
 	<application name="UserEvent" language="en_US">
 		<synopsis>
-			Send an arbitrary event to the manager interface.
+			Send an arbitrary user-defined event to parties interested in a channel (AMI users and relevant res_stasis applications).
 		</synopsis>
 		<syntax>
 			<parameter name="eventname" required="true" />
 			<parameter name="body" />
 		</syntax>
 		<description>
-			<para>Sends an arbitrary event to the manager interface, with an optional
+			<para>Sends an arbitrary event to interested parties, with an optional
 			<replaceable>body</replaceable> representing additional arguments. The
 			<replaceable>body</replaceable> may be specified as
-			a <literal>,</literal> delimited list of headers. Each additional
-			argument will be placed on a new line in the event. The format of the
-			event will be:</para>
+			a <literal>,</literal> delimited list of key:value pairs.</para>
+			<para>For AMI, each additional argument will be placed on a new line in
+			the event and the format of the event will be:</para>
 			<para>    Event: UserEvent</para>
 			<para>    UserEvent: &lt;specified event name&gt;</para>
 			<para>    [body]</para>
-			<para>If no <replaceable>body</replaceable> is specified, only Event and UserEvent headers will be present.</para>
+			<para>If no <replaceable>body</replaceable> is specified, only Event and
+			UserEvent headers will be present.</para>
+			<para>For res_stasis applications, the event will be provided as a JSON
+			blob with additional arguments appearing as keys in the object and the
+			<replaceable>eventname</replaceable> under the
+			<literal>eventname</literal> key.</para>
 		</description>
 	</application>
  ***/
@@ -70,7 +75,6 @@
 		AST_APP_ARG(eventname);
 		AST_APP_ARG(extra)[100];
 	);
-	RAII_VAR(struct ast_str *, body, ast_str_create(16), ast_free);
 	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 
@@ -79,25 +83,37 @@
 		return -1;
 	}
 
-	if (!body) {
-		ast_log(LOG_WARNING, "Unable to allocate buffer\n");
-		return -1;
-	}
-
 	parse = ast_strdupa(data);
 
 	AST_STANDARD_APP_ARGS(args, parse);
 
-	for (x = 0; x < args.argc - 1; x++) {
-		ast_str_append(&body, 0, "%s\r\n", args.extra[x]);
+	blob = ast_json_pack("{s: s, s: s}",
+			     "type", "userevent",
+			     "eventname", args.eventname);
+	if (!blob) {
+		return -1;
 	}
 
-	blob = ast_json_pack("{s: s, s: s}",
-			     "eventname", args.eventname,
-			     "body", ast_str_buffer(body));
-	if (!blob) {
-		ast_log(LOG_WARNING, "Unable to create message buffer\n");
-		return -1;
+	for (x = 0; x < args.argc - 1; x++) {
+		char *key, *value = args.extra[x];
+		struct ast_json *json_value;
+
+		key = strsep(&value, ":");
+		if (!value) {
+			/* no ':' in string? */
+			continue;
+		}
+
+		value = ast_strip(value);
+		json_value = ast_json_string_create(value);
+		if (!json_value) {
+			return -1;
+		}
+
+		/* ref stolen by ast_json_object_set */
+		if (ast_json_object_set(blob, key, json_value)) {
+			return -1;
+		}
 	}
 
 	msg = ast_channel_blob_create(

Modified: team/mmichelson/more_transfer/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/bridges/bridge_builtin_features.c?view=diff&rev=388648&r1=388647&r2=388648
==============================================================================
--- team/mmichelson/more_transfer/bridges/bridge_builtin_features.c (original)
+++ team/mmichelson/more_transfer/bridges/bridge_builtin_features.c Mon May 13 16:45:28 2013
@@ -48,6 +48,7 @@
 #include "asterisk/app.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/pbx.h"
+#include "asterisk/parking.h"
 
 /*!
  * \brief Helper function that presents dialtone and grabs extension

Modified: team/mmichelson/more_transfer/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/channels/chan_sip.c?view=diff&rev=388648&r1=388647&r2=388648
==============================================================================
--- team/mmichelson/more_transfer/channels/chan_sip.c (original)
+++ team/mmichelson/more_transfer/channels/chan_sip.c Mon May 13 16:45:28 2013
@@ -295,6 +295,7 @@
 #include "sip/include/security_events.h"
 #include "asterisk/sip_api.h"
 #include "asterisk/app.h"
+#include "asterisk/bridging.h"
 #include "asterisk/stasis_endpoints.h"
 
 /*** DOCUMENTATION
@@ -1202,8 +1203,6 @@
 					      struct sip_request *req, const char *uri);
 static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
 static void check_pendings(struct sip_pvt *p);
-static void *sip_park_thread(void *stuff);
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context);
 
 static void *sip_pickup_thread(void *stuff);
 static int sip_pickup(struct ast_channel *chan);
@@ -21729,7 +21728,7 @@
 					feat = ast_find_call_feature(p->relatedpeer->record_on_feature);
 				}
 			} else if (!strcasecmp(c, "off")) {
-				if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
+				if (ast_strlen_zero(p->relatedpeer->record_off_feature)) {
 					suppress_warning = 1;
 				} else {
 					feat = ast_find_call_feature(p->relatedpeer->record_off_feature);
@@ -24430,160 +24429,6 @@
 	}
 }
 
-
-/*! \brief Park SIP call support function
-	Starts in a new thread, then parks the call
-	XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the
-		audio can't be heard before hangup
-*/
-static void *sip_park_thread(void *stuff)
-{
-	struct ast_channel *transferee, *transferer;	/* Chan1: The transferee, Chan2: The transferer */
-	struct sip_dual *d;
-	int ext;
-	int res;
-
-	d = stuff;
-	transferee = d->chan1;
-	transferer = d->chan2;
-
-	ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", ast_channel_name(transferer), ast_channel_name(transferee));
-
-	res = ast_park_call_exten(transferee, transferer, d->park_exten, d->park_context, 0, &ext);
-
-	sip_pvt_lock(ast_channel_tech_pvt(transferer));
-#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
-	if (res) {
-		destroy_msg_headers(ast_channel_tech_pvt(transferer));
-		ast_string_field_set(ast_channel_tech_pvt(transferer), msg_body, "Unable to park call.");
-		transmit_message(ast_channel_tech_pvt(transferer), 0, 0);
-	} else {
-		/* Then tell the transferer what happened */
-		destroy_msg_headers(ast_channel_tech_pvt(transferer));
-		sprintf(buf, "Call parked on extension '%d'.", ext);
-		ast_string_field_set(ast_channel_tech_pvt(transferer), msg_body, buf);
-		transmit_message(ast_channel_tech_pvt(transferer), 0, 0);
-	}
-#endif
-
-	/* Any way back to the current call??? */
-	/* Transmit response to the REFER request */
-	if (!res)	{
-		/* Transfer succeeded */
-		append_history(ast_channel_tech_pvt(transferer), "SIPpark", "Parked call on %d", ext);
-		transmit_notify_with_sipfrag(ast_channel_tech_pvt(transferer), d->seqno, "200 OK", TRUE);
-		sip_pvt_unlock(ast_channel_tech_pvt(transferer));
-		ast_channel_hangupcause_set(transferer, AST_CAUSE_NORMAL_CLEARING);
-		ast_hangup(transferer); /* This will cause a BYE */
-		ast_debug(1, "SIP Call parked on extension '%d'\n", ext);
-	} else {
-		transmit_notify_with_sipfrag(ast_channel_tech_pvt(transferer), d->seqno, "503 Service Unavailable", TRUE);
-		append_history(ast_channel_tech_pvt(transferer), "SIPpark", "Parking failed\n");
-		sip_pvt_unlock(ast_channel_tech_pvt(transferer));
-		ast_debug(1, "SIP Call parked failed \n");
-		/* Do not hangup call */
-	}
-	deinit_req(&d->req);
-	ast_free(d->park_exten);
-	ast_free(d->park_context);
-	ast_free(d);
-	return NULL;
-}
-
-/*! DO NOT hold any locks while calling sip_park */
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context)
-{
-	struct sip_dual *d;
-	struct ast_channel *transferee, *transferer;
-	pthread_t th;
-
-	transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan1), ast_channel_exten(chan1), ast_channel_context(chan1), ast_channel_linkedid(chan1), ast_channel_amaflags(chan1), "Parking/%s", ast_channel_name(chan1));
-	transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan2), ast_channel_exten(chan2), ast_channel_context(chan2), ast_channel_linkedid(chan2), ast_channel_amaflags(chan2), "SIPPeer/%s", ast_channel_name(chan2));
-	d = ast_calloc(1, sizeof(*d));
-	if (!transferee || !transferer || !d) {
-		if (transferee) {
-			ast_hangup(transferee);
-		}
-		if (transferer) {
-			ast_hangup(transferer);
-		}
-		ast_free(d);
-		return -1;
-	}
-	d->park_exten = ast_strdup(park_exten);
-	d->park_context = ast_strdup(park_context);
-	if (!d->park_exten || !d->park_context) {
-		ast_hangup(transferee);
-		ast_hangup(transferer);
-		ast_free(d->park_exten);
-		ast_free(d->park_context);
-		ast_free(d);
-		return -1;
-	}
-
-	/* Make formats okay */
-	ast_format_copy(ast_channel_readformat(transferee), ast_channel_readformat(chan1));
-	ast_format_copy(ast_channel_writeformat(transferee), ast_channel_writeformat(chan1));
-
-	/* Prepare for taking over the channel */
-	if (ast_channel_masquerade(transferee, chan1)) {
-		ast_hangup(transferee);
-		ast_hangup(transferer);
-		ast_free(d->park_exten);
-		ast_free(d->park_context);
-		ast_free(d);
-		return -1;
-	}
-
-	/* Setup the extensions and such */
-	ast_channel_context_set(transferee, ast_channel_context(chan1));
-	ast_channel_exten_set(transferee, ast_channel_exten(chan1));
-	ast_channel_priority_set(transferee, ast_channel_priority(chan1));
-
-	ast_do_masquerade(transferee);
-
-	/* We make a clone of the peer channel too, so we can play
-	   back the announcement */
-
-	/* Make formats okay */
-	ast_format_copy(ast_channel_readformat(transferer), ast_channel_readformat(chan2));
-	ast_format_copy(ast_channel_writeformat(transferer), ast_channel_writeformat(chan2));
-	ast_channel_parkinglot_set(transferer, ast_channel_parkinglot(chan2));
-
-	/* Prepare for taking over the channel */
-	if (ast_channel_masquerade(transferer, chan2)) {
-		ast_hangup(transferer);
-		ast_free(d->park_exten);
-		ast_free(d->park_context);
-		ast_free(d);
-		return -1;
-	}
-
-	/* Setup the extensions and such */
-	ast_channel_context_set(transferer, ast_channel_context(chan2));
-	ast_channel_exten_set(transferer, ast_channel_exten(chan2));
-	ast_channel_priority_set(transferer, ast_channel_priority(chan2));
-
-	ast_do_masquerade(transferer);
-
-	/* Save original request for followup */
-	copy_request(&d->req, req);
-	d->chan1 = transferee;	/* Transferee */
-	d->chan2 = transferer;	/* Transferer */
-	d->seqno = seqno;
-	if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
-		/* Could not start thread */
-		deinit_req(&d->req);
-		ast_free(d->park_exten);
-		ast_free(d->park_context);
-		ast_free(d);	/* We don't need it anymore. If thread is created, d will be free'd
-				   by sip_park_thread() */
-		return -1;
-	}
-	return 0;
-}
-
-
 /*! \brief SIP pickup support function
  *	Starts in a new thread, then pickup the call
  */
@@ -26173,6 +26018,10 @@
  *
  *	If this function is successful, only the transferer pvt lock will remain on return.  Setting nounlock indicates
  *	to handle_request_do() that the pvt's owner it locked does not require an unlock.
+ */
+
+/* XXX XXX XXX XXX XXX XXX
+ * This function is COMPLETELY broken at the moment. It *will* crash if called
  */
 static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, uint32_t seqno, int *nounlock)
 {
@@ -26374,6 +26223,44 @@
 	return 1;
 }
 
+/*!
+ * Data to set on a channel that runs dialplan
+ * at the completion of a blind transfer
+ */
+struct blind_transfer_cb_data {
+	/*! Contents of the REFER's Referred-by header */
+	const char *referred_by;
+	/*! Domain of the URI in the REFER's Refer-To header */
+	const char *domain;
+	/*! Contents of what to place in a Replaces header of an INVITE */
+	const char *replaces;
+	/*! Redirecting information to set on the channel */
+	struct ast_party_redirecting redirecting;
+	/*! Parts of the redirecting structure that are to be updated */
+	struct ast_set_party_redirecting update_redirecting;
+};
+
+/*!
+ * \internal
+ * \brief Callback called on new outbound channel during blind transfer
+ *
+ * We use this opportunity to populate the channel with data from the REFER
+ * so that, if necessary, we can include proper information on any new INVITE
+ * we may send out.
+ *
+ * \param chan The new outbound channel
+ * \user_data A blind_transfer_cb_data struct
+ */
+static void blind_transfer_cb(struct ast_channel *chan, void *user_data)
+{
+	struct blind_transfer_cb_data *cb_data = user_data;
+
+	pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
+	pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by);
+	pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REPLACES", cb_data->replaces);
+	pbx_builtin_setvar_helper(chan, "SIPDOMAIN", cb_data->domain);
+	ast_channel_update_redirecting(chan, &cb_data->redirecting, &cb_data->update_redirecting);
+}
 
 /*! \brief Handle incoming REFER request */
 /*! \page SIP_REFER SIP transfer Support (REFER)
@@ -26440,22 +26327,13 @@
 	*/
 static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock)
 {
-	/*!
-	 * Chan1: Call between asterisk and transferer
-	 * Chan2: Call between asterisk and transferee
-	 */
-	struct sip_dual current = { 0, };
-	struct ast_channel *chans[2] = { 0, };
 	char *refer_to = NULL;
-	char *refer_to_domain = NULL;
 	char *refer_to_context = NULL;
-	char *referred_by = NULL;
-	char *callid = NULL;
-	int localtransfer = 0;
-	int attendedtransfer = 0;
 	int res = 0;
-	struct ast_party_redirecting redirecting;
-	struct ast_set_party_redirecting update_redirecting;
+	struct blind_transfer_cb_data cb_data;
+	enum ast_transfer_result transfer_res;
+	RAII_VAR(struct ast_channel *, transferer, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_str *, replaces_str, NULL, ast_free_ptr);
 
 	if (req->debug) {
 		ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n",
@@ -26473,8 +26351,7 @@
 			sip_alreadygone(p);
 			pvt_set_needdestroy(p, "outside of dialog");
 		}
-		res = 0;
-		goto handle_refer_cleanup;
+		return 0;
 	}
 
 	/* Check if transfer is allowed from this device */
@@ -26483,24 +26360,21 @@
 		transmit_response(p, "603 Declined (policy)", req);
 		append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
 		/* Do not destroy SIP session */
-		res = 0;
-		goto handle_refer_cleanup;
+		return 0;
 	}
 
 	if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
 		/* Already have a pending REFER */
 		transmit_response(p, "491 Request pending", req);
 		append_history(p, "Xfer", "Refer failed. Request pending.");
-		res = 0;
-		goto handle_refer_cleanup;
+		return 0;
 	}
 
 	/* Allocate memory for call transfer data */
 	if (!p->refer && !sip_refer_alloc(p)) {
 		transmit_response(p, "500 Internal Server Error", req);
 		append_history(p, "Xfer", "Refer failed. Memory allocation error.");
-		res = -3;
-		goto handle_refer_cleanup;
+		return -3;
 	}
 
 	res = get_refer_info(p, req);	/* Extract headers */
@@ -26534,9 +26408,9 @@
 			}
 			break;
 		}
-		res = 0;
-		goto handle_refer_cleanup;
-	}
+		return 0;
+	}
+
 	if (ast_strlen_zero(p->context)) {
 		ast_string_field_set(p, context, sip_cfg.default_context);
 	}
@@ -26557,70 +26431,16 @@
 	/* Is this a repeat of a current request? Ignore it */
 	/* Don't know what else to do right now. */
 	if (req->ignore) {
-		goto handle_refer_cleanup;
-	}
-
-	/* If this is a blind transfer, we have the following
-	channels to work with:
-	- chan1, chan2: The current call between transferer and transferee (2 channels)
-	- target_channel: A new call from the transferee to the target (1 channel)
-	We need to stay tuned to what happens in order to be able
-	to bring back the call to the transferer */
-
-	/* If this is a attended transfer, we should have all call legs within reach:
-	- chan1, chan2: The call between the transferer and transferee (2 channels)
-	- target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
-	We want to bridge chan2 with targetcall_pvt!
-
-	The replaces call id in the refer message points
-	to the call leg between Asterisk and the transferer.
-	So we need to connect the target and the transferee channel
-	and hangup the two other channels silently
-
-	If the target is non-local, the call ID could be on a remote
-	machine and we need to send an INVITE with replaces to the
-	target. We basically handle this as a blind transfer
-	and let the sip_call function catch that we need replaces
-	header in the INVITE.
-	*/
+		return 0;
+	}
 
 	/* Get the transferer's channel */
-	chans[0] = current.chan1 = p->owner;
-
-	/* Find the other part of the bridge (2) - transferee */
-	chans[1] = current.chan2 = ast_bridged_channel(current.chan1);
-
-	ast_channel_ref(current.chan1);
-	if (current.chan2) {
-		ast_channel_ref(current.chan2);
-	}
+	transferer = ast_channel_ref(p->owner);
 
 	if (sipdebug) {
-		ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n",
+		ast_debug(3, "SIP %s transfer: Transferer channel %s\n",
 			p->refer->attendedtransfer ? "attended" : "blind",
-			ast_channel_name(current.chan1),
-			current.chan2 ? ast_channel_name(current.chan2) : "<none>");
-	}
-
-	if (!current.chan2 && !p->refer->attendedtransfer) {
-		/* No bridged channel, propably IVR or echo or similar... */
-		/* Guess we should masquerade or something here */
-		/* Until we figure it out, refuse transfer of such calls */
-		if (sipdebug) {
-			ast_debug(3, "Refused SIP transfer on non-bridged channel.\n");
-		}
-		p->refer->status = REFER_FAILED;
-		append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
-		transmit_response(p, "603 Declined", req);
-		res = -1;
-		goto handle_refer_cleanup;
-	}
-
-	if (current.chan2) {
-		if (sipdebug) {
-			ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", ast_channel_name(current.chan2));
-		}
-		ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
+			ast_channel_name(transferer));
 	}
 
 	ast_set_flag(&p->flags[0], SIP_GOTREFER);
@@ -26631,8 +26451,9 @@
 	/* Attended transfer: Find all call legs and bridge transferee with target*/
 	if (p->refer->attendedtransfer) {
 		/* both p and p->owner _MUST_ be locked while calling local_attended_transfer */
-		if ((res = local_attended_transfer(p, &current, req, seqno, nounlock))) {
-			goto handle_refer_cleanup; /* We're done with the transfer */
+		if ((res = local_attended_transfer(p, NULL, req, seqno, nounlock))) {
+			ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+			return res;
 		}
 		/* Fall through for remote transfers that we did not find locally */
 		if (sipdebug) {
@@ -26643,210 +26464,74 @@
 
 	/* Copy data we can not safely access after letting the pvt lock go. */
 	refer_to = ast_strdupa(p->refer->refer_to);
-	refer_to_domain = ast_strdupa(p->refer->refer_to_domain);
 	refer_to_context = ast_strdupa(p->refer->refer_to_context);
-	referred_by = ast_strdupa(p->refer->referred_by);
-	callid = ast_strdupa(p->callid);
-	localtransfer = p->refer->localtransfer;
-	attendedtransfer = p->refer->attendedtransfer;
+
+	ast_party_redirecting_init(&cb_data.redirecting);
+	memset(&cb_data.update_redirecting, 0, sizeof(cb_data.update_redirecting));
+	change_redirecting_information(p, req, &cb_data.redirecting, &cb_data.update_redirecting, 0);
+
+	cb_data.domain = ast_strdupa(p->refer->refer_to_domain);
+	cb_data.referred_by = ast_strdupa(p->refer->referred_by);
+
+	if (!ast_strlen_zero(p->refer->replaces_callid)) {
+		replaces_str = ast_str_create(128);
+		if (!replaces_str) {
+			ast_log(LOG_NOTICE, "Unable to create Replaces string for remote attended transfer. Transfer failed\n");
+			ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+			ast_party_redirecting_free(&cb_data.redirecting);
+			return -1;
+		}
+		ast_str_append(&replaces_str, 0, "%s%s%s%s%s", p->refer->replaces_callid,
+				!ast_strlen_zero(p->refer->replaces_callid_totag) ? ";to-tag=" : "",
+				S_OR(p->refer->replaces_callid_totag, ""),
+				!ast_strlen_zero(p->refer->replaces_callid_fromtag) ? ";from-tag=" : "",
+				S_OR(p->refer->replaces_callid_fromtag, ""));
+		cb_data.replaces = ast_str_buffer(replaces_str);
+	} else {
+		cb_data.replaces = NULL;
+	}
 
 	if (!*nounlock) {
 		ast_channel_unlock(p->owner);
 		*nounlock = 1;
 	}
+
 	sip_pvt_unlock(p);
-
-	/* Parking a call.  DO NOT hold any locks while calling ast_parking_ext_valid() */
-	if (localtransfer && ast_parking_ext_valid(refer_to, current.chan1, refer_to_context)) {
-		sip_pvt_lock(p);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
-		p->refer->status = REFER_200OK;
-		append_history(p, "Xfer", "REFER to call parking.");
-		sip_pvt_unlock(p);
-
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
-			"TransferMethod: SIP\r\n"
-			"TransferType: Blind\r\n"
-			"Channel: %s\r\n"
-			"Uniqueid: %s\r\n"
-			"SIP-Callid: %s\r\n"
-			"TargetChannel: %s\r\n"
-			"TargetUniqueid: %s\r\n"
-			"TransferExten: %s\r\n"
-			"Transfer2Parking: Yes\r\n",
-			ast_channel_name(current.chan1),
-			ast_channel_uniqueid(current.chan1),
-			callid,
-			ast_channel_name(current.chan2),
-			ast_channel_uniqueid(current.chan2),
-			refer_to);
-
-		if (sipdebug) {
-			ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", ast_channel_name(current.chan2), ast_channel_name(current.chan1));
-		}
-
-		/* DO NOT hold any locks while calling sip_park */
-		if (sip_park(current.chan2, current.chan1, req, seqno, refer_to, refer_to_context)) {
-			sip_pvt_lock(p);
-			transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
-		} else {
-			sip_pvt_lock(p);
-		}
-		goto handle_refer_cleanup;
-	}
-
-	/* Blind transfers and remote attended xfers.
-	 * Locks should not be held while calling pbx_builtin_setvar_helper. This function
-	 * locks the channel being passed into it.*/
-	if (current.chan1 && current.chan2) {
-		ast_debug(3, "chan1->name: %s\n", ast_channel_name(current.chan1));
-		pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", ast_channel_name(current.chan2));
-	}
-
-	if (current.chan2) {
-		pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", ast_channel_name(current.chan1));
-		pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", refer_to_domain);
-		pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
-		/* One for the new channel */
-		pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
-		/* Attended transfer to remote host, prepare headers for the INVITE */
-		if (!ast_strlen_zero(referred_by)) {
-			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", referred_by);
-		}
-
-		/* When a call is transferred to voicemail from a Digium phone, there may be
-		 * a Diversion header present in the REFER with an appropriate reason parameter
-		 * set. We need to update the redirecting information appropriately.
-		 */
-		ast_channel_lock(p->owner);
-		sip_pvt_lock(p);
-		ast_party_redirecting_init(&redirecting);
-		memset(&update_redirecting, 0, sizeof(update_redirecting));
-		change_redirecting_information(p, req, &redirecting, &update_redirecting, FALSE);
-
-		/* Do not hold the pvt lock during a call that causes an indicate or an async_goto.
-		 * Those functions lock channels which will invalidate locking order if the pvt lock
-		 * is held.*/
-		sip_pvt_unlock(p);
-		ast_channel_unlock(p->owner);
-		ast_channel_update_redirecting(current.chan2, &redirecting, &update_redirecting);
-		ast_party_redirecting_free(&redirecting);
-	}
-
+	transfer_res = ast_bridge_transfer_blind(transferer, refer_to, refer_to_context, blind_transfer_cb, &cb_data);
 	sip_pvt_lock(p);
-	/* Generate a Replaces string to be used in the INVITE during attended transfer */
-	if (!ast_strlen_zero(p->refer->replaces_callid)) {
-		char tempheader[SIPBUFSIZE];
-		snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
-			p->refer->replaces_callid_totag ? ";to-tag=" : "",
-			p->refer->replaces_callid_totag,
-			p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
-			p->refer->replaces_callid_fromtag);
-
-		if (current.chan2) {
-			sip_pvt_unlock(p);
-			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
-			sip_pvt_lock(p);
-		}
-	}
-
-	/* Connect the call */
-
-	/* FAKE ringing if not attended transfer */
-	if (!p->refer->attendedtransfer) {
-		transmit_notify_with_sipfrag(p, seqno, "180 Ringing", FALSE);
-	}
-
-	/* For blind transfer, this will lead to a new call */
-	/* For attended transfer to remote host, this will lead to
-	   a new SIP call with a replaces header, if the dial plan allows it
-	*/
-	if (!current.chan2) {
-		/* We have no bridge, so we're talking with Asterisk somehow */
-		/* We need to masquerade this call */
-		/* What to do to fix this situation:
-		   * Set up the new call in a new channel
-		   * Let the new channel masq into this channel
-		   Please add that code here :-)
-		*/
+
+	switch (transfer_res) {
+	case AST_BRIDGE_TRANSFER_INVALID:
+		res = -1;
 		p->refer->status = REFER_FAILED;
 		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
 		append_history(p, "Xfer", "Refer failed (only bridged calls).");
+		break;
+	case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
 		res = -1;
-		goto handle_refer_cleanup;
-	}
-	ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */
-
-	sip_pvt_unlock(p);
-
-	/* For blind transfers, move the call to the new extensions. For attended transfers on multiple
-	 * servers - generate an INVITE with Replaces. Either way, let the dial plan decided
-	 * indicate before masquerade so the indication actually makes it to the real channel
-	 * when using local channels with MOH passthru */
-	ast_indicate(current.chan2, AST_CONTROL_UNHOLD);
-	res = ast_async_goto(current.chan2, refer_to_context, refer_to, 1);
-
-	if (!res) {
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
-			"TransferMethod: SIP\r\n"
-			"TransferType: Blind\r\n"
-			"Channel: %s\r\n"
-			"Uniqueid: %s\r\n"
-			"SIP-Callid: %s\r\n"
-			"TargetChannel: %s\r\n"
-			"TargetUniqueid: %s\r\n"
-			"TransferExten: %s\r\n"
-			"TransferContext: %s\r\n",
-			ast_channel_name(current.chan1),
-			ast_channel_uniqueid(current.chan1),
-			callid,
-			ast_channel_name(current.chan2),
-			ast_channel_uniqueid(current.chan2),
-			refer_to,
-			refer_to_context);
-		/* Success  - we have a new channel */
-		ast_debug(3, "%s transfer succeeded. Telling transferer.\n", attendedtransfer? "Attended" : "Blind");
-
-		/* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
-		ast_channel_lock(current.chan1);
-		ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
-		ast_channel_unlock(current.chan1);
-
-		sip_pvt_lock(p);
-		transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
-		if (p->refer->localtransfer) {
-			p->refer->status = REFER_200OK;
-		}
-		if (p->owner) {
-			ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
-		}
+		p->refer->status = REFER_FAILED;
+		transmit_notify_with_sipfrag(p, seqno, "403 Forbidden", TRUE);
+		append_history(p, "Xfer", "Refer failed (bridge does not permit transfers)");
+		break;
+	case AST_BRIDGE_TRANSFER_FAIL:
+		res = -1;
+		p->refer->status = REFER_FAILED;
+		transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
+		append_history(p, "Xfer", "Refer failed (internal error)");
+		break;
+	case AST_BRIDGE_TRANSFER_SUCCESS:
+		res = 0;
+		p->refer->status = REFER_200OK;
+		transmit_notify_with_sipfrag(p, seqno, "200 OK", TRUE);
+		ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
 		append_history(p, "Xfer", "Refer succeeded.");
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
-		/* Do not hangup call, the other side do that when we say 200 OK */
-		/* We could possibly implement a timer here, auto congestion */
-		res = 0;
-	} else {
-		sip_pvt_lock(p);
-		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Don't delay hangup */
-		ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
-		append_history(p, "Xfer", "Refer failed.");
-		/* Failure of some kind */
-		p->refer->status = REFER_FAILED;
-		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
-		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
-		res = -1;
-	}
-
-handle_refer_cleanup:
-	if (current.chan1) {
-		ast_channel_unref(current.chan1);
-	}
-	if (current.chan2) {
-		ast_channel_unref(current.chan2);
-	}
-
-	/* Make sure we exit with the pvt locked */
+		break;
+	default:
+		break;
+	}
+
+	ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+	ast_party_redirecting_free(&cb_data.redirecting);
 	return res;
 }
 

Modified: team/mmichelson/more_transfer/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/include/asterisk/bridging.h?view=diff&rev=388648&r1=388647&r2=388648
==============================================================================
--- team/mmichelson/more_transfer/include/asterisk/bridging.h (original)

[... 3762 lines stripped ...]



More information about the asterisk-commits mailing list