/*****************************************************************************
 *                                                                           *
 *         Licensed Materials - Property of IBM                              *
 *                                                                           *
 *         "Restricted Materials of IBM"                                     *
 *                                                                           *
 *         5799-QRY                                                          *
 *                                                                           *
 *         (C) Copyright IBM Corp. 1998                                      *
 *                                                                           *
 *****************************************************************************/
package com.ibm.devtools.SIPNoTE;

import java.io.*;
import java.util.*;

/**
 * This class represents a SIP 2.0 message, as described in version 4 of
 * the SIP Internet Draft from the IETF.  Not all request methods have
 * been fully implemented.  In fact, only REGISTER, UNREGISTER, MESSAGE,
 * and LOCATE have machinery behind them.  The rest have only stubs
 * where the implementation should be made.
 * 
 * @author Bill Nagy
 */
public class SIPMessage
{
  /**Unknown message type or method*/
  public static final int UNKNOWN = 0;
  /**Message type*/
  public static final int REQUEST = 1, RESPONSE = 2;
  /**Method type*/
  public static final int REGISTER = 1, UNREGISTER = 2, INVITE = 3, ACK = 4,
    OPTIONS = 5, BYE = 6, MESSAGE = 7, LOCATE = 8;
  int messagetype = UNKNOWN;
  int method = UNKNOWN;

  String startline;
  String[] messageheaderlines = new String[20];
  int currentline = 0;
  byte[] messagebody;
  int messagebodylen;
  String contenttype;
  int firstvialine = -1; /*Location in messageheaderlines of the first Via:*/
  int lastvialine = -1; /*Location in messageheaderlines of the last Via:.
			 This corresponds to the originator of the message.*/

  SIPurl[] requestURIs = new SIPurl[5];
  int statuscode;
  String reasonphrase;
  String sipversion = new String("SIP/2.0");
  int toline;
  int fromline;
  String [] via = new String[20];
  int timestampline;

  
  /**
   * Write the message out to the specified OutputStream.
   *
   * @param out The OutputStream to which the data should be written.
   * @exception MalformedMessageException Thrown when the SIP message does
                not fit within the specifications.  This may mean, that
		required fields have not been set, are set incorrectly, or
		that something else went wrong.
   */
  public void writeMessage(OutputStream out)
       throws MalformedMessageException
  {
    PrintWriter pout = new PrintWriter(new OutputStreamWriter(out), true);
    int cnt;

    switch (messagetype)
      {
      case REQUEST:
	{
	  String retstr;
	  switch (method)
	    {
	    case REGISTER:
	      retstr = validateRegister();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      pout.print("REGISTER ");
	      break;
	    case UNREGISTER:
	      retstr = validateUnregister();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      pout.print("UNREGISTER ");
	      break;
	    case INVITE:
	      break;
	    case ACK:
	      break;
	    case OPTIONS:
	      break;
	    case BYE:
	      break;
	    case MESSAGE:
	      retstr = validateMessage();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      pout.print("MESSAGE ");
	      break;
	    case LOCATE:
	      retstr = validateLocate();

	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      pout.print("LOCATE ");
	      break;
	    default:
	      throw new MalformedMessageException("Attempted to send unknown message type\n");
	    }

	  try
	    {
	      for (cnt = 0; cnt < requestURIs.length; cnt++)
		{
		  pout.print(requestURIs[cnt].toURLString()+" ");
		}
	      pout.print(sipversion+"\n");
	    }
	  catch (MalformedSIPurlException e)
	    {
	      throw new MalformedMessageException(e.toString());
	    }

	  for (cnt = 0; cnt < currentline; cnt++)
	    pout.print(messageheaderlines[cnt]+"\n");

	  //Why do we treat this header differently?
	  //We can't, this breaks it (we get multiple headers)
	  if (messagebodylen > 0)
	    {
	      pout.print("Content-Type: "+contenttype+"\n");
	      pout.print("Content-Length: "+messagebodylen+"\n");
	    }
	  
	  pout.print("\n");
	  pout.flush();

	  if (messagebodylen > 0)
	    {
	      try
		{
		  out.write(messagebody, 0, messagebodylen);
		}
	      catch (IOException e)
		{
		   throw new MalformedMessageException("IOException -->"+e);
		}
	    }
	  try
	    {
	      out.flush();
	    }
	  catch (IOException e)
	    {
	      throw new MalformedMessageException("IOException -->"+e);
	    }

	  break;
	}
      case RESPONSE:
	{
	  String retstr;
	  switch (method)
	    {
	    case REGISTER:
	      retstr = validateRegisterRsp();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      break;
	    case UNREGISTER:
	      retstr = validateUnregisterRsp();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      break;
	    case INVITE:
	      break;
	    case ACK:
	      break;
	    case OPTIONS:
	      break;
	    case BYE:
	      break;
	    case MESSAGE:
	      retstr = validateMessageRsp();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      break;
	    case LOCATE:
	      retstr = validateLocateRsp();
	      
	      if (retstr != null)
		{
		  throw new MalformedMessageException(retstr);
		}
	      break;
	    default:
	      throw new MalformedMessageException("Attempted to send unknown message type\n");
	    }

	  //We probably should force the status code to be 3 digits
	  pout.print(sipversion+" "+statuscode+" "+reasonphrase+"\n");
	  
	  for (cnt = 0; cnt < currentline; cnt++)
	    pout.print(messageheaderlines[cnt]+"\n");
	  
	  pout.print("\n");
	  pout.flush();

	  if (messagebodylen > 0)
	    {
	      try
		{
		  out.write(messagebody, 0, messagebodylen);
		}
	      catch (IOException e)
		{
		  throw new MalformedMessageException("IOException -->"+e);
		}
	    }
	  try
	    {
	      out.flush();
	    }
	  catch (IOException e)
	    {
	      throw new MalformedMessageException("IOException -->"+e);
	    }
	  
	  break;
	  
	}
      }
    return;
  }

