// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * KRB error
 */
public class KRBError {

	/**
	 * Version
	 */
	public int pvno;

	/**
	 * Message type
	 */
	public int msgType;

	/**
	 * Time
	 */
	public KerberosTime ctime; //optional

	/**
	 * Usec
	 */
	public Integer cusec; //optional

	/**
	 * Start time
	 */
	public KerberosTime stime;

	/**
	 * Start usec
	 */
	public int susec;

	/**
	 * Error code
	 */
	public int errorCode;

	/**
	 * Realm
	 */
	public Realm crealm; //optional

	/**
	 * Name
	 */
	public PrincipalName cname; //optional

	/**
	 * Realm
	 */
	public Realm realm;

	/**
	 * Name
	 */
	public PrincipalName sname;

	/**
	 * Error text
	 */
	public String eText; //optional

	/**
	 * Error data
	 */
	public byte[] eData; //optional

	/**
	 * Checksum
	 */
	public Checksum eCksum; //optional

	/**
	 * Class constructor
	 *
	 * @param new_apOptions is of type APOptions
	 * @param new_ctime is of type KerberosTime
	 * @param new_cusec is of type Integer
	 * @param new_stime is of type KerberosTime
	 * @param new_susec is of type int
	 * @param new_errorCode is of type int
	 * @param new_crealm is of type Realm
	 * @param new_cname is of type PrincipalName
	 * @param new_realm is of type Realm
	 * @param new_sname is of type PrincipalName
	 * @param new_eText is of type String
	 * @param new_eData is of type byte[]
	 * @see krb5.lib.APOptions
	 * @see krb5.lib.KerberosTime
	 * @see krb5.lib.Realm
	 * @see krb5.lib.PrincipalName
	 */
	public KRBError(
		APOptions new_apOptions,
		KerberosTime new_ctime,
		Integer new_cusec,
		KerberosTime new_stime,
		int new_susec,
		int new_errorCode,
		Realm new_crealm,
		PrincipalName new_cname,
		Realm new_realm,
		PrincipalName new_sname,
		String new_eText,
		byte[] new_eData
	) {
		pvno = Krb5.PVNO;
		msgType = Krb5.KRB_ERROR;
		ctime = new_ctime;
		cusec = new_cusec;
		stime = new_stime;
		susec = new_susec;
		errorCode = new_errorCode;
		crealm =  new_crealm;
		cname = new_cname;
		realm = new_realm;
		sname = new_sname;
		eText = new_eText;
		eData = new_eData;
	}

	/**
	 * Class constructor
	 *
	 * @param new_apOptions is of type APOptions
	 * @param new_ctime is of type KerberosTime
	 * @param new_cusec is of type Integer
	 * @param new_stime is of type KerberosTime
	 * @param new_susec is of type int
	 * @param new_errorCode is of type int
	 * @param new_crealm is of type Realm
	 * @param new_cname is of type PrincipalName
	 * @param new_realm is of type Realm
	 * @param new_sname is of type PrincipalName
	 * @param new_eText is of type String
	 * @param new_eData is of type byte[]
	 * @param new_eCksum is of type Checksum
	 * @see krb5.lib.APOptions
	 * @see krb5.lib.KerberosTime
	 * @see krb5.lib.Realm
	 * @see krb5.lib.PrincipalName
	 * @see krb5.lib.Checksum
	 */
	public KRBError(
		APOptions new_apOptions,
		KerberosTime new_ctime,
		Integer new_cusec,
		KerberosTime new_stime,
		int new_susec,
		int new_errorCode,
		Realm new_crealm,
		PrincipalName new_cname,
		Realm new_realm,
		PrincipalName new_sname,
		String new_eText,
		byte[] new_eData,
		Checksum new_eCksum
	) {
		pvno = Krb5.PVNO;
		msgType = Krb5.KRB_ERROR;
		ctime = new_ctime;
		cusec = new_cusec;
		stime = new_stime;
		susec = new_susec;
		errorCode = new_errorCode;
		crealm =  new_crealm;
		cname = new_cname;
		realm = new_realm;
		sname = new_sname;
		eText = new_eText;
		eData = new_eData;
		eCksum = new_eCksum;
	}

	/**
	 * Class constructor
	 *
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public KRBError(byte[] data) throws Asn1Exception,
		RealmException, KrbApErrException {
		this(new EncodeRef(data));
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public KRBError(EncodeRef ref) throws Asn1Exception,
		RealmException, KrbApErrException {
		if (decode.TagApp(ref) != Krb5.KRB_ERROR)
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		EncodeRef subRef = decode.Sequence(ref.startOfData());

		if (decode.Tag(subRef) == 0) {
			pvno = decode.Integer(subRef.startOfData());
			if (pvno != Krb5.PVNO)
				throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 1) {
			msgType = decode.Integer(subRef.startOfData());
			if (msgType != Krb5.KRB_ERROR)
				throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 2) {
			ctime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 3) {
			cusec = new Integer(decode.Integer(subRef.startOfData()));
			subRef.next();
		}

		if (decode.Tag(subRef) == 4) {
			stime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 5) {
			susec = decode.Integer(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 6) {
			errorCode = decode.Integer(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 7) {
			crealm = new Realm(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 8) {
			cname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 9) {
			realm = new Realm(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 10) {
			sname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 11) {
			eText = decode.GeneralString(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 12) {
			eData = decode.OctetString(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 13) {
			eCksum = new Checksum(subRef.startOfData());
			subRef.next();
		}

		//XXX consider allowing additional members in sequence
		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.RealmException
	 */
	public byte[] asn1Encode() throws Asn1Exception,
		RealmException {
		Vector tempKRBError = new Vector();

		tempKRBError.addElement(
			encode.prependExplicitTag(0, encode.Integer(pvno)));
		tempKRBError.addElement(
			encode.prependExplicitTag(1, encode.Integer(msgType)));

		if (ctime != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(2, ctime.asn1Encode()));
		if (cusec != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(3,
					encode.Integer(cusec.intValue())));
		
		tempKRBError.addElement(
			encode.prependExplicitTag(4, stime.asn1Encode()));
		tempKRBError.addElement(
			encode.prependExplicitTag(5, encode.Integer(susec)));
		tempKRBError.addElement(
			encode.prependExplicitTag(6, encode.Integer(errorCode)));
		
		if (crealm != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(7, crealm.asn1Encode()));
		if (cname != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(8, cname.asn1Encode()));
		
		tempKRBError.addElement(
			encode.prependExplicitTag(9, realm.asn1Encode()));
		tempKRBError.addElement(
			encode.prependExplicitTag(10, sname.asn1Encode()));

		if (eText != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(11,
					encode.GeneralString(eText)));
		if (eData != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(12,
					encode.OctetString(eData)));
		if (eCksum != null)
			tempKRBError.addElement(
				encode.prependExplicitTag(13,
					eCksum.asn1Encode()));

		byte[][] krb_error = new byte[tempKRBError.size()][];
		for (int i = 0; i < tempKRBError.size(); i++)
			krb_error[i] = (byte[])(tempKRBError.elementAt(i));

		return encode.prependExplicitTag(asn1Class.APPLICATION,
			msgType, encode.Sequence(krb_error));
	}

}