mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-29 15:36:13 +00:00 
			
		
		
		
	Add attended transfer support for chan_sip.c
This now uses the core API for performing attended transfers. Review https://reviewboard.asterisk.org/r/2513 (Closes issue ASTERISK-21520) reported by Matt Jordan git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389869 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -1200,7 +1200,8 @@ static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_reque | ||||
| static int copy_route(struct sip_route **dst, const struct sip_route *src); | ||||
| static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr, | ||||
| 					      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 int get_sip_pvt_from_replaces(const char *callid, const char *totag, const char *fromtag, | ||||
| 		struct sip_pvt **out_pvt, struct ast_channel **out_chan); | ||||
| static void check_pendings(struct sip_pvt *p); | ||||
| 
 | ||||
| static void *sip_pickup_thread(void *stuff); | ||||
| @@ -1271,8 +1272,6 @@ static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt); | ||||
| /* static int sip_addrcmp(char *name, struct sockaddr_in *sin);	Support for peer matching */ | ||||
| static int sip_refer_alloc(struct sip_pvt *p); | ||||
| static int sip_notify_alloc(struct sip_pvt *p); | ||||
| static void ast_quiet_chan(struct ast_channel *chan); | ||||
| static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); | ||||
| static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context); | ||||
| static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer); | ||||
| static void check_for_nat(const struct ast_sockaddr *them, struct sip_pvt *p); | ||||
| @@ -1475,9 +1474,10 @@ static int handle_request_message(struct sip_pvt *p, struct sip_request *req, st | ||||
| static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e); | ||||
| static void handle_request_info(struct sip_pvt *p, struct sip_request *req); | ||||
| static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e); | ||||
| static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *nounlock); | ||||
| static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, | ||||
| 		int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan); | ||||
| static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e); | ||||
| static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, uint32_t seqno, int *nounlock); | ||||
| static int local_attended_transfer(struct sip_pvt *transferer, struct ast_channel *transferer_chan, uint32_t seqno, int *nounlock); | ||||
| 
 | ||||
| /*------Response handling functions */ | ||||
| static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno); | ||||
| @@ -6654,9 +6654,6 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) | ||||
| 		p->udptl = NULL; | ||||
| 	} | ||||
| 	if (p->refer) { | ||||
| 		if (p->refer->refer_call) { | ||||
| 			p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call"); | ||||
| 		} | ||||
| 		ast_string_field_free_memory(p->refer); | ||||
| 		ast_free(p->refer); | ||||
| 		p->refer = NULL; | ||||
| @@ -17811,10 +17808,25 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re | ||||
| 	return SIP_GET_DEST_EXTEN_NOT_FOUND; | ||||
| } | ||||
| 
 | ||||
