#ifndef OBSPACE_OBSPACE_H
#define OBSPACE_OBSPACE_H

/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * Object Space interface.
 *
 * An "object space" encapsulates a view on some subset of OpenCM
 * objects as seen by a particular authenticated user (i.e. user key).
 * In a client operating on behalf of the Fred key, ObSpaces are
 * typically layered as follows:
 *
 *    universal_ObSpace.fred
 *      Cache_ObSpace.*
 *      NetServer_ObSpace(S0).fred -> Local_ObSpace(s0).fred
 *      NetServer_ObSpace(S1).fred -> Local_ObSpace(s1).fred
 *      ...
 *      NetServer_ObSpace(SN).fred -> Local_ObSpace(sN).fred
 *
 * where the arrows represent net connections. The universal ObSpace
 * builds subordinate connections on demand, and provides managment of
 * a local cache ObSpace. Each Local_ObSpace manages the object space
 * originating at a single repository.
 *
 * DIFFERENCES FROM THE PREVIOUS DESIGN
 *
 * Unlike the previous "repository" design, the Repos ObSpace
 * generically wraps any of several possible local store
 * implementations, and each of these stores may be locally accessed
 * via several different Repos_ObSpace instances at a time operating
 * on behalf of different processes. 
 *
 * Also unlike the previous design, the flow of calls and control is
 * strictly downward. In the older design, the FSRepository performed
 * recursive upcalls into a wrapping AuthRepository that performed the
 * access check. In the new design, Each Local_ObSpace performs its
 * own access checks by performing a *downward* call into
 * universal_ObSpace.anon.
 *
 * The design of access control checks is also somewhat revised. In
 * any ACL system, the access lists themselves suffer from an awkward
 * quandry: they are generally not implemented as first class objects
 * insofar as access checks are concerned. In OpenCM, a "group" is a
 * mutable object like any other mutable object, itself has a modify
 * group and a read group for access control purposes. Groups are
 * recursively defined, which leads to a somewhat odd consequence: a
 * user can be a member of a group via a chain of inclusions even
 * though they may not have authority to read or modify the
 * intervening groups. This raises the question: what are the access
 * rules for group membership tests?
 *
 * In the current OpenCM implementation, we have resolved this by
 * declaring (with some reservations) that the 
 *
 *        ObSpace_IsMember(group-mutable, ?member-mutable) -> bool
 *
 * operation can be performed by ANY user. This allows "fishing
 * expeditions", but resolves the problem of how servers can resolve
 * recursive membership questions without prior server to server
 * introductions. In particular, it means that an OpenCM server can
 * internally maintain a single common space
 *
 *       Union_ObSpace.anon  authObSpace
 *
 * and can resolve access decisions by calls to authObSpace.IsMember().
 */

typedef struct ObSpace ObSpace;
struct ObSpace {
  char *type;			/* descriptive */

  Mutable *authMutable;		/* mutable of authenticated user */
  User *authUser;		/* user object of authenticated user */
  unsigned int authAccess;	/* access rights for this user on this
				   ObSpace */

  const char *(*GetVersion)(ObSpace *s);

  void (*Connect)(ObSpace *s, PubKey *pk, OC_bool exclusive);
  void (*Disconnect)(ObSpace *s);

  /* ENTITY MANAGEMENT */
  Buffer *(*GetFrozen)(ObSpace *s, const char* mURI, const char *trueName);

  void  (*ReviseFrozen)(ObSpace *s, const char *mURI, 
		        const char *hint, Buffer *); 

  /* MUTABLE MANAGEMENT */
  Mutable *(*CreateMutable)(ObSpace *s, const char *name,
                            const char *desc,
			    void *content, 
			    unsigned flags);
			   
  /* Do we still need this? */
  Mutable *(*DupMutable)(ObSpace *s, const char *name,
			 const char *mURI,
			 OC_bool keepACLs,
			 uint64_t rev,
			 unsigned flags);
			
  Mutable *(*GetMutable)(ObSpace *s, const char *mURI);
			
  /* ReviseMutable is used when a new revision is being
     added. PutMutable should be used in all other cases. PutMutable
     is also used for mutable replication. */
  Mutable *(*PutMutable)(ObSpace *s, 
			 const char *mURI,
			 Buffer *);


  Mutable *(*ReviseMutable)(ObSpace *s, 
			    const char *mURI,
			    uint64_t curTopRev,
			    Buffer *);

  /* REVISION MANAGEMENT */
  Revision *(*GetRevision)(ObSpace *s, const char *mURI, 
			   uint64_t revNo);

  void (*PutRevision)(ObSpace *s, Revision *rev);

  /* USER MANAGEMENT */
  Mutable *(*GetUser)(ObSpace *s, PubKey *pk);
  unsigned int (*GetUserAccess)(ObSpace *s, PubKey *pk);
		     
  /* FIX: BindUser is really SetUserAccess. */
  Mutable *(*BindUser)(ObSpace *s, PubKey *pk, unsigned access);
  /* RebindUser differs from PutMutable because a user can rebind
     their own public key. */
  Mutable *(*RebindUser)(ObSpace *s, const char *mURI, PubKey *newkey);
};

#define ObSpace_GetVersion(s) \
            ((ObSpace *)s)->GetVersion((ObSpace *)s)
#define ObSpace_Connect(s, pk, excl) \
            ((ObSpace *)s)->Connect((ObSpace *)s, pk, excl)
#define ObSpace_Disconnect(s) \
            ((ObSpace *)s)->Disconnect((ObSpace *)s)

#define ObSpace_GetFrozen(s, mURI, trueName) \
            ((ObSpace *)s)->GetFrozen((ObSpace *)s, mURI, trueName)

#define ObSpace_GetMutable(s, mURI) \
            ((ObSpace *)s)->GetMutable((ObSpace *)s, mURI)

#define ObSpace_GetRevision(s, mURI, revNo) \
            ((ObSpace *)s)->GetRevision((ObSpace *)s, mURI, revNo)

#define ObSpace_GetUser(s, pk) \
            ((ObSpace *)s)->GetUser((ObSpace *)s, pk)

typedef struct LocalObSpace LocalObSpace;
struct LocalObSpace {
  struct ObSpace base;

  const char  *storeType;	/* store type */
  Store       *store;		/* underlying store, if any */

  URI         *uri;		/* URI by which we were opened */
  const char  *repositoryID;	/* our unique repository ID */
  PubKey      *pubKey;		/* repository's public key */
  const char  *adminGroup;      /* administrative group (truename) */

  const char  *activityLock;	/* file name of our activity lock */
};

/* INITIALIZERS */
extern void LocalObSpace_init(LocalObSpace *ls, const char *storeType);

/* HELPERS */
#define ObSpace_GetMutableContent(s, mutable) \
        ObSpaceH_GetMutableContent((ObSpace *)s, mutable)
Buffer *ObSpaceH_GetMutableContent(ObSpace *s, Mutable *m);

#define ObSpace_GetTopRev(s, m) \
        ObSpaceH_GetTopRev((ObSpace *)s, m)
Revision *ObSpaceH_GetTopRev(ObSpace *s, Mutable *m);

#endif /* OBSPACE_OBSPACE_H */
