#ifndef REPOS_COMMON_REPOSITORY_H
#define REPOS_COMMON_REPOSITORY_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.
 */

/* Repository interface.
 *
 * There are several repository implementations. Each is derived from
 * this common repository virtual base class.  Current implementations
 * include a local file system repository and a networked repository.
 * Note that while the local file system version doesn't require a
 * stateful connection, the style of the interface is connection
 * oriented in order to support the remote case.
 * 
 * The base class encapsulates the protocol between the user agent and
 * the repository.
 */

/* This used to be internal to FSRepos.h, and it used to be legal to
   set these to something other than SDR_BINARY. Not any more -- use
   the 'cm show object' command to see the objects in texty form. */
#define SDR_CONTENT SDR_BINARY

/* Things that can be enumerated. There is no need for an explicit way
   to enumerate revisions because anyone holding a mutable can make
   the necessary query to obtain all of its revisions. */
#define RENUM_MUTABLES  1
#define RENUM_ENTITIES  2
#define RENUM_USERS     3

#define RGC_START       1
#define RGC_END         2

#define REFLG_NEEDCONVERT  1u	/* enumerate only those objects
				   needing conversion */

struct Repository {
  /*  Serializable ser; */
  
  URI         *uri;		/* URI by which we were opened */

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

  /* FIX: this should be in file repository specific structure */
  OC_bool        doesAccess;      	/* does repository do access checks? */
  const char  *repositoryID;	/* our unique repository ID */
  PubKey      *svrPubKey;	         /* server's public key */
  Mutable     *authMutable;	/* mutable of user who authenticated */
  User        *authUser;       /* content of user who authenticated */
  unsigned int authAccess;      /* access that authUser has on this repos */
  const char  *adminGroup;      /* administrative group (truename) */
  void        *info;		/* type specific info */

  void        *entityCache;	/* ptr to entity cache */
  
  const char *(*GetVersion)(Repository *r);

  void (*Connect)(Repository *r, PubKey *pk);
  void (*Disconnect)(Repository *r);

  /* ENTITY MANAGEMENT: */
  void *(*GetEntity)(Repository *r, const char* mURI, const char *trueName);

  WireDelta *(*GetEntityDelta)(Repository *r, const char* mURI, 
			  const char *trueName, const char *baseTrueName);

  void  (*ReviseEntity)(Repository *r, const char *mURI, 
		        const char *prevTrueName, void *); 
  
  void  (*ReviseEntityDelta)(Repository *r, const char *mURI, 
			     const char *prevTrueName, WireDelta *); 
  
  /* Mutable management methods: */
  Mutable *(*CreateMutable)(Repository *r, const char *name,
                            const char *desc,
			    void *content, 
			    unsigned flags);
			   
  Mutable *(*DupMutable)(Repository *r, const char *name,
			 const char *mURI,
			 OC_bool keepACLs,
			 uint64_t rev,
			 unsigned flags);
			
  Mutable *(*GetMutable)(Repository *r, const char *mURI);
			
  Mutable *(*ReviseMutable)(Repository *r, 
			    const char *mURI,
			    uint64_t curTopRev,
			    void *);

  Mutable *(*SetMutableACL)(Repository *r, 
			    const char *mURI,
			    unsigned which, 
			    const char *aclURI);

  Mutable *(*SetMutableFlags)(Repository *r, 
			      const char *mURI,
			      unsigned flags);

  Mutable *(*SetMutableName)(Repository *r, const char *mURI, 
                             const char *name);

  Mutable *(*SetMutableDesc)(Repository *r, const char *mURI, 
                             const char *description);

  /* Revision management: */
  Revision *(*GetRevision)(Repository *r, const char *mURI, 
			   uint64_t revNo);

  void (*PutRevision)(Repository *r, Revision *rev);

  /* User management methods: */
  Mutable *(*BindUser)(Repository *r, PubKey *pk, unsigned access);

  Mutable *(*GetUser)(Repository *r, PubKey *pk);

  unsigned int (*GetUserAccess)(Repository *r, PubKey *pk);
		     
  Mutable *(*RebindUser)(Repository *r, const char *mURI, PubKey *newkey);

  /* Perform a clean shutdown of the repository server. I find that I
     want to be able to do this remotely, and sending SIGHUP remotely
     is not a good solution. More broadly, this is needed for remote
     administration. */
  void (*ShutDown)(Repository *r);

  /* Return truenames of up to two parents of the passed
     object. Currently works only for Change objects. Returns NULL for
     all other object types. */
  TnVec *(*GetParents)(Repository *r, const char* mURI, const char *trueName);

  /* Set the compression method on the wire. The only method currently
     supported is gzip. After compression is set, all subsequent
     requests and replies will be transmitted compressed. The reply to
     SetCompression is sent BEFORE the change in compression level
     takes effect. */
  void  (*SetCompression)(Repository *r, const char *method, unsigned level);

  TnVec *(*Enumerate)(Repository *r, const char *host, unsigned obType, unsigned flags);

  void (*GarbageCollect)(Repository *r, unsigned gcArg);
};

/* macros for all repository methods: */

