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.javax.sip.address.*;
     32 import gov.nist.core.*;
     33 
     34 import java.util.HashSet;
     35 import java.util.Hashtable;
     36 import java.util.LinkedList;
     37 import java.util.Set;
     38 import java.io.UnsupportedEncodingException;
     39 import java.util.Iterator;
     40 import javax.sip.address.URI;
     41 import javax.sip.message.*;
     42 
     43 import java.text.ParseException;
     44 import javax.sip.*;
     45 import javax.sip.header.*;
     46 
     47 import gov.nist.javax.sip.header.*;
     48 import gov.nist.javax.sip.stack.SIPTransactionStack;
     49 
     50 /*
     51  * Acknowledgements: Mark Bednarek made a few fixes to this code. Jeff Keyser added two methods
     52  * that create responses and generate cancel requests from incoming orignial requests without the
     53  * additional overhead of encoding and decoding messages. Bruno Konik noticed an extraneous
     54  * newline added to the end of the buffer when encoding it. Incorporates a bug report from Andreas
     55  * Bystrom. Szabo Barna noticed a contact in a cancel request - this is a pointless header for
     56  * cancel. Antonis Kyardis contributed bug fixes. Jeroen van Bemmel noted that method names are
     57  * case sensitive, should use equals() in getting CannonicalName
     58  *
     59  */
     60 
     61 /**
     62  * The SIP Request structure.
     63  *
     64  * @version 1.2 $Revision: 1.52 $ $Date: 2009/12/16 14:58:40 $
     65  * @since 1.1
     66  *
     67  * @author M. Ranganathan <br/>
     68  *
     69  *
     70  *
     71  */
     72 
     73 public final class SIPRequest extends SIPMessage implements javax.sip.message.Request, RequestExt {
     74 
     75     private static final long serialVersionUID = 3360720013577322927L;
     76 
     77     private static final String DEFAULT_USER = "ip";
     78 
     79     private static final String DEFAULT_TRANSPORT = "udp";
     80 
     81     private transient Object transactionPointer;
     82 
     83     private RequestLine requestLine;
     84 
     85     private transient Object messageChannel;
     86 
     87 
     88 
     89     private transient Object inviteTransaction; // The original invite request for a
     90     // given cancel request
     91 
     92     /**
     93      * Set of target refresh methods, currently: INVITE, UPDATE, SUBSCRIBE, NOTIFY, REFER
     94      *
     95      * A target refresh request and its response MUST have a Contact
     96      */
     97     private static final Set<String> targetRefreshMethods = new HashSet<String>();
     98 
     99     /*
    100      * A table that maps a name string to its cannonical constant. This is used to speed up
    101      * parsing of messages .equals reduces to == if we use the constant value.
    102      */
    103     private static final Hashtable<String, String> nameTable = new Hashtable<String, String>();
    104 
    105     private static void putName(String name) {
    106         nameTable.put(name, name);
    107     }
    108 
    109     static {
    110         targetRefreshMethods.add(Request.INVITE);
    111         targetRefreshMethods.add(Request.UPDATE);
    112         targetRefreshMethods.add(Request.SUBSCRIBE);
    113         targetRefreshMethods.add(Request.NOTIFY);
    114         targetRefreshMethods.add(Request.REFER);
    115 
    116         putName(Request.INVITE);
    117         putName(Request.BYE);
    118         putName(Request.CANCEL);
    119         putName(Request.ACK);
    120         putName(Request.PRACK);
    121         putName(Request.INFO);
    122         putName(Request.MESSAGE);
    123         putName(Request.NOTIFY);
    124         putName(Request.OPTIONS);
    125         putName(Request.PRACK);
    126         putName(Request.PUBLISH);
    127         putName(Request.REFER);
    128         putName(Request.REGISTER);
    129         putName(Request.SUBSCRIBE);
    130         putName(Request.UPDATE);
    131 
    132     }
    133 
    134     /**
    135      * @return true iff the method is a target refresh
    136      */
    137     public static boolean isTargetRefresh(String ucaseMethod) {
    138         return targetRefreshMethods.contains(ucaseMethod);
    139     }
    140 
    141     /**
    142      * @return true iff the method is a dialog creating method
    143      */
    144     public static boolean isDialogCreating(String ucaseMethod) {
    145         return SIPTransactionStack.isDialogCreated(ucaseMethod);
    146     }
    147 
    148     /**
    149      * Set to standard constants to speed up processing. this makes equals comparisons run much
    150      * faster in the stack because then it is just identity comparision. Character by char
    151      * comparison is not required. The method returns the String CONSTANT corresponding to the
    152      * String name.
    153      *
    154      */
    155     public static String getCannonicalName(String method) {
    156 
    157         if (nameTable.containsKey(method))
    158             return (String) nameTable.get(method);
    159         else
    160             return method;
    161     }
    162 
    163     /**
    164      * Get the Request Line of the SIPRequest.
    165      *
    166      * @return the request line of the SIP Request.
    167      */
    168 
    169     public RequestLine getRequestLine() {
    170         return requestLine;
    171     }
    172 
    173     /**
    174      * Set the request line of the SIP Request.
    175      *
    176      * @param requestLine is the request line to set in the SIP Request.
    177      */
    178 
    179     public void setRequestLine(RequestLine requestLine) {
    180         this.requestLine = requestLine;
    181     }
    182 
    183     /**
    184      * Constructor.
    185      */
    186     public SIPRequest() {
    187         super();
    188     }
    189 
    190     /**
    191      * Convert to a formatted string for pretty printing. Note that the encode method converts
    192      * this into a sip message that is suitable for transmission. Note hack here if you want to
    193      * convert the nice curly brackets into some grotesque XML tag.
    194      *
    195      * @return a string which can be used to examine the message contents.
    196      *
    197      */
    198     public String debugDump() {
    199         String superstring = super.debugDump();
    200         stringRepresentation = "";
    201         sprint(SIPRequest.class.getName());
    202         sprint("{");
    203         if (requestLine != null)
    204             sprint(requestLine.debugDump());
    205         sprint(superstring);
    206         sprint("}");
    207         return stringRepresentation;
    208     }
    209 
    210     /**
    211      * Check header for constraints. (1) Invite options and bye requests can only have SIP URIs in
    212      * the contact headers. (2) Request must have cseq, to and from and via headers. (3) Method in
    213      * request URI must match that in CSEQ.
    214      */
    215     public void checkHeaders() throws ParseException {
    216         String prefix = "Missing a required header : ";
    217 
    218         /* Check for required headers */
    219 
    220         if (getCSeq() == null) {
    221             throw new ParseException(prefix + CSeqHeader.NAME, 0);
    222         }
    223         if (getTo() == null) {
    224             throw new ParseException(prefix + ToHeader.NAME, 0);
    225         }
    226 
    227         if (this.callIdHeader == null || this.callIdHeader.getCallId() == null
    228                 || callIdHeader.getCallId().equals("")) {
    229             throw new ParseException(prefix + CallIdHeader.NAME, 0);
    230         }
    231         if (getFrom() == null) {
    232             throw new ParseException(prefix + FromHeader.NAME, 0);
    233         }
    234         if (getViaHeaders() == null) {
    235             throw new ParseException(prefix + ViaHeader.NAME, 0);
    236         }
    237         // BEGIN android-deleted
    238         /*
    239         if (getMaxForwards() == null) {
    240             throw new ParseException(prefix + MaxForwardsHeader.NAME, 0);
    241         }
    242         */
    243         // END android-deleted
    244 
    245         if (getTopmostVia() == null)
    246             throw new ParseException("No via header in request! ", 0);
    247 
    248         if (getMethod().equals(Request.NOTIFY)) {
    249             if (getHeader(SubscriptionStateHeader.NAME) == null)
    250                 throw new ParseException(prefix + SubscriptionStateHeader.NAME, 0);
    251 
    252             if (getHeader(EventHeader.NAME) == null)
    253                 throw new ParseException(prefix + EventHeader.NAME, 0);
    254 
    255         } else if (getMethod().equals(Request.PUBLISH)) {
    256             /*
    257              * For determining the type of the published event state, the EPA MUST include a
    258              * single Event header field in PUBLISH requests. The value of this header field
    259              * indicates the event package for which this request is publishing event state.
    260              */
    261             if (getHeader(EventHeader.NAME) == null)
    262                 throw new ParseException(prefix + EventHeader.NAME, 0);
    263         }
    264 
    265         /*
    266          * RFC 3261 8.1.1.8 The Contact header field MUST be present and contain exactly one SIP
    267          * or SIPS URI in any request that can result in the establishment of a dialog. For the
    268          * methods defined in this specification, that includes only the INVITE request. For these
    269          * requests, the scope of the Contact is global. That is, the Contact header field value
    270          * contains the URI at which the UA would like to receive requests, and this URI MUST be
    271          * valid even if used in subsequent requests outside of any dialogs.
    272          *
    273          * If the Request-URI or top Route header field value contains a SIPS URI, the Contact
    274          * header field MUST contain a SIPS URI as well.
    275          */
    276         if (requestLine.getMethod().equals(Request.INVITE)
    277                 || requestLine.getMethod().equals(Request.SUBSCRIBE)
    278                 || requestLine.getMethod().equals(Request.REFER)) {
    279             if (this.getContactHeader() == null) {
    280                 // Make sure this is not a target refresh. If this is a target
    281                 // refresh its ok not to have a contact header. Otherwise
    282                 // contact header is mandatory.
    283                 if (this.getToTag() == null)
    284                     throw new ParseException(prefix + ContactHeader.NAME, 0);
    285             }
    286 
    287             if (requestLine.getUri() instanceof SipUri) {
    288                 String scheme = ((SipUri) requestLine.getUri()).getScheme();
    289                 if ("sips".equalsIgnoreCase(scheme)) {
    290                     SipUri sipUri = (SipUri) this.getContactHeader().getAddress().getURI();
    291                     if (!sipUri.getScheme().equals("sips")) {
    292                         throw new ParseException("Scheme for contact should be sips:" + sipUri, 0);
    293                     }
    294                 }
    295             }
    296         }
    297 
    298         /*
    299          * Contact header is mandatory for a SIP INVITE request.
    300          */
    301         if (this.getContactHeader() == null
    302                 && (this.getMethod().equals(Request.INVITE)
    303                         || this.getMethod().equals(Request.REFER) || this.getMethod().equals(
    304                         Request.SUBSCRIBE))) {
    305             throw new ParseException("Contact Header is Mandatory for a SIP INVITE", 0);
    306         }
    307 
    308         if (requestLine != null && requestLine.getMethod() != null
    309                 && getCSeq().getMethod() != null
    310                 && requestLine.getMethod().compareTo(getCSeq().getMethod()) != 0) {
    311             throw new ParseException("CSEQ method mismatch with  Request-Line ", 0);
    312 
    313         }
    314 
    315     }
    316 
    317     /**
    318      * Set the default values in the request URI if necessary.
    319      */
    320     protected void setDefaults() {
    321         // The request line may be unparseable (set to null by the
    322         // exception handler.
    323         if (requestLine == null)
    324             return;
    325         String method = requestLine.getMethod();
    326         // The requestLine may be malformed!
    327         if (method == null)
    328             return;
    329         GenericURI u = requestLine.getUri();
    330         if (u == null)
    331             return;
    332         if (method.compareTo(Request.REGISTER) == 0 || method.compareTo(Request.INVITE) == 0) {
    333             if (u instanceof SipUri) {
    334                 SipUri sipUri = (SipUri) u;
    335                 sipUri.setUserParam(DEFAULT_USER);
    336                 try {
    337                     sipUri.setTransportParam(DEFAULT_TRANSPORT);
    338                 } catch (ParseException ex) {
    339                 }
    340             }
    341         }
    342     }
    343 
    344     /**
    345      * Patch up the request line as necessary.
    346      */
    347     protected void setRequestLineDefaults() {
    348         String method = requestLine.getMethod();
    349         if (method == null) {
    350             CSeq cseq = (CSeq) this.getCSeq();
    351             if (cseq != null) {
    352                 method = getCannonicalName(cseq.getMethod());
    353                 requestLine.setMethod(method);
    354             }
    355         }
    356     }
    357 
    358     /**
    359      * A conveniance function to access the Request URI.
    360      *
    361      * @return the requestURI if it exists.
    362      */
    363     public javax.sip.address.URI getRequestURI() {
    364         if (this.requestLine == null)
    365             return null;
    366         else
    367             return (javax.sip.address.URI) this.requestLine.getUri();
    368     }
    369 
    370     /**
    371      * Sets the RequestURI of Request. The Request-URI is a SIP or SIPS URI or a general URI. It
    372      * indicates the user or service to which this request is being addressed. SIP elements MAY
    373      * support Request-URIs with schemes other than "sip" and "sips", for example the "tel" URI
    374      * scheme. SIP elements MAY translate non-SIP URIs using any mechanism at their disposal,
    375      * resulting in SIP URI, SIPS URI, or some other scheme.
    376      *
    377      * @param uri the new Request URI of this request message
    378      */
    379     public void setRequestURI(URI uri) {
    380         if ( uri == null ) {
    381             throw new NullPointerException("Null request URI");
    382         }
    383         if (this.requestLine == null) {
    384             this.requestLine = new RequestLine();
    385         }
    386         this.requestLine.setUri((GenericURI) uri);
    387         this.nullRequest = false;
    388     }
    389 
    390     /**
    391      * Set the method.
    392      *
    393      * @param method is the method to set.
    394      * @throws IllegalArgumentException if the method is null
    395      */
    396     public void setMethod(String method) {
    397         if (method == null)
    398             throw new IllegalArgumentException("null method");
    399         if (this.requestLine == null) {
    400             this.requestLine = new RequestLine();
    401         }
    402 
    403         // Set to standard constants to speed up processing.
    404         // this makes equals compares run much faster in the
    405         // stack because then it is just identity comparision
    406 
    407         String meth = getCannonicalName(method);
    408         this.requestLine.setMethod(meth);
    409 
    410         if (this.cSeqHeader != null) {
    411             try {
    412                 this.cSeqHeader.setMethod(meth);
    413             } catch (ParseException e) {
    414             }
    415         }
    416     }
    417 
    418     /**
    419      * Get the method from the request line.
    420      *
    421      * @return the method from the request line if the method exits and null if the request line
    422      *         or the method does not exist.
    423      */
    424     public String getMethod() {
    425         if (requestLine == null)
    426             return null;
    427         else
    428             return requestLine.getMethod();
    429     }
    430 
    431     /**
    432      * Encode the SIP Request as a string.
    433      *
    434      * @return an encoded String containing the encoded SIP Message.
    435      */
    436 
    437     public String encode() {
    438         String retval;
    439         if (requestLine != null) {
    440             this.setRequestLineDefaults();
    441             retval = requestLine.encode() + super.encode();
    442         } else if (this.isNullRequest()) {
    443             retval = "\r\n\r\n";
    444         } else {
    445             retval = super.encode();
    446         }
    447         return retval;
    448     }
    449 
    450     /**
    451      * Encode only the headers and not the content.
    452      */
    453     public String encodeMessage() {
    454         String retval;
    455         if (requestLine != null) {
    456             this.setRequestLineDefaults();
    457             retval = requestLine.encode() + super.encodeSIPHeaders();
    458         } else if (this.isNullRequest()) {
    459             retval = "\r\n\r\n";
    460         } else
    461             retval = super.encodeSIPHeaders();
    462         return retval;
    463 
    464     }
    465 
    466     /**
    467      * ALias for encode above.
    468      */
    469     public String toString() {
    470         return this.encode();
    471     }
    472 
    473     /**
    474      * Make a clone (deep copy) of this object. You can use this if you want to modify a request
    475      * while preserving the original
    476      *
    477      * @return a deep copy of this object.
    478      */
    479 
    480     public Object clone() {
    481         SIPRequest retval = (SIPRequest) super.clone();
    482         // Do not copy over the tx pointer -- this is only for internal
    483         // tracking.
    484         retval.transactionPointer = null;
    485         if (this.requestLine != null)
    486             retval.requestLine = (RequestLine) this.requestLine.clone();
    487 
    488         return retval;
    489     }
    490 
    491     /**
    492      * Compare for equality.
    493      *
    494      * @param other object to compare ourselves with.
    495      */
    496     public boolean equals(Object other) {
    497         if (!this.getClass().equals(other.getClass()))
    498             return false;
    499         SIPRequest that = (SIPRequest) other;
    500 
    501         return requestLine.equals(that.requestLine) && super.equals(other);
    502     }
    503 
    504     /**
    505      * Get the message as a linked list of strings. Use this if you want to iterate through the
    506      * message.
    507      *
    508      * @return a linked list containing the request line and headers encoded as strings.
    509      */
    510     public LinkedList getMessageAsEncodedStrings() {
    511         LinkedList retval = super.getMessageAsEncodedStrings();
    512         if (requestLine != null) {
    513             this.setRequestLineDefaults();
    514             retval.addFirst(requestLine.encode());
    515         }
    516         return retval;
    517 
    518     }
    519 
    520     /**
    521      * Match with a template. You can use this if you want to match incoming messages with a
    522      * pattern and do something when you find a match. This is useful for building filters/pattern
    523      * matching responders etc.
    524      *
    525      * @param matchObj object to match ourselves with (null matches wildcard)
    526      *
    527      */
    528     public boolean match(Object matchObj) {
    529         if (matchObj == null)
    530             return true;
    531         else if (!matchObj.getClass().equals(this.getClass()))
    532             return false;
    533         else if (matchObj == this)
    534             return true;
    535         SIPRequest that = (SIPRequest) matchObj;
    536         RequestLine rline = that.requestLine;
    537         if (this.requestLine == null && rline != null)
    538             return false;
    539         else if (this.requestLine == rline)
    540             return super.match(matchObj);
    541         return requestLine.match(that.requestLine) && super.match(matchObj);
    542 
    543     }
    544 
    545     /**
    546      * Get a dialog identifier. Generates a string that can be used as a dialog identifier.
    547      *
    548      * @param isServer is set to true if this is the UAS and set to false if this is the UAC
    549      */
    550     public String getDialogId(boolean isServer) {
    551         CallID cid = (CallID) this.getCallId();
    552         StringBuffer retval = new StringBuffer(cid.getCallId());
    553         From from = (From) this.getFrom();
    554         To to = (To) this.getTo();
    555         if (!isServer) {
    556             // retval.append(COLON).append(from.getUserAtHostPort());
    557             if (from.getTag() != null) {
    558                 retval.append(COLON);
    559                 retval.append(from.getTag());
    560             }
    561             // retval.append(COLON).append(to.getUserAtHostPort());
    562             if (to.getTag() != null) {
    563                 retval.append(COLON);
    564                 retval.append(to.getTag());
    565             }
    566         } else {
    567             // retval.append(COLON).append(to.getUserAtHostPort());
    568             if (to.getTag() != null) {
    569                 retval.append(COLON);
    570                 retval.append(to.getTag());
    571             }
    572             // retval.append(COLON).append(from.getUserAtHostPort());
    573             if (from.getTag() != null) {
    574                 retval.append(COLON);
    575                 retval.append(from.getTag());
    576             }
    577         }
    578         return retval.toString().toLowerCase();
    579 
    580     }
    581 
    582     /**
    583      * Get a dialog id given the remote tag.
    584      */
    585     public String getDialogId(boolean isServer, String toTag) {
    586         From from = (From) this.getFrom();
    587         CallID cid = (CallID) this.getCallId();
    588         StringBuffer retval = new StringBuffer(cid.getCallId());
    589         if (!isServer) {
    590             // retval.append(COLON).append(from.getUserAtHostPort());
    591             if (from.getTag() != null) {
    592                 retval.append(COLON);
    593                 retval.append(from.getTag());
    594             }
    595             // retval.append(COLON).append(to.getUserAtHostPort());
    596             if (toTag != null) {
    597                 retval.append(COLON);
    598                 retval.append(toTag);
    599             }
    600         } else {
    601             // retval.append(COLON).append(to.getUserAtHostPort());
    602             if (toTag != null) {
    603                 retval.append(COLON);
    604                 retval.append(toTag);
    605             }
    606             // retval.append(COLON).append(from.getUserAtHostPort());
    607             if (from.getTag() != null) {
    608                 retval.append(COLON);
    609                 retval.append(from.getTag());
    610             }
    611         }
    612         return retval.toString().toLowerCase();
    613     }
    614 
    615     /**
    616      * Encode this into a byte array. This is used when the body has been set as a binary array
    617      * and you want to encode the body as a byte array for transmission.
    618      *
    619      * @return a byte array containing the SIPRequest encoded as a byte array.
    620      */
    621 
    622     public byte[] encodeAsBytes(String transport) {
    623         if (this.isNullRequest()) {
    624             // Encoding a null message for keepalive.
    625             return "\r\n\r\n".getBytes();
    626         } else if ( this.requestLine == null ) {
    627             return new byte[0];
    628         }
    629 
    630         byte[] rlbytes = null;
    631         if (requestLine != null) {
    632             try {
    633                 rlbytes = requestLine.encode().getBytes("UTF-8");
    634             } catch (UnsupportedEncodingException ex) {
    635                 InternalErrorHandler.handleException(ex);
    636             }
    637         }
    638         byte[] superbytes = super.encodeAsBytes(transport);
    639         byte[] retval = new byte[rlbytes.length + superbytes.length];
    640         System.arraycopy(rlbytes, 0, retval, 0, rlbytes.length);
    641         System.arraycopy(superbytes, 0, retval, rlbytes.length, superbytes.length);
    642         return retval;
    643     }
    644 
    645     /**
    646      * Creates a default SIPResponse message for this request. Note You must add the necessary
    647      * tags to outgoing responses if need be. For efficiency, this method does not clone the
    648      * incoming request. If you want to modify the outgoing response, be sure to clone the
    649      * incoming request as the headers are shared and any modification to the headers of the
    650      * outgoing response will result in a modification of the incoming request. Tag fields are
    651      * just copied from the incoming request. Contact headers are removed from the incoming
    652      * request. Added by Jeff Keyser.
    653      *
    654      * @param statusCode Status code for the response. Reason phrase is generated.
    655      *
    656      * @return A SIPResponse with the status and reason supplied, and a copy of all the original
    657      *         headers from this request.
    658      */
    659 
    660     public SIPResponse createResponse(int statusCode) {
    661 
    662         String reasonPhrase = SIPResponse.getReasonPhrase(statusCode);
    663         return this.createResponse(statusCode, reasonPhrase);
    664 
    665     }
    666 
    667     /**
    668      * Creates a default SIPResponse message for this request. Note You must add the necessary
    669      * tags to outgoing responses if need be. For efficiency, this method does not clone the
    670      * incoming request. If you want to modify the outgoing response, be sure to clone the
    671      * incoming request as the headers are shared and any modification to the headers of the
    672      * outgoing response will result in a modification of the incoming request. Tag fields are
    673      * just copied from the incoming request. Contact headers are removed from the incoming
    674      * request. Added by Jeff Keyser. Route headers are not added to the response.
    675      *
    676      * @param statusCode Status code for the response.
    677      * @param reasonPhrase Reason phrase for this response.
    678      *
    679      * @return A SIPResponse with the status and reason supplied, and a copy of all the original
    680      *         headers from this request except the ones that are not supposed to be part of the
    681      *         response .
    682      */
    683 
    684     public SIPResponse createResponse(int statusCode, String reasonPhrase) {
    685         SIPResponse newResponse;
    686         Iterator headerIterator;
    687         SIPHeader nextHeader;
    688 
    689         newResponse = new SIPResponse();
    690         try {
    691             newResponse.setStatusCode(statusCode);
    692         } catch (ParseException ex) {
    693             throw new IllegalArgumentException("Bad code " + statusCode);
    694         }
    695         if (reasonPhrase != null)
    696             newResponse.setReasonPhrase(reasonPhrase);
    697         else
    698             newResponse.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode));
    699         headerIterator = getHeaders();
    700         while (headerIterator.hasNext()) {
    701             nextHeader = (SIPHeader) headerIterator.next();
    702             if (nextHeader instanceof From
    703                     || nextHeader instanceof To
    704                     || nextHeader instanceof ViaList
    705                     || nextHeader instanceof CallID
    706                     || (nextHeader instanceof RecordRouteList && mustCopyRR(statusCode))
    707                     || nextHeader instanceof CSeq
    708                     // We just copy TimeStamp for all headers (not just 100).
    709                     || nextHeader instanceof TimeStamp) {
    710 
    711                 try {
    712 
    713                     newResponse.attachHeader((SIPHeader) nextHeader.clone(), false);
    714                 } catch (SIPDuplicateHeaderException e) {
    715                     e.printStackTrace();
    716                 }
    717             }
    718         }
    719         if (MessageFactoryImpl.getDefaultServerHeader() != null) {
    720             newResponse.setHeader(MessageFactoryImpl.getDefaultServerHeader());
    721 
    722         }
    723         if (newResponse.getStatusCode() == 100) {
    724             // Trying is never supposed to have the tag parameter set.
    725             newResponse.getTo().removeParameter("tag");
    726 
    727         }
    728         ServerHeader server = MessageFactoryImpl.getDefaultServerHeader();
    729         if (server != null) {
    730             newResponse.setHeader(server);
    731         }
    732         return newResponse;
    733     }
    734 
    735     // Helper method for createResponse, to avoid copying Record-Route unless needed
    736     private final boolean mustCopyRR( int code ) {
    737     	// Only for 1xx-2xx, not for 100 or errors
    738     	if ( code>100 && code<300 ) {
    739     		return isDialogCreating( this.getMethod() ) && getToTag() == null;
    740     	} else return false;
    741     }
    742 
    743     /**
    744      * Creates a default SIPResquest message that would cancel this request. Note that tag
    745      * assignment and removal of is left to the caller (we use whatever tags are present in the
    746      * original request).
    747      *
    748      * @return A CANCEL SIPRequest constructed according to RFC3261 section 9.1
    749      *
    750      * @throws SipException
    751      * @throws ParseException
    752      */
    753     public SIPRequest createCancelRequest() throws SipException {
    754 
    755         // see RFC3261 9.1
    756 
    757         // A CANCEL request SHOULD NOT be sent to cancel a request other than
    758         // INVITE
    759 
    760         if (!this.getMethod().equals(Request.INVITE))
    761             throw new SipException("Attempt to create CANCEL for " + this.getMethod());
    762 
    763         /*
    764          * The following procedures are used to construct a CANCEL request. The Request-URI,
    765          * Call-ID, To, the numeric part of CSeq, and From header fields in the CANCEL request
    766          * MUST be identical to those in the request being cancelled, including tags. A CANCEL
    767          * constructed by a client MUST have only a single Via header field value matching the top
    768          * Via value in the request being cancelled. Using the same values for these header fields
    769          * allows the CANCEL to be matched with the request it cancels (Section 9.2 indicates how
    770          * such matching occurs). However, the method part of the CSeq header field MUST have a
    771          * value of CANCEL. This allows it to be identified and processed as a transaction in its
    772          * own right (See Section 17).
    773          */
    774         SIPRequest cancel = new SIPRequest();
    775         cancel.setRequestLine((RequestLine) this.requestLine.clone());
    776         cancel.setMethod(Request.CANCEL);
    777         cancel.setHeader((Header) this.callIdHeader.clone());
    778         cancel.setHeader((Header) this.toHeader.clone());
    779         cancel.setHeader((Header) cSeqHeader.clone());
    780         try {
    781             cancel.getCSeq().setMethod(Request.CANCEL);
    782         } catch (ParseException e) {
    783             e.printStackTrace(); // should not happen
    784         }
    785         cancel.setHeader((Header) this.fromHeader.clone());
    786 
    787         cancel.addFirst((Header) this.getTopmostVia().clone());
    788         cancel.setHeader((Header) this.maxForwardsHeader.clone());
    789 
    790         /*
    791          * If the request being cancelled contains a Route header field, the CANCEL request MUST
    792          * include that Route header field's values.
    793          */
    794         if (this.getRouteHeaders() != null) {
    795             cancel.setHeader((SIPHeaderList< ? >) this.getRouteHeaders().clone());
    796         }
    797         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
    798             cancel.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
    799 
    800         }
    801         return cancel;
    802     }
    803 
    804     /**
    805      * Creates a default ACK SIPRequest message for this original request. Note that the
    806      * defaultACK SIPRequest does not include the content of the original SIPRequest. If
    807      * responseToHeader is null then the toHeader of this request is used to construct the ACK.
    808      * Note that tag fields are just copied from the original SIP Request. Added by Jeff Keyser.
    809      *
    810      * @param responseToHeader To header to use for this request.
    811      *
    812      * @return A SIPRequest with an ACK method.
    813      */
    814     public SIPRequest createAckRequest(To responseToHeader) {
    815         SIPRequest newRequest;
    816         Iterator headerIterator;
    817         SIPHeader nextHeader;
    818 
    819         newRequest = new SIPRequest();
    820         newRequest.setRequestLine((RequestLine) this.requestLine.clone());
    821         newRequest.setMethod(Request.ACK);
    822         headerIterator = getHeaders();
    823         while (headerIterator.hasNext()) {
    824             nextHeader = (SIPHeader) headerIterator.next();
    825             if (nextHeader instanceof RouteList) {
    826                 // Ack and cancel do not get ROUTE headers.
    827                 // Route header for ACK is assigned by the
    828                 // Dialog if necessary.
    829                 continue;
    830             } else if (nextHeader instanceof ProxyAuthorization) {
    831                 // Remove proxy auth header.
    832                 // Assigned by the Dialog if necessary.
    833                 continue;
    834             } else if (nextHeader instanceof ContentLength) {
    835                 // Adding content is responsibility of user.
    836                 nextHeader = (SIPHeader) nextHeader.clone();
    837                 try {
    838                     ((ContentLength) nextHeader).setContentLength(0);
    839                 } catch (InvalidArgumentException e) {
    840                 }
    841             } else if (nextHeader instanceof ContentType) {
    842                 // Content type header is removed since
    843                 // content length is 0.
    844                 continue;
    845             } else if (nextHeader instanceof CSeq) {
    846                 // The CSeq header field in the
    847                 // ACK MUST contain the same value for the
    848                 // sequence number as was present in the
    849                 // original request, but the method parameter
    850                 // MUST be equal to "ACK".
    851                 CSeq cseq = (CSeq) nextHeader.clone();
    852                 try {
    853                     cseq.setMethod(Request.ACK);
    854                 } catch (ParseException e) {
    855                 }
    856                 nextHeader = cseq;
    857             } else if (nextHeader instanceof To) {
    858                 if (responseToHeader != null) {
    859                     nextHeader = responseToHeader;
    860                 } else {
    861                     nextHeader = (SIPHeader) nextHeader.clone();
    862                 }
    863             } else if (nextHeader instanceof ContactList || nextHeader instanceof Expires) {
    864                 // CONTACT header does not apply for ACK requests.
    865                 continue;
    866             } else if (nextHeader instanceof ViaList) {
    867                 // Bug reported by Gianluca Martinello
    868                 // The ACK MUST contain a single Via header field,
    869                 // and this MUST be equal to the top Via header
    870                 // field of the original
    871                 // request.
    872 
    873                 nextHeader = (SIPHeader) ((ViaList) nextHeader).getFirst().clone();
    874             } else {
    875                 nextHeader = (SIPHeader) nextHeader.clone();
    876             }
    877 
    878             try {
    879                 newRequest.attachHeader(nextHeader, false);
    880             } catch (SIPDuplicateHeaderException e) {
    881                 e.printStackTrace();
    882             }
    883         }
    884         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
    885             newRequest.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
    886 
    887         }
    888         return newRequest;
    889     }
    890 
    891     /**
    892      * Creates an ACK for non-2xx responses according to RFC3261 17.1.1.3
    893      *
    894      * @return A SIPRequest with an ACK method.
    895      * @throws SipException
    896      * @throws NullPointerException
    897      * @throws ParseException
    898      *
    899      * @author jvb
    900      */
    901     public final SIPRequest createErrorAck(To responseToHeader) throws SipException,
    902             ParseException {
    903 
    904         /*
    905          * The ACK request constructed by the client transaction MUST contain values for the
    906          * Call-ID, From, and Request-URI that are equal to the values of those header fields in
    907          * the request passed to the transport by the client transaction (call this the "original
    908          * request"). The To header field in the ACK MUST equal the To header field in the
    909          * response being acknowledged, and therefore will usually differ from the To header field
    910          * in the original request by the addition of the tag parameter. The ACK MUST contain a
    911          * single Via header field, and this MUST be equal to the top Via header field of the
    912          * original request. The CSeq header field in the ACK MUST contain the same value for the
    913          * sequence number as was present in the original request, but the method parameter MUST
    914          * be equal to "ACK".
    915          */
    916         SIPRequest newRequest = new SIPRequest();
    917         newRequest.setRequestLine((RequestLine) this.requestLine.clone());
    918         newRequest.setMethod(Request.ACK);
    919         newRequest.setHeader((Header) this.callIdHeader.clone());
    920         newRequest.setHeader((Header) this.maxForwardsHeader.clone()); // ISSUE
    921         // 130
    922         // fix
    923         newRequest.setHeader((Header) this.fromHeader.clone());
    924         newRequest.setHeader((Header) responseToHeader.clone());
    925         newRequest.addFirst((Header) this.getTopmostVia().clone());
    926         newRequest.setHeader((Header) cSeqHeader.clone());
    927         newRequest.getCSeq().setMethod(Request.ACK);
    928 
    929         /*
    930          * If the INVITE request whose response is being acknowledged had Route header fields,
    931          * those header fields MUST appear in the ACK. This is to ensure that the ACK can be
    932          * routed properly through any downstream stateless proxies.
    933          */
    934         if (this.getRouteHeaders() != null) {
    935             newRequest.setHeader((SIPHeaderList) this.getRouteHeaders().clone());
    936         }
    937         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
    938             newRequest.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
    939 
    940         }
    941         return newRequest;
    942     }
    943 
    944     /**
    945      * Create a new default SIPRequest from the original request. Warning: the newly created
    946      * SIPRequest, shares the headers of this request but we generate any new headers that we need
    947      * to modify so the original request is umodified. However, if you modify the shared headers
    948      * after this request is created, then the newly created request will also be modified. If you
    949      * want to modify the original request without affecting the returned Request make sure you
    950      * clone it before calling this method.
    951      *
    952      * Only required headers are copied.
    953      * <ul>
    954      * <li> Contact headers are not included in the newly created request. Setting the appropriate
    955      * sequence number is the responsibility of the caller. </li>
    956      * <li> RouteList is not copied for ACK and CANCEL </li>
    957      * <li> Note that we DO NOT copy the body of the argument into the returned header. We do not
    958      * copy the content type header from the original request either. These have to be added
    959      * seperately and the content length has to be correctly set if necessary the content length
    960      * is set to 0 in the returned header. </li>
    961      * <li>Contact List is not copied from the original request.</li>
    962      * <li>RecordRoute List is not included from original request. </li>
    963      * <li>Via header is not included from the original request. </li>
    964      * </ul>
    965      *
    966      * @param requestLine is the new request line.
    967      *
    968      * @param switchHeaders is a boolean flag that causes to and from headers to switch (set this
    969      *        to true if you are the server of the transaction and are generating a BYE request).
    970      *        If the headers are switched, we generate new From and To headers otherwise we just
    971      *        use the incoming headers.
    972      *
    973      * @return a new Default SIP Request which has the requestLine specified.
    974      *
    975      */
    976     public SIPRequest createSIPRequest(RequestLine requestLine, boolean switchHeaders) {
    977         SIPRequest newRequest = new SIPRequest();
    978         newRequest.requestLine = requestLine;
    979         Iterator<SIPHeader> headerIterator = this.getHeaders();
    980         while (headerIterator.hasNext()) {
    981             SIPHeader nextHeader = (SIPHeader) headerIterator.next();
    982             // For BYE and cancel set the CSeq header to the
    983             // appropriate method.
    984             if (nextHeader instanceof CSeq) {
    985                 CSeq newCseq = (CSeq) nextHeader.clone();
    986                 nextHeader = newCseq;
    987                 try {
    988                     newCseq.setMethod(requestLine.getMethod());
    989                 } catch (ParseException e) {
    990                 }
    991             } else if (nextHeader instanceof ViaList) {
    992                 Via via = (Via) (((ViaList) nextHeader).getFirst().clone());
    993                 via.removeParameter("branch");
    994                 nextHeader = via;
    995                 // Cancel and ACK preserve the branch ID.
    996             } else if (nextHeader instanceof To) {
    997                 To to = (To) nextHeader;
    998                 if (switchHeaders) {
    999                     nextHeader = new From(to);
   1000                     ((From) nextHeader).removeTag();
   1001                 } else {
   1002                     nextHeader = (SIPHeader) to.clone();
   1003                     ((To) nextHeader).removeTag();
   1004                 }
   1005             } else if (nextHeader instanceof From) {
   1006                 From from = (From) nextHeader;
   1007                 if (switchHeaders) {
   1008                     nextHeader = new To(from);
   1009                     ((To) nextHeader).removeTag();
   1010                 } else {
   1011                     nextHeader = (SIPHeader) from.clone();
   1012                     ((From) nextHeader).removeTag();
   1013                 }
   1014             } else if (nextHeader instanceof ContentLength) {
   1015                 ContentLength cl = (ContentLength) nextHeader.clone();
   1016                 try {
   1017                     cl.setContentLength(0);
   1018                 } catch (InvalidArgumentException e) {
   1019                 }
   1020                 nextHeader = cl;
   1021             } else if (!(nextHeader instanceof CallID) && !(nextHeader instanceof MaxForwards)) {
   1022                 // Route is kept by dialog.
   1023                 // RR is added by the caller.
   1024                 // Contact is added by the Caller
   1025                 // Any extension headers must be added
   1026                 // by the caller.
   1027                 continue;
   1028             }
   1029             try {
   1030                 newRequest.attachHeader(nextHeader, false);
   1031             } catch (SIPDuplicateHeaderException e) {
   1032                 e.printStackTrace();
   1033             }
   1034         }
   1035         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
   1036             newRequest.setHeader(MessageFactoryImpl.getDefaultUserAgentHeader());
   1037 
   1038         }
   1039         return newRequest;
   1040 
   1041     }
   1042 
   1043     /**
   1044      * Create a BYE request from this request.
   1045      *
   1046      * @param switchHeaders is a boolean flag that causes from and isServerTransaction to headers
   1047      *        to be swapped. Set this to true if you are the server of the dialog and are
   1048      *        generating a BYE request for the dialog.
   1049      * @return a new default BYE request.
   1050      */
   1051     public SIPRequest createBYERequest(boolean switchHeaders) {
   1052         RequestLine requestLine = (RequestLine) this.requestLine.clone();
   1053         requestLine.setMethod("BYE");
   1054         return this.createSIPRequest(requestLine, switchHeaders);
   1055     }
   1056 
   1057     /**
   1058      * Create an ACK request from this request. This is suitable for generating an ACK for an
   1059      * INVITE client transaction.
   1060      *
   1061      * @return an ACK request that is generated from this request.
   1062      */
   1063     public SIPRequest createACKRequest() {
   1064         RequestLine requestLine = (RequestLine) this.requestLine.clone();
   1065         requestLine.setMethod(Request.ACK);
   1066         return this.createSIPRequest(requestLine, false);
   1067     }
   1068 
   1069     /**
   1070      * Get the host from the topmost via header.
   1071      *
   1072      * @return the string representation of the host from the topmost via header.
   1073      */
   1074     public String getViaHost() {
   1075         Via via = (Via) this.getViaHeaders().getFirst();
   1076         return via.getHost();
   1077 
   1078     }
   1079 
   1080     /**
   1081      * Get the port from the topmost via header.
   1082      *
   1083      * @return the port from the topmost via header (5060 if there is no port indicated).
   1084      */
   1085     public int getViaPort() {
   1086         Via via = (Via) this.getViaHeaders().getFirst();
   1087         if (via.hasPort())
   1088             return via.getPort();
   1089         else
   1090             return 5060;
   1091     }
   1092 
   1093     /**
   1094      * Get the first line encoded.
   1095      *
   1096      * @return a string containing the encoded request line.
   1097      */
   1098     public String getFirstLine() {
   1099         if (requestLine == null)
   1100             return null;
   1101         else
   1102             return this.requestLine.encode();
   1103     }
   1104 
   1105     /**
   1106      * Set the sip version.
   1107      *
   1108      * @param sipVersion the sip version to set.
   1109      */
   1110     public void setSIPVersion(String sipVersion) throws ParseException {
   1111         if (sipVersion == null || !sipVersion.equalsIgnoreCase("SIP/2.0"))
   1112             throw new ParseException("sipVersion", 0);
   1113         this.requestLine.setSipVersion(sipVersion);
   1114     }
   1115 
   1116     /**
   1117      * Get the SIP version.
   1118      *
   1119      * @return the SIP version from the request line.
   1120      */
   1121     public String getSIPVersion() {
   1122         return this.requestLine.getSipVersion();
   1123     }
   1124 
   1125     /**
   1126      * Book keeping method to return the current tx for the request if one exists.
   1127      *
   1128      * @return the assigned tx.
   1129      */
   1130     public Object getTransaction() {
   1131         // Return an opaque pointer to the transaction object.
   1132         // This is for consistency checking and quick lookup.
   1133         return this.transactionPointer;
   1134     }
   1135 
   1136     /**
   1137      * Book keeping field to set the current tx for the request.
   1138      *
   1139      * @param transaction
   1140      */
   1141     public void setTransaction(Object transaction) {
   1142         this.transactionPointer = transaction;
   1143     }
   1144 
   1145     /**
   1146      * Book keeping method to get the messasge channel for the request.
   1147      *
   1148      * @return the message channel for the request.
   1149      */
   1150 
   1151     public Object getMessageChannel() {
   1152         // return opaque ptr to the message chanel on
   1153         // which the message was recieved. For consistency
   1154         // checking and lookup.
   1155         return this.messageChannel;
   1156     }
   1157 
   1158     /**
   1159      * Set the message channel for the request ( bookkeeping field ).
   1160      *
   1161      * @param messageChannel
   1162      */
   1163 
   1164     public void setMessageChannel(Object messageChannel) {
   1165         this.messageChannel = messageChannel;
   1166     }
   1167 
   1168     /**
   1169      * Generates an Id for checking potentially merged requests.
   1170      *
   1171      * @return String to check for merged requests
   1172      */
   1173     public String getMergeId() {
   1174         /*
   1175          * generate an identifier from the From tag, Call-ID, and CSeq
   1176          */
   1177         String fromTag = this.getFromTag();
   1178         String cseq = this.cSeqHeader.toString();
   1179         String callId = this.callIdHeader.getCallId();
   1180         /* NOTE : The RFC does NOT specify you need to include a Request URI
   1181          * This is added here for the case of Back to Back User Agents.
   1182          */
   1183         String requestUri = this.getRequestURI().toString();
   1184 
   1185         if (fromTag != null) {
   1186             return new StringBuffer().append(requestUri).append(":").append(fromTag).append(":").append(cseq).append(":")
   1187                     .append(callId).toString();
   1188         } else
   1189             return null;
   1190 
   1191     }
   1192 
   1193     /**
   1194      * @param inviteTransaction the inviteTransaction to set
   1195      */
   1196     public void setInviteTransaction(Object inviteTransaction) {
   1197         this.inviteTransaction = inviteTransaction;
   1198     }
   1199 
   1200     /**
   1201      * @return the inviteTransaction
   1202      */
   1203     public Object getInviteTransaction() {
   1204         return inviteTransaction;
   1205     }
   1206 
   1207 
   1208 
   1209 
   1210 
   1211 }
   1212