/*
* 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.ims.PathHeader;
import gov.nist.javax.sip.stack.SIPTransaction;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.sip.Address;
import javax.servlet.sip.AuthInfo;
import javax.servlet.sip.B2buaHelper;
import javax.servlet.sip.Parameterable;
import javax.servlet.sip.Proxy;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.URI;
import javax.servlet.sip.SipSession.State;
import javax.servlet.sip.ar.SipApplicationRouterInfo;
import javax.servlet.sip.ar.SipApplicationRoutingDirective;
import javax.servlet.sip.ar.SipApplicationRoutingRegion;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogState;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.Transaction;
import javax.sip.address.TelURL;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ProxyAuthenticateHeader;
import javax.sip.header.RecordRouteHeader;
import javax.sip.header.RouteHeader;
import javax.sip.header.SubscriptionStateHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.apache.log4j.Logger;
import org.mobicents.servlet.sip.JainSipUtils;
import org.mobicents.servlet.sip.SipFactories;
import org.mobicents.servlet.sip.address.AddressImpl;
import org.mobicents.servlet.sip.address.SipURIImpl;
import org.mobicents.servlet.sip.address.TelURLImpl;
import org.mobicents.servlet.sip.address.URIImpl;
import org.mobicents.servlet.sip.core.ApplicationRoutingHeaderComposer;
import org.mobicents.servlet.sip.core.RoutingState;
import org.mobicents.servlet.sip.core.SipNetworkInterfaceManager;
import org.mobicents.servlet.sip.core.dispatchers.MessageDispatcher;
import org.mobicents.servlet.sip.core.session.MobicentsSipApplicationSession;
import org.mobicents.servlet.sip.core.session.MobicentsSipSession;
import org.mobicents.servlet.sip.core.session.SipApplicationSessionKey;
import org.mobicents.servlet.sip.core.session.SipRequestDispatcher;
import org.mobicents.servlet.sip.proxy.ProxyImpl;
import org.mobicents.servlet.sip.security.AuthInfoEntry;
import org.mobicents.servlet.sip.security.AuthInfoImpl;
import org.mobicents.servlet.sip.security.authentication.DigestAuthenticator;
import org.mobicents.servlet.sip.startup.loading.SipServletImpl;
public class SipServletRequestImpl extends SipServletMessageImpl implements
SipServletRequest {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(SipServletRequestImpl.class);
private static final String EXCEPTION_MESSAGE = "The context does not allow you to modify this request !";
public static final Set<String> nonInitialSipRequestMethods = new HashSet<String>();
static {
nonInitialSipRequestMethods.add("CANCEL");
nonInitialSipRequestMethods.add("BYE");
nonInitialSipRequestMethods.add("PRACK");
nonInitialSipRequestMethods.add("ACK");
nonInitialSipRequestMethods.add("UPDATE");
nonInitialSipRequestMethods.add("INFO");
};
/* Linked request (for b2bua) */
private SipServletRequestImpl linkedRequest;
private boolean createDialog;
/*
* Popped route header - when we are the UAS we pop and keep the route
* header
*/
private AddressImpl poppedRoute;
private RouteHeader poppedRouteHeader;
/* Cache the application routing directive in the record route header */
private SipApplicationRoutingDirective routingDirective = SipApplicationRoutingDirective.NEW;
private RoutingState routingState;
private transient SipServletResponse lastFinalResponse;
private transient SipServletResponse lastInformationalResponse;
/**
* Routing region.
*/
private SipApplicationRoutingRegion routingRegion;
private transient URI subscriberURI;
private boolean isInitial;
private boolean isFinalResponseGenerated;
private boolean is1xxResponseGenerated;
private transient boolean isReadOnly;
public SipServletRequestImpl(Request request, SipFactoryImpl sipFactoryImpl,
MobicentsSipSession sipSession, Transaction transaction, Dialog dialog,
boolean createDialog) {
super(request, sipFactoryImpl, transaction, sipSession, dialog);
this.createDialog = createDialog;
routingState = checkRoutingState(this, dialog);
if(RoutingState.INITIAL.equals(routingState)) {
isInitial = true;
}
isFinalResponseGenerated = false;
}
@Override
public boolean isSystemHeader(String headerName) {
String hName = getFullHeaderName(headerName);
/*
* Contact is a system header field in messages other than REGISTER
* requests and responses, 3xx and 485 responses, and 200/OPTIONS
* responses so it is not contained in system headers and as such
* as a special treatment
*/
boolean isSystemHeader = JainSipUtils.systemHeaders.contains(hName);
if (isSystemHeader)
return isSystemHeader;
boolean isContactSystem = false;
Request request = (Request) this.message;
String method = request.getMethod();
if (method.equals(Request.REGISTER)) {
isContactSystem = false;
} else {
isContactSystem = true;
}
if (isContactSystem && hName.equals(ContactHeader.NAME)) {
isSystemHeader = true;
} else {
isSystemHeader = false;
}
return isSystemHeader;
}
public SipServletRequest createCancel() {
checkReadOnly();
if (!((Request) message).getMethod().equals(Request.INVITE)) {
throw new IllegalStateException(
"Cannot create CANCEL for non inivte");
}
if (super.getTransaction() == null
|| super.getTransaction() instanceof ServerTransaction)
throw new IllegalStateException("No client transaction found!");
if(RoutingState.FINAL_RESPONSE_SENT.equals(routingState) || lastFinalResponse != null) {
throw new IllegalStateException("final response already sent!");
}
try {
Request request = (Request) super.message;
String transport = JainSipUtils.findTransport(request);
SipProvider sipProvider = sipFactoryImpl.getSipNetworkInterfaceManager().findMatchingListeningPoint(transport, false).getSipProvider();
Request cancelRequest = ((ClientTransaction) getTransaction())
.createCancel();
ClientTransaction clientTransaction = sipProvider
.getNewClientTransaction(cancelRequest);
SipServletRequest newRequest = new SipServletRequestImpl(
cancelRequest, sipFactoryImpl, getSipSession(),
clientTransaction, getTransaction().getDialog(), false);
return newRequest;
} catch (SipException ex) {
throw new IllegalStateException("Could not create cancel", ex);
}
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletRequest#createResponse(int)
*/
public SipServletResponse createResponse(int statusCode) {
return createResponse(statusCode, null);
}
public SipServletResponse createResponse(final int statusCode, final String reasonPhrase) {
return createResponse(statusCode, reasonPhrase, true);
}
public SipServletResponse createResponse(final int statusCode, final String reasonPhrase, boolean validate) {
checkReadOnly();
final Transaction transaction = getTransaction();
if(RoutingState.CANCELLED.equals(routingState)) {
throw new IllegalStateException("Cannot create a response for the invite, a CANCEL has been received and the INVITE was replied with a 487!");
}
if (transaction == null
|| transaction instanceof ClientTransaction) {
if(validate) {
throw new IllegalStateException(
"Cannot create a response - not a server transaction");
}
}
try {
final Request request = transaction.getRequest();
final Response response = SipFactories.messageFactory.createResponse(
statusCode, request);
final Dialog dialog = transaction.getDialog();
if(reasonPhrase!=null) {
response.setReasonPhrase(reasonPhrase);
}
final MobicentsSipSession session = getSipSession();
final String requestMethod = getMethod();
//add a To tag for all responses except Trying (or trying too if it's a subsequent request)
if((statusCode > Response.TRYING || !isInitial()) && statusCode <= Response.SESSION_NOT_ACCEPTABLE) {
final ToHeader toHeader = (ToHeader) response
.getHeader(ToHeader.NAME);
// If we already have a to tag, dont create new
if (toHeader.getTag() == null) {
// if a dialog has already been created
// reuse local tag
if(dialog != null && dialog.getLocalTag() != null && dialog.getLocalTag().length() > 0) {
toHeader.setTag(dialog.getLocalTag());
} else if(session != null && session.getSipApplicationSession() != null) {
final SipApplicationSessionKey sipAppSessionKey = session.getSipApplicationSession().getKey();
toHeader.setTag(ApplicationRoutingHeaderComposer.getHash(sipFactoryImpl.getSipApplicationDispatcher(), session.getKey().getApplicationName(), sipAppSessionKey.getId()));
} else {
//if the sessions are null, it means it is a cancel response
toHeader.setTag(Integer.toString(new Random().nextInt(10000000)));
}
}
if (statusCode == Response.OK ) {
//Following restrictions in JSR 289 Section 4.1.3 Contact Header Field
if(!Request.REGISTER.equals(requestMethod) && !Request.OPTIONS.equals(requestMethod)) {
// Add the contact header for the dialog.
final ContactHeader contactHeader = JainSipUtils.createContactHeader(
super.sipFactoryImpl.getSipNetworkInterfaceManager(), request, null);
if(logger.isDebugEnabled()) {
logger.debug("We're adding this contact header to our new response: '" + contactHeader + ", transport=" + JainSipUtils.findTransport(request));
}
response.setHeader(contactHeader);
}
}
}
// Application Routing : Adding the recorded route headers as route headers
final ListIterator<RecordRouteHeader> recordRouteHeaders = request.getHeaders(RecordRouteHeader.NAME);
while (recordRouteHeaders.hasNext()) {
RecordRouteHeader recordRouteHeader = (RecordRouteHeader) recordRouteHeaders
.next();
RouteHeader routeHeader = SipFactories.headerFactory.createRouteHeader(recordRouteHeader.getAddress());
response.addHeader(routeHeader);
}
final SipServletResponseImpl newSipServletResponse = new SipServletResponseImpl(response, super.sipFactoryImpl,
validate ? (ServerTransaction) transaction : transaction, session, getDialog());
newSipServletResponse.setOriginalRequest(this);
if(!Request.PRACK.equals(requestMethod) && statusCode >= Response.OK &&
statusCode <= Response.SESSION_NOT_ACCEPTABLE) {
isFinalResponseGenerated = true;
}
if(statusCode >= Response.TRYING &&
statusCode < Response.OK) {
is1xxResponseGenerated = true;
}
return newSipServletResponse;
} catch (ParseException ex) {
throw new IllegalArgumentException("Bad status code" + statusCode,
ex);
}
}
public B2buaHelper getB2buaHelper() {
checkReadOnly();
final MobicentsSipSession session = getSipSession();
if (session.getProxy() != null)
throw new IllegalStateException("Proxy already present");
B2buaHelperImpl b2buaHelper = session.getB2buaHelper();
if (b2buaHelper != null)
return b2buaHelper;
try {
b2buaHelper = new B2buaHelperImpl();
b2buaHelper.setSipFactoryImpl(sipFactoryImpl);
b2buaHelper.setSipManager(session.getSipApplicationSession().getSipContext().getSipManager());
Request request = (Request) super.message;
if (this.getTransaction() != null
&& this.getTransaction().getDialog() == null
&& JainSipUtils.dialogCreatingMethods.contains(request.getMethod())) {
String transport = JainSipUtils.findTransport(request);
SipProvider sipProvider = sipFactoryImpl.getSipNetworkInterfaceManager().findMatchingListeningPoint(
transport, false).getSipProvider();
Dialog dialog = sipProvider.getNewDialog(this
.getTransaction());
session.setSessionCreatingDialog(dialog);
dialog.setApplicationData( this.transactionApplicationData);
}
if(JainSipUtils.dialogCreatingMethods.contains(request.getMethod())) {
this.createDialog = true; // flag that we want to create a dialog for outgoing request.
}
session.setB2buaHelper(b2buaHelper);
return b2buaHelper;
} catch (SipException ex) {
throw new IllegalStateException("Cannot get B2BUAHelper", ex);
}
}
public ServletInputStream getInputStream() throws IOException {
return null;
}
public int getMaxForwards() {
return ((MaxForwardsHeader) ((Request) message)
.getHeader(MaxForwardsHeader.NAME)).getMaxForwards();
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletRequest#getPoppedRoute()
*/
public Address getPoppedRoute() {
if((this.poppedRoute == null && poppedRouteHeader != null) ||
(poppedRoute != null && poppedRouteHeader != null && !poppedRoute.getAddress().equals(poppedRouteHeader.getAddress()))) {
this.poppedRoute = new AddressImpl(poppedRouteHeader.getAddress(), null, getTransaction() == null ? true : false);
}
return poppedRoute;
}
public RouteHeader getPoppedRouteHeader() {
return poppedRouteHeader;
}
/**
* Set the popped route
*
* @param routeHeader
* the popped route header to set
*/
public void setPoppedRoute(RouteHeader routeHeader) {
this.poppedRouteHeader = routeHeader;
}
/**
* {@inheritDoc}
*/
public Proxy getProxy() throws TooManyHopsException {
checkReadOnly();
final MobicentsSipSession session = getSipSession();
if (session.getB2buaHelper() != null ) throw new IllegalStateException("Cannot proxy request");
return getProxy(true);
}
/**
* {@inheritDoc}
*/
public Proxy getProxy(boolean create) throws TooManyHopsException {
checkReadOnly();
final MobicentsSipSession session = getSipSession();
if (session.getB2buaHelper() != null ) throw new IllegalStateException("Cannot proxy request");
MaxForwardsHeader mfHeader = (MaxForwardsHeader)this.message.getHeader(MaxForwardsHeader.NAME);
if(mfHeader.getMaxForwards()<=0) {
try {
this.createResponse(483, "Too many hops").send();
} catch (IOException e) {
throw new RuntimeException(e);
}
throw new TooManyHopsException();
}
// For requests like PUBLISH dialogs(sessions) do not exist, but some clients
// attempt to send them in sequence as if they support dialogs and when such a subsequent request
// comes in it gets assigned to the previous request session where the proxy is destroyed.
// In this case we must create a new proxy. And we recoginze this case by additionally checking
// if this request is initial. TODO: Consider deleting the session contents too? JSR 289 says
// the session is keyed against the headers, not against initial/non-initial...
if (create) {
boolean createNewProxy = false;
if(isInitial() && session.getProxy() != null && session.getProxy().getOriginalRequest() == null) {
createNewProxy = true;
}
if(session.getProxy() == null || createNewProxy) {
ProxyImpl proxy = new ProxyImpl(this, super.sipFactoryImpl);
session.setProxy(proxy);
}
}
return session.getProxy();
}
/**
* {@inheritDoc}
*/
public BufferedReader getReader() throws IOException {
return null;
}
/**
* {@inheritDoc}
*/
public URI getRequestURI() {
Request request = (Request) super.message;
if (request.getRequestURI() instanceof javax.sip.address.SipURI)
return new SipURIImpl((javax.sip.address.SipURI) request
.getRequestURI());
else if (request.getRequestURI() instanceof javax.sip.address.TelURL)
return new TelURLImpl((javax.sip.address.TelURL) request
.getRequestURI());
else
throw new UnsupportedOperationException("Unsupported scheme");
}
/**
* {@inheritDoc}
*/
public boolean isInitial() {
return isInitial;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#isCommitted()
*/
public boolean isCommitted() {
//the message is an incoming request for which a final response has been generated
if(getTransaction() instanceof ServerTransaction &&
(RoutingState.FINAL_RESPONSE_SENT.equals(routingState) || isFinalResponseGenerated)) {
return true;
}
//the message is an outgoing request which has been sent
if(getTransaction() instanceof ClientTransaction && this.isMessageSent) {
return true;
}
/*
if(Request.ACK.equals((((Request)message).getMethod()))) {
return true;
}*/
return false;
}
protected void checkMessageState() {
if(isMessageSent || getTransaction() instanceof ServerTransaction) {
throw new IllegalStateException("Message already sent or incoming message");
}
}
/**
* {@inheritDoc}
*/
public void pushPath(Address uri) {
checkReadOnly();
if(!Request.REGISTER.equalsIgnoreCase(((Request)message).getMethod())) {
throw new IllegalStateException("Cannot push a Path on a non REGISTER request !");
}
if(uri.getURI() instanceof TelURL) {
throw new IllegalArgumentException("Cannot push a TelUrl as a path !");
}
if (logger.isDebugEnabled())
logger.debug("Pushing path into message of value [" + uri + "]");
try {
javax.sip.header.Header p = SipFactories.headerFactory
.createHeader(PathHeader.NAME, uri.toString());
this.message.addFirst(p);
} catch (Exception e) {
logger.error("Error while pushing path [" + uri + "]");
throw new IllegalArgumentException("Error pushing path ", e);
}
}
/**
* {@inheritDoc}
*/
public void pushRoute(Address address) {
checkReadOnly();
if(address.getURI() instanceof TelURL) {
throw new IllegalArgumentException("Cannot push a TelUrl as a route !");
}
javax.sip.address.SipURI sipUri = (javax.sip.address.SipURI) ((AddressImpl) address)
.getAddress().getURI();
pushRoute(sipUri);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletRequest#pushRoute(javax.servlet.sip.SipURI)
*/
public void pushRoute(SipURI uri) {
checkReadOnly();
javax.sip.address.SipURI sipUri = ((SipURIImpl) uri).getSipURI();
sipUri.setLrParam();
pushRoute(sipUri);
}
/**
* Pushes a route header on initial request based on the jain sip sipUri given on parameter
* @param sipUri the jain sip sip uri to use to construct the route header to push on the request
*/
private void pushRoute(javax.sip.address.SipURI sipUri) {
if(isInitial()) {
if (logger.isDebugEnabled())
logger.debug("Pushing route into message of value [" + sipUri
+ "]");
sipUri.setLrParam();
try {
javax.sip.header.Header p = SipFactories.headerFactory
.createRouteHeader(SipFactories.addressFactory
.createAddress(sipUri));
this.message.addFirst(p);
} catch (SipException e) {
logger.error("Error while pushing route [" + sipUri + "]");
throw new IllegalArgumentException("Error pushing route ", e);
}
} else {
//as per JSR 289 Section 11.1.3 Pushing Route Header Field Values
// pushRoute can only be done on the initial requests.
// Subsequent requests within a dialog follow the route set.
// Any attempt to do a pushRoute on a subsequent request in a dialog
// MUST throw and IllegalStateException.
throw new IllegalStateException("Cannot push route on subsequent requests, only intial ones");
}
}
public void setMaxForwards(int n) {
checkReadOnly();
MaxForwardsHeader mfh = (MaxForwardsHeader) this.message
.getHeader(MaxForwardsHeader.NAME);
try {
if (mfh != null) {
mfh.setMaxForwards(n);
}
} catch (Exception ex) {
String s = "Error while setting max forwards";
logger.error(s, ex);
throw new IllegalArgumentException(s, ex);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletRequest#setRequestURI(javax.servlet.sip.URI)
*/
public void setRequestURI(URI uri) {
checkReadOnly();
Request request = (Request) message;
URIImpl uriImpl = (URIImpl) uri;
javax.sip.address.URI wrappedUri = uriImpl.getURI();
request.setRequestURI(wrappedUri);
//TODO look through all contacts of the user and change them depending of if STUN is enabled
//and the request is aimed to the local network or outside
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletRequest#setRoutingDirective(javax.servlet.sip.SipApplicationRoutingDirective, javax.servlet.sip.SipServletRequest)
*/
public void setRoutingDirective(SipApplicationRoutingDirective directive,
SipServletRequest origRequest) throws IllegalStateException {
checkReadOnly();
SipServletRequestImpl origRequestImpl = (SipServletRequestImpl) origRequest;
final MobicentsSipSession session = getSipSession();
//@jean.deruelle Commenting this out, why origRequestImpl.isCommitted() is needed ?
// if ((directive == SipApplicationRoutingDirective.REVERSE || directive == SipApplicationRoutingDirective.CONTINUE)
// && (!origRequestImpl.isInitial() || origRequestImpl.isCommitted())) {
// If directive is CONTINUE or REVERSE, the parameter origRequest must be an
//initial request dispatched by the container to this application,
//i.e. origRequest.isInitial() must be true
if ((directive == SipApplicationRoutingDirective.REVERSE ||
directive == SipApplicationRoutingDirective.CONTINUE)){
if(origRequestImpl == null ||
!origRequestImpl.isInitial()) {
if(logger.isDebugEnabled()) {
logger.debug("directive to set : " + directive);
logger.debug("Original Request Routing State : " + origRequestImpl.getRoutingState());
}
throw new IllegalStateException(
"Bad state -- cannot set routing directive");
} else {
//set AR State Info from previous request
session.setStateInfo(origRequestImpl.getSipSession().getStateInfo());
//needed for application composition
currentApplicationName = origRequestImpl.getCurrentApplicationName();
//linking the requests
//FIXME one request can be linked to many more as for B2BUA
origRequestImpl.setLinkedRequest(this);
setLinkedRequest(origRequestImpl);
}
} else {
//This request must be a request created in a new SipSession
//or from an initial request, and must not have been sent.
//If any one of these preconditions are not met, the method throws an IllegalStateException.
Set<Transaction> ongoingTransactions = session.getOngoingTransactions();
if(!State.INITIAL.equals(session.getState()) && ongoingTransactions != null && ongoingTransactions.size() > 0) {
if(logger.isDebugEnabled()) {
logger.debug("session state : " + session.getState());
logger.debug("numbers of ongoing transactions : " + ongoingTransactions.size());
}
throw new IllegalStateException(
"Bad state -- cannot set routing directive");
}
}
routingDirective = directive;
linkedRequest = origRequestImpl;
//@jean.deruelle Commenting this out, is this really needed ?
// RecordRouteHeader rrh = (RecordRouteHeader) request
// .getHeader(RecordRouteHeader.NAME);
// javax.sip.address.SipURI sipUri = (javax.sip.address.SipURI) rrh
// .getAddress().getURI();
//
// try {
// if (directive == SipApplicationRoutingDirective.NEW) {
// sipUri.setParameter("rd", "NEW");
// } else if (directive == SipApplicationRoutingDirective.REVERSE) {
// sipUri.setParameter("rd", "REVERSE");
// } else if (directive == SipApplicationRoutingDirective.CONTINUE) {
// sipUri.setParameter("rd", "CONTINUE");
// }
//
// } catch (Exception ex) {
// String s = "error while setting routing directive";
// logger.error(s, ex);
// throw new IllegalArgumentException(s, ex);
// }
}
/*
* (non-Javadoc)
*
* @see javax.servlet.ServletRequest#getLocalName()
*/
public String getLocalName() {
// TODO Auto-generated method stub
return null;
}
public Locale getLocale() {
// TODO Auto-generated method stub
return null;
}
public Enumeration getLocales() {
// TODO Auto-generated method stub
return null;
}
/*
* (non-Javadoc)
*
* @see javax.servlet.ServletRequest#getParameter(java.lang.String)
*/
public String getParameter(String name) {
// JSR 289 Section 5.6.1 Parameters :
// For initial requests where a preloaded Route header specified the application to be invoked, the parameters are those of the SIP or SIPS URI in that Route header.
// For initial requests where the application is invoked the parameters are those present on the request URI,
// if this is a SIP or a SIPS URI. For other URI schemes, the parameter set is undefined.
// For subsequent requests in a dialog, the parameters presented to the application are those that the application itself
// set on the Record-Route header for the initial request or response (see 10.4 Record-Route Parameters).
// These will typically be the URI parameters of the top Route header field but if the upstream SIP element is a
// "strict router" they may be returned in the request URI (see RFC 3261).
// It is the containers responsibility to recognize whether the upstream element is a strict router and determine the right parameter set accordingly.
if(this.getPoppedRoute() != null) {
return this.getPoppedRoute().getURI().getParameter(name);
} else {
return this.getRequestURI().getParameter(name);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequest#getParameterMap()
*/
public Map<String, String> getParameterMap() {
// JSR 289 Section 5.6.1 Parameters :
// For initial requests where a preloaded Route header specified the application to be invoked, the parameters are those of the SIP or SIPS URI in that Route header.
// For initial requests where the application is invoked the parameters are those present on the request URI,
// if this is a SIP or a SIPS URI. For other URI schemes, the parameter set is undefined.
// For subsequent requests in a dialog, the parameters presented to the application are those that the application itself
// set on the Record-Route header for the initial request or response (see 10.4 Record-Route Parameters).
// These will typically be the URI parameters of the top Route header field but if the upstream SIP element is a
// "strict router" they may be returned in the request URI (see RFC 3261).
// It is the containers responsibility to recognize whether the upstream element is a strict router and determine the right parameter set accordingly.
HashMap<String, String> retval = new HashMap<String, String>();
if(this.getPoppedRoute() != null) {
Iterator<String> parameterNamesIt = this.getPoppedRoute().getURI().getParameterNames();
while (parameterNamesIt.hasNext()) {
String parameterName = parameterNamesIt.next();
retval.put(parameterName, this.getPoppedRoute().getURI().getParameter(parameterName));
}
} else {
Iterator<String> parameterNamesIt = this.getRequestURI().getParameterNames();
while (parameterNamesIt.hasNext()) {
String parameterName = parameterNamesIt.next();
retval.put(parameterName, this.getRequestURI().getParameter(parameterName));
}
}
return retval;
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequest#getParameterNames()
*/
public Enumeration<String> getParameterNames() {
// JSR 289 Section 5.6.1 Parameters :
// For initial requests where a preloaded Route header specified the application to be invoked, the parameters are those of the SIP or SIPS URI in that Route header.
// For initial requests where the application is invoked the parameters are those present on the request URI,
// if this is a SIP or a SIPS URI. For other URI schemes, the parameter set is undefined.
// For subsequent requests in a dialog, the parameters presented to the application are those that the application itself
// set on the Record-Route header for the initial request or response (see 10.4 Record-Route Parameters).
// These will typically be the URI parameters of the top Route header field but if the upstream SIP element is a
// "strict router" they may be returned in the request URI (see RFC 3261).
// It is the containers responsibility to recognize whether the upstream element is a strict router and determine the right parameter set accordingly.
Vector<String> retval = new Vector<String>();
if(this.getPoppedRoute() != null) {
Iterator<String> parameterNamesIt = this.getPoppedRoute().getURI().getParameterNames();
while (parameterNamesIt.hasNext()) {
String parameterName = parameterNamesIt.next();
retval.add(parameterName);
}
} else {
Iterator<String> parameterNamesIt = this.getRequestURI().getParameterNames();
while (parameterNamesIt.hasNext()) {
String parameterName = parameterNamesIt.next();
retval.add(parameterName);
}
}
return retval.elements();
}
/*
* (non-Javadoc)
*
* @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
*/
public String[] getParameterValues(String name) {
// JSR 289 Section 5.6.1 Parameters :
// The getParameterValues method returns an array of String objects containing all the parameter values associated with a parameter name.
// The value returned from the getParameter method must always equal the first value in the array of String objects returned by getParameterValues.
// Note:Support for multi-valued parameters is defined mainly for HTTP because HTML forms may contain multi-valued parameters in form submissions.
return new String[] {getParameter(name)};
}
public String getRealPath(String arg0) {
// TODO Auto-generated method stub
return null;
}
public String getRemoteHost() {
// TODO Auto-generated method stub
return null;
}
/**
* {@inheritDoc}
*/
public RequestDispatcher getRequestDispatcher(String handler) {
SipServletImpl sipServletImpl = (SipServletImpl)
getSipSession().getSipApplicationSession().getSipContext().getChildrenMap().get(handler);
if(sipServletImpl == null) {
throw new IllegalArgumentException(handler + " is not a valid servlet name");
}
return new SipRequestDispatcher(sipServletImpl);
}
public String getScheme() {
return ((Request)message).getRequestURI().getScheme();
}
public String getServerName() {
// TODO Auto-generated method stub
return null;
}
public int getServerPort() {
// TODO Auto-generated method stub
return 0;
}
/**
* @return the routingDirective
*/
public SipApplicationRoutingDirective getRoutingDirective() {
if(!isInitial()) {
throw new IllegalStateException("the request is not initial");
}
return routingDirective;
}
/*
* (non-Javadoc)
*
* @see org.mobicents.servlet.sip.message.SipServletMessageImpl#send()
*/
@Override
public void send() {
checkReadOnly();
try {
final Request request = (Request) super.message;
final String transport = JainSipUtils.findTransport(request);
final MobicentsSipSession session = getSipSession();
final ProxyImpl proxy = session.getProxy();
final SipNetworkInterfaceManager sipNetworkInterfaceManager = sipFactoryImpl.getSipNetworkInterfaceManager();
ViaHeader viaHeader = (ViaHeader) message.getHeader(ViaHeader.NAME);
//Issue 112 fix by folsson
if(!getMethod().equalsIgnoreCase(Request.CANCEL) && viaHeader == null) {
boolean addViaHeader = false;
if(proxy == null) {
addViaHeader = true;
} else if(isInitial) {
if(proxy.getRecordRoute()) {
addViaHeader = true;
}
} else if(proxy.getFinalBranchForSubsequentRequests() != null && proxy.getFinalBranchForSubsequentRequests().getRecordRoute()) {
addViaHeader = true;
}
if(addViaHeader) {
if(logger.isDebugEnabled()) {
logger.debug("Adding via Header");
}
viaHeader = JainSipUtils.createViaHeader(
sipNetworkInterfaceManager, request, null);
message.addHeader(viaHeader);
}
}
if(logger.isDebugEnabled()) {
logger.debug("The found transport for sending request is '" + transport + "'");
}
final String requestMethod = getMethod();
if(Request.ACK.equals(requestMethod)) {
session.getSessionCreatingDialog().sendAck(request);
return;
}
//Added for initial requests only (that are not REGISTER) not for subsequent requests
if(isInitial() && !Request.REGISTER.equalsIgnoreCase(requestMethod)) {
// Additional checks for https://sip-servlets.dev.java.net/issues/show_bug.cgi?id=29
// if(session.getProxy() != null &&
// session.getProxy().getRecordRoute()) // If the app is proxying it already does that
// {
// if(logger.isDebugEnabled()) {
// logger.debug("Add a record route header for app composition ");
// }
// getSipSession().getSipApplicationSession().getSipContext().getSipManager().dumpSipSessions();
// //Add a record route header for app composition
// addAppCompositionRRHeader();
// }
final SipApplicationRouterInfo routerInfo = sipFactoryImpl.getNextInterestedApplication(this);
if(routerInfo.getNextApplicationName() != null) {
if(logger.isDebugEnabled()) {
logger.debug("routing back to the container " +
"since the following app is interested " + routerInfo.getNextApplicationName());
}
//add a route header to direct the request back to the container
//to check if there is any other apps interested in it
addInfoForRoutingBackToContainer(routerInfo, session.getSipApplicationSession().getKey().getId(), session.getKey().getApplicationName());
} else {
if(logger.isDebugEnabled()) {
logger.debug("routing outside the container " +
"since no more apps are interested.");
}
//Adding Route Header for LB if we are in a HA configuration
if(sipFactoryImpl.isUseLoadBalancer() && isInitial) {
sipFactoryImpl.addLoadBalancerRouteHeader(request);
if(logger.isDebugEnabled()) {
logger.debug("adding route to Load Balancer since we are in a HA configuration " +
" and no more apps are interested.");
}
}
}
}
if(logger.isDebugEnabled()) {
getSipSession().getSipApplicationSession().getSipContext().getSipManager().dumpSipSessions();
}
if (super.getTransaction() == null) {
final SipProvider sipProvider = sipNetworkInterfaceManager.findMatchingListeningPoint(
transport, false).getSipProvider();
ContactHeader contactHeader = (ContactHeader)request.getHeader(ContactHeader.NAME);
if(contactHeader == null) {
final FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
final javax.sip.address.URI fromUri = fromHeader.getAddress().getURI();
String fromName = null;
if(fromUri instanceof javax.sip.address.SipURI) {
fromName = ((javax.sip.address.SipURI)fromUri).getUser();
}
// Create the contact name address.
contactHeader =
JainSipUtils.createContactHeader(sipNetworkInterfaceManager, request, fromName);
request.addHeader(contactHeader);
}
if(logger.isDebugEnabled()) {
logger.debug("Getting new Client Tx for request " + request);
}
final ClientTransaction ctx = sipProvider
.getNewClientTransaction(request);
session.setSessionCreatingTransaction(ctx);
Dialog dialog = ctx.getDialog();
if(session.getProxy() != null) dialog = null;
if (dialog == null && this.createDialog) {
dialog = sipProvider.getNewDialog(ctx);
session.setSessionCreatingDialog(dialog);
if(logger.isDebugEnabled()) {
logger.debug("new Dialog for request " + request + ", ref = " + dialog);
}
}
//Keeping the transactions mapping in application data for CANCEL handling
if(linkedRequest != null) {
final Transaction linkedTransaction = linkedRequest.getTransaction();
final Dialog linkedDialog = linkedRequest.getDialog();
//keeping the client transaction in the server transaction's application data
((TransactionApplicationData)linkedTransaction.getApplicationData()).setTransaction(ctx);
if(linkedDialog != null && linkedDialog.getApplicationData() != null) {
((TransactionApplicationData)linkedDialog.getApplicationData()).setTransaction(ctx);
}
//keeping the server transaction in the client transaction's application data
this.transactionApplicationData.setTransaction(linkedTransaction);
if(dialog!= null && dialog.getApplicationData() != null) {
((TransactionApplicationData)dialog.getApplicationData()).setTransaction(linkedTransaction);
}
}
// Make the dialog point here so that when the dialog event
// comes in we can find the session quickly.
if (dialog != null) {
dialog.setApplicationData(this.transactionApplicationData);
}
// SIP Request is ALWAYS pointed to by the client tx.
// Notice that the tx appplication data is cached in the request
// copied over to the tx so it can be quickly accessed when response
// arrives.
ctx.setApplicationData(this.transactionApplicationData);
super.setTransaction(ctx);
} else if (Request.PRACK.equals(request.getMethod())) {
final SipProvider sipProvider = sipNetworkInterfaceManager.findMatchingListeningPoint(
transport, false).getSipProvider();
final ClientTransaction ctx = sipProvider.getNewClientTransaction(request);
//Keeping the transactions mapping in application data for CANCEL handling
if(linkedRequest != null) {
//keeping the client transaction in the server transaction's application data
((TransactionApplicationData)linkedRequest.getTransaction().getApplicationData()).setTransaction(ctx);
if(linkedRequest.getDialog() != null && linkedRequest.getDialog().getApplicationData() != null) {
((TransactionApplicationData)linkedRequest.getDialog().getApplicationData()).setTransaction(ctx);
}
//keeping the server transaction in the client transaction's application data
this.transactionApplicationData.setTransaction(linkedRequest.getTransaction());
if(dialog!= null && dialog.getApplicationData() != null) {
((TransactionApplicationData)dialog.getApplicationData()).setTransaction(linkedRequest.getTransaction());
}
}
// SIP Request is ALWAYS pointed to by the client tx.
// Notice that the tx appplication data is cached in the request
// copied over to the tx so it can be quickly accessed when response
// arrives.
ctx.setApplicationData(this.transactionApplicationData);
setTransaction(ctx);
}
//tells the application dispatcher to stop routing the linked request
// (in this case it would be the original request) since it has been relayed
if(linkedRequest != null &&
!SipApplicationRoutingDirective.NEW.equals(routingDirective)) {
if(!RoutingState.PROXIED.equals(linkedRequest.getRoutingState())) {
linkedRequest.setRoutingState(RoutingState.RELAYED);
}
}
session.addOngoingTransaction(getTransaction());
// Update Session state
session.updateStateOnSubsequentRequest(this, false);
if(Request.NOTIFY.equals(getMethod())) {
final SubscriptionStateHeader subscriptionStateHeader = (SubscriptionStateHeader)
getMessage().getHeader(SubscriptionStateHeader.NAME);
// RFC 3265 : If a matching NOTIFY request contains a "Subscription-State" of "active" or "pending", it creates
// a new subscription and a new dialog (unless they have already been
// created by a matching response, as described above).
if (subscriptionStateHeader != null &&
(SubscriptionStateHeader.ACTIVE.equalsIgnoreCase(subscriptionStateHeader.getState()) ||
SubscriptionStateHeader.PENDING.equalsIgnoreCase(subscriptionStateHeader.getState()))) {
session.addSubscription(this);
}
// A subscription is destroyed when a notifier sends a NOTIFY request
// with a "Subscription-State" of "terminated".
if (subscriptionStateHeader != null &&
SubscriptionStateHeader.TERMINATED.equalsIgnoreCase(subscriptionStateHeader.getState())) {
session.removeSubscription(this);
}
}
final MobicentsSipApplicationSession sipApplicationSession = session.getSipApplicationSession();
viaHeader.setParameter(MessageDispatcher.RR_PARAM_APPLICATION_NAME,
sipFactoryImpl.getSipApplicationDispatcher().getHashFromApplicationName(session.getKey().getApplicationName()));
viaHeader.setParameter(MessageDispatcher.APP_ID,
sipApplicationSession.getKey().getId());
//updating the last accessed times
session.access();
sipApplicationSession.access();
Dialog dialog = getDialog();
if(session.getProxy() != null) dialog = null;
// If dialog does not exist or has no state.
if (dialog == null || dialog.getState() == null
|| (dialog.getState() == DialogState.EARLY && !Request.PRACK.equals(requestMethod))) {
if(logger.isInfoEnabled()) {
logger.info("Sending the request " + request);
}
((ClientTransaction) super.getTransaction()).sendRequest();
} else {
// This is a subsequent (an in-dialog) request.
// we don't redirect it to the container for now
if(logger.isInfoEnabled()) {
logger.info("Sending the in dialog request " + request);
}
dialog.sendRequest((ClientTransaction) getTransaction());
}
isMessageSent = true;
} catch (Exception ex) {
throw new IllegalStateException("Error sending request",ex);
}
}
/**
* Add a route header to route back to the container
* @param applicationName the application name that was chosen by the AR to route the request
* @throws ParseException
* @throws SipException
* @throws NullPointerException
*/
public void addInfoForRoutingBackToContainer(SipApplicationRouterInfo routerInfo, String applicationSessionId, String applicationName) throws ParseException, SipException {
final Request request = (Request) super.message;
final javax.sip.address.SipURI sipURI = JainSipUtils.createRecordRouteURI(
sipFactoryImpl.getSipNetworkInterfaceManager(),
request);
sipURI.setLrParam();
sipURI.setParameter(MessageDispatcher.ROUTE_PARAM_DIRECTIVE,
routingDirective.toString());
sipURI.setParameter(MessageDispatcher.ROUTE_PARAM_PREV_APPLICATION_NAME,
applicationName);
sipURI.setParameter(MessageDispatcher.ROUTE_PARAM_PREV_APP_ID,
applicationSessionId);
final javax.sip.address.Address routeAddress =
SipFactories.addressFactory.createAddress(sipURI);
final RouteHeader routeHeader =
SipFactories.headerFactory.createRouteHeader(routeAddress);
request.addFirst(routeHeader);
// adding the application router info to avoid calling the AppRouter twice
// See Issue 791 : http://code.google.com/p/mobicents/issues/detail?id=791
final MobicentsSipSession session = getSipSession();
session.setNextSipApplicationRouterInfo(routerInfo);
}
/**
* Add a record route header for app composition
* @throws ParseException if anything goes wrong while creating the record route header
* @throws SipException
* @throws NullPointerException
*/
public void addAppCompositionRRHeader()
throws ParseException, SipException {
final Request request = (Request) super.message;
final MobicentsSipSession session = getSipSession();
javax.sip.address.SipURI sipURI = JainSipUtils.createRecordRouteURI(
sipFactoryImpl.getSipNetworkInterfaceManager(),
request);
sipURI.setParameter(MessageDispatcher.RR_PARAM_APPLICATION_NAME, session.getKey().getApplicationName());
sipURI.setLrParam();
javax.sip.address.Address recordRouteAddress =
SipFactories.addressFactory.createAddress(sipURI);
RecordRouteHeader recordRouteHeader =
SipFactories.headerFactory.createRecordRouteHeader(recordRouteAddress);
request.addFirst(recordRouteHeader);
}
public void setLinkedRequest(SipServletRequestImpl linkedRequest) {
this.linkedRequest = linkedRequest;
}
public SipServletRequestImpl getLinkedRequest() {
return this.linkedRequest;
}
/**
* @return the routingState
*/
public RoutingState getRoutingState() {
return routingState;
}
/**
* @param routingState the routingState to set
*/
public void setRoutingState(RoutingState routingState) throws IllegalStateException {
//JSR 289 Section 11.2.3 && 10.2.6
if(routingState.equals(RoutingState.CANCELLED) &&
(this.routingState.equals(RoutingState.FINAL_RESPONSE_SENT) ||
this.routingState.equals(RoutingState.PROXIED))) {
throw new IllegalStateException("Cannot cancel final response already sent!");
}
if((routingState.equals(RoutingState.FINAL_RESPONSE_SENT)||
routingState.equals(RoutingState.PROXIED)) && this.routingState.equals(RoutingState.CANCELLED)) {
throw new IllegalStateException("Cancel received and already replied with a 487!");
}
if(routingState.equals(RoutingState.SUBSEQUENT)) {
isInitial = false;
}
this.routingState = routingState;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletRequest#addAuthHeader(javax.servlet.sip.SipServletResponse, javax.servlet.sip.AuthInfo)
*/
public void addAuthHeader(SipServletResponse challengeResponse,
AuthInfo authInfo) {
checkReadOnly();
AuthInfoImpl authInfoImpl = (AuthInfoImpl) authInfo;
SipServletResponseImpl challengeResponseImpl =
(SipServletResponseImpl) challengeResponse;
Response response = (Response) challengeResponseImpl.getMessage();
// First check for WWWAuthentication headers
ListIterator authHeaderIterator =
response.getHeaders(WWWAuthenticateHeader.NAME);
while(authHeaderIterator.hasNext()) {
WWWAuthenticateHeader wwwAuthHeader =
(WWWAuthenticateHeader) authHeaderIterator.next();
// String uri = wwwAuthHeader.getParameter("uri");
AuthInfoEntry authInfoEntry = authInfoImpl.getAuthInfo(wwwAuthHeader.getRealm());
if(authInfoEntry == null) throw new SecurityException(
"Cannot add authorization header. No credentials for the following realm: " + wwwAuthHeader.getRealm());
addChallengeResponse(wwwAuthHeader,
authInfoEntry.getUserName(),
authInfoEntry.getPassword(),
this.getRequestURI().toString());
}
// Now check for Proxy-Authentication
authHeaderIterator =
response.getHeaders(ProxyAuthenticateHeader.NAME);
while(authHeaderIterator.hasNext()) {
ProxyAuthenticateHeader wwwAuthHeader =
(ProxyAuthenticateHeader) authHeaderIterator.next();
// String uri = wwwAuthHeader.getParameter("uri");
AuthInfoEntry authInfoEntry = authInfoImpl.getAuthInfo(wwwAuthHeader.getRealm());
if(authInfoEntry == null) throw new SecurityException(
"No credentials for the following realm: " + wwwAuthHeader.getRealm());
addChallengeResponse(wwwAuthHeader,
authInfoEntry.getUserName(),
authInfoEntry.getPassword(),
this.getRequestURI().toString());
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletRequest#addAuthHeader(javax.servlet.sip.SipServletResponse, java.lang.String, java.lang.String)
*/
public void addAuthHeader(SipServletResponse challengeResponse,
String username, String password) {
checkReadOnly();
SipServletResponseImpl challengeResponseImpl =
(SipServletResponseImpl) challengeResponse;
Response response = (Response) challengeResponseImpl.getMessage();
ListIterator authHeaderIterator =
response.getHeaders(WWWAuthenticateHeader.NAME);
// First
while(authHeaderIterator.hasNext()) {
WWWAuthenticateHeader wwwAuthHeader =
(WWWAuthenticateHeader) authHeaderIterator.next();
// String uri = wwwAuthHeader.getParameter("uri");
addChallengeResponse(wwwAuthHeader, username, password, this.getRequestURI().toString());
}
authHeaderIterator =
response.getHeaders(ProxyAuthenticateHeader.NAME);
while(authHeaderIterator.hasNext()) {
ProxyAuthenticateHeader wwwAuthHeader =
(ProxyAuthenticateHeader) authHeaderIterator.next();
String uri = wwwAuthHeader.getParameter("uri");
if(uri == null) uri = this.getRequestURI().toString();
addChallengeResponse(wwwAuthHeader, username, password, uri);
}
}
private void addChallengeResponse(
WWWAuthenticateHeader wwwAuthHeader,
String username,
String password,
String uri) {
AuthorizationHeader authorization = DigestAuthenticator.getAuthorizationHeader(
getMethod(),
uri,
"", // TODO: What is this entity-body?
wwwAuthHeader,
username,
password);
message.addHeader(authorization);
}
/**
* @return the finalResponse
*/
public SipServletResponse getLastFinalResponse() {
return lastFinalResponse;
}
/**
* @param finalResponse the finalResponse to set
*/
public void setResponse(SipServletResponse response) {
if(response.getStatus() >= 200 &&
(lastFinalResponse == null || lastFinalResponse.getStatus() < response.getStatus())) {
this.lastFinalResponse = response;
}
if((response.getStatus() > 100 && response.getStatus() < 200) &&
(lastInformationalResponse == null || lastInformationalResponse.getStatus() < response.getStatus())) {
this.lastInformationalResponse = response;
}
}
/**
* {@inheritDoc}
*/
public Address getInitialPoppedRoute() {
return transactionApplicationData.getInitialPoppedRoute();
}
/**
* {@inheritDoc}
*/
public SipApplicationRoutingRegion getRegion() {
return routingRegion;
}
/**
* This method allows the application to set the region that the application
* is in with respect to this SipSession
* @param routingRegion the region that the application is in
*/
public void setRoutingRegion(SipApplicationRoutingRegion routingRegion) {
this.routingRegion = routingRegion;
}
/**
* {@inheritDoc}
*/
public URI getSubscriberURI() {
return subscriberURI;
}
public void setSubscriberURI(URI uri) {
this.subscriberURI = uri;
}
/**
* Method checking whether or not the sip servlet request in parameter is initial
* according to algorithm defined in JSR289 Appendix B
* @param sipServletRequest the sip servlet request to check
* @param dialog the dialog associated with this request
* @return true if the request is initial false otherwise
*/
private static RoutingState checkRoutingState(SipServletRequestImpl sipServletRequest, Dialog dialog) {
// 2. Ongoing Transaction Detection - Employ methods of Section 17.2.3 in RFC 3261
//to see if the request matches an existing transaction.
//If it does, stop. The request is not an initial request.
if(dialog != null && DialogState.CONFIRMED.equals(dialog.getState())) {
return RoutingState.SUBSEQUENT;
}
// 3. Examine Request Method. If it is CANCEL, BYE, PRACK or ACK, stop.
//The request is not an initial request for which application selection occurs.
if(nonInitialSipRequestMethods.contains(sipServletRequest.getMethod())) {
return RoutingState.SUBSEQUENT;
}
// 4. Existing Dialog Detection - If the request has a tag in the To header field,
// the container computes the dialog identifier (as specified in section 12 of RFC 3261)
// corresponding to the request and compares it with existing dialogs.
// If it matches an existing dialog, stop. The request is not an initial request.
// The request is a subsequent request and must be routed to the application path
// associated with the existing dialog.
// If the request has a tag in the To header field,
// but the dialog identifier does not match any existing dialogs,
// the container must reject the request with a 481 (Call/Transaction Does Not Exist).
// Note: When this occurs, RFC 3261 says either the UAS has crashed or the request was misrouted.
// In the latter case, the misrouted request is best handled by rejecting the request.
// For the Sip Servlet environment, a UAS crash may mean either an application crashed
// or the container itself crashed. In either case, it is impossible to route the request
// as a subsequent request and it is inappropriate to route it as an initial request.
// Therefore, the only viable approach is to reject the request.
if(dialog != null && !DialogState.EARLY.equals(dialog.getState())) {
return RoutingState.SUBSEQUENT;
}
// 6. Detection of Requests Sent to Encoded URIs -
// Requests may be sent to a container instance addressed to a URI obtained by calling
// the encodeURI() method of a SipApplicationSession managed by this container instance.
// When a container receives such a request, stop. This request is not an initial request.
// Refer to section 15.11.1 Session Targeting and Application Selection
//for more information on how a request sent to an encoded URI is handled by the container.
//This part will be done in routeIntialRequest since this is where the Session Targeting retrieval is done
return RoutingState.INITIAL;
}
/**
* @return the is1xxResponseGenerated
*/
public boolean is1xxResponseGenerated() {
return is1xxResponseGenerated;
}
/**
* @return the isFinalResponseGenerated
*/
public boolean isFinalResponseGenerated() {
return isFinalResponseGenerated;
}
/**
* @return the lastInformationalResponse
*/
public SipServletResponse getLastInformationalResponse() {
return lastInformationalResponse;
}
public void setReadOnly(boolean isReadOnly) {
this.isReadOnly = isReadOnly;
}
protected void checkReadOnly() {
if(isReadOnly) {
throw new IllegalStateException(EXCEPTION_MESSAGE);
}
}
@Override
public void addAcceptLanguage(Locale locale) {
checkReadOnly();
super.addAcceptLanguage(locale);
}
@Override
public void addAddressHeader(String name, Address addr, boolean first)
throws IllegalArgumentException {
checkReadOnly();
super.addAddressHeader(name, addr, first);
}
@Override
public void addHeader(String name, String value) {
checkReadOnly();
super.addHeader(name, value);
}
@Override
public void addParameterableHeader(String name, Parameterable param,
boolean first) {
checkReadOnly();
super.addParameterableHeader(name, param, first);
}
@Override
public void removeAttribute(String name) {
checkReadOnly();
super.removeAttribute(name);
}
@Override
public void removeHeader(String name) {
checkReadOnly();
super.removeHeader(name);
}
@Override
public void setAcceptLanguage(Locale locale) {
checkReadOnly();
super.setAcceptLanguage(locale);
}
@Override
public void setAddressHeader(String name, Address addr) {
checkReadOnly();
super.setAddressHeader(name, addr);
}
@Override
public void setAttribute(String name, Object o) {
checkReadOnly();
super.setAttribute(name, o);
}
@Override
public void setCharacterEncoding(String enc)
throws UnsupportedEncodingException {
checkReadOnly();
super.setCharacterEncoding(enc);
}
@Override
public void setContent(Object content, String contentType)
throws UnsupportedEncodingException {
checkReadOnly();
super.setContent(content, contentType);
}
@Override
public void setContentLanguage(Locale locale) {
checkReadOnly();
super.setContentLanguage(locale);
}
@Override
public void setContentType(String type) {
checkReadOnly();
super.setContentType(type);
}
@Override
public void setExpires(int seconds) {
checkReadOnly();
super.setExpires(seconds);
}
@Override
public void setHeader(String name, String value) {
checkReadOnly();
super.setHeader(name, value);
}
@Override
public void setHeaderForm(HeaderForm form) {
checkReadOnly();
super.setHeaderForm(form);
}
@Override
public void setParameterableHeader(String name, Parameterable param) {
checkReadOnly();
super.setParameterableHeader(name, param);
}
/**
* {@inheritDoc}
*/
public String getInitialRemoteAddr() {
if(((SIPTransaction)getTransaction()).getPeerPacketSourceAddress() != null) {
return ((SIPTransaction)getTransaction()).getPeerPacketSourceAddress().getHostAddress();
} else {
return ((SIPTransaction)getTransaction()).getPeerAddress();
}
}
/**
* {@inheritDoc}
*/
public int getInitialRemotePort() {
if(((SIPTransaction)getTransaction()).getPeerPacketSourceAddress() != null) {
return ((SIPTransaction)getTransaction()).getPeerPacketSourcePort();
} else {
return ((SIPTransaction)getTransaction()).getPeerPort();
}
}
/**
* {@inheritDoc}
*/
public String getInitialTransport() {
return ((SIPTransaction)getTransaction()).getTransport();
}
}