#define repos_GetVersion(r)          r->GetVersion(r)
#define repos_Connect(r,pk)          r->Connect(r,pk)
#define repos_Disconnect(r)          r->Disconnect(r)

/* Entity management:  */
extern void *repos_GetEntity(Repository *r, const char *mURI, const char *tn);
extern void repos_ReviseEntity(Repository *r, const char *mURI, 
			       const char *prevTrueName, void *); 

#define repos_GetEntityDelta(r,m,s1,s2)\
                                       r->GetEntityDelta(r,m,s1,s2)
#define repos_ReviseEntityDelta(r,m,s1,wd)\
                                       r->ReviseEntityDelta(r,m,s1,wd)

/* Mutable commands: */
extern Mutable *repos_GetMutable(Repository *r, const char *uri);
		
#define repos_CreateMutable(r,nm,d,e,f) \
                                       r->CreateMutable(r,nm,d,e,f)
#define repos_DupMutable(r,nm,m,k,v,f) r->DupMutable(r,nm,m,k,v,f)
#define repos_ReviseMutable(r,m,rev,e) r->ReviseMutable(r,m,rev,e)
#define repos_GetRevision(r,m,f)       r->GetRevision(r,m,f)
#define repos_PutRevision(r,rev)       r->PutRevision(r,rev)
#define repos_SetMutableACL(r,m,w,t)   r->SetMutableACL(r,m,w,t)
#define repos_SetMutableFlags(r,m,f)   r->SetMutableFlags(r,m,f)
#define repos_SetMutableName(r,m,n)    r->SetMutableName(r,m,n)
#define repos_SetMutableDesc(r,m,d)    r->SetMutableDesc(r,m,d)

/* User commands: */
#define repos_BindUser(r,pk,f)         r->BindUser(r,pk,f)
#define repos_RebindUser(r,m,new)      r->RebindUser(r,m,new)
#define repos_GetUser(r,pk)            r->GetUser(r,pk)
#define repos_GetUserAccess(r,pk)      r->GetUserAccess(r,pk)
#define repos_ShutDown(r)              r->ShutDown(r)
#define repos_GetParents(r,m,e)        r->GetParents(r,m,e)
#define repos_SetCompression(r,s,u)    r->SetCompression(r,s,u)
#define repos_Enumerate(r,s,u1,u2)     r->Enumerate(r,s,u1,u2)
#define repos_GarbageCollect(r,arg)    r->GarbageCollect(r,arg)

/** repository_open(): given a URI identifying a repository, perform the
    appropriate actions to open the repository and authenticate the
    client. */
Repository *repository_open(const char *argURI);
Repository *repository_dup(Repository *);

/* SUPPORTING ROUTINES FOR ALL KNOWN TYPES OF REPOSITORIES: */

/* Repository creation is done separately and currently can
 * only be done locally (for file-system based repositories) */
extern Repository *fsrepos_create(const char *path, const char *fstype,
				  PubKey *admin);

extern Repository *diskrepos_create(const char *path, const char *fstype,
				    PubKey *admin);

/* Once created, repositories must be initialized */
extern void authrepository_init(Repository *r);
extern void diskrepos_init(Repository *, const char *storeType);
extern void fsrepos_init(Repository *);
extern void sxdfsrepository_init(Repository *);
extern void cachedfilerepository_init(Repository *);
extern void netrepository_init(Repository *);

/* an 'authrepository' is a special-case wrapper repository,
 * usually wrapped around a file-system based repository */
extern Repository *authrepository_wrap(Repository *, PubKey *);

/* Supporting procedures: */
OC_bool repos_validate_pubkey(const char *reposName, PubKey *pk);
Serializable *repos_GetMutableContent(Repository *r, Mutable *m);
Revision *repos_GetTopRev(Repository *r, Mutable *m);
extern char *ReadOneLineFile(const char *path);
extern int WriteOneLineFile(const char *path, const char *string);
ObVec *repos_GetRevisions(Repository *r, const char *mURI,
                          uint64_t first, uint64_t last);
ObVec *repos_GetEntityVec(Repository *r, const char *mURI, StrVec *names);

#define CM_REPOS_FS   "fs"
#define CM_REPOS_GZFS "gzfs"
#define CM_REPOS_FILE "file"
#define CM_REPOS_SSL  "opencm"
#define CM_REPOS_AUTH "auth"
#define CM_REPOS_SXD2  "sxd2"
#define CM_VERSION_FILE "OPENCM-VERSION"

/* Here's how we check user permissions for different commands */
OC_bool repos_user_in_group(Repository *r, const char *gname, unsigned access);
OC_bool repos_check_ACL(Repository *r, Mutable *m, unsigned access);

#define repos_fail_access(r, a) \
  ( (r->authAccess & ACC_REVOKED) || r->authAccess < (a) )

#define repos_fail_ACL(r, m, a) \
  ( repos_check_ACL(r, m, a) == 0 )

void repos_FlushCache(Repository *r);

#define SDR_WIRE  SDR_BINARY

#endif /* REPOS_COMMON_REPOSITORY_H */
