/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.security;

import java.sql.*;
import java.util.*;
import com.silverwrist.util.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

class AceOperations_mysql extends AceOperations
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final int PRINCIPALFLAG_USER = 0;
  private static final int PRINCIPALFLAG_GROUP = 1;

  private static final int ACEFLAG_POSITIVE = 0;
  private static final int ACEFLAG_NEGATIVE = 16;

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private DBUtilities m_utils;                // reference to utilities object

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  AceOperations_mysql(DBConnectionPool pool, DBUtilities utils)
  {
    super(pool);
    m_utils = utils;

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Abstract implementations from class AceOperations
   *--------------------------------------------------------------------------------
   */

  Collection loadPermissions(int aceid) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try
    { // get a connection
      conn = getConnection();

      // create the SQL statement to read all permissions
      stmt = conn.prepareStatement("SELECT perm_nsid, perm_name FROM acedata WHERE aceid = ?;");
      stmt.setInt(1,aceid);
      rs = stmt.executeQuery();
      ArrayList rc = new ArrayList();
      while (rs.next())  // turn them into PropertyKeys
	rc.add(new PropertyKey(rs.getInt(1),rs.getString(2)));
      rc.trimToSize();
      return rc;

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end loadPermissions

  void setPrincipal(int aceid, PrincipalID prid) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    ResultSet rs = null;
    try
    { // get a connection
      conn = getConnection();

      // lock the ACE table for the nonce
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES ace WRITE;");

      // create the update statement that sets the principal
      stmt = conn.prepareStatement("UPDATE ace SET pri = ?, flags = ((flags & ~1) | ?) WHERE aceid = ?;");
      stmt.setInt(1,prid.getID());
      stmt.setInt(2,(prid.isGroup() ? PRINCIPALFLAG_GROUP : PRINCIPALFLAG_USER));
      stmt.setInt(3,aceid);
      if (stmt.executeUpdate()<1)
      { // throw DatabaseException if the ACE isn't there
	DatabaseException de = new DatabaseException(SRMOperations_mysql.class,"SecurityMessages",
						     "ace.notFound");
	de.setParameter(0,String.valueOf(aceid));
	throw de;

      } // end if

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end setPrincipal

  void setNegative(int aceid) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    ResultSet rs = null;
    try
    { // get a connection
      conn = getConnection();

      // lock the ACE table for the nonce
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES ace WRITE;");

      // create the update statement that turns on the appropriate flag.
      stmt = conn.prepareStatement("UPDATE ace SET flags = (flags | 16) WHERE aceid = ?;");
      stmt.setInt(1,aceid);
      if (stmt.executeUpdate()<1)
      { // throw DatabaseException if the ACE isn't there
	DatabaseException de = new DatabaseException(SRMOperations_mysql.class,"SecurityMessages",
						     "ace.notFound");
	de.setParameter(0,String.valueOf(aceid));
	throw de;

      } // end if

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(rs);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end setNegative

  void addPermission(int aceid, PropertyKey perm) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    try
    { // get a connection
      conn = getConnection();

      // lock the ACE data table for the nonce
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES acedata WRITE;");

      // insert the ACE data into the table
      stmt = conn.prepareStatement("INSERT INTO acedata (aceid, perm_nsid, perm_name) VALUES (?, ?, ?);");
      stmt.setInt(1,aceid);
      stmt.setInt(2,perm.getNamespaceID());
      stmt.setString(3,perm.getName());
      stmt.executeUpdate();

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end addPermission

  void removePermission(int aceid, PropertyKey perm) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    try
    { // get a connection
      conn = getConnection();

      // lock the ACE data table for the nonce
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES acedata WRITE;");

      // delete the ACE data from the table
      stmt = conn.prepareStatement("DELETE FROM acedata WhERE aceid = ? AND perm_nsid = ? AND perm_name = ?;");
      stmt.setInt(1,aceid);
      stmt.setInt(2,perm.getNamespaceID());
      stmt.setString(3,perm.getName());
      stmt.executeUpdate();

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end removePermission

  int duplicateAce(PrincipalID prid, boolean negative, Collection perms) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    try
    { // get a connection
      conn = getConnection();

      // throw locks onto the tables
      stmt2 = conn.createStatement();
      stmt2.executeUpdate("LOCK TABLES ace WRITE, acedata WRITE;");

      // create the ACE "head" entry and get the new ACE ID
      stmt = conn.prepareStatement("INSERT INTO ace (pri, flags) VALUES (?, ?);");
      if (prid==null)
	stmt.setNull(1,Types.INTEGER);
      else
	stmt.setInt(1,prid.getID());
      int flags = 0;
      if ((prid!=null) && prid.isGroup())
	flags |= PRINCIPALFLAG_GROUP;
      if (negative)
	flags |= ACEFLAG_NEGATIVE;
      stmt.setInt(2,flags);
      stmt.executeUpdate();
      int new_id = MySQLUtils.getLastInsertInt(conn);

      if (perms.size()>0)
      { // add the ACE data entries
	SQLUtils.shutdown(stmt);
	stmt = conn.prepareStatement("INSERT INTO acedata (aceid, perm_nsid, perm_name) VALUES (?, ?, ?);");
	Iterator it = perms.iterator();
	while (it.hasNext())
	{ // add each permission in turn
	  PropertyKey pkey = (PropertyKey)(it.next());
	  stmt.setInt(1,new_id);
	  stmt.setInt(2,pkey.getNamespaceID());
	  stmt.setString(3,pkey.getName());
	  stmt.executeUpdate();

	} // end while

      } // end if

      return new_id;  // return the new ACE ID

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      MySQLUtils.unlockTables(conn);
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end duplicateAce

} // end class AceOperations_mysql