| /*! \brief Lock dialog lock and find matching pvt lock
 | ||||
| 	\return a reference, remember to release it when done | ||||
| */ | ||||
| static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag) | ||||
| /*! \brief Find a companion dialog based on Replaces information
 | ||||
|  * | ||||
|  * This information may come from a Refer-To header in a REFER or from | ||||
|  * a Replaces header in an INVITE. | ||||
|  * | ||||
|  * This function will find the appropriate sip_pvt and increment the refcount | ||||
|  * of both the sip_pvt and its owner channel. These two references are returned | ||||
|  * in the out parameters | ||||
|  * | ||||
|  * \param callid Callid to search for | ||||
|  * \param totag to-tag parameter from Replaces | ||||
|  * \param fromtag from-tag parameter from Replaces | ||||
|  * \param[out] out_pvt The found sip_pvt. | ||||
|  * \param[out] out_chan The found sip_pvt's owner channel. | ||||
|  * \retval 0 Success | ||||
|  * \retval non-zero Failure | ||||
|  */ | ||||
| static int get_sip_pvt_from_replaces(const char *callid, const char *totag, | ||||
| 		const char *fromtag, struct sip_pvt **out_pvt, struct ast_channel **out_chan) | ||||
| { | ||||
| 	struct sip_pvt *sip_pvt_ptr; | ||||
| 	struct sip_pvt tmp_dialog = { | ||||
| @@ -17830,22 +17842,20 @@ static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *t | ||||
| 	sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find of dialog in dialogs table"); | ||||
| 	if (sip_pvt_ptr) { | ||||
| 		/* Go ahead and lock it (and its owner) before returning */ | ||||
| 		sip_pvt_lock(sip_pvt_ptr); | ||||
| 		SCOPED_LOCK(lock, sip_pvt_ptr, sip_pvt_lock, sip_pvt_unlock); | ||||
| 		if (sip_cfg.pedanticsipchecking) { | ||||
| 			unsigned char frommismatch = 0, tomismatch = 0; | ||||
| 
 | ||||
| 			if (ast_strlen_zero(fromtag)) { | ||||
| 				sip_pvt_unlock(sip_pvt_ptr); | ||||
| 				ast_debug(4, "Matched %s call for callid=%s - no from tag specified, pedantic check fails\n", | ||||
| 					  sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid); | ||||
| 				return NULL; | ||||
| 				return -1; | ||||
| 			} | ||||
| 
 | ||||
| 			if (ast_strlen_zero(totag)) { | ||||
| 				sip_pvt_unlock(sip_pvt_ptr); | ||||
| 				ast_debug(4, "Matched %s call for callid=%s - no to tag specified, pedantic check fails\n", | ||||
| 					  sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid); | ||||
| 				return NULL; | ||||
| 				return -1; | ||||
| 			} | ||||
| 			/* RFC 3891
 | ||||
| 			 * > 3.  User Agent Server Behavior: Receiving a Replaces Header | ||||
| @@ -17868,7 +17878,6 @@ static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *t | ||||
| 			 * can change when the call is not answered yet. | ||||
| 			 */ | ||||
| 			if ((frommismatch && ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) || tomismatch) { | ||||
| 				sip_pvt_unlock(sip_pvt_ptr); | ||||
| 				if (frommismatch) { | ||||
| 					ast_debug(4, "Matched %s call for callid=%s - pedantic from tag check fails; their tag is %s our tag is %s\n", | ||||
| 						  sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid, | ||||
| @@ -17879,7 +17888,7 @@ static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *t | ||||
| 						  sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid, | ||||
| 						  totag, sip_pvt_ptr->tag); | ||||
| 				} | ||||
| 				return NULL; | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @@ -17888,15 +17897,13 @@ static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *t | ||||
| 					  sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", | ||||
| 					  sip_pvt_ptr->theirtag, sip_pvt_ptr->tag); | ||||
| 
 | ||||
| 		/* deadlock avoidance... */ | ||||
| 		while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) { | ||||
| 			sip_pvt_unlock(sip_pvt_ptr); | ||||
| 			usleep(1); | ||||
| 			sip_pvt_lock(sip_pvt_ptr); | ||||
| 		*out_pvt = sip_pvt_ptr; | ||||
| 		if (out_chan) { | ||||
| 			*out_chan = sip_pvt_ptr->owner ? ast_channel_ref(sip_pvt_ptr->owner) : NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return sip_pvt_ptr; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*! \brief Call transfer support (the REFER method)
 | ||||
| @@ -24451,90 +24458,6 @@ static int sip_pickup(struct ast_channel *chan) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*! \brief Turn off generator data
 | ||||
| 	XXX Does this function belong in the SIP channel? | ||||
| */ | ||||
| static void ast_quiet_chan(struct ast_channel *chan) | ||||
| { | ||||
| 	if (chan && ast_channel_state(chan) == AST_STATE_UP) { | ||||
| 		if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH)) | ||||
| 			ast_moh_stop(chan); | ||||
| 		else if (ast_channel_generatordata(chan)) | ||||
| 			ast_deactivate_generator(chan); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*! \brief Attempt transfer of SIP call
 | ||||
| 	This fix for attended transfers on a local PBX */ | ||||
| static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target) | ||||
| { | ||||
| 	int res = 0; | ||||
| 	struct ast_channel *peera = NULL, | ||||
| 		*peerb = NULL, | ||||
| 		*peerc = NULL, | ||||
| 		*peerd = NULL; | ||||
| 
 | ||||
| 
 | ||||
| 	/* We will try to connect the transferee with the target and hangup
 | ||||
| 	   all channels to the transferer */ | ||||
| 	ast_debug(4, "Sip transfer:--------------------\n"); | ||||
| 	if (transferer->chan1) | ||||
| 		ast_debug(4, "-- Transferer to PBX channel: %s State %s\n", ast_channel_name(transferer->chan1), ast_state2str(ast_channel_state(transferer->chan1))); | ||||
| 	else | ||||
| 		ast_debug(4, "-- No transferer first channel - odd??? \n"); | ||||
| 	if (target->chan1) | ||||
| 		ast_debug(4, "-- Transferer to PBX second channel (target): %s State %s\n", ast_channel_name(target->chan1), ast_state2str(ast_channel_state(target->chan1))); | ||||
| 	else | ||||
| 		ast_debug(4, "-- No target first channel ---\n"); | ||||
| 	if (transferer->chan2) | ||||
| 		ast_debug(4, "-- Bridged call to transferee: %s State %s\n", ast_channel_name(transferer->chan2), ast_state2str(ast_channel_state(transferer->chan2))); | ||||
| 	else | ||||
| 		ast_debug(4, "-- No bridged call to transferee\n"); | ||||
| 	if (target->chan2) | ||||
| 		ast_debug(4, "-- Bridged call to transfer target: %s State %s\n", target->chan2 ? ast_channel_name(target->chan2) : "<none>", target->chan2 ? ast_state2str(ast_channel_state(target->chan2)) : "(none)"); | ||||
| 	else | ||||
| 		ast_debug(4, "-- No target second channel ---\n"); | ||||
| 	ast_debug(4, "-- END Sip transfer:--------------------\n"); | ||||
| 	if (transferer->chan2) { /* We have a bridge on the transferer's channel */ | ||||
| 		peera = transferer->chan1;	/* Transferer - PBX -> transferee channel * the one we hangup */ | ||||
| 		peerb = target->chan1;		/* Transferer - PBX -> target channel - This will get lost in masq */ | ||||
| 		peerc = transferer->chan2;	/* Asterisk to Transferee */ | ||||
| 		peerd = target->chan2;		/* Asterisk to Target */ | ||||
| 		ast_debug(3, "SIP transfer: Four channels to handle\n"); | ||||
| 	} else if (target->chan2) {	/* Transferer has no bridge (IVR), but transferee */ | ||||
| 		peera = target->chan1;		/* Transferer to PBX -> target channel */ | ||||
| 		peerb = transferer->chan1;	/* Transferer to IVR*/ | ||||
| 		peerc = target->chan2;		/* Asterisk to Target */ | ||||
| 		peerd = transferer->chan2;	/* Nothing */ | ||||
| 		ast_debug(3, "SIP transfer: Three channels to handle\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	if (peera && peerb && peerc && (peerb != peerc)) { | ||||
| 		ast_quiet_chan(peera);		/* Stop generators */ | ||||
| 		ast_quiet_chan(peerb); | ||||
| 		ast_quiet_chan(peerc); | ||||
| 		if (peerd) | ||||
| 			ast_quiet_chan(peerd); | ||||
| 
 | ||||
| 		ast_debug(4, "SIP transfer: trying to masquerade %s into %s\n", ast_channel_name(peerc), ast_channel_name(peerb)); | ||||
| 		if (ast_channel_masquerade(peerb, peerc)) { | ||||
| 			ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", ast_channel_name(peerb), ast_channel_name(peerc)); | ||||
| 			res = -1; | ||||
| 		} else | ||||
| 			ast_debug(4, "SIP transfer: Succeeded to masquerade channels.\n"); | ||||
| 		return res; | ||||
| 	} else { | ||||
| 		ast_log(LOG_NOTICE, "SIP Transfer attempted with no appropriate bridged calls to transfer\n"); | ||||
| 		if (transferer->chan1) | ||||
| 			ast_softhangup_nolock(transferer->chan1, AST_SOFTHANGUP_DEV); | ||||
| 		if (target->chan1) | ||||
| 			ast_softhangup_nolock(target->chan1, AST_SOFTHANGUP_DEV); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*! \brief Get tag from packet
 | ||||
|  * | ||||
|  * \return Returns the pointer to the provided tag buffer, | ||||
| @@ -24850,132 +24773,68 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st | ||||
| } | ||||
| 
 | ||||
| /*! \brief Handle the transfer part of INVITE with a replaces: header,
 | ||||
|     meaning a target pickup or an attended transfer. | ||||
|     Used only once. | ||||
| 	XXX 'ignore' is unused. | ||||
| 
 | ||||
| 	\note this function is called by handle_request_invite(). Four locks | ||||
| 	held at the beginning of this function, p, p->owner, p->refer->refer_call and | ||||
| 	p->refere->refer_call->owner.  only p's lock should remain at the end of this | ||||
| 	function.  p's lock as well as the channel p->owner's lock are held by | ||||
| 	handle_request_do(), we unlock p->owner before the masq.  By setting nounlock | ||||
| 	we are indicating to handle_request_do() that we have already unlocked the owner. | ||||
|  * | ||||
|  * This is used for call-pickup and for attended transfers initiated on | ||||
|  * remote endpoints (i.e. a REFER received on a remote server). | ||||
|  * | ||||
|  * \note p and p->owner are locked upon entering this function. If the | ||||
|  * call pickup or attended transfer is successful, then p->owner will | ||||
|  * be unlocked upon exiting this function. This is communicated to the | ||||
|  * caller through the nounlock parameter. | ||||
|  * | ||||
|  * \param p The sip_pvt where the INVITE with Replaces was received | ||||
|  * \param req The incoming INVITE | ||||
|  * \param[out] nounlock Indicator if p->owner should remained locked. On successful transfer, this will be set true. | ||||
|  * \param replaces_pvt sip_pvt referenced by Replaces header | ||||
|  * \param replaces_chan replaces_pvt's owner channel | ||||
|  * \retval 0 Success | ||||
|  * \retval non-zero Failure | ||||
|  */ | ||||
| static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *nounlock) | ||||
| static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, | ||||
| 		int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan) | ||||
| { | ||||
| 	int earlyreplace = 0; | ||||
| 	int oneleggedreplace = 0;		/* Call with no bridge, propably IVR or voice message */ | ||||
| 	struct ast_channel *c = p->owner;	/* Our incoming call */ | ||||
| 	struct ast_channel *replacecall = p->refer->refer_call->owner;	/* The channel we're about to take over */ | ||||
| 	struct ast_channel *targetcall;		/* The bridge to the take-over target */ | ||||
| 
 | ||||
| 	/* Check if we're in ring state */ | ||||
| 	if (ast_channel_state(replacecall) == AST_STATE_RING) | ||||
| 		earlyreplace = 1; | ||||
| 
 | ||||
| 	/* Check if we have a bridge */ | ||||
| 	if (!(targetcall = ast_bridged_channel(replacecall))) { | ||||
| 		/* We have no bridge */ | ||||
| 		if (!earlyreplace) { | ||||
| 			ast_debug(2, "	Attended transfer attempted to replace call with no bridge (maybe ringing). Channel %s!\n", ast_channel_name(replacecall)); | ||||
| 			oneleggedreplace = 1; | ||||
| 		} | ||||
| 	} | ||||
| 	if (targetcall && ast_channel_state(targetcall) == AST_STATE_RINGING) | ||||
| 		ast_debug(4, "SIP transfer: Target channel is in ringing state\n"); | ||||
| 
 | ||||
| 	if (targetcall) | ||||
| 		ast_debug(4, "SIP transfer: Invite Replace incoming channel should bridge to channel %s while hanging up channel %s\n", ast_channel_name(targetcall), ast_channel_name(replacecall)); | ||||
| 	else | ||||
| 		ast_debug(4, "SIP transfer: Invite Replace incoming channel should replace and hang up channel %s (one call leg)\n", ast_channel_name(replacecall)); | ||||
| 	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); | ||||
| 	RAII_VAR(struct ast_channel *, c, NULL, ao2_cleanup); | ||||
| 
 | ||||
| 	if (req->ignore) { | ||||
| 		ast_log(LOG_NOTICE, "Ignoring this INVITE with replaces in a stupid way.\n"); | ||||
| 		/* We should answer something here. If we are here, the
 | ||||
| 			call we are replacing exists, so an accepted | ||||
| 			can't harm */ | ||||
| 		transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); | ||||
| 		/* Do something more clever here */ | ||||
| 		if (c) { | ||||
| 			*nounlock = 1; | ||||
| 			ast_channel_unlock(c); | ||||
| 		return 0; | ||||
| 	} | ||||
| 		ast_channel_unlock(replacecall); | ||||
| 		sip_pvt_unlock(p->refer->refer_call); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	if (!c) { | ||||
| 
 | ||||
| 	if (!p->owner) { | ||||
| 		/* What to do if no channel ??? */ | ||||
| 		ast_log(LOG_ERROR, "Unable to create new channel.  Invite/replace failed.\n"); | ||||
| 		transmit_response_reliable(p, "503 Service Unavailable", req); | ||||
| 		append_history(p, "Xfer", "INVITE/Replace Failed. No new channel."); | ||||
| 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); | ||||
| 		ast_channel_unlock(replacecall); | ||||
| 		sip_pvt_unlock(p->refer->refer_call); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	append_history(p, "Xfer", "INVITE/Replace received"); | ||||
| 	/* We have three channels to play with
 | ||||
| 		channel c: New incoming call | ||||
| 		targetcall: Call from PBX to target | ||||
| 		p->refer->refer_call: SIP pvt dialog from transferer to pbx. | ||||
| 		replacecall: The owner of the previous | ||||
| 		We need to masq C into refer_call to connect to | ||||
| 		targetcall; | ||||
| 		If we are talking to internal audio stream, target call is null. | ||||
| 	*/ | ||||
| 
 | ||||
| 	c = ast_channel_ref(p->owner); | ||||
| 
 | ||||
| 	/* Fake call progress */ | ||||
| 	transmit_response(p, "100 Trying", req); | ||||
| 	ast_setstate(c, AST_STATE_RING); | ||||
| 
 | ||||
| 	/* Masquerade the new call into the referred call to connect to target call
 | ||||
| 	   Targetcall is not touched by the masq */ | ||||
| 	ast_debug(4, "Invite/Replaces: preparing to replace %s with %s\n", ast_channel_name(replaces_chan), ast_channel_name(c)); | ||||
| 
 | ||||
| 	/* Answer the incoming call and set channel to UP state */ | ||||
| 	transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); | ||||
| 
 | ||||
| 	ast_setstate(c, AST_STATE_UP); | ||||
| 
 | ||||
| 	/* Stop music on hold and other generators */ | ||||
| 	ast_quiet_chan(replacecall); | ||||
| 	ast_quiet_chan(targetcall); | ||||
| 	ast_debug(4, "Invite/Replaces: preparing to masquerade %s into %s\n", ast_channel_name(c), ast_channel_name(replacecall)); | ||||
| 
 | ||||
| 	/* Make sure that the masq does not free our PVT for the old call */ | ||||
| 	if (! earlyreplace && ! oneleggedreplace ) | ||||
| 		ast_set_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */ | ||||
| 
 | ||||
| 	/* Prepare the masquerade - if this does not happen, we will be gone */ | ||||
| 	if(ast_channel_masquerade(replacecall, c)) | ||||
| 		ast_log(LOG_ERROR, "Failed to masquerade C into Replacecall\n"); | ||||
| 	else | ||||
| 		ast_debug(4, "Invite/Replaces: Going to masquerade %s into %s\n", ast_channel_name(c), ast_channel_name(replacecall)); | ||||
| 
 | ||||
| 	/* C should now be in place of replacecall. all channel locks and pvt locks should be removed
 | ||||
| 	 * before issuing the masq.  Since we are unlocking both the pvt (p) and its owner channel (c) | ||||
| 	 * it is possible for channel c to be destroyed on us.  To prevent this, we must give c a reference | ||||
| 	 * before any unlocking takes place and remove it only once we are completely done with it */ | ||||
| 	ast_channel_ref(c); | ||||
| 	ast_channel_unlock(replacecall); | ||||
| 	ast_channel_unlock(c); | ||||
| 	sip_pvt_unlock(p->refer->refer_call); | ||||
| 	sip_pvt_unlock(p); | ||||
| 	ast_do_masquerade(replacecall); | ||||
| 	ast_channel_lock(c); | ||||
| 	if (earlyreplace || oneleggedreplace ) { | ||||
| 		ast_channel_hangupcause_set(c, AST_CAUSE_SWITCH_CONGESTION); | ||||
| 	} | ||||
| 	ast_setstate(c, AST_STATE_DOWN); | ||||
| 	ast_channel_unlock(c); | ||||
| 
 | ||||
| 	/* c and c's tech pvt must be unlocked at this point for ast_hangup */ | ||||
| 	ast_hangup(c); | ||||
| 	/* this indicates to handle_request_do that the owner channel has already been unlocked */ | ||||
| 	*nounlock = 1; | ||||
| 	/* lock PVT structure again after hangup */ | ||||
| 	ast_channel_unlock(c); | ||||
| 	sip_pvt_unlock(p); | ||||
| 
 | ||||
| 	ast_raw_answer(c, 1); | ||||
| 
 | ||||
| 	ast_channel_lock(replaces_chan); | ||||
| 	bridge = ast_channel_get_bridge(replaces_chan); | ||||
| 	ast_channel_unlock(replaces_chan); | ||||
| 
 | ||||
| 	if (bridge) { | ||||
| 		ast_bridge_impart(bridge, c, replaces_chan, NULL, 1); | ||||
| 	} else { | ||||
| 		ast_channel_move(replaces_chan, c); | ||||
| 		ast_hangup(c); | ||||
| 	} | ||||
| 	sip_pvt_lock(p); | ||||
| 	ast_channel_unref(c); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @@ -25085,7 +24944,6 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 	int gotdest; | ||||
| 	const char *p_replaces; | ||||
| 	char *replace_id = NULL; | ||||
| 	int refer_locked = 0; | ||||
| 	const char *required; | ||||
| 	unsigned int required_profile = 0; | ||||
| 	struct ast_channel *c = NULL;		/* New channel */ | ||||
| @@ -25110,6 +24968,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 	} pickup = { | ||||
| 			.exten = "", | ||||
| 	}; | ||||
| 	RAII_VAR(struct sip_pvt *, replaces_pvt, NULL, ao2_cleanup); | ||||
| 	RAII_VAR(struct ast_channel *, replaces_chan, NULL, ao2_cleanup); | ||||
| 
 | ||||
| 	/* Find out what they support */ | ||||
| 	if (!p->sipoptions) { | ||||
| @@ -25287,45 +25147,41 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 		   First we cheat a little and look for a magic call-id from phones that support | ||||
| 		   dialog-info+xml so we can do technology independent pickup... */ | ||||
| 		if (strncmp(replace_id, "pickup-", 7) == 0) { | ||||
| 			struct sip_pvt *subscription = NULL; | ||||
| 			RAII_VAR(struct sip_pvt *, subscription, NULL, ao2_cleanup); | ||||
| 			RAII_VAR(struct ast_channel *, subscription_chan, NULL, ao2_cleanup); | ||||
| 
 | ||||
| 			replace_id += 7; /* Worst case we are looking at \0 */ | ||||
| 
 | ||||
| 			if ((subscription = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { | ||||
| 			if (get_sip_pvt_from_replaces(replace_id, totag, fromtag, &subscription, &subscription_chan)) { | ||||
| 				ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id); | ||||
| 				transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); | ||||
| 				error = 1; | ||||
| 			} else { | ||||
| 				SCOPED_LOCK(lock, subscription, sip_pvt_lock, sip_pvt_unlock); | ||||
| 				ast_log(LOG_NOTICE, "Trying to pick up %s@%s\n", subscription->exten, subscription->context); | ||||
| 				ast_copy_string(pickup.exten, subscription->exten, sizeof(pickup.exten)); | ||||
| 				ast_copy_string(pickup.context, subscription->context, sizeof(pickup.context)); | ||||
| 				sip_pvt_unlock(subscription); | ||||
| 				if (subscription->owner) { | ||||
| 					ast_channel_unlock(subscription->owner); | ||||
| 				} | ||||
| 				subscription = dialog_unref(subscription, "unref dialog subscription"); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* This locks both refer_call pvt and refer_call pvt's owner!!!*/ | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && get_sip_pvt_from_replaces(replace_id, | ||||
| 					totag, fromtag, &replaces_pvt, &replaces_chan)) { | ||||
| 			ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id); | ||||
| 			transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); | ||||
| 			error = 1; | ||||
| 		} else { | ||||
| 			refer_locked = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* 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) { | ||||
| 		if (replaces_pvt == p) { | ||||
| 			ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid); | ||||
| 			transmit_response_reliable(p, "400 Bad request", req);	/* The best way to not not accept the transfer */ | ||||
| 			error = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && !p->refer->refer_call->owner) { | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && !replaces_chan) { | ||||
| 			/* 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 */ | ||||
| @@ -25333,7 +25189,10 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 			error = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RINGING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_UP) { | ||||
| 		if (!error && ast_strlen_zero(pickup.exten) && | ||||
| 				ast_channel_state(replaces_chan) != AST_STATE_RINGING && | ||||
| 				ast_channel_state(replaces_chan) != AST_STATE_RING && | ||||
| 				ast_channel_state(replaces_chan) != AST_STATE_UP) { | ||||
| 			ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id); | ||||
| 			transmit_response_reliable(p, "603 Declined (Replaces)", req); | ||||
| 			error = 1; | ||||
| @@ -25342,15 +25201,6 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 		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); | ||||
| 				if (p->refer->refer_call->owner) { | ||||
| 					ast_channel_unlock(p->refer->refer_call->owner); | ||||
| 				} | ||||
| 				p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call"); | ||||
| 			} | ||||
| 			refer_locked = 0; | ||||
| 			p->invitestate = INV_COMPLETED; | ||||
| 			res = INV_REQ_ERROR; | ||||
| 			check_via(p, req); | ||||
| @@ -25791,9 +25641,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 		} else { | ||||
| 			/* Go and take over the target call */ | ||||
| 			if (sipdebug) | ||||
| 				ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid); | ||||
| 			res = handle_invite_replaces(p, req, addr, seqno, nounlock); | ||||
| 			refer_locked = 0; | ||||
| 				ast_debug(4, "Sending this call to the invite/replaces handler %s\n", p->callid); | ||||
| 			res = handle_invite_replaces(p, req, nounlock, replaces_pvt, replaces_chan); | ||||
| 			goto request_invite_cleanup; | ||||
| 		} | ||||
| 	} | ||||
| @@ -25932,13 +25781,6 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str | ||||
| 
 | ||||
