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

import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Set;
import java.util.Iterator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import javax.naming.*;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchResult;
import javax.naming.directory.SearchControls;
import com.dstc.security.provider.OID;
import com.dstc.security.asn1.Asn1;
import com.dstc.security.x509.X500Name;
import com.dstc.security.x509.RDN;
import com.dstc.security.x509.X509CertImpl;

/**
 * An LDAP Certficate repository tool
 *
 * @version 0.98, 98/11/01
 * @author Ming Yung
 */
public class Repository
{
  protected String BASE;
  protected String USER_CERT_ATTR_TYPE = "userCertificate;binary";
  protected String CA_CERT_ATTR_TYPE = "caCertificate;binary";
  protected DirContext ctx;
  protected CertificateFactory fact;

  /**
   * Construct a Repository from a Properties
   */
  public Repository(Properties env)
  {
    try
    {
      this.fact = CertificateFactory.getInstance("X509");

      String val = (String)env.get("jcsi.rep.userCertAttrType");
      if (val != null)
        setUserCertificateAttributeType(val);

      val = (String)env.get("jcsi.rep.caCertAttrType");
      if (val != null)
        setCACertificateAttributeType(val);

      val = (String)env.get("jcsi.rep.searchBase");
      if (val != null)
        setSearchBase(val);

      val = (String)env.get("jcsi.rep.url");

      env.put("java.naming.provider.url", val);
      env.put("java.naming.factory.initial", 
              "com.sun.jndi.ldap.LdapCtxFactory");
      env.put("java.naming.ldap.version", "2");

      ctx = new InitialDirContext(env);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  public void setUserCertificateAttributeType(String type)
  {
    USER_CERT_ATTR_TYPE = type;
  }

  public void setCACertificateAttributeType(String type)
  {
    CA_CERT_ATTR_TYPE = type;
  }

  public void setSearchBase(String base)
  {
    BASE = base;
  }

  public void authenticate(String dn, String password)
  {
  }

  /**
   * Returns a user certificate for the given distinguished name
   */
  public X509Certificate getUserCertificate(String dn) 
    throws NamingException
  {
    return getCert(USER_CERT_ATTR_TYPE, dn);
  }

  /**
   * Returns a CA certificate for the given distinguished name
   */
  public X509Certificate getCACertificate(String dn) 
    throws NamingException
  {
    return getCert(CA_CERT_ATTR_TYPE, dn);
  }

  /**
   * Publish the given user X509Certificate and other Attributes 
   * under the entry name being the given distinguished name
   */
  public void publishUserCertificate(String dn, X509Certificate cert,
                                 Attributes otherAtts) 
    throws NamingException
  {
    publish(USER_CERT_ATTR_TYPE, dn, cert, otherAtts);
  }

  /**
   * Publish the given CA X509Certificate and other Attributes 
   * under the entry name being the given distinguished name
   */
  public void publishCACertificate(String dn, X509Certificate cert,
                                 Attributes otherAtts) 
    throws NamingException
  {
    publish(CA_CERT_ATTR_TYPE, dn, cert, otherAtts);
  }

  /////////////////////////////////////////////////////////////////////////

  private void publish(String attrType, String dn, X509Certificate cert,
                       Attributes otherAtts) throws NamingException
  {
    try 
    {      
      BasicAttribute certAtt = 
        new BasicAttribute(attrType, cert.getEncoded());

      Attributes atts;
      if (otherAtts != null)
        atts = (Attributes)otherAtts.clone();
      else
        atts = new BasicAttributes(true);

      atts.put(certAtt);

      ctx.rebind(dn, null,atts);
    } 
    catch (NamingException e)
    {
      throw e;
    }
    catch (Exception e) 
    {
      e.printStackTrace();
    }
  }

  private X509Certificate getCert(String attrType, String dn) 
    throws NamingException
  {
    X509Certificate retval;

    try 
    {      
      Attributes attrs = ctx.getAttributes(dn);

      if (attrs == null) 
        throw new Exception(dn + " has no attributes");

      byte[] certEnc = (byte[])attrs.get(attrType).get();

      if (certEnc == null)
        throw new Exception(dn + "has no associated certificate");

      ByteArrayInputStream bais = new ByteArrayInputStream(certEnc);
      return (X509Certificate)fact.generateCertificate(bais);
    } 
    catch (NamingException e)
    {
      throw e;
    }
    catch (Exception e) 
    {
      e.printStackTrace();
      return null;
    }
  }
}
