// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * KDC request body
 */
public class KDCReqBody {

	/**
	 * KDC options
	 */
	public KDCOptions kdcOptions;

	/**
	 * Name
	 */
	public PrincipalName cname; //optional in TGSReq only

	/**
	 * Realm
	 */
	public Realm crealm;

	/**
	 * Name
	 */
	public PrincipalName sname; //optional

	/**
	 * From time
	 */
	public KerberosTime from; //optional

	/**
	 * Till time
	 */
	public KerberosTime till;

	/**
	 * Time
	 */
	public KerberosTime rtime; //optional

	/**
	 * Nounce
	 */
	public int nonce;

	/**
	 * Encryption type
	 */
	public int[] eType; //a sequence; not optional

	/**
	 * Addresses
	 */
	public HostAddresses addresses; //optional

	/**
	 * Encrypted authorization data
	 */
	public EncryptedData encAuthorizationData; //optional

	/**
	 * Additional tickets
	 */
	public Ticket[] additionalTickets; //optional

	/**
	 * Class constructor
	 *
	 * @param new_kdcOptions is of type KDCOptions
	 * @param new_cname is of type PrincipalName
	 * @param new_crealm is of type Realm
	 * @param new_sname is of type PrincipalName
	 * @param new_from is of type KerberosTime
	 * @param new_till is of type KerberosTime
	 * @param new_rtime is of type KerberosTime
	 * @param new_nonce is of type int
	 * @param new_eType is of type int[]
	 * @param new_addresses is of type HostAddresses
	 * @param new_encAuthorizationData is of type EncryptedData
	 * @param new_additionalTickets is of type Ticket[]
	 * @see krb5.lib.KDCOptions
	 * @see krb5.lib.PrincipalName
	 * @see krb5.lib.Realm
	 * @see krb5.lib.KerberosTime
	 * @see krb5.lib.HostAddresses
	 * @see krb5.lib.EncryptedData
	 * @see krb5.lib.Ticket
	 */
	public KDCReqBody(
		KDCOptions new_kdcOptions,
		PrincipalName new_cname, //optional in TGSReq only
		Realm new_crealm,
		PrincipalName new_sname, //optional
		KerberosTime new_from, //optional
		KerberosTime new_till,
		KerberosTime new_rtime, //optional
		int new_nonce,
		int[] new_eType, //a sequence; not optional
		HostAddresses new_addresses, //optional
		EncryptedData new_encAuthorizationData, //optional
		Ticket[] new_additionalTickets //optional
	) {
		kdcOptions = new_kdcOptions;
		cname = new_cname;
		crealm = new_crealm;
		sname = new_sname;
		from = new_from;
		till = new_till;
		rtime = new_rtime;
		nonce = new_nonce;
		eType = new_eType;
		addresses = new_addresses;
		encAuthorizationData = new_encAuthorizationData;
		additionalTickets = new_additionalTickets;
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @param msgType is of type int
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public KDCReqBody(EncodeRef ref, int msgType)
		throws Asn1Exception, BitStringException, RealmException,
		KrbApErrException {
		EncodeRef subRef = decode.Sequence(ref);
		if (decode.Tag(subRef) == 0) {
			kdcOptions = new KDCOptions(subRef.startOfData());
			//XXX need to validate that the kdcOptions are legal
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 1) {
			if (msgType != Krb5.KRB_AS_REQ)
				//XXX need to use more appropriate return code if available
				throw new Asn1Exception(Krb5.ASN1_BAD_ID);
			cname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}
		else
			if (msgType == Krb5.KRB_AS_REQ)
				//XXX need to use more appropriate return code if available
				throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 2) {
			crealm = new Realm(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 3) {
			sname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 4) {
			from = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 5) {
			till = new KerberosTime(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 6) {
			rtime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 7) {
			nonce = decode.Integer(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 8) {
			Vector tempEType = new Vector();
			EncodeRef subSubRef = decode.Sequence(subRef.startOfData());
			while (subSubRef.isMoreInSequence()) {
				tempEType.addElement(
					new Integer(decode.Integer(subSubRef)));
				subSubRef.next();
			}
			if (tempEType.size() > 0) {
				eType = new int[tempEType.size()];
				for (int i = 0; i < tempEType.size(); i++)
					eType[i] = ((Integer)tempEType.elementAt(i)).intValue();
			}
			//XXX empty eType sequence may be an error; check it out
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		//XXX need to verify that other types are checking for
		//isMoreInSequence() before testing the tag, when they
		//have trailing optional items
		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 9) {
			addresses = new HostAddresses(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 10) {
			encAuthorizationData = new EncryptedData(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 11) {
			Vector tempTickets = new Vector();
			EncodeRef subSubRef = decode.Sequence(subRef.startOfData());
			while (subSubRef.isMoreInSequence()) {
				tempTickets.addElement(new Ticket(subSubRef));
				subSubRef.next();
			}
			if (tempTickets.size() > 0) {
				additionalTickets = new Ticket[tempTickets.size()];
				tempTickets.copyInto(additionalTickets);
			}
			subRef.next();
		}
		//XXX need to verify that other types are checking for
		//no more items beyond the last valid sequence tag,
		//when they have trailing optional items
		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @param msgType is of type int
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 */
	public byte[] asn1Encode(int msgType) throws Asn1Exception,
		BitStringException {
		Vector tempKDCReqBody = new Vector();

		tempKDCReqBody.addElement(
			encode.prependExplicitTag(0, kdcOptions.asn1Encode()));
		if (msgType == Krb5.KRB_AS_REQ) {
			if (cname != null)
				tempKDCReqBody.addElement(
					encode.prependExplicitTag(1, cname.asn1Encode()));
			//else
				//XXX error; tbd
		}
		//else
			//if (cname != null)
				//XXX error; tbd
		tempKDCReqBody.addElement(
			encode.prependExplicitTag(2, crealm.asn1Encode()));
		if (sname != null)
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(3, sname.asn1Encode()));
		if (from != null)
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(4, from.asn1Encode()));
		tempKDCReqBody.addElement(
			encode.prependExplicitTag(5, till.asn1Encode()));
		if (rtime != null)
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(6, rtime.asn1Encode()));
		tempKDCReqBody.addElement(
			encode.prependExplicitTag(7, encode.Integer(nonce)));
		//XXX revisit, if empty eType sequences are allowed
		byte[][] e_type = new byte[eType.length][];
		for (int i = 0; i < eType.length; i++)
			e_type[i] = encode.Integer(eType[i]);
		tempKDCReqBody.addElement(
			encode.prependExplicitTag(8, encode.Sequence(e_type)));
		if (addresses != null)
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(9, addresses.asn1Encode()));
		if (encAuthorizationData != null)
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(10,
					encAuthorizationData.asn1Encode()));
		if (additionalTickets != null && additionalTickets.length > 0) {
			byte[][] additional_tickets =
				new byte[additionalTickets.length][];
			for (int i = 0; i < additionalTickets.length; i++)
				additional_tickets[i] =
					additionalTickets[i].asn1Encode();
			tempKDCReqBody.addElement(
				encode.prependExplicitTag(11,
					encode.Sequence(additional_tickets)));
		}

		byte[][] kdc_req_body = new byte[tempKDCReqBody.size()][];
		for (int i = 0; i < tempKDCReqBody.size(); i++)
			kdc_req_body[i] = (byte[])(tempKDCReqBody.elementAt(i));
		return encode.Sequence(kdc_req_body);
	}

}