| request_invite_cleanup: | ||||
| 
 | ||||
| 	if (refer_locked && p->refer && p->refer->refer_call) { | ||||
| 		sip_pvt_unlock(p->refer->refer_call); | ||||
| 		if (p->refer->refer_call->owner) { | ||||
| 			ast_channel_unlock(p->refer->refer_call->owner); | ||||
| 		} | ||||
| 		p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call"); | ||||
| 	} | ||||
| 	if (authpeer) { | ||||
| 		authpeer = sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_invite authpeer"); | ||||
| 	} | ||||
| @@ -26004,24 +25846,17 @@ static void parse_oli(struct sip_request *req, struct ast_channel *chan) | ||||
|  *	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) | ||||
| static int local_attended_transfer(struct sip_pvt *transferer, struct ast_channel *transferer_chan, uint32_t seqno, int *nounlock) | ||||
| { | ||||
| 	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; | ||||
| 	struct ast_party_connected_line connected_to_transferee; | ||||
| 	struct ast_party_connected_line connected_to_target; | ||||
| 	char transferer_linkedid[32]; | ||||
| 	struct ast_channel *chans[2]; | ||||
| 	RAII_VAR(struct sip_pvt *, targetcall_pvt, NULL, ao2_cleanup); | ||||
| 	RAII_VAR(struct ast_channel *, targetcall_chan, NULL, ao2_cleanup); | ||||
| 	enum ast_transfer_result transfer_res; | ||||
| 
 | ||||
| 	/* 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 (get_sip_pvt_from_replaces(transferer->refer->replaces_callid, | ||||
| 				transferer->refer->replaces_callid_totag, | ||||
| 				transferer->refer->replaces_callid_fromtag, | ||||
| 				&targetcall_pvt, &targetcall_chan)) { | ||||
| 		if (transferer->refer->localtransfer) { | ||||
| 			/* We did not find the refered call. Sorry, can't accept then */ | ||||
| 			/* Let's fake a response from someone else in order
 | ||||
| @@ -26037,174 +25872,51 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual * | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Ok, we can accept this transfer */ | ||||
| 	append_history(transferer, "Xfer", "Refer accepted"); | ||||
| 	if (!targetcall_pvt->owner) {	/* No active channel */ | ||||
| 	if (!targetcall_chan) {	/* No active channel */ | ||||
| 		ast_debug(4, "SIP attended transfer: Error: No owner of target call\n"); | ||||
| 		/* Cancel transfer */ | ||||
| 		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); | ||||
| 		if (targetcall_pvt) | ||||
| 			ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We have a channel, find the bridge */ | ||||
| 	target.chan1 = ast_channel_ref(targetcall_pvt->owner);				/* Transferer to Asterisk */ | ||||
| 	target.chan2 = ast_bridged_channel(targetcall_pvt->owner);	/* Asterisk to target */ | ||||
| 	if (target.chan2) { | ||||
| 		ast_channel_ref(target.chan2); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!target.chan2 || !(ast_channel_state(target.chan2) == AST_STATE_UP || ast_channel_state(target.chan2) == 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(ast_channel_state(target.chan2))); | ||||
| 		else if (ast_channel_state(target.chan1) != 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", ast_channel_name(target.chan1), ast_channel_name(current->chan2)); | ||||
| 		else			/* One bridge, propably transfer of IVR/voicemail etc */ | ||||
| 			ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", ast_channel_name(target.chan1), ast_channel_name(current->chan1)); | ||||
| 	} | ||||
| 
 | ||||
| 	ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Delay hangup */ | ||||
| 
 | ||||
| 	ast_copy_string(transferer_linkedid, ast_channel_linkedid(transferer->owner), sizeof(transferer_linkedid)); | ||||
| 	sip_pvt_unlock(transferer); | ||||
| 	ast_channel_unlock(transferer_chan); | ||||
| 	*nounlock = 1; | ||||
| 
 | ||||
| 	/* Perform the transfer */ | ||||
| 	chans[0] = transferer->owner; | ||||
| 	chans[1] = target.chan1; | ||||
| 	ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, | ||||
| 		"TransferMethod: SIP\r\n" | ||||
| 		"TransferType: Attended\r\n" | ||||
| 		"Channel: %s\r\n" | ||||
| 		"Uniqueid: %s\r\n" | ||||
| 		"SIP-Callid: %s\r\n" | ||||
| 		"TargetChannel: %s\r\n" | ||||
| 		"TargetUniqueid: %s\r\n", | ||||
| 		ast_channel_name(transferer->owner), | ||||
| 		ast_channel_uniqueid(transferer->owner), | ||||
| 		transferer->callid, | ||||
| 		ast_channel_name(target.chan1), | ||||
| 		ast_channel_uniqueid(target.chan1)); | ||||
| 	ast_party_connected_line_init(&connected_to_transferee); | ||||
| 	ast_party_connected_line_init(&connected_to_target); | ||||
| 	/* No need to lock current->chan1 here since it was locked in sipsock_read */ | ||||
| 	ast_party_connected_line_copy(&connected_to_transferee, ast_channel_connected(current->chan1)); | ||||
| 	/* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */ | ||||
| 	ast_party_connected_line_copy(&connected_to_target, ast_channel_connected(target.chan1)); | ||||
| 	/* Reset any earlier private connected id representation */ | ||||
| 	ast_party_id_reset(&connected_to_transferee.priv); | ||||
| 	ast_party_id_reset(&connected_to_target.priv); | ||||
| 	connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; | ||||
| 	res = attempt_transfer(current, &target); | ||||
| 	if (res) { | ||||
| 		/* Failed transfer */ | ||||
| 		transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE); | ||||
| 		append_history(transferer, "Xfer", "Refer failed"); | ||||
| 		ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); | ||||
| 		/* if transfer failed, go ahead and unlock targetcall_pvt and it's owner channel */ | ||||
| 		sip_pvt_unlock(targetcall_pvt); | ||||
| 		ast_channel_unlock(target.chan1); | ||||
| 	} else { | ||||
| 		/* Transfer succeeded! */ | ||||
| 		const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND"); | ||||
| 	transfer_res = ast_bridge_transfer_attended(transferer_chan, targetcall_chan); | ||||
| 
 | ||||
| 		/* target.chan1 was locked in get_sip_pvt_byid_locked, do not unlock target.chan1 before this */ | ||||
| 		ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2); | ||||
| 	sip_pvt_lock(transferer); | ||||
| 
 | ||||