  /**
   * Read a SIP message in from the specified InputStream.
   *
   * @param in The InputStream from which the message should be read.
   * @exception MalformedMessageException Thrown when the SIP message
                which is read in is not well formed based upon the
		specification, and we are unable to fill in or fix the
		required information.
   */
  public void readMessage(InputStream in)
       throws MalformedMessageException
  {
    ModDataInputStream mdis = new ModDataInputStream(in);
    String inputline;
    String retstr = null;
    
    try
      {
	/*Read start-line*/
	while (true)
	  {
	    /*Skip any initial CRLFs (pg20)*/
	    inputline = mdis.readLinelf();
	    if (inputline == null)
	      {
		throw new
		  MalformedMessageException("Missing start-line of SIP message.");
	      }
	    if (inputline.length() != 0)
	      break;
	  }
	if (DebugDefine.DEBUG)
	  System.out.println("Read startline: "+inputline);
	setStartLine(inputline);
	
	/*Read message-header (pg20)*/
	while (true)
	  {
	    inputline = mdis.readLinelf();
	    if (inputline == null)
	      {
		//Is this really an invalid state???
		//May be missing if no message body and bad server
		throw new
		  MalformedMessageException("Missing message-header in SIP message.");
	      }
	    if (inputline.length() == 0)
	      break;
	    if (DebugDefine.DEBUG)
	      System.out.println("Read msg header: "+inputline);
	    //This would be faster if we didn't make the function call here
	    setMessageHeaderLine(inputline);
	  }
	
	if (messagetype == REQUEST)
	  {
	    switch (method)
	      {
	      case REGISTER:
		retstr = validateRegister();
		break;
	      case UNREGISTER:
		retstr = validateUnregister();
		break;
	      case INVITE:
		break;
	      case ACK:
		break;
	      case OPTIONS:
		break;
	      case BYE:
		break;
	      case MESSAGE:
		retstr = validateMessage();
		break;
	      case LOCATE:
		retstr = validateLocate();
		break;
	      }
	  }
	else
	  {
	    switch (method)
	      {
	      case REGISTER:
		retstr = validateRegisterRsp();
		break;
	      case UNREGISTER:
		retstr = validateUnregisterRsp();
		break;
	      case INVITE:
		break;
	      case ACK:
		break;
	      case OPTIONS:
		break;
	      case BYE:
		break;
	      case MESSAGE:
		retstr = validateMessageRsp();
		break;
	      case LOCATE:
		retstr = validateLocateRsp();
		break;
	      }
	  }
	
	if (retstr != null)
	  throw new MalformedMessageException(retstr);
	
	//Insert message reading code here -- What is this???
	messagebody = new byte[messagebodylen];
	int amntlefttoread = messagebodylen;
	int amntread;

	while(amntlefttoread > 0)
	  {
	    amntread = in.read(messagebody, messagebody.length-amntlefttoread,
			       amntlefttoread);
	    if (amntread == -1)
	      {
		throw new MalformedMessageException("Missing message data. Read: "+(int)(messagebody.length-amntlefttoread)+" bytes, Expecting: "+messagebody.length+" bytes.");
	      }
	    amntlefttoread -= amntread;
	  }
	
	return;
      }
    catch (IOException e)
      {
	throw new MalformedMessageException("IOException -->"+e);
      }
  }

