//////////////////////////////////////////////////////////////////////////// 
// 
// 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.util.Arrays;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Iterator;
import java.util.Hashtable;
import javax.net.ssl.SSLSessionContext;

/**
 * An implementation of an in-memory session cache.
 *
 * As long as a reference to a socket factory or a serversocket factory
 * is held, the cache is active. All sessions in the cache are considered
 * valid unless invalidate() is explicitly called on the session.
 */
final class SessionCache implements SSLSessionContext
{
  private Hashtable tab;

  SessionCache()
  {
    this.tab = new Hashtable();
  }

  public javax.net.ssl.SSLSession getSession(byte[] id)
  {
    SSLSession retval = (SSLSession)this.tab.get(new SessionId(id));
    if (retval != null)
    {
      if (retval.isValid())
      {
        return retval;
      } 
    }

    return null;
  }

  public Enumeration getIds()
  {
    Vector temp = new Vector(this.tab.size());
    Iterator it = tab.keySet().iterator();
    while (it.hasNext())
    {
      temp.addElement(((SessionId)it.next()).getId());
    }
    return temp.elements();
  }

  ///////////////////////////////////////////////////////////////
  // methods not defined in SSLSessionContext
  ///////////////////////////////////////////////////////////////

  void clear()
  {
    this.tab.clear();
  }

  void addSession(SSLSession sess)
  {
    this.tab.put(new SessionId(sess.getId()), sess);
  }

  SSLSession getFirstValidSession()
  {
    SSLSession retval;
    Iterator it = this.tab.keySet().iterator();
    while (it.hasNext())
    {
      retval = (SSLSession)this.tab.get((SessionId)it.next());
      if (retval.isValid())
        return retval;
    }
    return null;
  }

  /**
   * Holder for session ids to be used as keys in a Hashtable
   */
  final class SessionId
  {
    private byte[] id;
  
    SessionId(byte[] id)
    {
      this.id = id;
    }
  
    byte[] getId()
    {
      return this.id;
    }
  
    public boolean equals(Object obj)
    {
      try
      {
        return Arrays.equals(this.id, ((SessionId)obj).getId());
      }
      catch (ClassCastException e)
      {
        return false;
      }
    }
  
    public int hashCode()
    {
      int retval = 0;
      int shift = 0;
      for (int i=0; i<this.id.length; i++)
      {
        retval |= (int)(this.id[i] << 8*shift);
        shift++;
        if (shift == 4)
          shift = 0;
      }
      return retval;
    }
  }
}
