Newer
Older
ast_channel_unlock(c);
if (earlyreplace || oneleggedreplace ) {
/* Force the masq to happen */
if ((f = ast_read(replacecall))) { /* Force the masq to happen */
ast_frfree(f);
f = NULL;
ast_debug(4, "Invite/Replace: Could successfully read frame from RING channel!\n");
} else {
ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from RING channel \n");
}
c->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
if (!oneleggedreplace)
ast_channel_unlock(replacecall);
} else { /* Bridged call, UP channel */
if ((f = ast_read(replacecall))) { /* Force the masq to happen */
/* Masq ok */
ast_frfree(f);
f = NULL;
ast_debug(3, "Invite/Replace: Could successfully read frame from channel! Masq done.\n");
} else {
ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from channel. Transfer failed\n");
}
ast_channel_unlock(replacecall);
}
sip_pvt_unlock(p->refer->refer_call);
ast_setstate(c, AST_STATE_DOWN);
ast_debug(4, "After transfer:----------------------------\n");
ast_debug(4, " -- C: %s State %s\n", c->name, ast_state2str(c->_state));
if (replacecall)
ast_debug(4, " -- replacecall: %s State %s\n", replacecall->name, ast_state2str(replacecall->_state));
if (p->owner) {
ast_debug(4, " -- P->owner: %s State %s\n", p->owner->name, ast_state2str(p->owner->_state));
test = ast_bridged_channel(p->owner);
if (test)
ast_debug(4, " -- Call bridged to P->owner: %s State %s\n", test->name, ast_state2str(test->_state));
else
ast_debug(4, " -- No call bridged to C->owner \n");
} else
ast_debug(4, " -- No channel yet \n");
ast_debug(4, "End After transfer:----------------------------\n");
ast_channel_unlock(p->owner); /* Unlock new owner */
if (!oneleggedreplace)
sip_pvt_unlock(p); /* Unlock SIP structure */
/* The call should be down with no ast_channel, so hang it up */
c->tech_pvt = dialog_unref(c->tech_pvt);
ast_hangup(c);
return 0;
}
/*! \brief Handle incoming INVITE request
\note If the INVITE has a Replaces header, it is part of an
* attended transfer. If so, we do not go through the dial
* plan but tries to find the active call and masquerade
* into it
*/
static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock)
{
int res = 1;
int gotdest;
const char *p_replaces;
char *replace_id = NULL;
const char *required;
unsigned int required_profile = 0;
struct ast_channel *c = NULL; /* New channel */
/* Find out what they support */
if (!p->sipoptions) {
const char *supported = get_header(req, "Supported");
if (!ast_strlen_zero(supported))
parse_sip_options(p, supported);
}
/* Find out what they require */
if (!ast_strlen_zero(required)) {
required_profile = parse_sip_options(NULL, required);
if (required_profile && required_profile != SIP_OPT_REPLACES) {
/* At this point we only support REPLACES */
transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, required);
Olle Johansson
committed
ast_log(LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s\n", required);
p->invitestate = INV_COMPLETED;
Kevin P. Fleming
committed
if (!p->lastinvite)
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return -1;
}
}
/* Check if this is a loop */
if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->owner->_state != AST_STATE_UP)) {
/* This is a call to ourself. Send ourselves an error code and stop
processing immediately, as SIP really has no good mechanism for
being able to call yourself */
/* If pedantic is on, we need to check the tags. If they're different, this is
in fact a forked call through a SIP proxy somewhere. */
transmit_response(p, "482 Loop Detected", req);
p->invitestate = INV_COMPLETED;
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return 0;
}
if (!req->ignore && p->pendinginvite) {
/* We already have a pending invite. Sorry. You are on hold. */
transmit_response(p, "491 Request Pending", req);
ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
/* Don't destroy dialog here */
return 0;
}
p_replaces = get_header(req, "Replaces");
if (!ast_strlen_zero(p_replaces)) {
/* We have a replaces header */
char *ptr;
char *fromtag = NULL;
char *totag = NULL;
char *start, *to;
int error = 0;
if (p->owner) {
ast_debug(3, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid);
transmit_response(p, "400 Bad request", req); /* The best way to not not accept the transfer */
/* Do not destroy existing call */
return -1;
}
if (sipdebug)
ast_debug(3, "INVITE part of call transfer. Replaces [%s]\n", p_replaces);
/* Create a buffer we can manipulate */
replace_id = ast_strdupa(p_replaces);
ast_uri_decode(replace_id);
if (!p->refer && !sip_refer_allocate(p)) {
transmit_response(p, "500 Server Internal Error", req);
append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory.");
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
p->invitestate = INV_COMPLETED;
return -1;
}
/* Todo: (When we find phones that support this)
if the replaces header contains ";early-only"
we can only replace the call in early
stage, not after it's up.
If it's not in early mode, 486 Busy.
*/
/* Skip leading whitespace */
replace_id = ast_skip_blanks(replace_id);
start = replace_id;
while ( (ptr = strsep(&start, ";")) ) {
ptr = ast_skip_blanks(ptr); /* XXX maybe unnecessary ? */
if ( (to = strcasestr(ptr, "to-tag=") ) )
totag = to + 7; /* skip the keyword */
else if ( (to = strcasestr(ptr, "from-tag=") ) ) {
fromtag = to + 9; /* skip the keyword */
fromtag = strsep(&fromtag, "&"); /* trim what ? */
}
}
if (sipdebug)
ast_debug(4,"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>");
15169
15170
15171
15172
15173
15174
15175
15176
15177
15178
15179
15180
15181
15182
15183
15184
15185
15186
15187
/* 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) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
transmit_response(p, "481 Call Leg Does Not Exist (Replaces)", req);
error = 1;
}
/* At this point, bot the pvt and the owner of the call to be replaced is locked */
/* The matched call is the call from the transferer to Asterisk .
We want to bridge the bridged part of the call to the
incoming invite, thus taking over the refered call */
if (p->refer->refer_call == p) {
ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
p->refer->refer_call = dialog_unref(p->refer->refer_call);
transmit_response(p, "400 Bad request", req); /* The best way to not not accept the transfer */
error = 1;
}
if (!error && !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 */
transmit_response(p, "481 Call Leg Does Not Exist (Replace)", req);
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 ) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
transmit_response(p, "603 Declined (Replaces)", req);
error = 1;
}
if (error) { /* Give up this dialog */
append_history(p, "Xfer", "INVITE/Replace Failed.");
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
sip_pvt_unlock(p);
if (p->refer->refer_call) {
sip_pvt_unlock(p->refer->refer_call);
ast_channel_unlock(p->refer->refer_call->owner);
}
p->invitestate = INV_COMPLETED;
return -1;
}
}
/* Check if this is an INVITE that sets up a new dialog or
a re-invite in an existing dialog */
if (!req->ignore) {
int newcall = (p->initreq.headers ? TRUE : FALSE);
sip_cancel_destroy(p);
/* This also counts as a pending invite */
p->pendinginvite = seqno;
check_via(p, req);
copy_request(&p->initreq, req); /* Save this INVITE as the transaction basis */
if (sipdebug)
ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
if (!p->owner) { /* Not a re-invite */
if (debug)
ast_verbose("Using INVITE request as basis request - %s\n", p->callid);
if (newcall)
append_history(p, "Invite", "New call: %s", p->callid);
parse_ok_contact(p, req);
} else { /* Re-invite on existing call */
ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */
/* Handle SDP here if we already have an owner */
if (find_sdp(req)) {
if (process_sdp(p, req)) {
transmit_response(p, "488 Not acceptable here", req);
Kevin P. Fleming
committed
if (!p->lastinvite)
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
} else {
p->jointcapability = p->capability;
ast_debug(1, "Hm.... No sdp for the moment\n");
if (p->do_history) /* This is a response, note what it was for */
}
} else if (debug)
ast_verbose("Ignoring this INVITE request\n");
if (!p->lastinvite && !req->ignore && !p->owner) {
/* This is a new invite */
/* Handle authentication if this is our first invite */
res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
if (res == AUTH_CHALLENGE_SENT) {
p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */
if (res < 0) { /* Something failed in authentication */
if (res == AUTH_FAKE_AUTH) {
ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
transmit_fake_auth_response(p, req, 1);
} else {
ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
transmit_response_reliable(p, "403 Forbidden", req);
p->invitestate = INV_COMPLETED;
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_string_field_set(p, theirtag, NULL);
return 0;
}
/* We have a succesful authentication, process the SDP portion if there is one */
if (find_sdp(req)) {
if (process_sdp(p, req)) {
/* Unacceptable codecs */
transmit_response_reliable(p, "488 Not acceptable here", req);
p->invitestate = INV_COMPLETED;
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_debug(1, "No compatible codecs for this SIP call.\n");
} else { /* No SDP in invite, call control session */
p->jointcapability = p->capability;
ast_debug(2, "No SDP in Invite, third party call control\n");
/* Queue NULL frame to prod ast_rtp_bridge if appropriate */
/* This seems redundant ... see !p-owner above */
ast_queue_frame(p->owner, &ast_null_frame);
/* Initialize the context if it hasn't been already */
if (ast_strlen_zero(p->context))
ast_string_field_set(p, context, default_context);
/* Check number of concurrent calls -vs- incoming limit HERE */
ast_debug(1, "Checking SIP call limits for device %s\n", p->username);
if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
ast_log(LOG_NOTICE, "Failed to place call for user %s, too many calls\n", p->username);
transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
p->invitestate = INV_COMPLETED;
}
return 0;
}
gotdest = get_destination(p, NULL); /* Get destination right away */
get_rdnis(p, NULL); /* Get redirect information */
extract_uri(p, req); /* Get the Contact URI */
build_contact(p); /* Build our contact header */
ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
}
if (!replace_id && gotdest) { /* No matching extension found */
if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP))
transmit_response_reliable(p, "484 Address Incomplete", req);
transmit_response_reliable(p, "404 Not Found", req);
ast_log(LOG_NOTICE, "Call from '%s' to extension"
" '%s' rejected because extension not found.\n",
S_OR(p->username, p->peername), p->exten);
}
p->invitestate = INV_COMPLETED;
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
} else {
/* If no extension was specified, use the s one */
/* Basically for calling to IP/Host name only */
if (ast_strlen_zero(p->exten))
ast_string_field_set(p, exten, "s");
/* Initialize our tag */
Kevin P. Fleming
committed
make_our_tag(p->tag, sizeof(p->tag));
/* First invitation - create the channel */
c = sip_new(p, AST_STATE_DOWN, S_OR(p->username, NULL));
/* Save Record-Route for any later requests we make on this dialogue */
build_route(p, req, 0);
if (c) {
/* Pre-lock the call */
} else {
if (sipdebug) {
if (!req->ignore)
ast_debug(2, "Got a SIP re-invite for call %s\n", p->callid);
ast_debug(2, "Got a SIP re-transmit of INVITE for call %s\n", p->callid);
if (!req->ignore && p)
p->lastinvite = seqno;
if (replace_id) { /* Attended transfer or call pickup - we're the target */
/* Go and take over the target call */
if (sipdebug)
ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
return handle_invite_replaces(p, req, debug, seqno, sin);
}
if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */
switch(c->_state) {
case AST_STATE_DOWN:
ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
transmit_response(p, "100 Trying", req);
Olle Johansson
committed
p->invitestate = INV_PROCEEDING;
ast_setstate(c, AST_STATE_RING);
if (strcmp(p->exten, ast_pickup_ext())) { /* Call to extension -start pbx on this call */
Kevin P. Fleming
committed
enum ast_pbx_result res;
res = ast_pbx_start(c);
switch(res) {
Kevin P. Fleming
committed
case AST_PBX_FAILED:
ast_log(LOG_WARNING, "Failed to start PBX :(\n");
Olle Johansson
committed
p->invitestate = INV_COMPLETED;
if (req->ignore)
Kevin P. Fleming
committed
transmit_response(p, "503 Unavailable", req);
else
transmit_response_reliable(p, "503 Unavailable", req);
Kevin P. Fleming
committed
break;
case AST_PBX_CALL_LIMIT:
ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
Olle Johansson
committed
p->invitestate = INV_COMPLETED;
if (req->ignore)
Kevin P. Fleming
committed
transmit_response(p, "480 Temporarily Unavailable", req);
else
transmit_response_reliable(p, "480 Temporarily Unavailable", req);
Kevin P. Fleming
committed
break;
case AST_PBX_SUCCESS:
/* nothing to do */
break;
}
if (res) {
/* Unlock locks so ast_hangup can do its magic */
sip_pvt_unlock(p);
sip_pvt_lock(p);
c = NULL;
}
} else { /* Pickup call in call group */
if (ast_pickup_call(c)) {
ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid);
if (req->ignore)
transmit_response(p, "503 Unavailable", req); /* OEJ - Right answer? */
transmit_response_reliable(p, "503 Unavailable", req);
/* Unlock locks so ast_hangup can do its magic */
sip_pvt_unlock(p);
c->hangupcause = AST_CAUSE_CALL_REJECTED;
sip_pvt_unlock(p);
ast_setstate(c, AST_STATE_DOWN);
c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
Olle Johansson
committed
p->invitestate = INV_COMPLETED;
ast_hangup(c);
sip_pvt_lock(p);
c = NULL;
}
break;
case AST_STATE_RING:
transmit_response(p, "100 Trying", req);
Olle Johansson
committed
p->invitestate = INV_PROCEEDING;
break;
case AST_STATE_RINGING:
transmit_response(p, "180 Ringing", req);
Olle Johansson
committed
p->invitestate = INV_PROCEEDING;
break;
case AST_STATE_UP:
ast_debug(2, "%s: This call is UP.... \n", c->name);
Olle Johansson
committed
transmit_response(p, "100 Trying", req);
if (p->t38.state == T38_PEER_REINVITE) {
struct ast_channel *bridgepeer = NULL;
struct sip_pvt *bridgepvt = NULL;
if ((bridgepeer = ast_bridged_channel(p->owner))) {
/* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/
/*! XXX: we should also check here does the other side supports t38 at all !!! XXX */
if (IS_SIP_TECH(bridgepeer->tech)) {
bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
if (bridgepvt->t38.state == T38_DISABLED) {
if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */
/* Send re-invite to the bridged channel */
sip_handle_t38_reinvite(bridgepeer, p, 1);
} else { /* Something is wrong with peers udptl struct */
ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n");
sip_pvt_lock(bridgepvt);
bridgepvt->t38.state = T38_DISABLED;
sip_pvt_unlock(bridgepvt);
ast_debug(2,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->name);
if (req->ignore)
transmit_response(p, "488 Not acceptable here", req);
else
transmit_response_reliable(p, "488 Not acceptable here", req);
} else {
/* The other side is already setup for T.38 most likely so we need to acknowledge this too */
transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL);
p->t38.state = T38_ENABLED;
ast_debug(1, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
}
} else {
/* Other side is not a SIP channel */
if (req->ignore)
transmit_response(p, "488 Not acceptable here", req);
else
transmit_response_reliable(p, "488 Not acceptable here", req);
p->t38.state = T38_DISABLED;
ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
if (!p->lastinvite) /* Only destroy if this is *not* a re-invite */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
}
} else {
/* we are not bridged in a call */
transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL);
p->t38.state = T38_ENABLED;
ast_debug(1,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
}
} else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */
Olle Johansson
committed
int sendok = TRUE;
/* If we are bridged to a channel that has T38 enabled than this is a case of RTP re-invite after T38 session */
/* so handle it here (re-invite other party to RTP) */
struct ast_channel *bridgepeer = NULL;
struct sip_pvt *bridgepvt = NULL;
if ((bridgepeer = ast_bridged_channel(p->owner))) {
if (IS_SIP_TECH(bridgepeer->tech)) {
bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
Olle Johansson
committed
/* Does the bridged peer have T38 ? */
if (bridgepvt->t38.state == T38_ENABLED) {
ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
/* Insted of this we should somehow re-invite the other side of the bridge to RTP */
if (req->ignore)
transmit_response(p, "488 Not Acceptable Here (unsupported)", req);
else
transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req);
Olle Johansson
committed
sendok = FALSE;
}
/* No bridged peer with T38 enabled*/
Olle Johansson
committed
}
/* Respond to normal re-invite */
Olle Johansson
committed
if (sendok)
/* If this is not a re-invite or something to ignore - it's critical */
transmit_response_with_sdp(p, "200 OK", req, (reinvite || req->ignore) ? XMIT_UNRELIABLE : XMIT_CRITICAL);
Olle Johansson
committed
p->invitestate = INV_TERMINATED;
break;
default:
ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
transmit_response(p, "100 Trying", req);
break;
if (p && (p->autokillid == -1)) {
const char *msg;
if (!p->jointcapability)
msg = "488 Not Acceptable Here (codec error)";
else {
ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
if (req->ignore)
transmit_response(p, msg, req);
else
transmit_response_reliable(p, msg, req);
Olle Johansson
committed
p->invitestate = INV_COMPLETED;
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
}
}
return res;
}
/*! \brief Find all call legs and bridge transferee with target
* called from handle_request_refer */
static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno)
15574
15575
15576
15577
15578
15579
15580
15581
15582
15583
15584
15585
15586
15587
15588
15589
15590
15591
15592
15593
15594
{
struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
/* Chan 2: Call from Asterisk to target */
int res = 0;
struct sip_pvt *targetcall_pvt;
/* Check if the call ID of the replaces header does exist locally */
if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
transferer->refer->replaces_callid_fromtag))) {
if (transferer->refer->localtransfer) {
/* We did not find the refered call. Sorry, can't accept then */
transmit_response(transferer, "202 Accepted", req);
/* Let's fake a response from someone else in order
to follow the standard */
transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
append_history(transferer, "Xfer", "Refer failed");
ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
transferer->refer->status = REFER_FAILED;
return -1;
}
/* Fall through for remote transfers that we did not find locally */
ast_debug(3, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
return 0;
}
/* Ok, we can accept this transfer */
transmit_response(transferer, "202 Accepted", req);
append_history(transferer, "Xfer", "Refer accepted");
if (!targetcall_pvt->owner) { /* No active channel */
ast_debug(4, "SIP attended transfer: Error: No owner of target call\n");
transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
append_history(transferer, "Xfer", "Refer failed");
ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
transferer->refer->status = REFER_FAILED;
sip_pvt_unlock(targetcall_pvt);
ast_channel_unlock(current->chan1);
return -1;
}
/* We have a channel, find the bridge */
target.chan1 = targetcall_pvt->owner; /* Transferer to Asterisk */
target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */
if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
/* Wrong state of new channel */
if (target.chan2)
ast_debug(4, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
else if (target.chan1->_state != AST_STATE_RING)
ast_debug(4, "SIP attended transfer: Error: No target channel\n");
else
ast_debug(4, "SIP attended transfer: Attempting transfer in ringing state\n");
/* Transfer */
if (sipdebug) {
if (current->chan2) /* We have two bridges */
ast_debug(4, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
else /* One bridge, propably transfer of IVR/voicemail etc */
ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
}
ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
/* Perform the transfer */
Olle Johansson
committed
manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
transferer->owner->name,
transferer->owner->uniqueid,
transferer->callid,
target.chan1->name,
target.chan1->uniqueid);
res = attempt_transfer(current, &target);
sip_pvt_unlock(targetcall_pvt);
if (res) {
/* Failed transfer */
transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
append_history(transferer, "Xfer", "Refer failed");
transferer->refer->status = REFER_FAILED;
if (targetcall_pvt->owner)
ast_channel_unlock(targetcall_pvt->owner);
/* Right now, we have to hangup, sorry. Bridge is destroyed */
if (res != -2)
ast_hangup(transferer->owner);
else
ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
} else {
/* Transfer succeeded! */
/* Tell transferer that we're done. */
transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
append_history(transferer, "Xfer", "Refer succeeded");
transferer->refer->status = REFER_200OK;
if (targetcall_pvt->owner) {
ast_debug(1, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
ast_channel_unlock(targetcall_pvt->owner);
}
}
return 1;
}
/*! \brief Handle incoming REFER request */
15676
15677
15678
15679
15680
15681
15682
15683
15684
15685
15686
15687
15688
15689
15690
15691
15692
15693
15694
15695
15696
15697
15698
15699
15700
15701
15702
15703
15704
15705
15706
15707
15708
15709
15710
15711
15712
15713
15714
15715
15716
15717
15718
15719
15720
15721
15722
15723
15724
15725
15726
15727
15728
15729
15730
15731
15732
15733
15734
15735
15736
15737
/*! \page SIP_REFER SIP transfer Support (REFER)
REFER is used for call transfer in SIP. We get a REFER
to place a new call with an INVITE somwhere and then
keep the transferor up-to-date of the transfer. If the
transfer fails, get back on line with the orginal call.
- REFER can be sent outside or inside of a dialog.
Asterisk only accepts REFER inside of a dialog.
- If we get a replaces header, it is an attended transfer
\par Blind transfers
The transferor provides the transferee
with the transfer targets contact. The signalling between
transferer or transferee should not be cancelled, so the
call is recoverable if the transfer target can not be reached
by the transferee.
In this case, Asterisk receives a TRANSFER from
the transferor, thus is the transferee. We should
try to set up a call to the contact provided
and if that fails, re-connect the current session.
If the new call is set up, we issue a hangup.
In this scenario, we are following section 5.2
in the SIP CC Transfer draft. (Transfer without
a GRUU)
\par Transfer with consultation hold
In this case, the transferor
talks to the transfer target before the transfer takes place.
This is implemented with SIP hold and transfer.
Note: The invite From: string could indicate a transfer.
(Section 6. Transfer with consultation hold)
The transferor places the transferee on hold, starts a call
with the transfer target to alert them to the impending
transfer, terminates the connection with the target, then
proceeds with the transfer (as in Blind transfer above)
\par Attended transfer
The transferor places the transferee
on hold, calls the transfer target to alert them,
places the target on hold, then proceeds with the transfer
using a Replaces header field in the Refer-to header. This
will force the transfee to send an Invite to the target,
with a replaces header that instructs the target to
hangup the call between the transferor and the target.
In this case, the Refer/to: uses the AOR address. (The same
URI that the transferee used to establish the session with
the transfer target (To: ). The Require: replaces header should
be in the INVITE to avoid the wrong UA in a forked SIP proxy
scenario to answer and have no call to replace with.
The referred-by header is *NOT* required, but if we get it,
can be copied into the INVITE to the transfer target to
inform the target about the transferor
"Any REFER request has to be appropriately authenticated.".
We can't destroy dialogs, since we want the call to continue.
*/
static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock)
struct sip_dual current; /* Chan1: Call between asterisk and transferer */
/* Chan2: Call between asterisk and transferee */
int res = 0;
if (req->debug)
ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
if (!p->owner) {
/* This is a REFER outside of an existing SIP dialog */
/* We can't handle that, so decline it */
ast_debug(3, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
transmit_response(p, "603 Declined (No dialog)", req);
if (!req->ignore) {
append_history(p, "Xfer", "Refer failed. Outside of dialog.");
p->needdestroy = 1;
}
return 0;
}
/* Check if transfer is allowed from this device */
if (p->allowtransfer == TRANSFER_CLOSED ) {
/* Transfer not allowed, decline */
transmit_response(p, "603 Declined (policy)", req);
append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
/* Do not destroy SIP session */
return 0;
}
if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
15772
15773
15774
15775
15776
15777
15778
15779
15780
15781
15782
15783
15784
15785
15786
15787
15788
15789
15790
15791
15792
15793
/* Already have a pending REFER */
transmit_response(p, "491 Request pending", req);
append_history(p, "Xfer", "Refer failed. Request pending.");
return 0;
}
/* Allocate memory for call transfer data */
if (!p->refer && !sip_refer_allocate(p)) {
transmit_response(p, "500 Internal Server Error", req);
append_history(p, "Xfer", "Refer failed. Memory allocation error.");
return -3;
}
res = get_refer_info(p, req); /* Extract headers */
p->refer->status = REFER_SENT;
if (res != 0) {
switch (res) {
case -2: /* Syntax error */
transmit_response(p, "400 Bad Request (Refer-to missing)", req);
append_history(p, "Xfer", "Refer failed. Refer-to missing.");
if (req->debug)
ast_debug(1, "SIP transfer to black hole can't be handled (no refer-to: )\n");
break;
case -3:
transmit_response(p, "603 Declined (Non sip: uri)", req);
append_history(p, "Xfer", "Refer failed. Non SIP uri");
if (req->debug)
ast_debug(1, "SIP transfer to non-SIP uri denied\n");
break;
default:
/* Refer-to extension not found, fake a failed transfer */
transmit_response(p, "202 Accepted", req);
append_history(p, "Xfer", "Refer failed. Bad extension.");
transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
if (req->debug)
ast_debug(1, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
break;
}
return 0;
}
if (ast_strlen_zero(p->context))
ast_string_field_set(p, context, default_context);
/* If we do not support SIP domains, all transfers are local */
if (allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
p->refer->localtransfer = 1;
if (sipdebug)
ast_debug(3, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
} else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
/* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */
p->refer->localtransfer = 1;
ast_debug(3, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
/* Is this a repeat of a current request? Ignore it */
/* Don't know what else to do right now. */
if (req->ignore)
return res;
/* 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.
*/
/* Get the transferer's channel */
current.chan1 = p->owner;
/* Find the other part of the bridge (2) - transferee */
current.chan2 = ast_bridged_channel(current.chan1);
if (sipdebug)
ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<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);
return -1;
}
Olle Johansson
committed
if (current.chan2) {
if (sipdebug)
ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
Kevin P. Fleming
committed
ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
Olle Johansson
committed
}
ast_set_flag(&p->flags[0], SIP_GOTREFER);
/* Attended transfer: Find all call legs and bridge transferee with target*/
if (p->refer->attendedtransfer) {
if ((res = local_attended_transfer(p, ¤t, req, seqno)))
return res; /* We're done with the transfer */
/* Fall through for remote transfers that we did not find locally */
if (sipdebug)
ast_debug(4, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
/* Fallthrough if we can't find the call leg internally */
}
/* Parking a call */
if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
/* Must release c's lock now, because it will not longer be accessible after the transfer! */
*nounlock = 1;
ast_channel_unlock(current.chan1);
copy_request(¤t.req, req);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
p->refer->status = REFER_200OK;
append_history(p, "Xfer", "REFER to call parking.");
Olle Johansson
committed
manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransfer2Parking: Yes\r\n",
current.chan1->name,
current.chan1->uniqueid,
p->callid,
current.chan2->name,
current.chan2->uniqueid,
p->refer->refer_to);
if (sipdebug)
ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
sip_park(current.chan2, current.chan1, req, seqno);
return res;
}
/* Blind transfers and remote attended xfers */
transmit_response(p, "202 Accepted", req);
Olle Johansson
committed
if (current.chan1 && current.chan2) {
ast_debug(3, "chan1->name: %s\n", current.chan1->name);
pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
}
if (current.chan2) {
Olle Johansson
committed
pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->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 (p->refer->referred_by)
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
/* Generate a Replaces string to be used in the INVITE during attended transfer */
if (!ast_strlen_zero(p->refer->replaces_callid)) {
char tempheader[BUFSIZ];
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)
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
}
/* Must release lock now, because it will not longer
*nounlock = 1;
ast_channel_unlock(current.chan1);
/* Connect the call */
Olle Johansson
committed
/* FAKE ringing if not attended transfer */
if (!p->refer->attendedtransfer)
transmit_notify_with_sipfrag(p, seqno, "183 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).");
return -1;
}
ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
Olle Johansson
committed
/* 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 */
res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
if (!res) {
Olle Johansson
committed
manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransferContext: %s\r\n",
current.chan1->name,
current.chan1->uniqueid,
p->callid,
current.chan2->name,
current.chan2->uniqueid,
p->refer->refer_to, p->refer->refer_to_context);
/* Success - we have a new channel */
ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
if (p->refer->localtransfer)
p->refer->status = REFER_200OK;
if (p->owner)
p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;