001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: /*
026: */
027: package gov.nist.siplite.message;
028:
029: import gov.nist.core.*;
030: import gov.nist.siplite.address.*;
031: import gov.nist.siplite.header.*;
032: import gov.nist.siplite.SIPConstants;
033: import java.util.*;
034: import java.io.UnsupportedEncodingException;
035: import javax.microedition.sip.SipException;
036:
037: import com.sun.midp.log.Logging;
038: import com.sun.midp.log.LogChannels;
039: import gov.nist.microedition.io.j2me.sip.DistributedRandom;
040:
041: /**
042: * The SIP Request structure-- this belongs to the parser who fills it up.
043: *
044: * @version JAIN-SIP-1.1
045: *
046: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
047: *
048: */
049: public final class Request extends Message {
050: /** Acknowledgement request. */
051: public static final String ACK = "ACK";
052:
053: /** End of session request. */
054: public static final String BYE = "BYE";
055:
056: /** Terminate session request. */
057: public static final String CANCEL = "CANCEL";
058:
059: /** Invitation request. */
060: public static final String INVITE = "INVITE";
061:
062: /** Optional settings request. */
063: public static final String OPTIONS = "OPTIONS";
064:
065: /** Regsitration request. */
066: public static final String REGISTER = "REGISTER";
067:
068: /** Notification request. */
069: public static final String NOTIFY = "NOTIFY";
070:
071: /** Subscription for notification request. */
072: public static final String SUBSCRIBE = "SUBSCRIBE";
073:
074: /** Message request. */
075: public static final String MESSAGE = "MESSAGE";
076:
077: /** Redirection request. */
078: public static final String REFER = "REFER";
079:
080: /** Basic information request. */
081: public static final String INFO = "INFO";
082:
083: /** PRACK ??? RFC. */
084: public static final String PRACK = "PRACK";
085:
086: /** Update request. */
087: public static final String UPDATE = "UPDATE";
088:
089: /** Publish request. */
090: public static final String PUBLISH = "PUBLISH";
091:
092: /** Default user name is "ip". */
093: public static final String DEFAULT_USER = "ip";
094:
095: /** Default time to live is 1 second. */
096: public static final int DEFAULT_TTL = 1;
097:
098: /** Default transport is "udp". */
099: public static final String DEFAULT_TRANSPORT = SIPConstants.TRANSPORT_UDP;
100:
101: /** Default method is to intiate an INVITE. */
102: public static final String DEFAULT_METHOD = INVITE;
103:
104: /** Current transaction pointer. */
105: private Object transactionPointer;
106:
107: /** Current requestline. */
108: protected RequestLine requestLine;
109:
110: /**
111: * Gets the Request Line of the Request.
112: * @return the request line of the SIP Request.
113: */
114: public RequestLine getRequestLine() {
115: return requestLine;
116: }
117:
118: /**
119: * Sets the request line of the SIP Request.
120: * @param requestLine is the request line to set in the SIP Request.
121: */
122: public void setRequestLine(RequestLine requestLine) {
123: this .requestLine = requestLine;
124: }
125:
126: /**
127: * Constructor.
128: */
129: public Request() {
130: super ();
131: }
132:
133: /**
134: * Checks header for constraints.
135: * <pre>
136: * (1) Invite options and bye requests can only have SIP URIs in the
137: * contact headers.
138: * (2) Request must have cseq, to and from and via headers.
139: * (3) Method in request URI must match that in CSEQ.
140: * </pre>
141: */
142: protected void checkHeaders() throws ParseException {
143: String prefix = "Missing Header ";
144:
145: /* Check for required headers */
146:
147: if (getCSeqHeader() == null) {
148: throw new ParseException(prefix + Header.CSEQ, 0);
149: }
150: if (getTo() == null) {
151: throw new ParseException(prefix + Header.TO, 0);
152: }
153: if (getFromHeader() == null) {
154: throw new ParseException(prefix + Header.FROM, 0);
155: }
156: if (getViaHeaders() == null) {
157: throw new ParseException(prefix + Header.VIA, 0);
158: }
159:
160: /*
161: * BUGBUG
162: * Need to revisit this check later...
163: * for now we just leave this to the
164: * application to catch.
165: */
166:
167: if (requestLine != null
168: && requestLine.getMethod() != null
169: && getCSeqHeader().getMethod() != null
170: && compareToIgnoreCase(requestLine.getMethod(),
171: getCSeqHeader().getMethod()) != 0) {
172: throw new ParseException(
173: "CSEQ method mismatch with Request-Line ", 0);
174:
175: }
176:
177: }
178:
179: /**
180: * Sets the default values in the request URI if necessary.
181: */
182: protected void setDefaults() {
183: // The request line may be unparseable (set to null by the
184: // exception handler.
185: if (requestLine == null)
186: return;
187: String method = requestLine.getMethod();
188: // The requestLine may be malformed!
189: if (method == null)
190: return;
191: URI u = requestLine.getUri();
192: if (u == null)
193: return;
194: if (method.compareTo(REGISTER) == 0
195: || method.compareTo(INVITE) == 0) {
196: if (u instanceof SipURI) {
197: SipURI sipUri = (SipURI) u;
198: sipUri.setUserParam(DEFAULT_USER);
199: try {
200: sipUri.setTransportParam(DEFAULT_TRANSPORT);
201: } catch (ParseException ex) {
202: }
203: }
204: }
205: }
206:
207: /**
208: * Patch up the request line as necessary.
209: */
210: protected void setRequestLineDefaults() {
211: String method = requestLine.getMethod();
212: if (method == null) {
213: CSeqHeader cseq = (CSeqHeader) this .getCSeqHeader();
214: if (cseq != null) {
215: method = cseq.getMethod();
216: requestLine.setMethod(method);
217: }
218: }
219: }
220:
221: /**
222: * A conveniance function to access the Request URI.
223: * @return the requestURI if it exists.
224: */
225: public URI getRequestURI() {
226: if (this .requestLine == null)
227: return null;
228: else
229: return this .requestLine.getUri();
230: }
231:
232: /**
233: * Sets the RequestURI of Request. The Request-URI is a SIP or
234: * SIPS URI or a general URI. It indicates the user or service to which
235: * this request is being addressed. SIP elements MAY support
236: * Request-URIs with schemes other than "sip" and "sips", for
237: * example the "tel" URI scheme. SIP elements MAY translate
238: * non-SIP URIs using any mechanism at their disposal, resulting
239: * in SIP URI, SIPS URI, or some other scheme.
240: *
241: * @param uri the new Request URI of this request message
242: */
243: public void setRequestURI(URI uri) {
244: if (this .requestLine == null) {
245: this .requestLine = new RequestLine();
246: }
247: this .requestLine.setUri((URI) uri);
248: }
249:
250: /**
251: * Sets the method.
252: * @param method is the method to set.
253: * @throws IllegalArgumentException if the method is null
254: */
255: public void setMethod(String method)
256: throws IllegalArgumentException {
257: if (method == null)
258: throw new IllegalArgumentException("null method");
259: if (this .requestLine == null) {
260: this .requestLine = new RequestLine();
261: }
262: this .requestLine.setMethod(method);
263: if (this .cSeqHeader != null) {
264: this .cSeqHeader.setMethod(method);
265: }
266: }
267:
268: /**
269: * Gets the method from the request line.
270: * @return the method from the request line if the method exits and
271: * null if the request line or the method does not exist.
272: */
273: public String getMethod() {
274: if (requestLine == null)
275: return null;
276: else
277: return requestLine.getMethod();
278: }
279:
280: /**
281: * Encodes the SIP Request as a string.
282: *
283: * @return an encoded String containing the encoded SIP Message.
284: */
285:
286: public String encode() {
287: String retval;
288: if (requestLine != null) {
289: this .setRequestLineDefaults();
290: retval = requestLine.encode() + super .encode();
291: } else
292: retval = super .encode();
293: return retval;
294: }
295:
296: /**
297: * Alias for encode above.
298: * @return encoded string of object contents
299: */
300: public String toString() {
301: return this .encode();
302: }
303:
304: /**
305: * Makes a clone (deep copy) of this object.
306: * You can use this if you
307: * want to modify a request while preserving the original
308: *
309: * @return a deep copy of this object.
310: */
311:
312: public Object clone() {
313:
314: Request retval = (Request) super .clone();
315: if (this .requestLine != null) {
316: retval.requestLine = (RequestLine) this .requestLine.clone();
317: retval.setRequestLineDefaults();
318: }
319: return retval;
320: }
321:
322: /**
323: * Compares for equality.
324: *
325: * @param other object to compare ourselves with.
326: * @return true if objects match
327: */
328: public boolean equals(Object other) {
329: if (!this .getClass().equals(other.getClass()))
330: return false;
331: Request that = (Request) other;
332:
333: boolean retval = requestLine.equals(that.requestLine)
334: && super .equals(other);
335:
336: if (Logging.REPORT_LEVEL <= Logging.INFORMATION && !retval) {
337: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
338: "this ... >>>>" + encode());
339: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
340: "other ... >>>>" + that.encode());
341: }
342:
343: return retval;
344: }
345:
346: /**
347: * Encodes this into a byte array.
348: * This is used when the body has been set as a binary array
349: * and you want to encode the body as a byte array for transmission.
350: *
351: * @return a byte array containing the Request encoded as a byte
352: * array.
353: */
354: public byte[] encodeAsBytes() {
355: byte[] rlbytes = null;
356: if (requestLine != null) {
357: try {
358: rlbytes = requestLine.encode().getBytes("UTF-8");
359: } catch (UnsupportedEncodingException ex) {
360: InternalErrorHandler.handleException(ex);
361: }
362: }
363: byte[] super bytes = super .encodeAsBytes();
364: byte[] retval = new byte[rlbytes.length + super bytes.length];
365: int i = 0;
366: System.arraycopy(rlbytes, 0, retval, 0, rlbytes.length);
367: System.arraycopy(super bytes, 0, retval, rlbytes.length,
368: super bytes.length);
369: return retval;
370: }
371:
372: /**
373: * Creates a default Response message for this request. Note
374: * You must add the necessary tags to outgoing responses if need
375: * be. For efficiency, this method does not clone the incoming
376: * request. If you want to modify the outgoing response, be sure
377: * to clone the incoming request as the headers are shared and
378: * any modification to the headers of the outgoing response will
379: * result in a modification of the incoming request.
380: * Tag fields are just copied from the incoming request.
381: * Contact headers are removed from the incoming request.
382: * Added by Jeff Keyser.
383: *
384: * @param statusCode Status code for the response.
385: * Reason phrase is generated.
386: *
387: * @return A Response with the status and reason supplied, and a copy
388: *of all the original headers from this request.
389: */
390: public Response createResponse(int statusCode) {
391: String reasonPhrase = Response.getReasonPhrase(statusCode);
392: return createResponse(statusCode, reasonPhrase);
393: }
394:
395: /**
396: * Creates a default Response message for this request. Note
397: * You must add the necessary tags to outgoing responses if need
398: * be. For efficiency, this method does not clone the incoming
399: * request. If you want to modify the outgoing response, be sure
400: * to clone the incoming request as the headers are shared and
401: * any modification to the headers of the outgoing response will
402: * result in a modification of the incoming request.
403: * Tag fields are just copied from the incoming request.
404: * Contact headers are removed from the incoming request.
405: * Added by Jeff Keyser. Route headers are not added to the
406: * response.
407: *
408: * @param statusCode Status code for the response.
409: * @param reasonPhrase Reason phrase for this response.
410: * @return A Response with the status and reason supplied.
411: * @throws IllegalArgumentException if some argument has an invalid value.
412: */
413: public Response createResponse(int statusCode, String reasonPhrase)
414: throws IllegalArgumentException {
415: Response newResponse;
416: Enumeration headerIterator;
417: Header nextHeader;
418:
419: newResponse = new Response();
420: try {
421: newResponse.setStatusCode(statusCode);
422: } catch (ParseException ex) {
423: throw new IllegalArgumentException("Bad code " + statusCode);
424: }
425:
426: if (reasonPhrase != null) {
427: newResponse.setReasonPhrase(reasonPhrase);
428: } else {
429: newResponse.setReasonPhrase(Response
430: .getReasonPhrase(statusCode));
431: }
432:
433: headerIterator = super .getHeaders();
434:
435: // Time stamp header should be stamped with delay but
436: // we dont support this.
437: while (headerIterator.hasMoreElements()) {
438: nextHeader = (Header) headerIterator.nextElement();
439: if (nextHeader instanceof FromHeader
440: || nextHeader instanceof ToHeader
441: || nextHeader instanceof ViaList
442: || nextHeader instanceof CallIdHeader
443: || nextHeader instanceof RecordRouteList
444: || nextHeader instanceof CSeqHeader
445: ||
446: // RFC 3265, 3.1.1 200-class responses to SUBSCRIBE
447: // requests also MUST contain an "Expires" header.
448: nextHeader instanceof ExpiresHeader
449: || Utils.equalsIgnoreCase(nextHeader.getName(),
450: Header.TIMESTAMP)) {
451: try {
452: newResponse.attachHeader(nextHeader, false);
453: } catch (SipException ex) {
454: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
455: Logging.report(Logging.ERROR,
456: LogChannels.LC_JSR180,
457: "Request.createResponse(): can't attach header '"
458: + nextHeader.getHeaderName()
459: + "'.");
460: ex.printStackTrace();
461: }
462: }
463: } else if (Utils.equalsIgnoreCase(nextHeader.getName(),
464: Header.REQUIRE)) {
465: /*
466: * RFC3262, SECTION 3
467: * If the next header contains "Require" header with option
468: * tag as "100rel", we should add this header and also include
469: * RSeq header field
470: */
471: boolean isReliableProvResponse = Header
472: .isReliableTagPresent(nextHeader
473: .getHeaderValue());
474:
475: if (isReliableProvResponse) {
476: try {
477: newResponse.attachHeader(nextHeader, true);
478: } catch (SipException ex) {
479: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
480: Logging.report(Logging.ERROR,
481: LogChannels.LC_JSR180,
482: "Request.createResponse(): can't attach header"
483: + nextHeader
484: .getHeaderName()
485: + "'.");
486: // ex.printStackTrace();
487: }
488: }
489: }
490:
491: }
492: }
493:
494: // RFC 3903, p. 5:
495: // The Record-Route header field has no meaning in PUBLISH
496: // requests or responses, and MUST be ignored if present.
497: //
498: // RFC 3261, p. 63:
499: // Registrars MUST ignore the Record-Route header field if it is
500: // included in a REGISTER request. Registrars MUST NOT include a
501: // Record-Route header field in any response to a REGISTER request.
502: String method = getMethod();
503: if (method.equals(Request.PUBLISH)
504: || method.equals(Request.REGISTER)) {
505: newResponse.removeHeader(Header.RECORD_ROUTE);
506: }
507:
508: return newResponse;
509: }
510:
511: /**
512: * Creates a default SIPResquest message that would cancel
513: * this request. Note that tag assignment and removal of
514: * is left to the caller (we use whatever tags are present in the
515: * original request). Acknowledgement: Added by Jeff Keyser.
516: *
517: * @return A CANCEL Request with a copy all the original headers
518: * from this request except for Require, ProxyRequire.
519: * @throws SipException if the request can't be created.
520: */
521: public Request createCancelRequest() throws SipException {
522: Request newRequest;
523: Enumeration headerIterator;
524: Header nextHeader;
525:
526: newRequest = new Request();
527:
528: // JSR180: Request-URI // copy from original request
529: RequestLine cancelRequestLine = (RequestLine) this
530: .getRequestLine().clone();
531: cancelRequestLine.setMethod(CANCEL);
532: newRequest.setRequestLine(cancelRequestLine);
533: newRequest.setMethod(CANCEL);
534:
535: // JSR180: To // copy from original request
536: ToHeader toHeader = this .getTo();
537: if (toHeader != null) {
538: newRequest.setHeader(toHeader);
539: }
540:
541: // JSR180: From // copy from original request
542: FromHeader fromHeader = this .getFromHeader();
543: if (fromHeader != null) {
544: newRequest.setHeader(fromHeader);
545: }
546:
547: // JSR180: CSeq // same value for the sequence
548: // number as was present in the original request, but
549: // the method parameter MUST be equal to "CANCEL"
550: CSeqHeader cseqHeader = (CSeqHeader) this .getCSeqHeader()
551: .clone();
552: if (cseqHeader != null) {
553: cseqHeader.setMethod(CANCEL);
554: newRequest.setHeader(cseqHeader);
555: }
556:
557: // JSR180: Call-ID // copy from original request
558: CallIdHeader callIdHeader = this .getCallId();
559: if (callIdHeader != null) {
560: newRequest.setHeader(callIdHeader);
561: }
562:
563: // JSR180: Via // single value equal to the
564: // top Via header field of the request being cancelled
565: ViaHeader viaHeader = this .getTopmostVia();
566: if (viaHeader != null) {
567: newRequest.setHeader(viaHeader);
568: }
569:
570: // JSR180: Route // If the request being cancelled
571: // contains a Route header field, the CANCEL request MUST
572: // include that Route header field's values
573: RouteList routeList = this .getRouteHeaders();
574: if (routeList != null) {
575: newRequest.setHeaders(routeList.getHeaders());
576: }
577:
578: // JSR180: Max-Forwards (TBD)// header field serves to limit the
579: // number of hops a request can transit on the way to its destination.
580: // Current version: copy from original request
581: MaxForwardsHeader mfHeader = (MaxForwardsHeader) getHeader(Header.MAX_FORWARDS);
582: if (mfHeader != null) {
583: newRequest.setHeader(mfHeader);
584: }
585:
586: return newRequest;
587: }
588:
589: /**
590: * Creates a default ACK Request message for this original request.
591: * Note that the defaultACK Request does not include the
592: * content of the original Request. If responseToHeader
593: * is null then the toHeader of this request is used to
594: * construct the ACK. Note that tag fields are just copied
595: * from the original SIP Request. Added by Jeff Keyser.
596: *
597: * @param responseToHeader To header to use for this request.
598: * @return A Request with an ACK method.
599: * @throws SipException if the request can't be created.
600: */
601: public Request createAckRequest(ToHeader responseToHeader)
602: throws SipException {
603: Request newRequest;
604: Enumeration headerIterator;
605: Header nextHeader;
606:
607: newRequest = new Request();
608: newRequest.setRequestLine((RequestLine) this .requestLine
609: .clone());
610: newRequest.setMethod(ACK);
611: headerIterator = getHeaders();
612: while (headerIterator.hasMoreElements()) {
613: nextHeader = (Header) headerIterator.nextElement();
614: if (nextHeader.getHeaderName().equals(Header.ROUTE)) {
615:
616: // Route header for ACK is assigned by the
617: // Dialog if necessary.
618: continue;
619: } else if (nextHeader.getHeaderName().equals(
620: Header.PROXY_AUTHORIZATION)) {
621: // Remove proxy auth header.
622: // Assigned by the Dialog if necessary.
623: continue;
624: } else if (nextHeader instanceof ContentLengthHeader) {
625: // Adding content is responsibility of user.
626: nextHeader = (Header) nextHeader.clone();
627: ((ContentLengthHeader) nextHeader).setContentLength(0);
628:
629: } else if (nextHeader instanceof ContentTypeHeader) {
630: // Content type header is removed since
631: // content length is 0. Bug fix from
632: // Antonis Kyardas.
633: continue;
634: } else if (nextHeader instanceof CSeqHeader) {
635: CSeqHeader cseq = (CSeqHeader) nextHeader.clone();
636: cseq.setMethod(ACK);
637: nextHeader = cseq;
638: } else if (nextHeader instanceof ToHeader) {
639: if (responseToHeader != null) {
640: nextHeader = responseToHeader;
641: } else {
642: nextHeader = (Header) nextHeader.clone();
643: }
644: } else {
645: nextHeader = (Header) nextHeader.clone();
646: }
647:
648: newRequest.attachHeader(nextHeader, false);
649: }
650:
651: return newRequest;
652: }
653:
654: /**
655: * Creates a new default Request from the original request. Warning:
656: * the newly created Request, shares the headers of
657: * this request but we generate any new headers that we need to modify
658: * so the original request is umodified. However, if you modify the
659: * shared headers after this request is created, then the newly
660: * created request will also be modified.
661: * If you want to modify the original request
662: * without affecting the returned Request
663: * make sure you clone it before calling this method.
664: * Following are the differences between the original request headers
665: * and the generated request headers.
666: * <ul>
667: * <li>
668: * Contact headers are not included in the newly created request.
669: * Setting the appropriate sequence number is the responsibility of
670: * the caller. </li>
671: * <li> RouteList is not copied for ACK and CANCEL </li>
672: * <li> Note that we DO NOT copy the body of the
673: * argument into the returned header. We do not copy the content
674: * type header from the original request either. These have to be
675: * added seperately and the content length has to be correctly set
676: * if necessary the content length is set to 0 in the returned header.
677: * </li>
678: * <li>Contact List is not copied from the original request.</li>
679: * <li>RecordRoute List is not included from original request. </li>
680: * <li>Via header is not included from the original request. </li>
681: * </ul>
682: *
683: * @param requestLine is the new request line.
684: *
685: * @param switchHeaders is a boolean flag that causes to and from
686: * headers to switch (set this to true if you are the
687: * server of the transaction and are generating a BYE
688: * request). If the headers are switched, we generate
689: * new FromHeader and To headers otherwise we just use the
690: * incoming headers.
691: *
692: * @return a new Default SIP Request which has the requestLine specified.
693: *
694: * @throws SipException if the request can't be created.
695: */
696: public Request createRequest(RequestLine requestLine,
697: boolean switchHeaders) throws SipException {
698: Request newRequest = new Request();
699: newRequest.requestLine = requestLine;
700: Enumeration headerIterator = this .getHeaders();
701: while (headerIterator.hasMoreElements()) {
702: Header nextHeader = (Header) headerIterator.nextElement();
703: // For BYE and cancel set the CSeqHeader header to the
704: // appropriate method.
705: if (nextHeader instanceof CSeqHeader) {
706: CSeqHeader newCseq = (CSeqHeader) nextHeader.clone();
707: nextHeader = newCseq;
708: newCseq.setMethod(requestLine.getMethod());
709: } else if (requestLine.getMethod().equals(ACK)
710: && nextHeader instanceof ContactList) {
711: // ACKS never get Contact headers.
712: continue;
713: } else if (nextHeader instanceof ViaList) {
714: ViaHeader via = (ViaHeader) (((ViaList) nextHeader)
715: .getFirst().clone());
716: via.removeParameter(SIPConstants.GENERAL_BRANCH);
717: nextHeader = via;
718: // Cancel and ACK preserve the branch ID.
719: } else if (nextHeader instanceof RouteList) {
720: continue; // Route is kept by dialog.
721: } else if (nextHeader instanceof RecordRouteList) {
722: continue; // RR is added by the caller.
723: } else if (nextHeader instanceof ContactList) {
724: continue;
725: } else if (nextHeader instanceof ToHeader) {
726: ToHeader to = (ToHeader) nextHeader;
727: if (switchHeaders) {
728: nextHeader = new FromHeader(to);
729: ((FromHeader) nextHeader).removeTag();
730: } else {
731: nextHeader = (Header) to.clone();
732: ((ToHeader) nextHeader).removeTag();
733: }
734: } else if (nextHeader instanceof FromHeader) {
735: FromHeader from = (FromHeader) nextHeader;
736: if (switchHeaders) {
737: nextHeader = new ToHeader(from);
738: ((ToHeader) nextHeader).removeTag();
739: } else {
740: nextHeader = (Header) from.clone();
741: ((FromHeader) nextHeader).removeTag();
742: }
743: } else if (nextHeader instanceof ContentLengthHeader) {
744: ContentLengthHeader cl = (ContentLengthHeader) nextHeader
745: .clone();
746: cl.setContentLength(0);
747: nextHeader = cl;
748: } else if (nextHeader instanceof ContentTypeHeader) {
749: continue;
750: } else if (nextHeader instanceof MaxForwardsHeader) {
751: // Header is regenerated if the request is to be switched
752: if (switchHeaders) {
753: MaxForwardsHeader mf = (MaxForwardsHeader) nextHeader
754: .clone();
755: mf.setMaxForwards(70);
756: nextHeader = mf;
757: }
758: } else if (!(nextHeader instanceof CallIdHeader)
759: && !(nextHeader instanceof MaxForwardsHeader)) {
760: // Route is kept by dialog.
761: // RR is added by the caller.
762: // Contact is added by the Caller
763: // Any extension headers must be added
764: // by the caller.
765: continue;
766: }
767:
768: newRequest.attachHeader(nextHeader, false);
769:
770: }
771: return newRequest;
772:
773: }
774:
775: /**
776: * Creates a BYE request from this request.
777: *
778: * @param switchHeaders is a boolean flag that causes from and
779: * isServerTransaction to headers to be swapped. Set this
780: * to true if you are the server of the dialog and are generating
781: * a BYE request for the dialog.
782: * @return a new default BYE request.
783: * @throws SipException if the request can't be created.
784: */
785: public Request createBYERequest(boolean switchHeaders)
786: throws SipException {
787: RequestLine rl = (RequestLine) requestLine.clone();
788: rl.setMethod(BYE);
789: return createRequest(rl, switchHeaders);
790: }
791:
792: /**
793: * Creates an ACK request from this request. This is suitable for
794: * generating an ACK for an INVITE client transaction.
795: *
796: * @return an ACK request that is generated from this request.
797: * @throws SipException if the request can't be created.
798: */
799: public Request createACKRequest() throws SipException {
800: RequestLine rl = (RequestLine) requestLine.clone();
801: rl.setMethod(ACK);
802: return createRequest(rl, false);
803: }
804:
805: /**
806: * Gets the host from the topmost via header.
807: *
808: * @return the string representation of the host from the topmost via
809: * header.
810: */
811: public String getViaHost() {
812: ViaHeader via = (ViaHeader) this .getViaHeaders().getFirst();
813: return via.getHost();
814:
815: }
816:
817: /**
818: * Gets the port from the topmost via header.
819: *
820: * @return the port from the topmost via header (5060 if there is
821: * no port indicated).
822: */
823: public int getViaPort() {
824: ViaHeader via = (ViaHeader) this .getViaHeaders().getFirst();
825: if (via.hasPort())
826: return via.getPort();
827: else
828: return 5060;
829: }
830:
831: /**
832: * Gets the first line encoded.
833: *
834: * @return a string containing the encoded request line.
835: */
836: public String getFirstLine() {
837: if (requestLine == null)
838: return null;
839: else
840: return this .requestLine.encode();
841: }
842:
843: /**
844: * Sets the sip version.
845: *
846: * @param sipVersion the sip version to set.
847: */
848:
849: public void setSIPVersion(String sipVersion) throws ParseException {
850: if (sipVersion == null || !sipVersion.equals("SIP/2.0"))
851: throw new ParseException("sipVersion", 0);
852: this .requestLine.setSIPVersion(sipVersion);
853: }
854:
855: /**
856: * Gets the SIP version.
857: *
858: * @return the SIP version from the request line.
859: */
860: public String getSIPVersion() {
861: return this .requestLine.getSipVersion();
862: }
863:
864: /**
865: * Gets the transaction pointer.
866: * @return the transaction pointer
867: */
868: public Object getTransaction() {
869: // Return an opaque pointer to the transaction object.
870: // This is for consistency checking and quick lookup.
871: return this .transactionPointer;
872: }
873:
874: /**
875: * Sets the transaction pointer.
876: * @param transaction thenew transaction pointer
877: */
878: public void setTransaction(Object transaction) {
879: this .transactionPointer = transaction;
880: }
881:
882: /**
883: * Gets the Accept-Contact header (null if one does not exist).
884: * @return Accept-Contact header
885: */
886: public AcceptContactHeader getAcceptContact() {
887: return (AcceptContactHeader) getHeader(Header.ACCEPT_CONTACT);
888: }
889:
890: }
|