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 *******************************************************************************/
29package gov.nist.javax.sip.message;
30
31import gov.nist.javax.sip.address.*;
32import gov.nist.core.*;
33
34import java.util.HashSet;
35import java.util.Hashtable;
36import java.util.LinkedList;
37import java.util.Set;
38import java.io.UnsupportedEncodingException;
39import java.util.Iterator;
40import javax.sip.address.URI;
41import javax.sip.message.*;
42
43import java.text.ParseException;
44import javax.sip.*;
45import javax.sip.header.*;
46
47import gov.nist.javax.sip.header.*;
48import 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
73public 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