  /**
   * Parses the request/response header.
   * 
   * @exception MalformedMessageException Thrown if the message is
   *            incorrectly formed.
   */
  private void setStartLine(String line)
       throws MalformedMessageException
  {
    int cnt;
    String nexttoken;

    /*Figure out what kind of a message we are in */
    try
      {
	StringTokenizer st = new StringTokenizer(line);
	String startlinetoken = st.nextToken();
    
	startline = line;

	if (startlinetoken.indexOf("SIP") == -1)
	  {
	    /*We are a request message*/
	    if (DebugDefine.DEBUG)
	      System.out.println("Parsing request message");
	    messagetype = REQUEST;
	    if (startlinetoken.equalsIgnoreCase("REGISTER"))
	      method = REGISTER;
	    else
	      if (startlinetoken.equalsIgnoreCase("UNREGISTER"))
		method = UNREGISTER;
	      else
		if (startlinetoken.equalsIgnoreCase("INVITE"))
		  method = INVITE;
		else
		  if (startlinetoken.equalsIgnoreCase("ACK"))
		    method = ACK;
		  else
		    if (startlinetoken.equalsIgnoreCase("OPTIONS"))
		      method = OPTIONS;
		    else
		      if (startlinetoken.equalsIgnoreCase("BYE"))
			method = BYE;
		      else
			if (startlinetoken.equalsIgnoreCase("MESSAGE"))
			  method = MESSAGE;
			else
			  if (startlinetoken.equalsIgnoreCase("LOCATE"))
			    method = LOCATE;
			  else
			    throw new MalformedMessageException("Unknown method on start-line of SIP message --> "+line);

	    //MULTI - Need to be able to read in multiple URIs and
	    // distinguish between uri and sipversion
	    try
	      {
		cnt = 0;
		while(true)
		  {
		    /*read the next token*/
		    nexttoken = st.nextToken();
		    /*if starts with SIP, use it as the version
		    otherwise add it to the requestURIs*/
		    if ((nexttoken.toUpperCase()).startsWith("SIP"))
		      break;

		    if (cnt == requestURIs.length) /*Increase array*/
		      {
			SIPurl[] tempVar = new SIPurl[requestURIs.length+5];
			System.arraycopy(requestURIs, 0, tempVar, 0,
					 requestURIs.length);
			requestURIs = tempVar;
		      }

		    requestURIs[cnt] = new SIPurl();
		    requestURIs[cnt].fromString(nexttoken);

		    cnt++;
		  }
		
		if (cnt != requestURIs.length)
		  {
		    /*Truncate this, so we know exactly how long it is*/
		    SIPurl[] tempVar = new SIPurl[cnt];
		    System.arraycopy(requestURIs, 0, tempVar, 0, cnt);
		    requestURIs = tempVar;
		  }
	      }
	    catch (MalformedSIPurlException e)
	      {
		throw new MalformedMessageException("Invalid requestURI on start-line of SIP message --> "+line);
	      }

	    sipversion = nexttoken;
	    //Something should probably be done if this is not SIP2.0, but
	    //what should that be?  Depends upon if future versions are
	    //guaranteed to be backwards compatible.
	  }
	else
	  {
	    String tempstr;
	    if (DebugDefine.DEBUG)
	      System.out.println("Parsing response message");
	    messagetype = RESPONSE;
	    //Something should probably be done if this is not SIP2.0, but
	    //what should that be?  Depends upon if future versions are
	    //guaranteed to be backwards compatible.
	    sipversion = startlinetoken;

	    tempstr = st.nextToken();
	    try
	      {
		statuscode = (new Integer(tempstr)).intValue();
	      }
	    catch (NumberFormatException e)
	      {
		throw new
		  MalformedMessageException("Bad Status-Code --> "+tempstr);
	      }
	    
	    //Make this CRLF
	    reasonphrase = st.nextToken("\n");
	  }
	
	return;
      }
    //Should I propogate the NoSuchElementException up to say
    //exactly what went wrong?
    catch (NoSuchElementException e)
      {
	throw new MalformedMessageException("Malformed start-line on SIP message --> "+line);
      }
  }

