• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
putName(String name)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      */
isTargetRefresh(String ucaseMethod)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      */
isDialogCreating(String ucaseMethod)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      */
getCannonicalName(String method)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 
getRequestLine()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 
setRequestLine(RequestLine requestLine)179     public void setRequestLine(RequestLine requestLine) {
180         this.requestLine = requestLine;
181     }
182 
183     /**
184      * Constructor.
185      */
SIPRequest()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      */
debugDump()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      */
checkHeaders()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      */
setDefaults()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      */
setRequestLineDefaults()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      */
getRequestURI()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      */
setRequestURI(URI uri)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      */
setMethod(String method)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      */
getMethod()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 
encode()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      */
encodeMessage()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      */
toString()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 
clone()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      */
equals(Object other)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      */
getMessageAsEncodedStrings()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      */
match(Object matchObj)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      */
getDialogId(boolean isServer)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      */
getDialogId(boolean isServer, String toTag)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 
encodeAsBytes(String transport)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 
createResponse(int statusCode)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 
createResponse(int statusCode, String reasonPhrase)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
mustCopyRR( int code )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      */
createCancelRequest()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      */
createAckRequest(To responseToHeader)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      */
createErrorAck(To responseToHeader)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      */
createSIPRequest(RequestLine requestLine, boolean switchHeaders)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      */
createBYERequest(boolean switchHeaders)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      */
createACKRequest()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      */
getViaHost()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      */
getViaPort()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      */
getFirstLine()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      */
setSIPVersion(String sipVersion)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      */
getSIPVersion()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      */
getTransaction()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      */
setTransaction(Object transaction)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 
getMessageChannel()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 
setMessageChannel(Object messageChannel)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      */
getMergeId()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      */
setInviteTransaction(Object inviteTransaction)1196     public void setInviteTransaction(Object inviteTransaction) {
1197         this.inviteTransaction = inviteTransaction;
1198     }
1199 
1200     /**
1201      * @return the inviteTransaction
1202      */
getInviteTransaction()1203     public Object getInviteTransaction() {
1204         return inviteTransaction;
1205     }
1206 
1207 
1208 
1209 
1210 
1211 }
1212