/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip;
import com.ericsson.ssa.config.LayerHandler;
import com.ericsson.ssa.sip.B2buaHelperImpl.TempSession;
import com.ericsson.ssa.sip.PathNode.Type;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.security.auth.impl.AuthHeaderProcessor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.sip.Address;
import javax.servlet.sip.Proxy;
import javax.servlet.sip.ProxyBranch;
import javax.servlet.sip.Rel100Exception;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.URI;
import org.jvnet.glassfish.comms.util.LogUtil;
/**
* @author ekrigro
*
*/
public class SipServletResponseImpl extends SipServletMessageImpl
implements SipServletResponse, Serializable {
/**
* Comment for <code>serialVersionUID</code>
*/
public static final String CLIENT_CERT = "javax.servlet.response.X509Certificate";
private static final long serialVersionUID = 3256728389854769457L;
public static final Map<Integer, String> REASON_PHRASE_MAP = new HashMap<Integer, String>(65);
private static final Logger _logger = LogUtil.SIP_LOGGER.getLogger();
static {
REASON_PHRASE_MAP.put(new Integer(100), "Trying");
REASON_PHRASE_MAP.put(new Integer(180), "Ringing");
REASON_PHRASE_MAP.put(new Integer(181), "Call Is Being Forwarded");
REASON_PHRASE_MAP.put(new Integer(182), "Queued");
REASON_PHRASE_MAP.put(new Integer(183), "Session Progress");
REASON_PHRASE_MAP.put(new Integer(200), "OK");
REASON_PHRASE_MAP.put(new Integer(202), "Accepted");
REASON_PHRASE_MAP.put(new Integer(300), "Multiple Choices");
REASON_PHRASE_MAP.put(new Integer(301), "Moved Permanently");
REASON_PHRASE_MAP.put(new Integer(302), "Moved Temporarily");
REASON_PHRASE_MAP.put(new Integer(305), "Use Proxy");
REASON_PHRASE_MAP.put(new Integer(380), "Alternative Service");
REASON_PHRASE_MAP.put(new Integer(400), "Bad Request");
REASON_PHRASE_MAP.put(new Integer(401), "Unauthorized");
REASON_PHRASE_MAP.put(new Integer(402), "Payment Required");
REASON_PHRASE_MAP.put(new Integer(403), "Forbidden");
REASON_PHRASE_MAP.put(new Integer(404), "Not Found");
REASON_PHRASE_MAP.put(new Integer(405), "Method Not Allowed");
REASON_PHRASE_MAP.put(new Integer(406), "Not Acceptable");
REASON_PHRASE_MAP.put(new Integer(407), "Proxy Authentication Required");
REASON_PHRASE_MAP.put(new Integer(408), "Request Timeout");
REASON_PHRASE_MAP.put(new Integer(410), "Gone");
REASON_PHRASE_MAP.put(new Integer(412), "Conditional Request Failed");
REASON_PHRASE_MAP.put(new Integer(413), "Request Entity Too Large");
REASON_PHRASE_MAP.put(new Integer(414), "Request-URI Too Long");
REASON_PHRASE_MAP.put(new Integer(415), "Unsupported Media Type");
REASON_PHRASE_MAP.put(new Integer(416), "Unsupported URI Scheme");
REASON_PHRASE_MAP.put(new Integer(420), "Bad Extension");
REASON_PHRASE_MAP.put(new Integer(421), "Extension Required");
REASON_PHRASE_MAP.put(new Integer(422), "Session Interval Too Small");
REASON_PHRASE_MAP.put(new Integer(423), "Interval Too Brief");
REASON_PHRASE_MAP.put(new Integer(428), "Use Identity Header");
REASON_PHRASE_MAP.put(new Integer(429), "Provide Referrer Identity");
REASON_PHRASE_MAP.put(new Integer(436), "Bad Identity-Info");
REASON_PHRASE_MAP.put(new Integer(437), "Unsupported Certificate");
REASON_PHRASE_MAP.put(new Integer(438), "Invalid Identity Header");
REASON_PHRASE_MAP.put(new Integer(480), "Temporarily Unavailable");
REASON_PHRASE_MAP.put(new Integer(481),
"Call/Transaction Does Not Exist");
REASON_PHRASE_MAP.put(new Integer(482), "Loop Detected");
REASON_PHRASE_MAP.put(new Integer(483), "Too Many Hops");
REASON_PHRASE_MAP.put(new Integer(484), "Address Incomplete");
REASON_PHRASE_MAP.put(new Integer(485), "Ambiguous");
REASON_PHRASE_MAP.put(new Integer(486), "Busy Here");
REASON_PHRASE_MAP.put(new Integer(487), "Request Terminated");
REASON_PHRASE_MAP.put(new Integer(488), "Not Acceptable Here");
REASON_PHRASE_MAP.put(new Integer(489), "Bad Event");
REASON_PHRASE_MAP.put(new Integer(491), "Request Pending");
REASON_PHRASE_MAP.put(new Integer(493), "Undecipherable");
REASON_PHRASE_MAP.put(new Integer(494), "Security Agreement Required");
REASON_PHRASE_MAP.put(new Integer(500), "Server Internal Error");
REASON_PHRASE_MAP.put(new Integer(501), "Not Implemented");
REASON_PHRASE_MAP.put(new Integer(502), "Bad Gateway");
REASON_PHRASE_MAP.put(new Integer(503), "Service Unavailable");
REASON_PHRASE_MAP.put(new Integer(504), "Server Time-out");
REASON_PHRASE_MAP.put(new Integer(505), "Version Not Supported");
REASON_PHRASE_MAP.put(new Integer(513), "Message Too Large");
REASON_PHRASE_MAP.put(new Integer(580), "Precondition Failure");
REASON_PHRASE_MAP.put(new Integer(600), "Busy Everywhere");
REASON_PHRASE_MAP.put(new Integer(603), "Decline");
REASON_PHRASE_MAP.put(new Integer(604), "Does Not Exist Anywhere");
REASON_PHRASE_MAP.put(new Integer(606), "Not Acceptable");
}
private AuthHeaderProcessor ahp = null;
private List<String> values = null;
private List<String> pvalues = null;
private int _statusCode;
private String _phrase;
private SipServletRequestImpl _currentRequest;
protected List<Dispatcher> _retransmission_transactionStack = null;
private boolean _isAlreadyACKGenerated = false;
private boolean _isAlreadyPRACKGenerated = false;
private boolean _responseSent = false;
private boolean incoming = false;
private boolean isBranchResponse = false;
/**
* Indicates if the Response is to be sent reliable
*
*/
private boolean _reliableProvisionalResponse = false;
private PathNode _current = null;
private PathNode _previous = null;
Header _viaOfCancel = null;
private TempSession _sessionToLink = null;
// Redirection 3xx
private boolean _alreadyRedirected = false;
private ByteBuffer _bytebuffer = null;
private boolean needSerialization = false;
private transient boolean internalTransportFailure = false;
public SipServletResponseImpl() {
super();
_messageType = SipMessageType.SipResponse;
}
public SipServletResponseImpl(SipServletRequestImpl originalRequest,
String protocol, int code) {
super(protocol);
_messageType = SipMessageType.SipResponse;
_statusCode = code;
_phrase = REASON_PHRASE_MAP.get(new Integer(code));
_currentRequest = originalRequest;
if (_currentRequest != null) {
_currentRequest.setResponseStatusCode(code);
}
// If phrase is null, use default phrase.
if (_phrase == null) { // A range check is already preformed.
int defaultCode = (code / 100);
switch (defaultCode) {
case 1:
_phrase = REASON_PHRASE_MAP.get(new Integer(100));
break;
case 2:
_phrase = REASON_PHRASE_MAP.get(new Integer(200));
break;
case 3:
_phrase = REASON_PHRASE_MAP.get(new Integer(300));
break;
case 4:
_phrase = REASON_PHRASE_MAP.get(new Integer(400));
break;
case 5:
_phrase = REASON_PHRASE_MAP.get(new Integer(500));
break;
case 6:
_phrase = REASON_PHRASE_MAP.get(new Integer(600));
break;
default:
_phrase = REASON_PHRASE_MAP.get(new Integer(500));
}
}
}
public SipServletResponseImpl(SipServletRequestImpl originalRequest,
String protocol, int code, String phrase) {
super(protocol);
_messageType = SipMessageType.SipResponse;
_statusCode = code;
_phrase = phrase;
_currentRequest = originalRequest;
if (_currentRequest != null)
_currentRequest.setResponseStatusCode(code);
}
private void writeObject(ObjectOutputStream stream)
throws IOException {
try {
stream.defaultWriteObject();
} catch (Exception ignore) {
}
}
private void readObject(ObjectInputStream stream) throws IOException {
try {
stream.defaultReadObject();
_transactionStack = new ArrayList<Dispatcher>();
_applicationStack = new ArrayList<Dispatcher>();
} catch (Exception ignore) {
}
}
/**
* Returns true if it is a redirect 3xx response that already has been
* redirected at least one of its contacts.
*/
public boolean isAlreadyRedirected() {
return _alreadyRedirected;
}
/**
* Only possible to set this to true if it is a redirect 3xx response
*/
public void setAlreadyRedirected() {
if ((getStatus() / 100) == 3) {
_alreadyRedirected = true;
}
}
public SipServletRequest getRequest() {
return getRequestImpl();
}
public SipServletRequestImpl getRequestImpl() {
return _currentRequest;
}
/**
* Enables the container to update the associated request of this response
* before it is delivered to a servlet
*
* @param request
*/
public void setRequest(SipServletRequestImpl request) {
_currentRequest = request;
if (_currentRequest != null)
_currentRequest.setResponseStatusCode(_statusCode);
}
// the specialized getMethod() below was introduced when fixing TR B0007
/**
* Overrides the definition of getMethod() in SipServletMessageImpl. The
* idea is that we should never return an empty string. If it can be proved
* that the _method field of the superclass always gets set then we would
* not need to override it here, but it appears that the constructors of
* SipServletResponseImpl really don't set the _method.
*/
public String getMethod() {
String s = super.getMethod();
if (!s.equals("")) {
return s;
} else if (_currentRequest != null) {
return _currentRequest.getMethod();
}
return "";
}
public int getStatus() {
return _statusCode;
}
public void setStatus(int code) {
if (!(code >= 100 && code <= 699))
throw new IllegalArgumentException("Code is not allowed SIP status code");
_statusCode = code;
if (_currentRequest != null)
_currentRequest.setResponseStatusCode(code);
_phrase = REASON_PHRASE_MAP.get(new Integer(code));
}
public void setStatus(int code, String phrase) {
if (!(code >= 100 && code <= 699))
throw new IllegalArgumentException("Code is not allowed SIP status code");
_statusCode = code;
if (_currentRequest != null)
_currentRequest.setResponseStatusCode(code);
_phrase = phrase;
}
public String getReasonPhrase() {
return _phrase;
}
public ServletOutputStream getOutputStream() throws IOException {
// TODO Auto-generated method stub
return null;
}
public PrintWriter getWriter() throws IOException {
// TODO Auto-generated method stub
return null;
}
public Proxy getProxy() {
Proxy p = null;
try {
p = getRequest().getProxy();
} catch (TooManyHopsException e) {
}
return p;
}
public void setNeedSerialization(boolean needserialization) {
needSerialization = needserialization;
}
public boolean getNeedSerialization() {
return needSerialization;
}
public boolean isSerialized() {
return (_bytebuffer != null);
}
public void sendReliably() throws Rel100Exception {
// 1. Check that the associated request is "INVITE"
if (getRequestImpl().getMethod().equals("INVITE") == false) {
throw new Rel100Exception(Rel100Exception.NOT_INVITE);
}
// 2. Check that response is a 1xx response
if ((getStatus() < 101) || (getStatus() > 199)) {
throw new Rel100Exception(Rel100Exception.NOT_1XX);
}
// Check that the UAC Support or Require the provisional
// response to be sent reliable, i.e. The Request has
// the header Require or Supported with the value "100rel"
boolean rel100supported = false;
ListIterator lIterator = getRequestImpl()
.getHeaders("Require");
while (lIterator.hasNext()) {
String headerValue = (String) lIterator.next();
if (headerValue.equalsIgnoreCase("100rel")) {
rel100supported = true;
}
}
lIterator = getRequestImpl().getHeaders("Supported");
while (lIterator.hasNext()) {
String headerValue = (String) lIterator.next();
if (headerValue.equalsIgnoreCase("100rel")) {
rel100supported = true;
}
}
if (!rel100supported) {
throw new Rel100Exception(Rel100Exception.NO_REQ_SUPPORT);
}
// 3.Initialize a flag 1xx Reliable response ongoing in the SipSession
// and specify if the response contain an SDP
//
// Only one at a time is permitted (Cf IP on CR38 PRACK)
if (!getSessionImpl()
.set1xxReliableOngoing((getContentLength() == 0) ? false
: true)) {
// There is an ongoing 1xx Reliable response
throw new Rel100Exception(Rel100Exception.NOT_SUPPORTED);
}
// All Error checks completed, send the response
// Indicate that this response is reliable
setReliableProvisionalResponse(true);
ListIterator<String> requireTags = getHeaders(Header.REQUIRE);
if (requireTags.hasNext()) {
boolean app100rel = false;
do {
if (requireTags.next().equals(SipFactoryImpl.SUPPORTED_100REL)) {
app100rel = true;
break;
}
} while (requireTags.hasNext());
if (!app100rel) {
requireTags.add(SipFactoryImpl.SUPPORTED_100REL);
}
} else {
setPrettyHeader(Header.REQUIRE, SipFactoryImpl.SUPPORTED_100REL);
}
Header rseq = new SingleLineHeader(Header.RSEQ, true);
rseq.setValue(Integer.toString(getRequestImpl().getAndIncrementRSeq()),
true);
setHeader(rseq);
int status = getStatus();
if (status >= 200 && status <= 299) {
SipServletRequestImpl reqImpl = this.getRequestImpl();
reqImpl.addAuthInfoHeader(this);
}
try {
sendInternal();
} catch (IOException e) {
getSessionImpl().reset1xxReliable();
throw new IllegalStateException("Problem while sending response.", e);
}
}
/**
* Check if this SipServletResponseImpl is originally created in this
* container
*
* @return true if originally created here, false otherwise
*/
private boolean isLocallyCreated() {
// No to tag in dialog == no dialog is set up yet == response created
// here
if ((getDialog() == null) || (getDialog().getToTag() == null)) {
return true;
}
// For existing dialogs: getLocalParty() == From header in locally
// created requests
// These should differ for locally created responses.
return !(_currentRequest.getFromImpl()
.equals(getSessionImpl().getLocalParty()));
}
public SipServletRequest createAck() throws IllegalStateException {
/*
* SSA1.0 tell us: Throws: java.lang.IllegalStateException - if the
* transaction state is such that it doesn't allow an ACK to be sent
* now, e.g. if the original request was not an INVITE, if this response
* is provisional only, or if an ACK has already been generated
*/
if (_currentRequest.getMethod().equals("INVITE") &&
(_statusCode > 199) && !_isAlreadyACKGenerated &&
!isLocallyCreated()) {
// Indicate that we are to send an ACK.
_isAlreadyACKGenerated = true;
return createAckImpl();
} else {
throw new IllegalStateException(
"Not allowed to create an ACK at this transaction state.");
}
}
public SipServletRequest createPrack() throws Rel100Exception {
if (_currentRequest.getMethod().equals("INVITE") &&
((_statusCode > 100) && (_statusCode < 200)) &&
!_isAlreadyPRACKGenerated && !isLocallyCreated()) {
if (getHeader(Header.RSEQ) == null) {
throw new Rel100Exception(Rel100Exception.NOT_1XX);
}
// Indicate that we are to send an PRACK.
_isAlreadyPRACKGenerated = true;
return getSession().createRequest("PRACK");
} else if ((_statusCode < 101) || (_statusCode > 199)) {
throw new Rel100Exception(Rel100Exception.NOT_1XX);
}
if (!_currentRequest.getMethod().equals("INVITE")) {
throw new Rel100Exception(Rel100Exception.NOT_INVITE);
}
throw new IllegalStateException(
"Not allowed to create an PRACK at this transaction state.");
}
public SipServletRequestImpl createHopAckImpl(SipServletRequestImpl req)
throws IllegalStateException {
/*
* SSA1.0 tell us: Throws: java.lang.IllegalStateException - if the
* transaction state is such that it doesn't allow an ACK to be sent
* now, e.g. if the original request was not an INVITE, if this response
* is provisional only, or if an ACK has already been generated
*/
if (_currentRequest.getMethod().equals("INVITE") &&
(_statusCode > 199) && !_isAlreadyACKGenerated) {
// Indicate that we are to send an ACK.
_isAlreadyACKGenerated = true;
Address fromCopy = (Address) ((AddressImpl) getFromImpl()).clone(true,
true);
Address toCopy = (Address) ((AddressImpl) getToImpl()).clone(true,
true);
SipServletRequestImpl ack = new SipServletRequestImpl("ACK",
req.getRequestURI(), SipFactoryImpl.PROTOCOL_LINE);
// set To
Header toHeader = new SingleLineHeader(Header.TO, true);
toHeader.setAddressValue(toCopy, false);
ack.setHeader(toHeader);
// set From
Header fromHeader = new SingleLineHeader(Header.FROM, true);
fromHeader.setAddressValue(fromCopy, false);
ack.setHeader(fromHeader);
// set Max-Forwards
Header maxForwardsHeader = new SingleLineHeader(Header.MAX_FORWARDS,
false);
maxForwardsHeader.setValue("70", false);
ack.setHeader(maxForwardsHeader);
// set CallID
Header callIDHeader = new SingleLineHeader(Header.CALL_ID, true);
callIDHeader.setValue(getCallId(), false);
ack.setHeader(callIDHeader);
// set CSeq
Header cSeqHeader = new SingleLineHeader(Header.CSEQ, true);
cSeqHeader.setValue(Integer.toString(getCSeqNumber()) + " ACK",
false);
ack.setHeader(cSeqHeader);
// Get the top VIA of the request
ViaImpl v = new ViaImpl(req.getHeader(Header.VIA));
Header via = new MultiLineHeader(Header.VIA, true);
via.setValue(v.toString(), true);
ack.setHeader(via);
// the hopack shall follow the same path as the request
// (hopByHop) and is never resolved by ResolverManager
// so we must assign remote target here
ack.setRemote(req.getRemote());
// subsequent request
ack.setInitial(false);
// add Routes
Header rawRoute = req.getRawHeader(Header.ROUTE);
if (rawRoute != null) {
Header route = (Header) rawRoute.clone();
ack.setHeader(route);
}
return ack;
} else {
throw new IllegalStateException(
"Not allowed to create an ACK in this transaction state.");
}
}
public SipServletRequestImpl createAckImpl() throws IllegalStateException {
Address fromCopy = (Address) ((AddressImpl) getFromImpl()).clone(true,
true);
Address toCopy = (Address) ((AddressImpl) getToImpl()).clone(true, true);
DialogFragment df = getDialog();
UA uac = null;
if (isDirection() == Type.Caller) {
// need to fetch the uac from the first item of the application path
uac = (UA) df.getFirst();
} else if (isDirection() == Type.Callee) {
// need to fetch the uac from the last item of the application
// path
uac = (UA) df.getLast();
} else {
throw new IllegalStateException(
"Don't know the direction of the flow.");
}
URI remoteTarget;
remoteTarget = findRemoteTarget(uac);
SipServletRequestImpl req = new SipServletRequestImpl("ACK",
remoteTarget, SipFactoryImpl.PROTOCOL_LINE);
// set To
Header toHeader = new SingleLineHeader(Header.TO, true);
toHeader.setAddressValue(toCopy, false);
req.setHeader(toHeader);
// set From
Header fromHeader = new SingleLineHeader(Header.FROM, true);
fromHeader.setAddressValue(fromCopy, false);
req.setHeader(fromHeader);
// set Max-Forwards
Header maxForwardsHeader = new SingleLineHeader(Header.MAX_FORWARDS,
false);
maxForwardsHeader.setValue("70", false);
req.setHeader(maxForwardsHeader);
// set CallID
Header callIDHeader = new SingleLineHeader(Header.CALL_ID, true);
callIDHeader.setValue(getCallId(), false);
req.setHeader(callIDHeader);
// set CSeq
Header cSeqHeader = new SingleLineHeader(Header.CSEQ, true);
cSeqHeader.setValue(Integer.toString(getCSeqNumber()) + " ACK", false);
req.setHeader(cSeqHeader);
// update new request with session...
req.setSession(getSessionImpl());
// ...and dialog
req.setDialog(getDialog());
// find out direction
req.setDirection(isDirection());
// subsequent request
req.setInitial(false);
// Fetch the Application stack
List<Layer> layers = LayerHandler.getInstance().getLayers();
req._applicationStack.addAll(layers);
return req;
}
/**
* Finds the remote target for this object.
*
* @param uac
* @return A clone or the remote target object.
*/
private URI findRemoteTarget(UA uac) {
URI remoteTarget;
remoteTarget = uac.getRemoteTarget();
if (remoteTarget == null) {
try {
Address contact = getAddressHeaderImpl(Header.CONTACT);
if (contact != null) {
remoteTarget = getAddressHeaderImpl(Header.CONTACT).getURI();
if (remoteTarget == null) {
throw new IllegalStateException(
"Unable to fetch remote target from Contact header");
}
} else {
throw new IllegalStateException(
"Missing Contact header field");
}
} catch (ServletParseException e) {
throw new IllegalStateException(e);
}
}
return (URI) remoteTarget.clone();
}
public void setCharacterEncoding(String enc) {
_setCharacterEncoding(enc);
}
public void setBufferSize(int arg0) {
// TODO Auto-generated method stub
}
public int getBufferSize() {
// TODO Auto-generated method stub
return 0;
}
public void flushBuffer() throws IOException {
// TODO Auto-generated method stub
}
public void resetBuffer() {
// TODO Auto-generated method stub
}
public void reset() {
// TODO Auto-generated method stub
}
public void setLocale(Locale arg0) {
// TODO Auto-generated method stub
}
public Locale getLocale() {
// TODO Auto-generated method stub
return null;
}
public void restoreRetransmissionTransactionStack() {
_transactionStack = new ArrayList<Dispatcher>(_retransmission_transactionStack);
}
public void serializeForTransmission() throws UnsupportedEncodingException {
//_currentRequest = null;
_applicationStack.clear();
_retransmission_transactionStack = new ArrayList<Dispatcher>(_transactionStack);
}
public void serializeForReTransmission(int buffersize) throws UnsupportedEncodingException {
synchronized (byteBufferLock){
_currentRequest = null;
// we do not need to serialize the content
if (_bytebuffer == null) {
// TODO retrieve value from domain.xml send-buffer-size-in-bytes
_bytebuffer =
ByteBuffer.allocate(buffersize);
_bytebuffer.clear();
super.toBuffer(_bytebuffer);
_bytebuffer.flip();
}
// trim some data to be GC'd ASAP
_content_enc = null;
_content_byte = null;
attrib = null;
systemAttrib = null;
headerMap = null;
_roles = null;
// keep _remote = _remote;
// keep _local = _local;
_Type = null;
_sf = null;
}
}
public void copyTransactionStack() {
if (_currentRequest != null) {
_transactionStack.addAll(_currentRequest._transactionStack);
}
}
public Object clone() {
SipServletResponseImpl clone = new SipServletResponseImpl(_currentRequest,
getProtocol(), getStatus(), getReasonPhrase());
Iterator<Header> i = headerMap.values().iterator();
// deep copy
while (i.hasNext()) {
Header next = i.next();
clone.headerMap.put(next.getName(), (Header) next.clone());
}
// Fix for issue#955, application attributes shall not be cloned
// Note that when using SipRequestDispatcher.forward() the request
// is not cloned and thus the attributes are available for the servlet
// to which the response is forwarded.
if (systemAttrib != null) {
clone.systemAttrib = new HashMap<String, Object>(systemAttrib.size());
clone.systemAttrib.putAll(systemAttrib);
}
// shallow copy
clone._reliableProvisionalResponse = _reliableProvisionalResponse;
clone._content_enc = _content_enc;
clone._content_byte = _content_byte;
clone._content_obj = _content_obj;
clone._roles = _roles;
clone._initialRemote = _initialRemote;
clone._remoteHop = _remoteHop;
clone._remote = _remote;
clone._local = _local;
clone._session = _session;
clone._dialog = _dialog;
clone._method = _method;
clone._sf = _sf;
clone._headersComplete = _headersComplete;
clone._messageComplete = _messageComplete;
clone._current = _current;
clone._previous = _previous;
clone._viaOfCancel = _viaOfCancel;
clone._IsContactIndicated = _IsContactIndicated;
clone.oi = oi;
// find out direction
clone.setDirection(isDirection());
//Set bekey
clone._beKey = _beKey;
// copy transaction stack (omit application stack)
if (_transactionStack != null) {
clone._transactionStack.addAll(_transactionStack);
}
return clone;
}
private String toString(ByteBuffer bb){
StringBuilder sb = new StringBuilder();
if (_bytebuffer != null) {
try {
int l = _bytebuffer.limit();
int p = _bytebuffer.position();
for (int i = 0; i < _bytebuffer.limit(); i++){
sb.append((char)_bytebuffer.get(i));
}
_bytebuffer.limit(l);
_bytebuffer.position(p);
} catch (Exception e) {
// Build string from parts instead.
}
}
return sb.toString();
}
public String toString() {
if (_bytebuffer != null){
return toString(_bytebuffer);
}
StringBuilder sb = new StringBuilder(SipFactoryImpl.PROTOCOL_LINE);
sb.append(' ');
sb.append(_statusCode);
sb.append(' ');
sb.append(_phrase);
sb.append(SipFactoryImpl.NEW_LINE);
HashMap<String, Header> clonedHeaders = headerMap;
if (headerForm == HeaderForm.COMPACT) {
clonedHeaders = new HashMap<String, Header>(headerMap);
for (String s : Header.LONG_TO_SHORT_MAP.keySet()) {
if (clonedHeaders.containsKey(s)) {
Header h = clonedHeaders.get(s);
h.setName(Header.LONG_TO_SHORT_MAP.get(s));
}
}
}
Iterator<Header> i = clonedHeaders.values().iterator();
while (i.hasNext()) {
sb.append(i.next());
}
sb.append(SipFactoryImpl.NEW_LINE);
if ((_content_byte != null) && (_content_byte.length > 0)) {
sb.append(new String(_content_byte));
}
return sb.toString();
}
ByteBuffer toBufferFirstline(ByteBuffer bb)
throws UnsupportedEncodingException {
bb.put(SipFactoryImpl.PROTOCOL_LINE.getBytes());
bb.put((byte) 0x20);
bb.put(String.valueOf(_statusCode).getBytes());
bb.put((byte) 0x20);
if (_phrase == null)
_phrase = REASON_PHRASE_MAP.get(new Integer(_statusCode));
bb.put(_phrase.getBytes(SipFactoryImpl.SIP_CHARSET));
bb.put(SipFactoryImpl.NEW_LINE.getBytes());
return bb;
}
@Override
public boolean toBufferHasRemaining() {
synchronized (byteBufferLock){
if (_bytebuffer != null) {
return false;
}
}
return super.toBufferHasRemaining();
}
@Override
public ByteBuffer toBuffer(ByteBuffer bb)
throws UnsupportedEncodingException {
synchronized (byteBufferLock){
if (_bytebuffer != null) {
bb.put(_bytebuffer);
_bytebuffer.flip();
return bb;
}
}
return super.toBuffer(bb);
}
public String toDebugString() {
if (_bytebuffer != null) {
return toString(_bytebuffer);
}
StringBuilder sb = new StringBuilder(Integer.toString(_statusCode));
sb.append(" ");
sb.append(_phrase);
sb.append(" ");
sb.append(super.toDebugString());
return sb.toString();
}
/**
* This method is added as a fix for issue 1816.
*/
public void sendError() {
if (getSessionImpl() == null) {
/**
* If the container sends the error response before the session
* is setup, it should directly dispatch it via the next layers.
*/
Dispatcher dispatcher = popDispatcher();
if (dispatcher != null) {
/**
* TODO :: Should we submit dispatching to SipContainerThreadPool to run on a different thread?
*/
dispatcher.dispatch(this);
} else {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("Dispatcher is null. Unable to dispatch this " +
"response :\n " + this);
}
}
} else {
/**
* If the container sends the error response after the session
* is setup, then it should do a normal send() so that the SipSession
* state updation, IWR, etc take place correctly.
*/
try {
send();
} catch (Exception ex) {
if (_logger.isLoggable(Level.WARNING)) {
_logger.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
}
public void send() throws IOException {
validateMessageCommitted();
int status = getStatus();
if (status >= 200 && status <= 299) {
SipServletRequestImpl reqImpl = this.getRequestImpl();
reqImpl.addAuthInfoHeader(this);
}
if (SipFactoryImpl.getContactRequirement(this)
!= HeaderRequirement.NOT_APPLICAPLE) {
if (SipFactoryImpl.contactAllowedFromServlet(this)) {
try {
if (getAddressHeaderImpl("Contact") == null) {
// if not REGISTER or if REGISTER and status is not 2XX
if (!getRequestImpl().getMethod().equals("REGISTER")) {
getRequestImpl().populateContactHeader(this);
} else if (status % 100 != 2) {
getRequestImpl().populateContactHeader(this);
}
}
} catch (ServletParseException ex) {
_logger.log(Level.WARNING, ex.getLocalizedMessage(), ex);
}
}
}
if (getRequestImpl().getMethod().equals("INVITE")) {
// SendReliable must be called if the inital request contain
// the header REQUIRE: 100Rel
if ((getStatus() > 100) && (getStatus() < 200)) {
ListIterator lIterator = getRequestImpl().getHeaders("Require");
while (lIterator.hasNext()) {
String headerValue = (String) lIterator.next();
if (headerValue.equalsIgnoreCase("100rel")) {
throw new IllegalStateException(
"Request with Require header 100rel, Provisional Response must be sent reliable");
}
}
// Only none SDP 1XX Reliable response can be overpass by a
// 200OK
} else if ((getStatus() == 200) &&
getSessionImpl().is1xxReliableSDP()) {
throw new IllegalStateException(
"Final response not allowed, 1XX reliable response with SDP ongoing RFC3262");
}
}
sendInternal();
}
/**
* Support of JSR 289 10.2.3 Sending Responses Helper function of the
* Proxy.startVirtualProxyBranch
*
* @throws IOException
*/
private void startVirtualProxyBranch() throws IOException {
// check that the proxy has been created
if (_currentRequest.isInitial() &&
(_currentRequest.getProxyContext() != null)) {
_currentRequest.getProxyContext().getProxy()
.startVirtualProxyBranch(this);
// must be added to response path
pushTransactionDispatcher(_currentRequest.getProxyContext());
// must update contact
if (SipFactoryImpl.getContactRequirement(this) != HeaderRequirement.NOT_APPLICAPLE) {
// since it's a new fragment id the contact must be replaced...
Header contact = getRawHeader(Header.CONTACT);
contact.setReadOnly(false);
contact.removeValues();
dialogManager.addContact(this);
}
}
}
/**
* Support of JSR 289 10.2.3 Sending Responses Adding the ProxyContext to
* the path if this is a Proxy acting as a UAS NOTE: only added for response
* to initial request
*
* @throws IOException
*/
private void loadVirtualProxyBranch() throws IOException {
// check that the proxy has been created
if (_currentRequest.isInitial() &&
(_currentRequest.getProxyContext() != null)) {
// must be added to response path
pushTransactionDispatcher(_currentRequest.getProxyContext());
}
}
public void sendInternal() throws IOException {
PathNode uas = null;
if (getRequest().isInitial() && !_currentRequest.isInApplicationPath()) {
// it's a raise condition between answers,
// use double check locking pattern
synchronized (_currentRequest) {
if (!_currentRequest.isInApplicationPath()) {
// support of JSR289, 10.2.3 Sending Responses (as a Proxy)
startVirtualProxyBranch();
// create the uas
uas = new UA(getApplicationSessionImpl(), false);
// lets add the current uas to the application & transaction
// path
getDialog().addToPath(uas);
// lets notify request that it is in app path
_currentRequest.setInApplicationPath(true);
setCurrentVisited(uas);
} else {
// need to fetch the uas from the last item of the
// application
// path
uas = getDialog().getLast();
setCurrentVisited(uas);
}
setSentOnThread(true);
}
} else {
loadVirtualProxyBranch();
// lets build the dispatcher list from the transaction path
if (isDirection() == Type.Caller) {
// need to fetch the uas from the last item of the application
// path
uas = getDialog().getLast();
} else if (isDirection() == Type.Callee) {
// need to fetch the uas from the first item of the application
// path
uas = getDialog().getFirst();
} else {
throw new IOException("Don't know the direction of the flow.");
}
}
// Tell the request that we will send a response.
// 1xx 2xx, 3xx, 4xx, 5xx and 6xx.
if ((_statusCode > 100) && (_statusCode < 700)) {
_currentRequest.setSentResponse(_statusCode);
}
// send
uas.send(this);
_responseSent = true;
}
@Override
public void setCertificate(X509Certificate[] cert) {
super.setCertificate(cert);
if (systemAttrib == null) {
systemAttrib = new HashMap<String, Object>(8);
}
this.systemAttrib.put(CLIENT_CERT, cert);
}
public String getSessionCase() {
// TODO Auto-generated method stub
return null;
}
public void setSessionCase(String sessionCase) {
// TODO Auto-generated method stub
}
public Header getCancelVia() {
return _viaOfCancel;
}
public void setCancelVia(Header via) {
_viaOfCancel = via;
}
/**
* @return the previous path node visited from the dispatcher stack or null
* if no path node was available
*/
public PathNode getPreviousVisited() {
return _previous;
}
/**
* @return the current path node visited from the dispatcher stack or null
* if no path node was available
*/
public PathNode getCurrentVisited() {
return _current;
}
/**
* Set the start path node
*
* @param p
*/
private void setCurrentVisited(PathNode p) {
_current = p;
}
public Dispatcher popDispatcher() {
int size = _transactionStack.size();
if (size > 0) {
Dispatcher d = _transactionStack.remove(size - 1);
if ((getRequestImpl() != null) && getRequestImpl().isInitial() &&
d instanceof PathNode) {
// the current path node is now set as previous
_previous = _current;
// save the poped path node as current
_current = (PathNode) d;
}
return d;
}
return null;
}
public Dispatcher peekDispatcher() {
int size = _transactionStack.size();
if (size > 0) {
return _transactionStack.get(size - 1);
}
return null;
}
public boolean isReliableProvisionalResponse() {
return _reliableProvisionalResponse;
}
public void setReliableProvisionalResponse(
boolean reliableProvisionalResponse) {
_reliableProvisionalResponse = reliableProvisionalResponse;
}
private TempSession getB2buaHelperTempSession() {
return _sessionToLink;
}
public void setB2buaHelperTempSession(TempSession s) {
_sessionToLink = s;
}
public void setSession(SipSessionBase sessionImpl) {
// signal of b2buaHelper to link sessions...
if (getB2buaHelperTempSession() != null) {
// session were not jet linked, lets link them
getRequestImpl().getB2buaHelper()
.linkSipSessions(sessionImpl,
getB2buaHelperTempSession().getDerivedUACSessionToLink());
// move attributes from temp. session to real session
String name = null;
Enumeration<String> names = getB2buaHelperTempSession()
.getAttributeNames();
while (names.hasMoreElements()) {
name = names.nextElement();
sessionImpl.setAttribute(name,
getB2buaHelperTempSession().getAttribute(name));
}
}
super.setSession(sessionImpl);
}
public Iterator<String> getChallengeRealms() {
if (ahp == null) {
ahp = new AuthHeaderProcessor();
values = ahp.getHeaderValues(this, "WWW-Authenticate", "realm");
pvalues = ahp.getHeaderValues(this, "Proxy-Authenticate", "realm");
values.addAll(pvalues);
}
return values.iterator();
}
public void setIncoming() {
incoming = true;
}
@Override
public boolean isCommitted() {
if (_responseSent) {
return true;
}
if (incoming) {
if ((_statusCode >= 100) && (_statusCode < 200)) {
if (getHeader(Header.RSEQ) != null) { // reliable prov
if (_isAlreadyPRACKGenerated)
return true;
else
return false;
}
return true;
}
if (_statusCode > 199) { // final response
if (getRequestImpl().getMethod().equals("INVITE") == false) {
return true;
} else if (_isAlreadyACKGenerated) {
return true;
}
}
}
return false;
}
public ProxyBranch getProxyBranch() {
Proxy p = getProxy();
if (p != null) {
List<ProxyBranch> branches = p.getProxyBranches();
if (branches != null) {
for (ProxyBranch branch : branches) {
// Should return the branch for the transaction. The transaction
// may be identified by the request object. When doing proxyTo()
// there are different request objects for different branches and
// hence different transactions.
//
// It is sufficient to check for request object identity (Not for equals() as it should be the same objects!!).
if (((ProxyBranchImpl)branch).getRequestImpl() == this.getRequestImpl()) {
return branch;
}
}
}
}
return null;
}
void setBranchResponse(boolean isBranchReponse) {
this.isBranchResponse = isBranchReponse;
}
public boolean isBranchResponse() {
return isBranchResponse;
}
public void setInternalTransportFailure(boolean internalTransportFailure) {
this.internalTransportFailure = internalTransportFailure;
}
public boolean isInternalTransportFailure() {
return internalTransportFailure;
}
}