  /**
   * Sets the next free message header line in this SIP message to
   * be the value which is passed in, and perform a simple parse
   * of this line, to determine whether or not we should set any other
   * values.
   *
   * @param line The header line to be parsed.
   * @exception MalformedMessageException Thrown if the header line
   *            causes the message to become malformed.
   */
  private void setMessageHeaderLine(String line)
       throws MalformedMessageException
  {
    messageheaderlines[currentline] = line;
    currentline++;
    /* See if we need to increase array */
    if (currentline == messageheaderlines.length)
      {
	String [] tmpVar = new String[messageheaderlines.length + 20];
	System.arraycopy(messageheaderlines, 0, tmpVar, 0,
			 messageheaderlines.length);
	messageheaderlines = tmpVar;
      }

    try
      {
	StringTokenizer st = new StringTokenizer(line);
	String headerlinetoken = st.nextToken();
	
	/*Keep track of the first Via line, so that we can add ourselves
	  before it if needed. (pg41)*/
	if (headerlinetoken.equalsIgnoreCase("Via:"))
	  {
	    if (firstvialine == -1)
	      firstvialine = currentline-1;
	    lastvialine = currentline-1;
	  }
	
	else
	  if (headerlinetoken.equalsIgnoreCase("Content-Length:"))
	    {
	      try
		{
		  messagebodylen = (new Integer(st.nextToken())).intValue();
		}
	      catch (NumberFormatException e)
		{
		  throw new MalformedMessageException("Bad Message-Header Line--> "+line);
		}
	    }
	else
	  if (headerlinetoken.equalsIgnoreCase("Content-Type:"))
	    {
	      contenttype = st.nextToken();
	    }
	  else
	    if (headerlinetoken.equalsIgnoreCase("To:"))
	      toline = currentline-1;
	    else
	      if (headerlinetoken.equalsIgnoreCase("From:"))
		fromline = currentline-1;
      }
    catch (NoSuchElementException e)
      {
	throw new MalformedMessageException("Bad Message-Header Line--> "+line);
      }
  }
  
  /**
   * Determine whether or not the REGISTER message contains all of the
   * necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateRegister()
  {
    if (requestURIs.length > 1)
      return new String("Too many request URIs in SIP Register message.");
    if (toline == -1)
      return new String("Missing To: header in SIP Register message.");
    if (firstvialine == -1)
      return new String("Missing Via: line(s) in SIP Register message.");
    return null;
  }
    
  /**
   * Determine whether or not the REGISTER response message contains all
   * of the necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateRegisterRsp()
  {
    if (firstvialine == -1)
      return new
	String("Missing Via: line(s) in SIP Register Response message.");
    return null;
  }

  /**
   * Determine whether or not the UNREGISTER message contains all of the
   * necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateUnregister()
  {
    if (requestURIs.length > 1)
      return new String("Too many request URIs in SIP Register message.");
    if (toline == -1)
      return new String("Missing To: header in SIP Unregister message.");
    if (firstvialine == -1)
      return new String("Missing Via: line(s) in SIP Unregister message.");
    return null;
  }
    
  /**
   * Determine whether or not the UNREGISTER response message contains all
   * of the necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateUnregisterRsp()
  {
    if (firstvialine == -1)
      return new
	String("Missing Via: line(s) in SIP Unregister Response message.");
    return null;
  }

  /**
   * Determine whether or not the MESSAGE message contains all of the
   * necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateMessage()
  {
    if (toline == -1)
      return new String("Missing To: header in SIP Register message.");
    if (fromline == -1)
      return new String("Missing To: header in SIP Register message.");
    if (firstvialine == -1)
      return new String("Missing Via: line(s) in SIP Register message.");
    return null;
  }
    
  /**
   * Determine whether or not the MESSAGE response message contains all
   * of the necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateMessageRsp()
  {
    if (firstvialine == -1)
      return new
	String("Missing Via: line(s) in SIP Register Response message.");
    return null;
  }

  /**
   * Determine whether or not the LOCATE message contains all of the
   * necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateLocate()
  {
    if (toline == -1)
      return new String("Missing To: header in SIP Locate message.");
    if (firstvialine == -1)
      return new String("Missing Via: line(s) in SIP Locate message.");
    return null;
  }

  /**
   * Determine whether or not the LOCATE response message contains all
   * of the necessary headers.
   *
   * @return A string containing an error message if necessary, or null otherwise.
   */
  private String validateLocateRsp()
  {
    if (firstvialine == -1)
      return new
	String("Missing Via: line(s) in SIP Locate Response message.");
    return null;
  }

