All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.servlet.sip.message.B2buaHelperImpl Maven / Gradle / Ivy

There is a newer version: 4.0.128
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.servlet.sip.message;

import gov.nist.javax.sip.header.HeaderExt;
import gov.nist.javax.sip.header.ims.PathHeader;
import gov.nist.javax.sip.message.MessageExt;

import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;

import javax.servlet.ServletException;
import javax.servlet.sip.SipServletMessage;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.SipSession.State;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.UAMode;
import javax.servlet.sip.ar.SipApplicationRoutingDirective;
import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ServerTransaction;
import javax.sip.Transaction;
import javax.sip.TransactionState;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.Header;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.RecordRouteHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Message;
import javax.sip.message.Request;

import org.apache.log4j.Logger;
import org.mobicents.ha.javax.sip.SipLoadBalancer;
import org.mobicents.servlet.sip.JainSipUtils;
import org.mobicents.servlet.sip.address.AddressImpl;
import org.mobicents.servlet.sip.address.AddressImpl.ModifiableRule;
import org.mobicents.servlet.sip.core.ApplicationRoutingHeaderComposer;
import org.mobicents.servlet.sip.core.MobicentsExtendedListeningPoint;
import org.mobicents.servlet.sip.core.MobicentsSipFactory;
import org.mobicents.servlet.sip.core.RoutingState;
import org.mobicents.servlet.sip.core.SipApplicationDispatcher;
import org.mobicents.servlet.sip.core.SipManager;
import org.mobicents.servlet.sip.core.b2bua.MobicentsB2BUAHelper;
import org.mobicents.servlet.sip.core.message.MobicentsSipServletMessage;
import org.mobicents.servlet.sip.core.message.MobicentsSipServletRequest;
import org.mobicents.servlet.sip.core.session.MobicentsSipApplicationSession;
import org.mobicents.servlet.sip.core.session.MobicentsSipSession;
import org.mobicents.servlet.sip.core.session.MobicentsSipSessionKey;
import org.mobicents.servlet.sip.core.session.SessionManagerUtil;
import org.mobicents.servlet.sip.core.session.SipSessionKey;

/**
 * Implementation of the B2BUA helper class.
 * 
 * 
 * @author mranga
 * @author Jean Deruelle
 */

public class B2buaHelperImpl implements MobicentsB2BUAHelper, Serializable {
	private static final long serialVersionUID = 1L;

	private static final Logger logger = Logger.getLogger(B2buaHelperImpl.class);
	
	protected static final Set B2BUA_SYSTEM_HEADERS = new HashSet();
	static {
		B2BUA_SYSTEM_HEADERS.add(CallIdHeader.NAME);
		B2BUA_SYSTEM_HEADERS.add(CSeqHeader.NAME);
		B2BUA_SYSTEM_HEADERS.add(ViaHeader.NAME);
		// Fix http://code.google.com/p/mobicents/issues/detail?id=3060
		// B2buaHelper.createRequest method throws IllegalArgumentException when "Route" header is included in "headerMap"
//		B2BUA_SYSTEM_HEADERS.add(RouteHeader.NAME);
		B2BUA_SYSTEM_HEADERS.add(RecordRouteHeader.NAME);
		B2BUA_SYSTEM_HEADERS.add(PathHeader.NAME);
	}
	
	// contact parameters not allowed to be modified as per JSR 289 Section 4.1.3
	protected static final Set CONTACT_FORBIDDEN_PARAMETER = new HashSet();
	static {
		CONTACT_FORBIDDEN_PARAMETER.add("method");
		CONTACT_FORBIDDEN_PARAMETER.add("ttl");
		CONTACT_FORBIDDEN_PARAMETER.add("maddr");
		CONTACT_FORBIDDEN_PARAMETER.add("lr");
	}
	
	//Map to handle linked sessions
	private Map sessionMap = null;	
	//Map to handle linked derived sessions
	private Map derivedSessionMap = null;

	//Map to handle responses to original request and cancel on original request
	// Issue 1550 http://code.google.com/p/mobicents/issues/detail?id=1550
	// IllegalStateException: Cannot create a response - not a server transaction gov.nist.javax.sip.stack.SIPClientTransaction
	// this map should be able to handle multiple linked requests at the same time
	private transient Map originalRequestMap = null;

	private transient SipFactoryImpl sipFactoryImpl;
	
	private transient SipManager sipManager;

	public B2buaHelperImpl() {
		sessionMap = new ConcurrentHashMap();
		derivedSessionMap = new ConcurrentHashMap();
		originalRequestMap = new ConcurrentHashMap();
	}
	
