/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This file is part of MjSip (http://www.mjsip.org)
 * 
 * MjSip is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * MjSip is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MjSip; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package org.zoolu.sip.message;


import org.zoolu.sip.provider.*;
import org.zoolu.sip.header.*;
import org.zoolu.sip.address.*;
import org.zoolu.sip.message.SipMethods;
import org.zoolu.net.UdpPacket;
import java.util.*;


/** Class BaseMessage implements a generic SIP Message. */
public abstract class BaseMessage
{
   /** UDP */
   public static final String PROTO_UDP="udp"; 
   /** TCP */
   public static final String PROTO_TCP="tcp"; 
   /** TLS */
   public static final String PROTO_TLS="tls"; 
   /** SCTP */
   public static final String PROTO_SCTP="sctp"; 

   /** Maximum receiving packet size */
   protected static int MAX_PKT_SIZE=8000; 
   
   /** The remote ip address */
   protected String remote_addr;

   /** The remote port */
   protected int remote_port;
   
   /** Transport protocol */
   protected String transport_proto;

   /** Connection identifier */
   protected ConnectionIdentifier connection_id;

   /** Packet length */
   //protected int packet_length;   

   /** The message string */
   private String message;


   /** Inits empty Message */
   private void init()
   {  //message="";
      remote_addr=null;
      remote_port=0;
      transport_proto=null;
      connection_id=null;
   }

   /** Costructs a new empty Message */
   public BaseMessage()
   {  init();
      message="";
   }

   /** Costructs a new Message */
   public BaseMessage(byte[] data, int offset, int len)
   {  init();
      message=new String(data,offset,len);
   }
   
   /** Costructs a new Message */
   public BaseMessage(UdpPacket packet)
   {  init();
      message=new String(packet.getData(),packet.getOffset(),packet.getLength());
   }

   /** Costructs a new Message */
   public BaseMessage(String str)
   {  init();
      message=new String(str);
   }

   /** Costructs a new Message */
   public BaseMessage(BaseMessage msg)
   {  //message=new String(msg.message);
      message=msg.message;
      remote_addr=msg.remote_addr;
      remote_port=msg.remote_port;
      transport_proto=msg.transport_proto;
      connection_id=msg.connection_id;
      //packet_length=msg.packet_length;
   }
   
   /** Creates and returns a clone of the Message */
   abstract public Object clone();
   //{  return new Message(message);
   //}

   /** Sets the entire message */
   public void setMessage(String message)
   {  this.message=message;
   }   

   /** Gets string representation of Message */
   public String toString()
   {  return message;
   }
   
   /** Gets remote ip address */
   public String getRemoteAddress()
   {  return remote_addr;
   }

   /** Gets remote port */
   public int getRemotePort()
   {  return remote_port;
   }   

   /** Gets transport protocol */
   public String getTransportProtocol()
   {  return transport_proto;
   }

   /** Gets connection identifier */
   public ConnectionIdentifier getConnectionId()
   {  return connection_id;
   }

   /** Gets message length */
   public int getLength()
   {  return message.length();
   }   

   /** Sets remote ip address */
   public void setRemoteAddress(String addr)
   {  remote_addr=addr;
   }

   /** Sets remote port */
   public void setRemotePort(int port)
   {  remote_port=port;
   }   

   /** Sets transport protocol */
   public void setTransport(String proto)
   {  transport_proto=proto;
   }

   /** Sets connection identifier */
   public void setConnectionId(ConnectionIdentifier conn_id)
   {  connection_id=conn_id;
   }
   
   /** Gets the inique DialogIdentifier for an INCOMING message */
   public DialogIdentifier getDialogId()
   {  String call_id=getCallIdHeader().getCallId();
      String local_tag, remote_tag;
      if (isRequest()) {  local_tag=getToHeader().getTag(); remote_tag=getFromHeader().getTag(); }
      else {  local_tag=getFromHeader().getTag(); remote_tag=getToHeader().getTag(); }
      return new DialogIdentifier(call_id,local_tag,remote_tag);
   }


   /** Gets the inique TransactionIdentifier */
   public TransactionIdentifier getTransactionId()
   {  String call_id=getCallIdHeader().getCallId();
      ViaHeader top_via=getViaHeader();
      String branch=null;
      if (top_via.hasBranch()) branch=top_via.getBranch();
      String sent_by=top_via.getSentBy();
      CSeqHeader cseqh=getCSeqHeader();
      long seqn=cseqh.getSequenceNumber();      
      String method=cseqh.getMethod();
      return new TransactionIdentifier(call_id,seqn,method,sent_by,branch);
   }


   /** Gets the MethodIdentifier */
   public MethodIdentifier getMethodId()
   {  String method=getCSeqHeader().getMethod();
      return new MethodIdentifier(method);
   }


   //**************************** Requests ****************************/

   /** Whether Message is a Request */
   public boolean isRequest() throws NullPointerException
   {  // Req-Line = Method ' ' SIP-URL ' ' "SIP/2.0" CRLF
      if (message==null || isResponse()) return false;
      String firstline=(new SipParser(message)).getLine();
      String version=(new SipParser(firstline)).skipString().skipString().getString();
      if (version==null || version.length()<4) return false;
      version=version.substring(0,4);     
      String target="SIP/"; 
      //if (version.compareToIgnoreCase(target)==0) return true; 
      if (version.equalsIgnoreCase(target)) return true;
      return false;
   }
   
   /** Whether Message is a <i>method</i> request */
   public boolean isRequest(String method)
   {  //if (message==null) return false;
      if (message.startsWith(method)) return true; else return false;
   }
   
   /** Whether Message is a Method that creates a dialog */
   public boolean createsDialog()
   {  if (!isRequest()) return false;
      //else
      String method=getRequestLine().getMethod();
      for (int i=0; i<SipMethods.dialog_methods.length; i++)
         if (method.equalsIgnoreCase(SipMethods.dialog_methods[i])) return true;
      //else
      return false;
   }

   /** Whether Message is an Invite */
   public boolean isInvite()
   {  return isRequest(SipMethods.INVITE);
   }

   /** Whether Message is a Register */
   public boolean isRegister()
   {  return isRequest(SipMethods.REGISTER);
   }

   /** Whether Message is a Cancel */
   public boolean isCancel()
   {  return isRequest(SipMethods.CANCEL);
   }

   /** Whether Message is a Bye */
   public boolean isBye()
   {  return isRequest(SipMethods.BYE);
   }
   
   /** Whether Message is an Ack */
   public boolean isAck()
   {  return isRequest(SipMethods.ACK);
   }

   /** Whether Message is an Info */
   public boolean isInfo()
   {  return isRequest(SipMethods.INFO);
   }

   /** Whether Message is an Option */
   public boolean isOption()
   {  return isRequest(SipMethods.OPTION);
   }

   /** Whether Message has Request-line */
   protected boolean hasRequestLine()
   {  return isRequest();
   }

   /** Gets RequestLine in Message (Returns null if called for no request message) */
   public RequestLine getRequestLine()
   {  if (!isRequest())
      {  //printWarning("getRequestLine(): called for no request message\n",1);
         return null;
      }
      SipParser par=new SipParser(message);
      String method=par.getString();
      par.skipWSP();
      par=new SipParser(par.subParser(par.indexOfEOH()-par.getPos()));
      return new RequestLine(method,par.getSipURL());
   }

   /** Sets RequestLine of the Message */
   public void setRequestLine(RequestLine rl)
   {  if (hasRequestLine()) removeRequestLine();
      String value=rl.toString();
      message=value+message;
   }   
   
   /** Removes RequestLine of the Message */
   public void removeRequestLine()
   {  if(!isRequest()) return;
      removeFirstLine();
   } 


   //**************************** Responses ****************************/

   /** Whether Message is a Response */
   public boolean isResponse() throws NullPointerException
   {  // Status-Line = "SIP/2.0" ' ' Status-Code ' 'Reason-Phrase" CRLF
      //if (message==null) return false;
      if (message==null || message.length()<4) return false;
      String version=message.substring(0,4);     
      String target="SIP/"; 
      //if (version.compareToIgnoreCase(target)==0) return true; 
      if (version.equalsIgnoreCase(target)) return true; 

      return false;     
   }
   
   /** Whether Message has Status-line */
   protected boolean hasStatusLine()
   {  return isResponse();
   }

   /** Gets StautsLine in Message (Returns null if called for no response message) */
   public StatusLine getStatusLine()
   {  if (!isResponse())
      {  //printWarning("getStatusLine(): called for no response message\n",1);
         return null;
      }
      SipParser par=new SipParser(message);
      par.skipString().skipWSP(); // "SIP/2.0 "
      int code=par.getInt();
      int begin=par.getPos();
      int end=par.indexOfEOH();
      String reason=message.substring(begin,end).trim();
      return new StatusLine(code,reason);
   }

   /** Sets StatusLine of the Message */
   public void setStatusLine(StatusLine sl)
   {  if (hasStatusLine()) removeStatusLine();
      message=sl.toString()+message;
   }      
   
   /** Removes StatusLine of the Message */
   public void removeStatusLine()
   {  if(!isResponse()) return;
      removeFirstLine();
   } 


   //**************************** Generic Headers ****************************/

   /** Returns the transaction method */
   public String getTransactionMethod()
   {  return getCSeqHeader().getMethod();
   } 
 
   /** Gets the first line of the Message */
   public String getFirstLine()
   {  if (isRequest()) return getRequestLine().toString();
      if (isResponse()) return getStatusLine().toString();
      return null;
   }  

   /** Sets Request/Status Line of the Message */
   /*private void setFirstLine(String value)
   {  message=value+" \r\n"+message;
   } */  
   
   /** Removes Request\Status Line of the Message */
   protected void removeFirstLine()
   {  message=message.substring((new SipParser(message)).indexOfNextHeader());
   }
     
   /** Whether Message has any headers of specified name */   
   public boolean hasHeader(String name)
   {  Header hd=getHeader(name);
      if (hd==null) return false;
      else return true;
   }
   
   /** Gets the first Header of specified name (Returns null if no Header is found) */
   public Header getHeader(String hname)
   {  SipParser par=new SipParser(message);
      return par.getHeader(hname);
   }

   /** Gets a Vector of all Headers of specified name (Returns empty Vector if no Header is found) */
   public Vector getHeaders(String hname)
   {  Vector v=new Vector();
      SipParser par=new SipParser(message);
      Header h;
      while ((h=par.getHeader(hname))!=null)
      {  v.addElement(h);
      }
      return v; 
   }

   /** Adds Header at the top/bottom.
     * The bottom is considered before the Content-Length and Content-Type headers */
   public void addHeader(Header header, boolean top) 
   {  addHeaders(header.toString(),top);
   }

   /** Adds a Vector of Headers at the top/bottom */
   public void addHeaders(Vector headers, boolean top) 
   {  String str="";
      for (int i=0; i<headers.size(); i++) str+=((Header)headers.elementAt(i)).toString();
      addHeaders(str,top);
   }

   /** Adds MultipleHeader(s) <i>mheader</i> at the top/bottom */
   public void addHeaders(MultipleHeader mheader, boolean top) 
   {  addHeaders(mheader.toString(),top);
   }

   /** Adds a one or more Headers at the top/bottom.
     * The bottom is considered before the Content-Length and Content-Type headers */
   protected void addHeaders(String str, boolean top) 
   {  int i,aux;
      if (top)
      {  if (this.hasRequestLine() || this.hasStatusLine())
         {  SipParser par=new SipParser(message);
            par.goToNextHeader();
            i=par.getPos();
         }
         else i=0;   
      }
      else
      {  SipParser par=new SipParser(message);
         // index the end of headers
         i=par.goToEndOfLastHeader().goToNextLine().getPos();         
         par=new SipParser(message);
         // if Content_Length is present, jump before
         aux=par.indexOfHeader(SipHeaders.Content_Length);
         if (aux<i) i=aux;
         // if Content_Type is present, jump before
         aux=par.indexOfHeader(SipHeaders.Content_Type);
         if (aux<i) i=aux; 
      }            
      String head=message.substring(0,i);
      String tail=message.substring(i);
      String new_message=head.concat(str);
      new_message=new_message.concat(tail);
      message=new_message;
   }

   /** Adds Headers on position <i>index</i> within the Message */
   protected void addHeaders(String str, int index) 
   {  if (index>message.length()) index=message.length();
      message=message.substring(0,index)+str+message.substring(index);
   }

   /** Adds Header before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, it is added at top */
   public void addHeaderBefore(Header new_header, String refer_header) 
   {  addHeadersBefore(new_header.toString(),refer_header);
   }

   /** Adds MultipleHeader(s) before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at top */
   public void addHeadersBefore(MultipleHeader mheader, String refer_header) 
   {  addHeadersBefore(mheader.toString(),refer_header);
   }

   /** Adds Headers before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at top */
   protected void addHeadersBefore(String str, String refer_header) 
   {  if (!hasHeader(refer_header)) addHeaders(str,true);
      else
      {  SipParser par=new SipParser(message);
         par.goTo(refer_header);
         int here=par.getPos();
         message=message.substring(0,here)+str+message.substring(here);
      }
   }

   /** Adds Header after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, it is added at bottom */
   public void addHeaderAfter(Header new_header, String refer_header) 
   {  addHeadersAfter(new_header.toString(),refer_header);
   }

   /** Adds MultipleHeader(s) after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at bottom */
   public void addHeadersAfter(MultipleHeader mheader, String refer_header) 
   {  addHeadersAfter(mheader.toString(),refer_header);
   }

   /** Adds Headers after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at bottom */
   protected void addHeadersAfter(String str, String refer_header) 
   {  if (!hasHeader(refer_header)) addHeaders(str,false);
      else
      {  SipParser par=new SipParser(message);
         par.goTo(refer_header);
         int here=par.indexOfNextHeader();
         message=message.substring(0,here)+str+message.substring(here);
      }
   }

   /** Removes first Header of specified name */
   public void removeHeader(String hname)
   {  removeHeader(hname,true);
   }

   /** Removes first (or last) Header of specified name */
   public void removeHeader(String hname, boolean first)
   {  String[] target={'\n'+hname, '\r'+hname};
      SipParser par=new SipParser(message);
      par.goTo(target);
      if (!par.hasMore()) return;
      if (!first)
         while(true)
         {  int next=par.indexOf(target);
            if (next<0) break;
            par.setPos(next);
         }
      par.skipChar();
      String head=message.substring(0,par.getPos());
      par.goToNextHeader();
      String tail=message.substring(par.getPos());
      message=head.concat(tail);
   }
   
   /** Sets the new Header (removing any previous headers of the same name) */
   /*public void setHeader(Header hd) 
   {  if (hasHeader(hd.getName())) removeAllHeaders(hd.getName());
      addHeader(hd,false);               
   }*/          

   /** Sets the Header <i>hd</i> removing any previous headers of the same type */
   public void setHeader(Header hd) 
   {  String hname=hd.getName();
      if (hasHeader(hname))
      {  int index=(new SipParser(message)).indexOfHeader(hname);
         removeAllHeaders(hname);
         addHeaders(hd.toString(),index);
      }
      else
         addHeader(hd,false);               
   }          

   /** Removes all Headers of specified name */
   public void removeAllHeaders(String hname) 
   {  String[] target={'\n'+hname, '\r'+hname};
      SipParser par=new SipParser(message);
      par.goTo(target);
      while (par.hasMore())
      {  par.skipChar();
         String head=message.substring(0,par.getPos());
         String tail=message.substring(par.indexOfNextHeader());
         message=head.concat(tail);
         par=new SipParser(message,par.getPos()-1);
         par.goTo(target);
      }         
   }
   
   /** Sets MultipleHeader <i>mheader</i> */
   /*public void setHeaders(MultipleHeader mheader) 
   {  if (hasHeader(mheader.getName())) removeAllHeaders(mheader.getName());
      addHeaders(mheader,false);            
   }*/          

   /** Sets MultipleHeader <i>mheader</i> */
   public void setHeaders(MultipleHeader mheader) 
   {  String hname=mheader.getName();
      if (hasHeader(hname))
      {  int index=(new SipParser(message)).indexOfHeader(hname);
         removeAllHeaders(hname);
         addHeaders(mheader.toString(),index);
      }
      else
         addHeaders(mheader,false);            
   }          


   //**************************** Specific Headers ****************************/

   /** Whether Message has MaxForwardsHeader */
   public boolean hasMaxForwardsHeader()
   {  return hasHeader(SipHeaders.Max_Forwards);
   }  
   /** Gets MaxForwardsHeader of Message */
   public MaxForwardsHeader getMaxForwardsHeader()
   {  Header h=getHeader(SipHeaders.Max_Forwards);
      if (h==null) return null;
      else return new MaxForwardsHeader(h);
   } 
   /** Sets MaxForwardsHeader of Message */
   public void setMaxForwardsHeader(MaxForwardsHeader mfh)
   {  setHeader(mfh);
   }  
   /** Removes MaxForwardsHeader from Message */
   public void removeMaxForwardsHeader() 
   {  removeHeader(SipHeaders.Max_Forwards);
   }

     
   /** Whether Message has FromHeader */
   public boolean hasFromHeader()
   {  return hasHeader(SipHeaders.From);
   }  
   /** Gets FromHeader of Message */
   public FromHeader getFromHeader()
   {  Header h=getHeader(SipHeaders.From);
      if (h==null) return null;
      else return new FromHeader(h);
   } 
   /** Sets FromHeader of Message */
   public void setFromHeader(FromHeader fh) 
   {  setHeader(fh);
   }  
   /** Removes FromHeader from Message */
   public void removeFromHeader() 
   {  removeHeader(SipHeaders.From);
   }

   /** Whether Message has ToHeader */
   public boolean hasToHeader()
   {  return hasHeader(SipHeaders.To);
   } 
   /** Gets ToHeader of Message */
   public ToHeader getToHeader()
   {  Header h=getHeader(SipHeaders.To);
      if (h==null) return null;
      else return new ToHeader(h);
   } 
   /** Sets ToHeader of Message */
   public void setToHeader(ToHeader th) 
   {  setHeader(th);
   } 
   /** Removes ToHeader from Message */
   public void removeToHeader() 
   {  removeHeader(SipHeaders.To);
   }

   
   /** Whether Message has ContactHeader */
   public boolean hasContactHeader()
   {  return hasHeader(SipHeaders.Contact);
   }
   /** <b>Deprecated</b>. Gets ContactHeader of Message. Use getContacts instead. */  
   public ContactHeader getContactHeader()
   {  //Header h=getHeader(SipHeaders.Contact);
      //if (h==null) return null; else return new ContactHeader(h);
      MultipleHeader mh=getContacts();
      if (mh==null) return null; return new ContactHeader(mh.getTop());
   } 
   /** Adds ContactHeader */
   public void addContactHeader(ContactHeader ch, boolean top) 
   {  addHeader(ch,top);
   }   
   /** Sets ContactHeader */
   public void setContactHeader(ContactHeader ch) 
   {  if (hasContactHeader()) removeContacts();
      addHeader(ch,false);
   }
   /** Gets a MultipleHeader of Contacts */
   public MultipleHeader getContacts()
   {  Vector v=getHeaders(SipHeaders.Contact);
      if (v.size()>0) return new MultipleHeader(v);
      else return null;
   }   
   /** Adds Contacts */
   public void addContacts(MultipleHeader contacts, boolean top) 
   {  addHeaders(contacts,top);
   }   
   /** Sets Contacts */
   public void setContacts(MultipleHeader contacts) 
   {  if (hasContactHeader()) removeContacts();
      addContacts(contacts,false);
   }    
   /** Removes ContactHeaders from Message */
   public void removeContacts() 
   {  removeAllHeaders(SipHeaders.Contact);
   }
 
   
   /** Whether Message has ViaHeaders */
   public boolean hasViaHeader()
   {  return hasHeader(SipHeaders.Via);
   }      
   /** Adds ViaHeader at the top */
   public void addViaHeader(ViaHeader vh) 
   {  addHeader(vh,true);
   } 
   /** Gets the first ViaHeader */
   public ViaHeader getViaHeader()
   {  //Header h=getHeader(SipHeaders.Via);
      //if (h==null) return null; else return new ViaHeader(h);
      MultipleHeader mh=getVias();
      if (mh==null) return null; return new ViaHeader(mh.getTop());
   } 
   /** Removes the top ViaHeader */
   public void removeViaHeader() 
   {  //removeHeader(SipHeaders.Via);
      MultipleHeader mh=getVias();
      mh.removeTop();
      setVias(mh);
   }  
   /** Gets all Vias */
   public MultipleHeader getVias()
   {  Vector v=getHeaders(SipHeaders.Via);
      if (v.size()>0) return new MultipleHeader(v);
      else return null;
   }
   /** Adds Vias */
   public void addVias(MultipleHeader vias, boolean top) 
   {  addHeaders(vias,top);
   }   
   /** Sets Vias */
   public void setVias(MultipleHeader vias) 
   {  if (hasViaHeader()) removeVias();
      addContacts(vias,true);
   }    
   /** Removes ViaHeaders from Message (if any exists) */
   public void removeVias() 
   {  removeAllHeaders(SipHeaders.Via);
   }
   
      
   /** Whether Message has RouteHeader */
   public boolean hasRouteHeader()
   {  return hasHeader(SipHeaders.Route);
   }      
   /** Adds RouteHeader at the top */
   public void addRouteHeader(RouteHeader h) 
   {  addHeaderAfter(h,SipHeaders.Via);
   } 
   /** Adds multiple Route headers at the top */
   public void addRoutes(MultipleHeader routes) 
   {  addHeadersAfter(routes,SipHeaders.Via);
   } 
   /** Gets the top RouteHeader */
   public RouteHeader getRouteHeader()
   {  //Header h=getHeader(SipHeaders.Route);
      //if (h==null) return null; else return new RouteHeader(h);
      MultipleHeader mh=getRoutes();
      if (mh==null) return null; return new RouteHeader(mh.getTop());
   } 
   /** Gets the whole route */
   public MultipleHeader getRoutes()
   {  Vector v=getHeaders(SipHeaders.Route);
      if (v.size()>0) return new MultipleHeader(v);
      else return null;
   }
   /** Removes the top RouteHeader */
   public void removeRouteHeader() 
   {  //removeHeader(SipHeaders.Route);
      MultipleHeader mh=getRoutes();
      mh.removeTop();
      setRoutes(mh);
   }  
   /** Removes all RouteHeaders from Message (if any exists) */
   public void removeRoutes() 
   {  removeAllHeaders(SipHeaders.Route);
   }
   /** Sets the whole route */
   public void setRoutes(MultipleHeader routes) 
   {  if (hasRouteHeader()) removeRoutes();
      addRoutes(routes);
   }    
   

   /** Whether Message has RecordRouteHeader */
   public boolean hasRecordRouteHeader()
   {  return hasHeader(SipHeaders.Record_Route);
   }      
   /** Adds RecordRouteHeader at the top */
   public void addRecordRouteHeader(RecordRouteHeader rr) 
   {  //addHeaderAfter(rr,SipHeaders.Via);
      addHeaderAfter(rr,SipHeaders.CSeq);
   } 
   /** Adds multiple RecordRoute headers at the top */
   public void addRecordRoutes(MultipleHeader routes) 
   {  //addHeadersAfter(routes,SipHeaders.Via);
      addHeadersAfter(routes,SipHeaders.CSeq);
   } 
   /** Gets the top RecordRouteHeader */
   public RecordRouteHeader getRecordRouteHeader()
   {  //Header h=getHeader(SipHeaders.Record_Route);
      //if (h==null) return null; else return new RecordRouteHeader(h);
      MultipleHeader mh=getRecordRoutes();
      if (mh==null) return null; return new RecordRouteHeader(mh.getTop());
   } 
   /** Gets the whole RecordRoute headers */
   public MultipleHeader getRecordRoutes()
   {  Vector v=getHeaders(SipHeaders.Record_Route);
      if (v.size()>0) return new MultipleHeader(v);
      else return null;
   }
   /** Removes the top RecordRouteHeader */
   public void removeRecordRouteHeader() 
   {  //removeHeader(SipHeaders.Record_Route);
      MultipleHeader mh=getRecordRoutes();
      mh.removeTop();
      setRecordRoutes(mh);
   }  
   /** Removes all RecordRouteHeader from Message (if any exists) */
   public void removeRecordRoutes() 
   {  removeAllHeaders(SipHeaders.Record_Route);
   }
   /** Sets the whole RecordRoute headers */
   public void setRecordRoutes(MultipleHeader routes) 
   {  if (hasRecordRouteHeader()) removeRecordRoutes();
      addRecordRoutes(routes);
   }


   /** Whether Message has CSeqHeader */
   public boolean hasCSeqHeader()
   {  return hasHeader(SipHeaders.CSeq);
   }  
   /** Gets CSeqHeader of Message */
   public CSeqHeader getCSeqHeader()
   {  Header h=getHeader(SipHeaders.CSeq);
      if (h==null) return null;
      else return new CSeqHeader(h);
   } 
   /** Sets CSeqHeader of Message */
   public void setCSeqHeader(CSeqHeader csh) 
   {  setHeader(csh);
   } 
   /** Removes CSeqHeader from Message */
   public void removeCSeqHeader() 
   {  removeHeader(SipHeaders.CSeq);
   }
   
      
   /** Whether has CallIdHeader */
   public boolean hasCallIdHeader()
   {  return hasHeader(SipHeaders.Call_ID);
   }
   /** Sets CallIdHeader of Message */
   public void setCallIdHeader(CallIdHeader cih) 
   {  setHeader(cih);
   } 
   /** Gets CallIdHeader of Message */
   public CallIdHeader getCallIdHeader()
   {  Header h=getHeader(SipHeaders.Call_ID);
      if (h==null) return null;
      else return new CallIdHeader(h);
   } 
   /** Removes CallIdHeader from Message */
   public void removeCallIdHeader() 
   {  removeHeader(SipHeaders.Call_ID);
   }


   /** Whether Message has SubjectHeader */
   public boolean hasSubjectHeader()
   {  return hasHeader(SipHeaders.Subject);
   }
   /** Sets SubjectHeader of Message */
   public void setSubjectHeader(SubjectHeader sh) 
   {  setHeader(sh);
   } 
   /** Gets SubjectHeader of Message */
   public SubjectHeader getSubjectHeader()
   {  Header h=getHeader(SipHeaders.Subject);
      if (h==null) return null;
      else return new SubjectHeader(h);
   } 
   /** Removes SubjectHeader from Message */
   public void removeSubjectHeader() 
   {  removeHeader(SipHeaders.Subject);
   }

   
   /** Whether Message has DateHeader */   
   public boolean hasDateHeader()
   {  return hasHeader(SipHeaders.Date);
   }  
   /** Gets DateHeader of Message */
   public DateHeader getDateHeader()
   {  Header h=getHeader(SipHeaders.Date);
      if (h==null) return null;
      else return new DateHeader(h);
   } 
   /** Sets DateHeader of Message */
   public void setDateHeader(DateHeader dh) 
   {  setHeader(dh);
   }  
   /** Removes DateHeader from Message (if it exists) */
   public void removeDateHeader() 
   {  removeHeader(SipHeaders.Date);
   }

   
   /** Whether has UserAgentHeader */
   public boolean hasUserAgentHeader()
   {  return hasHeader(SipHeaders.User_Agent);
   }
   /** Sets UserAgentHeader */
   public void setUserAgentHeader(UserAgentHeader h) 
   {  setHeader(h);
   } 
   /** Gets UserAgentHeader */
   public UserAgentHeader getUserAgentHeader()
   {  Header h=getHeader(SipHeaders.User_Agent);
      if (h==null) return null;
      else return new UserAgentHeader(h);
   } 
   /** Removes UserAgentHeader */
   public void removeUserAgentHeader() 
   {  removeHeader(SipHeaders.User_Agent);
   }


   /** Whether has ServerHeader */
   public boolean hasServerHeader()
   {  return hasHeader(SipHeaders.Server);
   }
   /** Sets ServerHeader */
   public void setServerHeader(ServerHeader h) 
   {  setHeader(h);
   } 
   /** Gets ServerHeader */
   public ServerHeader getServerHeader()
   {  Header h=getHeader(SipHeaders.Server);
      if (h==null) return null;
      else return new ServerHeader(h);
   } 
   /** Removes ServerHeader */
   public void removeServerHeader() 
   {  removeHeader(SipHeaders.Server);
   }


   /** Whether has AcceptHeader */
   public boolean hasAcceptHeader()
   {  return hasHeader(SipHeaders.Accept);
   }
   /** Sets AcceptHeader */
   public void setAcceptHeader(AcceptHeader h) 
   {  setHeader(h);
   } 
   /** Gets AcceptHeader */
   public AcceptHeader getAcceptHeader()
   {  Header h=getHeader(SipHeaders.Accept);
      if (h==null) return null;
      else return new AcceptHeader(h);
   } 
   /** Removes AcceptHeader */
   public void removeAcceptHeader() 
   {  removeHeader(SipHeaders.Accept);
   }


   /** Whether has AlertInfoHeader */
   public boolean hasAlertInfoHeader()
   {  return hasHeader(SipHeaders.Alert_Info);
   }
   /** Sets AlertInfoHeader */
   public void setAlertInfoHeader(AlertInfoHeader h) 
   {  setHeader(h);
   } 
   /** Gets AlertInfoHeader */
   public AlertInfoHeader getAlertInfoHeader()
   {  Header h=getHeader(SipHeaders.Alert_Info);
      if (h==null) return null;
      else return new AlertInfoHeader(h);
   } 
   /** Removes AlertInfoHeader */
   public void removeAlertInfoHeader() 
   {  removeHeader(SipHeaders.Alert_Info);
   }


   /** Whether has AllowHeader */
   public boolean hasAllowHeader()
   {  return hasHeader(SipHeaders.Allow);
   }
   /** Sets AllowHeader */
   public void setAllowHeader(AllowHeader h) 
   {  setHeader(h);
   } 
   /** Gets AllowHeader */
   public AllowHeader getAllowHeader()
   {  Header h=getHeader(SipHeaders.Allow);
      if (h==null) return null;
      else return new AllowHeader(h);
   } 
   /** Removes AllowHeader */
   public void removeAllowHeader() 
   {  removeHeader(SipHeaders.Allow);
   }


   /** Whether Message has ExpiresHeader */   
   public boolean hasExpiresHeader()
   {  return hasHeader(SipHeaders.Expires);
   }   
   /** Gets ExpiresHeader of Message */
   public ExpiresHeader getExpiresHeader()
   {  Header h=getHeader(SipHeaders.Expires);
      if (h==null) return null;
      else return new ExpiresHeader(h);
   } 
   /** Sets ExpiresHeader of Message */
   public void setExpiresHeader(ExpiresHeader eh) 
   {  setHeader(eh);
   }    
   /** Removes ExpiresHeader from Message (if it exists) */
   public void removeExpiresHeader() 
   {  removeHeader(SipHeaders.Expires);
   }   

   
   /** Whether Message has ContentTypeHeader */   
   public boolean hasContentTypeHeader()
   {  return hasHeader(SipHeaders.Content_Type);
   }   
   /** Gets ContentTypeHeader of Message */
   public ContentTypeHeader getContentTypeHeader()
   {  Header h=getHeader(SipHeaders.Content_Type);
      if (h==null) return null;
      else return new ContentTypeHeader(h);
   } 
   /** Sets ContentTypeHeader of Message */
   protected void setContentTypeHeader(ContentTypeHeader cth) 
   {  setHeader(cth);
   }    
   /** Removes ContentTypeHeader from Message (if it exists) */
   protected void removeContentTypeHeader() 
   {  removeHeader(SipHeaders.Content_Type);
   }
 
   
   /** Whether Message has ContentLengthHeader */   
   public boolean hasContentLengthHeader()
   {  return hasHeader(SipHeaders.Content_Length);
   }  
   /** Gets ContentLengthHeader of Message */
   public ContentLengthHeader getContentLengthHeader()
   {  Header h=getHeader(SipHeaders.Content_Length);
      if (h==null) return null;
      else return new ContentLengthHeader(h);
   } 
   /** Sets ContentLengthHeader of Message */
   protected void setContentLengthHeader(ContentLengthHeader clh) 
   {  setHeader(clh);
   }    
   /** Removes ContentLengthHeader from Message (if it exists) */
   protected void removeContentLengthHeader() 
   {  removeHeader(SipHeaders.Content_Length);
   }   

  
   /** Whether Message has Body */   
   public boolean hasBody()
   {  if (hasContentLengthHeader()) return getContentLengthHeader().getContentLength()>0;
      else return hasContentTypeHeader();
   }
   /** Gets body(content) type */
   public String getBodyType()
   {  return getContentTypeHeader().getContentType();
   } 
   /** Sets the message body */
   public void setBody(String content_type, String body) 
   {  removeBody();
      if (body!=null && body.length()>0)
      {  setContentTypeHeader(new ContentTypeHeader(content_type));
         setContentLengthHeader(new ContentLengthHeader(body.length()));
         message=message+"\r\n"+body;
      }
      else
      {  setContentLengthHeader(new ContentLengthHeader(0));
         message=message+"\r\n";
      }
   }          
   /** Sets sdp body */
   public void setBody(String body) 
   {  setBody("application/sdp",body);
   }            
   /** Gets message body. The end of body is evaluated
     * from the Content-Length header if present (SIP-RFC compliant),
     * or from the end of message if no Content-Length header is present (non-SIP-RFC compliant) */
   public String getBody()
   {  //if (!hasBody()) return "";
      if (!hasBody()) return null;
      int begin=(new SipParser(message)).goToBody().getPos();
      int len;
      // the following 'if' is for robustness with non SIP-compliant UAs;
      // copliant UAs must insert Content-Length header when body is present..
      if (this.hasContentLengthHeader()) len=getContentLengthHeader().getContentLength();
      else
      {  //printWarning("No Content-Length header found for the Body",3);
         len=message.length()-begin;
      }
      int end=begin+len;
      if (end>message.length())
      {  //printWarning("Found a Message Body shorter than Content-Length",3);
         end=message.length();
      }
      return message.substring(begin,end);
   }  
   /** Removes the message body (if it exists) and the final empty line */
   public void removeBody() 
   {  int pos=(new SipParser(message)).goToEndOfLastHeader().goToNextLine().getPos(); 
      message=message.substring(0,pos);
      removeContentLengthHeader();
      removeContentTypeHeader();
   }
   
   
   //**************************** Authentication ****************************/


   /** Whether has AuthenticationInfoHeader */
   public boolean hasAuthenticationInfoHeader()
   {  return hasHeader(SipHeaders.Authentication_Info);
   }
   /** Sets AuthenticationInfoHeader */
   public void setAuthenticationInfoHeader(AuthenticationInfoHeader h) 
   {  setHeader(h);
   } 
   /** Gets AuthenticationInfoHeader */
   public AuthenticationInfoHeader getAuthenticationInfoHeader()
   {  Header h=getHeader(SipHeaders.Authentication_Info);
      if (h==null) return null;
      else return new AuthenticationInfoHeader(h);
   } 
   /** Removes AuthenticationInfoHeader */
   public void removeAuthenticationInfoHeader() 
   {  removeHeader(SipHeaders.Authentication_Info);
   }


   /** Whether has AuthorizationHeader */
   public boolean hasAuthorizationHeader()
   {  return hasHeader(SipHeaders.Authorization);
   }
   /** Sets AuthorizationHeader */
   public void setAuthorizationHeader(AuthorizationHeader h) 
   {  setHeader(h);
   } 
   /** Gets AuthorizationHeader */
   public AuthorizationHeader getAuthorizationHeader()
   {  Header h=getHeader(SipHeaders.Authorization);
      if (h==null) return null;
      else return new AuthorizationHeader(h);
   } 
   /** Removes AuthorizationHeader */
   public void removeAuthorizationHeader() 
   {  removeHeader(SipHeaders.Authorization);
   }


   /** Whether has WwwAuthenticateHeader */
   public boolean hasWwwAuthenticateHeader()
   {  return hasHeader(SipHeaders.WWW_Authenticate);
   }
   /** Sets WwwAuthenticateHeader */
   public void setWwwAuthenticateHeader(WwwAuthenticateHeader h) 
   {  setHeader(h);
   } 
   /** Gets WwwAuthenticateHeader */
   public WwwAuthenticateHeader getWwwAuthenticateHeader()
   {  Header h=getHeader(SipHeaders.WWW_Authenticate);
      if (h==null) return null;
      else return new WwwAuthenticateHeader(h);
   } 
   /** Removes WwwAuthenticateHeader */
   public void removeWwwAuthenticateHeader() 
   {  removeHeader(SipHeaders.WWW_Authenticate);
   }


   /** Whether has ProxyAuthenticateHeader */
   public boolean hasProxyAuthenticateHeader()
   {  return hasHeader(SipHeaders.Proxy_Authenticate);
   }
   /** Sets ProxyAuthenticateHeader */
   public void setProxyAuthenticateHeader(ProxyAuthenticateHeader h) 
   {  setHeader(h);
   } 
   /** Gets ProxyAuthenticateHeader */
   public ProxyAuthenticateHeader getProxyAuthenticateHeader()
   {  Header h=getHeader(SipHeaders.Proxy_Authenticate);
      if (h==null) return null;
      else return new ProxyAuthenticateHeader(h);
   } 
   /** Removes ProxyAuthenticateHeader */
   public void removeProxyAuthenticateHeader() 
   {  removeHeader(SipHeaders.Proxy_Authenticate);
   }


   /** Whether has ProxyAuthorizationHeader */
   public boolean hasProxyAuthorizationHeader()
   {  return hasHeader(SipHeaders.Proxy_Authorization);
   }
   /** Sets ProxyAuthorizationHeader */
   public void setProxyAuthorizationHeader(ProxyAuthorizationHeader h) 
   {  setHeader(h);
   } 
   /** Gets ProxyAuthorizationHeader */
   public ProxyAuthorizationHeader getProxyAuthorizationHeader()
   {  Header h=getHeader(SipHeaders.Proxy_Authorization);
      if (h==null) return null;
      else return new ProxyAuthorizationHeader(h);
   } 
   /** Removes ProxyAuthorizationHeader */
   public void removeProxyAuthorizationHeader() 
   {  removeHeader(SipHeaders.Proxy_Authorization);
   }

   
   
   //**************************** RFC 2543 Legacy ****************************/


   /** Checks whether the next Route is formed according to RFC2543 Strict Route
     * and adapts the message. */
   public void rfc2543RouteAdapt() 
   {  if (hasRouteHeader())
      {  MultipleHeader mrh=getRoutes();
         RouteHeader rh=new RouteHeader(mrh.getTop());
         if (!(new RouteHeader(mrh.getTop())).getNameAddress().getAddress().hasLr())
         {  // re-format the message according to the RFC2543 Strict Route rule
            SipURL next_hop=(new RouteHeader(mrh.getTop())).getNameAddress().getAddress();
            SipURL recipient=getRequestLine().getAddress();
            mrh.removeTop();
            mrh.addBottom(new RouteHeader(new NameAddress(recipient)));
            setRoutes(mrh);
            setRequestLine(new RequestLine(getRequestLine().getMethod(),next_hop));
         }   
      }
   }
  

   /** Changes form RFC2543 Strict Route to RFC3261 Lose Route.
     * <p> The Request-URI is replaced with the last
     * value from the Route header, and that value is removed from the
     * Route header. */
   public void rfc2543toRfc3261RouteUpdate() 
   {  // the message is formed according with RFC2543 strict route
      // the next hop is the request-uri
      // the recipient of the message is the last Route value
      RequestLine request_line=getRequestLine();
      SipURL next_hop=request_line.getAddress();
      MultipleHeader mrh=getRoutes();
      SipURL target=(new RouteHeader(mrh.getBottom())).getNameAddress().getAddress();
      mrh.removeBottom();
      next_hop.addLr();
      mrh.addTop(new RouteHeader(new NameAddress(next_hop)));
      removeRoutes();
      addRoutes(mrh);
      setRequestLine(new RequestLine(request_line.getMethod(),target));
   }

}