  /**
   * Get the type of message this is (request vs. response).
   *
   * @return An integer representing the type of message.
   */
  public int getMessageType()
  {
    return messagetype;
  }

  /**
   * Sets the type of this message.
   *
   * @param type The type of message this is.
   */
  public void setMessageType(int type)
  {
    messagetype = type;
    return;
  }

  /**
   * Get the method to which this message belongs.
   *
   * @return An integer representing the method.
   */
  public int getMethod()
    {
      return method;
    }

  /**
   * Sets the method associated with this message.
   *
   * @param type The method which this message represents.
   */
  public void setMethod(int type)
    {
      method = type;
      return;
    }

  /**
   * Get the URL's from the request-line.
   *
   * @return An array of SIP URL's.
   */
  public SIPurl[] getRequestURI()
  {
    return requestURIs;
  }

  /**
   * Sets the URI's to which this message should be sent.
   *
   * @param urls The array of URL's to be set on the request-line.
   */
  public void setRequestURI(SIPurl[] urls)
  {
    requestURIs = new SIPurl[urls.length];
    System.arraycopy(urls, 0, requestURIs, 0, urls.length);
    return;
  }

  /**
   * Get the status code returned in this message. (For responses only.)
   *
   * @return An integer representing the status code.
   */
  public int getStatusCode()
  {
    return statuscode;
  }

  /**
   * Sets the status code associated with this response.
   *
   * @param code The status code to be sent.
   */
  public void setStatusCode(int code)
  {
    statuscode = code;
  }

  /**
   * Get the reason phrase associated with this response.
   *
   * @return A string containing the returned reason-phrase.
   */
  public String getReasonPhrase()
  {
    return reasonphrase;
  }

  /**
   * Sets the reason-phrase associated with this response. 
   *
   * @param reason The reason to be sent.
   */
  public void setReasonPhrase(String reason)
  {
    reasonphrase = reason;
  }

  /**
   * Get the first Via: which was added to this message.  This
   * indicates the originator of the message (we hope.)
   *
   * @return A ViaEntity with the originator information.
   * @exception MalformedHeaderException Thrown if the header line
   *            is syntactically invalid.
   */
  public ViaEntity getOriginatorVia()
       throws MalformedHeaderException
  {
    //Do we want to cache this?
    ViaEntity origvia = new ViaEntity();
    origvia.fromString(messageheaderlines[lastvialine]);
    return origvia;
  }

  /**
   * Adds a new Via: header above the other Via: headers.
   *
   * @param via The ViaEntity to be added.
   * @exception MalformedMessageException Thrown if the addition
   *            of this header causes the message to be syntactically
   *            malformed.
   */
  public void addVia(ViaEntity via)
       throws MalformedMessageException
  {
    //Do we want to add this as the first line, or do we want to 
    //add it just in front of the first via?
    String [] temp = new String[messageheaderlines.length+1];

    try
      {
	temp[0] = via.toViaString();
      }
    catch (MalformedHeaderException e)
      {
	throw new MalformedMessageException(e.toString());
      }
    System.arraycopy(messageheaderlines,0, temp, 1,
		     messageheaderlines.length);

    messageheaderlines = temp;

    firstvialine = 0;
    if (lastvialine == -1)
      lastvialine = 0;

    currentline++;

    return;
  }

  /**
   * Get the subscription information from all of the Subscribe:
   * headers within this message.
   *
   * @return An array of strings containing the subscriptions.
   */
  public String[] getSubscriptions()
  {
    int cnt, loop;
    String[] subs = new String[5];

    cnt = 0;
    for (loop = 0; loop < currentline; loop++)
      {
	if ((messageheaderlines[loop].toUpperCase()).startsWith("SUBSCRIBE:"))
	  {
	    if (cnt == subs.length) /*Increase array*/
	      {
		String[] tempVar = new String[subs.length+5];
		System.arraycopy(subs, 0, tempVar, 0,
				 subs.length);
		subs = tempVar;
	      }
	    
	    subs[cnt] = messageheaderlines[loop].substring(11);
	    cnt++;
	  }
      }

    if (cnt != subs.length)
      {
	/*Truncate this, so we know exactly how long it is*/
	String[] tempVar = new String[cnt];
	System.arraycopy(subs, 0, tempVar, 0, cnt);
	subs = tempVar;
      }
    return subs;
  }