	/*
	 * (non-Javadoc)
	 * @see javax.servlet.sip.B2buaHelper#createRequest(javax.servlet.sip.SipServletRequest, boolean, java.util.Map)
	 */
	public SipServletRequest createRequest(SipServletRequest origRequest,
			boolean linked, Map> headerMap) throws TooManyHopsException {
		
		if(origRequest == null) {
			throw new NullPointerException("original request cannot be null");
		}

		if(origRequest.getMaxForwards() == 0) {
			throw new TooManyHopsException();
		}
		
		try {
			final SipServletRequestImpl origRequestImpl = (SipServletRequestImpl) origRequest;
			Request newRequest = (Request) origRequestImpl.message.clone();
			((MessageExt)newRequest).setApplicationData(null);
			//content should be copied too, so commented out
//		 	newRequest.removeContent();				
			//removing the via header from original request
			newRequest.removeHeader(ViaHeader.NAME);	
					
			// Remove the route header ( will point to us ).
			// commented as per issue 649
//			newRequest.removeHeader(RouteHeader.NAME);
//			String tag = Integer.toString((int) (Math.random()*1000));
//			((FromHeader) newRequest.getHeader(FromHeader.NAME)).setParameter("tag", tag);
			
			// Remove the record route headers. This is a new call leg.
			newRequest.removeHeader(RecordRouteHeader.NAME);			
	
			// Issue 1490 : http://code.google.com/p/mobicents/issues/detail?id=1490 
			// B2buaHelper.createRequest does not decrement Max-forwards
			MaxForwardsHeader maxForwardsHeader = (MaxForwardsHeader) newRequest.getHeader(MaxForwardsHeader.NAME);
			try {
				maxForwardsHeader.setMaxForwards(maxForwardsHeader.getMaxForwards() -1);
			} catch (InvalidArgumentException e) {
				throw new IllegalArgumentException(e);
			}
			
			//Creating new call id
			final Iterator listeningPointsIterator = sipFactoryImpl.getSipNetworkInterfaceManager().getExtendedListeningPoints();				
			if(!listeningPointsIterator.hasNext()) {				
				throw new IllegalStateException("There is no SIP connectors available to create the request");
			}
			final MobicentsExtendedListeningPoint extendedListeningPoint = listeningPointsIterator.next();
			final CallIdHeader callIdHeader = sipFactoryImpl.getSipApplicationDispatcher().getCallId(extendedListeningPoint, null);
			newRequest.setHeader(callIdHeader);
			
			final List contactHeaderSet = retrieveContactHeaders(headerMap,
					newRequest, origRequest.getSession().getServletContext());	
			final FromHeader newFromHeader = (FromHeader) newRequest.getHeader(FromHeader.NAME);
			newFromHeader.removeParameter("tag");
			((ToHeader) newRequest.getHeader(ToHeader.NAME))
					.removeParameter("tag");
			
			final MobicentsSipSession originalSession = origRequestImpl.getSipSession();
			final MobicentsSipApplicationSession appSession = originalSession
					.getSipApplicationSession();			
			
			newFromHeader.setTag(ApplicationRoutingHeaderComposer.getHash(sipFactoryImpl.getSipApplicationDispatcher(), originalSession.getKey().getApplicationName(), appSession.getKey().getId()));
			
			final MobicentsSipSessionKey key = SessionManagerUtil.getSipSessionKey(appSession.getKey().getId(), originalSession.getKey().getApplicationName(), newRequest, false);
			final MobicentsSipSession session = appSession.getSipContext().getSipManager().getSipSession(key, true, sipFactoryImpl, appSession);			
			session.setHandler(originalSession.getHandler());
		
			
			// cater to http://code.google.com/p/sipservlets/issues/detail?id=31 to be able to set the rport in applications
			final SipApplicationDispatcher sipApplicationDispatcher = sipFactoryImpl.getSipApplicationDispatcher();
			final String branch = JainSipUtils.createBranch(appSession.getKey().getId(),  sipApplicationDispatcher.getHashFromApplicationName(appSession.getKey().getApplicationName()));
			ViaHeader viaHeader = JainSipUtils.createViaHeader(
    				sipFactoryImpl.getSipNetworkInterfaceManager(), newRequest, branch, session.getOutboundInterface());
			newRequest.addHeader(viaHeader);
			
			final SipServletRequestImpl newSipServletRequest = (SipServletRequestImpl) sipFactoryImpl.getMobicentsSipServletMessageFactory().createSipServletRequest(
					newRequest,
					session, 
					null, 
					null, 
					JainSipUtils.DIALOG_CREATING_METHODS.contains(newRequest.getMethod()));			
			//JSR 289 Section 15.1.6	
			newSipServletRequest.setRoutingDirective(SipApplicationRoutingDirective.CONTINUE, origRequest);			
			
			final String method = origRequest.getMethod();
			// For non-REGISTER requests, the Contact header field is not copied but is populated by the container as usual but if Contact header is present in the headerMap 
			// then relevant portions of Contact header is to be used in the request created, in accordance with section 4.1.3 of the specification.
			if(Request.REGISTER.equalsIgnoreCase(method)) {
				// Issue 2565 http://code.google.com/p/mobicents/issues/detail?id=2565
				// So if the request is REGISTER the Contact Header field is copied and is used 
				if(contactHeaderSet.size() > 0) {
					// And additional contact from the map are added + stripping the forbidden params
					for (String contactHeaderValue : contactHeaderSet) {
						newSipServletRequest.addHeaderInternal(ContactHeader.NAME, contactHeaderValue, true);						
					}
					ListIterator contactHeaders = newSipServletRequest.getMessage().getHeaders(ContactHeader.NAME);
					while (contactHeaders.hasNext()) {
						final URI contactURI = contactHeaders.next().getAddress().getURI();
						// and reset its user part and params accoridng to 4.1.3 The Contact Header Field
						if(contactURI instanceof SipURI) {
							stripForbiddenContactURIParams((SipURI)contactURI);
						}
					}
				}
			} else {
				newRequest.removeHeader(ContactHeader.NAME);	
				//Creating container contact header
				ContactHeader contactHeader = null;
				String fromName = null;
				String diaplayName = newFromHeader.getAddress().getDisplayName();
				if(newFromHeader.getAddress().getURI() instanceof javax.sip.address.SipURI) {
					fromName = ((javax.sip.address.SipURI) newFromHeader.getAddress().getURI()).getUser();
				}										
				// if a sip load balancer is present in front of the server, the contact header is the one from the sip lb
				// so that the subsequent requests can be failed over
				if(sipFactoryImpl.isUseLoadBalancer()) {
					MobicentsExtendedListeningPoint listeningPoint = JainSipUtils.findListeningPoint(sipFactoryImpl.getSipNetworkInterfaceManager(), newRequest, session.getOutboundInterface());
					if(listeningPoint != null && listeningPoint.isUseLoadBalancer()) {
						// https://github.com/RestComm/sip-servlets/issues/137
						SipLoadBalancer loadBalancerToUse = null; 
						if(listeningPoint.getLoadBalancer() == null) {
							loadBalancerToUse = sipFactoryImpl.getLoadBalancerToUse();
							if(logger.isDebugEnabled()) {
								logger.debug("Using listeningPoint " + listeningPoint + " for global load balancer " + sipFactoryImpl.getLoadBalancerToUse());
							}
						} else {
							loadBalancerToUse = listeningPoint.getLoadBalancer();
							if(logger.isDebugEnabled()) {
								logger.debug("Using listeningPoint " + listeningPoint + " for connector specific load balancer " + listeningPoint.getLoadBalancer());
							}
						}
						
						javax.sip.address.SipURI sipURI = sipFactoryImpl.getAddressFactory().createSipURI(fromName, loadBalancerToUse.getAddress().getHostAddress());
						sipURI.setHost(loadBalancerToUse.getAddress().getHostAddress());
						sipURI.setPort(loadBalancerToUse.getSipPort());			
						sipURI.setTransportParam(ListeningPoint.UDP);
						javax.sip.address.Address contactAddress = sipFactoryImpl.getAddressFactory().createAddress(sipURI);
						if(diaplayName != null && diaplayName.length() > 0) {
							contactAddress.setDisplayName(diaplayName);
						}
						contactHeader = sipFactoryImpl.getHeaderFactory().createContactHeader(contactAddress);
					} else {
						if(logger.isDebugEnabled()) {
							logger.debug("Not Using load balancer as it is not enabled for listeningPoint " + listeningPoint);
						}
						contactHeader = JainSipUtils.createContactHeader(sipFactoryImpl.getSipNetworkInterfaceManager(), newRequest, diaplayName, fromName, session.getOutboundInterface());
					}
				} else {					
					contactHeader = JainSipUtils.createContactHeader(sipFactoryImpl.getSipNetworkInterfaceManager(), newRequest, diaplayName, fromName, session.getOutboundInterface());
				}	
				if(contactHeaderSet.size() > 0) {
					// if the set is not empty then we adjust the values of the set to match the host and port + forbidden params of the container
					setContactHeaders(contactHeaderSet, newSipServletRequest, contactHeader);
				} else if(JainSipUtils.CONTACT_HEADER_METHODS.contains(method)) {
					// otherwise we set the container contact header for allowed methods
					newRequest.setHeader(contactHeader);
				}
			}
			
			originalRequestMap.put(newSipServletRequest, origRequestImpl);
			originalRequestMap.put(origRequestImpl, newSipServletRequest);
			
			if (linked) {
				sessionMap.put(originalSession.getKey(), session.getKey());
				sessionMap.put(session.getKey(), originalSession.getKey());
				dumpLinkedSessions();
			}
			session.setB2buaHelper(this);
			originalSession.setB2buaHelper(this);
			session.setSessionCreatingTransactionRequest(newSipServletRequest);
			
			return newSipServletRequest;
		} catch (ParseException ex) {
			logger.error("Unexpected parse exception ", ex);
			throw new IllegalArgumentException(
					"Illegal arg encountered while creating b2bua", ex);
		} catch (ServletException ex) {
			logger.error("Unexpected exception ", ex);
			throw new IllegalArgumentException(
					"Unexpected problem while creating b2bua", ex);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see javax.servlet.sip.B2buaHelper#createRequest(javax.servlet.sip.SipSession, javax.servlet.sip.SipServletRequest, java.util.Map)
	 */
	public SipServletRequest createRequest(SipSession session,
			SipServletRequest origRequest, Map> headerMap) {
		
		if(origRequest == null) {
			throw new NullPointerException("original request cannot be null");
		}
		
		try {
			final SipServletRequestImpl origRequestImpl = (SipServletRequestImpl) origRequest;
			final MobicentsSipSession originalSession = origRequestImpl.getSipSession();
			final MobicentsSipSession sessionImpl = (MobicentsSipSession) session;

			final SipServletRequestImpl newSubsequentServletRequest = (SipServletRequestImpl) session.createRequest(origRequest.getMethod());
						
			//For non-REGISTER requests, the Contact header field is not copied 
			//but is populated by the container as usual
			
			//commented since this is not true in this case since this is a subsequent request
			// this is needed for sending challenge requests
//			if(!Request.REGISTER.equalsIgnoreCase(origRequest.getMethod())) {
//				newSubsequentServletRequest.getMessage().removeHeader(ContactHeader.NAME);
//			}
			//If Contact header is present in the headerMap 
			//then relevant portions of Contact header is to be used in the request created, 
			//in accordance with section 4.1.3 of the specification.
			//They will be added later after the sip servlet request has been created
			final List contactHeaderSet = retrieveContactHeaders(headerMap,
					(Request) newSubsequentServletRequest.getMessage(),
                                        originalSession.getServletContext());
						
			//If Contact header is present in the headerMap 
			//then relevant portions of Contact header is to be used in the request created, 
			//in accordance with section 4.1.3 of the specification.
			Request subsequentRequest = (Request)newSubsequentServletRequest.getMessage();
			
			// Issue 1490 : http://code.google.com/p/mobicents/issues/detail?id=1490 
			// B2buaHelper.createRequest does not decrement Max-forwards
			MaxForwardsHeader maxForwardsHeader = (MaxForwardsHeader) subsequentRequest.getHeader(MaxForwardsHeader.NAME);
			try {
				maxForwardsHeader.setMaxForwards(maxForwardsHeader.getMaxForwards() -1);
			} catch (InvalidArgumentException e) {
				throw new IllegalArgumentException(e);
			}
			
			ContactHeader contactHeader = (ContactHeader) subsequentRequest.getHeader(ContactHeader.NAME);
			if(contactHeader != null && contactHeaderSet.size() > 0) {
				subsequentRequest.removeHeader(ContactHeader.NAME);
				setContactHeaders(contactHeaderSet, newSubsequentServletRequest, contactHeader);
			}
			
			//Fix for Issue 585 by alexandre sova
			if(origRequest.getContent() != null && origRequest.getContentType() != null) {
				newSubsequentServletRequest.setContentLength(origRequest.getContentLength());
				newSubsequentServletRequest.setContent(origRequest.getContent(), origRequest.getContentType());
			}
			
			if(logger.isDebugEnabled()) {
				logger.debug("newSubsequentServletRequest = " + newSubsequentServletRequest);
			}			
			
			// Added for Issue 1409 http://code.google.com/p/mobicents/issues/detail?id=1409
			copyNonSystemHeaders(origRequestImpl, newSubsequentServletRequest);
			
			originalRequestMap.put(newSubsequentServletRequest, origRequestImpl);
			originalRequestMap.put(origRequestImpl, newSubsequentServletRequest);
			
			sessionMap.put(originalSession.getKey(), sessionImpl.getKey());
			sessionMap.put(sessionImpl.getKey(), originalSession.getKey());
			dumpLinkedSessions();

			sessionImpl.setB2buaHelper(this);
			originalSession.setB2buaHelper(this);
			return newSubsequentServletRequest;
		} catch (Exception ex) {
			logger.error("Unexpected exception ", ex);
			throw new IllegalArgumentException(
					"Illegal arg encountered while creating b2bua", ex);
		}
	}

	/**
	 * Copies all the non system headers from the original request into the new subsequent request
	 * (Not needed for initial requests since a clone of the request is done)
	 * 
	 * Added for Issue 1409 http://code.google.com/p/mobicents/issues/detail?id=1409
	 */
	private void copyNonSystemHeaders(SipServletRequestImpl origRequestImpl,
			SipServletRequestImpl newSubsequentServletRequest) {
		final Message origMessage = origRequestImpl.getMessage();
		final Message subsequentMessage = newSubsequentServletRequest.getMessage();		
		ListIterator headerNames = origMessage.getHeaderNames();
		while (headerNames.hasNext()) {
			String headerName = headerNames.next();
			if(!JainSipUtils.SYSTEM_HEADERS.contains(headerName) && !headerName.equalsIgnoreCase(ContactHeader.NAME)
					&& !headerName.equalsIgnoreCase(FromHeader.NAME) && !headerName.equalsIgnoreCase(ToHeader.NAME)) {
				// Issue 184 : http://code.google.com/p/sipservlets/issues/detail?id=184
				// Not all headers are copied for subsequent requests using B2buaHelper.createRequest(session, request, map)
				// Fix by Alexander Saveliev, iterate through all headers and copy them
                ListIterator
origHeaderIt = origMessage.getHeaders(headerName); ListIterator
subsHeaderIt = subsequentMessage.getHeaders(headerName); while (origHeaderIt.hasNext()) { HeaderExt origHeader = (HeaderExt) origHeaderIt.next(); // Issue http://code.google.com/p/mobicents/issues/detail?id=2094 // B2b re-invite for authentication will duplicate Remote-Party-ID header // checking if the subsequent request and original request share the same header name and value // before copying to avoid duplicating the headers boolean headerNameValueAlreadyPresent = false; while (subsHeaderIt.hasNext() && !headerNameValueAlreadyPresent) { HeaderExt subsHeader = (HeaderExt) subsHeaderIt.next(); if (origHeader.getValue().equals(subsHeader.getValue())) { headerNameValueAlreadyPresent = true; } } if (!headerNameValueAlreadyPresent) { if (origHeader != null) { subsequentMessage.addHeader(((Header) origHeader.clone())); if (logger.isDebugEnabled()) { logger.debug("original header " + origHeader + " copied in the new subsequent request"); } } else { // Issue 2500 http://code.google.com/p/mobicents/issues/detail?id=2500 // B2buaHelper.createRequest() throws a NullPointerException if the request contains an empty header if (logger.isDebugEnabled()) { logger.debug("trying to copy original header name " + headerName + " into the new subsequent request with an empty value"); } try { subsequentMessage.addHeader(sipFactoryImpl.getHeaderFactory().createHeader(headerName, "")); } catch (ParseException e) { if (logger.isDebugEnabled()) { logger.debug("couldn't copy original header name " + headerName + " into the new subsequent request with an empty value"); } } } } } } } } /** * Issue 1151 : * For Contact header if present only the user part and some parameters are to be used as defined in 4.1.3 The Contact Header Field * * @param newRequest * @param contactHeaderSet * @param newSipServletRequest * @param contactHeader * @throws ParseException */ private void setContactHeaders( final List contactHeaderSet, final SipServletRequestImpl newSipServletRequest, ContactHeader contactHeader) throws ParseException { // we parse and add the contact headers defined in the map Request newRequest = (Request) newSipServletRequest.getMessage(); for (String contactHeaderValue : contactHeaderSet) { newSipServletRequest.addHeaderInternal(ContactHeader.NAME, contactHeaderValue, true); } // we set up a list of contact headers to be added to the request List newContactHeaders = new ArrayList(); ListIterator contactHeaders = newRequest.getHeaders(ContactHeader.NAME); while (contactHeaders.hasNext()) { // we clone the default Mobicents Sip Servlets Contact Header ContactHeader newContactHeader = (ContactHeader) contactHeader.clone(); ContactHeader newRequestContactHeader = contactHeaders.next(); final URI newURI = newRequestContactHeader.getAddress().getURI(); newContactHeader.getAddress().setDisplayName(newRequestContactHeader.getAddress().getDisplayName()); // and reset its user part and params accoridng to 4.1.3 The Contact Header Field if(newURI instanceof SipURI) { SipURI newSipURI = (SipURI) newURI; SipURI newContactSipURI = (SipURI) newContactHeader.getAddress().getURI(); ((SipURI)newContactHeader.getAddress().getURI()).setUser(newSipURI.getUser()); Iterator uriParameters = newSipURI.getParameterNames(); while (uriParameters.hasNext()) { String parameter = uriParameters.next(); if(!CONTACT_FORBIDDEN_PARAMETER.contains(parameter)) { String value = newSipURI.getParameter(parameter); newContactSipURI.setParameter(parameter, "".equals(value) ? null : value); } } } // reset the header params according to 4.1.3 The Contact Header Field Iterator headerParameters = newRequestContactHeader.getParameterNames(); while (headerParameters.hasNext()) { String parameter = headerParameters.next(); String value = newRequestContactHeader.getParameter(parameter); newContactHeader.setParameter(parameter, "".equals(value) ? null : value); } newContactHeaders.add(newContactHeader); } // we remove the previously added contact headers newRequest.removeHeader(ContactHeader.NAME); // and set the new correct ones for (ContactHeader newContactHeader : newContactHeaders) { newRequest.addHeader(newContactHeader); } } private void stripForbiddenContactURIParams(SipURI contactURI) { Iterator uriParameters = contactURI.getParameterNames(); while (uriParameters.hasNext()) { String parameter = uriParameters.next(); if(CONTACT_FORBIDDEN_PARAMETER.contains(parameter)) { contactURI.removeParameter(parameter); uriParameters = contactURI.getParameterNames(); } } } /** * @param headerMap * @param newRequest * @return * @throws ParseException */ private final List retrieveContactHeaders( Map> headerMap, Request newRequest, ServletContext servCtx) throws ParseException { List contactHeaderList = new ArrayList(); if(headerMap != null) { for (Entry> entry : headerMap.entrySet()) { final String headerName = entry.getKey(); if(!headerName.equalsIgnoreCase(ContactHeader.NAME)) { if (B2BUA_SYSTEM_HEADERS.contains(headerName)) { String overridenRuleStr = servCtx.getInitParameter(SipServletMessageImpl.SYS_HDR_MOD_OVERRIDE); if (overridenRuleStr == null || !AddressImpl.ModifiableRule.valueOf(overridenRuleStr).equals(ModifiableRule.Modifiable)) { throw new IllegalArgumentException(headerName + " in the provided map is a system header"); } } // Fix for Issue 1002 : The header field map is then used to // override the headers in the newly created request so the header copied from the original request is removed if(entry.getValue().size() > 0) { newRequest.removeHeader(headerName); } for (String value : entry.getValue()) { final Header header = sipFactoryImpl.getHeaderFactory().createHeader( headerName, value); if(! JainSipUtils.SINGLETON_HEADER_NAMES.contains(header.getName())) { newRequest.addHeader(header); } else { newRequest.setHeader(header); } } } else { contactHeaderList = headerMap.get(headerName); } } } return contactHeaderList; } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#createResponseToOriginalRequest(javax.servlet.sip.SipSession, int, java.lang.String) */ public SipServletResponse createResponseToOriginalRequest( SipSession session, int status, String reasonPhrase) { if (session == null) { throw new NullPointerException("Null arg"); } final MobicentsSipSession sipSession = (MobicentsSipSession) session; if(!sipSession.isValidInternal()) { throw new IllegalArgumentException("sip session " + sipSession.getId() + " is invalid !"); } final MobicentsSipServletMessage sipServletMessageImpl = sipSession.getSessionCreatingTransactionRequest(); if(!(sipServletMessageImpl instanceof SipServletRequestImpl)) { throw new IllegalStateException("session creating transaction message is not a request !"); } final SipServletRequestImpl sipServletRequestImpl = (SipServletRequestImpl) sipServletMessageImpl; if(RoutingState.FINAL_RESPONSE_SENT.equals(sipServletRequestImpl.getRoutingState())) { // checked by TCK test com.bea.sipservlet.tck.agents.api.javax_servlet_sip.B2buaHelperTest.testCreateResponseToOriginalRequest101 throw new IllegalStateException("subsequent response is inconsistent with an already sent response. a Final response has already been sent for this request " + sipServletRequestImpl); } if(logger.isDebugEnabled()) { logger.debug("creating response to original request " + sipServletRequestImpl + " on session " + session); } return sipServletRequestImpl.createResponse(status, reasonPhrase); } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#getLinkedSession(javax.servlet.sip.SipSession) */ public SipSession getLinkedSession(final SipSession session) { return getLinkedSession(session, true); } public SipSession getLinkedSession(final SipSession session, boolean checkSession) { if ( session == null) { throw new NullPointerException("the argument is null"); } final MobicentsSipSession mobicentsSipSession = (MobicentsSipSession)session; if(checkSession && !mobicentsSipSession.isValidInternal()) { throw new IllegalArgumentException("the session " + mobicentsSipSession + " is invalid"); } MobicentsSipSessionKey sipSessionKey = this.sessionMap.get(mobicentsSipSession.getKey()); if(sipSessionKey == null) { dumpLinkedSessions(); if(logger.isDebugEnabled()) { logger.debug("No Linked Session found for this session " + session); } return null; } if(logger.isDebugEnabled()) { logger.debug(" trying to find linked session with key " + sipSessionKey + " for session " + mobicentsSipSession); } MobicentsSipSession linkedSession = sipManager.getSipSession(sipSessionKey, false, null, mobicentsSipSession.getSipApplicationSession()); if(logger.isDebugEnabled()) { if(linkedSession != null) { logger.debug("Linked Session found : " + linkedSession + " for this session " + session); } else { logger.debug("No Linked Session found for this session " + session); } } if(mobicentsSipSession.getParentSession() != null) { if(logger.isDebugEnabled()) { logger.debug(mobicentsSipSession + " has a parent session, it means we need to handle a forked case"); } // Issue 2354 handling of forking String linkedDerivedSessionId = derivedSessionMap.get(mobicentsSipSession.getId()); if(linkedDerivedSessionId == null) { SipServletRequestImpl originalSipServletRequestImpl = (SipServletRequestImpl) linkedSession.getSessionCreatingTransactionRequest(); String newToTag = ApplicationRoutingHeaderComposer.getHash(sipFactoryImpl.getSipApplicationDispatcher(),sipSessionKey.getApplicationName(), sipSessionKey.getApplicationSessionId()); if(logger.isDebugEnabled()) { logger.debug("derived session " + mobicentsSipSession + " has no linked forked session yet, lazily creating one with new ToTag " + newToTag); } sipSessionKey = new SipSessionKey(sipSessionKey.getFromTag(), newToTag, sipSessionKey.getCallId(), sipSessionKey.getApplicationSessionId(), sipSessionKey.getApplicationName()); linkedSession = sipManager.getSipSession(sipSessionKey, false, null, mobicentsSipSession.getSipApplicationSession()); // need to clone the original request to create the forked response SipServletRequestImpl clonedOriginalRequest = (SipServletRequestImpl)originalSipServletRequestImpl.clone(); clonedOriginalRequest.setSipSession(linkedSession); linkedSession.setSessionCreatingDialog(null); linkedSession.setSessionCreatingTransactionRequest(clonedOriginalRequest); derivedSessionMap.put(mobicentsSipSession.getId(), linkedSession.getId()); derivedSessionMap.put(linkedSession.getId(), mobicentsSipSession.getId()); } else { if(logger.isDebugEnabled()) { logger.debug("derived session " + mobicentsSipSession + " has already a linked forked session " + linkedDerivedSessionId + " reusing it"); } try { sipSessionKey = SessionManagerUtil.parseSipSessionKey(linkedDerivedSessionId); if(logger.isDebugEnabled()) { logger.debug(" trying to find derived linked session with key " + sipSessionKey + " for session " + mobicentsSipSession); } linkedSession = sipManager.getSipSession(sipSessionKey, false, null, mobicentsSipSession.getSipApplicationSession()); } catch (ParseException e) { logger.error("Couldn't parse linkedDerivedSessionId " + linkedDerivedSessionId + " linked to derived session " + mobicentsSipSession, e); } } } if(linkedSession != null) { return linkedSession.getFacade(); } else { return null; } } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#getLinkedSipServletRequest(javax.servlet.sip.SipServletRequest) */ public SipServletRequest getLinkedSipServletRequest(SipServletRequest req) { if ( req == null) { throw new NullPointerException("the argument is null"); } return originalRequestMap.get(req); } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#getPendingMessages(javax.servlet.sip.SipSession, javax.servlet.sip.UAMode) */ public List getPendingMessages(SipSession session, UAMode mode) { final MobicentsSipSession sipSessionImpl = (MobicentsSipSession) session; if(!sipSessionImpl.isValidInternal()) { throw new IllegalArgumentException("the session " + sipSessionImpl.getId() + " is invalid"); } final List retval = new ArrayList (); if (mode.equals(UAMode.UAC)) { final Set ongoingTransactions = sipSessionImpl.getOngoingTransactions(); if(ongoingTransactions != null) { for (Transaction transaction: ongoingTransactions) { if (transaction instanceof ClientTransaction) { final TransactionApplicationData tad = (TransactionApplicationData) transaction.getApplicationData(); // Issue1571 http://code.google.com/p/mobicents/issues/detail?id=1571 // NullPointerException in SipServletResponseImpl.isCommitted // race condition can occur between app code thread and processTxTerminated or Timeout thread if(tad != null) { final SipServletMessage sipServletMessage = tad.getSipServletMessage(); if(sipServletMessage != null) { //not specified if ACK is a committed message in the spec but it seems not since Proxy api test //testCancel101 method adds a header to the ACK and it cannot be on a committed message //so we don't want to return ACK as pending messages here. related to TCK test B2BUAHelper.testCreateRequest002 if (!sipServletMessage.isCommitted() && !Request.ACK.equals(sipServletMessage.getMethod()) && !Request.PRACK.equals(sipServletMessage.getMethod())) { retval.add(sipServletMessage); } final Set sipServletsResponses = tad.getSipServletResponses(); if(sipServletsResponses != null) { for(SipServletResponseImpl sipServletResponseImpl : sipServletsResponses) { if (!sipServletResponseImpl.isCommitted()) { retval.add(sipServletResponseImpl); } } } } } } } } } else { final Set ongoingTransactions = sipSessionImpl.getOngoingTransactions(); if(ongoingTransactions != null) { for (Transaction transaction: ongoingTransactions) { if (transaction instanceof ServerTransaction) { final TransactionApplicationData tad = (TransactionApplicationData) transaction.getApplicationData(); if(tad != null) { final SipServletMessage sipServletMessage = tad.getSipServletMessage(); if(sipServletMessage != null) { //not specified if ACK is a committed message in the spec but it seems not since Proxy api test //testCanacel101 method adds a header to the ACK and it cannot be on a committed message //so we don't want to return ACK as pending messages here. related to TCK test B2BUAHelper.testCreateRequest002 if (!sipServletMessage.isCommitted() && !Request.ACK.equals(sipServletMessage.getMethod())) { retval.add(sipServletMessage); } } } } } } } return retval; } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#linkSipSessions(javax.servlet.sip.SipSession, javax.servlet.sip.SipSession) */ public void linkSipSessions(SipSession session1, SipSession session2) { if ( session1 == null) { throw new NullPointerException("First argument is null"); } if ( session2 == null) { throw new NullPointerException("Second argument is null"); } if(!((MobicentsSipSession)session1).isValidInternal() || !((MobicentsSipSession)session2).isValidInternal() || State.TERMINATED.equals(((MobicentsSipSession)session1).getState()) || State.TERMINATED.equals(((MobicentsSipSession)session2).getState()) || !session1.getApplicationSession().equals(session2.getApplicationSession()) || (sessionMap.get(((MobicentsSipSession)session1).getKey()) != null && sessionMap.get(((MobicentsSipSession)session1).getKey()) != ((MobicentsSipSession)session2).getKey()) || (sessionMap.get(((MobicentsSipSession)session2).getKey()) != null && sessionMap.get(((MobicentsSipSession)session2).getKey()) != ((MobicentsSipSession)session1).getKey())) { throw new IllegalArgumentException("either of the specified sessions has been terminated " + "or the sessions do not belong to the same application session or " + "one or both the sessions are already linked with some other session(s)"); } this.sessionMap.put(((MobicentsSipSession)session1).getKey(), ((MobicentsSipSession)session2).getKey()); this.sessionMap.put(((MobicentsSipSession)session2).getKey(), ((MobicentsSipSession) session1).getKey()); // https://github.com/Mobicents/sip-servlets/issues/56 if(!this.equals(((MobicentsSipSession)session1).getB2buaHelper())) { if(((MobicentsSipSession)session1).getB2buaHelper() == null) { ((MobicentsSipSession)session1).setB2buaHelper(this); } else { Map forkedSessionMap = ((MobicentsSipSession)session1).getB2buaHelper().getSessionMap(); forkedSessionMap.put(((MobicentsSipSession)session1).getKey(), ((MobicentsSipSession)session2).getKey()); forkedSessionMap.put(((MobicentsSipSession)session2).getKey(), ((MobicentsSipSession) session1).getKey()); } } // https://github.com/Mobicents/sip-servlets/issues/56 if(!this.equals(((MobicentsSipSession)session2).getB2buaHelper())) { if(((MobicentsSipSession)session2).getB2buaHelper() == null) { ((MobicentsSipSession)session2).setB2buaHelper(this); } else { Map forkedSessionMap = ((MobicentsSipSession)session2).getB2buaHelper().getSessionMap(); forkedSessionMap.put(((MobicentsSipSession)session1).getKey(), ((MobicentsSipSession)session2).getKey()); forkedSessionMap.put(((MobicentsSipSession)session2).getKey(), ((MobicentsSipSession) session1).getKey()); } } if(logger.isDebugEnabled()) { logger.debug("sipsession " + ((MobicentsSipSession)session1).getKey() + " linked to sip session " + ((MobicentsSipSession)session2).getKey()); } dumpLinkedSessions(); } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#unlinkSipSessions(javax.servlet.sip.SipSession) */ public void unlinkSipSessions(SipSession session) { unlinkSipSessionsInternal(session, true); } /** * * @param session * @param checkSession */ public void unlinkSipSessionsInternal(SipSession session, boolean checkSession) { if ( session == null) { throw new NullPointerException("the argument is null"); } final MobicentsSipSession key = (MobicentsSipSession) session; final MobicentsSipSessionKey sipSessionKey = key.getKey(); if(checkSession) { if(!((MobicentsSipSession)session).isValidInternal() || State.TERMINATED.equals(key.getState()) || sessionMap.get(sipSessionKey) == null) { throw new IllegalArgumentException("the session is not currently linked to another session or it has been terminated"); } } final MobicentsSipSessionKey value = this.sessionMap.get(sipSessionKey); if (value != null) { SipSession linkedSipSession = getLinkedSession(session, checkSession); this.sessionMap.remove(sipSessionKey); this.sessionMap.remove(value); if(logger.isDebugEnabled()) { logger.debug("sipsession " + sipSessionKey + " unlinked from sip session " + value); } if(linkedSipSession != null) { // https://github.com/Mobicents/sip-servlets/issues/56 MobicentsB2BUAHelper linkedB2buaHelper = ((MobicentsSipSession)linkedSipSession).getB2buaHelper(); if(!linkedB2buaHelper.equals(this)) { linkedB2buaHelper.unlinkSipSessionsInternal(linkedSipSession, false); } else { linkedB2buaHelper = ((MobicentsSipSession)session).getB2buaHelper(); linkedB2buaHelper.unlinkSipSessionsInternal(linkedSipSession, false); } } } else if(logger.isDebugEnabled()) { logger.debug("no sipsession for " + sipSessionKey + " to unlink"); } final String linkedDerivedSessionId = this.derivedSessionMap.get(sipSessionKey.toString()); if (linkedDerivedSessionId != null) { SipSession linkedSipSession = getLinkedSession(session, checkSession); this.derivedSessionMap.remove(sipSessionKey.toString()); this.derivedSessionMap.remove(linkedDerivedSessionId); if(logger.isDebugEnabled()) { logger.debug("derived sipsession " + sipSessionKey.toString() + " unlinked from derived sip session " + linkedDerivedSessionId); } if(linkedSipSession != null) { // https://github.com/Mobicents/sip-servlets/issues/56 MobicentsB2BUAHelper linkedB2buaHelper = ((MobicentsSipSession)linkedSipSession).getB2buaHelper(); if(!linkedB2buaHelper.equals(this)) { linkedB2buaHelper.unlinkSipSessions(linkedSipSession); } else { linkedB2buaHelper = ((MobicentsSipSession)session).getB2buaHelper(); linkedB2buaHelper.unlinkSipSessions(linkedSipSession); } } } else if(logger.isDebugEnabled()) { logger.debug("no derived sipsession for " + sipSessionKey.toString() + " to unlink"); } unlinkOriginalRequestInternal(sipSessionKey, !checkSession); dumpLinkedSessions(); } /** * * @param session * @param checkSession */ public void unlinkOriginalRequestInternal(MobicentsSipSessionKey sipSessionKey, boolean force) { for (Entry linkedRequests : originalRequestMap.entrySet()) { SipServletRequestImpl request1 = linkedRequests.getKey(); SipServletRequestImpl request2 = linkedRequests.getValue(); if(request1 != null && request2 != null) { MobicentsSipSessionKey key1 = request1.getSipSessionKey(); MobicentsSipSessionKey key2 = request2.getSipSessionKey(); if((key1!=null&&key1.equals(sipSessionKey)) || (key2!=null&&key2.equals(sipSessionKey))) { unlinkOriginalRequestInternal(linkedRequests.getKey(), force); unlinkOriginalRequestInternal(linkedRequests.getValue(), force); } } } } /** * * @param session * @param checkSession */ public void unlinkOriginalRequestInternal(MobicentsSipServletRequest sipServletRequestImpl, boolean force) { if(sipServletRequestImpl != null) { SipServletRequestImpl linkedRequest = this.originalRequestMap.get(sipServletRequestImpl); if(sipServletRequestImpl != null) { if(linkedRequest != null) { // Issue 2419 we unlink only if both tx are in terminated state or transaction is null (which means it has already been cleaned up) Transaction transaction = sipServletRequestImpl.getTransaction(); Transaction linkedTransaction = linkedRequest.getTransaction(); if(logger.isDebugEnabled()) { logger.debug("force " + force); logger.debug("transaction " + transaction); logger.debug("Linkedtransaction " + linkedTransaction); if(transaction != null) { logger.debug("transaction state " + transaction.getState()); } if(linkedTransaction != null) { logger.debug("linked transaction state " + linkedTransaction.getState()); } } if(!Request.ACK.equalsIgnoreCase(sipServletRequestImpl.getMethod()) && (force || ((transaction == null || TransactionState.TERMINATED.equals(transaction.getState())) && (linkedTransaction == null || TransactionState.TERMINATED.equals(linkedTransaction.getState()))))) { this.originalRequestMap.remove(sipServletRequestImpl); this.originalRequestMap.remove(linkedRequest); if(logger.isDebugEnabled()) { logger.debug("following linked request " + linkedRequest + " unlinked from " + sipServletRequestImpl); } if(!force) { // in case of forceful invalidate there is no need to clean up everything here as we will use standard transaction terminated events // TCK com.bea.sipservlet.tck.apps.apitestapp.B2buaHelper.testGetPendingMessages101 and testGetLinkedSession101 // don't remove the Linked Transaction if force is null as the response on the original cannot be sent as the transaction will be null if(linkedTransaction != null) { linkedRequest.getSipSession().removeOngoingTransaction(linkedTransaction); if(linkedTransaction.getApplicationData() != null) { ((TransactionApplicationData)linkedTransaction.getApplicationData()).cleanUp(); ((TransactionApplicationData)linkedTransaction.getApplicationData()).cleanUpMessage(); } } if(transaction != null) { sipServletRequestImpl.getSipSession().removeOngoingTransaction(transaction); if(transaction.getApplicationData() != null) { ((TransactionApplicationData)transaction.getApplicationData()).cleanUp(); ((TransactionApplicationData)transaction.getApplicationData()).cleanUpMessage(); } } if(linkedRequest.getSipSession().getOngoingTransactions().isEmpty()) { linkedRequest.getSipSession().cleanDialogInformation(false); } if(sipServletRequestImpl.getSipSession().getOngoingTransactions().isEmpty()) { sipServletRequestImpl.getSipSession().cleanDialogInformation(false); } if(!force && linkedRequest.getSipSession().isValidInternal() && // https://code.google.com/p/sipservlets/issues/detail?id=279 linkedRequest.getSipSession().isReadyToInvalidateInternal()) { linkedRequest.getSipSession().onTerminatedState(); } if(sipServletRequestImpl.getSipSession().isValidInternal() && // https://code.google.com/p/sipservlets/issues/detail?id=279 sipServletRequestImpl.getSipSession().isReadyToInvalidateInternal()) { sipServletRequestImpl.getSipSession().onTerminatedState(); } } } } } } } /* * (non-Javadoc) * @see javax.servlet.sip.B2buaHelper#createRequest(javax.servlet.sip.SipServletRequest) */ public SipServletRequest createRequest(SipServletRequest origRequest) { final SipServletRequestImpl newSipServletRequest = (SipServletRequestImpl) sipFactoryImpl.createRequest(origRequest, false); final SipServletRequestImpl origRequestImpl = (SipServletRequestImpl) origRequest; final MobicentsSipSession originalSession = origRequestImpl.getSipSession(); final MobicentsSipSession session = newSipServletRequest.getSipSession(); // B2buaHelperTest.testLinkSipSessions101 assumes the sessions shouldn't be linked together // sessionMap.put(originalSession.getKey(), session.getKey()); // sessionMap.put(session.getKey(), originalSession.getKey()); // dumpLinkedSessions(); // originalRequestMap.put(newSipServletRequest, origRequestImpl); // originalRequestMap.put(origRequestImpl, newSipServletRequest); session.setB2buaHelper(this); originalSession.setB2buaHelper(this); return newSipServletRequest; } /** * {@inheritDoc} */ public SipServletRequest createCancel(SipSession session) { if(session == null) throw new NullPointerException("The session for createCancel cannot be null"); for (SipServletRequestImpl linkedRequest : originalRequestMap.keySet()) { if(linkedRequest.getSipSessionKey().equals(((MobicentsSipSession) session).getKey()) && linkedRequest.getMethod().equalsIgnoreCase(Request.INVITE) && !linkedRequest.isFinalResponseGenerated() && // Fix for Issue http://code.google.com/p/mobicents/issues/detail?id=2114 // In B2b servlet, after re-INVITE, and try to create CANCEL will get "final response already sent!" exception. linkedRequest.getLastFinalResponse() == null) { final SipServletRequestImpl sipServletRequestImpl = (SipServletRequestImpl)linkedRequest.createCancel(); ((MobicentsSipSession)sipServletRequestImpl.getSession()).setB2buaHelper(this); return sipServletRequestImpl; } } return null; } /** * @return the sipFactoryImpl */ public SipFactoryImpl getSipFactoryImpl() { return sipFactoryImpl; } /** * @param sipFactoryImpl the sipFactoryImpl to set */ public void setMobicentsSipFactory(MobicentsSipFactory sipFactoryImpl) { this.sipFactoryImpl = (SipFactoryImpl) sipFactoryImpl; } /** * @return the sipManager */ public SipManager getSipManager() { return sipManager; } /** * @param sipManager the sipManager to set */ public void setSipManager(SipManager sipManager) { this.sipManager = sipManager; } private void dumpLinkedSessions() { if(logger.isDebugEnabled()) { for (MobicentsSipSessionKey key : sessionMap.keySet()) { logger.debug(key + " tied to session " + sessionMap.get(key)); } for (String key : derivedSessionMap.keySet()) { logger.debug("forked " + key + " tied to forked session " + derivedSessionMap.get(key)); } } } /** * @param sessionMap the sessionMap to set */ public void setSessionMap(Map sessionMap) { this.sessionMap = sessionMap; } /** * @return the sessionMap */ public Map getSessionMap() { return sessionMap; } /** * @return the originalRequestMap */ public Map getOriginalRequestMap() { return originalRequestMap; } /** * @param originalRequestsMap the originalRequests to set */ public void setOriginalRequestMap(Map originalRequestsMap) { this.originalRequestMap = originalRequestsMap; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy