summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/dse.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/dse.c')
-rw-r--r--ldap/servers/slapd/dse.c2304
1 files changed, 2304 insertions, 0 deletions
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
new file mode 100644
index 00000000..31c23e3b
--- /dev/null
+++ b/ldap/servers/slapd/dse.c
@@ -0,0 +1,2304 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * dse.c - DSE (DSA-Specific Entry) persistent storage.
+ *
+ * The DSE store is an LDIF file contained in the file
+ * INSTANCEDIR/config/XXX.ldif, where INSTANCEDIR is
+ * the directory of the server instance, and XXX is
+ * dfined by the caller of dse_new.
+ *
+ * In core, the DSEs are stored in an AVL tree, keyed on
+ * DN. Whenever a modification is made to a DSE, the
+ * in-core entry is updated, then dse_write_file() is
+ * called to commit the changes to disk.
+ *
+ * This is designed for a small number of DSEs, say
+ * a maximum of 10 or 20. If large numbers of DSEs
+ * need to be stored, this approach of writing out
+ * the entire contents on every modification will
+ * be insufficient.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <prio.h>
+#include <prcountr.h>
+#include "slap.h"
+
+#if !defined (_WIN32)
+#include <pwd.h>
+#endif /* _WIN32 */
+
+/* #define SLAPI_DSE_DEBUG */ /* define this to force trace log */
+ /* messages to always be logged */
+
+#ifdef SLAPI_DSE_DEBUG
+#define SLAPI_DSE_TRACELEVEL LDAP_DEBUG_ANY
+#else /* SLAPI_DSE_DEBUG */
+#define SLAPI_DSE_TRACELEVEL LDAP_DEBUG_TRACE
+#endif /* SLAPI_DSE_DEBUG */
+
+#define STOP_TRAVERSAL -2
+
+/* This is returned by dupentry_replace if the duplicate entry was found and
+ replaced. This is returned up through the avl_insert() in
+ dse_replace_entry(). Otherwise, if avl_insert() returns 0, the
+ entry was added i.e. a duplicate was not found.
+*/
+#define DSE_ENTRY_WAS_REPLACED -3
+/* This is returned by dupentry_merge if the duplicate entry was found and
+ merged. This is returned up through the avl_insert() in dse_add_entry_pb().
+ Otherwise, if avl_insert() returns 0, the
+ entry was added i.e. a duplicate was not found.
+*/
+#define DSE_ENTRY_WAS_MERGED -4
+
+/* some functions can be used either from within a lock or "standalone" */
+#define DSE_USE_LOCK 1
+#define DSE_NO_LOCK 0
+
+struct dse_callback
+{
+ int operation;
+ int flags;
+ Slapi_DN *base;
+ int scope;
+ char *filter; /* NULL means match all entries */
+ Slapi_Filter *slapifilter; /* NULL means match all entries */
+ int (*fn)(Slapi_PBlock *,Slapi_Entry *,Slapi_Entry *,int*,char*,void *);
+ void *fn_arg;
+ struct dse_callback *next;
+};
+
+struct dse
+{
+ char *dse_filename; /* these are the primary files which get read from */
+ char *dse_tmpfile; /* and written to when changes are made via LDAP */
+ char *dse_fileback; /* contain the latest info, just before a new change */
+ char *dse_filestartOK; /* contain the latest info with which the server has successfully started */
+ Avlnode *dse_tree;
+ struct dse_callback *dse_callback;
+ PRRWLock *dse_rwlock; /* a read-write lock to protect the whole dse backend */
+ char **dse_filelist; /* these are additional read only files used to */
+ /* initialize the dse */
+ int dse_is_updateable; /* if non-zero, this DSE can be written to */
+ int dse_readonly_error_reported; /* used to ensure that read-only errors are logged only once */
+};
+
+struct dse_node
+{
+ Slapi_Entry *entry;
+};
+
+/* search set stuff - used to pass search results to the frontend */
+typedef struct dse_search_set
+{
+ DataList dl;
+ int current_entry;
+} dse_search_set;
+
+static int dse_permission_to_write(struct dse* pdse, int loglevel);
+static int dse_write_file_nolock(struct dse* pdse);
+static int dse_apply_nolock(struct dse* pdse, IFP fp, caddr_t arg);
+static int dse_replace_entry( struct dse* pdse, Slapi_Entry *e, int write_file, int use_lock );
+static dse_search_set* dse_search_set_new ();
+static void dse_search_set_delete (dse_search_set *ss);
+static void dse_search_set_clean (dse_search_set *ss);
+static void dse_free_entry (void **data);
+static void dse_search_set_add_entry (dse_search_set *ss, Slapi_Entry *e);
+static Slapi_Entry* dse_search_set_get_next_entry (dse_search_set *ss);
+static int dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb);
+static struct dse_node *dse_find_node( struct dse* pdse, const Slapi_DN *dn );
+
+/*
+ richm: In almost all modes e.g. db2ldif, ldif2db, etc. we do not need/want
+ to write out the dse.ldif and ldbm.ldif files. The only mode which really
+ needs to write out the file is the regular server mode. The variable
+ dont_ever_write_dse_files tells dse_write_file_nolock whether or not to write
+ the .ldif file for the entry. The default is 1, which means never write the
+ file. The server, when it starts up in regular mode, must call
+ dse_unset_dont_ever_write_dse_files() to enable this file to be written
+*/
+static int dont_ever_write_dse_files = 1;
+
+/* Forward declarations */
+static int entry_dn_cmp( caddr_t d1, caddr_t d2 );
+static int dupentry_disallow( caddr_t d1, caddr_t d2 );
+static int dupentry_merge( caddr_t d1, caddr_t d2 );
+static int dse_write_entry( caddr_t data, caddr_t arg );
+static int ldif_record_end( char *p );
+static int dse_call_callback(struct dse* pdse, Slapi_PBlock *pb, int operation, int flags, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char* returntext);
+
+/*
+ * Map a DN onto a dse_node.
+ * Returns NULL if not found.
+ * You must have a read or write lock on the dse_rwlock while
+ * using the returned node.
+ */
+static struct dse_node *
+dse_find_node( struct dse* pdse, const Slapi_DN *dn )
+{
+ struct dse_node *n = NULL;
+ if ( NULL != dn )
+ {
+ struct dse_node searchNode;
+ Slapi_Entry *fe= slapi_entry_alloc();
+ slapi_entry_init(fe, NULL, NULL);
+ slapi_entry_set_sdn(fe,dn);
+ searchNode.entry= fe;
+
+ n = (struct dse_node *)avl_find( pdse->dse_tree, &searchNode, entry_dn_cmp );
+
+ slapi_entry_free(fe);
+ }
+ return n;
+}
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(dse_entries_exist);
+
+/*
+ * Map a DN onto a real Entry.
+ * Returns NULL if not found.
+ */
+static Slapi_Entry *
+dse_get_entry_copy( struct dse* pdse, const Slapi_DN *dn, int use_lock )
+{
+ Slapi_Entry *e= NULL;
+ struct dse_node *n;
+
+ if (use_lock == DSE_USE_LOCK)
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+
+ n = dse_find_node( pdse, dn );
+ if(n!=NULL)
+ {
+ e = slapi_entry_dup(n->entry);
+ }
+
+ if (use_lock == DSE_USE_LOCK)
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ return e;
+}
+
+static struct dse_callback *
+dse_callback_new(int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ struct dse_callback *p= NULL;
+ p = (struct dse_callback *)slapi_ch_malloc(sizeof(struct dse_callback));
+ if (p!=NULL) {
+ p->operation= operation;
+ p->flags = flags;
+ p->base= slapi_sdn_dup(base);
+ p->scope= scope;
+ if ( NULL == filter ) {
+ p->filter= NULL;
+ p->slapifilter= NULL;
+ } else {
+ p->filter= slapi_ch_strdup(filter);
+ p->slapifilter= slapi_str2filter( p->filter );
+ filter_normalize(p->slapifilter);
+ }
+ p->fn= fn;
+ p->fn_arg= fn_arg;
+ p->next= NULL;
+ }
+ return p;
+}
+
+static void
+dse_callback_delete(struct dse_callback **pp)
+{
+ if (pp!=NULL) {
+ slapi_sdn_free(&((*pp)->base));
+ slapi_ch_free((void**)&((*pp)->filter));
+ slapi_filter_free((*pp)->slapifilter,1);
+ slapi_ch_free((void**)pp);
+ }
+}
+
+static void
+dse_callback_deletelist(struct dse_callback **pp)
+{
+ if(pp!=NULL)
+ {
+ struct dse_callback *p, *n;
+ for(p= *pp;p!=NULL;)
+ {
+ n= p->next;
+ dse_callback_delete(&p);
+ p= n;
+ }
+ }
+}
+
+/*
+ Makes a copy of the entry passed in, so it's const
+*/
+static struct dse_node *
+dse_node_new(const Slapi_Entry *entry)
+{
+ struct dse_node *p= NULL;
+ p= (struct dse_node *)slapi_ch_malloc(sizeof(struct dse_node));
+ if(p!=NULL)
+ {
+ p->entry= slapi_entry_dup(entry);
+ }
+ if(!counters_created)
+ {
+ PR_CREATE_COUNTER(dse_entries_exist,"DSE","entries","");
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(dse_entries_exist);
+ return p;
+}
+
+static void
+dse_node_delete(struct dse_node **pp)
+{
+ slapi_entry_free((*pp)->entry);
+ slapi_ch_free((void **)&(*pp));
+ PR_DECREMENT_COUNTER(dse_entries_exist);
+}
+
+static void
+dse_callback_addtolist(struct dse_callback **pplist, struct dse_callback *p)
+{
+ if(pplist!=NULL)
+ {
+ p->next= NULL;
+ if(*pplist==NULL)
+ {
+ *pplist= p;
+ }
+ else
+ {
+ struct dse_callback *t= *pplist;
+ for(;t->next!=NULL;t= t->next);
+ t->next= p;
+ }
+ }
+}
+
+static void
+dse_callback_removefromlist(struct dse_callback **pplist, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ if (pplist != NULL) {
+ struct dse_callback *t= *pplist;
+ struct dse_callback *prev= NULL;
+ for(; t!=NULL; ) {
+ if ((t->operation == operation) && (t->flags == flags) &&
+ (t->fn == fn) && (scope == t->scope) &&
+ (slapi_sdn_compare(base,t->base) == 0) &&
+ (( NULL == filter && NULL == t->filter ) ||
+ (strcasecmp(filter, t->filter) == 0))) {
+ if (prev == NULL) {
+ *pplist= t->next;
+ } else {
+ prev->next= t->next;
+ }
+ dse_callback_delete(&t);
+ t= NULL;
+ } else {
+ prev= t;
+ t= t->next;
+ }
+ }
+ }
+}
+
+/*
+ * Create a new dse structure.
+ */
+struct dse *
+dse_new( char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir)
+{
+ struct dse *pdse= NULL;
+ const char *config_sub_dir = "config";
+ char *id = config_get_instancedir();
+ char *realconfigdir = NULL;
+
+ if (configdir!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(configdir)+1);
+ strcpy(realconfigdir, configdir);
+ } else if (id!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(id)+strlen(config_sub_dir)+3);
+ sprintf(realconfigdir, "%s/%s", id, config_sub_dir);
+ }
+ if(realconfigdir!=NULL)
+ {
+ pdse= (struct dse *)slapi_ch_calloc(1, sizeof(struct dse));
+ if(pdse!=NULL)
+ {
+ pdse->dse_rwlock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,"dse lock");
+ /* Set the full path name for the config DSE entry */
+ if (!strstr(filename, realconfigdir))
+ {
+ pdse->dse_filename = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( filename ) + 3 );
+ sprintf( pdse->dse_filename, "%s/%s", realconfigdir, filename );
+ }
+ else
+ pdse->dse_filename = slapi_ch_strdup(filename);
+
+ if (!strstr(tmpfilename, realconfigdir)) {
+ pdse->dse_tmpfile = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( tmpfilename ) + 3 );
+ sprintf( pdse->dse_tmpfile, "%s/%s", realconfigdir, tmpfilename );
+ }
+ else
+ pdse->dse_tmpfile = slapi_ch_strdup(tmpfilename);
+
+ if ( backfilename != NULL )
+ {
+ if (!strstr(backfilename, realconfigdir)) {
+ pdse->dse_fileback = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( backfilename ) + 3 );
+ sprintf( pdse->dse_fileback, "%s/%s", realconfigdir, backfilename );
+ }
+ else
+ pdse->dse_fileback = slapi_ch_strdup(backfilename);
+ }
+ else
+ pdse->dse_fileback = NULL;
+
+ if ( startokfilename != NULL )
+ {
+ if (!strstr(startokfilename, realconfigdir)) {
+ pdse->dse_filestartOK = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( startokfilename ) + 3 );
+ sprintf( pdse->dse_filestartOK, "%s/%s", realconfigdir, startokfilename );
+ }
+ else
+ pdse->dse_filestartOK = slapi_ch_strdup(startokfilename);
+ }
+ else
+ pdse->dse_filestartOK = NULL;
+
+ pdse->dse_tree= NULL;
+ pdse->dse_callback= NULL;
+ pdse->dse_is_updateable = dse_permission_to_write(pdse,
+ SLAPI_LOG_TRACE);
+ }
+ slapi_ch_free( (void **) &realconfigdir );
+ }
+ slapi_ch_free( (void **) &id );
+ return pdse;
+}
+
+/*
+ * Create a new dse structure with a file list
+ */
+struct dse *
+dse_new_with_filelist(char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir,
+ char **filelist)
+{
+ struct dse *newdse = dse_new(filename, tmpfilename, backfilename, startokfilename, configdir);
+ newdse->dse_filelist = filelist;
+ return newdse;
+}
+
+static int
+dse_internal_delete_entry( caddr_t data, caddr_t arg )
+{
+ struct dse_node *n = (struct dse_node *)data;
+ dse_node_delete(&n);
+ return 0;
+}
+
+/*
+ * Get rid of a dse structure.
+ */
+int
+dse_deletedse(Slapi_PBlock *pb)
+{
+ struct dse *pdse = NULL;
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &pdse);
+
+ if (pdse)
+ {
+ int nentries = 0;
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ slapi_ch_free((void **)&(pdse->dse_filename));
+ slapi_ch_free((void **)&(pdse->dse_tmpfile));
+ slapi_ch_free((void **)&(pdse->dse_fileback));
+ slapi_ch_free((void **)&(pdse->dse_filestartOK));
+ dse_callback_deletelist(&pdse->dse_callback);
+ nentries = avl_free(pdse->dse_tree, dse_internal_delete_entry);
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ PR_DestroyRWLock(pdse->dse_rwlock);
+ slapi_ch_free((void **)&pdse);
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "Removed [%d] entries from the dse tree.\n",
+ nentries,0,0 );
+ }
+
+ /* data is freed, so make sure no one tries to use it */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, NULL);
+
+ return 0;
+}
+
+static char* subordinatecount = "numsubordinates";
+
+/*
+ * Get the number of subordinates for this entry.
+ */
+static size_t
+dse_numsubordinates(Slapi_Entry *entry)
+{
+ int ret= 0;
+ Slapi_Attr *read_attr = NULL;
+ size_t current_sub_count = 0;
+
+ /* Get the present value of the subcount attr, or 0 if not present */
+ ret = slapi_entry_attr_find(entry,subordinatecount,&read_attr);
+ if (0 == ret)
+ {
+ /* decode the value */
+ Slapi_Value *sval;
+ slapi_attr_first_value( read_attr,&sval );
+ if (sval!=NULL)
+ {
+ const struct berval *bval = slapi_value_get_berval( sval );
+ if( NULL != bval )
+ current_sub_count = atol(bval->bv_val);
+ }
+ }
+ return current_sub_count;
+}
+
+/*
+ * Update the numsubordinates count.
+ * mod_op is either an Add or Delete.
+ */
+static void
+dse_updateNumSubordinates(Slapi_Entry *entry, int op)
+{
+ int ret= 0;
+ int mod_op = 0;
+ Slapi_Attr *read_attr = NULL;
+ size_t current_sub_count = 0;
+ int already_present = 0;
+
+ /* For now, we're only interested in subordinatecount.
+ We first examine the present value for the attribute.
+ If it isn't present and we're adding, we assign value 1 to the attribute and add it.
+ If it is present, we increment or decrement depending upon whether we're adding or deleting.
+ If the value after decrementing is zero, we remove it.
+ */
+
+ /* Get the present value of the subcount attr, or 0 if not present */
+ ret = slapi_entry_attr_find(entry,subordinatecount,&read_attr);
+ if (0 == ret)
+ {
+ /* decode the value */
+ Slapi_Value *sval;
+ slapi_attr_first_value( read_attr,&sval );
+ if (sval!=NULL)
+ {
+ const struct berval *bval = slapi_value_get_berval( sval );
+ if (bval!=NULL)
+ {
+ already_present = 1;
+ current_sub_count = atol(bval->bv_val);
+ }
+ }
+ }
+
+ /* are we adding ? */
+ if ( (SLAPI_OPERATION_ADD == op) && !already_present)
+ {
+ /* If so, and the parent entry does not already have a subcount attribute, we need to add it */
+ mod_op = LDAP_MOD_ADD;
+ }
+ else
+ {
+ if (SLAPI_OPERATION_DELETE == op)
+ {
+ if (!already_present)
+ {
+ /* This means that something is wrong---deleting a child but no subcount present on parent */
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "numsubordinates assertion failure\n" );
+ return;
+ }
+ else
+ {
+ if (current_sub_count == 1)
+ {
+ mod_op = LDAP_MOD_DELETE;
+ }
+ else
+ {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+ }
+ else
+ {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+
+ /* Now compute the new value */
+ if (SLAPI_OPERATION_ADD == op)
+ {
+ current_sub_count++;
+ }
+ else
+ {
+ current_sub_count--;
+ }
+ {
+ char value_buffer[20]; /* enough digits for 2^64 children */
+ struct berval *vals[2];
+ struct berval val;
+ vals[0] = &val;
+ vals[1] = NULL;
+ sprintf(value_buffer,"%u",current_sub_count);
+ val.bv_val = value_buffer;
+ val.bv_len = strlen (val.bv_val);
+ switch(mod_op)
+ {
+ case LDAP_MOD_ADD:
+ attrlist_merge( &entry->e_attrs, subordinatecount, vals);
+ break;
+ case LDAP_MOD_REPLACE:
+ attrlist_replace( &entry->e_attrs, subordinatecount, vals);
+ break;
+ case LDAP_MOD_DELETE:
+ attrlist_delete( &entry->e_attrs, subordinatecount);
+ break;
+ }
+ }
+}
+
+/* the write lock should always be acquired before calling this function */
+static void
+dse_updateNumSubOfParent(struct dse *pdse, const Slapi_DN *child, int op)
+{
+ Slapi_DN parent;
+ slapi_sdn_init(&parent);
+ slapi_sdn_get_parent(child, &parent);
+ if ( !slapi_sdn_isempty(&parent) )
+ {
+ /* no lock because caller should already have the write lock */
+ Slapi_Entry *parententry= dse_get_entry_copy( pdse, &parent, DSE_NO_LOCK );
+ if( parententry!=NULL )
+ {
+ /* Decrement the numsubordinate count of the parent entry */
+ dse_updateNumSubordinates(parententry, op);
+ /* no lock because caller should always have the write lock */
+ dse_replace_entry( pdse, parententry, 0, DSE_NO_LOCK );
+ slapi_entry_free(parententry);
+ }
+ }
+ slapi_sdn_done(&parent);
+}
+
+static int
+dse_read_one_file(struct dse *pdse, const char *filename, Slapi_PBlock *pb,
+ int primary_file )
+{
+ Slapi_Entry *e= NULL;
+ char *entrystr= NULL;
+ char *buf = NULL;
+ char *lastp = NULL;
+ int rc= 0; /* Fail */
+ PRInt32 remaining;
+ PRInt32 nr = 0;
+ PRFileInfo prfinfo;
+ PRFileDesc *prfd = 0;
+
+ if ( (NULL != pdse) && (NULL != filename) )
+ {
+ if ( (rc = PR_GetFileInfo( filename, &prfinfo )) != PR_SUCCESS )
+ {
+ /* the "real" file does not exist; see if there is a tmpfile */
+ if ( pdse->dse_tmpfile &&
+ PR_GetFileInfo( pdse->dse_tmpfile, &prfinfo ) == PR_SUCCESS ) {
+ rc = PR_Rename(pdse->dse_tmpfile, filename);
+ if (rc == PR_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s was restored from backup %s\n",
+ filename, pdse->dse_tmpfile);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s was not restored from backup %s, error %d\n",
+ filename, pdse->dse_tmpfile, rc);
+ rc = 0;
+ }
+ } else {
+ rc = 0; /* fail */
+ }
+ }
+ if ( (rc = PR_GetFileInfo( filename, &prfinfo )) != PR_SUCCESS )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s could not be accessed, error %d\n",
+ filename, rc);
+ rc = 0; /* Fail */
+ }
+ else if (( prfd = PR_Open( filename, PR_RDONLY, SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s could not be read. "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ filename,
+ PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ rc = 0; /* Fail */
+ }
+ else
+ {
+ int done= 0;
+ /* read the entire file into core */
+ buf = slapi_ch_malloc( prfinfo.size + 1 );
+ remaining = prfinfo.size;
+ if (( nr = slapi_read_buffer( prfd, buf, prfinfo.size )) < 0 )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "Could only read %d of %d bytes from config file %s\n",
+ nr, prfinfo.size, filename);
+ rc = 0; /* Fail */
+ done= 1;
+ }
+
+ (void)PR_Close( prfd );
+ buf[ nr ] = '\0';
+
+ if(!done)
+ {
+ int dont_check_dups = 0;
+ int str2entry_flags = SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES |
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF ;
+
+ PR_ASSERT(pb);
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_CHECK_DUPS, &dont_check_dups);
+ if ( !dont_check_dups ) {
+ str2entry_flags |= SLAPI_STR2ENTRY_REMOVEDUPVALS;
+ }
+
+ /* Convert LDIF to entry structures */
+ rc= 1; /* assume we will succeed */
+ while (( entrystr = dse_read_next_entry( buf, &lastp )) != NULL )
+ {
+ e = slapi_str2entry( entrystr, str2entry_flags );
+ if ( e != NULL )
+ {
+ int returncode = 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= {0};
+
+ LDAPDebug(SLAPI_DSE_TRACELEVEL, "dse_read_one_file"
+ " processing entry \"%s\" in file %s%s\n",
+ slapi_entry_get_dn_const(e), filename,
+ primary_file ? " (primary file)" : "" );
+
+ /* remove the numsubordinates attr, which may be bogus */
+ slapi_entry_attr_delete(e, subordinatecount);
+
+ /* set the "primary file" flag if appropriate */
+ slapi_pblock_set( pb, SLAPI_DSE_IS_PRIMARY_FILE, &primary_file );
+ if(dse_call_callback(pdse, pb, DSE_OPERATION_READ,
+ DSE_FLAG_PREOP, e, NULL, &returncode,
+ returntext) == SLAPI_DSE_CALLBACK_OK)
+ {
+ /* this will free the entry if not added, so it is
+ definitely consumed by this call */
+ dse_add_entry_pb(pdse, e, pb);
+ }
+ else /* free entry if not used */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The entry %s in file %s is invalid, error code %d (%s) - %s\n",
+ slapi_entry_get_dn_const(e),
+ filename, returncode,
+ ldap_err2string(returncode),
+ returntext);
+ slapi_entry_free(e);
+ rc = 0; /* failure */
+ }
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "parsing dse entry [%s]\n", entrystr );
+ rc = 0; /* failure */
+ }
+ }
+ }
+ slapi_ch_free((void **)&buf);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Read the file we were initialised with into memory.
+ * If not NULL call entry_filter_fn on each entry as it's read.
+ * The function is free to modify the entry before it's places
+ * into the AVL tree. True means add the entry. False means don't.
+ *
+ * Return 1 for OK, 0 for Fail.
+ */
+int
+dse_read_file(struct dse *pdse, Slapi_PBlock *pb)
+{
+ int rc= 1; /* Good */
+ int ii;
+ char **filelist = 0;
+ char *filename = 0;
+
+ filelist = charray_dup(pdse->dse_filelist);
+ filename = slapi_ch_strdup(pdse->dse_filename);
+
+ for (ii = 0; rc && filelist && filelist[ii]; ++ii)
+ {
+ if (strcasecmp(filename, filelist[ii])!=0)
+ {
+ rc = dse_read_one_file(pdse, filelist[ii], pb, 0 /* not primary */);
+ }
+ }
+
+ if (rc)
+ {
+ rc = dse_read_one_file(pdse, filename, pb, 1 /* primary file */);
+ }
+
+ charray_free(filelist);
+ slapi_ch_free((void **)&filename);
+
+ return rc;
+}
+
+/*
+ * Structure to carry context information whilst
+ * traversing the tree writing the entries to disk.
+ */
+typedef struct _fpw
+{
+ PRFileDesc *fpw_prfd;
+ int fpw_rc;
+ struct dse *fpw_pdse;
+} FPWrapper;
+
+
+static int
+dse_rw_permission_to_one_file(const char *name, int loglevel)
+{
+ PRErrorCode prerr = 0;
+ const char *accesstype = "";
+
+ if ( NULL == name ) {
+ return 1; /* file won't be used -- return "sufficient permission" */
+ }
+
+ if ( PR_Access( name, PR_ACCESS_EXISTS ) == PR_SUCCESS ) {
+ /* file exists: check for read and write permission */
+ if ( PR_Access( name, PR_ACCESS_WRITE_OK ) != PR_SUCCESS ) {
+ prerr = PR_GetError();
+ accesstype = "write";
+ } else if ( PR_Access( name, PR_ACCESS_READ_OK ) != PR_SUCCESS ) {
+ prerr = PR_GetError();
+ accesstype = "read";
+ }
+ } else {
+ /* file does not exist: make sure we can create it */
+ PRFileDesc *prfd;
+
+ prfd = PR_Open( name, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ SLAPD_DEFAULT_FILE_MODE );
+ if ( NULL == prfd ) {
+ prerr = PR_GetError();
+ accesstype = "create";
+ } else {
+ PR_Close( prfd );
+ PR_Delete( name );
+ }
+ }
+
+ if ( prerr != 0 ) {
+ slapi_log_error( loglevel, "dse", "Unable to %s \"%s\": "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ accesstype, name, prerr, slapd_pr_strerror(prerr));
+ return 0; /* insufficient permission */
+ } else {
+ return 1; /* sufficient permission */
+ }
+}
+
+
+/*
+ * Check that we have permission to write to all the files that
+ * dse_write_file_nolock() uses.
+ * Returns a non-zero value if sufficient permission and 0 if not.
+ */
+static int
+dse_permission_to_write(struct dse* pdse, int loglevel)
+{
+ int rc = 1; /* sufficient permission */
+
+ if ( NULL != pdse->dse_filename ) {
+ if ( !dse_rw_permission_to_one_file( pdse->dse_filename, loglevel ) ||
+ !dse_rw_permission_to_one_file( pdse->dse_fileback, loglevel ) ||
+ !dse_rw_permission_to_one_file( pdse->dse_tmpfile, loglevel )) {
+ rc = 0; /* insufficient permission */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * Check for read-only status and return an appropriate error to the
+ * LDAP client.
+ * Returns 0 if no error was returned and non-zero if one was.
+ */
+static int
+dse_check_for_readonly_error(Slapi_PBlock *pb, struct dse* pdse)
+{
+ int rc = 0; /* default: no error */
+
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+
+ if ( !pdse->dse_is_updateable ) {
+ if ( !pdse->dse_readonly_error_reported ) {
+ if ( NULL != pdse->dse_filename ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "The DSE database stored in \"%s\" is not writeable\n",
+ pdse->dse_filename );
+ /* log the details too */
+ (void)dse_permission_to_write(pdse, SLAPI_LOG_FATAL);
+ }
+ pdse->dse_readonly_error_reported = 1;
+ }
+ rc = 1; /* return an error to the client */
+ }
+
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ if ( rc != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "DSE database is read-only", 0, NULL );
+ }
+
+ return rc; /* no error */
+}
+
+
+/*
+ * Write the AVL tree of entries back to the LDIF file.
+ */
+static int
+dse_write_file_nolock(struct dse* pdse)
+{
+ FPWrapper fpw;
+ int rc = 0;
+
+ if (dont_ever_write_dse_files)
+ return rc;
+
+ fpw.fpw_rc = 0;
+ fpw.fpw_prfd = NULL;
+
+ if ( NULL != pdse->dse_filename )
+ {
+ if (( fpw.fpw_prfd = PR_Open( pdse->dse_tmpfile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ rc = PR_GetOSError();
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot open "
+ "temporary DSE file \"%s\" for update: OS error %d (%s)\n",
+ pdse->dse_tmpfile, rc, slapd_system_strerror( rc ));
+ }
+ else
+ {
+ fpw.fpw_pdse = pdse;
+ if ( avl_apply( pdse->dse_tree, dse_write_entry, &fpw, STOP_TRAVERSAL, AVL_INORDER ) == STOP_TRAVERSAL )
+ {
+ rc = fpw.fpw_rc;
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot write "
+ " temporary DSE file \"%s\": OS error %d (%s)\n",
+ pdse->dse_tmpfile, rc, slapd_system_strerror( rc ));
+ (void)PR_Close( fpw.fpw_prfd );
+ fpw.fpw_prfd = NULL;
+ }
+ else
+ {
+ (void)PR_Close( fpw.fpw_prfd );
+ fpw.fpw_prfd = NULL;
+ if ( pdse->dse_fileback != NULL )
+ {
+ rc = slapi_destructive_rename( pdse->dse_filename, pdse->dse_fileback );
+ if ( rc != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot backup"
+ " DSE file \"%s\" to \"%s\": OS error %d (%s)\n",
+ pdse->dse_filename, pdse->dse_fileback,
+ rc, slapd_system_strerror( rc ));
+ }
+ }
+ rc = slapi_destructive_rename( pdse->dse_tmpfile, pdse->dse_filename );
+ if ( rc != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot rename"
+ " temporary DSE file \"%s\" to \"%s\":"
+ " OS error %d (%s)\n",
+ pdse->dse_tmpfile, pdse->dse_filename,
+ rc, slapd_system_strerror( rc ));
+ }
+ }
+ }
+ if (fpw.fpw_prfd)
+ (void)PR_Close(fpw.fpw_prfd);
+ }
+
+ return rc;
+}
+
+/*
+ * Local function for writing an entry to a file.
+ * Called by the AVL code during traversal.
+ */
+static int
+dse_write_entry( caddr_t data, caddr_t arg )
+{
+ struct dse_node *n = (struct dse_node *)data;
+ FPWrapper *fpw = (FPWrapper *)arg;
+ char *s;
+ PRInt32 len;
+
+ if ( NULL != n && NULL != n->entry )
+ {
+ int returncode;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ /* need to make a duplicate here for two reasons:
+ 1) we don't want to hold on to the raw data in the node for any longer
+ than we have to; we will usually be inside the dse write lock, but . . .
+ 2) the write callback may modify the entry, so we want to pass it a
+ writeable copy rather than the raw avl tree data pointer
+ */
+ Slapi_Entry *ec = slapi_entry_dup(n->entry);
+ if(dse_call_callback(fpw->fpw_pdse, NULL, DSE_OPERATION_WRITE,
+ DSE_FLAG_PREOP, ec, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ /*
+ * 3-August-2000 mcs: We used to pass the SLAPI_DUMP_NOOPATTRS
+ * option to slapi_entry2str_with_options() so that operational
+ * attributes were NOT stored in the DSE LDIF files. But now
+ * we store all attribute types.
+ */
+ if (( s = slapi_entry2str_with_options( ec, &len, 0 )) != NULL )
+ {
+ if ( slapi_write_buffer( fpw->fpw_prfd, s, len ) != len )
+ {
+ fpw->fpw_rc = PR_GetOSError();;
+ slapi_ch_free((void **) &s);
+ return STOP_TRAVERSAL;
+ }
+ if ( slapi_write_buffer( fpw->fpw_prfd, "\n", 1 ) != 1 )
+ {
+ fpw->fpw_rc = PR_GetOSError();;
+ slapi_ch_free((void **) &s);
+ return STOP_TRAVERSAL;
+ }
+ slapi_ch_free((void **) &s);
+ }
+ }
+ slapi_entry_free(ec);
+ }
+ return 0;
+}
+
+static int
+dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb)
+{
+ int dont_write_file = 0, merge = 0; /* defaults */
+ int rc= 0;
+ struct dse_node *n = dse_node_new(e); /* copies e */
+ Slapi_Entry *schemacheckentry= NULL; /* to use for schema checking */
+
+ PR_ASSERT(pb);
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ slapi_pblock_get(pb, SLAPI_DSE_MERGE_WHEN_ADDING, &merge);
+
+ /* keep write lock during both tree update and file write operations */
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ if (merge)
+ {
+ rc= avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_merge );
+ }
+ else
+ {
+ rc= avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_disallow );
+ }
+ if (-1 != rc) {
+ /* update num sub of parent with no lock; we already hold the write lock */
+ if (0 == rc) { /* entry was added, not merged; update numsub */
+ dse_updateNumSubOfParent(pdse, slapi_entry_get_sdn_const(e),
+ SLAPI_OPERATION_ADD);
+ } else { /* entry was merged, free temp unused data */
+ dse_node_delete(&n);
+ }
+ if (!dont_write_file) {
+ dse_write_file_nolock(pdse);
+ }
+ } else { /* duplicate entry ignored */
+ dse_node_delete(&n); /* This also deletes the contained entry */
+ }
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ if (rc == -1)
+ {
+ /* duplicate entry ignored */
+ schemacheckentry = dse_get_entry_copy( pdse,
+ slapi_entry_get_sdn_const(e),
+ DSE_USE_LOCK );
+ }
+ else /* entry added or merged */
+ {
+ /* entry was added or merged */
+ if (0 == rc) /* 0 return means entry was added, not merged */
+ {
+ /* save a search of the tree, since we added the entry, the
+ contents of e should be the same as what is in the tree */
+ schemacheckentry = slapi_entry_dup(e);
+ }
+ else /* merged */
+ {
+ /* schema check the new merged entry, so get it from the tree */
+ schemacheckentry = dse_get_entry_copy( pdse,
+ slapi_entry_get_sdn_const(e),
+ DSE_USE_LOCK );
+ }
+ }
+ if ( NULL != schemacheckentry )
+ {
+ /*
+ * Verify that the new or merged entry conforms to the schema.
+ * Errors are logged by slapi_entry_schema_check().
+ */
+ (void)slapi_entry_schema_check( pb, schemacheckentry );
+ slapi_entry_free(schemacheckentry);
+ }
+
+ /* callers expect e (SLAPI_ADD_ENTRY) to be freed or otherwise consumed */
+ slapi_entry_free(e);
+
+ return rc;
+}
+
+/*
+ * Local function for comparing two entries by DN. Store the entries
+ * so that when they are printed out, the child entries are below their
+ * ancestor entries
+ */
+static int
+entry_dn_cmp( caddr_t d1, caddr_t d2 )
+{
+ struct dse_node *n1 = (struct dse_node *)d1;
+ struct dse_node *n2 = (struct dse_node *)d2;
+ const Slapi_DN *dn1 = slapi_entry_get_sdn_const(n1->entry);
+ const Slapi_DN *dn2 = slapi_entry_get_sdn_const(n2->entry);
+ int retval = slapi_sdn_compare(dn1, dn2);
+
+ if (retval != 0)
+ {
+ if (slapi_sdn_issuffix(dn1, dn2))
+ {
+ retval = 1;
+ }
+ else if (slapi_sdn_issuffix(dn2, dn1))
+ {
+ retval = -1;
+ }
+ else
+ {
+ /* put fewer rdns before more rdns */
+ int rc = 0;
+ char **dnlist1 = ldap_explode_dn(slapi_sdn_get_ndn(dn1), 0);
+ char **dnlist2 = ldap_explode_dn(slapi_sdn_get_ndn(dn2), 0);
+ int len1 = 0;
+ int len2 = 0;
+ if (dnlist1)
+ for (len1 = 0; dnlist1[len1]; ++len1);
+ if (dnlist2)
+ for (len2 = 0; dnlist2[len2]; ++len2);
+
+ if (len1 == len2)
+ {
+ len1--;
+ for (; (rc == 0) && (len1 >= 0); --len1)
+ {
+ rc = strcmp(dnlist1[len1], dnlist2[len1]);
+ }
+ if (rc)
+ retval = rc;
+ }
+ else
+ retval = len1 - len2;
+
+ if (dnlist1)
+ ldap_value_free(dnlist1);
+ if (dnlist2)
+ ldap_value_free(dnlist2);
+ }
+ }
+ /* else entries are equal if dns are equal */
+
+ return retval;
+}
+
+
+static int
+dupentry_disallow( caddr_t d1, caddr_t d2 )
+{
+ return -1;
+}
+
+
+static int
+dupentry_replace( caddr_t d1, caddr_t d2 )
+{
+ /*
+ * Hack attack: since we don't have the address of the pointer
+ * in the avl node, we have to replace the e_dn and e_attrs
+ * members of the entry which is in the AVL tree with our
+ * new entry DN and attrs. We then point the "new" entry's
+ * e_dn and e_attrs pointers to point to the values we just
+ * replaced, on the assumption that the caller will be freeing
+ * these.
+ */
+ struct dse_node *n1 = (struct dse_node *)d1; /* OLD */
+ struct dse_node *n2 = (struct dse_node *)d2; /* NEW */
+ Slapi_Entry *e= n1->entry;
+ n1->entry= n2->entry;
+ n2->entry= e;
+ return DSE_ENTRY_WAS_REPLACED;
+}
+
+static int
+dupentry_merge( caddr_t d1, caddr_t d2 )
+{
+ struct dse_node *n1 = (struct dse_node *)d1; /* OLD */
+ struct dse_node *n2 = (struct dse_node *)d2; /* NEW */
+ Slapi_Entry *e1= n1->entry;
+ Slapi_Entry *e2= n2->entry;
+ int rc = 0;
+ Slapi_Attr *newattr = 0;
+
+ for (rc = slapi_entry_first_attr(e2, &newattr);
+ !rc && newattr;
+ rc = slapi_entry_next_attr(e2, newattr, &newattr)) {
+ char *type = 0;
+ slapi_attr_get_type(newattr, &type);
+ if (type) {
+ /* insure there are no duplicate values in e1 */
+ rc = slapi_entry_merge_values_sv(e1, type,
+ attr_get_present_values(newattr));
+ }
+ }
+
+ return DSE_ENTRY_WAS_MERGED;
+}
+
+/*
+ * Add an entry to the DSE without locking the DSE avl tree.
+ * Replaces the entry if it already exists.
+ *
+ * The given entry e is never consumed. It is the responsibility of the
+ * caller to free it when it is no longer needed.
+ *
+ * The write_file flag is used if we want to update the entry in memory
+ * but we do not want to write out the file. For example, if we update
+ * the numsubordinates in the entry, this is an operational attribute that
+ * we do not want saved to disk.
+ */
+static int
+dse_replace_entry( struct dse* pdse, Slapi_Entry *e, int write_file, int use_lock )
+{
+ int rc= -1;
+ if ( NULL != e )
+ {
+ struct dse_node *n= dse_node_new(e);
+ if (use_lock)
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ rc = avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_replace );
+ if (write_file)
+ dse_write_file_nolock(pdse);
+ /* If the entry was replaced i.e. not added as a new entry, we need to
+ free the old data, which is set in dupentry_replace */
+ if (DSE_ENTRY_WAS_REPLACED == rc) {
+ dse_node_delete(&n);
+ rc = 0; /* for return to caller */
+ }
+ if (use_lock)
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ }
+ return rc;
+}
+
+
+/*
+ * Return -1 if p does not point to a valid LDIF
+ * end-of-record delimiter (a NULL, two newlines, or two
+ * pairs of CRLF). Otherwise, return the length of
+ * the delimiter found.
+ */
+static int
+ldif_record_end( char *p )
+{
+ if ( NULL != p )
+ {
+ if ( '\0' == *p )
+ {
+ return 0;
+ }
+ else if ( '\n' == *p && '\n' == *( p + 1 ))
+ {
+ return 2;
+ }
+ else if ( '\r' == *p && '\n' == *( p + 1 ) && '\r' == *( p + 2 ) && '\n' == *( p + 3 ))
+ {
+ return 4;
+ }
+ }
+ return -1;
+}
+
+char *
+dse_read_next_entry( char *buf, char **lastp )
+{
+ char *p, *start;
+
+ if ( NULL == buf )
+ {
+ *lastp = NULL;
+ return NULL;
+ }
+ p = start = ( NULL == *lastp ) ? buf : *lastp;
+ /* Skip over any leading record delimiters */
+ while ( '\n' == *p || '\r' == *p )
+ {
+ p++;
+ }
+ if ( '\0' == *p )
+ {
+ *lastp = NULL;
+ return NULL;
+ }
+ while ( '\0' != *p )
+ {
+ int rc;
+ if (( rc = ldif_record_end( p )) >= 0 )
+ {
+ /* Found end of LDIF record */
+ *p = '\0';
+ p += rc;
+ break;
+ }
+ else
+ {
+ p++;
+ }
+ }
+ *lastp = p;
+ return start;
+}
+
+/*
+ * Apply the function to each entry. The caller is responsible for locking
+ * the rwlock in the dse for the appropriate type of operation e.g. for
+ * searching, a read lock, for modifying in place, a write lock
+ */
+static int
+dse_apply_nolock(struct dse* pdse,IFP fp,caddr_t arg)
+{
+ avl_apply( pdse->dse_tree, fp, arg, STOP_TRAVERSAL, AVL_INORDER );
+ return 1;
+}
+
+
+/*
+ * Remove the entry from the tree.
+ * Returns 1 if entry is removed and 0 if not.
+ */
+static int
+dse_delete_entry(struct dse* pdse, Slapi_PBlock *pb, const Slapi_Entry *e)
+{
+ int dont_write_file = 0;
+ struct dse_node *n= dse_node_new(e);
+ struct dse_node *deleted_node = NULL;
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+
+ /* keep write lock for both tree deleting and file writing */
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ if (deleted_node = (struct dse_node *)avl_delete(&pdse->dse_tree,
+ n, entry_dn_cmp))
+ dse_node_delete(&deleted_node);
+ dse_node_delete(&n);
+
+ if (!dont_write_file)
+ {
+ /* Decrement the numsubordinate count of the parent entry */
+ dse_updateNumSubOfParent(pdse, slapi_entry_get_sdn_const(e),
+ SLAPI_OPERATION_DELETE);
+ dse_write_file_nolock(pdse);
+ }
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ return 1;
+}
+
+
+/*
+ * Returns a SLAPI_BIND_xxx retun code.
+ */
+int
+dse_bind( Slapi_PBlock *pb ) /* JCM There should only be one exit point from this function! */
+{
+ char *dn; /* The bind DN */
+ int method; /* The bind method */
+ struct berval *cred; /* The bind credentials */
+ Slapi_Value **bvals;
+ struct dse* pdse;
+ Slapi_Attr *attr;
+ Slapi_DN sdn;
+ Slapi_Entry *ec= NULL;
+
+ /*Get the parameters*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return SLAPI_BIND_FAIL;
+ }
+
+ /* always allow noauth simple binds */
+ if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 )
+ {
+ /*
+ * report success to client, but return
+ * SLAPI_BIND_FAIL so we don't
+ * authorize based on noauth credentials
+ */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return( SLAPI_BIND_FAIL );
+ }
+
+ /* Find the entry that the person is attempting to bind as */
+ slapi_sdn_init_dn_byref(&sdn,dn);
+ ec = dse_get_entry_copy(pdse,&sdn,DSE_USE_LOCK);
+ if ( ec == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return( SLAPI_BIND_FAIL );
+ }
+
+ switch ( method )
+ {
+ case LDAP_AUTH_SIMPLE:
+ {
+ Slapi_Value cv;
+ if ( slapi_entry_attr_find( ec, "userpassword", &attr ) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ return SLAPI_BIND_FAIL;
+ }
+ bvals= attr_get_present_values( attr );
+
+ slapi_value_init_berval(&cv,cred);
+ if ( slapi_pw_find_sv( bvals, &cv ) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ value_done(&cv);
+ return SLAPI_BIND_FAIL;
+ }
+ value_done(&cv);
+ }
+ break;
+
+ default:
+ slapi_send_ldap_result( pb, LDAP_STRONG_AUTH_NOT_SUPPORTED, NULL, "auth method not supported", 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ return SLAPI_BIND_FAIL;
+ }
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ /* success: front end will send result */
+ return SLAPI_BIND_SUCCESS;
+}
+
+int
+dse_unbind( Slapi_PBlock *pb )
+{
+ return 0;
+}
+
+/*
+ * This structure is simply to pass parameters to dse_search_filter_entry.
+ */
+struct magicSearchStuff
+{
+ Slapi_PBlock *pb;
+ struct dse *pdse;
+ int scope;
+ const Slapi_DN *basedn;
+ Slapi_Filter *filter;
+ int nentries;
+ char **attrs; /*Attributes*/
+ int attrsonly; /*Should we just return the attributes found?*/
+ dse_search_set *ss; /* for the temporary results - to pass to the dse search callbacks */
+};
+
+/*
+ * The function which is called on each node of the AVL tree.
+ */
+static int
+dse_search_filter_entry(caddr_t data, caddr_t arg)
+{
+ struct dse_node *n = (struct dse_node *)data;
+ struct magicSearchStuff *p= (struct magicSearchStuff *)arg;
+ if(slapi_sdn_scope_test( slapi_entry_get_sdn_const(n->entry), p->basedn, p->scope))
+ {
+ if(slapi_vattr_filter_test( p->pb, n->entry, p->filter, 1 /* verify access */ )==0)
+ {
+ Slapi_Entry *ec = slapi_entry_dup( n->entry );
+ p->nentries++;
+ if (!p->ss)
+ {
+ p->ss = dse_search_set_new();
+ }
+ dse_search_set_add_entry(p->ss, ec); /* consumes the entry */
+ } else {
+/*
+ slapd_log_error_proc("dse_search_filter_entry",
+ "filter test failed: dn %s did not match filter %d\n",
+ slapi_entry_get_dn_const(n->entry), p->filter->f_choice);
+*/
+ }
+ } else {
+/*
+ slapd_log_error_proc("dse_search_filter_entry",
+ "scope test failed: dn %s is not in scope %d of dn [%s]\n",
+ slapi_entry_get_dn_const(n->entry), p->scope,
+ slapi_sdn_get_dn(p->basedn));
+*/
+ }
+ return 0;
+}
+
+/*
+ * The function which kicks off the traversal of the AVL tree.
+ * Returns the number of entries returned.
+ */
+/* jcm: Not very efficient if there are many DSE entries. */
+/* jcm: It applies the filter to every node in the tree regardless */
+static int
+do_dse_search(struct dse* pdse, Slapi_PBlock *pb, int scope, const Slapi_DN *basedn, Slapi_Filter *filter, char **attrs, int attrsonly)
+{
+ struct magicSearchStuff stuff;
+ stuff.pb= pb;
+ stuff.pdse= pdse;
+ stuff.scope= scope;
+ stuff.basedn= basedn;
+ stuff.filter= filter;
+ stuff.nentries= 0;
+ stuff.attrs= attrs;
+ stuff.attrsonly= attrsonly;
+ stuff.ss = NULL;
+
+ /*
+ * If this is a persistent search and the client is only interested in
+ * entries that change, we skip looking through the DSE entries.
+ */
+ if ( pb->pb_op == NULL
+ || !operation_is_flag_set( pb->pb_op, OP_FLAG_PS_CHANGESONLY )) {
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+ dse_apply_nolock(pdse,dse_search_filter_entry,(caddr_t)&stuff);
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ }
+
+ if (stuff.ss) /* something was found which matched our criteria */
+ {
+ Slapi_Entry *e = NULL;
+ for (e = dse_search_set_get_next_entry(stuff.ss);
+ e;
+ e = dse_search_set_get_next_entry(stuff.ss))
+ {
+ int returncode = 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_SEARCH,
+ DSE_FLAG_PREOP, e, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ dse_search_set *ss = NULL;
+ slapi_pblock_get (pb, SLAPI_SEARCH_RESULT_SET, &ss);
+ /* if this is the first entry - allocate dse_search_set structure */
+ if (ss == NULL)
+ {
+ ss = dse_search_set_new ();
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_SET, ss);
+ }
+ /* make another reference to e (stuff.ss references it too)
+ the stuff.ss reference is removed by dse_search_set_clean()
+ below, leaving ss as the sole owner of the memory */
+ dse_search_set_add_entry(ss, e);
+ } else {
+ stuff.nentries--; /* rejected entry */
+ /* this leaves a freed pointer in stuff.ss, but that's ok because
+ it should never be referenced, and the reference is removed by
+ the call to dse_search_set_clean() below */
+ slapi_entry_free(e);
+ }
+ }
+ dse_search_set_clean(stuff.ss);
+ }
+
+ /* the pblock ss now contains the "real" search result set and the copies of
+ the entries allocated in dse_search_filter_entry; any entries rejected by
+ the search callback were freed above by the call to slapi_entry_free() */
+ return stuff.nentries;
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+int
+dse_search(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *base; /*Base of the search*/
+ int scope; /*Scope of the search*/
+ Slapi_Filter *filter; /*The filter*/
+ char **attrs; /*Attributes*/
+ int attrsonly; /*Should we just return the attributes found?*/
+ /*int nentries= 0; Number of entries found thus far*/
+ struct dse* pdse;
+ int returncode= LDAP_SUCCESS;
+ int isrootdse= 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN basesdn;
+
+ /*
+ * Get private information created in the init routine.
+ * Also get the parameters of the search operation. These come
+ * more or less directly from the client.
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly ) <0)
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ slapi_sdn_init_dn_byref(&basesdn,base);
+
+ /*
+ * Sadly the root dse is still a special case. We must not allow
+ * acl checks on it, or allow onelevel or subtree searches on it.
+ */
+ isrootdse= slapi_sdn_isempty(&basesdn);
+
+ switch(scope)
+ {
+ case LDAP_SCOPE_BASE:
+ {
+ Slapi_Entry *baseentry= NULL;
+ baseentry = dse_get_entry_copy(pdse,&basesdn,DSE_USE_LOCK);
+ if ( baseentry == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_log_error(SLAPI_LOG_PLUGIN,"dse_search", "node %s was not found\n",
+ slapi_sdn_get_dn(&basesdn));
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+ /*
+ * We don't want to do an acl check for the root dse... because the acl
+ * code thinks it's a suffix of every target... so every acl applies to
+ * the root dse... which is wrong.
+ */
+ if(slapi_vattr_filter_test( pb, baseentry, filter, !isrootdse /* verify access */ )==0)
+ {
+ /* Callbacks modify a copy of the entry */
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_SEARCH,
+ DSE_FLAG_PREOP, baseentry, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ dse_search_set *ss;
+ ss = dse_search_set_new ();
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_SET, ss);
+ dse_search_set_add_entry (ss, baseentry); /* consumes the entry */
+ baseentry= NULL;
+ }
+ }
+ slapi_entry_free(baseentry);
+ }
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ /* FALL THROUGH */
+ case LDAP_SCOPE_SUBTREE:
+ if(!isrootdse)
+ {
+ do_dse_search(pdse, pb, scope, &basesdn, filter, attrs, attrsonly);
+ }
+ break;
+ }
+
+ /* Search is done, send LDAP_SUCCESS */
+ slapi_sdn_done(&basesdn);
+ return 0;
+}
+
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+
+static int
+dse_modify_return( int rv, Slapi_Entry *ec, Slapi_Entry *ecc )
+{
+ slapi_entry_free(ec);
+ slapi_entry_free(ecc);
+ return rv;
+}
+
+int
+dse_modify(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ int err; /*House keeping stuff*/
+ LDAPMod **mods; /*Used to apply the modifications*/
+ char *dn; /*Storage for the dn*/
+ char *errbuf = NULL; /* To get error back */
+ struct dse* pdse;
+ Slapi_Entry *ec= NULL;
+ Slapi_Entry *ecc= NULL;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN sdn;
+ int dont_write_file = 0; /* default */
+
+ PR_ASSERT(pb);
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) < 0 || (NULL == pdse))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return( -1 );
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( -1 );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ /* Find the entry we are about to modify. */
+ ec = dse_get_entry_copy(pdse,&sdn,DSE_USE_LOCK);
+ if ( ec == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Check acl */
+ err = plugin_call_acl_mods_access ( pb, ec, mods, &errbuf );
+ if ( err != LDAP_SUCCESS )
+ {
+ slapi_send_ldap_result( pb, err, NULL, errbuf, 0, NULL );
+ if (errbuf) slapi_ch_free ((void**)&errbuf);
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Save away a copy of the entry, before modifications */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( ec )); /* JCM - When does this get free'd? */
+ /* richm - it is freed in modify.c */
+
+ /* Modify a copy of the entry*/
+ ecc = slapi_entry_dup( ec );
+ err = entry_apply_mods( ecc, mods );
+
+ /* XXXmcs: should we expand objectclass values here?? */
+
+ switch(dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, ec, ecc, &returncode, returntext))
+ {
+ case SLAPI_DSE_CALLBACK_ERROR:
+ {
+ /* Error occured in the callback -- return error code from callback */
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ case SLAPI_DSE_CALLBACK_DO_NOT_APPLY:
+ {
+ /* Callback says don't apply the changes -- return Success */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( 0, ec, ecc );
+ }
+
+ case SLAPI_DSE_CALLBACK_OK:
+ {
+ /* The callback may alter the mods in the pblock. This happens
+ for example in the schema code. Since the schema attributes
+ are managed exclusively by the schema code, we should not
+ apply those mods. However, for reasons unknown to me, we
+ must in the general case call entry_apply_mods before calling
+ the modify callback above. In the case of schema, the schema
+ code will remove the schema attributes from the mods. So, we
+ reapply the mods to the entry for the attributes we manage in
+ the dse code (e.g. aci)
+ */
+ int reapply_mods = 0; /* default is to not reapply entry_apply_mods */
+ slapi_pblock_get(pb, SLAPI_DSE_REAPPLY_MODS, &reapply_mods);
+ /* Callback says apply the changes */
+ if ( reapply_mods )
+ {
+ LDAPMod **modsagain = NULL; /*Used to apply the modifications*/
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modsagain );
+ if (NULL != modsagain)
+ {
+ /* the dse modify callback must have modified ecc back to it's
+ original state, before the earlier apply_mods, but without the
+ attributes it did not want us to apply mods to */
+ err = entry_apply_mods( ecc, modsagain );
+ }
+ }
+
+ if (err != 0)
+ {
+ /* entry_apply_mods() failed above, so return an error now */
+ slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+ break;
+ }
+ }
+
+ /* We're applying the mods... check that the entry still obeys the schema */
+ if ( slapi_entry_schema_check( pb, ecc ) != 0 )
+ {
+ char *errtext;
+
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, errtext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Change the entry itself both on disk and in the AVL tree */
+ /* dse_replace_entry free's the existing entry. */
+ if (dse_replace_entry( pdse, ecc, !dont_write_file, DSE_USE_LOCK )!=0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup(ecc) ); /* JCM - When does this get free'd? */
+ /* richm - it is freed in modify.c */
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, ec, ecc, &returncode, returntext);
+
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+
+ slapi_sdn_done(&sdn);
+ return dse_modify_return(0, ec, ecc);
+}
+
+static int
+dse_add_return( int rv, Slapi_Entry *e)
+{
+ slapi_entry_free(e);
+ return rv;
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+int
+dse_add(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *dn = NULL;
+ Slapi_Entry *e; /*The new entry to add*/
+ Slapi_Entry *e_copy = NULL; /* copy of added entry */
+ char *errbuf = NULL;
+ int rc = LDAP_SUCCESS;
+ int error = -1;
+ int dont_write_file = 0; /* default */
+ struct dse* pdse;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN sdn;
+ Slapi_DN parent;
+
+ /*
+ * Get the database, the dn and the entry to add
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) < 0 || (NULL == pdse))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return error;
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( error );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ /*
+ * Check to make sure the entry passes the schema check
+ */
+ if ( slapi_entry_schema_check( pb, e ) != 0 )
+ {
+ char *errtext;
+ LDAPDebug( SLAPI_DSE_TRACELEVEL,
+ "dse_add: entry failed schema check\n", 0, 0, 0 );
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, errtext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return error;
+ }
+
+ /*
+ * Attempt to find this dn.
+ */
+ {
+ Slapi_Entry *existingentry= dse_get_entry_copy( pdse, &sdn, DSE_USE_LOCK );
+ if(existingentry!=NULL)
+ {
+ /*
+ * If we've reached this code, there is an entry
+ * whose dn matches dn, so tell the user and return
+ */
+ slapi_send_ldap_result( pb, LDAP_ALREADY_EXISTS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ slapi_entry_free(existingentry);
+ return dse_add_return(error, NULL);
+ }
+ }
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ * If the parent does not exist, only allow the "root" user to
+ * add the entry.
+ */
+ slapi_sdn_init(&parent);
+ slapi_sdn_get_parent(&sdn,&parent);
+ if ( !slapi_sdn_isempty(&parent) )
+ {
+ Slapi_Entry *parententry= NULL;
+ parententry= dse_get_entry_copy( pdse, &parent, DSE_USE_LOCK );
+ if( parententry==NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: parent does not exist\n", 0, 0, 0 );
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(error, NULL);
+ }
+ rc= plugin_call_acl_plugin ( pb, parententry, NULL, NULL, SLAPI_ACL_ADD, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ slapi_entry_free(parententry);
+ if ( rc!=LDAP_SUCCESS )
+ {
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: no access to parent\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ slapi_ch_free((void**)&errbuf);
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(rc, NULL);
+ }
+ }
+ else
+ {
+ /* no parent */
+ int isroot;
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ if ( !isroot )
+ {
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: no parent and not root\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(error, NULL);
+ }
+ }
+ slapi_sdn_done(&parent);
+
+ /*
+ * Before we add the entry, find out if the syntax of the aci
+ * aci attribute values are correct or not. We don't want to add
+ * the entry if the syntax is incorrect.
+ */
+ if ( plugin_call_acl_verify_syntax (pb, e, &errbuf) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void**)&errbuf);
+ return dse_add_return(error, NULL);
+ }
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, e,
+ NULL, &returncode, returntext)!=SLAPI_DSE_CALLBACK_OK)
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_add_return(error, NULL);
+ }
+
+ /* make copy for postop fns because add_entry_pb consumes e */
+ e_copy = slapi_entry_dup(e);
+ if ( dse_add_entry_pb(pdse, e, pb) != 0)
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_add_return(error, e_copy);
+ }
+
+ /* e has been consumed, so use the copy for the post ops */
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, e_copy);
+
+ /* The postop must be called after the write lock is released. */
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, e_copy, NULL, &returncode, returntext);
+
+ /* We have been successful. Tell the user */
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e_copy ));
+
+ /* entry has been freed, so make sure no one tries to use it later */
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, NULL);
+
+ /* Free the dn, and return */
+ slapi_sdn_done(&sdn);
+
+ return dse_add_return(rc, e_copy);
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+
+static int
+dse_delete_return( int rv, Slapi_Entry *ec)
+{
+ slapi_entry_free(ec);
+ return rv;
+}
+
+int
+dse_delete(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *dn = NULL;
+ int rc= -1;
+ int dont_write_file = 0; /* default */
+ struct dse* pdse = NULL;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ char *entry_str = "entry";
+ char *errbuf = NULL;
+ char *attrs[2] = { NULL, NULL };
+ Slapi_DN sdn;
+ Slapi_Entry *ec = NULL; /* copy of entry to delete */
+
+ /*
+ * Get the database and the dn
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn ) < 0 || (pdse == NULL))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return rc;
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( rc );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ ec= dse_get_entry_copy( pdse, &sdn, DSE_USE_LOCK );
+ if (ec == NULL)
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ /*
+ * Check if this node has any children.
+ */
+ if(dse_numsubordinates(ec)>0)
+ {
+ slapi_send_ldap_result( pb, LDAP_NOT_ALLOWED_ON_NONLEAF, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ /*
+ * Check the access
+ */
+ attrs[0] = entry_str;
+ attrs[1] = NULL;
+ returncode= plugin_call_acl_plugin ( pb, ec, attrs, NULL, SLAPI_ACL_DELETE, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ if ( returncode!=LDAP_SUCCESS)
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+ slapi_ch_free ( (void**)&errbuf );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, ec, NULL, &returncode,returntext)==SLAPI_DSE_CALLBACK_OK)
+ {
+ if(dse_delete_entry(pdse, pb, ec)==0)
+ {
+ returncode= LDAP_OPERATIONS_ERROR;
+ }
+ }
+ else
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, ec, NULL, &returncode, returntext);
+
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( ec ));
+ slapi_sdn_done(&sdn);
+ return dse_delete_return(0, ec);
+}
+
+struct dse_callback *
+dse_register_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ struct dse_callback *callback = dse_callback_new(operation, flags, base, scope, filter, fn, fn_arg);
+ dse_callback_addtolist(&pdse->dse_callback, callback);
+ return callback;
+}
+
+void
+dse_remove_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ dse_callback_removefromlist(&pdse->dse_callback, operation, flags, base, scope, filter, fn);
+}
+
+/*
+ * Return values:
+ * SLAPI_DSE_CALLBACK_ERROR -- Callback failed.
+ * SLAPI_DSE_CALLBACK_OK -- OK, do it.
+ * SLAPI_DSE_CALLBACK_DO_NOT_APPLY -- No error, but don't apply changes.
+ */
+static int
+dse_call_callback(struct dse* pdse, Slapi_PBlock *pb, int operation, int flags, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext)
+{
+ /* ONREPL callbacks can potentially modify pblock parameters like backend
+ * which would cause problems during request processing. We need to save
+ * "important" fields before calls and restoring them afterwards */
+ int r = SLAPI_DSE_CALLBACK_OK;
+ if (pdse->dse_callback != NULL) {
+ struct dse_callback *p;
+ p=pdse->dse_callback;
+ while (p!=NULL) {
+ struct dse_callback *p_next = p->next;
+ if ((p->operation & operation) && (p->flags & flags)) {
+ if(slapi_sdn_scope_test(slapi_entry_get_sdn_const(entryBefore), p->base, p->scope))
+ {
+ if(NULL == p->slapifilter ||
+ slapi_vattr_filter_test(pb, entryBefore, p->slapifilter,
+ 0 /* !verify access */ )==0)
+ {
+ int result= (*p->fn)(pb, entryBefore,entryAfter,returncode,returntext,p->fn_arg);
+ if(result<r)
+ {
+ r= result;
+ }
+ }
+ }
+ }
+ p = p_next;
+ }
+ }
+ return r;
+}
+
+int
+slapi_config_register_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ int rc= 0;
+ Slapi_Backend *be= slapi_be_select_by_instance_name(DSE_BACKEND);
+ if (be != NULL) {
+ struct dse* pdse= (struct dse*)be->be_database->plg_private;
+ if (pdse!=NULL) {
+ Slapi_DN dn;
+ slapi_sdn_init_dn_byref(&dn,base);
+ rc = (NULL != dse_register_callback(pdse, operation, flags, &dn, scope, filter, fn, fn_arg));
+ slapi_sdn_done(&dn);
+ }
+ }
+ return rc;
+}
+
+int
+slapi_config_remove_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ int rc= 0;
+ Slapi_Backend *be= slapi_be_select_by_instance_name(DSE_BACKEND);
+ if(be != NULL) {
+ struct dse* pdse = (struct dse*)be->be_database->plg_private;
+ if (pdse != NULL) {
+ Slapi_DN dn;
+ slapi_sdn_init_dn_byref(&dn,base);
+ dse_remove_callback(pdse, operation, flags, &dn, scope, filter, fn);
+ slapi_sdn_done(&dn);
+ rc= 1;
+ }
+ }
+ return rc;
+}
+
+void
+dse_set_dont_ever_write_dse_files()
+{
+ dont_ever_write_dse_files = 1;
+}
+
+void
+dse_unset_dont_ever_write_dse_files()
+{
+ dont_ever_write_dse_files = 0;
+}
+
+static dse_search_set*
+dse_search_set_new ()
+{
+ dse_search_set *ss;
+
+ ss = (dse_search_set *)slapi_ch_malloc (sizeof (*ss));
+
+ if (ss)
+ {
+ dl_init (&ss->dl, 0);
+ ss->current_entry = -1;
+ }
+
+ return ss;
+}
+
+/* This is similar to delete, but it does not free the entries contained in the
+ search set. This is useful in do_dse_search when we copy the entries from
+ 1 search set to the other. */
+static void
+dse_search_set_clean(dse_search_set *ss)
+{
+ if (ss)
+ {
+ dl_cleanup(&ss->dl, NULL);
+ slapi_ch_free((void **)&ss);
+ }
+}
+
+static void
+dse_search_set_delete (dse_search_set *ss)
+{
+ if (ss)
+ {
+ dl_cleanup (&ss->dl, dse_free_entry);
+ slapi_ch_free ((void**)&ss);
+ }
+}
+
+static void
+dse_free_entry (void **data)
+{
+ Slapi_Entry **e;
+
+ if (data)
+ {
+ e = (Slapi_Entry **)data;
+ if (*e)
+ slapi_entry_free (*e);
+ }
+}
+
+static void
+dse_search_set_add_entry (dse_search_set *ss, Slapi_Entry *e)
+{
+ PR_ASSERT (ss && e);
+
+ dl_add (&ss->dl, e);
+}
+
+static Slapi_Entry*
+dse_search_set_get_next_entry (dse_search_set *ss)
+{
+ PR_ASSERT (ss);
+
+ if (ss->current_entry == -1)
+ return (dl_get_first (&ss->dl, &ss->current_entry));
+ else
+ return (dl_get_next (&ss->dl, &ss->current_entry));
+}
+
+int
+dse_next_search_entry (Slapi_PBlock *pb)
+{
+ dse_search_set *ss;
+ Slapi_Entry *e;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &ss);
+
+ /* no entries to return */
+ if (ss == NULL)
+ {
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_ENTRY, NULL);
+ return 0;
+ }
+
+ e = dse_search_set_get_next_entry (ss);
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_ENTRY, e);
+
+ /* we reached the end of the list */
+ if (e == NULL)
+ {
+ dse_search_set_delete (ss);
+ slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
+ }
+
+ return 0;
+}