1 /*
2  * Conditions Of Use
3  *
4  * This software was developed by employees of the National Institute of
5  * Standards and Technology (NIST), an agency of the Federal Government.
6  * Pursuant to title 15 Untied States Code Section 105, works of NIST
7  * employees are not subject to copyright protection in the United States
8  * and are considered to be in the public domain.  As a result, a formal
9  * license is not needed to use the software.
10  *
11  * This software is provided by NIST as a service and is expressly
12  * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
13  * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
14  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
15  * AND DATA ACCURACY.  NIST does not warrant or make any representations
16  * regarding the use of the software or the results thereof, including but
17  * not limited to the correctness, accuracy, reliability or usefulness of
18  * the software.
19  *
20  * Permission to use this software is contingent upon your acceptance
21  * of the terms of this agreement
22  *
23  * .
24  *
25  */
26 /*******************************************************************************
27  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)        *
28  ******************************************************************************/
29 package gov.nist.javax.sip.message;
30 
31 import gov.nist.core.InternalErrorHandler;
32 import gov.nist.javax.sip.SIPConstants;
33 import gov.nist.javax.sip.Utils;
34 import gov.nist.javax.sip.header.AlertInfo;
35 import gov.nist.javax.sip.header.Authorization;
36 import gov.nist.javax.sip.header.CSeq;
37 import gov.nist.javax.sip.header.CallID;
38 import gov.nist.javax.sip.header.Contact;
39 import gov.nist.javax.sip.header.ContactList;
40 import gov.nist.javax.sip.header.ContentLength;
41 import gov.nist.javax.sip.header.ContentType;
42 import gov.nist.javax.sip.header.ErrorInfo;
43 import gov.nist.javax.sip.header.ErrorInfoList;
44 import gov.nist.javax.sip.header.From;
45 import gov.nist.javax.sip.header.InReplyTo;
46 import gov.nist.javax.sip.header.MaxForwards;
47 import gov.nist.javax.sip.header.Priority;
48 import gov.nist.javax.sip.header.ProxyAuthenticate;
49 import gov.nist.javax.sip.header.ProxyAuthorization;
50 import gov.nist.javax.sip.header.ProxyRequire;
51 import gov.nist.javax.sip.header.ProxyRequireList;
52 import gov.nist.javax.sip.header.RSeq;
53 import gov.nist.javax.sip.header.RecordRouteList;
54 import gov.nist.javax.sip.header.RetryAfter;
55 import gov.nist.javax.sip.header.Route;
56 import gov.nist.javax.sip.header.RouteList;
57 import gov.nist.javax.sip.header.SIPETag;
58 import gov.nist.javax.sip.header.SIPHeader;
59 import gov.nist.javax.sip.header.SIPHeaderList;
60 import gov.nist.javax.sip.header.SIPHeaderNamesCache;
61 import gov.nist.javax.sip.header.SIPIfMatch;
62 import gov.nist.javax.sip.header.Server;
63 import gov.nist.javax.sip.header.Subject;
64 import gov.nist.javax.sip.header.To;
65 import gov.nist.javax.sip.header.Unsupported;
66 import gov.nist.javax.sip.header.UserAgent;
67 import gov.nist.javax.sip.header.Via;
68 import gov.nist.javax.sip.header.ViaList;
69 import gov.nist.javax.sip.header.WWWAuthenticate;
70 import gov.nist.javax.sip.header.Warning;
71 import gov.nist.javax.sip.parser.HeaderParser;
72 import gov.nist.javax.sip.parser.ParserFactory;
73 import gov.nist.javax.sip.parser.PipelinedMsgParser;
74 import gov.nist.javax.sip.parser.StringMsgParser;
75 
76 import java.io.UnsupportedEncodingException;
77 import java.lang.reflect.Field;
78 import java.text.ParseException;
79 import java.util.Collection;
80 import java.util.Hashtable;
81 import java.util.Iterator;
82 import java.util.LinkedList;
83 import java.util.List;
84 import java.util.ListIterator;
85 import java.util.concurrent.ConcurrentLinkedQueue;
86 
87 import javax.sip.InvalidArgumentException;
88 import javax.sip.SipException;
89 import javax.sip.header.AuthorizationHeader;
90 import javax.sip.header.CSeqHeader;
91 import javax.sip.header.CallIdHeader;
92 import javax.sip.header.ContactHeader;
93 import javax.sip.header.ContentDispositionHeader;
94 import javax.sip.header.ContentEncodingHeader;
95 import javax.sip.header.ContentLanguageHeader;
96 import javax.sip.header.ContentLengthHeader;
97 import javax.sip.header.ContentTypeHeader;
98 import javax.sip.header.ExpiresHeader;
99 import javax.sip.header.FromHeader;
100 import javax.sip.header.Header;
101 import javax.sip.header.MaxForwardsHeader;
102 import javax.sip.header.RecordRouteHeader;
103 import javax.sip.header.RouteHeader;
104 import javax.sip.header.ToHeader;
105 import javax.sip.header.ViaHeader;
106 import javax.sip.message.Request;
107 
108 /*
109  * Acknowledgements: Yanick Belanger sent in a patch for the right content length when the content
110  * is a String. Bill Mccormick from Nortel Networks sent in a bug fix for setContent.
111  *
112  */
113 /**
114  * This is the main SIP Message structure.
115  *
116  * @see StringMsgParser
117  * @see PipelinedMsgParser
118  *
119  * @version 1.2 $Revision: 1.53 $ $Date: 2009/12/16 14:58:40 $
120  * @since 1.1
121  *
122  * @author M. Ranganathan <br/>
123  *
124  *
125  */
126 public abstract class SIPMessage extends MessageObject implements javax.sip.message.Message,
127         MessageExt {
128 
129 	// JvB: use static here?
130     private String contentEncodingCharset = MessageFactoryImpl.getDefaultContentEncodingCharset();
131 
132     /*
133      * True if this is a null request.
134      */
135     protected boolean nullRequest;
136 
137     /**
138      * unparsed headers
139      */
140     protected LinkedList<String> unrecognizedHeaders;
141 
142     /**
143      * List of parsed headers (in the order they were added)
144      */
145     protected ConcurrentLinkedQueue<SIPHeader> headers;
146 
147     /**
148      * Direct accessors for frequently accessed headers
149      */
150     protected From fromHeader;
151 
152     protected To toHeader;
153 
154     protected CSeq cSeqHeader;
155 
156     protected CallID callIdHeader;
157 
158     protected ContentLength contentLengthHeader;
159 
160     protected MaxForwards maxForwardsHeader;
161 
162     // Cumulative size of all the headers.
163     protected int size;
164 
165     // Payload
166     private String messageContent;
167 
168     private byte[] messageContentBytes;
169 
170     private Object messageContentObject;
171 
172     // Table of headers indexed by name.
173     private Hashtable<String, SIPHeader> nameTable;
174 
175     /**
176      * The application data pointer. This is un-interpreted by the stack. This is provided as a
177      * convenient way of keeping book-keeping data for applications.
178      */
179     protected Object applicationData;
180 
181     /**
182      * Return true if the header belongs only in a Request.
183      *
184      * @param sipHeader is the header to test.
185      */
isRequestHeader(SIPHeader sipHeader)186     public static boolean isRequestHeader(SIPHeader sipHeader) {
187         return sipHeader instanceof AlertInfo || sipHeader instanceof InReplyTo
188                 || sipHeader instanceof Authorization || sipHeader instanceof MaxForwards
189                 || sipHeader instanceof UserAgent || sipHeader instanceof Priority
190                 || sipHeader instanceof ProxyAuthorization || sipHeader instanceof ProxyRequire
191                 || sipHeader instanceof ProxyRequireList || sipHeader instanceof Route
192                 || sipHeader instanceof RouteList || sipHeader instanceof Subject
193                 || sipHeader instanceof SIPIfMatch;
194     }
195 
196     /**
197      * Return true if the header belongs only in a response.
198      *
199      * @param sipHeader is the header to test.
200      */
isResponseHeader(SIPHeader sipHeader)201     public static boolean isResponseHeader(SIPHeader sipHeader) {
202         return sipHeader instanceof ErrorInfo || sipHeader instanceof ProxyAuthenticate
203                 || sipHeader instanceof Server || sipHeader instanceof Unsupported
204                 || sipHeader instanceof RetryAfter || sipHeader instanceof Warning
205                 || sipHeader instanceof WWWAuthenticate || sipHeader instanceof SIPETag
206                 || sipHeader instanceof RSeq;
207 
208     }
209 
210     /**
211      * Get the headers as a linked list of encoded Strings
212      *
213      * @return a linked list with each element of the list containing a string encoded header in
214      *         canonical form.
215      */
getMessageAsEncodedStrings()216     public LinkedList<String> getMessageAsEncodedStrings() {
217         LinkedList<String> retval = new LinkedList<String>();
218         Iterator<SIPHeader> li = headers.iterator();
219         while (li.hasNext()) {
220             SIPHeader sipHeader = (SIPHeader) li.next();
221             if (sipHeader instanceof SIPHeaderList) {
222                 SIPHeaderList< ? > shl = (SIPHeaderList< ? >) sipHeader;
223                 retval.addAll(shl.getHeadersAsEncodedStrings());
224             } else {
225                 retval.add(sipHeader.encode());
226             }
227         }
228 
229         return retval;
230     }
231 
232     /**
233      * Encode only the message and exclude the contents (for debugging);
234      *
235      * @return a string with all the headers encoded.
236      */
encodeSIPHeaders()237     protected String encodeSIPHeaders() {
238         StringBuffer encoding = new StringBuffer();
239         Iterator<SIPHeader> it = this.headers.iterator();
240 
241         while (it.hasNext()) {
242             SIPHeader siphdr = (SIPHeader) it.next();
243             if (!(siphdr instanceof ContentLength))
244                 siphdr.encode(encoding);
245         }
246 
247         return contentLengthHeader.encode(encoding).append(NEWLINE).toString();
248     }
249 
250     /**
251      * Encode all the headers except the contents. For debug logging.
252      */
encodeMessage()253     public abstract String encodeMessage();
254 
255     /**
256      * Get A dialog identifier constructed from this messsage. This is an id that can be used to
257      * identify dialogs.
258      *
259      * @param isServerTransaction is a flag that indicates whether this is a server transaction.
260      */
getDialogId(boolean isServerTransaction)261     public abstract String getDialogId(boolean isServerTransaction);
262 
263     /**
264      * Template match for SIP messages. The matchObj is a SIPMessage template to match against.
265      * This method allows you to do pattern matching with incoming SIP messages. Null matches wild
266      * card.
267      *
268      * @param other is the match template to match against.
269      * @return true if a match occured and false otherwise.
270      */
match(Object other)271     public boolean match(Object other) {
272         if (other == null)
273             return true;
274         if (!other.getClass().equals(this.getClass()))
275             return false;
276         SIPMessage matchObj = (SIPMessage) other;
277         Iterator<SIPHeader> li = matchObj.getHeaders();
278         while (li.hasNext()) {
279             SIPHeader hisHeaders = (SIPHeader) li.next();
280             List<SIPHeader> myHeaders = this.getHeaderList(hisHeaders.getHeaderName());
281 
282             // Could not find a header to match his header.
283             if (myHeaders == null || myHeaders.size() == 0)
284                 return false;
285 
286             if (hisHeaders instanceof SIPHeaderList) {
287                 ListIterator< ? > outerIterator = ((SIPHeaderList< ? >) hisHeaders)
288                         .listIterator();
289                 while (outerIterator.hasNext()) {
290                     SIPHeader hisHeader = (SIPHeader) outerIterator.next();
291                     if (hisHeader instanceof ContentLength)
292                         continue;
293                     ListIterator< ? > innerIterator = myHeaders.listIterator();
294                     boolean found = false;
295                     while (innerIterator.hasNext()) {
296                         SIPHeader myHeader = (SIPHeader) innerIterator.next();
297                         if (myHeader.match(hisHeader)) {
298                             found = true;
299                             break;
300                         }
301                     }
302                     if (!found)
303                         return false;
304                 }
305             } else {
306                 SIPHeader hisHeader = hisHeaders;
307                 ListIterator<SIPHeader> innerIterator = myHeaders.listIterator();
308                 boolean found = false;
309                 while (innerIterator.hasNext()) {
310                     SIPHeader myHeader = (SIPHeader) innerIterator.next();
311                     if (myHeader.match(hisHeader)) {
312                         found = true;
313                         break;
314                     }
315                 }
316                 if (!found)
317                     return false;
318             }
319         }
320         return true;
321 
322     }
323 
324     /**
325      * Merge a request with a template
326      *
327      * @param template -- template to merge with.
328      *
329      */
merge(Object template)330     public void merge(Object template) {
331         if (!template.getClass().equals(this.getClass()))
332             throw new IllegalArgumentException("Bad class " + template.getClass());
333         SIPMessage templateMessage = (SIPMessage) template;
334         Object[] templateHeaders = templateMessage.headers.toArray();
335         for (int i = 0; i < templateHeaders.length; i++) {
336             SIPHeader hdr = (SIPHeader) templateHeaders[i];
337             String hdrName = hdr.getHeaderName();
338             List<SIPHeader> myHdrs = this.getHeaderList(hdrName);
339             if (myHdrs == null) {
340                 this.attachHeader(hdr);
341             } else {
342                 ListIterator<SIPHeader> it = myHdrs.listIterator();
343                 while (it.hasNext()) {
344                     SIPHeader sipHdr = (SIPHeader) it.next();
345                     sipHdr.merge(hdr);
346                 }
347             }
348         }
349 
350     }
351 
352     /**
353      * Encode this message as a string. This is more efficient when the payload is a string
354      * (rather than a binary array of bytes). If the payload cannot be encoded as a UTF-8 string
355      * then it is simply ignored (will not appear in the encoded message).
356      *
357      * @return The Canonical String representation of the message (including the canonical string
358      *         representation of the SDP payload if it exists).
359      */
encode()360     public String encode() {
361         StringBuffer encoding = new StringBuffer();
362         Iterator<SIPHeader> it = this.headers.iterator();
363 
364         while (it.hasNext()) {
365             SIPHeader siphdr = (SIPHeader) it.next();
366             if (!(siphdr instanceof ContentLength))
367                 encoding.append(siphdr.encode());
368         }
369         // Append the unrecognized headers. Headers that are not
370         // recognized are passed through unchanged.
371         for (String unrecognized : this.unrecognizedHeaders) {
372             encoding.append(unrecognized).append(NEWLINE);
373         }
374 
375         encoding.append(contentLengthHeader.encode()).append(NEWLINE);
376 
377         if (this.messageContentObject != null) {
378             String mbody = this.getContent().toString();
379 
380             encoding.append(mbody);
381         } else if (this.messageContent != null || this.messageContentBytes != null) {
382 
383             String content = null;
384             try {
385                 if (messageContent != null)
386                     content = messageContent;
387                 else {
388                 	// JvB: Check for 'charset' parameter which overrides the default UTF-8
389                     content = new String(messageContentBytes, getCharset() );
390                 }
391             } catch (UnsupportedEncodingException ex) {
392             	InternalErrorHandler.handleException(ex);
393             }
394 
395             encoding.append(content);
396         }
397         return encoding.toString();
398     }
399 
400     /**
401      * Encode the message as a byte array. Use this when the message payload is a binary byte
402      * array.
403      *
404      * @return The Canonical byte array representation of the message (including the canonical
405      *         byte array representation of the SDP payload if it exists all in one contiguous
406      *         byte array).
407      */
encodeAsBytes(String transport)408     public byte[] encodeAsBytes(String transport) {
409         if (this instanceof SIPRequest && ((SIPRequest) this).isNullRequest()) {
410             return "\r\n\r\n".getBytes();
411         }
412         // JvB: added to fix case where application provides the wrong transport
413         // in the topmost Via header
414         ViaHeader topVia = (ViaHeader) this.getHeader(ViaHeader.NAME);
415         try {
416             topVia.setTransport(transport);
417         } catch (ParseException e) {
418             InternalErrorHandler.handleException(e);
419         }
420 
421         StringBuffer encoding = new StringBuffer();
422         synchronized (this.headers) {
423             Iterator<SIPHeader> it = this.headers.iterator();
424 
425             while (it.hasNext()) {
426                 SIPHeader siphdr = (SIPHeader) it.next();
427                 if (!(siphdr instanceof ContentLength))
428                     siphdr.encode(encoding);
429 
430             }
431         }
432         contentLengthHeader.encode(encoding);
433         encoding.append(NEWLINE);
434 
435         byte[] retval = null;
436         byte[] content = this.getRawContent();
437         if (content != null) {
438             // Append the content
439 
440             byte[] msgarray = null;
441             try {
442                 msgarray = encoding.toString().getBytes( getCharset() );
443             } catch (UnsupportedEncodingException ex) {
444                 InternalErrorHandler.handleException(ex);
445             }
446 
447             retval = new byte[msgarray.length + content.length];
448             System.arraycopy(msgarray, 0, retval, 0, msgarray.length);
449             System.arraycopy(content, 0, retval, msgarray.length, content.length);
450         } else {
451             // Message content does not exist.
452 
453             try {
454                 retval = encoding.toString().getBytes( getCharset() );
455             } catch (UnsupportedEncodingException ex) {
456                 InternalErrorHandler.handleException(ex);
457             }
458         }
459         return retval;
460     }
461 
462     /**
463      * clone this message (create a new deep physical copy). All headers in the message are
464      * cloned. You can modify the cloned copy without affecting the original. The content is
465      * handled as follows: If the content is a String, or a byte array, a new copy of the content
466      * is allocated and copied over. If the content is an Object that supports the clone method,
467      * then the clone method is invoked and the cloned content is the new content. Otherwise, the
468      * content of the new message is set equal to the old one.
469      *
470      * @return A cloned copy of this object.
471      */
clone()472     public Object clone() {
473         SIPMessage retval = (SIPMessage) super.clone();
474         retval.nameTable = new Hashtable<String, SIPHeader>();
475         retval.fromHeader = null;
476         retval.toHeader = null;
477         retval.cSeqHeader = null;
478         retval.callIdHeader = null;
479         retval.contentLengthHeader = null;
480         retval.maxForwardsHeader = null;
481         if (this.headers != null) {
482             retval.headers = new ConcurrentLinkedQueue<SIPHeader>();
483             for (Iterator<SIPHeader> iter = headers.iterator(); iter.hasNext();) {
484                 SIPHeader hdr = (SIPHeader) iter.next();
485                 retval.attachHeader((SIPHeader) hdr.clone());
486             }
487 
488         }
489         if (this.messageContentBytes != null)
490             retval.messageContentBytes = (byte[]) this.messageContentBytes.clone();
491         if (this.messageContentObject != null)
492             retval.messageContentObject = makeClone(messageContentObject);
493         retval.unrecognizedHeaders = this.unrecognizedHeaders;
494         return retval;
495     }
496 
497     /**
498      * Get the string representation of this header (for pretty printing the generated structure).
499      *
500      * @return Formatted string representation of the object. Note that this is NOT the same as
501      *         encode(). This is used mainly for debugging purposes.
502      */
debugDump()503     public String debugDump() {
504         stringRepresentation = "";
505         sprint("SIPMessage:");
506         sprint("{");
507         try {
508 
509             Field[] fields = this.getClass().getDeclaredFields();
510             for (int i = 0; i < fields.length; i++) {
511                 Field f = fields[i];
512                 Class< ? > fieldType = f.getType();
513                 String fieldName = f.getName();
514                 if (f.get(this) != null && SIPHeader.class.isAssignableFrom(fieldType)
515                         && fieldName.compareTo("headers") != 0) {
516                     sprint(fieldName + "=");
517                     sprint(((SIPHeader) f.get(this)).debugDump());
518                 }
519             }
520         } catch (Exception ex) {
521             InternalErrorHandler.handleException(ex);
522         }
523 
524         sprint("List of headers : ");
525         sprint(headers.toString());
526         sprint("messageContent = ");
527         sprint("{");
528         sprint(messageContent);
529         sprint("}");
530         if (this.getContent() != null) {
531             sprint(this.getContent().toString());
532         }
533         sprint("}");
534         return stringRepresentation;
535     }
536 
537     /**
538      * Constructor: Initializes lists and list headers. All the headers for which there can be
539      * multiple occurances in a message are derived from the SIPHeaderListClass. All singleton
540      * headers are derived from SIPHeader class.
541      */
SIPMessage()542     public SIPMessage() {
543         this.unrecognizedHeaders = new LinkedList<String>();
544         this.headers = new ConcurrentLinkedQueue<SIPHeader>();
545         nameTable = new Hashtable<String, SIPHeader>();
546         try {
547             this.attachHeader(new ContentLength(0), false);
548         } catch (Exception ex) {
549         }
550     }
551 
552     /**
553      * Attach a header and die if you get a duplicate header exception.
554      *
555      * @param h SIPHeader to attach.
556      */
attachHeader(SIPHeader h)557     private void attachHeader(SIPHeader h) {
558         if (h == null)
559             throw new IllegalArgumentException("null header!");
560         try {
561             if (h instanceof SIPHeaderList) {
562                 SIPHeaderList< ? > hl = (SIPHeaderList< ? >) h;
563                 if (hl.isEmpty()) {
564                     return;
565                 }
566             }
567             attachHeader(h, false, false);
568         } catch (SIPDuplicateHeaderException ex) {
569             // InternalErrorHandler.handleException(ex);
570         }
571     }
572 
573     /**
574      * Attach a header (replacing the original header).
575      *
576      * @param sipHeader SIPHeader that replaces a header of the same type.
577      */
setHeader(Header sipHeader)578     public void setHeader(Header sipHeader) {
579         SIPHeader header = (SIPHeader) sipHeader;
580         if (header == null)
581             throw new IllegalArgumentException("null header!");
582         try {
583             if (header instanceof SIPHeaderList) {
584                 SIPHeaderList< ? > hl = (SIPHeaderList< ? >) header;
585                 // Ignore empty lists.
586                 if (hl.isEmpty())
587                     return;
588             }
589             this.removeHeader(header.getHeaderName());
590             attachHeader(header, true, false);
591         } catch (SIPDuplicateHeaderException ex) {
592             InternalErrorHandler.handleException(ex);
593         }
594     }
595 
596     /**
597      * Set a header from a linked list of headers.
598      *
599      * @param headers -- a list of headers to set.
600      */
setHeaders(java.util.List<SIPHeader> headers)601     public void setHeaders(java.util.List<SIPHeader> headers) {
602         ListIterator<SIPHeader> listIterator = headers.listIterator();
603         while (listIterator.hasNext()) {
604             SIPHeader sipHeader = (SIPHeader) listIterator.next();
605             try {
606                 this.attachHeader(sipHeader, false);
607             } catch (SIPDuplicateHeaderException ex) {
608             }
609         }
610     }
611 
612     /**
613      * Attach a header to the end of the existing headers in this SIPMessage structure. This is
614      * equivalent to the attachHeader(SIPHeader,replaceflag,false); which is the normal way in
615      * which headers are attached. This was added in support of JAIN-SIP.
616      *
617      * @param h header to attach.
618      * @param replaceflag if true then replace a header if it exists.
619      * @throws SIPDuplicateHeaderException If replaceFlag is false and only a singleton header is
620      *         allowed (fpr example CSeq).
621      */
attachHeader(SIPHeader h, boolean replaceflag)622     public void attachHeader(SIPHeader h, boolean replaceflag) throws SIPDuplicateHeaderException {
623         this.attachHeader(h, replaceflag, false);
624     }
625 
626     /**
627      * Attach the header to the SIP Message structure at a specified position in its list of
628      * headers.
629      *
630      * @param header Header to attach.
631      * @param replaceFlag If true then replace the existing header.
632      * @param top Location in the header list to insert the header.
633      * @exception SIPDuplicateHeaderException if the header is of a type that cannot tolerate
634      *            duplicates and one of this type already exists (e.g. CSeq header).
635      * @throws IndexOutOfBoundsException If the index specified is greater than the number of
636      *         headers that are in this message.
637      */
638 
attachHeader(SIPHeader header, boolean replaceFlag, boolean top)639     public void attachHeader(SIPHeader header, boolean replaceFlag, boolean top)
640             throws SIPDuplicateHeaderException {
641         if (header == null) {
642             throw new NullPointerException("null header");
643         }
644 
645         SIPHeader h;
646 
647         if (ListMap.hasList(header) && !SIPHeaderList.class.isAssignableFrom(header.getClass())) {
648             SIPHeaderList<SIPHeader> hdrList = ListMap.getList(header);
649             hdrList.add(header);
650             h = hdrList;
651         } else {
652             h = header;
653         }
654 
655         String headerNameLowerCase = SIPHeaderNamesCache.toLowerCase(h.getName());
656         if (replaceFlag) {
657             nameTable.remove(headerNameLowerCase);
658         } else if (nameTable.containsKey(headerNameLowerCase) && !(h instanceof SIPHeaderList)) {
659             if (h instanceof ContentLength) {
660                 try {
661                     ContentLength cl = (ContentLength) h;
662                     contentLengthHeader.setContentLength(cl.getContentLength());
663                 } catch (InvalidArgumentException e) {
664                 }
665             }
666             // Just ignore duplicate header.
667             return;
668         }
669 
670         SIPHeader originalHeader = (SIPHeader) getHeader(header.getName());
671 
672         // Delete the original header from our list structure.
673         if (originalHeader != null) {
674             Iterator<SIPHeader> li = headers.iterator();
675             while (li.hasNext()) {
676                 SIPHeader next = (SIPHeader) li.next();
677                 if (next.equals(originalHeader)) {
678                     li.remove();
679                 }
680             }
681         }
682 
683         if (!nameTable.containsKey(headerNameLowerCase)) {
684             nameTable.put(headerNameLowerCase, h);
685             headers.add(h);
686         } else {
687             if (h instanceof SIPHeaderList) {
688                 SIPHeaderList< ? > hdrlist = (SIPHeaderList< ? >) nameTable
689                         .get(headerNameLowerCase);
690                 if (hdrlist != null)
691                     hdrlist.concatenate((SIPHeaderList) h, top);
692                 else
693                     nameTable.put(headerNameLowerCase, h);
694             } else {
695                 nameTable.put(headerNameLowerCase, h);
696             }
697         }
698 
699         // Direct accessor fields for frequently accessed headers.
700         if (h instanceof From) {
701             this.fromHeader = (From) h;
702         } else if (h instanceof ContentLength) {
703             this.contentLengthHeader = (ContentLength) h;
704         } else if (h instanceof To) {
705             this.toHeader = (To) h;
706         } else if (h instanceof CSeq) {
707             this.cSeqHeader = (CSeq) h;
708         } else if (h instanceof CallID) {
709             this.callIdHeader = (CallID) h;
710         } else if (h instanceof MaxForwards) {
711             this.maxForwardsHeader = (MaxForwards) h;
712         }
713 
714     }
715 
716     /**
717      * Remove a header given its name. If multiple headers of a given name are present then the
718      * top flag determines which end to remove headers from.
719      *
720      * @param headerName is the name of the header to remove.
721      * @param top -- flag that indicates which end of header list to process.
722      */
removeHeader(String headerName, boolean top)723     public void removeHeader(String headerName, boolean top) {
724 
725         String headerNameLowerCase = SIPHeaderNamesCache.toLowerCase(headerName);
726         SIPHeader toRemove = (SIPHeader) nameTable.get(headerNameLowerCase);
727         // nothing to do then we are done.
728         if (toRemove == null)
729             return;
730         if (toRemove instanceof SIPHeaderList) {
731             SIPHeaderList< ? > hdrList = (SIPHeaderList< ? >) toRemove;
732             if (top)
733                 hdrList.removeFirst();
734             else
735                 hdrList.removeLast();
736             // Clean up empty list
737             if (hdrList.isEmpty()) {
738                 Iterator<SIPHeader> li = this.headers.iterator();
739                 while (li.hasNext()) {
740                     SIPHeader sipHeader = (SIPHeader) li.next();
741                     if (sipHeader.getName().equalsIgnoreCase(headerNameLowerCase))
742                         li.remove();
743                 }
744 
745                 // JvB: also remove it from the nameTable! Else NPE in
746                 // DefaultRouter
747                 nameTable.remove(headerNameLowerCase);
748             }
749         } else {
750             this.nameTable.remove(headerNameLowerCase);
751             if (toRemove instanceof From) {
752                 this.fromHeader = null;
753             } else if (toRemove instanceof To) {
754                 this.toHeader = null;
755             } else if (toRemove instanceof CSeq) {
756                 this.cSeqHeader = null;
757             } else if (toRemove instanceof CallID) {
758                 this.callIdHeader = null;
759             } else if (toRemove instanceof MaxForwards) {
760                 this.maxForwardsHeader = null;
761             } else if (toRemove instanceof ContentLength) {
762                 this.contentLengthHeader = null;
763             }
764             Iterator<SIPHeader> li = this.headers.iterator();
765             while (li.hasNext()) {
766                 SIPHeader sipHeader = (SIPHeader) li.next();
767                 if (sipHeader.getName().equalsIgnoreCase(headerName))
768                     li.remove();
769             }
770         }
771 
772     }
773 
774     /**
775      * Remove all headers given its name.
776      *
777      * @param headerName is the name of the header to remove.
778      */
removeHeader(String headerName)779     public void removeHeader(String headerName) {
780 
781         if (headerName == null)
782             throw new NullPointerException("null arg");
783         String headerNameLowerCase = SIPHeaderNamesCache.toLowerCase(headerName);
784         SIPHeader removed = (SIPHeader) nameTable.remove(headerNameLowerCase);
785         // nothing to do then we are done.
786         if (removed == null)
787             return;
788 
789         // Remove the fast accessor fields.
790         if (removed instanceof From) {
791             this.fromHeader = null;
792         } else if (removed instanceof To) {
793             this.toHeader = null;
794         } else if (removed instanceof CSeq) {
795             this.cSeqHeader = null;
796         } else if (removed instanceof CallID) {
797             this.callIdHeader = null;
798         } else if (removed instanceof MaxForwards) {
799             this.maxForwardsHeader = null;
800         } else if (removed instanceof ContentLength) {
801             this.contentLengthHeader = null;
802         }
803 
804         Iterator<SIPHeader> li = this.headers.iterator();
805         while (li.hasNext()) {
806             SIPHeader sipHeader = (SIPHeader) li.next();
807             if (sipHeader.getName().equalsIgnoreCase(headerNameLowerCase))
808                 li.remove();
809 
810         }
811     }
812 
813     /**
814      * Generate (compute) a transaction ID for this SIP message.
815      *
816      * @return A string containing the concatenation of various portions of the From,To,Via and
817      *         RequestURI portions of this message as specified in RFC 2543: All responses to a
818      *         request contain the same values in the Call-ID, CSeq, To, and From fields (with the
819      *         possible addition of a tag in the To field (section 10.43)). This allows responses
820      *         to be matched with requests. Incorporates a bug fix for a bug sent in by Gordon
821      *         Ledgard of IPera for generating transactionIDs when no port is present in the via
822      *         header. Incorporates a bug fix for a bug report sent in by Chris Mills of Nortel
823      *         Networks (converts to lower case when returning the transaction identifier).
824      *
825      * @return a string that can be used as a transaction identifier for this message. This can be
826      *         used for matching responses and requests (i.e. an outgoing request and its matching
827      *         response have the same computed transaction identifier).
828      */
getTransactionId()829     public String getTransactionId() {
830         Via topVia = null;
831         if (!this.getViaHeaders().isEmpty()) {
832             topVia = (Via) this.getViaHeaders().getFirst();
833         }
834         // Have specified a branch Identifier so we can use it to identify
835         // the transaction. BranchId is not case sensitive.
836         // Branch Id prefix is not case sensitive.
837         if (topVia != null
838                 && topVia.getBranch() != null
839                 && topVia.getBranch().toUpperCase().startsWith(
840                         SIPConstants.BRANCH_MAGIC_COOKIE_UPPER_CASE)) {
841             // Bis 09 compatible branch assignment algorithm.
842             // implies that the branch id can be used as a transaction
843             // identifier.
844             if (this.getCSeq().getMethod().equals(Request.CANCEL))
845                 return (topVia.getBranch() + ":" + this.getCSeq().getMethod()).toLowerCase();
846             else
847                 return topVia.getBranch().toLowerCase();
848         } else {
849             // Old style client so construct the transaction identifier
850             // from various fields of the request.
851             StringBuffer retval = new StringBuffer();
852             From from = (From) this.getFrom();
853             To to = (To) this.getTo();
854             // String hpFrom = from.getUserAtHostPort();
855             // retval.append(hpFrom).append(":");
856             if (from.hasTag())
857                 retval.append(from.getTag()).append("-");
858             // String hpTo = to.getUserAtHostPort();
859             // retval.append(hpTo).append(":");
860             String cid = this.callIdHeader.getCallId();
861             retval.append(cid).append("-");
862             retval.append(this.cSeqHeader.getSequenceNumber()).append("-").append(
863                     this.cSeqHeader.getMethod());
864             if (topVia != null) {
865                 retval.append("-").append(topVia.getSentBy().encode());
866                 if (!topVia.getSentBy().hasPort()) {
867                     retval.append("-").append(5060);
868                 }
869             }
870             if (this.getCSeq().getMethod().equals(Request.CANCEL)) {
871                 retval.append(Request.CANCEL);
872             }
873             return retval.toString().toLowerCase().replace(":", "-").replace("@", "-")
874                     + Utils.getSignature();
875         }
876     }
877 
878     /**
879      * Override the hashcode method ( see issue # 55 ) Note that if you try to use this method
880      * before you assemble a valid request, you will get a constant ( -1 ). Beware of placing any
881      * half formed requests in a table.
882      */
hashCode()883     public int hashCode() {
884         if (this.callIdHeader == null)
885             throw new RuntimeException(
886                     "Invalid message! Cannot compute hashcode! call-id header is missing !");
887         else
888             return this.callIdHeader.getCallId().hashCode();
889     }
890 
891     /**
892      * Return true if this message has a body.
893      */
hasContent()894     public boolean hasContent() {
895         return messageContent != null || messageContentBytes != null;
896     }
897 
898     /**
899      * Return an iterator for the list of headers in this message.
900      *
901      * @return an Iterator for the headers of this message.
902      */
getHeaders()903     public Iterator<SIPHeader> getHeaders() {
904         return headers.iterator();
905     }
906 
907     /**
908      * Get the first header of the given name.
909      *
910      * @return header -- the first header of the given name.
911      */
getHeader(String headerName)912     public Header getHeader(String headerName) {
913         return getHeaderLowerCase(SIPHeaderNamesCache.toLowerCase(headerName));
914     }
915 
getHeaderLowerCase(String lowerCaseHeaderName)916     private Header getHeaderLowerCase(String lowerCaseHeaderName) {
917         if (lowerCaseHeaderName == null)
918             throw new NullPointerException("bad name");
919         SIPHeader sipHeader = (SIPHeader) nameTable.get(lowerCaseHeaderName);
920         if (sipHeader instanceof SIPHeaderList)
921             return (Header) ((SIPHeaderList) sipHeader).getFirst();
922         else
923             return (Header) sipHeader;
924     }
925 
926     /**
927      * Get the contentType header (null if one does not exist).
928      *
929      * @return contentType header
930      */
931 
getContentTypeHeader()932     public ContentType getContentTypeHeader() {
933         return (ContentType) getHeaderLowerCase(CONTENT_TYPE_LOWERCASE);
934     }
935 
936     private static final String CONTENT_TYPE_LOWERCASE = SIPHeaderNamesCache
937     .toLowerCase(ContentTypeHeader.NAME);
938 
939 
940     /**
941      * Get the contentLength header.
942      */
getContentLengthHeader()943     public ContentLengthHeader getContentLengthHeader() {
944         return this.getContentLength();
945     }
946 
947 
948     /**
949      * Get the from header.
950      *
951      * @return -- the from header.
952      */
getFrom()953     public FromHeader getFrom() {
954         return (FromHeader) fromHeader;
955     }
956 
957     /**
958      * Get the ErrorInfo list of headers (null if one does not exist).
959      *
960      * @return List containing ErrorInfo headers.
961      */
getErrorInfoHeaders()962     public ErrorInfoList getErrorInfoHeaders() {
963         return (ErrorInfoList) getSIPHeaderListLowerCase(ERROR_LOWERCASE);
964     }
965 
966     private static final String ERROR_LOWERCASE = SIPHeaderNamesCache.toLowerCase(ErrorInfo.NAME);
967 
968     /**
969      * Get the Contact list of headers (null if one does not exist).
970      *
971      * @return List containing Contact headers.
972      */
getContactHeaders()973     public ContactList getContactHeaders() {
974         return (ContactList) this.getSIPHeaderListLowerCase(CONTACT_LOWERCASE);
975     }
976 
977     private static final String CONTACT_LOWERCASE = SIPHeaderNamesCache
978             .toLowerCase(ContactHeader.NAME);
979 
980     /**
981      * Get the contact header ( the first contact header) which is all we need for the most part.
982      *
983      */
getContactHeader()984     public Contact getContactHeader() {
985         ContactList clist = this.getContactHeaders();
986         if (clist != null) {
987             return (Contact) clist.getFirst();
988 
989         } else {
990             return null;
991         }
992     }
993 
994     /**
995      * Get the Via list of headers (null if one does not exist).
996      *
997      * @return List containing Via headers.
998      */
getViaHeaders()999     public ViaList getViaHeaders() {
1000         return (ViaList) getSIPHeaderListLowerCase(VIA_LOWERCASE);
1001     }
1002 
1003     private static final String VIA_LOWERCASE = SIPHeaderNamesCache.toLowerCase(ViaHeader.NAME);
1004 
1005     /**
1006      * Set A list of via headers.
1007      *
1008      * @param viaList a list of via headers to add.
1009      */
setVia(java.util.List viaList)1010     public void setVia(java.util.List viaList) {
1011         ViaList vList = new ViaList();
1012         ListIterator it = viaList.listIterator();
1013         while (it.hasNext()) {
1014             Via via = (Via) it.next();
1015             vList.add(via);
1016         }
1017         this.setHeader(vList);
1018     }
1019 
1020     /**
1021      * Set the header given a list of headers.
1022      *
1023      * @param sipHeaderList a headerList to set
1024      */
1025 
setHeader(SIPHeaderList<Via> sipHeaderList)1026     public void setHeader(SIPHeaderList<Via> sipHeaderList) {
1027         this.setHeader((Header) sipHeaderList);
1028     }
1029 
1030     /**
1031      * Get the topmost via header.
1032      *
1033      * @return the top most via header if one exists or null if none exists.
1034      */
getTopmostVia()1035     public Via getTopmostVia() {
1036         if (this.getViaHeaders() == null)
1037             return null;
1038         else
1039             return (Via) (getViaHeaders().getFirst());
1040     }
1041 
1042     /**
1043      * Get the CSeq list of header (null if one does not exist).
1044      *
1045      * @return CSeq header
1046      */
getCSeq()1047     public CSeqHeader getCSeq() {
1048         return (CSeqHeader) cSeqHeader;
1049     }
1050 
1051     /**
1052      * Get the Authorization header (null if one does not exist).
1053      *
1054      * @return Authorization header.
1055      */
getAuthorization()1056     public Authorization getAuthorization() {
1057         return (Authorization) getHeaderLowerCase(AUTHORIZATION_LOWERCASE);
1058     }
1059 
1060     private static final String AUTHORIZATION_LOWERCASE = SIPHeaderNamesCache
1061             .toLowerCase(AuthorizationHeader.NAME);
1062 
1063     /**
1064      * Get the MaxForwards header (null if one does not exist).
1065      *
1066      * @return Max-Forwards header
1067      */
1068 
getMaxForwards()1069     public MaxForwardsHeader getMaxForwards() {
1070         return maxForwardsHeader;
1071     }
1072 
1073     /**
1074      * Set the max forwards header.
1075      *
1076      * @param maxForwards is the MaxForwardsHeader to set.
1077      */
setMaxForwards(MaxForwardsHeader maxForwards)1078     public void setMaxForwards(MaxForwardsHeader maxForwards) {
1079         this.setHeader(maxForwards);
1080     }
1081 
1082     /**
1083      * Get the Route List of headers (null if one does not exist).
1084      *
1085      * @return List containing Route headers
1086      */
getRouteHeaders()1087     public RouteList getRouteHeaders() {
1088         return (RouteList) getSIPHeaderListLowerCase(ROUTE_LOWERCASE);
1089     }
1090 
1091     private static final String ROUTE_LOWERCASE = SIPHeaderNamesCache
1092             .toLowerCase(RouteHeader.NAME);
1093 
1094     /**
1095      * Get the CallID header (null if one does not exist)
1096      *
1097      * @return Call-ID header .
1098      */
getCallId()1099     public CallIdHeader getCallId() {
1100         return callIdHeader;
1101     }
1102 
1103     /**
1104      * Set the call id header.
1105      *
1106      * @param callId call idHeader (what else could it be?)
1107      */
setCallId(CallIdHeader callId)1108     public void setCallId(CallIdHeader callId) {
1109         this.setHeader(callId);
1110     }
1111 
1112     /**
1113      * Get the CallID header (null if one does not exist)
1114      *
1115      * @param callId -- the call identifier to be assigned to the call id header
1116      */
setCallId(String callId)1117     public void setCallId(String callId) throws java.text.ParseException {
1118         if (callIdHeader == null) {
1119             this.setHeader(new CallID());
1120         }
1121         callIdHeader.setCallId(callId);
1122     }
1123 
1124     /**
1125      * Get the RecordRoute header list (null if one does not exist).
1126      *
1127      * @return Record-Route header
1128      */
getRecordRouteHeaders()1129     public RecordRouteList getRecordRouteHeaders() {
1130         return (RecordRouteList) this.getSIPHeaderListLowerCase(RECORDROUTE_LOWERCASE);
1131     }
1132 
1133     private static final String RECORDROUTE_LOWERCASE = SIPHeaderNamesCache
1134             .toLowerCase(RecordRouteHeader.NAME);
1135 
1136     /**
1137      * Get the To header (null if one does not exist).
1138      *
1139      * @return To header
1140      */
getTo()1141     public ToHeader getTo() {
1142         return (ToHeader) toHeader;
1143     }
1144 
setTo(ToHeader to)1145     public void setTo(ToHeader to) {
1146         this.setHeader(to);
1147     }
1148 
setFrom(FromHeader from)1149     public void setFrom(FromHeader from) {
1150         this.setHeader(from);
1151 
1152     }
1153 
1154     /**
1155      * Get the ContentLength header (null if one does not exist).
1156      *
1157      * @return content-length header.
1158      */
getContentLength()1159     public ContentLengthHeader getContentLength() {
1160         return this.contentLengthHeader;
1161     }
1162 
1163     /**
1164      * Get the message body as a string. If the message contains a content type header with a
1165      * specified charset, and if the payload has been read as a byte array, then it is returned
1166      * encoded into this charset.
1167      *
1168      * @return Message body (as a string)
1169      * @throws UnsupportedEncodingException if the platform does not support the charset specified
1170      *         in the content type header.
1171      *
1172      */
getMessageContent()1173     public String getMessageContent() throws UnsupportedEncodingException {
1174         if (this.messageContent == null && this.messageContentBytes == null)
1175             return null;
1176         else if (this.messageContent == null) {
1177             this.messageContent = new String(messageContentBytes, getCharset() );
1178         }
1179         return this.messageContent;
1180     }
1181 
1182     /**
1183      * Get the message content as an array of bytes. If the payload has been read as a String then
1184      * it is decoded using the charset specified in the content type header if it exists.
1185      * Otherwise, it is encoded using the default encoding which is UTF-8.
1186      *
1187      * @return an array of bytes that is the message payload.
1188      */
getRawContent()1189     public byte[] getRawContent() {
1190         try {
1191             if ( this.messageContentBytes != null ) {
1192                 // return messageContentBytes;
1193             } else if (this.messageContentObject != null) {
1194                 String messageContent = this.messageContentObject.toString();
1195                 this.messageContentBytes = messageContent.getBytes( getCharset() );
1196             } else if (this.messageContent != null) {
1197             	this.messageContentBytes = messageContent.getBytes( getCharset() );
1198             }
1199             return this.messageContentBytes;
1200         } catch (UnsupportedEncodingException ex) {
1201             InternalErrorHandler.handleException(ex);
1202             return null;
1203         }
1204     }
1205 
1206     /**
1207      * Set the message content given type and subtype.
1208      *
1209      * @param type is the message type (eg. application)
1210      * @param subType is the message sybtype (eg. sdp)
1211      * @param messageContent is the messge content as a string.
1212      */
setMessageContent(String type, String subType, String messageContent)1213     public void setMessageContent(String type, String subType, String messageContent) {
1214         if (messageContent == null)
1215             throw new IllegalArgumentException("messgeContent is null");
1216         ContentType ct = new ContentType(type, subType);
1217         this.setHeader(ct);
1218         this.messageContent = messageContent;
1219         this.messageContentBytes = null;
1220         this.messageContentObject = null;
1221         // Could be double byte so we need to compute length
1222         // after converting to byte[]
1223         computeContentLength(messageContent);
1224     }
1225 
1226     /**
1227      * Set the message content after converting the given object to a String.
1228      *
1229      * @param content -- content to set.
1230      * @param contentTypeHeader -- content type header corresponding to content.
1231      */
setContent(Object content, ContentTypeHeader contentTypeHeader)1232     public void setContent(Object content, ContentTypeHeader contentTypeHeader)
1233             throws ParseException {
1234         if (content == null)
1235             throw new NullPointerException("null content");
1236         this.setHeader(contentTypeHeader);
1237 
1238         this.messageContent = null;
1239         this.messageContentBytes = null;
1240         this.messageContentObject = null;
1241 
1242         if (content instanceof String) {
1243             this.messageContent = (String) content;
1244         } else if (content instanceof byte[]) {
1245             this.messageContentBytes = (byte[]) content;
1246         } else
1247             this.messageContentObject = content;
1248 
1249         computeContentLength(content);
1250     }
1251 
1252     /**
1253      * Get the content (body) of the message.
1254      *
1255      * @return the content of the sip message.
1256      */
getContent()1257     public Object getContent() {
1258         if (this.messageContentObject != null)
1259             return messageContentObject;
1260         else if (this.messageContent != null)
1261             return this.messageContent;
1262         else if (this.messageContentBytes != null)
1263             return this.messageContentBytes;
1264         else
1265             return null;
1266     }
1267 
1268     /**
1269      * Set the message content for a given type and subtype.
1270      *
1271      * @param type is the messge type.
1272      * @param subType is the message subType.
1273      * @param messageContent is the message content as a byte array.
1274      */
setMessageContent(String type, String subType, byte[] messageContent)1275     public void setMessageContent(String type, String subType, byte[] messageContent) {
1276         ContentType ct = new ContentType(type, subType);
1277         this.setHeader(ct);
1278         this.setMessageContent(messageContent);
1279 
1280         computeContentLength(messageContent);
1281     }
1282 
1283     /**
1284      * Set the message content for this message.
1285      *
1286      * @param content Message body as a string.
1287      */
setMessageContent(String content, boolean strict, boolean computeContentLength, int givenLength)1288     public void setMessageContent(String content, boolean strict, boolean computeContentLength, int givenLength)
1289             throws ParseException {
1290         // Note that that this could be a double byte character
1291         // set - bug report by Masafumi Watanabe
1292         computeContentLength(content);
1293         if ((!computeContentLength)) {
1294             if ( (!strict && this.contentLengthHeader.getContentLength() != givenLength)
1295                     || this.contentLengthHeader.getContentLength() < givenLength) {
1296                 throw new ParseException("Invalid content length "
1297                         + this.contentLengthHeader.getContentLength() + " / " + givenLength, 0);
1298             }
1299         }
1300 
1301         messageContent = content;
1302         messageContentBytes = null;
1303         messageContentObject = null;
1304     }
1305 
1306     /**
1307      * Set the message content as an array of bytes.
1308      *
1309      * @param content is the content of the message as an array of bytes.
1310      */
setMessageContent(byte[] content)1311     public void setMessageContent(byte[] content) {
1312         computeContentLength(content);
1313 
1314         messageContentBytes = content;
1315         messageContent = null;
1316         messageContentObject = null;
1317     }
1318 
1319     /**
1320      * Method to set the content - called by the parser
1321      *
1322      * @param content
1323      * @throws ParseException
1324      */
setMessageContent(byte[] content, boolean computeContentLength, int givenLength)1325     public void setMessageContent(byte[] content, boolean computeContentLength, int givenLength)
1326             throws ParseException {
1327         computeContentLength(content);
1328         if ((!computeContentLength) && this.contentLengthHeader.getContentLength() < givenLength) {
1329             // System.out.println("!!!!!!!!!!! MISMATCH !!!!!!!!!!!");
1330             throw new ParseException("Invalid content length "
1331                     + this.contentLengthHeader.getContentLength() + " / " + givenLength, 0);
1332         }
1333         messageContentBytes = content;
1334         messageContent = null;
1335         messageContentObject = null;
1336     }
1337 
1338     /**
1339      * Compute and set the Content-length header based on the given content object.
1340      *
1341      * @param content is the content, as String, array of bytes, or other object.
1342      */
computeContentLength(Object content)1343     private void computeContentLength(Object content) {
1344         int length = 0;
1345         if (content != null) {
1346             if (content instanceof String) {
1347                 try {
1348                     length = ((String) content).getBytes( getCharset() ).length;
1349                 } catch (UnsupportedEncodingException ex) {
1350                     InternalErrorHandler.handleException(ex);
1351                 }
1352             } else if (content instanceof byte[]) {
1353                 length = ((byte[]) content).length;
1354             } else {
1355                 length = content.toString().length();
1356             }
1357         }
1358 
1359         try {
1360             contentLengthHeader.setContentLength(length);
1361         } catch (InvalidArgumentException e) {
1362             // Cannot happen.
1363         }
1364     }
1365 
1366     /**
1367      * Remove the message content if it exists.
1368      */
removeContent()1369     public void removeContent() {
1370         messageContent = null;
1371         messageContentBytes = null;
1372         messageContentObject = null;
1373         try {
1374             this.contentLengthHeader.setContentLength(0);
1375         } catch (InvalidArgumentException ex) {
1376         }
1377     }
1378 
1379     /**
1380      * Get a SIP header or Header list given its name.
1381      *
1382      * @param headerName is the name of the header to get.
1383      * @return a header or header list that contians the retrieved header.
1384      */
1385     @SuppressWarnings("unchecked")
getHeaders(String headerName)1386     public ListIterator<SIPHeader> getHeaders(String headerName) {
1387         if (headerName == null)
1388             throw new NullPointerException("null headerName");
1389         SIPHeader sipHeader = (SIPHeader) nameTable.get(SIPHeaderNamesCache
1390                 .toLowerCase(headerName));
1391         // empty iterator
1392         if (sipHeader == null)
1393             return new LinkedList<SIPHeader>().listIterator();
1394         if (sipHeader instanceof SIPHeaderList) {
1395             return ((SIPHeaderList<SIPHeader>) sipHeader).listIterator();
1396         } else {
1397             return new HeaderIterator(this, sipHeader);
1398         }
1399     }
1400 
1401     /**
1402      * Get a header of the given name as a string. This concatenates the headers of a given type
1403      * as a comma separted list. This is useful for formatting and printing headers.
1404      *
1405      * @param name
1406      * @return the header as a formatted string
1407      */
getHeaderAsFormattedString(String name)1408     public String getHeaderAsFormattedString(String name) {
1409         String lowerCaseName = name.toLowerCase();
1410         if (this.nameTable.containsKey(lowerCaseName)) {
1411             return this.nameTable.get(lowerCaseName).toString();
1412         } else {
1413             return this.getHeader(name).toString();
1414         }
1415     }
1416 
getSIPHeaderListLowerCase(String lowerCaseHeaderName)1417     private SIPHeader getSIPHeaderListLowerCase(String lowerCaseHeaderName) {
1418         return nameTable.get(lowerCaseHeaderName);
1419     }
1420 
1421     /**
1422      * Get a list of headers of the given name ( or null if no such header exists ).
1423      *
1424      * @param headerName -- a header name from which to retrieve the list.
1425      * @return -- a list of headers with that name.
1426      */
1427     @SuppressWarnings("unchecked")
getHeaderList(String headerName)1428     private List<SIPHeader> getHeaderList(String headerName) {
1429         SIPHeader sipHeader = (SIPHeader) nameTable.get(SIPHeaderNamesCache
1430                 .toLowerCase(headerName));
1431         if (sipHeader == null)
1432             return null;
1433         else if (sipHeader instanceof SIPHeaderList)
1434             return (List<SIPHeader>) (((SIPHeaderList< ? >) sipHeader).getHeaderList());
1435         else {
1436             LinkedList<SIPHeader> ll = new LinkedList<SIPHeader>();
1437             ll.add(sipHeader);
1438             return ll;
1439         }
1440     }
1441 
1442     /**
1443      * Return true if the SIPMessage has a header of the given name.
1444      *
1445      * @param headerName is the header name for which we are testing.
1446      * @return true if the header is present in the message
1447      */
hasHeader(String headerName)1448     public boolean hasHeader(String headerName) {
1449         return nameTable.containsKey(SIPHeaderNamesCache.toLowerCase(headerName));
1450     }
1451 
1452     /**
1453      * Return true if the message has a From header tag.
1454      *
1455      * @return true if the message has a from header and that header has a tag.
1456      */
hasFromTag()1457     public boolean hasFromTag() {
1458         return fromHeader != null && fromHeader.getTag() != null;
1459     }
1460 
1461     /**
1462      * Return true if the message has a To header tag.
1463      *
1464      * @return true if the message has a to header and that header has a tag.
1465      */
hasToTag()1466     public boolean hasToTag() {
1467         return toHeader != null && toHeader.getTag() != null;
1468     }
1469 
1470     /**
1471      * Return the from tag.
1472      *
1473      * @return the tag from the from header.
1474      *
1475      */
getFromTag()1476     public String getFromTag() {
1477         return fromHeader == null ? null : fromHeader.getTag();
1478     }
1479 
1480     /**
1481      * Set the From Tag.
1482      *
1483      * @param tag -- tag to set in the from header.
1484      */
setFromTag(String tag)1485     public void setFromTag(String tag) {
1486         try {
1487             fromHeader.setTag(tag);
1488         } catch (ParseException e) {
1489         }
1490     }
1491 
1492     /**
1493      * Set the to tag.
1494      *
1495      * @param tag -- tag to set.
1496      */
setToTag(String tag)1497     public void setToTag(String tag) {
1498         try {
1499             toHeader.setTag(tag);
1500         } catch (ParseException e) {
1501         }
1502     }
1503 
1504     /**
1505      * Return the to tag.
1506      */
getToTag()1507     public String getToTag() {
1508         return toHeader == null ? null : toHeader.getTag();
1509     }
1510 
1511     /**
1512      * Return the encoded first line.
1513      */
getFirstLine()1514     public abstract String getFirstLine();
1515 
1516     /**
1517      * Add a SIP header.
1518      *
1519      * @param sipHeader -- sip header to add.
1520      */
addHeader(Header sipHeader)1521     public void addHeader(Header sipHeader) {
1522         // Content length is never stored. Just computed.
1523         SIPHeader sh = (SIPHeader) sipHeader;
1524         try {
1525             if ((sipHeader instanceof ViaHeader) || (sipHeader instanceof RecordRouteHeader)) {
1526                 attachHeader(sh, false, true);
1527             } else {
1528                 attachHeader(sh, false, false);
1529             }
1530         } catch (SIPDuplicateHeaderException ex) {
1531             try {
1532                 if (sipHeader instanceof ContentLength) {
1533                     ContentLength cl = (ContentLength) sipHeader;
1534                     contentLengthHeader.setContentLength(cl.getContentLength());
1535                 }
1536             } catch (InvalidArgumentException e) {
1537             }
1538         }
1539     }
1540 
1541     /**
1542      * Add a header to the unparsed list of headers.
1543      *
1544      * @param unparsed -- unparsed header to add to the list.
1545      */
addUnparsed(String unparsed)1546     public void addUnparsed(String unparsed) {
1547         this.unrecognizedHeaders.add(unparsed);
1548     }
1549 
1550     /**
1551      * Add a SIP header.
1552      *
1553      * @param sipHeader -- string version of SIP header to add.
1554      */
1555 
addHeader(String sipHeader)1556     public void addHeader(String sipHeader) {
1557         String hdrString = sipHeader.trim() + "\n";
1558         try {
1559             HeaderParser parser = ParserFactory.createParser(sipHeader);
1560             SIPHeader sh = parser.parse();
1561             this.attachHeader(sh, false);
1562         } catch (ParseException ex) {
1563             this.unrecognizedHeaders.add(hdrString);
1564         }
1565     }
1566 
1567     /**
1568      * Get a list containing the unrecognized headers.
1569      *
1570      * @return a linked list containing unrecongnized headers.
1571      */
getUnrecognizedHeaders()1572     public ListIterator<String> getUnrecognizedHeaders() {
1573         return this.unrecognizedHeaders.listIterator();
1574     }
1575 
1576     /**
1577      * Get the header names.
1578      *
1579      * @return a list iterator to a list of header names. These are ordered in the same order as
1580      *         are present in the message.
1581      */
getHeaderNames()1582     public ListIterator<String> getHeaderNames() {
1583         Iterator<SIPHeader> li = this.headers.iterator();
1584         LinkedList<String> retval = new LinkedList<String>();
1585         while (li.hasNext()) {
1586             SIPHeader sipHeader = (SIPHeader) li.next();
1587             String name = sipHeader.getName();
1588             retval.add(name);
1589         }
1590         return retval.listIterator();
1591     }
1592 
1593     /**
1594      * Compare for equality.
1595      *
1596      * @param other -- the other object to compare with.
1597      */
equals(Object other)1598     public boolean equals(Object other) {
1599         if (!other.getClass().equals(this.getClass())) {
1600             return false;
1601         }
1602         SIPMessage otherMessage = (SIPMessage) other;
1603         Collection<SIPHeader> values = this.nameTable.values();
1604         Iterator<SIPHeader> it = values.iterator();
1605         if (nameTable.size() != otherMessage.nameTable.size()) {
1606             return false;
1607         }
1608 
1609         while (it.hasNext()) {
1610             SIPHeader mine = (SIPHeader) it.next();
1611             SIPHeader his = (SIPHeader) (otherMessage.nameTable.get(SIPHeaderNamesCache
1612                     .toLowerCase(mine.getName())));
1613             if (his == null) {
1614                 return false;
1615             } else if (!his.equals(mine)) {
1616                 return false;
1617             }
1618         }
1619         return true;
1620     }
1621 
1622     /**
1623      * get content disposition header or null if no such header exists.
1624      *
1625      * @return the contentDisposition header
1626      */
getContentDisposition()1627     public javax.sip.header.ContentDispositionHeader getContentDisposition() {
1628         return (ContentDispositionHeader) getHeaderLowerCase(CONTENT_DISPOSITION_LOWERCASE);
1629     }
1630 
1631     private static final String CONTENT_DISPOSITION_LOWERCASE = SIPHeaderNamesCache
1632             .toLowerCase(ContentDispositionHeader.NAME);
1633 
1634     /**
1635      * get the content encoding header.
1636      *
1637      * @return the contentEncoding header.
1638      */
getContentEncoding()1639     public javax.sip.header.ContentEncodingHeader getContentEncoding() {
1640         return (ContentEncodingHeader) getHeaderLowerCase(CONTENT_ENCODING_LOWERCASE);
1641     }
1642 
1643     private static final String CONTENT_ENCODING_LOWERCASE = SIPHeaderNamesCache
1644             .toLowerCase(ContentEncodingHeader.NAME);
1645 
1646     /**
1647      * Get the contentLanguage header.
1648      *
1649      * @return the content language header.
1650      */
getContentLanguage()1651     public javax.sip.header.ContentLanguageHeader getContentLanguage() {
1652         return (ContentLanguageHeader) getHeaderLowerCase(CONTENT_LANGUAGE_LOWERCASE);
1653     }
1654 
1655     private static final String CONTENT_LANGUAGE_LOWERCASE = SIPHeaderNamesCache
1656             .toLowerCase(ContentLanguageHeader.NAME);
1657 
1658     /**
1659      * Get the exipres header.
1660      *
1661      * @return the expires header or null if one does not exist.
1662      */
getExpires()1663     public javax.sip.header.ExpiresHeader getExpires() {
1664         return (ExpiresHeader) getHeaderLowerCase(EXPIRES_LOWERCASE);
1665     }
1666 
1667     private static final String EXPIRES_LOWERCASE = SIPHeaderNamesCache
1668             .toLowerCase(ExpiresHeader.NAME);
1669 
1670     /**
1671      * Set the expiresHeader
1672      *
1673      * @param expiresHeader -- the expires header to set.
1674      */
1675 
setExpires(ExpiresHeader expiresHeader)1676     public void setExpires(ExpiresHeader expiresHeader) {
1677         this.setHeader(expiresHeader);
1678     }
1679 
1680     /**
1681      * Set the content disposition header.
1682      *
1683      * @param contentDispositionHeader -- content disposition header.
1684      */
1685 
setContentDisposition(ContentDispositionHeader contentDispositionHeader)1686     public void setContentDisposition(ContentDispositionHeader contentDispositionHeader) {
1687         this.setHeader(contentDispositionHeader);
1688 
1689     }
1690 
setContentEncoding(ContentEncodingHeader contentEncodingHeader)1691     public void setContentEncoding(ContentEncodingHeader contentEncodingHeader) {
1692         this.setHeader(contentEncodingHeader);
1693 
1694     }
1695 
setContentLanguage(ContentLanguageHeader contentLanguageHeader)1696     public void setContentLanguage(ContentLanguageHeader contentLanguageHeader) {
1697         this.setHeader(contentLanguageHeader);
1698     }
1699 
1700     /**
1701      * Set the content length header.
1702      *
1703      * @param contentLength -- content length header.
1704      */
setContentLength(ContentLengthHeader contentLength)1705     public void setContentLength(ContentLengthHeader contentLength) {
1706         try {
1707             this.contentLengthHeader.setContentLength(contentLength.getContentLength());
1708         } catch (InvalidArgumentException ex) {
1709         }
1710 
1711     }
1712 
1713     /**
1714      * Set the size of all the headers. This is for book keeping. Called by the parser.
1715      *
1716      * @param size -- size of the headers.
1717      */
setSize(int size)1718     public void setSize(int size) {
1719         this.size = size;
1720     }
1721 
getSize()1722     public int getSize() {
1723         return this.size;
1724     }
1725 
1726     /*
1727      * (non-Javadoc)
1728      *
1729      * @see javax.sip.message.Message#addLast(javax.sip.header.Header)
1730      */
addLast(Header header)1731     public void addLast(Header header) throws SipException, NullPointerException {
1732         if (header == null)
1733             throw new NullPointerException("null arg!");
1734 
1735         try {
1736             this.attachHeader((SIPHeader) header, false, false);
1737         } catch (SIPDuplicateHeaderException ex) {
1738             throw new SipException("Cannot add header - header already exists");
1739         }
1740 
1741     }
1742 
1743     /*
1744      * (non-Javadoc)
1745      *
1746      * @see javax.sip.message.Message#addFirst(javax.sip.header.Header)
1747      */
addFirst(Header header)1748     public void addFirst(Header header) throws SipException, NullPointerException {
1749 
1750         if (header == null)
1751             throw new NullPointerException("null arg!");
1752 
1753         try {
1754             this.attachHeader((SIPHeader) header, false, true);
1755         } catch (SIPDuplicateHeaderException ex) {
1756             throw new SipException("Cannot add header - header already exists");
1757         }
1758 
1759     }
1760 
1761     /*
1762      * (non-Javadoc)
1763      *
1764      * @see javax.sip.message.Message#removeFirst(java.lang.String)
1765      */
removeFirst(String headerName)1766     public void removeFirst(String headerName) throws NullPointerException {
1767         if (headerName == null)
1768             throw new NullPointerException("Null argument Provided!");
1769         this.removeHeader(headerName, true);
1770 
1771     }
1772 
1773     /*
1774      * (non-Javadoc)
1775      *
1776      * @see javax.sip.message.Message#removeLast(java.lang.String)
1777      */
removeLast(String headerName)1778     public void removeLast(String headerName) {
1779         if (headerName == null)
1780             throw new NullPointerException("Null argument Provided!");
1781         this.removeHeader(headerName, false);
1782 
1783     }
1784 
1785     /**
1786      * Set the CSeq header.
1787      *
1788      * @param cseqHeader -- CSeq Header.
1789      */
1790 
setCSeq(CSeqHeader cseqHeader)1791     public void setCSeq(CSeqHeader cseqHeader) {
1792         this.setHeader(cseqHeader);
1793     }
1794 
1795     /**
1796      * Set the application data pointer. This method is not used the stack. It is provided as a
1797      * convenient way of storing book-keeping data for applications. Note that null clears the
1798      * application data pointer (releases it).
1799      *
1800      * @param applicationData -- application data pointer to set. null clears the application data
1801      *        pointer.
1802      */
setApplicationData(Object applicationData)1803     public void setApplicationData(Object applicationData) {
1804         this.applicationData = applicationData;
1805     }
1806 
1807     /**
1808      * Get the application data associated with this message.
1809      *
1810      * @return stored application data.
1811      */
getApplicationData()1812     public Object getApplicationData() {
1813         return this.applicationData;
1814     }
1815 
1816     /**
1817      * Get the multipart MIME content
1818      *
1819      */
getMultipartMimeContent()1820     public MultipartMimeContent getMultipartMimeContent() throws ParseException {
1821         if (this.contentLengthHeader.getContentLength() == 0) {
1822             return null;
1823         }
1824         MultipartMimeContentImpl retval = new MultipartMimeContentImpl(this
1825                 .getContentTypeHeader());
1826         byte[] rawContent = getRawContent();
1827 		try {
1828 			String body = new String( rawContent, getCharset() );
1829 	        retval.createContentList(body);
1830 	        return retval;
1831 		} catch (UnsupportedEncodingException e) {
1832 			InternalErrorHandler.handleException(e);
1833 			return null;
1834 		}
1835     }
1836 
getCallIdHeader()1837     public CallIdHeader getCallIdHeader() {
1838         return this.callIdHeader;
1839     }
1840 
1841 
getFromHeader()1842     public FromHeader getFromHeader() {
1843         return this.fromHeader;
1844     }
1845 
1846 
getToHeader()1847     public ToHeader getToHeader() {
1848         return this.toHeader;
1849     }
1850 
1851 
getTopmostViaHeader()1852     public ViaHeader getTopmostViaHeader() {
1853         return this.getTopmostVia();
1854     }
1855 
getCSeqHeader()1856     public CSeqHeader getCSeqHeader() {
1857         return this.cSeqHeader;
1858     }
1859 
1860     /**
1861      * Returns the charset to use for encoding/decoding the body of this message
1862      */
getCharset()1863     protected final String getCharset() {
1864     	ContentType ct = getContentTypeHeader();
1865     	if (ct!=null) {
1866     		String c = ct.getCharset();
1867     		return c!=null ? c : contentEncodingCharset;
1868     	} else return contentEncodingCharset;
1869     }
1870 
1871     /**
1872      * Return true if this is a null request (i.e. does not have a request line ).
1873      *
1874      * @return true if null request.
1875      */
isNullRequest()1876     public boolean isNullRequest() {
1877         return  this.nullRequest;
1878     }
1879 
1880     /**
1881      * Set a flag to indiate this is a special message ( encoded with CRLFCRLF ).
1882      *
1883      */
setNullRequest()1884     public void setNullRequest() {
1885         this.nullRequest = true;
1886     }
1887 
1888 
setSIPVersion(String sipVersion)1889     public abstract void setSIPVersion(String sipVersion) throws ParseException;
1890 
getSIPVersion()1891     public abstract String getSIPVersion();
1892 
toString()1893     public abstract String toString();
1894 
1895 }
1896