// AuthRealm.java
// $Id: AuthRealm.java,v 1.7 1997/02/05 19:04:22 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.auth;

import java.io.*;
import java.util.*;

import w3c.tools.store.*;
import w3c.jigsaw.resources.*;
import w3c.jigsaw.http.*;

public class AuthRealm extends Resource implements ResourceStoreHolder {
    /**
     * The server we are attached to.
     */
    protected static int ATTR_SERVER = -1 ;
    /** 
     * Attribute index - The repository for the realm.
     */
    protected static int ATTR_REPOSITORY = -1 ;
    
    static {
	Attribute a   = null ;
	Class     cls = null ;

	try {
	    cls = Class.forName("w3c.jigsaw.auth.AuthRealm");
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
	// Our repository attribute
	a = new FilenameAttribute("repository"
				  , null
				  , Attribute.EDITABLE|Attribute.MANDATORY);
	ATTR_REPOSITORY = AttributeRegistry.registerAttribute(cls, a);
	// Our server attribute
	a = new ObjectAttribute("server"
				, "w3c.jigsaw.http.httpd"
				, null
				, Attribute.DONTSAVE|Attribute.COMPUTED) ;
	ATTR_SERVER = AttributeRegistry.registerAttribute(cls, a) ;
    }

    /**
     * Our user store.
     */
    protected ResourceStore store = null ;

    /**
     * Acquire and load the users store.
     * Should only be called from a synchronized method.
     */

    protected synchronized void acquireStore() {
	if ( store != null )
	    return  ;
	ResourceStoreManager manager = getServer().getResourceStoreManager() ;
	store = manager.loadResourceStore(this, getRepository()) ;
	return ;
    }

    /**
     * Get our repository.
     */

    public File getRepository() {
	// Get the filename:
	String filename = (String) getValue(ATTR_REPOSITORY, null);
	if ( filename == null )
	    return null;
	// Absolutize it:
	File   file     = new File(filename);
	if ( ! file.isAbsolute() ) 
	    file = new File(getServer().getAuthDirectory(), filename);
	return file;
    }

    /**
     * Get our attached server.
     */

    public httpd getServer() {
	return (httpd) getValue(ATTR_SERVER, null) ;
    }

    /**
     * Load the user having this name.
     * @param name The user's name.
     * @return An instance of AuthUser or <strong>null</strong> if not found.
     */

    public synchronized AuthUser loadUser(String name) {
	acquireStore() ;
	if ( store == null )
	    return null ;
	try {
	    return (AuthUser) store.loadResource(name, null) ;
	} catch (InvalidResourceException ex) {
	    // Emit an error to the errlog:
	    String msg = ("Unable to restore user \""+name+"\" from realm \""
			  + getIdentifier() + "\", details: "+ex.getMessage());
	    getServer().errlog(this, msg);
	}
	// Not reached
	return null;
    }

    /**
     * register this new user in the realm.
     * @param user  The new user.
     */

    public synchronized void registerUser(AuthUser user) {
	acquireStore() ;
	if ( store == null )
	    throw new RuntimeException("no store !") ;
	store.addResource(user) ;
    }

    /**
     * Unregister a user from the realm.
     * @param name The user's name.
     */

    public synchronized void unregisterUser(String name) {
	acquireStore() ;
	if ( store == null )
	    return ;
	store.removeResource(name) ;
	return ;
    }

    /**
     * Enumerate this realm user's name.
     */

    public synchronized Enumeration enumerateUserNames() {
	acquireStore() ;
	if ( store == null )
	    return null ;
	return store.enumerateResourceIdentifiers() ;
    }

    /**
     * create a new empty realm.
     * @param name The name of the realm.
     * @param repository The file to use to store the realm database.
     */

    public static AuthRealm makeRealm(httpd server
				      , String name
				      , File repository) {
	// Un-absolutize repository if possible:
	File   authdir = server.getAuthDirectory();
	String repname = null;
	if ( new File(repository.getParent()).equals(authdir) ) {
	    repname = repository.getName();
	} else {
	    repname = repository.getAbsolutePath();
	}
	Hashtable defs = new Hashtable(3) ;
	defs.put("identifier", name) ;
	defs.put("repository", repname) ;
	AuthRealm realm = new AuthRealm() ;
	realm.initialize(defs) ;
	return realm ;
    }

    /**
     * Delete this authentication realm.
     * This method will remove all traces of the existence of this 
     * authentication realm.
     */

    public void delete() {
	// Erase our resource store repository
	File file = getRepository();
	if ( file != null )
	    file.delete();
	// Finish the deletion:
	super.delete();
    }

    /**
     * Save our store.
     */

    public synchronized void save() {
	if ( store != null )
	    store.save() ;
    }

    /**
     * ResourceStoreHolder implementation - Unload the store.
     * @param st The store to unload.
     * @return A boolean <strong>true</strong> if store was successfully
     *    unloaded.
     */

    public synchronized boolean acceptStoreUnload(ResourceStore st) {
	if ( st != store ) 
	    throw new RuntimeException("ResourceStoreManager inconsistent.");
	return true ;
    }

    /**
     * The resource store asks us to shutdown our associated store.
     * @param st The store to shutdown.
     */

    public void notifyStoreShutdown(ResourceStore st) {
	if ( st != store ) 
	    throw new RuntimeException("ResourceStoreManager inconsistent.");
	store = null ;
    }

    /**
     * ResourceStoreHolder implementation - Save our store.
     * @param st The store to be saved.
     * @return A boolean <strong>true</strong> if saving the store is now
     *    not modified.
     */

    public void notifyStoreStabilize(ResourceStore st) {
	return ;
    }

    public AuthRealm() {
	super() ;
    }
}