| 		/* Tell transferer that we're done. */ | ||||
| 	switch (transfer_res) { | ||||
| 	case AST_BRIDGE_TRANSFER_SUCCESS: | ||||
| 		transferer->refer->status = REFER_200OK; | ||||
| 		transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE); | ||||
| 		append_history(transferer, "Xfer", "Refer succeeded"); | ||||
| 		transferer->refer->status = REFER_200OK; | ||||
| 		if (target.chan2 && !ast_strlen_zero(xfersound) && ast_streamfile(target.chan2, xfersound, ast_channel_language(target.chan2)) >= 0) { | ||||
| 			ast_waitstream(target.chan2, ""); | ||||
| 		return 1; | ||||
| 	case AST_BRIDGE_TRANSFER_FAIL: | ||||
| 		transferer->refer->status = REFER_FAILED; | ||||
| 		transmit_notify_with_sipfrag(transferer, seqno, "500 Internal Server Error", TRUE); | ||||
| 		append_history(transferer, "Xfer", "Refer failed (internal error)"); | ||||
| 		return -1; | ||||
| 	case AST_BRIDGE_TRANSFER_INVALID: | ||||
| 		transferer->refer->status = REFER_FAILED; | ||||
| 		transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE); | ||||
| 		append_history(transferer, "Xfer", "Refer failed (invalid bridge state)"); | ||||
| 		return -1; | ||||
| 	case AST_BRIDGE_TRANSFER_NOT_PERMITTED: | ||||
| 		transferer->refer->status = REFER_FAILED; | ||||
| 		transmit_notify_with_sipfrag(transferer, seqno, "403 Forbidden", TRUE); | ||||
| 		append_history(transferer, "Xfer", "Refer failed (operation not permitted)"); | ||||
| 		return -1; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 		/* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
 | ||||
| 		 * can queue connected line updates where they need to go. | ||||
| 		 * | ||||
| 		 * before a masquerade, all channel and pvt locks must be unlocked.  Any recursive | ||||
| 		 * channel locks held before this function invalidates channel container locking order. | ||||
| 		 * Since we are unlocking both the pvt (transferer) and its owner channel (current.chan1) | ||||
| 		 * it is possible for current.chan1 to be destroyed in the pbx thread.  To prevent this | ||||
| 		 * we must give c a reference before any unlocking takes place. | ||||
| 		 */ | ||||
| 
 | ||||
