[svn-commits] mmichelson: branch mmichelson/sip_transfer r387022 - /team/mmichelson/sip_tra...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Tue Apr 30 16:11:05 CDT 2013
Author: mmichelson
Date: Tue Apr 30 16:11:02 2013
New Revision: 387022
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=387022
Log:
First pass at changing blind transfer behavior in SIP.
Quite a lot has been cut, including all of the parking code.
Some of what has been cut needs to come back in some form
or another. I have made a list of cut items at the top of
handle_request_refer() and have indicated whether they need
to return somehow or if they can stay gone.
Modified:
team/mmichelson/sip_transfer/channels/chan_sip.c
Modified: team/mmichelson/sip_transfer/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/sip_transfer/channels/chan_sip.c?view=diff&rev=387022&r1=387021&r2=387022
==============================================================================
--- team/mmichelson/sip_transfer/channels/chan_sip.c (original)
+++ team/mmichelson/sip_transfer/channels/chan_sip.c Tue Apr 30 16:11:02 2013
@@ -295,6 +295,7 @@
#include "sip/include/security_events.h"
#include "asterisk/sip_api.h"
#include "asterisk/app.h"
+#include "asterisk/bridging.h"
/*** DOCUMENTATION
<application name="SIPDtmfMode" language="en_US">
@@ -1199,8 +1200,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);
@@ -24377,163 +24376,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
-*/
-
-/*XXX Get rid of this atrocity */
-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 */
-/* XXX Get rid of this atrocity */
-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
*/
@@ -26391,14 +26233,67 @@
We can't destroy dialogs, since we want the call to continue.
*/
+
+/* XXX First round of cleaning of this function has been completed. The following
+ * are things that are missing at the moment and likely should be brought back
+ * somehow:
+ *
+ * 1) local_attended_transfer is completely untouched. Due to the changes in
+ * handle_request_refer(), we pass a NULL sip_dual into local_attended_transfer now,
+ * so it is completely broken at this point.
+ *
+ * 2) There are several channel variables that are no longer set on the transferee
+ * channel during blind transfers. This is because we cannot be making assumptions
+ * about the nature of the transfer, such as that we are bridged to only one party.
+ * The following is a list of the channel variable settings that are currently missing:
+ *
+ * a) BLINDTRANSFER = transferer channel name
+ * b) SIP_DOMAIN = refer_to_domain
+ * c) _SIP_TRANSFER = "yes"
+ * d) _SIP_TRANSFER_REFERER = referred_by
+ * e) _SIPTRANSFER_REPLACES = callid;to-tag=blah;from-tag=blah
+ *
+ * We likely will need to add a new parameter to ast_bridge_transfer_blind() so
+ * that either a callback can be called to set data on the channel that goes out
+ * to the dialplan.
+ *
+ * 3) We no longer update redirecting information on the transferee if there is
+ * a diversion header in the REFER. This was useful for determining a "reason" for
+ * the transfer, such as a transfer to voicemail. The solution provided for
+ * point 2) will help here too.
+ *
+ * 4) Some debugging and history are gone. While it likely won't be possible
+ * to have the same level of debugging and history as before, we should still
+ * strive to put in as much as possible.
+ *
+ * And the following are things that have changed but are likely not a problem:
+ *
+ * 1) We no longer queue a hold and unhold frame on the transferer channel if
+ * bridged to a transferee channel when doing a blind transfer. Performing the
+ * hold/unhold was odd because a) the transferee was likely already on hold
+ * anyway during the time the transferer was dialing the remote party, and
+ * b) the time between the hold and unhold would be fractions of a second.
+ *
+ * 2) We no longer send a SIP NOTIFY with sipfrag "180 Ringing". There are
+ * two reasons why:
+ * a) We were faking the ringing in the first place.
+ * b) Many devices will interpret a ringing notification as a successful transfer.
+ * This means that if we were to send the ringing notification and then a failure,
+ * the blind transfer would fail and would be unrecoverable.
+ *
+ * 3) Some failures are indicated differently now. A lot of the failures are not
+ * fleshed out at this point, but it's safe to say that some failures (such as
+ * a lack of a bridge on a blind transfer) will have a different error response
+ * than previously. This is really not an issue since communicating failure is
+ * the biggest thing needed.
+ *
+ * 4) Manager events are now gone. This is on purpose because the idea now is for
+ * the bridging core to issue general-purpose stasis events for transfers instead.
+ * This will make transfers easier to indicate. It does, however, result in some
+ * information, such as SIP Call-IDs missing from such transfer events.
+ */
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;
@@ -26407,8 +26302,7 @@
int localtransfer = 0;
int attendedtransfer = 0;
int res = 0;
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
+ RAII_VAR(struct ast_channel *, transferer, NULL, ao2_cleanup);
if (req->debug) {
ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n",
@@ -26490,6 +26384,7 @@
res = 0;
goto handle_refer_cleanup;
}
+
if (ast_strlen_zero(p->context)) {
ast_string_field_set(p, context, sip_cfg.default_context);
}
@@ -26513,88 +26408,13 @@
goto handle_refer_cleanup;
}
- /* XXX Up to this point, everything is fine. It's the stuff below this point
- * where we can start to cull code.
- */
-
- /* 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.
- */
-
- /* XXX There's no need for the 'current' structure any longer. It
- * requires reaching across the bridge to populate and we don't need
- * to do that any more. We just need the transferer channel(s).
- */
-
/* 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) {
- /* XXX This message needs to be changed not to reference the transferee
- * channel. We can't know what's on the other side of the bridge
- */
- 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>");
- }
-
- /* XXX This is trying to prevent a blind transfer of an unbridged channel.
- * This is taken care of already by the bridging core and can be removed
- * entirely. This does raise the point that different transfer failures will
- * need to result in different responses/history entries
- */
- 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;
- }
-
- /* XXX Hm, queuing a hold frame is interesting. Should that maybe become
- * part of the bridging core? Should we only queue a hold if the channel
- * is bridged?
- */
- 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);
@@ -26605,7 +26425,7 @@
/* 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, ¤t, req, seqno, nounlock))) {
+ if ((res = local_attended_transfer(p, NULL, req, seqno, nounlock))) {
goto handle_refer_cleanup; /* We're done with the transfer */
}
/* Fall through for remote transfers that we did not find locally */
@@ -26614,18 +26434,6 @@
}
/* Fallthrough if we can't find the call leg internally */
}
-
- /* XXX From here on, we know we're not doing a local attended transfer (i.e. attended transfer
- * of a call on the system). We're either doing a remote attended transfer or blind transfer.
- *
- * In the case of a remote attended transfer, what we're supposed to do is generate an INVITE with replaces
- * to the destination specified in the Refer-to header. We treat the destination as being an extension
- * in the dialplan, so the treatment of remote attended transfers and blind transfers is more-or-less the
- * same. The big difference is that the resulting INVITE on a remote attended transfer will have a Replaces
- * header. This is making a pretty BIG assumption that the resulting dialplan will result in an outbound
- * call and that it will be to a SIP destination, and that the SIP destination will be able to replace
- * as appropriate.
- */
/* Copy data we can not safely access after letting the pvt lock go. */
refer_to = ast_strdupa(p->refer->refer_to);
@@ -26640,227 +26448,27 @@
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() */
-
- /* XXX This takes care of blind transferring to a parking lot. The bridge transfer code
- * does this for us. No need to do anything special here. That AMI event. yowza.
- * Transfer2Parking? really?
- */
- 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));
- }
-
- /* XXX Now we run into an interesting issue. We're used to setting channel variables on the
- * transferee channel. Some of these appear to be intended to go onto a newly-created channel
- * when we create a new one. These we can just put on the transferer channel since it is used
- * as the requestor for the new channel in the bridge blind transfer code. The other diagnostic
- * channel variables...I'm not so sure about.
- */
- 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.
- */
-
- /* XXX This bit should probably still exist in our transfer code. However, it really
- * has nothing to do with the transferee channel. We could potentially change to do
- * this only if the original call was bridged.
- */
- 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);
- }
-
+ switch (ast_bridge_transfer_blind(transferer, refer_to, refer_to_context, NULL)) {
+ case AST_BRIDGE_TRANSFER_INVALID:
+ case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
+ case AST_BRIDGE_TRANSFER_FAIL:
+ res = -1;
+ transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", FALSE);
+ p->refer->status = REFER_FAILED;
+ break;
+ case AST_BRIDGE_TRANSFER_SUCCESS:
+ res = 0;
+ transmit_notify_with_sipfrag(p, seqno, "200 OK", FALSE);
+ ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
+ break;
+ default:
+ break;
+ }
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);
-
- /* XXX Set this on the transferer channel instead. The new channel in a blind
- * transfer will get this.
- */
- 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 */
- /* XXX Technically, we could add a framehook to the call to ast_bridge_transfer_blind()
- * so that we can give real ringing notification to the transferer, but since the goal
- * is simply to convert to using the core API, we'll hold off on that for the time being
- */
- 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 :-)
- */
- 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).");
- 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);
-
- /* XXX This check of 'res' needs to be changed to check the potential
- * returns of ast_bridge_transfer_blind(). Most of what's done here
- * can be removed though. Manager and CEL events will be generated
- * by the core.
- */
- 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);
- }
- 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 */
return res;
}
More information about the svn-commits
mailing list