Home | History | Annotate | Download | only in message
      1 /*
      2 * Conditions Of Use
      3 *
      4 * This software was developed by employees of the National Institute of
      5 * Standards and Technology (NIST), an agency of the Federal Government.
      6 * Pursuant to title 15 Untied States Code Section 105, works of NIST
      7 * employees are not subject to copyright protection in the United States
      8 * and are considered to be in the public domain.  As a result, a formal
      9 * license is not needed to use the software.
     10 *
     11 * This software is provided by NIST as a service and is expressly
     12 * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
     13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
     14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
     15 * AND DATA ACCURACY.  NIST does not warrant or make any representations
     16 * regarding the use of the software or the results thereof, including but
     17 * not limited to the correctness, accuracy, reliability or usefulness of
     18 * the software.
     19 *
     20 * Permission to use this software is contingent upon your acceptance
     21 * of the terms of this agreement
     22 *
     23 * .
     24 *
     25 */
     26 /*******************************************************************************
     27  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)         *
     28  *******************************************************************************/
     29 package gov.nist.javax.sip.message;
     30 
     31 import gov.nist.core.InternalErrorHandler;
     32 import gov.nist.javax.sip.Utils;
     33 import gov.nist.javax.sip.address.SipUri;
     34 import gov.nist.javax.sip.header.CSeq;
     35 import gov.nist.javax.sip.header.CallID;
     36 import gov.nist.javax.sip.header.ContactList;
     37 import gov.nist.javax.sip.header.ContentLength;
     38 import gov.nist.javax.sip.header.ContentType;
     39 import gov.nist.javax.sip.header.From;
     40 import gov.nist.javax.sip.header.MaxForwards;
     41 import gov.nist.javax.sip.header.ReasonList;
     42 import gov.nist.javax.sip.header.RecordRouteList;
     43 import gov.nist.javax.sip.header.RequireList;
     44 import gov.nist.javax.sip.header.SIPHeader;
     45 import gov.nist.javax.sip.header.StatusLine;
     46 import gov.nist.javax.sip.header.To;
     47 import gov.nist.javax.sip.header.Via;
     48 import gov.nist.javax.sip.header.ViaList;
     49 import gov.nist.javax.sip.header.extensions.SessionExpires;
     50 
     51 import java.io.UnsupportedEncodingException;
     52 import java.text.ParseException;
     53 import java.util.Iterator;
     54 import java.util.LinkedList;
     55 
     56 import javax.sip.header.ReasonHeader;
     57 import javax.sip.header.ServerHeader;
     58 import javax.sip.message.Request;
     59 
     60 
     61 /**
     62  * SIP Response structure.
     63  *
     64  * @version 1.2 $Revision: 1.29 $ $Date: 2009/10/25 03:07:52 $
     65  * @since 1.1
     66  *
     67  * @author M. Ranganathan   <br/>
     68  *
     69  *
     70  */
     71 public final class SIPResponse
     72     extends SIPMessage
     73     implements javax.sip.message.Response, ResponseExt {
     74     protected StatusLine statusLine;
     75 
     76     public static String getReasonPhrase(int rc) {
     77         String retval = null;
     78         switch (rc) {
     79 
     80             case TRYING :
     81                 retval = "Trying";
     82                 break;
     83 
     84             case RINGING :
     85                 retval = "Ringing";
     86                 break;
     87 
     88             case CALL_IS_BEING_FORWARDED :
     89                 retval = "Call is being forwarded";
     90                 break;
     91 
     92             case QUEUED :
     93                 retval = "Queued";
     94                 break;
     95 
     96             case SESSION_PROGRESS :
     97                 retval = "Session progress";
     98                 break;
     99 
    100             case OK :
    101                 retval = "OK";
    102                 break;
    103 
    104             case ACCEPTED :
    105                 retval = "Accepted";
    106                 break;
    107 
    108             case MULTIPLE_CHOICES :
    109                 retval = "Multiple choices";
    110                 break;
    111 
    112             case MOVED_PERMANENTLY :
    113                 retval = "Moved permanently";
    114                 break;
    115 
    116             case MOVED_TEMPORARILY :
    117                 retval = "Moved Temporarily";
    118                 break;
    119 
    120             case USE_PROXY :
    121                 retval = "Use proxy";
    122                 break;
    123 
    124             case ALTERNATIVE_SERVICE :
    125                 retval = "Alternative service";
    126                 break;
    127 
    128             case BAD_REQUEST :
    129                 retval = "Bad request";
    130                 break;
    131 
    132             case UNAUTHORIZED :
    133                 retval = "Unauthorized";
    134                 break;
    135 
    136             case PAYMENT_REQUIRED :
    137                 retval = "Payment required";
    138                 break;
    139 
    140             case FORBIDDEN :
    141                 retval = "Forbidden";
    142                 break;
    143 
    144             case NOT_FOUND :
    145                 retval = "Not found";
    146                 break;
    147 
    148             case METHOD_NOT_ALLOWED :
    149                 retval = "Method not allowed";
    150                 break;
    151 
    152             case NOT_ACCEPTABLE :
    153                 retval = "Not acceptable";
    154                 break;
    155 
    156             case PROXY_AUTHENTICATION_REQUIRED :
    157                 retval = "Proxy Authentication required";
    158                 break;
    159 
    160             case REQUEST_TIMEOUT :
    161                 retval = "Request timeout";
    162                 break;
    163 
    164             case GONE :
    165                 retval = "Gone";
    166                 break;
    167 
    168             case TEMPORARILY_UNAVAILABLE :
    169                 retval = "Temporarily Unavailable";
    170                 break;
    171 
    172             case REQUEST_ENTITY_TOO_LARGE :
    173                 retval = "Request entity too large";
    174                 break;
    175 
    176             case REQUEST_URI_TOO_LONG :
    177                 retval = "Request-URI too large";
    178                 break;
    179 
    180             case UNSUPPORTED_MEDIA_TYPE :
    181                 retval = "Unsupported media type";
    182                 break;
    183 
    184             case UNSUPPORTED_URI_SCHEME :
    185                 retval = "Unsupported URI Scheme";
    186                 break;
    187 
    188             case BAD_EXTENSION :
    189                 retval = "Bad extension";
    190                 break;
    191 
    192             case EXTENSION_REQUIRED :
    193                 retval = "Etension Required";
    194                 break;
    195 
    196             case INTERVAL_TOO_BRIEF :
    197                 retval = "Interval too brief";
    198                 break;
    199 
    200             case CALL_OR_TRANSACTION_DOES_NOT_EXIST :
    201                 retval = "Call leg/Transaction does not exist";
    202                 break;
    203 
    204             case LOOP_DETECTED :
    205                 retval = "Loop detected";
    206                 break;
    207 
    208             case TOO_MANY_HOPS :
    209                 retval = "Too many hops";
    210                 break;
    211 
    212             case ADDRESS_INCOMPLETE :
    213                 retval = "Address incomplete";
    214                 break;
    215 
    216             case AMBIGUOUS :
    217                 retval = "Ambiguous";
    218                 break;
    219 
    220             case BUSY_HERE :
    221                 retval = "Busy here";
    222                 break;
    223 
    224             case REQUEST_TERMINATED :
    225                 retval = "Request Terminated";
    226                 break;
    227 
    228             //Issue 168, Typo fix reported by fre on the retval
    229             case NOT_ACCEPTABLE_HERE :
    230                 retval = "Not Acceptable here";
    231                 break;
    232 
    233             case BAD_EVENT :
    234                 retval = "Bad Event";
    235                 break;
    236 
    237             case REQUEST_PENDING :
    238                 retval = "Request Pending";
    239                 break;
    240 
    241             case SERVER_INTERNAL_ERROR :
    242                 retval = "Server Internal Error";
    243                 break;
    244 
    245             case UNDECIPHERABLE :
    246                 retval = "Undecipherable";
    247                 break;
    248 
    249             case NOT_IMPLEMENTED :
    250                 retval = "Not implemented";
    251                 break;
    252 
    253             case BAD_GATEWAY :
    254                 retval = "Bad gateway";
    255                 break;
    256 
    257             case SERVICE_UNAVAILABLE :
    258                 retval = "Service unavailable";
    259                 break;
    260 
    261             case SERVER_TIMEOUT :
    262                 retval = "Gateway timeout";
    263                 break;
    264 
    265             case VERSION_NOT_SUPPORTED :
    266                 retval = "SIP version not supported";
    267                 break;
    268 
    269             case MESSAGE_TOO_LARGE :
    270                 retval = "Message Too Large";
    271                 break;
    272 
    273             case BUSY_EVERYWHERE :
    274                 retval = "Busy everywhere";
    275                 break;
    276 
    277             case DECLINE :
    278                 retval = "Decline";
    279                 break;
    280 
    281             case DOES_NOT_EXIST_ANYWHERE :
    282                 retval = "Does not exist anywhere";
    283                 break;
    284 
    285             case SESSION_NOT_ACCEPTABLE :
    286                 retval = "Session Not acceptable";
    287                 break;
    288 
    289             case CONDITIONAL_REQUEST_FAILED:
    290                 retval = "Conditional request failed";
    291                 break;
    292 
    293             default :
    294                 retval = "Unknown Status";
    295 
    296         }
    297         return retval;
    298 
    299     }
    300 
    301     /** set the status code.
    302      *@param statusCode is the status code to set.
    303      *@throws IlegalArgumentException if invalid status code.
    304      */
    305     public void setStatusCode(int statusCode) throws ParseException {
    306 
    307       // RFC3261 defines statuscode as 3DIGIT, 606 is the highest officially
    308       // defined code but extensions may add others (in theory up to 999,
    309       // but in practice up to 699 since the 6xx range is defined as 'final error')
    310         if (statusCode < 100 || statusCode > 699)
    311             throw new ParseException("bad status code", 0);
    312         if (this.statusLine == null)
    313             this.statusLine = new StatusLine();
    314         this.statusLine.setStatusCode(statusCode);
    315     }
    316 
    317     /**
    318      * Get the status line of the response.
    319      *@return StatusLine
    320      */
    321     public StatusLine getStatusLine() {
    322         return statusLine;
    323     }
    324 
    325     /** Get the staus code (conveniance function).
    326      *@return the status code of the status line.
    327      */
    328     public int getStatusCode() {
    329         return statusLine.getStatusCode();
    330     }
    331 
    332     /** Set the reason phrase.
    333      *@param reasonPhrase the reason phrase.
    334      *@throws IllegalArgumentException if null string
    335      */
    336     public void setReasonPhrase(String reasonPhrase) {
    337         if (reasonPhrase == null)
    338             throw new IllegalArgumentException("Bad reason phrase");
    339         if (this.statusLine == null)
    340             this.statusLine = new StatusLine();
    341         this.statusLine.setReasonPhrase(reasonPhrase);
    342     }
    343 
    344     /** Get the reason phrase.
    345      *@return the reason phrase.
    346      */
    347     public String getReasonPhrase() {
    348         if (statusLine == null || statusLine.getReasonPhrase() == null)
    349             return "";
    350         else
    351             return statusLine.getReasonPhrase();
    352     }
    353 
    354     /** Return true if the response is a final response.
    355      *@param rc is the return code.
    356      *@return true if the parameter is between the range 200 and 700.
    357      */
    358     public static boolean isFinalResponse(int rc) {
    359         return rc >= 200 && rc < 700;
    360     }
    361 
    362     /** Is this a final response?
    363      *@return true if this is a final response.
    364      */
    365     public boolean isFinalResponse() {
    366         return isFinalResponse(statusLine.getStatusCode());
    367     }
    368 
    369     /**
    370      * Set the status line field.
    371      *@param sl Status line to set.
    372      */
    373     public void setStatusLine(StatusLine sl) {
    374         statusLine = sl;
    375     }
    376 
    377     /** Constructor.
    378      */
    379     public SIPResponse() {
    380         super();
    381     }
    382     /**
    383      * Print formatting function.
    384      *Indent and parenthesize for pretty printing.
    385      * Note -- use the encode method for formatting the message.
    386      * Hack here to XMLize.
    387      *
    388      *@return a string for pretty printing.
    389      */
    390     public String debugDump() {
    391         String superstring = super.debugDump();
    392         stringRepresentation = "";
    393         sprint(SIPResponse.class.getCanonicalName());
    394         sprint("{");
    395         if (statusLine != null) {
    396             sprint(statusLine.debugDump());
    397         }
    398         sprint(superstring);
    399         sprint("}");
    400         return stringRepresentation;
    401     }
    402 
    403     /**
    404      * Check the response structure. Must have from, to CSEQ and VIA
    405      * headers.
    406      */
    407     public void checkHeaders() throws ParseException {
    408         if (getCSeq() == null) {
    409             throw new ParseException(CSeq.NAME+ " Is missing ", 0);
    410         }
    411         if (getTo() == null) {
    412             throw new ParseException(To.NAME+ " Is missing ", 0);
    413         }
    414         if (getFrom() == null) {
    415             throw new ParseException(From.NAME+ " Is missing ", 0);
    416         }
    417         if (getViaHeaders() == null) {
    418             throw new ParseException(Via.NAME+ " Is missing ", 0);
    419         }
    420         if (getCallId() == null) {
    421             throw new ParseException(CallID.NAME + " Is missing ", 0);
    422         }
    423 
    424 
    425         if (getStatusCode() > 699) {
    426             throw new ParseException("Unknown error code!" + getStatusCode(), 0);
    427         }
    428 
    429     }
    430 
    431     /**
    432      *  Encode the SIP Request as a string.
    433      *@return The string encoded canonical form of the message.
    434      */
    435 
    436     public String encode() {
    437         String retval;
    438         if (statusLine != null)
    439             retval = statusLine.encode() + super.encode();
    440         else
    441             retval = super.encode();
    442         return retval ;
    443     }
    444 
    445     /** Encode the message except for the body.
    446     *
    447     *@return The string except for the body.
    448     */
    449 
    450     public String encodeMessage() {
    451         String retval;
    452         if (statusLine != null)
    453             retval = statusLine.encode() + super.encodeSIPHeaders();
    454         else
    455             retval = super.encodeSIPHeaders();
    456         return retval ;
    457     }
    458 
    459 
    460 
    461     /** Get this message as a list of encoded strings.
    462      *@return LinkedList containing encoded strings for each header in
    463      *   the message.
    464      */
    465 
    466     public LinkedList getMessageAsEncodedStrings() {
    467         LinkedList retval = super.getMessageAsEncodedStrings();
    468 
    469         if (statusLine != null)
    470             retval.addFirst(statusLine.encode());
    471         return retval;
    472 
    473     }
    474 
    475     /**
    476      * Make a clone (deep copy) of this object.
    477      *@return a deep copy of this object.
    478      */
    479 
    480     public Object clone() {
    481         SIPResponse retval = (SIPResponse) super.clone();
    482         if (this.statusLine != null)
    483             retval.statusLine = (StatusLine) this.statusLine.clone();
    484         return retval;
    485     }
    486 
    487 
    488     /**
    489      * Compare for equality.
    490      *@param other other object to compare with.
    491      */
    492     public boolean equals(Object other) {
    493         if (!this.getClass().equals(other.getClass()))
    494             return false;
    495         SIPResponse that = (SIPResponse) other;
    496         return statusLine.equals(that.statusLine) && super.equals(other);
    497     }
    498 
    499     /**
    500      * Match with a template.
    501      *@param matchObj template object to match ourselves with (null
    502      * in any position in the template object matches wildcard)
    503      */
    504     public boolean match(Object matchObj) {
    505         if (matchObj == null)
    506             return true;
    507         else if (!matchObj.getClass().equals(this.getClass())) {
    508             return false;
    509         } else if (matchObj == this)
    510             return true;
    511         SIPResponse that = (SIPResponse) matchObj;
    512 
    513         StatusLine rline = that.statusLine;
    514         if (this.statusLine == null && rline != null)
    515             return false;
    516         else if (this.statusLine == rline)
    517             return super.match(matchObj);
    518         else {
    519 
    520             return statusLine.match(that.statusLine) && super.match(matchObj);
    521         }
    522 
    523     }
    524 
    525     /** Encode this into a byte array.
    526      * This is used when the body has been set as a binary array
    527      * and you want to encode the body as a byte array for transmission.
    528      *
    529      *@return a byte array containing the SIPRequest encoded as a byte
    530      *  array.
    531      */
    532 
    533     public byte[] encodeAsBytes( String transport ) {
    534         byte[] slbytes = null;
    535         if (statusLine != null) {
    536             try {
    537                 slbytes = statusLine.encode().getBytes("UTF-8");
    538             } catch (UnsupportedEncodingException ex) {
    539                 InternalErrorHandler.handleException(ex);
    540             }
    541         }
    542         byte[] superbytes = super.encodeAsBytes( transport );
    543         byte[] retval = new byte[slbytes.length + superbytes.length];
    544         System.arraycopy(slbytes, 0, retval, 0, slbytes.length);
    545         System.arraycopy(superbytes, 0, retval, slbytes.length,
    546                 superbytes.length);
    547         return retval;
    548     }
    549 
    550 
    551 
    552     /** Get a dialog identifier.
    553      * Generates a string that can be used as a dialog identifier.
    554      *
    555      * @param isServer is set to true if this is the UAS
    556      * and set to false if this is the UAC
    557      */
    558     public String getDialogId(boolean isServer) {
    559         CallID cid = (CallID) this.getCallId();
    560         From from = (From) this.getFrom();
    561         To to = (To) this.getTo();
    562         StringBuffer retval = new StringBuffer(cid.getCallId());
    563         if (!isServer) {
    564             //retval.append(COLON).append(from.getUserAtHostPort());
    565             if (from.getTag() != null) {
    566                 retval.append(COLON);
    567                 retval.append(from.getTag());
    568             }
    569             //retval.append(COLON).append(to.getUserAtHostPort());
    570             if (to.getTag() != null) {
    571                 retval.append(COLON);
    572                 retval.append(to.getTag());
    573             }
    574         } else {
    575             //retval.append(COLON).append(to.getUserAtHostPort());
    576             if (to.getTag() != null) {
    577                 retval.append(COLON);
    578                 retval.append(to.getTag());
    579             }
    580             //retval.append(COLON).append(from.getUserAtHostPort());
    581             if (from.getTag() != null) {
    582                 retval.append(COLON);
    583                 retval.append(from.getTag());
    584             }
    585         }
    586         return retval.toString().toLowerCase();
    587     }
    588 
    589     public String getDialogId(boolean isServer, String toTag) {
    590         CallID cid = (CallID) this.getCallId();
    591         From from = (From) this.getFrom();
    592         StringBuffer retval = new StringBuffer(cid.getCallId());
    593         if (!isServer) {
    594             //retval.append(COLON).append(from.getUserAtHostPort());
    595             if (from.getTag() != null) {
    596                 retval.append(COLON);
    597                 retval.append(from.getTag());
    598             }
    599             //retval.append(COLON).append(to.getUserAtHostPort());
    600             if (toTag != null) {
    601                 retval.append(COLON);
    602                 retval.append(toTag);
    603             }
    604         } else {
    605             //retval.append(COLON).append(to.getUserAtHostPort());
    606             if (toTag != null) {
    607                 retval.append(COLON);
    608                 retval.append(toTag);
    609             }
    610             //retval.append(COLON).append(from.getUserAtHostPort());
    611             if (from.getTag() != null) {
    612                 retval.append(COLON);
    613                 retval.append(from.getTag());
    614             }
    615         }
    616         return retval.toString().toLowerCase();
    617     }
    618 
    619     /**
    620      * Sets the Via branch for CANCEL or ACK requests
    621      *
    622      * @param via
    623      * @param method
    624      * @throws ParseException
    625      */
    626     private final void setBranch( Via via, String method ) {
    627         String branch;
    628         if (method.equals( Request.ACK ) ) {
    629             if (statusLine.getStatusCode() >= 300 ) {
    630                 branch = getTopmostVia().getBranch();   // non-2xx ACK uses same branch
    631             } else {
    632                 branch = Utils.getInstance().generateBranchId();    // 2xx ACK gets new branch
    633             }
    634         } else if (method.equals( Request.CANCEL )) {
    635             branch = getTopmostVia().getBranch();   // CANCEL uses same branch
    636         } else return;
    637 
    638         try {
    639             via.setBranch( branch );
    640         } catch (ParseException e) {
    641             e.printStackTrace();
    642         }
    643     }
    644 
    645 
    646     /**
    647      * Get the encoded first line.
    648      *
    649      *@return the status line encoded.
    650      *
    651      */
    652     public String getFirstLine() {
    653         if (this.statusLine == null)
    654             return null;
    655         else
    656             return this.statusLine.encode();
    657     }
    658 
    659     public void setSIPVersion(String sipVersion) {
    660         this.statusLine.setSipVersion(sipVersion);
    661     }
    662 
    663     public String getSIPVersion() {
    664         return this.statusLine.getSipVersion();
    665     }
    666 
    667     public String toString() {
    668         if (statusLine == null) return  "";
    669         else return statusLine.encode() + super.encode();
    670     }
    671 
    672     /**
    673      * Generate a request from a response.
    674      *
    675      * @param requestURI -- the request URI to assign to the request.
    676      * @param via -- the Via header to assign to the request
    677      * @param cseq -- the CSeq header to assign to the request
    678      * @param from -- the From header to assign to the request
    679      * @param to -- the To header to assign to the request
    680      * @return -- the newly generated sip request.
    681      */
    682     public SIPRequest createRequest(SipUri requestURI, Via via, CSeq cseq, From from, To to) {
    683         SIPRequest newRequest = new SIPRequest();
    684         String method = cseq.getMethod();
    685 
    686         newRequest.setMethod(method);
    687         newRequest.setRequestURI(requestURI);
    688         this.setBranch( via, method );
    689         newRequest.setHeader(via);
    690         newRequest.setHeader(cseq);
    691         Iterator headerIterator = getHeaders();
    692         while (headerIterator.hasNext()) {
    693             SIPHeader nextHeader = (SIPHeader) headerIterator.next();
    694             // Some headers do not belong in a Request ....
    695             if (SIPMessage.isResponseHeader(nextHeader)
    696                 || nextHeader instanceof ViaList
    697                 || nextHeader instanceof CSeq
    698                 || nextHeader instanceof ContentType
    699                 || nextHeader instanceof ContentLength
    700                 || nextHeader instanceof RecordRouteList
    701                 || nextHeader instanceof RequireList
    702                 || nextHeader instanceof ContactList    // JvB: added
    703                 || nextHeader instanceof ContentLength
    704                 || nextHeader instanceof ServerHeader
    705                 || nextHeader instanceof ReasonHeader
    706                 || nextHeader instanceof SessionExpires
    707                 || nextHeader instanceof ReasonList) {
    708                 continue;
    709             }
    710             if (nextHeader instanceof To)
    711                 nextHeader = (SIPHeader) to;
    712             else if (nextHeader instanceof From)
    713                 nextHeader = (SIPHeader) from;
    714             try {
    715                 newRequest.attachHeader(nextHeader, false);
    716             } catch (SIPDuplicateHeaderException e) {
    717                 //Should not happen!
    718                 e.printStackTrace();
    719             }
    720         }
    721 
    722         try {
    723           // JvB: all requests need a Max-Forwards
    724           newRequest.attachHeader( new MaxForwards(70), false);
    725         } catch (Exception d) {
    726 
    727         }
    728 
    729         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null ) {
    730             newRequest.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
    731         }
    732         return newRequest;
    733 
    734     }
    735 }
    736