//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.ssl;

import java.io.IOException;
import java.io.ByteArrayInputStream;

import javax.net.ssl.SSLHandshakeException;

/**
 * An abstract class for Handshake messages
 *
 * @author Ming Yung
 */
abstract class HandShake extends V3Message
{
  protected static final byte HELLO_REQUEST = 0x00;
  protected static final byte CLIENT_HELLO = 0x01;
  protected static final byte SERVER_HELLO = 0x02;
  protected static final byte CERTIFICATE = 0x0b;
  protected static final byte SERVER_KEY_EXCHANGE = 0x0c;
  protected static final byte CERTIFICATE_REQUEST = 0x0d;
  protected static final byte SERVER_HELLO_DONE = 0x0e;
  protected static final byte CERTIFICATE_VERIFY = 0x0f;
  protected static final byte CLIENT_KEY_EXCHANGE = 0x10;
  protected static final byte FINISHED = 0x14;

  protected byte msgType; //Handshake message type
  protected int bodyLength;
  protected HandShaker handShaker;

  /**
   * Default constructor for a HandShake message
   */
  protected HandShake() 
  {
    this.contentType = SSLProtocolUnit.HANDSHAKE;
  }

  /**
   * Constructs a HandShake message from an XDRInputStream with
   * a callback to the controlling HandShaker
   */
  protected HandShake(HandShaker handShaker, byte[] data) 
    throws IOException
  {
    this();

    this.handShaker = handShaker;

    XDRInputStream is 
      = new XDRInputStream(new ByteArrayInputStream(data));
      
    this.msgType = is.readByte();
    this.bodyLength = is.read3Bytes();

    readBody(is);

    handShaker.updateHashes(data);
  }

  /**
   * Constructs a HandShake message from an XDRInputStream with
   * a callback to the controlling HandShaker
   */
  protected static HandShake getInstance(HandShaker handShaker, byte[] data)
    throws IOException
  {
    byte msgType = data[0];

    switch (msgType)
    {
      case HELLO_REQUEST:
        return new HelloRequest((ClientHandShaker)handShaker, data);

      case CLIENT_HELLO:
        return new ClientHello((ServerHandShaker)handShaker, data);

      case SERVER_HELLO:
        return new ServerHello((ClientHandShaker)handShaker, data);

      case CERTIFICATE:
        return new Certificate(handShaker, data);

      case CERTIFICATE_REQUEST:
        return new CertificateRequest((ClientHandShaker)handShaker, data);

      case CERTIFICATE_VERIFY:
        return new CertificateVerify((ServerHandShaker)handShaker, data);

      case SERVER_KEY_EXCHANGE:
        if (handShaker.isDiffieHellmanKeyX())
          return new DHServerKeyExchange((ClientHandShaker)handShaker, data);
        else
          return new RSAServerKeyExchange((ClientHandShaker)handShaker, data);

      case SERVER_HELLO_DONE:
        return new ServerHelloDone((ClientHandShaker)handShaker, data);

      case CLIENT_KEY_EXCHANGE:
        if (handShaker.isDiffieHellmanKeyX())
          return new DHClientKeyExchange((ServerHandShaker)handShaker, data);
        else
          return new RSAClientKeyExchange((ServerHandShaker)handShaker, data);

      case FINISHED:
        return new Finished(handShaker, data);

      default:
        throw new SSLHandshakeException("Unknown handshake message type: " 
          + msgType);
    }
  }

  /**
   * Returns the length of the data in this HandShake message
   */
  public int getLength()
  {
    return (this.bodyLength + 4);
  }

  /**
   * Writes the data in this HandShake message to the XDROutputStream
   */
  protected void write(XDROutputStream os) throws IOException
  {
    os.writeByte(this.msgType);
    os.write3Bytes(this.bodyLength);
    
    writeBody(os);
  }

  protected byte getMessageType()
  {
    return this.msgType;
  }

  /**
   * Reads the body for this HandShake message from the XDRInputStream
   */
  protected abstract void readBody(XDRInputStream is) 
    throws IOException;

  /**
   * Writes the body for this HandShake message to the XDROutputStream
   */
  protected abstract void writeBody(XDROutputStream os) throws IOException;
}