| 		ast_channel_ref(current->chan1); | ||||
| 		ast_channel_unlock(current->chan1); /* current.chan1 is p->owner before the masq, it was locked by socket_read()*/ | ||||
| 		ast_channel_unlock(target.chan1); | ||||
| 		*nounlock = 1;  /* we just unlocked the dialog's channel and have no plans of locking it again. */ | ||||
| 		sip_pvt_unlock(targetcall_pvt); | ||||
| 		sip_pvt_unlock(transferer); | ||||
| 
 | ||||
| 		ast_do_masquerade(target.chan1); | ||||
| 
 | ||||
| 		ast_indicate(target.chan1, AST_CONTROL_UNHOLD); | ||||
| 		if (target.chan2) { | ||||
| 			ast_indicate(target.chan2, AST_CONTROL_UNHOLD); | ||||
| 		} | ||||
| 
 | ||||
| 		if (current->chan2 && ast_channel_state(current->chan2) == AST_STATE_RING) { | ||||
| 			ast_indicate(target.chan1, AST_CONTROL_RINGING); | ||||
| 		} | ||||
| 
 | ||||
| 		if (target.chan2) { | ||||
| 			ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee, NULL); | ||||
| 			ast_channel_queue_connected_line_update(target.chan2, &connected_to_target, NULL); | ||||
| 		} else { | ||||
| 			/* Since target.chan1 isn't actually connected to another channel, there is no way for us
 | ||||
| 			 * to queue a frame so that its connected line status will be updated. | ||||
| 			 * | ||||
| 			 * Instead, we use the somewhat hackish approach of using a special control frame type that | ||||
| 			 * instructs ast_read to perform a specific action. In this case, the frame we queue tells | ||||
| 			 * ast_read to call the connected line interception macro configured for target.chan1. | ||||
| 			 */ | ||||
| 			struct ast_control_read_action_payload *frame_payload; | ||||
| 			int payload_size; | ||||
| 			int frame_size; | ||||
| 			unsigned char connected_line_data[1024]; | ||||
| 			payload_size = ast_connected_line_build_data(connected_line_data, | ||||
| 				sizeof(connected_line_data), &connected_to_target, NULL); | ||||
| 			frame_size = payload_size + sizeof(*frame_payload); | ||||
| 			if (payload_size != -1) { | ||||
| 				frame_payload = ast_alloca(frame_size); | ||||
| 				frame_payload->payload_size = payload_size; | ||||
| 				memcpy(frame_payload->payload, connected_line_data, payload_size); | ||||
| 				frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO; | ||||
| 				ast_queue_control_data(target.chan1, AST_CONTROL_READ_ACTION, frame_payload, frame_size); | ||||
| 			} | ||||
| 			/* In addition to queueing the read action frame so that target.chan1's connected line info
 | ||||
| 			 * will be updated, we also are going to queue a plain old connected line update on target.chan1. This | ||||
| 			 * way, either Dial or Queue can apply this connected line update to the outgoing ringing channel. | ||||
| 			 */ | ||||
| 			ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee, NULL); | ||||
| 
 | ||||
