////////////////////////////////////////////////////////////////////////////
//
// 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.kerberos;

import java.io.File;
import java.util.Properties;
import java.util.Vector;
import java.util.StringTokenizer;
import java.net.InetAddress;

import com.dstc.security.kerberos.v5.crypto.EncryptionType;

/**
 * <p>A class representing the Kerberos context construct.
 *
 * A Kerberos context is the context of a running process utilising the
 * Kerberos service. This context includes configuration information of the
 * Kerberos installation as well as information specific to the owner of the
 * process. Configuration information includes default realm, KDC, encryption
 * types, KDC and ticket options, allowable skew time, etc. Process owner
 * specific information may include user name, path to credential cache, etc.
 *
 * @version 0.98, 98/07/01
 * @author Nam Tran
 */
public class KerberosContext
{
  // installation, realm info
  private String realmName;
  private InetAddress kdcAddr;
  private int kdcPort;
  private EncryptionType etype;
  private RequestOptions defOptions;
  private int defTktLife;   // hours
  private int defRenewTime; // hours, applied only if renewable

  // process owner specific info
  private String userName;
  private int ccType;
  private String ccName;
  private String keyTabName;

  /**
   * Construct a KerberosContext instance for the default realm from a
   * configuration (properties) file.
   *
   * @param config The Properties object representing the configuration.
   */
  public KerberosContext(Properties config) throws KerberosException
  {
    // get the default realm
    realmName = config.getProperty("jcsi.krb5.default.realm");
    if (realmName == null)
      throw new KerberosException(KerberosException.KRB_IMPL_ERR_CONFIG);

    buildContext(realmName, config);
  }

  /**
   * Construct a KerberosContext instance for a realm from a
   * configuration (properties) file.
   *
   * @param realm  The realm to work with.
   * @param config The Properties object representing the configuration.
   */
  public KerberosContext(String realm, Properties config)
    throws KerberosException
  {
    buildContext(realm, config);
  }

  private void buildContext(String realm, Properties config) throws
      KerberosException
  {
    realmName = realm;

    // get the installation-wide info
    String tmpStr = config.getProperty("jcsi.krb5.default.options");
    if (tmpStr != null)
      defOptions = new RequestOptions(tmpStr);

    tmpStr = config.getProperty("jcsi.krb5.default.etype");
    StringTokenizer tknz = new StringTokenizer(tmpStr, " \t\n\r\f,");
    String type;
    Vector types = new Vector();
    while (tknz.hasMoreTokens())
    {
      type = tknz.nextToken().toUpperCase();
      if (type.equals("DES-CBC-CRC"))
        types.addElement(new Integer(1));
      else if (type.equals("DES-CBC-MD4"))
        types.addElement(new Integer(2));
      else if (type.equals("DES-CBC-MD5"))
        types.addElement(new Integer(3));
      // add more if supported
    }

    if (types.isEmpty())
      types.addElement(new Integer(1));     // at least have DES-CBC-CRC
    etype = new EncryptionType(types);

    tmpStr = config.getProperty("jcsi.krb5.default.ticket.lifetime");
    defTktLife = parseTime(tmpStr, 10);

    tmpStr = config.getProperty("jcsi.krb5.default.ticket.renewtime");
    defRenewTime = parseTime(tmpStr, 24*7);

    // get the realm specific info
    try
    {
      kdcAddr = InetAddress.getByName(config.getProperty("jcsi.kdc.host." +
                                                          realmName));
      kdcPort = Integer.parseInt(config.getProperty("jcsi.kdc.port." +
                                                     realmName));
    }
    catch (Exception e)
    {
      e.printStackTrace();
      throw new KerberosException(KerberosException.KRB_IMPL_ERR_CONFIG);
    }

    String keyDir = (String)config.getProperty("jcsi.kerberos.key.dir");
    if (keyDir == null)
      throw new KerberosException(KerberosException.KRB_IMPL_ERR_CONFIG);

    String name;
    name = (String)config.getProperty("jcsi.kerberos.ccache");
    if (name == null)
      name = "krb5.ccache";
    ccName = keyDir + File.separator + name;

    name = (String)config.getProperty("jcsi.kerberos.keytab");
    if (name == null)
      name = "krb5.keytab";
    keyTabName = keyDir + File.separator + name;

    // get process owner specific info if possible
    try
    {

      userName = System.getProperty("user.name");
      //ccName = System.getProperty("user.home") +
      //System.getProperty("file.separator") + ".krb5.ccache";
      //keyTabName = System.getProperty("user.home") +
      //System.getProperty("file.separator") + ".krb5.keytab";

    }
    catch (Exception e)
    {
      // ignore exception thrown by SecurityManager for applets
      // set ccName to be relative to current dir, whatever that is
      //ccName = keyDir + File.separator + "krb5.ccache";
      //keyTabName = keyDir + File.separtor + "krb5.keytab";
    }
  }

  /**
   * parse a string into a time duration in hours. The string has the
   * format [integer ("d" | "D" | "h" | "H")].
   * Return the result of a successful parse or the default given otherwise.
   */
  private static int parseTime(String str, int def)
  {
    // ignore easily recognisable invalid strings
    if ((str == null) || (str.length() < 2))
      return def;

    char unit = Character.toUpperCase(str.charAt(str.length() - 1));
    if ((unit != 'D') && (unit != 'H'))
      return def;

    String amtStr = str.substring(0, str.length() - 1);
    try
    {
      int amount = Integer.parseInt(amtStr);
      if (unit == 'D')
        amount *= 24;
      return amount;
    }
    catch (NumberFormatException e)
    {
      // ignore and fall through
    }

    return def;
  }

  /**
   * Returns the realm name.
   */
  public String getRealm()
  {
    return realmName;
  }

  /**
   * Returns the address of the KDC.
   */
  public InetAddress getKDCAddress()
  {
    return kdcAddr;
  }

  /**
   * Returns the port of the KDC.
   */
  public int getKDCPort()
  {
    return kdcPort;
  }

  /**
   * Returns the default encryption types.
   */
  public Vector getEncType()
  {
    return etype.getTypes();
  }

  /**
   * Returns the default KDC options.
   */
  public RequestOptions getDefaultOptions()
  {
    return defOptions;
  }

  public void setRequestOptions(RequestOptions opts)
  {
    this.defOptions = opts;
  }

  /**
   * Returns the user name.
   */
  public String getUsername()
  {
    return userName;
  }

  /**
   * Sets the user name.
   */
  public void setUsername(String user)
  {
    userName = user;
  }

  /**
   * Returns the credential cache name.
   */
  public String getCCName()
  {
    return ccName;
  }

  /**
   * Sets the credential cache name.
   */
  public void setCCName(String ccName)
  {
    this.ccName = ccName;
  }

  /**
   * Returns the default ticket lifetime in hours.
   */
  public int getDefTicketLife()
  {
    return defTktLife;
  }

  /**
   * Returns the default ticket renew time in hours.
   */
  public int getDefRenewTime()
  {
    return defRenewTime;
  }

  public void setKeyTabName(String name)
  {
    keyTabName = name;
  }

  public String getKeyTabName()
  {
    return keyTabName;
  }
}
