content: /*
  * Conditions Of Use
  *
  * This software was developed by employees of the National Institute of
  * Standards and Technology (NIST), an agency of the Federal Government.
  * Pursuant to title 15 Untied States Code Section 105, works of NIST
  * employees are not subject to copyright protection in the United States
  * and are considered to be in the public domain.  As a result, a formal
  * license is not needed to use the software.
  *
  * This software is provided by NIST as a service and is expressly
  * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
  * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
  * AND DATA ACCURACY.  NIST does not warrant or make any representations
  * regarding the use of the software or the results thereof, including but
  * not limited to the correctness, accuracy, reliability or usefulness of
  * the software.
  *
  * Permission to use this software is contingent upon your acceptance
  * of the terms of this agreement
  *
  * .
  *
  */
  /*******************************************************************************
   * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)         *
   *******************************************************************************/
  package gov2.nist.javax2.sip.message;
  
  import gov2.nist.core.InternalErrorHandler;
  import gov2.nist.javax2.sip.Utils;
  import gov2.nist.javax2.sip.address.SipUri;
  import gov2.nist.javax2.sip.header.CSeq;
  import gov2.nist.javax2.sip.header.CallID;
  import gov2.nist.javax2.sip.header.ContactList;
  import gov2.nist.javax2.sip.header.ContentLength;
  import gov2.nist.javax2.sip.header.ContentType;
  import gov2.nist.javax2.sip.header.From;
  import gov2.nist.javax2.sip.header.MaxForwards;
  import gov2.nist.javax2.sip.header.ReasonList;
  import gov2.nist.javax2.sip.header.RecordRouteList;
  import gov2.nist.javax2.sip.header.RequireList;
  import gov2.nist.javax2.sip.header.SIPHeader;
  import gov2.nist.javax2.sip.header.StatusLine;
  import gov2.nist.javax2.sip.header.To;
  import gov2.nist.javax2.sip.header.Via;
  import gov2.nist.javax2.sip.header.ViaList;
  import gov2.nist.javax2.sip.header.extensions.SessionExpires;
  
  import java.io.UnsupportedEncodingException;
  import java.text.ParseException;
  import java.util.Iterator;
  import java.util.LinkedList;
  
  import javax2.sip.header.ReasonHeader;
  import javax2.sip.header.ServerHeader;
  import javax2.sip.message.Request;
  
  
  /**
   * SIP Response structure.
   *
   * @version 1.2 $Revision: 1.29 $ $Date: 2009/10/25 03:07:52 $
   * @since 1.1
   *
   * @author M. Ranganathan   <br/>
   *
   *
   */
  public final class SIPResponse
      extends SIPMessage
      implements javax2.sip.message.Response, ResponseExt {
      protected StatusLine statusLine;
  
      public static String getReasonPhrase(int rc) {
          String retval = null;
          switch (rc) {
  
              case TRYING :
                  retval = "Trying";
                  break;
  
              case RINGING :
                  retval = "Ringing";
                  break;
  
              case CALL_IS_BEING_FORWARDED :
                  retval = "Call is being forwarded";
                  break;
  
              case QUEUED :
                  retval = "Queued";
                  break;
  
              case SESSION_PROGRESS :
                  retval = "Session progress";
                  break;
  
              case OK :
                  retval = "OK";
                  break;
  
              case ACCEPTED :
                  retval = "Accepted";
                  break;
  
              case MULTIPLE_CHOICES :
                  retval = "Multiple choices";
                  break;
  
              case MOVED_PERMANENTLY :
                  retval = "Moved permanently";
                  break;
  
              case MOVED_TEMPORARILY :
                  retval = "Moved Temporarily";
                  break;
  
              case USE_PROXY :
                  retval = "Use proxy";
                  break;
  
              case ALTERNATIVE_SERVICE :
                  retval = "Alternative service";
                  break;
  
              case BAD_REQUEST :
                  retval = "Bad request";
                  break;
  
              case UNAUTHORIZED :
                  retval = "Unauthorized";
                  break;
  
              case PAYMENT_REQUIRED :
                  retval = "Payment required";
                  break;
  
              case FORBIDDEN :
                  retval = "Forbidden";
                  break;
  
              case NOT_FOUND :
                  retval = "Not found";
                  break;
  
              case METHOD_NOT_ALLOWED :
                  retval = "Method not allowed";
                  break;
  
              case NOT_ACCEPTABLE :
                  retval = "Not acceptable";
                  break;
  
              case PROXY_AUTHENTICATION_REQUIRED :
                  retval = "Proxy Authentication required";
                  break;
  
              case REQUEST_TIMEOUT :
                  retval = "Request timeout";
                  break;
  
              case GONE :
                  retval = "Gone";
                  break;
  
              case TEMPORARILY_UNAVAILABLE :
                  retval = "Temporarily Unavailable";
                  break;
  
              case REQUEST_ENTITY_TOO_LARGE :
                  retval = "Request entity too large";
                  break;
  
              case REQUEST_URI_TOO_LONG :
                  retval = "Request-URI too large";
                  break;
  
              case UNSUPPORTED_MEDIA_TYPE :
                  retval = "Unsupported media type";
                  break;
  
              case UNSUPPORTED_URI_SCHEME :
                  retval = "Unsupported URI Scheme";
                  break;
  
              case BAD_EXTENSION :
                  retval = "Bad extension";
                  break;
  
              case EXTENSION_REQUIRED :
                  retval = "Etension Required";
                  break;
  
              case INTERVAL_TOO_BRIEF :
                  retval = "Interval too brief";
                  break;
  
              case CALL_OR_TRANSACTION_DOES_NOT_EXIST :
                  retval = "Call leg/Transaction does not exist";
                  break;
  
              case LOOP_DETECTED :
                  retval = "Loop detected";
                  break;
  
              case TOO_MANY_HOPS :
                  retval = "Too many hops";
                  break;
  
              case ADDRESS_INCOMPLETE :
                  retval = "Address incomplete";
                  break;
  
              case AMBIGUOUS :
                  retval = "Ambiguous";
                  break;
  
              case BUSY_HERE :
                  retval = "Busy here";
                  break;
  
              case REQUEST_TERMINATED :
                  retval = "Request Terminated";
                  break;
  
              //Issue 168, Typo fix reported by fre on the retval
              case NOT_ACCEPTABLE_HERE :
                  retval = "Not Acceptable here";
                  break;
  
              case BAD_EVENT :
                  retval = "Bad Event";
                  break;
  
              case REQUEST_PENDING :
                  retval = "Request Pending";
                  break;
  
              case SERVER_INTERNAL_ERROR :
                  retval = "Server Internal Error";
                  break;
  
              case UNDECIPHERABLE :
                  retval = "Undecipherable";
                  break;
  
              case NOT_IMPLEMENTED :
                  retval = "Not implemented";
                  break;
  
              case BAD_GATEWAY :
                  retval = "Bad gateway";
                  break;
  
              case SERVICE_UNAVAILABLE :
                  retval = "Service unavailable";
                  break;
  
              case SERVER_TIMEOUT :
                  retval = "Gateway timeout";
                  break;
  
              case VERSION_NOT_SUPPORTED :
                  retval = "SIP version not supported";
                  break;
  
              case MESSAGE_TOO_LARGE :
                  retval = "Message Too Large";
                  break;
  
              case BUSY_EVERYWHERE :
                  retval = "Busy everywhere";
                  break;
  
              case DECLINE :
                  retval = "Decline";
                  break;
  
              case DOES_NOT_EXIST_ANYWHERE :
                  retval = "Does not exist anywhere";
                  break;
  
              case SESSION_NOT_ACCEPTABLE :
                  retval = "Session Not acceptable";
                  break;
  
              case CONDITIONAL_REQUEST_FAILED:
                  retval = "Conditional request failed";
                  break;
  
              default :
                  retval = "Unknown Status";
  
          }
          return retval;
  
      }
  
      /** set the status code.
       *@param statusCode is the status code to set.
       *@throws IlegalArgumentException if invalid status code.
       */
      public void setStatusCode(int statusCode) throws ParseException {
  
        // RFC3261 defines statuscode as 3DIGIT, 606 is the highest officially
        // defined code but extensions may add others (in theory up to 999,
        // but in practice up to 699 since the 6xx range is defined as 'final error')
          if (statusCode < 100 || statusCode > 699)
              throw new ParseException("bad status code", 0);
          if (this.statusLine == null)
              this.statusLine = new StatusLine();
          this.statusLine.setStatusCode(statusCode);
      }
  
      /**
       * Get the status line of the response.
       *@return StatusLine
       */
      public StatusLine getStatusLine() {
          return statusLine;
      }
  
      /** Get the staus code (conveniance function).
       *@return the status code of the status line.
       */
      public int getStatusCode() {
          return statusLine.getStatusCode();
      }
  
      /** Set the reason phrase.
       *@param reasonPhrase the reason phrase.
       *@throws IllegalArgumentException if null string
       */
      public void setReasonPhrase(String reasonPhrase) {
          if (reasonPhrase == null)
              throw new IllegalArgumentException("Bad reason phrase");
          if (this.statusLine == null)
              this.statusLine = new StatusLine();
          this.statusLine.setReasonPhrase(reasonPhrase);
      }
  
      /** Get the reason phrase.
       *@return the reason phrase.
       */
      public String getReasonPhrase() {
          if (statusLine == null || statusLine.getReasonPhrase() == null)
              return "";
          else
              return statusLine.getReasonPhrase();
      }
  
      /** Return true if the response is a final response.
       *@param rc is the return code.
       *@return true if the parameter is between the range 200 and 700.
       */
      public static boolean isFinalResponse(int rc) {
          return rc >= 200 && rc < 700;
      }
  
      /** Is this a final response?
       *@return true if this is a final response.
       */
      public boolean isFinalResponse() {
          return isFinalResponse(statusLine.getStatusCode());
      }
  
      /**
       * Set the status line field.
       *@param sl Status line to set.
       */
      public void setStatusLine(StatusLine sl) {
          statusLine = sl;
      }
  
      /** Constructor.
       */
      public SIPResponse() {
          super();
      }
      /**
       * Print formatting function.
       *Indent and parenthesize for pretty printing.
       * Note -- use the encode method for formatting the message.
       * Hack here to XMLize.
       *
       *@return a string for pretty printing.
       */
      public String debugDump() {
          String superstring = super.debugDump();
          stringRepresentation = "";
          sprint(SIPResponse.class.getCanonicalName());
          sprint("{");
          if (statusLine != null) {
              sprint(statusLine.debugDump());
          }
          sprint(superstring);
          sprint("}");
          return stringRepresentation;
      }
  
      /**
       * Check the response structure. Must have from, to CSEQ and VIA
       * headers.
       */
      public void checkHeaders() throws ParseException {
          if (getCSeq() == null) {
              throw new ParseException(CSeq.NAME+ " Is missing ", 0);
          }
          if (getTo() == null) {
              throw new ParseException(To.NAME+ " Is missing ", 0);
          }
          if (getFrom() == null) {
              throw new ParseException(From.NAME+ " Is missing ", 0);
          }
          if (getViaHeaders() == null) {
              throw new ParseException(Via.NAME+ " Is missing ", 0);
          }
          if (getCallId() == null) {
              throw new ParseException(CallID.NAME + " Is missing ", 0);
          }
  
  
          if (getStatusCode() > 699) {
              throw new ParseException("Unknown error code!" + getStatusCode(), 0);
          }
  
      }
  
      /**
       *  Encode the SIP Request as a string.
       *@return The string encoded canonical form of the message.
       */
  
      public String encode() {
          String retval;
          if (statusLine != null)
              retval = statusLine.encode() + super.encode();
          else
              retval = super.encode();
          return retval ;
      }
  
      /** Encode the message except for the body.
      *
      *@return The string except for the body.
      */
  
      public String encodeMessage() {
          String retval;
          if (statusLine != null)
              retval = statusLine.encode() + super.encodeSIPHeaders();
          else
              retval = super.encodeSIPHeaders();
          return retval ;
      }
  
  
  
      /** Get this message as a list of encoded strings.
       *@return LinkedList containing encoded strings for each header in
       *   the message.
       */
  
      public LinkedList getMessageAsEncodedStrings() {
          LinkedList retval = super.getMessageAsEncodedStrings();
  
          if (statusLine != null)
              retval.addFirst(statusLine.encode());
          return retval;
  
      }
  
      /**
       * Make a clone (deep copy) of this object.
       *@return a deep copy of this object.
       */
  
      public Object clone() {
          SIPResponse retval = (SIPResponse) super.clone();
          if (this.statusLine != null)
              retval.statusLine = (StatusLine) this.statusLine.clone();
          return retval;
      }
  
  
      /**
       * Compare for equality.
       *@param other other object to compare with.
       */
      public boolean equals(Object other) {
          if (!this.getClass().equals(other.getClass()))
              return false;
          SIPResponse that = (SIPResponse) other;
          return statusLine.equals(that.statusLine) && super.equals(other);
      }
  
      /**
       * Match with a template.
       *@param matchObj template object to match ourselves with (null
       * in any position in the template object matches wildcard)
       */
      public boolean match(Object matchObj) {
          if (matchObj == null)
              return true;
          else if (!matchObj.getClass().equals(this.getClass())) {
              return false;
          } else if (matchObj == this)
              return true;
          SIPResponse that = (SIPResponse) matchObj;
  
          StatusLine rline = that.statusLine;
          if (this.statusLine == null && rline != null)
              return false;
          else if (this.statusLine == rline)
              return super.match(matchObj);
          else {
  
              return statusLine.match(that.statusLine) && super.match(matchObj);
          }
  
      }
  
      /** Encode this into a byte array.
       * This is used when the body has been set as a binary array
       * and you want to encode the body as a byte array for transmission.
       *
       *@return a byte array containing the SIPRequest encoded as a byte
       *  array.
       */
  
      public byte[] encodeAsBytes( String transport ) {
          byte[] slbytes = null;
          if (statusLine != null) {
              try {
                  slbytes = statusLine.encode().getBytes("UTF-8");
              } catch (UnsupportedEncodingException ex) {
                  InternalErrorHandler.handleException(ex);
              }
          }
          byte[] superbytes = super.encodeAsBytes( transport );
          byte[] retval = new byte[slbytes.length + superbytes.length];
          System.arraycopy(slbytes, 0, retval, 0, slbytes.length);
          System.arraycopy(superbytes, 0, retval, slbytes.length,
                  superbytes.length);
          return retval;
      }
  
  
  
      /** Get a dialog identifier.
       * Generates a string that can be used as a dialog identifier.
       *
       * @param isServer is set to true if this is the UAS
       * and set to false if this is the UAC
       */
      public String getDialogId(boolean isServer) {
          CallID cid = (CallID) this.getCallId();
          From from = (From) this.getFrom();
          To to = (To) this.getTo();
          StringBuffer retval = new StringBuffer(cid.getCallId());
          if (!isServer) {
              //retval.append(COLON).append(from.getUserAtHostPort());
              if (from.getTag() != null) {
                  retval.append(COLON);
                  retval.append(from.getTag());
              }
              //retval.append(COLON).append(to.getUserAtHostPort());
              if (to.getTag() != null) {
                  retval.append(COLON);
                  retval.append(to.getTag());
              }
          } else {
              //retval.append(COLON).append(to.getUserAtHostPort());
              if (to.getTag() != null) {
                  retval.append(COLON);
                  retval.append(to.getTag());
              }
              //retval.append(COLON).append(from.getUserAtHostPort());
              if (from.getTag() != null) {
                  retval.append(COLON);
                  retval.append(from.getTag());
              }
          }
          return retval.toString().toLowerCase();
      }
  
      public String getDialogId(boolean isServer, String toTag) {
          CallID cid = (CallID) this.getCallId();
          From from = (From) this.getFrom();
          StringBuffer retval = new StringBuffer(cid.getCallId());
          if (!isServer) {
              //retval.append(COLON).append(from.getUserAtHostPort());
              if (from.getTag() != null) {
                  retval.append(COLON);
                  retval.append(from.getTag());
              }
              //retval.append(COLON).append(to.getUserAtHostPort());
              if (toTag != null) {
                  retval.append(COLON);
                  retval.append(toTag);
              }
          } else {
              //retval.append(COLON).append(to.getUserAtHostPort());
              if (toTag != null) {
                  retval.append(COLON);
                  retval.append(toTag);
              }
              //retval.append(COLON).append(from.getUserAtHostPort());
              if (from.getTag() != null) {
                  retval.append(COLON);
                  retval.append(from.getTag());
              }
          }
          return retval.toString().toLowerCase();
      }
  
      /**
       * Sets the Via branch for CANCEL or ACK requests
       *
       * @param via
       * @param method
       * @throws ParseException
       */
      private final void setBranch( Via via, String method ) {
          String branch;
          if (method.equals( Request.ACK ) ) {
              if (statusLine.getStatusCode() >= 300 ) {
                  branch = getTopmostVia().getBranch();   // non-2xx ACK uses same branch
              } else {
                  branch = Utils.getInstance().generateBranchId();    // 2xx ACK gets new branch
              }
          } else if (method.equals( Request.CANCEL )) {
              branch = getTopmostVia().getBranch();   // CANCEL uses same branch
          } else return;
  
          try {
              via.setBranch( branch );
          } catch (ParseException e) {
              e.printStackTrace();
          }
      }
  
  
      /**
       * Get the encoded first line.
       *
       *@return the status line encoded.
       *
       */
      public String getFirstLine() {
          if (this.statusLine == null)
              return null;
          else
              return this.statusLine.encode();
      }
  
      public void setSIPVersion(String sipVersion) {
          this.statusLine.setSipVersion(sipVersion);
      }
  
      public String getSIPVersion() {
          return this.statusLine.getSipVersion();
      }
  
      public String toString() {
          if (statusLine == null) return  "";
          else return statusLine.encode() + super.encode();
      }
  
      /**
       * Generate a request from a response.
       *
       * @param requestURI -- the request URI to assign to the request.
       * @param via -- the Via header to assign to the request
       * @param cseq -- the CSeq header to assign to the request
       * @param from -- the From header to assign to the request
       * @param to -- the To header to assign to the request
       * @return -- the newly generated sip request.
       */
      public SIPRequest createRequest(SipUri requestURI, Via via, CSeq cseq, From from, To to) {
          SIPRequest newRequest = new SIPRequest();
          String method = cseq.getMethod();
  
          newRequest.setMethod(method);
          newRequest.setRequestURI(requestURI);
          this.setBranch( via, method );
          newRequest.setHeader(via);
          newRequest.setHeader(cseq);
          // aligned with NIST version 1.2
          newRequest.setHeader(from);
          newRequest.setHeader(to);
          newRequest.setHeader(getCallId());
  
          try {
            // JvB: all requests need a Max-Forwards
            newRequest.attachHeader( new MaxForwards(70), false);
          } catch (Exception d) {
  
          }
  
          if (MessageFactoryImpl.getDefaultUserAgentHeader() != null ) {
              newRequest.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
          }
          return newRequest;
  
      }
  }
  