| 		} | ||||
| 		sip_pvt_lock(transferer); /* the transferer pvt is expected to remain locked on return */ | ||||
| 
 | ||||
| 		ast_channel_unref(current->chan1); | ||||
| 	} | ||||
| 
 | ||||
| 	/* at this point if the transfer is successful only the transferer pvt should be locked. */ | ||||
| 	ast_party_connected_line_free(&connected_to_target); | ||||
| 	ast_party_connected_line_free(&connected_to_transferee); | ||||
| 	ast_channel_unref(target.chan1); | ||||
| 	if (target.chan2) { | ||||
| 		ast_channel_unref(target.chan2); | ||||
| 	} | ||||
| 	if (targetcall_pvt) | ||||
| 		ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt"); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| @@ -26438,7 +26150,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint | ||||
| 	/* 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, NULL, req, seqno, nounlock))) { | ||||
| 		if ((res = local_attended_transfer(p, transferer, seqno, nounlock))) { | ||||
| 			ast_clear_flag(&p->flags[0], SIP_GOTREFER); | ||||
| 			return res; | ||||
| 		} | ||||
|   | ||||
| @@ -834,16 +834,6 @@ struct sip_request { | ||||
|  */ | ||||
| #define REQ_OFFSET_TO_STR(req,offset) (ast_str_buffer((req)->data) + ((req)->offset)) | ||||
|  | ||||
| /*! \brief structure used in transfers */ | ||||
| struct sip_dual { | ||||
| 	struct ast_channel *chan1;   /*!< First channel involved */ | ||||
| 	struct ast_channel *chan2;   /*!< Second channel involved */ | ||||
| 	struct sip_request req;      /*!< Request that caused the transfer (REFER) */ | ||||
| 	uint32_t seqno;              /*!< Sequence number */ | ||||
| 	char *park_exten; | ||||
| 	char *park_context; | ||||
| }; | ||||
|  | ||||
| /*! \brief Parameters to the transmit_invite function */ | ||||
| struct sip_invite_param { | ||||
| 	int addsipheaders;          /*!< Add extra SIP headers */ | ||||
| @@ -935,10 +925,6 @@ struct sip_refer { | ||||
| 		AST_STRING_FIELD(replaces_callid_totag);   /*!< Replace info: to-tag */ | ||||
| 		AST_STRING_FIELD(replaces_callid_fromtag); /*!< Replace info: from-tag */ | ||||
| 	); | ||||
| 	struct sip_pvt *refer_call;                     /*!< Call we are referring. This is just a reference to a | ||||
| 							 * dialog owned by someone else, so we should not destroy | ||||
| 							 * it when the sip_refer object goes. | ||||
| 							 */ | ||||
| 	int attendedtransfer;                           /*!< Attended or blind transfer? */ | ||||
| 	int localtransfer;                              /*!< Transfer to local domain? */ | ||||
| 	enum referstatus status;                        /*!< REFER status */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user