Package com.ericsson.ssa.sip

Source Code of com.ericsson.ssa.sip.SipServletResponseImpl

/*
* 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;
  }


}
TOP

Related Classes of com.ericsson.ssa.sip.SipServletResponseImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.