Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 					if (option_debug)
    						ast_log(LOG_DEBUG,"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 */
    
    				/* 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 (!strcasecmp(bridgepeer->tech->type, sip_tech.type)) {
    						bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
    
    						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 (ast_test_flag(req, SIP_PKT_IGNORE))
    								transmit_response(p, "488 Not Acceptable Here (unsupported)", req);
    							else
    								transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req);
    							ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    							sendok = FALSE;
    						} 
    						/* No bridged peer with T38 enabled*/
    
    					transmit_response_with_sdp(p, "200 OK", req, XMIT_CRITICAL);
    
    			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);
    
    		if (p && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) {
    
    			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");
    
    				msg = "503 Unavailable";
    
    			if (ast_test_flag(req, SIP_PKT_IGNORE))
    				transmit_response(p, msg, req);
    			else
    				transmit_response_reliable(p, msg, req);
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    /*! \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)
    
    {
    	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;
    	int error = 0;
    
    	/* 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 */
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "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 */
    		if (option_debug > 3)
    			ast_log(LOG_DEBUG, "SIP attended transfer: Error: No owner of target call\n");
    		error = 1;
    	}
    	/* We have a channel, find the bridge */
    	target.chan1 = targetcall_pvt->owner;				/* Transferer to Asterisk */
    
    	if (!error) {
    		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 (option_debug > 3) {
    				if (target.chan2) 
    					ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "SIP attended transfer: Error: No target channel\n");
    				else
    					ast_log(LOG_DEBUG, "SIP attended transfer: Attempting transfer in ringing state\n");
    			}
    			if (target.chan1->_state != AST_STATE_RING)
    				error = 1;
    		}
    	}
    	if (error) {	/* 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;
    		ast_mutex_unlock(&targetcall_pvt->lock);
    		ast_channel_unlock(current->chan1);
    		ast_channel_unlock(target.chan1);
    		return -1;
    	}
    
    	/* Transfer */
    	if (option_debug > 3 && sipdebug) {
    		if (current->chan2)	/* We have two bridges */
    			ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 */
    	res = attempt_transfer(current, &target);
    	ast_mutex_unlock(&targetcall_pvt->lock);
    	if (res) {
    		/* Failed transfer */
    		/* Could find better message, but they will get the point */
    		transmit_notify_with_sipfrag(transferer, seqno, "486 Busy", TRUE);
    		append_history(transferer, "Xfer", "Refer failed");
    		if (targetcall_pvt->owner)
    			ast_channel_unlock(targetcall_pvt->owner);
    		/* Right now, we have to hangup, sorry. Bridge is destroyed */
    		ast_hangup(transferer->owner);
    	} 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_log(LOG_DEBUG, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
    			ast_channel_unlock(targetcall_pvt->owner);
    		}
    	}
    	return 1;
    }
    
    
    
    /*! \brief Handle incoming REFER request */
    
    /*! \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 ignore, int seqno, int *nounlock)
    {
    
    	struct sip_dual current;	/* Chan1: Call between asterisk and transferer */
    					/* Chan2: Call between asterisk and transferee */
    
    	if (ast_test_flag(req, SIP_PKT_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 */
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
    		transmit_response(p, "603 Declined (No dialog)", req);
    		if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
    			append_history(p, "Xfer", "Refer failed. Outside of dialog.");
    			ast_set_flag(&p->flags[0], SIP_ALREADYGONE);	
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    		}
    		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(!ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
    		/* Already have a pending REFER */	
    		transmit_response(p, "491 Request pending", req);
    		append_history(p, "Xfer", "Refer failed. Request pending.");
    		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 (ast_test_flag(req, SIP_PKT_DEBUG))
    				ast_log(LOG_DEBUG, "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 (ast_test_flag(req, SIP_PKT_DEBUG))
    				ast_log(LOG_DEBUG, "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 (ast_test_flag(req, SIP_PKT_DEBUG))
    				ast_log(LOG_DEBUG, "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 && option_debug > 2)
    			ast_log(LOG_DEBUG, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
    
    	} else if (AST_LIST_EMPTY(&domain_list)) {
    
    		/* This PBX don't bother with SIP domains, so all transfers are local */
    		p->refer->localtransfer = 1;
    	} else
    		if (sipdebug && option_debug > 2)
    			ast_log(LOG_DEBUG, "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 (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 && option_debug > 2)
    		ast_log(LOG_DEBUG, "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 && option_debug > 2)
    			ast_log(LOG_DEBUG,"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;
    	}
    
    
    	if (current.chan2) {
    		if (sipdebug && option_debug > 3)
    			ast_log(LOG_DEBUG, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
    
    		ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
    
    
    	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, &current, req, seqno)))
    			return res;	/* We're done with the transfer */
    		/* Fall through for remote transfers that we did not find locally */
    		if (sipdebug && option_debug > 3)
    			ast_log(LOG_DEBUG, "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(&current.req, req);
    		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
    		p->refer->status = REFER_200OK;
    		append_history(p, "Xfer", "REFER to call parking.");
    		if (sipdebug && option_debug > 3)
    			ast_log(LOG_DEBUG, "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);
    
    		if (option_debug > 2)
    
    			ast_log(LOG_DEBUG, "chan1->name: %s\n", current.chan1->name);
    
    		pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
    	}
    	if (current.chan2) {
    
    		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");
    		if (p->refer->referred_by)
    			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
    		if (p->refer->referred_by)
    		/* Attended transfer to remote host, prepare headers for the INVITE */
    		pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
    	}
    	/* Generate an URI-encoded string */
    	if (p->refer->replaces_callid && !ast_strlen_zero(p->refer->replaces_callid)) {
    		char tempheader[BUFSIZ];
    		char tempheader2[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);
    
    		/* Convert it to URL encoding, also convert reserved strings */
    		ast_uri_encode(tempheader, tempheader2, sizeof(tempheader2), 1);
    
    		if (current.chan2)
    			pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader2);
    	}
    	/* Must release lock now, because it will not longer
        	   be accessible after the transfer! */
    	*nounlock = 1;
    	ast_channel_unlock(current.chan1);
    	ast_channel_unlock(current.chan2);
    
    	/* Connect the call */
    
    	/* 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 :-)
    		*/
    		transmit_response(p, "202 Accepted", req);
    		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 */
    
    	/* 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) {
    		/* Success  - we have a new channel */
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "%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;
    		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 {
    		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Don't delay hangup */
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "%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;
    
    /*! \brief Handle incoming CANCEL request */
    
    static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
    
    	ast_set_flag(&p->flags[0], SIP_ALREADYGONE);	
    
    	
    	if (p->owner && p->owner->_state == AST_STATE_UP) {
    		/* This call is up, cancel is ignored, we need a bye */
    		transmit_response(p, "200 OK", req);
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Got CANCEL on an answered call. Ignoring... \n");
    		return 0;
    	}
    
    	if (p->rtp) {
    		/* Immediately stop RTP */
    		ast_rtp_stop(p->rtp);
    	}
    	if (p->vrtp) {
    		/* Immediately stop VRTP */
    		ast_rtp_stop(p->vrtp);
    	}
    
    	if (p->udptl) {
    		/* Immediately stop UDPTL */
    		ast_udptl_stop(p->udptl);
    	}
    
    	if (p->owner)
    		ast_queue_hangup(p->owner);
    	else
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    	if (p->initreq.len > 0) {
    
    		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
    
    		transmit_response(p, "200 OK", req);
    		return 1;
    	} else {
    		transmit_response(p, "481 Call Leg Does Not Exist", req);
    		return 0;
    	}
    }
    
    
    /*! \brief Handle incoming BYE request */
    
    static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
    
    {
    	struct ast_channel *c=NULL;
    	int res;
    	struct ast_channel *bridged_to;
    
    	char *audioqos = NULL, *videoqos = NULL;
    
    	if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_test_flag(req, SIP_PKT_IGNORE))
    
    		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
    
    
    	copy_request(&p->initreq, req);
    	check_via(p, req);
    
    	ast_set_flag(&p->flags[0], SIP_ALREADYGONE);	
    
    	if (p->rtp)
    		audioqos = ast_rtp_get_quality(p->rtp);
    	if (p->vrtp)
    		videoqos = ast_rtp_get_quality(p->vrtp);
    
    
    	/* Get RTCP quality before end of call */
    	if (recordhistory) {
    		if (p->rtp)
    
    			append_history(p, "RTCPaudio", "Quality:%s", audioqos);
    
    		if (p->vrtp)
    
    			append_history(p, "RTCPvideo", "Quality:%s", videoqos);
    
    		if (p->owner)
    			pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
    
    		/* Immediately stop RTP */
    		ast_rtp_stop(p->rtp);
    	}
    	if (p->vrtp) {
    
    		if (p->owner)
    			pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
    
    		/* Immediately stop VRTP */
    		ast_rtp_stop(p->vrtp);
    	}
    
    	if (p->udptl) {
    		/* Immediately stop UDPTL */
    		ast_udptl_stop(p->udptl);
    	}
    
    	if (!ast_strlen_zero(get_header(req, "Also"))) {
    		ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method.  Ask vendor to support REFER instead\n",
    
    		if (ast_strlen_zero(p->context))
    
    			ast_string_field_set(p, context, default_context);
    
    		res = get_also_info(p, req);
    		if (!res) {
    			c = p->owner;
    			if (c) {
    				bridged_to = ast_bridged_channel(c);
    				if (bridged_to) {
    					/* Don't actually hangup here... */
    
    					ast_queue_control(c, AST_CONTROL_UNHOLD);
    
    					ast_async_goto(bridged_to, p->context, p->refer->refer_to,1);
    
    				} else
    					ast_queue_hangup(p->owner);
    			}
    		} else {
    
    			ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
    
    			if (p->owner)
    				ast_queue_hangup(p->owner);
    
    		}
    	} else if (p->owner)
    		ast_queue_hangup(p->owner);
    	else
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    	transmit_response(p, "200 OK", req);
    
    	return 1;
    }
    
    
    /*! \brief Handle incoming MESSAGE request */
    
    static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
    
    	if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
    		if (ast_test_flag(req, SIP_PKT_DEBUG))
    
    			ast_verbose("Receiving message!\n");
    		receive_message(p, req);
    
    	} 
    	transmit_response(p, "202 Accepted", req);
    
    /*! \brief  Handle incoming SUBSCRIBE request */
    static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e)
    
    	const char *event = get_header(req, "Event");	/* Get Event package name */
    	const char *accept = get_header(req, "Accept");
    
    	int resubscribe = (p->subscribed != NONE);
    
    		/* We already have a dialog */
    		if (p->initreq.method != SIP_SUBSCRIBE) {
    			/* This is a SUBSCRIBE within another SIP dialog, which we do not support */
    			/* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
     			transmit_response(p, "403 Forbidden (within dialog)", req);
    			/* Do not destroy session, since we will break the call if we do */
    			ast_log(LOG_DEBUG, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
    			return 0;
    
    		} else if (ast_test_flag(req, SIP_PKT_DEBUG)) {
    
    				ast_log(LOG_DEBUG, "Got a re-subscribe on existing subscription %s\n", p->callid);
    
    			else
    				ast_log(LOG_DEBUG, "Got a new subscription %s (possibly with auth)\n", p->callid);
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    	/* Check if we have a global disallow setting on subscriptions. 
    		if so, we don't have to check peer/user settings after auth, which saves a lot of processing
    	*/
    	if (!global_allowsubscribe) {
     		transmit_response(p, "403 Forbidden (policy)", req);
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    Olle Johansson's avatar
    Olle Johansson committed
    		return 0;
    	}
    
    
    	if (!ast_test_flag(req, SIP_PKT_IGNORE) && !p->initreq.headers) {	/* Set up dialog, new subscription */
    
    		/* Use this as the basis */
    
    		if (ast_test_flag(req, SIP_PKT_DEBUG))
    
    		/* This call is no longer outgoing if it ever was */
    
    		ast_clear_flag(&p->flags[0], SIP_OUTGOING);
    
    		copy_request(&p->initreq, req);
    		check_via(p, req);
    
    	} else if (ast_test_flag(req, SIP_PKT_DEBUG) && ast_test_flag(req, SIP_PKT_IGNORE))
    
    		ast_verbose("Ignoring this SUBSCRIBE request\n");
    
    
    	/* Find parameters to Event: header value and remove them for now */
    
    	event = strsep((char **)&event, ";");	/* XXX bug here, overwrite string */
    
    	/* Handle authentication if this is our first subscribe */
    
    	res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer);
    
    	/* if an authentication response was sent, we are done here */
    	if (res == AUTH_CHALLENGE_SENT)
    		return 0;
    	if (res < 0) {
    		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 for SUBSCRIBE\n", get_header(req, "From"));
    
    			transmit_response_reliable(p, "403 Forbidden", req);
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    		return 0;
    	}
    
    	/* Check if this user/peer is allowed to subscribe at all */
    	if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
    		transmit_response(p, "403 Forbidden (policy)", req);
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    		return 0;
    	}
    
    
    	/* Get destination right away */
    	gotdest = get_destination(p, NULL);
    
    	/* Initialize the context if it hasn't been already;
    	   note this is done _after_ handling any domain lookups,
    	   because the context specified there is for calls, not
    	   subscriptions
    	*/
    
    	if (!ast_strlen_zero(p->subscribecontext))
    		ast_string_field_set(p, context, p->subscribecontext);
    	else if (ast_strlen_zero(p->context))
    		ast_string_field_set(p, context, default_context);
    
    	build_contact(p);
    	if (gotdest) {
    		transmit_response(p, "404 Not Found", req);
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    		return 0;
    	} else {
    
    		/* Initialize tag for new subscriptions */	
    		if (ast_strlen_zero(p->tag))
    			make_our_tag(p->tag, sizeof(p->tag));
    
    		if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
    
    			/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
     			if (strstr(accept, "application/pidf+xml")) {
     				p->subscribed = PIDF_XML;         /* RFC 3863 format */
     			} else if (strstr(accept, "application/dialog-info+xml")) {
     				p->subscribed = DIALOG_INFO_XML;
     				/* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
     			} else if (strstr(accept, "application/cpim-pidf+xml")) {
     				p->subscribed = CPIM_PIDF_XML;    /* RFC 3863 format */
     			} else if (strstr(accept, "application/xpidf+xml")) {
     				p->subscribed = XPIDF_XML;        /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
     			} else if (strstr(p->useragent, "Polycom")) {
     				p->subscribed = XPIDF_XML;        /*  Polycoms subscribe for "event: dialog" but don't include an "accept:" header */
    			} else {
     				/* Can't find a format for events that we know about */
     				transmit_response(p, "489 Bad Event", req);
    
     		} else if (!strcmp(event, "message-summary")) { 
    			if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) {
    				/* Format requested that we do not support */
    				transmit_response(p, "406 Not Acceptable", req);
    				if (option_debug > 1)
    					ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", accept);
     				ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    				return 0;
    			}
    
    			/* Looks like they actually want a mailbox status 
    			  This version of Asterisk supports mailbox subscriptions
    			  The subscribed URI needs to exist in the dial plan
    			  In most devices, this is configurable to the voicemailmain extension you use
    			*/
    			if (!authpeer || ast_strlen_zero(authpeer->mailbox)) {
    				transmit_response(p, "404 Not found (no mailbox)", req);
    				ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    				ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
    
    
     			p->subscribed = MWI_NOTIFICATION;
    			if (authpeer->mwipvt && authpeer->mwipvt != p)	/* Destroy old PVT if this is a new one */
    				/* We only allow one subscription per peer */
    				sip_destroy(authpeer->mwipvt);
    			authpeer->mwipvt = p;		/* Link from peer to pvt */
    			p->relatedpeer = authpeer;	/* Link from pvt to peer */
    		} else { /* At this point, Asterisk does not understand the specified event */
    			transmit_response(p, "489 Bad Event", req);
    			if (option_debug > 1)
    				ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: %s\n", event);
     			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    			return 0;
    
    		if (p->subscribed != MWI_NOTIFICATION && !resubscribe)
    
    			p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
    
    	if (!ast_test_flag(req, SIP_PKT_IGNORE) && p)
    
    	if (p && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) {
    
    		p->expiry = atoi(get_header(req, "Expires"));
    
    
    		/* check if the requested expiry-time is within the approved limits from sip.conf */
    		if (p->expiry > max_expiry)
    			p->expiry = max_expiry;
    
    		if (p->expiry < min_expiry && p->expiry > 0)
    
    		if (sipdebug || option_debug > 1) {
    			if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
    				ast_log(LOG_DEBUG, "Adding subscription for mailbox notification - peer %s Mailbox %s\n", p->relatedpeer->name, p->relatedpeer->mailbox);
    			else
    				ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
    		}
    
    		if (p->autokillid > -1)
    			sip_cancel_destroy(p);	/* Remove subscription expiry for renewals */
    
    		if (p->expiry > 0)
    			sip_scheddestroy(p, (p->expiry + 10) * 1000);	/* Set timer for destruction of call at expiration */
    
    			transmit_response(p, "200 OK", req);
    
    			if (p->relatedpeer) {	/* Send first notification */
    				ASTOBJ_WRLOCK(p->relatedpeer);
    				sip_send_mwi_to_peer(p->relatedpeer);
    				ASTOBJ_UNLOCK(p->relatedpeer);
    			}
    		} else {
    			if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
    
    				ast_log(LOG_ERROR, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension\n", p->exten, p->context, ast_inet_ntoa(p->sa.sin_addr));
    
    				transmit_response(p, "404 Not found", req);
    				ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    				return 0;
    			} else {
    
    				/* XXX reduce nesting here */
    
    				struct sip_pvt *p_old;
    	
    				transmit_response(p, "200 OK", req);
    				transmit_state_notify(p, firststate, 1);	/* Send first notification */
    				append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate));
    
    				/* hide the 'complete' exten/context in the refer_to field for later display */
    
    				ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
    
    
    				/* remove any old subscription from this peer for the same exten/context,
    			   	as the peer has obviously forgotten about it and it's wasteful to wait
    			   	for it to expire and send NOTIFY messages to the peer only to have them
    			   	ignored (or generate errors)
    				*/
    				ast_mutex_lock(&iflock);
    				for (p_old = iflist; p_old; p_old = p_old->next) {
    					if (p_old == p)
    						continue;
    					if (p_old->initreq.method != SIP_SUBSCRIBE)
    						continue;
    					if (p_old->subscribed == NONE)
    						continue;
    					ast_mutex_lock(&p_old->lock);
    					if (!strcmp(p_old->username, p->username)) {
    						if (!strcmp(p_old->exten, p->exten) &&
    						    !strcmp(p_old->context, p->context)) {
    							ast_set_flag(&p_old->flags[0], SIP_NEEDDESTROY);
    							ast_mutex_unlock(&p_old->lock);
    							break;
    						}
    
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    	if (authpeer)
    		ASTOBJ_UNREF(authpeer, sip_destroy_peer);
    
    /*! \brief Handle incoming REGISTER request */
    
    static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e)
    
    	if (ast_test_flag(req, SIP_PKT_DEBUG))
    
    		ast_verbose("Using latest REGISTER request as basis request\n");
    
    	copy_request(&p->initreq, req);
    	check_via(p, req);
    
    	if ((res = register_verify(p, sin, req, e)) < 0) {
    		const char *reason = "";
    
    		switch (res) {
    		case AUTH_SECRET_FAILED:
    			reason = "Wrong password";
    			break;
    		case AUTH_USERNAME_MISMATCH:
    			reason = "Username/auth name mismatch";
    			break;
    		case AUTH_NOT_FOUND:
    			reason = "No matching peer found";
    			break;
    		case AUTH_UNKNOWN_DOMAIN:
    			reason = "Not a local domain";
    			break;
    		default:
    			break;
    		}
    		ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
    
    			get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
    
    	if (res < 1) {
    		/* Destroy the session, but keep us around for just a bit in case they don't
    		   get our 200 OK */
    
    	append_history(p, "RegRequest", "%s : Account %s", res ? "Failed": "Succeeded", get_header(req, "To"));
    
    /*! \brief Handle incoming SIP requests (methods) 
    \note	This is where all incoming requests go first   */
    
    /* called with p and p->owner locked */
    
    static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things
    	   relatively static */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request resp;
    
    	const char *cmd;
    	const char *cseq;
    	const char *useragent;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int seqno;
    	int len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int respid;
    
    	int debug = sip_debug_test_pvt(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Clear out potential response */
    	memset(&resp, 0, sizeof(resp));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Get Method and Cseq */
    	cseq = get_header(req, "Cseq");
    	cmd = req->header[0];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Must have Cseq */
    
    	if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) {
    		ast_log(LOG_ERROR, "Missing Cseq. Dropping this SIP message, it's incomplete.\n");
    		error = 1;
    	}
    	if (!error && sscanf(cseq, "%d%n", &seqno, &len) != 1) {
    		ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd);
    		error = 1;
    	}
    	if (error) {
    		if (!p->initreq.header)	/* New call */
    
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	/* Make sure we destroy this dialog */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	cmd = req->rlPart1;
    	e = req->rlPart2;
    
    
    	/* Save useragent of the client */
    	useragent = get_header(req, "User-Agent");
    
    		ast_string_field_set(p, useragent, useragent);
    
    	/* Find out SIP method for incoming request */
    
    	if (req->method == SIP_RESPONSE) {	/* Response to our request */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Response to our request -- Do some sanity checks */	
    		if (!p->initreq.headers) {
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "That's odd...  Got a response on a call we dont know about. Cseq %d Cmd %s\n", seqno, cmd);
    
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		} else if (p->ocseq && (p->ocseq < seqno)) {
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		} else if (p->ocseq && (p->ocseq != seqno)) {
    			/* ignore means "don't do anything with it" but still have to 
    			   respond appropriately  */
    
    			ignore = TRUE;
    			ast_set_flag(req, SIP_PKT_IGNORE);
    			ast_set_flag(req, SIP_PKT_IGNORE_RESP);
    			append_history(p, "Ignore", "Ignoring this retransmit\n");
    
    			ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
    
    			/* More SIP ridiculousness, we have to ignore bogus contacts in 100 etc responses */
    			if ((respid == 200) || ((respid >= 300) && (respid <= 399)))
    				extract_uri(p, req);
    			handle_response(p, respid, e + len, req, ignore, seqno);