0001: /*
0002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
0003: * Reserved. Use is subject to license terms.
0004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License version
0008: * 2 only, as published by the Free Software Foundation.
0009: *
0010: * This program is distributed in the hope that it will be useful, but
0011: * WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * General Public License version 2 for more details (a copy is
0014: * included at /legal/license.txt).
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * version 2 along with this work; if not, write to the Free Software
0018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0019: * 02110-1301 USA
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0022: * Clara, CA 95054 or visit www.sun.com if you need additional
0023: * information or have any questions.
0024: */
0025: /*
0026: */
0027: package gov.nist.siplite.message;
0028:
0029: import gov.nist.siplite.header.*;
0030: import gov.nist.siplite.parser.*;
0031: import gov.nist.siplite.*;
0032: import java.util.*;
0033: import gov.nist.core.*;
0034: import java.io.UnsupportedEncodingException;
0035: import javax.microedition.sip.SipException;
0036:
0037: import com.sun.midp.log.Logging;
0038: import com.sun.midp.log.LogChannels;
0039:
0040: /**
0041: * This is the main SIP Message structure.
0042: *
0043: * @see StringMsgParser
0044: * @see PipelinedMsgParser
0045: *
0046: *
0047: * @version JAIN-SIP-1.1
0048: *
0049: *
0050: *<a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
0051: *
0052: * IMPL_NOTE: remove 'Vector headers' because its contents is duplicated in nameTable
0053: */
0054: public abstract class Message extends GenericObject {
0055: /** Class handle. */
0056: private static Class sipHeaderListClass;
0057: /** Default encoding string. */
0058: protected static final String DEFAULT_ENCODING = "UTF-8";
0059: /** Unparsed headers. */
0060: protected Vector unrecognizedHeaders;
0061: /** List of parsed headers (in the order they were added). */
0062: protected Vector headers;
0063: /** From header. */
0064: protected FromHeader fromHeader;
0065: /** To header. */
0066: protected ToHeader toHeader;
0067: /** C sequence header. */
0068: protected CSeqHeader cSeqHeader;
0069: /** Caller identification header. */
0070: protected CallIdHeader callIdHeader;
0071: /** Content length header. */
0072: protected ContentLengthHeader contentLengthHeader;
0073: // protected MaxForwards maxForwardsHeader;
0074: /** Body of message content. */
0075: protected String messageContent;
0076: /** Length of message content in bytes. */
0077: protected byte[] messageContentBytes;
0078: /** Object holding body contents. */
0079: protected Object messageContentObject;
0080:
0081: static {
0082: try {
0083: sipHeaderListClass = Class
0084: .forName("gov.nist.siplite.header.HeaderList");
0085: } catch (ClassNotFoundException ex) {
0086: InternalErrorHandler.handleException(ex);
0087: }
0088: }
0089:
0090: /** Table of headers indexed by name. */
0091: private Hashtable nameTable;
0092:
0093: /**
0094: * Removes the specified header from "headers" list.
0095: *
0096: * @param headerName expanded name of the header to remove.
0097: */
0098: private void removeHeaderFromList(String headerName) {
0099: Enumeration li = headers.elements();
0100: int index = -1;
0101:
0102: while (li.hasMoreElements()) {
0103: Header sipHeader = (Header) li.nextElement();
0104: index++;
0105:
0106: String currName = NameMap.expandHeaderName(sipHeader
0107: .getName());
0108:
0109: if (Utils.equalsIgnoreCase(currName, headerName)) {
0110: break;
0111: }
0112: }
0113:
0114: if (index != -1 && index < headers.size()) {
0115: headers.removeElementAt(index);
0116: }
0117: }
0118:
0119: /**
0120: * Returns true if the header belongs only in a Request.
0121: *
0122: * @param sipHeader is the header to test.
0123: * @return true if header is part of a request
0124: */
0125: public static boolean isRequestHeader(Header sipHeader) {
0126: return sipHeader.getHeaderName().equals(Header.ALERT_INFO)
0127: || sipHeader.getHeaderName().equals(Header.IN_REPLY_TO)
0128: || sipHeader.getHeaderName().equals(
0129: Header.AUTHORIZATION)
0130: || sipHeader.getHeaderName()
0131: .equals(Header.MAX_FORWARDS)
0132: || sipHeader.getHeaderName().equals(Header.PRIORITY)
0133: || sipHeader.getHeaderName().equals(
0134: Header.PROXY_AUTHORIZATION)
0135: || sipHeader.getHeaderName().equals(
0136: Header.PROXY_REQUIRE)
0137: || sipHeader.getHeaderName().equals(Header.ROUTE)
0138: || sipHeader.getHeaderName().equals(Header.SUBJECT)
0139: || sipHeader.getHeaderName().equals(
0140: Header.ACCEPT_CONTACT);
0141:
0142: }
0143:
0144: /**
0145: * Returns true if the header belongs only in a response.
0146: *
0147: * @param sipHeader is the header to test.
0148: * @return true if header is part of a response
0149: */
0150: public static boolean isResponseHeader(Header sipHeader) {
0151: return sipHeader.getHeaderName().equals(Header.ERROR_INFO)
0152: || sipHeader.getHeaderName().equals(
0153: Header.PROXY_AUTHENTICATE)
0154: || sipHeader.getHeaderName().equals(Header.SERVER)
0155: || sipHeader.getHeaderName().equals(Header.UNSUPPORTED)
0156: || sipHeader.getHeaderName().equals(Header.RETRY_AFTER)
0157: || sipHeader.getHeaderName().equals(Header.WARNING)
0158: || sipHeader.getHeaderName().equals(
0159: Header.WWW_AUTHENTICATE);
0160:
0161: }
0162:
0163: /**
0164: * Gets a dialog identifier.
0165: * Generates a string that can be used as a dialog identifier.
0166: *
0167: * @param isServer is set to true if this is the UAS
0168: * and set to false if this is the UAC
0169: * @return the dialig identifier
0170: */
0171: public String getDialogId(boolean isServer) {
0172: CallIdHeader cid = (CallIdHeader) this .getCallId();
0173: StringBuffer retval = new StringBuffer(cid.getCallId());
0174: FromHeader from = (FromHeader) this .getFromHeader();
0175: ToHeader to = (ToHeader) this .getTo();
0176: if (!isServer) {
0177: if (to.getTag() != null) {
0178: retval.append(to.getTag());
0179: }
0180: if (from.getTag() != null) {
0181: retval.append(from.getTag());
0182: }
0183: } else {
0184: if (from.getTag() != null) {
0185: retval.append(from.getTag());
0186: }
0187: if (to.getTag() != null) {
0188: retval.append(to.getTag());
0189: }
0190: }
0191: return retval.toString().toLowerCase();
0192:
0193: }
0194:
0195: /**
0196: * Gets a dialog id given the remote tag.
0197: * @param isServer flag indicating a server request
0198: * @param toTag the target recipient
0199: * @return the dialog identifier
0200: */
0201: public String getDialogId(boolean isServer, String toTag) {
0202: FromHeader from = (FromHeader) this .getFromHeader();
0203: ToHeader to = (ToHeader) this .getTo();
0204: CallIdHeader cid = (CallIdHeader) this .getCallId();
0205: StringBuffer retval = new StringBuffer(cid.getCallId());
0206: if (!isServer) {
0207: if (toTag != null) {
0208: retval.append(toTag);
0209: }
0210: if (from.getTag() != null) {
0211: retval.append(from.getTag());
0212: }
0213: } else {
0214: if (from.getTag() != null) {
0215: retval.append(from.getTag());
0216: }
0217: if (toTag != null) {
0218: retval.append(toTag);
0219: }
0220: }
0221: return retval.toString().toLowerCase();
0222: }
0223:
0224: /**
0225: * Encodes this message as a string. This is more efficient when
0226: * the payload is a string (rather than a binary array of bytes).
0227: * If the payload cannot be encoded as a UTF-8 string then it is
0228: * simply ignored (will not appear in the encoded message).
0229: * @return The Canonical String representation of the message
0230: * (including the canonical string representation of
0231: * the SDP payload if it exists).
0232: */
0233: public String encode() {
0234: StringBuffer encoding = new StringBuffer();
0235: // Synchronization added because of concurrent modification exception
0236: // noticed by Lamine Brahimi.
0237: synchronized (this .headers) {
0238: Enumeration it = this .headers.elements();
0239:
0240: while (it.hasMoreElements()) {
0241: Header siphdr = (Header) it.nextElement();
0242: if (!(siphdr instanceof ContentLengthHeader)) {
0243: encoding.append(siphdr.encode());
0244: }
0245: }
0246: }
0247:
0248: // Add the content-length header
0249: if (contentLengthHeader != null) {
0250: encoding.append(contentLengthHeader.encode()).append(
0251: Separators.NEWLINE);
0252: }
0253:
0254: if (this .messageContentObject != null) {
0255: String mbody = this .getContent().toString();
0256: encoding.append(mbody);
0257: } else if (this .messageContent != null
0258: || this .messageContentBytes != null) {
0259: String content = null;
0260: try {
0261: if (messageContent != null) {
0262: content = messageContent;
0263: } else {
0264: content = new String(messageContentBytes,
0265: DEFAULT_ENCODING);
0266: }
0267: } catch (UnsupportedEncodingException ex) {
0268: content = "";
0269: }
0270: encoding.append(content);
0271: }
0272:
0273: return encoding.toString();
0274: }
0275:
0276: /**
0277: * Encodes the message as a byte array.
0278: * Use this when the message payload is a binary byte array.
0279: *
0280: * @return The Canonical byte array representation of the message
0281: * (including the canonical byte array representation of
0282: * the SDP payload if it exists all in one contiguous byte array).
0283: *
0284: */
0285: public byte[] encodeAsBytes() {
0286: StringBuffer encoding = new StringBuffer();
0287: Enumeration it = this .headers.elements();
0288:
0289: while (it.hasMoreElements()) {
0290: Header siphdr = (Header) it.nextElement();
0291: if (!(siphdr instanceof ContentLengthHeader))
0292: encoding.append(siphdr.encode());
0293:
0294: }
0295: byte[] retval = null;
0296: byte[] content = this .getRawContent();
0297: if (content != null) {
0298: encoding.append(Header.CONTENT_LENGTH + Separators.COLON
0299: + Separators.SP + content.length
0300: + Separators.NEWLINE);
0301: encoding.append(Separators.NEWLINE);
0302: // Append the content
0303: byte[] msgarray = null;
0304: try {
0305: msgarray = encoding.toString().getBytes("UTF-8");
0306: } catch (UnsupportedEncodingException ex) {
0307: InternalErrorHandler.handleException(ex);
0308: }
0309:
0310: retval = new byte[msgarray.length + content.length];
0311: System.arraycopy(msgarray, 0, retval, 0, msgarray.length);
0312: System.arraycopy(content, 0, retval, msgarray.length,
0313: content.length);
0314: } else {
0315: // Message content does not exist.
0316: encoding.append(Header.CONTENT_LENGTH + Separators.COLON
0317: + Separators.SP + '0' + Separators.NEWLINE);
0318: encoding.append(Separators.NEWLINE);
0319: try {
0320: retval = encoding.toString().getBytes("UTF-8");
0321: } catch (UnsupportedEncodingException ex) {
0322: InternalErrorHandler.handleException(ex);
0323: }
0324: }
0325: return retval;
0326: }
0327:
0328: /**
0329: * Clones this message (create a new deep physical copy).
0330: * All headers in the message are cloned.
0331: * You can modify the cloned copy without affecting
0332: * the original.
0333: *
0334: * @return A cloned copy of this object.
0335: */
0336: public Object clone() {
0337: Message retval = null;
0338: try {
0339: retval = (Message) this .getClass().newInstance();
0340: } catch (IllegalAccessException ex) {
0341: InternalErrorHandler.handleException(ex);
0342: } catch (InstantiationException ex) {
0343: InternalErrorHandler.handleException(ex);
0344: }
0345:
0346: Enumeration li = headers.elements();
0347: while (li.hasMoreElements()) {
0348: Header sipHeader = (Header) ((Header) li.nextElement())
0349: .clone();
0350: try {
0351: retval.attachHeader(sipHeader);
0352: } catch (SipException ex) {
0353: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0354: Logging.report(Logging.ERROR,
0355: LogChannels.LC_JSR180,
0356: "Message.clone(): can't attach header '"
0357: + sipHeader.getName() + "'.");
0358: ex.printStackTrace();
0359: }
0360: }
0361: }
0362:
0363: if (retval instanceof Request) {
0364: Request this Request = (Request) this ;
0365: RequestLine rl = (RequestLine) (this Request
0366: .getRequestLine()).clone();
0367: ((Request) retval).setRequestLine(rl);
0368: } else {
0369: Response this Response = (Response) this ;
0370: StatusLine sl = (StatusLine) (this Response.getStatusLine())
0371: .clone();
0372: ((Response) retval).setStatusLine(sl);
0373: }
0374:
0375: if (getContent() != null) {
0376: try {
0377: retval.setContent(getContent(), getContentTypeHeader());
0378: } catch (SipException ex) { // Ignore
0379: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0380: Logging.report(Logging.ERROR,
0381: LogChannels.LC_JSR180,
0382: "Message.clone(): can't set the content!");
0383: ex.printStackTrace();
0384: }
0385: }
0386: }
0387:
0388: return retval;
0389: }
0390:
0391: /**
0392: *
0393: * Constructor: Initializes lists and list headers.
0394: * All the headers for which there can be multiple occurances in
0395: * a message are derived from the HeaderListClass. All singleton
0396: * headers are derived from Header class.
0397: *
0398: */
0399: public Message() {
0400: unrecognizedHeaders = new Vector();
0401: headers = new Vector();
0402: nameTable = new Hashtable();
0403: }
0404:
0405: /**
0406: * Attaches a header and dies if you get a duplicate header exception.
0407: * @param h Header to attach.
0408: * @throws IllegalArgumentException if the header to attach is null.
0409: * @throws SipException if the header can't be attached for some reason.
0410: */
0411: private void attachHeader(Header h)
0412: throws IllegalArgumentException, SipException {
0413: if (h == null)
0414: throw new IllegalArgumentException("null header!");
0415:
0416: if (h instanceof HeaderList) {
0417: HeaderList hl = (HeaderList) h;
0418: if (hl.isEmpty()) {
0419: // System.out.println("Attaching an empty header: " +
0420: // h.getClass().getName());
0421: return;
0422: }
0423: }
0424:
0425: attachHeader(h, false, false);
0426: }
0427:
0428: /**
0429: * Attaches a header (replacing the original header).
0430: * @param header Header that replaces a header of the same type.
0431: * @throws IllegalArgumentException if the header to add is null.
0432: * @throws SipException if the header can't be set for some reason.
0433: */
0434: public void setHeader(Header header)
0435: throws IllegalArgumentException, SipException {
0436: if (header == null)
0437: throw new IllegalArgumentException("null header!");
0438:
0439: if (header instanceof HeaderList) {
0440: HeaderList hl = (HeaderList) header;
0441: // Ignore empty lists.
0442: if (hl.isEmpty())
0443: return;
0444: }
0445:
0446: attachHeader(header, true, false);
0447: }
0448:
0449: /**
0450: * Sets a header from a linked list of headers.
0451: *
0452: * @param headers -- a list of headers to set.
0453: * @throws SipException if some header can't be set for some reason.
0454: */
0455: public void setHeaders(Vector headers) throws SipException {
0456: Enumeration elements = headers.elements();
0457: while (elements.hasMoreElements()) {
0458: Header sipHeader = (Header) elements.nextElement();
0459: attachHeader(sipHeader, false);
0460: }
0461: }
0462:
0463: /**
0464: * Attaches a header to the end of the existing headers in
0465: * this Message structure.
0466: * This is equivalent to the attachHeader(Header,replaceflag,false);
0467: * which is the normal way in which headers are attached.
0468: * This was added in support of JAIN-SIP.
0469: *
0470: * @since 1.0 (made this public)
0471: * @param h header to attach.
0472: * @param replaceflag if true then replace a header if it exists.
0473: * @throws SipException if the header can't be attached for some reason.
0474: */
0475: public void attachHeader(Header h, boolean replaceflag)
0476: throws SipException {
0477: attachHeader(h, replaceflag, false);
0478: }
0479:
0480: /**
0481: * Attaches the header to the SIP Message structure at a specified
0482: * position in its list of headers.
0483: *
0484: * @param header Header to attach.
0485: * @param replaceFlag If true then replace the existing header.
0486: * @param top flag to indicate attaching header to the front of list.
0487: * @throws SipException if the header can't be attached for some reason.
0488: */
0489: public void attachHeader(Header header, boolean replaceFlag,
0490: boolean top) throws SipException {
0491: if (header == null) {
0492: throw new NullPointerException("null header");
0493: }
0494:
0495: // System.out.println(">>> attachHeader( " +
0496: // header + ", " + replaceFlag + ");");
0497:
0498: Header h;
0499: String expandedHeaderName = NameMap.expandHeaderName(
0500: header.getHeaderName()).toLowerCase();
0501:
0502: if (ListMap.hasList(header)
0503: && !sipHeaderListClass.isAssignableFrom(header
0504: .getClass())) {
0505: HeaderList hdrList = ListMap.getList(header);
0506:
0507: // Actually, hdrList.size() is always 0.
0508: if (replaceFlag && (hdrList.size() > 0)) {
0509: // remove first element
0510: hdrList.removeElement(hdrList.elementAt(0));
0511: }
0512:
0513: hdrList.add(header);
0514: h = hdrList;
0515: } else {
0516: h = header;
0517: }
0518:
0519: if (!replaceFlag && nameTable.containsKey(expandedHeaderName)
0520: && !(h instanceof HeaderList)) {
0521: // Throw an exception here because according to JSR180:
0522: // "The implementations MAY restrict the access to some
0523: // headers according to RFC 3261."
0524: //
0525: // It may happen if this function is called to add a header that
0526: // already exist and the only one header of this type may present
0527: // (Call-Id, From, To).
0528: //
0529: throw new SipException(
0530: "Header '"
0531: + header.getHeaderName()
0532: + "' already exist. Only one header of this type is allowed.",
0533: SipException.INVALID_OPERATION);
0534: }
0535:
0536: // Delete the first header with name = headerName
0537: // from our list structure.
0538: // If case of HeaderList the whole list is removed
0539: // to avoid duplication (it is added bellow).
0540: if (replaceFlag || (h instanceof HeaderList)) {
0541: Enumeration li = headers.elements();
0542: int index;
0543:
0544: for (index = 0; li.hasMoreElements(); index++) {
0545: Header next = (Header) li.nextElement();
0546: String currName = NameMap.expandHeaderName(next
0547: .getHeaderName());
0548:
0549: if (expandedHeaderName.equalsIgnoreCase(currName)) {
0550: headers.removeElementAt(index);
0551: break;
0552: }
0553: }
0554: }
0555:
0556: Header hRef = h;
0557:
0558: if (h instanceof HeaderList) {
0559: HeaderList hdrlist = (HeaderList) nameTable
0560: .get(expandedHeaderName);
0561:
0562: if (hdrlist != null) {
0563: if (replaceFlag) {
0564: hdrlist.removeFirst();
0565: }
0566:
0567: hdrlist.concatenate((HeaderList) h, top);
0568:
0569: // This is required due to the way that 'concatenate'
0570: // is implemented: if 'top' is false, it modifies
0571: // the objects itself; otherwise, the list passed
0572: // as the first parameter is modified.
0573: if (!top) {
0574: hRef = hdrlist;
0575: }
0576: }
0577: }
0578:
0579: nameTable.put(expandedHeaderName, hRef);
0580: headers.addElement(hRef);
0581:
0582: // Direct accessor fields for frequently accessed headers.
0583: if (h instanceof FromHeader) {
0584: this .fromHeader = (FromHeader) h;
0585: } else if (h instanceof ContentLengthHeader) {
0586: this .contentLengthHeader = (ContentLengthHeader) h;
0587: } else if (h instanceof ToHeader) {
0588: this .toHeader = (ToHeader) h;
0589: } else if (h instanceof CSeqHeader) {
0590: this .cSeqHeader = (CSeqHeader) h;
0591: } else if (h instanceof CallIdHeader) {
0592: this .callIdHeader = (CallIdHeader) h;
0593: }
0594: }
0595:
0596: /**
0597: * Removes a header given its name. If multiple headers of a given name
0598: * are present then the top flag determines which end to remove headers
0599: * from.
0600: *
0601: * @param headerName is the name of the header to remove.
0602: * @param top flag that indicates which end of header list to process.
0603: */
0604: public void removeHeader(String headerName, boolean top) {
0605: // System.out.println("removeHeader " + headerName);
0606:
0607: headerName = NameMap.expandHeaderName(headerName).toLowerCase();
0608: Header toRemove = (Header) nameTable.get(headerName);
0609:
0610: // nothing to do then we are done.
0611: if (toRemove == null)
0612: return;
0613:
0614: if (toRemove instanceof HeaderList) {
0615: HeaderList hdrList = (HeaderList) toRemove;
0616: if (top)
0617: hdrList.removeFirst();
0618: else
0619: hdrList.removeLast();
0620:
0621: // Clean up empty list
0622: if (hdrList.isEmpty()) {
0623: removeHeaderFromList(headerName);
0624: }
0625: } else {
0626: nameTable.remove(headerName);
0627:
0628: if (toRemove instanceof FromHeader) {
0629: this .fromHeader = null;
0630: } else if (toRemove instanceof ToHeader) {
0631: this .toHeader = null;
0632: } else if (toRemove instanceof CSeqHeader) {
0633: this .cSeqHeader = null;
0634: } else if (toRemove instanceof CallIdHeader) {
0635: this .callIdHeader = null;
0636: } else if (toRemove instanceof ContentLengthHeader) {
0637: this .contentLengthHeader = null;
0638: }
0639:
0640: removeHeaderFromList(headerName);
0641: }
0642: }
0643:
0644: /**
0645: * Removes all headers given its name.
0646: *
0647: * @param headerName is the name of the header to remove.
0648: */
0649: public void removeHeader(String headerName) {
0650: if (headerName == null) {
0651: throw new NullPointerException("null arg");
0652: }
0653:
0654: headerName = NameMap.expandHeaderName(headerName).toLowerCase();
0655: Header toRemove = (Header) nameTable.get(headerName);
0656:
0657: // nothing to do then we are done.
0658: if (toRemove == null)
0659: return;
0660:
0661: nameTable.remove(headerName);
0662:
0663: // Remove the fast accessor fields.
0664: if (toRemove instanceof FromHeader) {
0665: this .fromHeader = null;
0666: } else if (toRemove instanceof ToHeader) {
0667: this .toHeader = null;
0668: } else if (toRemove instanceof CSeqHeader) {
0669: this .cSeqHeader = null;
0670: } else if (toRemove instanceof CallIdHeader) {
0671: this .callIdHeader = null;
0672: } else if (toRemove instanceof ContentLengthHeader) {
0673: this .contentLengthHeader = null;
0674: }
0675:
0676: removeHeaderFromList(headerName);
0677: }
0678:
0679: /**
0680: * Generates (compute) a transaction ID for this SIP message.
0681: * @return A string containing the concatenation of various
0682: * portions of the FromHeader,To,Via and RequestURI portions
0683: * of this message as specified in RFC 2543:
0684: * All responses to a request contain the same values in
0685: * the Call-ID, CSeqHeader, To, and FromHeader fields
0686: * (with the possible addition of a tag in the To field
0687: * (section 10.43)). This allows responses to be matched with requests.
0688: * Incorporates a fix
0689: * for generating transactionIDs when no port is present in the
0690: * via header.
0691: * Incorporates a fix
0692: * (converts to lower case when returning the
0693: * transaction identifier).
0694: *
0695: * @return a string that can be used as a transaction identifier
0696: * for this message. This can be used for matching responses and
0697: * requests (i.e. an outgoing request and its matching response have
0698: * the same computed transaction identifier).
0699: */
0700: public String getTransactionId() {
0701: ViaHeader topVia = null;
0702: if (!this .getViaHeaders().isEmpty()) {
0703: topVia = (ViaHeader) this .getViaHeaders().first();
0704: }
0705: // Have specified a branch Identifier so we can use it to identify
0706: // the transaction.
0707: if (topVia.getBranch() != null
0708: && topVia.getBranch().startsWith(
0709: SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE)) {
0710: // Bis 09 compatible branch assignment algorithm.
0711: // implies that the branch id can be used as a transaction
0712: // identifier.
0713: return topVia.getBranch().toLowerCase();
0714: } else {
0715: // Old style client so construct the transaction identifier
0716: // from various fields of the request.
0717: StringBuffer retval = new StringBuffer();
0718: FromHeader from = (FromHeader) this .getFromHeader();
0719: ToHeader to = (ToHeader) this .getTo();
0720: String hpFromHeader = from.getUserAtHostPort();
0721: retval.append(hpFromHeader).append(":");
0722: if (from.hasTag())
0723: retval.append(from.getTag()).append(":");
0724: String hpTo = to.getUserAtHostPort();
0725: retval.append(hpTo).append(":");
0726: String cid = this .callIdHeader.getCallId();
0727: retval.append(cid).append(":");
0728: retval.append(this .cSeqHeader.getSequenceNumber()).append(
0729: ":").append(this .cSeqHeader.getMethod());
0730: if (topVia != null) {
0731: retval.append(":").append(topVia.getSentBy().encode());
0732: if (!topVia.getSentBy().hasPort()) {
0733: retval.append(":").append(5060);
0734: }
0735: }
0736: String hc = Utils.toHexString(retval.toString()
0737: .toLowerCase().getBytes());
0738: if (hc.length() < 32)
0739: return hc;
0740: else
0741: return hc.substring(hc.length() - 32, hc.length() - 1);
0742: }
0743: // Convert to lower case
0744: }
0745:
0746: /**
0747: * Returns true if this message has a body.
0748: * @return true if message body included
0749: */
0750: public boolean hasContent() {
0751: return messageContent != null || messageContentBytes != null;
0752: }
0753:
0754: /**
0755: * Returns an iterator for the list of headers in this message.
0756: * @return an Iterator for the headers of this message.
0757: */
0758: public Enumeration getHeaders() {
0759: return headers.elements();
0760: }
0761:
0762: /**
0763: * Gets the first header of the given name.
0764: * @param headerName requested header
0765: * @return header the first header of the given name.
0766: */
0767: public Header getHeader(String headerName) {
0768: if (headerName == null)
0769: throw new NullPointerException("bad name");
0770:
0771: headerName = NameMap.expandHeaderName(headerName).toLowerCase();
0772: Header sipHeader = (Header) nameTable.get(headerName);
0773:
0774: if (sipHeader == null) {
0775: return null;
0776: }
0777:
0778: if (sipHeader instanceof HeaderList) {
0779: return (Header) ((HeaderList) sipHeader).getFirst();
0780: } else {
0781: return (Header) sipHeader;
0782: }
0783: }
0784:
0785: /**
0786: * Gets the contentType header (null if one does not exist).
0787: * @return contentType header
0788: */
0789: public ContentTypeHeader getContentTypeHeader() {
0790: return (ContentTypeHeader) getHeader(Header.CONTENT_TYPE);
0791: }
0792:
0793: /**
0794: * Gets the from header.
0795: * @return the from header.
0796: */
0797: public FromHeader getFromHeader() {
0798: return (FromHeader) fromHeader;
0799: }
0800:
0801: /**
0802: * Gets the Contact list of headers (null if one does not exist).
0803: * @return List containing Contact headers.
0804: */
0805: public ContactList getContactHeaders() {
0806: return (ContactList) getHeaderList(Header.CONTACT);
0807: }
0808:
0809: /**
0810: * Gets the Via list of headers (null if one does not exist).
0811: * @return List containing Via headers.
0812: */
0813: public ViaList getViaHeaders() {
0814: return (ViaList) getHeaderList(Header.VIA);
0815: }
0816:
0817: /**
0818: * Gets an iterator to the list of vial headers.
0819: * @return a list iterator to the list of via headers.
0820: * public ListIterator getVia() {
0821: * return this.viaHeaders.listIterator();
0822: * }
0823: */
0824:
0825: /**
0826: * Sets a list of via headers.
0827: * @param viaList a list of via headers to add.
0828: * @throws SipException if the header can't be set for some reason.
0829: */
0830: public void setVia(ViaList viaList) throws SipException {
0831: setHeader(viaList);
0832: }
0833:
0834: /**
0835: * Sets a list of via headers.
0836: * @param viaList a list of via headers to add.
0837: * @throws SipException if the header can't be set for some reason.
0838: */
0839: public void setVia(Vector viaList) throws SipException {
0840: this .removeHeader(ViaHeader.NAME);
0841: for (int i = 0; i < viaList.size(); i++) {
0842: ViaHeader via = (ViaHeader) viaList.elementAt(i);
0843: this .addHeader(via);
0844: }
0845: }
0846:
0847: /**
0848: * Sets the header given a list of headers.
0849: *
0850: * @param sipHeaderList a headerList to set
0851: * @throws SipException if the header can't be set for some reason.
0852: */
0853: public void setHeader(HeaderList sipHeaderList) throws SipException {
0854: setHeader((Header) sipHeaderList);
0855: }
0856:
0857: /**
0858: * Gets the topmost via header.
0859: * @return the top most via header if one exists or null if none exists.
0860: * @throws SipException if the header can't be set for some reason.
0861: */
0862: public ViaHeader getTopmostVia() {
0863: if (getViaHeaders() == null)
0864: return null;
0865: else
0866: return (ViaHeader) (getViaHeaders().getFirst());
0867: }
0868:
0869: /**
0870: * Gets the CSeqHeader list of header (null if one does not exist).
0871: * @return CSeqHeader header
0872: */
0873: public CSeqHeader getCSeqHeader() {
0874: return cSeqHeader;
0875: }
0876:
0877: /**
0878: * Gets the sequence number.
0879: * @return the sequence number.
0880: */
0881: public int getCSeqHeaderNumber() {
0882: return cSeqHeader.getSequenceNumber();
0883: }
0884:
0885: /**
0886: * Gets the Route List of headers (null if one does not exist).
0887: * @return List containing Route headers
0888: */
0889: public RouteList getRouteHeaders() {
0890: return (RouteList) getHeaderList(Header.ROUTE);
0891: }
0892:
0893: /**
0894: * Gets the CallIdHeader header (null if one does not exist).
0895: *
0896: * @return Call-ID header.
0897: */
0898: public CallIdHeader getCallId() {
0899: return callIdHeader;
0900: }
0901:
0902: /**
0903: * Sets the call id header.
0904: *
0905: * @param callId call idHeader (what else could it be?)
0906: * @throws SipException if the header can't be set for some reason.
0907: */
0908: public void setCallId(CallIdHeader callId) throws SipException {
0909: setHeader(callId);
0910: }
0911:
0912: /**
0913: * Gets the CallIdHeader header (null if one does not exist)
0914: *
0915: * @param callId -- the call identifier to be assigned to the call id header
0916: * @throws SipException if the header can't be set for some reason.
0917: */
0918: public void setCallId(String callId) throws ParseException,
0919: SipException {
0920: if (callIdHeader == null) {
0921: setHeader(new CallIdHeader());
0922: }
0923: callIdHeader.setCallId(callId);
0924: }
0925:
0926: /**
0927: * Gets the call ID string.
0928: * A conveniance function that returns the stuff following
0929: * the header name for the call id header.
0930: *
0931: * @return the call identifier.
0932: *
0933: */
0934: public String getCallIdentifier() {
0935: return callIdHeader.getCallId();
0936: }
0937:
0938: /**
0939: * Gets the RecordRoute header list (null if one does not exist).
0940: *
0941: * @return Record-Route header
0942: */
0943: public RecordRouteList getRecordRouteHeaders() {
0944: return (RecordRouteList) this
0945: .getHeaderList(Header.RECORD_ROUTE);
0946: }
0947:
0948: /**
0949: * Gets the To header (null if one does not exist).
0950: * @return To header
0951: */
0952: public ToHeader getTo() {
0953: return (ToHeader) toHeader;
0954: }
0955:
0956: /**
0957: * Sets the To header field value.
0958: * @param to the new To field value
0959: * @throws SipException if the header can't be set for some reason.
0960: */
0961: public void setTo(ToHeader to) throws SipException {
0962: setHeader(to);
0963: }
0964:
0965: /**
0966: * Sets the From header field value.
0967: * @param from the new From header field value.
0968: * @throws SipException if the header can't be set for some reason.
0969: */
0970: public void setFromHeader(FromHeader from) throws SipException {
0971: setHeader(from);
0972: }
0973:
0974: /**
0975: * Gets the ContentLengthHeader header (null if one does not exist).
0976: *
0977: * @return content-length header.
0978: */
0979: public ContentLengthHeader getContentLengthHeader() {
0980: return contentLengthHeader;
0981: }
0982:
0983: /**
0984: * Gets the message body as a string.
0985: * If the message contains a content type header with a specified
0986: * charset, and if the payload has been read as a byte array, then
0987: * it is returned encoded into this charset.
0988: *
0989: * @return Message body (as a string)
0990: */
0991: public String getMessageContent()
0992: throws UnsupportedEncodingException {
0993: if (this .messageContent == null
0994: && this .messageContentBytes == null)
0995: return null;
0996: else if (this .messageContent == null) {
0997: ContentTypeHeader contentTypeHeader = (ContentTypeHeader) this .nameTable
0998: .get(Header.CONTENT_TYPE.toLowerCase());
0999: if (contentTypeHeader != null) {
1000: String charset = contentTypeHeader.getCharset();
1001: if (charset != null) {
1002: this .messageContent = new String(
1003: messageContentBytes, charset);
1004: } else {
1005: this .messageContent = new String(
1006: messageContentBytes, DEFAULT_ENCODING);
1007: }
1008: } else
1009: this .messageContent = new String(messageContentBytes,
1010: DEFAULT_ENCODING);
1011: }
1012: return this .messageContent;
1013: }
1014:
1015: /**
1016: * Gets the message content as an array of bytes.
1017: * If the payload has been read as a String then it is decoded using
1018: * the charset specified in the content type header if it exists.
1019: * Otherwise, it is encoded using the default encoding which is
1020: * UTF-8.
1021: *
1022: * @return an array of bytes that is the message payload.
1023: *
1024: */
1025: public byte[] getRawContent() {
1026: try {
1027: if (this .messageContent == null
1028: && this .messageContentBytes == null
1029: && this .messageContentObject == null) {
1030: return null;
1031: } else if (this .messageContentObject != null) {
1032: String messageContent = this .messageContentObject
1033: .toString();
1034: byte[] messageContentBytes;
1035: ContentTypeHeader contentTypeHeader = (ContentTypeHeader) this .nameTable
1036: .get(Header.CONTENT_TYPE.toLowerCase());
1037: if (contentTypeHeader != null) {
1038: String charset = contentTypeHeader.getCharset();
1039: if (charset != null) {
1040: messageContentBytes = messageContent
1041: .getBytes(charset);
1042: } else {
1043: messageContentBytes = messageContent
1044: .getBytes(DEFAULT_ENCODING);
1045: }
1046: } else
1047: messageContentBytes = messageContent
1048: .getBytes(DEFAULT_ENCODING);
1049: return messageContentBytes;
1050: } else if (this .messageContent != null) {
1051: byte[] messageContentBytes;
1052: ContentTypeHeader contentTypeHeader = (ContentTypeHeader) this .nameTable
1053: .get(Header.CONTENT_TYPE.toLowerCase());
1054: if (contentTypeHeader != null) {
1055: String charset = contentTypeHeader.getCharset();
1056: if (charset != null) {
1057: messageContentBytes = this .messageContent
1058: .getBytes(charset);
1059: } else {
1060: messageContentBytes = this .messageContent
1061: .getBytes(DEFAULT_ENCODING);
1062: }
1063: } else
1064: messageContentBytes = this .messageContent
1065: .getBytes(DEFAULT_ENCODING);
1066: return messageContentBytes;
1067: } else {
1068: return messageContentBytes;
1069: }
1070: } catch (UnsupportedEncodingException ex) {
1071: InternalErrorHandler.handleException(ex);
1072: return null;
1073: }
1074: }
1075:
1076: /**
1077: * Sets the message content given type and subtype.
1078: *
1079: * @param type is the message type (eg. application)
1080: * @param subType is the message sybtype (eg. sdp)
1081: * @param messageContent is the message content as a string.
1082: * @throws IllegalArgumentException if some parameter is invalid.
1083: */
1084: public void setMessageContent(String type, String subType,
1085: String messageContent) throws IllegalArgumentException {
1086: if (messageContent == null) {
1087: throw new IllegalArgumentException("messgeContent is null");
1088: }
1089:
1090: ContentTypeHeader ct = new ContentTypeHeader(type, subType);
1091:
1092: try {
1093: setHeader(ct);
1094: } catch (SipException se) {
1095: throw new IllegalArgumentException(se.getMessage());
1096: }
1097:
1098: messageContent = messageContent;
1099: messageContentBytes = null;
1100: messageContentObject = null;
1101:
1102: ContentLengthHeader h = getContentLengthHeader();
1103: if (h != null) {
1104: h.setContentLength(messageContent.length());
1105: }
1106: }
1107:
1108: /**
1109: * Sets the message content after converting the given object to a
1110: * String.
1111: *
1112: * @param content content to set.
1113: * @param contentTypeHeader content type header corresponding to
1114: * content.
1115: * @throws NullPointerException if the 'content' parameter is null.
1116: * @throws SipException if the content can't be set for some reason.
1117: */
1118: public void setContent(Object content,
1119: ContentTypeHeader contentTypeHeader) throws SipException {
1120: if (content == null) {
1121: throw new NullPointerException("null content");
1122: }
1123:
1124: String contentString = content.toString();
1125: this .setMessageContent(contentString);
1126: this .setHeader(contentTypeHeader);
1127: this .removeContent();
1128:
1129: if (content instanceof String) {
1130: this .messageContent = (String) content;
1131: } else if (content instanceof byte[]) {
1132: this .messageContentBytes = (byte[]) content;
1133: } else
1134: this .messageContentObject = content;
1135:
1136: int length = -1;
1137: if (content instanceof String)
1138: length = ((String) content).length();
1139: else if (content instanceof byte[])
1140: length = ((byte[]) content).length;
1141:
1142: ContentLengthHeader h = getContentLengthHeader();
1143: if (length != -1 && h != null) {
1144: h.setContentLength(length);
1145: }
1146: }
1147:
1148: /**
1149: * Sets the message content after converting the given object to a
1150: * String.
1151: *
1152: * @param content content to set.
1153: * @throws NullPointerException if the content parameter is null.
1154: */
1155: public void setContent(Object content) {
1156: if (content == null)
1157: throw new NullPointerException("null content");
1158:
1159: String contentString = content.toString();
1160: this .setMessageContent(contentString);
1161: this .removeContent();
1162:
1163: if (content instanceof String) {
1164: this .messageContent = (String) content;
1165: } else if (content instanceof byte[]) {
1166: this .messageContentBytes = (byte[]) content;
1167: } else
1168: this .messageContentObject = content;
1169:
1170: int length = -1;
1171: if (content instanceof String)
1172: length = ((String) content).length();
1173: else if (content instanceof byte[])
1174: length = ((byte[]) content).length;
1175:
1176: ContentLengthHeader h = getContentLengthHeader();
1177: if (length != -1 && h != null) {
1178: h.setContentLength(length);
1179: }
1180: }
1181:
1182: /**
1183: * Gets the content of the header.
1184: *
1185: * @return the content of the sip message.
1186: */
1187: public Object getContent() {
1188: if (this .messageContentObject != null)
1189: return messageContentObject;
1190: else if (this .messageContentBytes != null)
1191: return this .messageContentBytes;
1192: else if (this .messageContent != null)
1193: return this .messageContent;
1194: else
1195: return null;
1196: }
1197:
1198: /**
1199: * Sets the message content for a given type and subtype.
1200: *
1201: * @param type is the messge type.
1202: * @param subType is the message subType.
1203: * @param messageContent is the message content as a byte array.
1204: * @throws SipException if the message content can't be set.
1205: */
1206: public void setMessageContent(String type, String subType,
1207: byte[] messageContent) throws SipException {
1208: ContentTypeHeader ct = new ContentTypeHeader(type, subType);
1209: setHeader(ct);
1210: setMessageContent(messageContent);
1211:
1212: ContentLengthHeader h = getContentLengthHeader();
1213: if (h != null) {
1214: h.setContentLength(messageContent.length);
1215: }
1216: }
1217:
1218: /**
1219: * Sets the message content for this message.
1220: *
1221: * @param content Message body as a string.
1222: */
1223: public void setMessageContent(String content) {
1224: ContentLengthHeader h = getContentLengthHeader();
1225:
1226: if (h != null) {
1227: int clength = (content == null ? 0 : content.length());
1228: h.setContentLength(clength);
1229: }
1230:
1231: messageContent = content;
1232: messageContentBytes = null;
1233: messageContentObject = null;
1234: }
1235:
1236: /**
1237: * Sets the message content as an array of bytes.
1238: *
1239: * @param content is the content of the message as an array of bytes.
1240: */
1241: public void setMessageContent(byte[] content) {
1242: ContentLengthHeader h = getContentLengthHeader();
1243:
1244: if (h != null) {
1245: h.setContentLength(content.length);
1246: }
1247:
1248: messageContentBytes = content;
1249: messageContent = null;
1250: messageContentObject = null;
1251: }
1252:
1253: /**
1254: * Removes the message content if it exists.
1255: */
1256: public void removeContent() {
1257: messageContent = null;
1258: messageContentBytes = null;
1259: messageContentObject = null;
1260: }
1261:
1262: /**
1263: * Gets a SIP header or Header list given its name.
1264: * @param headerName is the name of the header to get.
1265: * @return a header or header list that contians the retrieved header.
1266: */
1267: public Enumeration getHeaders(String headerName) {
1268: if (headerName == null) {
1269: throw new NullPointerException("null headerName");
1270: }
1271:
1272: Header sipHeader = (Header) nameTable.get(NameMap
1273: .expandHeaderName(headerName).toLowerCase());
1274:
1275: // empty iterator
1276: if (sipHeader == null) {
1277: return new Vector().elements();
1278: }
1279:
1280: if (sipHeader instanceof HeaderList) {
1281: return ((HeaderList) sipHeader).getElements();
1282: } else {
1283: Vector v = new Vector();
1284: v.addElement(sipHeader);
1285: return v.elements();
1286: }
1287: }
1288:
1289: /**
1290: * Gets a SIP Header list given its name.
1291: * @param headerName is the name of the header to get.
1292: * @return a header list that contains the retrieved header.
1293: */
1294: public HeaderList getHeaderList(String headerName) {
1295: Header header = (Header) nameTable.get(NameMap
1296: .expandHeaderName(headerName).toLowerCase());
1297:
1298: if (header == null) {
1299: return null;
1300: }
1301:
1302: if (header instanceof HeaderList) {
1303: return (HeaderList) header;
1304: } else {
1305: HeaderList hl = new HeaderList();
1306: hl.add(header);
1307: return hl;
1308: }
1309: }
1310:
1311: /**
1312: * Returns true if the Message has a header of the given name.
1313: *
1314: * @param headerName is the header name for which we are testing.
1315: * @return true if the header is present in the message
1316: */
1317: public boolean hasHeader(String headerName) {
1318: return nameTable.containsKey(NameMap.expandHeaderName(
1319: headerName).toLowerCase());
1320: }
1321:
1322: /**
1323: * Returns true if the message has a FromHeader header tag.
1324: *
1325: * @return true if the message has a from header and that header has
1326: * a tag.
1327: */
1328: public boolean hasFromHeaderTag() {
1329: return fromHeader != null && fromHeader.getTag() != null;
1330: }
1331:
1332: /**
1333: * Returns true if the message has a To header tag.
1334: *
1335: * @return true if the message has a to header and that header has
1336: * a tag.
1337: */
1338: public boolean hasToTag() {
1339: return toHeader != null && toHeader.getTag() != null;
1340: }
1341:
1342: /**
1343: * Returns the from tag.
1344: *
1345: * @return the tag from the from header.
1346: *
1347: */
1348: public String getFromHeaderTag() {
1349: return fromHeader == null ? null : fromHeader.getTag();
1350: }
1351:
1352: /**
1353: * Sets the FromHeader Tag.
1354: *
1355: * @param tag -- tag to set in the from header.
1356: * @throws SipException if the header can't be set for some reason.
1357: */
1358: public void setFromHeaderTag(String tag) throws SipException {
1359: fromHeader.setTag(tag);
1360: }
1361:
1362: /**
1363: * Sets the to tag.
1364: *
1365: * @param tag -- tag to set.
1366: * @throws SipException if the header can't be set for some reason.
1367: */
1368: public void setToTag(String tag) throws SipException {
1369: toHeader.setTag(tag);
1370: }
1371:
1372: /**
1373: * Returns the To tag.
1374: * @return the To tag field
1375: */
1376: public String getToTag() {
1377: return toHeader == null ? null : toHeader.getTag();
1378: }
1379:
1380: /**
1381: * Returns the encoded first line.
1382: * @return the first line
1383: */
1384: public abstract String getFirstLine();
1385:
1386: /**
1387: * Adds a SIP header.
1388: * @param sipHeader -- sip header to add.
1389: * @throws SipException if the header can't be added for some reason.
1390: */
1391: public void addHeader(Header sipHeader) throws SipException {
1392: Header sh = (Header) sipHeader;
1393: // Add the header value topmost of this type of headers
1394: // as required by JSR180.
1395: attachHeader(sh, false, true);
1396:
1397: /*
1398: if (sipHeader instanceof ViaHeader) {
1399: attachHeader(sh, false, true);
1400: } else {
1401: attachHeader(sh, false, false);
1402: }
1403: */
1404: }
1405:
1406: /**
1407: * Adds a header to the unparsed list of headers.
1408: *
1409: * @param unparsed -- unparsed header to add to the list.
1410: */
1411: public void addUnparsed(String unparsed) {
1412: unrecognizedHeaders.addElement(unparsed);
1413: }
1414:
1415: /**
1416: * Adds a SIP header.
1417: * @param sipHeader -- string version of SIP header to add.
1418: * @throws SipException if the header can't be added for some reason.
1419: */
1420: public void addHeader(String sipHeader) throws SipException {
1421: String hdrString = sipHeader.trim() + "\n";
1422: try {
1423: HeaderParser parser = ParserFactory.createParser(sipHeader);
1424: Header sh = parser.parse();
1425: attachHeader(sh, false);
1426: } catch (ParseException ex) {
1427: unrecognizedHeaders.addElement(hdrString);
1428: }
1429: }
1430:
1431: /**
1432: * Gets a list containing the unrecognized headers.
1433: * @return a linked list containing unrecongnized headers.
1434: */
1435: public Enumeration getUnrecognizedHeaders() {
1436: return unrecognizedHeaders.elements();
1437: }
1438:
1439: /**
1440: * Gets the header names.
1441: *
1442: * @return a list iterator to a list of header names. These are ordered
1443: * in the same order as are present in the message.
1444: */
1445: public Enumeration getHeaderNames() {
1446: Enumeration li = this .headers.elements();
1447: Vector retval = new Vector();
1448:
1449: while (li.hasMoreElements()) {
1450: Header sipHeader = (Header) li.nextElement();
1451: String name = sipHeader.getName();
1452: retval.addElement(name);
1453: }
1454:
1455: return retval.elements();
1456: }
1457:
1458: /**
1459: * Compares for equality.
1460: *
1461: * @param other the other object to compare with.
1462: * @return true if object matches
1463: */
1464: public boolean equals(Object other) {
1465: if (!other.getClass().equals(this .getClass()))
1466: return false;
1467:
1468: Message otherMessage = (Message) other;
1469: Enumeration values = this .nameTable.elements();
1470: Hashtable otherNameTable = otherMessage.nameTable;
1471:
1472: if (otherNameTable.size() != nameTable.size())
1473: return false;
1474:
1475: while (values.hasMoreElements()) {
1476: Header mine = (Header) values.nextElement();
1477: // maybe short form
1478: String mineName = NameMap.expandHeaderName(
1479: mine.getHeaderName()).trim().toLowerCase();
1480: Header hisHeader = (Header) otherNameTable.get(mineName);
1481: String his = null;
1482: if (hisHeader != null) {
1483: his = hisHeader.toString().trim();
1484: }
1485:
1486: if (his == null) {
1487: return false;
1488: } else if (!his.equals(mine.toString().trim())) {
1489: return false;
1490: }
1491: }
1492:
1493: return true;
1494: }
1495:
1496: /**
1497: * Sets the content length header.
1498: *
1499: * @param contentLength content length header.
1500: * @throws SipException if Content-Length header can't be set
1501: * for some reason.
1502: */
1503: public void setContentLength(ContentLengthHeader contentLength)
1504: throws SipException {
1505: setHeader(contentLength);
1506: }
1507:
1508: /**
1509: * Sets the CSeqHeader header.
1510: *
1511: * @param cseqHeader CSeqHeader Header.
1512: * @throws SipException if CSeq header can't be added for some reason.
1513: */
1514: public void setCSeqHeader(CSeqHeader cseqHeader)
1515: throws SipException {
1516: setHeader(cseqHeader);
1517: }
1518:
1519: /**
1520: * Sets the SIP version header.
1521: * @param sipVersion the new version string
1522: */
1523: public abstract void setSIPVersion(String sipVersion)
1524: throws ParseException;
1525:
1526: /**
1527: * Gets the SIP vesrion string.
1528: * @return the SIP version string
1529: */
1530: public abstract String getSIPVersion();
1531: }
|