  /**
   * Adds a new Subscribe: header to the message.
   *
   * @param sub The instance to be subscribed to.
   * @exception MalformedMessageException Thrown if the addition
   *            of this header line makes the message malformed.
   */
  public void addSubscription(String sub)
       throws MalformedMessageException
  {
    setMessageHeaderLine("Subscribe: "+sub);
      
    return;
  }

  /**
   * Get the contents of any Location: headers which were included
   * in this message.
   *
   * @return An array of SIP URL's containing the location information.
   * @exception MalformedMessageException Thrown if the header from
   *            which we are trying to fetch the information is malformed.
   */
  public SIPurl[] getLocation()
       throws MalformedMessageException
  {
    SIPurl[] locs = new SIPurl[5];
    int cnt, loop;

    cnt = 0;
    for (loop = 0; loop < currentline; loop++)
      {
	try
	  {
	    if ((messageheaderlines[loop].toUpperCase()).startsWith("LOCATION:"))
	      {
		if (cnt == locs.length) /*Increase array*/
		  {
		    SIPurl[] tempVar = new SIPurl[locs.length+5];
		    System.arraycopy(locs, 0, tempVar, 0,
				     locs.length);
		    locs = tempVar;
		  }

		locs[cnt] = new SIPurl();
		locs[cnt].fromString(messageheaderlines[loop].substring(10));
		cnt++;
	      }
	  }
	catch (MalformedSIPurlException e)
	  {
	    throw new MalformedMessageException("Invalid location in SIP message --> "+messageheaderlines[loop]);
	  }
      }

    if (cnt != locs.length)
      {
	/*Truncate this, so we know exactly how long it is*/
	SIPurl[] tempVar = new SIPurl[cnt];
	System.arraycopy(locs, 0, tempVar, 0, cnt);
	locs = tempVar;
      }
    return locs;
  }
  
  /**
   * Add a new Location: header to this message.
   *
   * @param loc The SIP URL representing the location to be added.
   * @exception MalformedMessageException Thrown if the header which
   *            is being added causes the message to be malformed.
   */
  public void addLocation(SIPurl loc)
       throws MalformedMessageException
  {
    try
      {
	setMessageHeaderLine("Location: "+loc.toURLString());
      }
    catch (MalformedSIPurlException e)
      {
	throw new MalformedMessageException(e.toString());
      }
      
    return;
  }

  /**
   * Add a new To: header to this message.
   *
   * @param to The ToEntity array containing the addresses to be added.
   * @exception MalformedMessageException Thrown if the header which
   *            is being added causes the message to be malformed.
   */
  public void addTo(ToEntity[] tolist)
       throws MalformedMessageException
  {
    int cnt;
    String tostring = new String();

    try
      {
	for (cnt = 0; cnt < tolist.length; cnt++)
	  {
	    if (cnt == 0)
	      {
		tostring = tolist[cnt].toToString();
	      }
	    else
	      {
		tostring = tostring+", "+tolist[cnt].toToStringWithoutHeader();
	      }
	  }
      }
    catch(MalformedHeaderException e)
      {
 	throw new MalformedMessageException(e.toString());
      }
    
    if (tolist.length != 0) 
      setMessageHeaderLine(tostring);
  }

  /**
   * Add a new From: header to this message.
   *
   * @param from The FromEntity representing the address to be added.
   * @exception MalformedMessageException Thrown if the header which
   *            is being added causes the message to be malformed.
   */
  public void addFrom(FromEntity from)
       throws MalformedMessageException
  {
    try
      {
	setMessageHeaderLine(from.toFromString());
      }
    catch (MalformedHeaderException e)
      {
	throw new MalformedMessageException(e.toString());
      }
      
    return;
  }

  /**
   * Sets the Content-Type header for this message.
   *
   * @param type The string representing the type of content included.
   */
  public void setContentType(String type)
  {
    contenttype = type;
    return;
  }

  /**
   * Get the Content-Type associated with the contents of this message.
   *
   * @return The string containing the content-type.
   */
  public String getContentType()
  {
    return contenttype;
  }

  /**
   * Sets the data for the message body.
   *
   * @param data The byte array which contains the data to be sent.
   */
  public void setMessageBody(byte[] data)
  {
    messagebody = data;
    messagebodylen = data.length;
    return;
  }

  /**
   * Get the body of this message.
   *
   * @return A byte array containing the contents of the message body.
   */
  public byte[] getMessageBody()
  {
    return messagebody;
  }
}
