]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldif/ldif.c
allow empty modlist
[openldap] / servers / slapd / back-ldif / ldif.c
1 /* ldif.c - the ldif backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2006 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was originally developed by Eric Stokes for inclusion
18  * in OpenLDAP Software.
19  */
20
21 #include "portable.h"
22 #include <stdio.h>
23 #include <ac/string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <ac/dirent.h>
27 #include <fcntl.h>
28 #include <ac/errno.h>
29 #include <ac/unistd.h>
30 #include "slap.h"
31 #include "lutil.h"
32 #include "config.h"
33
34 typedef struct enumCookie {
35         Operation *op;
36         SlapReply *rs;
37         Entry **entries;
38         int elen;
39         int eind;
40 } enumCookie;
41
42 struct ldif_info {
43         struct berval li_base_path;
44         enumCookie li_tool_cookie;
45         ID li_tool_current;
46         ldap_pvt_thread_mutex_t  li_mutex;
47 };
48
49 #ifdef _WIN32
50 #define mkdir(a,b)      mkdir(a)
51 #endif
52
53 #define LDIF    ".ldif"
54
55 #define IX_DNL  '{'
56 #define IX_DNR  '}'
57 #ifndef IX_FSL
58 #define IX_FSL  IX_DNL
59 #define IX_FSR  IX_DNR
60 #endif
61
62 #define ENTRY_BUFF_INCREMENT 500
63
64 static ConfigTable ldifcfg[] = {
65         { "directory", "dir", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
66                 (void *)offsetof(struct ldif_info, li_base_path),
67                 "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
68                         "DESC 'Directory for database content' "
69                         "EQUALITY caseIgnoreMatch "
70                         "SYNTAX OMsDirectoryString )", NULL, NULL },
71         { NULL, NULL, 0, 0, 0, ARG_IGNORED,
72                 NULL, NULL, NULL, NULL }
73 };
74
75 static ConfigOCs ldifocs[] = {
76         { "( OLcfgDbOc:2.1 "
77                 "NAME 'olcLdifConfig' "
78                 "DESC 'LDIF backend configuration' "
79                 "SUP olcDatabaseConfig "
80                 "MUST ( olcDbDirectory ) )", Cft_Database, ldifcfg },
81         { NULL, 0, NULL }
82 };
83
84 static void
85 dn2path(struct berval * dn, struct berval * suffixdn, struct berval * base_path,
86         struct berval *res)
87 {
88         char *ptr, *sep, *end;
89
90         assert( dn != NULL );
91         assert( !BER_BVISNULL( dn ) );
92         assert( suffixdn != NULL );
93         assert( !BER_BVISNULL( suffixdn ) );
94         assert( dnIsSuffix( dn, suffixdn ) );
95
96         res->bv_len = dn->bv_len + base_path->bv_len + 1 + STRLENOF( LDIF );
97         res->bv_val = ch_malloc( res->bv_len + 1 );
98         ptr = lutil_strcopy( res->bv_val, base_path->bv_val );
99         *ptr++ = LDAP_DIRSEP[0];
100         ptr = lutil_strcopy( ptr, suffixdn->bv_val );
101         end = dn->bv_val + dn->bv_len - suffixdn->bv_len - 1;
102         while ( end > dn->bv_val ) {
103                 for (sep = end-1; sep >=dn->bv_val && !DN_SEPARATOR( *sep ); sep--);
104                 *ptr++ = LDAP_DIRSEP[0];
105                 ptr = lutil_strncopy( ptr, sep+1, end-sep-1 );
106                 end = sep;
107         }
108         strcpy(ptr, LDIF);
109 #if IX_FSL != IX_DNL
110         ptr = res->bv_val;
111         while( ptr=strchr(ptr, IX_DNL) ) {
112                 *ptr++ = IX_FSL;
113                 ptr = strchr(ptr, IX_DNR);
114                 if ( ptr )
115                         *ptr++ = IX_FSR;
116                 else
117                         break;
118         }
119 #endif
120 }
121
122 static char * slurp_file(int fd) {
123         int read_chars_total = 0;
124         int read_chars = 0;
125         int entry_size;
126         char * entry;
127         char * entry_pos;
128         struct stat st;
129
130         fstat(fd, &st);
131         entry_size = st.st_size;
132         entry = ch_malloc( entry_size+1 );
133         entry_pos = entry;
134         
135         while(1) {
136                 read_chars = read(fd, (void *) entry_pos, entry_size - read_chars_total);
137                 if(read_chars == -1) {
138                         SLAP_FREE(entry);
139                         return NULL;
140                 }
141                 if(read_chars == 0) {
142                         entry[read_chars_total] = '\0';
143                         break;
144                 }
145                 else {
146                         read_chars_total += read_chars;
147                         entry_pos += read_chars;
148                 }
149         }
150         return entry;
151 }
152
153 static int spew_file(int fd, char * spew, int len) {
154         int writeres = 0;
155         
156         while(len > 0) {
157                 writeres = write(fd, spew, len);
158                 if(writeres == -1) {
159                         Debug( LDAP_DEBUG_ANY, "could not spew write: %s\n",
160                                 strerror( errno ), 0, 0 );
161                         return -1;
162                 }
163                 else {
164                         spew += writeres;
165                         len -= writeres;
166                 }
167         }
168         return writeres;
169 }
170
171 static int spew_entry(Entry * e, struct berval * path) {
172         int rs;
173         int openres;
174         int spew_res;
175         int entry_length;
176         char * entry_as_string;
177
178         openres = open(path->bv_val, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR);
179         if(openres == -1) {
180                 if(errno == ENOENT)
181                         rs = LDAP_NO_SUCH_OBJECT;
182                 else
183                         rs = LDAP_UNWILLING_TO_PERFORM;
184                 Debug( LDAP_DEBUG_ANY, "could not open \"%s\": %s\n",
185                         path->bv_val, strerror( errno ), 0 );
186         }
187         else {
188                 struct berval rdn;
189                 int tmp;
190
191                 /* Only save the RDN onto disk */
192                 dnRdn( &e->e_name, &rdn );
193                 if ( rdn.bv_len != e->e_name.bv_len ) {
194                         e->e_name.bv_val[rdn.bv_len] = '\0';
195                         tmp = e->e_name.bv_len;
196                         e->e_name.bv_len = rdn.bv_len;
197                         rdn.bv_len = tmp;
198                 }
199
200                 entry_as_string = entry2str(e, &entry_length);
201
202                 /* Restore full DN */
203                 if ( rdn.bv_len != e->e_name.bv_len ) {
204                         e->e_name.bv_val[e->e_name.bv_len] = ',';
205                         e->e_name.bv_len = rdn.bv_len;
206                 }
207
208                 if(entry_as_string == NULL) {
209                         rs = LDAP_UNWILLING_TO_PERFORM;
210                         close(openres);
211                 }
212                 else {
213                         spew_res = spew_file(openres, entry_as_string, entry_length);
214                         close(openres);
215                         if(spew_res == -1)
216                                 rs = LDAP_UNWILLING_TO_PERFORM;
217                         else
218                                 rs = LDAP_SUCCESS;
219                 }
220         }
221         return rs;
222 }
223
224 static Entry * get_entry_for_fd(int fd,
225         struct berval *pdn,
226         struct berval *pndn)
227 {
228         char * entry = (char *) slurp_file(fd);
229         Entry * ldentry = NULL;
230         
231         /* error reading file */
232         if(entry == NULL) {
233                 goto return_value;
234         }
235
236         ldentry = str2entry(entry);
237         if ( ldentry ) {
238                 struct berval rdn;
239                 rdn = ldentry->e_name;
240                 build_new_dn( &ldentry->e_name, pdn, &rdn, NULL );
241                 ch_free( rdn.bv_val );
242                 rdn = ldentry->e_nname;
243                 build_new_dn( &ldentry->e_nname, pndn, &rdn, NULL );
244                 ch_free( rdn.bv_val );
245         }
246
247  return_value:
248         if(fd != -1) {
249                 if(close(fd) != 0) {
250                         /* log error */
251                 }
252         }
253         if(entry != NULL)
254                 SLAP_FREE(entry);
255         return ldentry;
256 }
257
258 static Entry * get_entry(Operation *op, struct berval *base_path) {
259         struct berval path, pdn, pndn;
260         int fd;
261
262         dnParent(&op->o_req_dn, &pdn);
263         dnParent(&op->o_req_ndn, &pndn);
264         dn2path(&op->o_req_ndn, op->o_bd->be_nsuffix, base_path, &path);
265         fd = open(path.bv_val, O_RDONLY);
266         /* error opening file (mebbe should log error) */
267         if(fd == -1) {
268                 Debug(LDAP_DEBUG_ANY, "failed to open file \"%s\": %s\n",
269                         path.bv_val, strerror(errno), 0 );
270         }
271
272         if(path.bv_val != NULL)
273                 SLAP_FREE(path.bv_val);
274
275         if ( fd != -1 ) {
276                 return get_entry_for_fd(fd, &pdn, &pndn);
277         }
278
279         return NULL;
280 }
281
282 static void fullpath(struct berval *base, struct berval *name, struct berval *res) {
283         char *ptr;
284         res->bv_len = name->bv_len + base->bv_len + 1;
285         res->bv_val = ch_malloc( res->bv_len + 1 );
286         strcpy(res->bv_val, base->bv_val);
287         ptr = res->bv_val + base->bv_len;
288         *ptr++ = LDAP_DIRSEP[0];
289         strcpy(ptr, name->bv_val);
290 }
291
292 typedef struct bvlist {
293         struct bvlist *next;
294         struct berval bv;
295         struct berval num;
296         unsigned int inum;
297         int off;
298 } bvlist;
299
300
301 static int r_enum_tree(enumCookie *ck, struct berval *path,
302         struct berval *pdn, struct berval *pndn)
303 {
304         Entry *e;
305         int fd, rc = LDAP_SUCCESS;
306
307         fd = open( path->bv_val, O_RDONLY );
308         if ( fd < 0 ) {
309                 Debug( LDAP_DEBUG_ANY,
310                         "=> ldif_enum_tree: failed to open %s: %s\n",
311                         path->bv_val, strerror(errno), 0 );
312                 return LDAP_NO_SUCH_OBJECT;
313         }
314
315         e = get_entry_for_fd(fd, pdn, pndn);
316         if ( !e ) {
317                 Debug( LDAP_DEBUG_ANY,
318                         "=> ldif_enum_tree: failed to read entry for %s\n",
319                         path->bv_val, 0, 0 );
320                 return LDAP_BUSY;
321         }
322
323         if ( ck->op->ors_scope == LDAP_SCOPE_BASE ||
324                 ck->op->ors_scope == LDAP_SCOPE_SUBTREE ) {
325                 /* Send right away? */
326                 if ( ck->rs ) {
327                         /*
328                          * if it's a referral, add it to the list of referrals. only do
329                          * this for non-base searches, and don't check the filter
330                          * explicitly here since it's only a candidate anyway.
331                          */
332                         if ( !get_manageDSAit( ck->op )
333                                         && ck->op->ors_scope != LDAP_SCOPE_BASE
334                                         && is_entry_referral( e ) )
335                         {
336                                 BerVarray erefs = get_entry_referrals( ck->op, e );
337                                 ck->rs->sr_ref = referral_rewrite( erefs,
338                                                 &e->e_name, NULL,
339                                                 ck->op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
340                                                         ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
341
342                                 ck->rs->sr_entry = e;
343                                 rc = send_search_reference( ck->op, ck->rs );
344                                 ber_bvarray_free( ck->rs->sr_ref );
345                                 ber_bvarray_free( erefs );
346                                 ck->rs->sr_ref = NULL;
347                                 ck->rs->sr_entry = NULL;
348
349                         } else if ( test_filter( ck->op, e, ck->op->ors_filter ) == LDAP_COMPARE_TRUE )
350                         {
351                                 ck->rs->sr_entry = e;
352                                 ck->rs->sr_attrs = ck->op->ors_attrs;
353                                 ck->rs->sr_flags = REP_ENTRY_MODIFIABLE;
354                                 rc = send_search_entry(ck->op, ck->rs);
355                                 ck->rs->sr_entry = NULL;
356                         }
357                         fd = 1;
358                         if ( rc )
359                                 goto done;
360                 } else {
361                 /* Queueing up for tool mode */
362                         if(ck->entries == NULL) {
363                                 ck->entries = (Entry **) ch_malloc(sizeof(Entry *) * ENTRY_BUFF_INCREMENT);
364                                 ck->elen = ENTRY_BUFF_INCREMENT;
365                         }
366                         if(ck->eind >= ck->elen) { /* grow entries if necessary */      
367                                 ck->entries = (Entry **) ch_realloc(ck->entries, sizeof(Entry *) * (ck->elen) * 2);
368                                 ck->elen *= 2;
369                         }
370
371                         ck->entries[ck->eind++] = e;
372                         fd = 0;
373                 }
374         } else {
375                 fd = 1;
376         }
377
378         if ( ck->op->ors_scope != LDAP_SCOPE_BASE ) {
379                 DIR * dir_of_path;
380                 bvlist *list = NULL, *ptr;
381
382                 path->bv_len -= STRLENOF( LDIF );
383                 path->bv_val[path->bv_len] = '\0';
384
385                 dir_of_path = opendir(path->bv_val);
386                 if(dir_of_path == NULL) { /* can't open directory */
387                         if ( errno != ENOENT ) {
388                                 /* it shouldn't be treated as an error
389                                  * only if the directory doesn't exist */
390                                 rc = LDAP_BUSY;
391                                 Debug( LDAP_DEBUG_ANY,
392                                         "=> ldif_enum_tree: failed to opendir %s (%d)\n",
393                                         path->bv_val, errno, 0 );
394                         }
395                         goto done;
396                 }
397         
398                 while(1) {
399                         struct berval fname, itmp;
400                         struct dirent * dir;
401                         bvlist *bvl, **prev;
402
403                         dir = readdir(dir_of_path);
404                         if(dir == NULL) break; /* end of the directory */
405                         fname.bv_len = strlen( dir->d_name );
406                         if ( fname.bv_len <= STRLENOF( LDIF ))
407                                 continue;
408                         if ( strcmp( dir->d_name + (fname.bv_len - STRLENOF(LDIF)), LDIF))
409                                 continue;
410                         fname.bv_val = dir->d_name;
411
412                         bvl = ch_malloc( sizeof(bvlist) );
413                         ber_dupbv( &bvl->bv, &fname );
414                         BER_BVZERO( &bvl->num );
415                         itmp.bv_val = strchr( bvl->bv.bv_val, IX_FSL );
416                         if ( itmp.bv_val ) {
417                                 char *ptr;
418                                 itmp.bv_val++;
419                                 ptr = strchr( itmp.bv_val, IX_FSR );
420                                 if ( ptr ) {
421                                         itmp.bv_len = ptr - itmp.bv_val;
422                                         ber_dupbv( &bvl->num, &itmp );
423                                         /* FIXME: handle error? */
424                                         assert( itmp.bv_val[ 0 ] != '-' );
425                                         bvl->inum = strtoul( itmp.bv_val, NULL, 0 );
426                                         itmp.bv_val[0] = '\0';
427                                         bvl->off = itmp.bv_val - bvl->bv.bv_val;
428                                 }
429                         }
430
431                         for (prev = &list; (ptr = *prev) != NULL; prev = &ptr->next) {
432                                 int cmp = strcmp( bvl->bv.bv_val, ptr->bv.bv_val );
433                                 if ( !cmp && bvl->num.bv_val )
434                                         cmp = bvl->inum - ptr->inum;
435                                 if ( cmp < 0 )
436                                         break;
437                         }
438                         *prev = bvl;
439                         bvl->next = ptr;
440                                 
441                 }
442                 closedir(dir_of_path);
443
444                 if (ck->op->ors_scope == LDAP_SCOPE_ONELEVEL)
445                         ck->op->ors_scope = LDAP_SCOPE_BASE;
446                 else if ( ck->op->ors_scope == LDAP_SCOPE_SUBORDINATE)
447                         ck->op->ors_scope = LDAP_SCOPE_SUBTREE;
448
449                 while ( ( ptr = list ) ) {
450                         struct berval fpath;
451
452                         list = ptr->next;
453
454                         if ( rc == LDAP_SUCCESS ) {
455                                 if ( ptr->num.bv_val )
456                                         AC_MEMCPY( ptr->bv.bv_val + ptr->off, ptr->num.bv_val,
457                                                 ptr->num.bv_len );
458                                 fullpath( path, &ptr->bv, &fpath );
459                                 rc = r_enum_tree(ck, &fpath, &e->e_name, &e->e_nname );
460                                 free(fpath.bv_val);
461                         }
462                         if ( ptr->num.bv_val )
463                                 free( ptr->num.bv_val );
464                         free(ptr->bv.bv_val);
465                         free(ptr);
466                 }
467         }
468 done:
469         if ( fd ) entry_free( e );
470         return rc;
471 }
472
473 static int
474 enum_tree(
475         enumCookie *ck
476 )
477 {
478         struct ldif_info *ni = (struct ldif_info *) ck->op->o_bd->be_private;
479         struct berval path;
480         struct berval pdn, pndn;
481         int rc;
482
483         dnParent( &ck->op->o_req_dn, &pdn );
484         dnParent( &ck->op->o_req_ndn, &pndn );
485         dn2path( &ck->op->o_req_ndn, &ck->op->o_bd->be_nsuffix[0], &ni->li_base_path, &path);
486         rc = r_enum_tree(ck, &path, &pdn, &pndn);
487         ch_free( path.bv_val );
488         return rc;
489 }
490
491 /* Get the parent path plus the LDIF suffix */
492 static void get_parent_path(struct berval * dnpath, struct berval *res) {
493         int dnpathlen = dnpath->bv_len;
494         int i;
495         
496         for(i = dnpathlen;i>0;i--) /* find the first path seperator */
497                 if(dnpath->bv_val[i] == LDAP_DIRSEP[0])
498                         break;
499         res->bv_len = i;
500         res->bv_val = ch_malloc( res->bv_len + 1 + STRLENOF(LDIF) );
501         strncpy(res->bv_val, dnpath->bv_val, i);
502         strcpy(res->bv_val+i, LDIF);
503         res->bv_val[i] = '\0';
504 }
505
506 static int apply_modify_to_entry(Entry * entry,
507                                 Modifications * modlist,
508                                 Operation * op,
509                                 SlapReply * rs)
510 {
511         char textbuf[SLAP_TEXT_BUFLEN];
512         int rc = modlist ? LDAP_UNWILLING_TO_PERFORM : LDAP_SUCCESS;
513         int is_oc = 0;
514         Modification *mods = NULL;
515
516         if (!acl_check_modlist(op, entry, modlist)) {
517                 return LDAP_INSUFFICIENT_ACCESS;
518         }
519
520         for (; modlist != NULL; modlist = modlist->sml_next) {
521                 mods = &modlist->sml_mod;
522
523                 if ( mods->sm_desc == slap_schema.si_ad_objectClass ) {
524                         is_oc = 1;
525                 }
526                 switch (mods->sm_op) {
527                 case LDAP_MOD_ADD:
528                         rc = modify_add_values(entry, mods,
529                                    get_permissiveModify(op),
530                                    &rs->sr_text, textbuf,
531                                    sizeof( textbuf ) );
532                         break;
533                                 
534                 case LDAP_MOD_DELETE:
535                         rc = modify_delete_values(entry, mods,
536                                 get_permissiveModify(op),
537                                 &rs->sr_text, textbuf,
538                                 sizeof( textbuf ) );
539                         break;
540                                 
541                 case LDAP_MOD_REPLACE:
542                         rc = modify_replace_values(entry, mods,
543                                  get_permissiveModify(op),
544                                  &rs->sr_text, textbuf,
545                                  sizeof( textbuf ) );
546                         break;
547
548                 case LDAP_MOD_INCREMENT:
549                         rc = modify_increment_values( entry,
550                                 mods, get_permissiveModify(op),
551                                 &rs->sr_text, textbuf,
552                                 sizeof( textbuf ) );
553                         break;
554
555                         break;
556
557                 case SLAP_MOD_SOFTADD:
558                         mods->sm_op = LDAP_MOD_ADD;
559                         rc = modify_add_values(entry, mods,
560                                    get_permissiveModify(op),
561                                    &rs->sr_text, textbuf,
562                                    sizeof( textbuf ) );
563                         mods->sm_op = SLAP_MOD_SOFTADD;
564                         if (rc == LDAP_TYPE_OR_VALUE_EXISTS) {
565                                 rc = LDAP_SUCCESS;
566                         }
567                         break;
568                 default:
569                         break;
570                 }
571                 if(rc != LDAP_SUCCESS) break;
572         }
573         
574         if(rc == LDAP_SUCCESS) {
575                 if ( is_oc ) {
576                         entry->e_ocflags = 0;
577                 }
578                 /* check that the entry still obeys the schema */
579                 rc = entry_schema_check( op, entry, NULL, 0,
580                           &rs->sr_text, textbuf, sizeof( textbuf ) );
581         }
582
583         return rc;
584 }
585
586 int
587 ldif_back_referrals( Operation *op, SlapReply *rs )
588 {
589         struct ldif_info        *ni = NULL;
590         Entry                   *entry;
591         int                     rc = LDAP_SUCCESS;
592
593 #if 0
594         if ( op->o_tag == LDAP_REQ_SEARCH ) {
595                 /* let search take care of itself */
596                 return rc;
597         }
598 #endif
599
600         if ( get_manageDSAit( op ) ) {
601                 /* let op take care of DSA management */
602                 return rc;
603         }
604
605         ni = (struct ldif_info *)op->o_bd->be_private;
606         ldap_pvt_thread_mutex_lock( &ni->li_mutex );
607         entry = (Entry *)get_entry( op, &ni->li_base_path );
608
609         /* no object is found for them */
610         if ( entry == NULL ) {
611                 struct berval   odn = op->o_req_dn;
612                 struct berval   ondn = op->o_req_ndn;
613
614                 struct berval   pndn = op->o_req_ndn;
615
616                 for ( ; entry == NULL; ) {
617                         dnParent( &pndn, &pndn );
618                         
619                         if ( !dnIsSuffix( &pndn, &op->o_bd->be_nsuffix[0] ) ) {
620                                 break;
621                         }
622
623                         op->o_req_dn = pndn;
624                         op->o_req_ndn = pndn;
625
626                         entry = (Entry *)get_entry( op, &ni->li_base_path );
627                 }
628
629                 ldap_pvt_thread_mutex_unlock( &ni->li_mutex );
630
631                 op->o_req_dn = odn;
632                 op->o_req_ndn = ondn;
633
634                 rc = LDAP_SUCCESS;
635                 rs->sr_matched = NULL;
636                 if ( entry != NULL ) {
637                         Debug( LDAP_DEBUG_TRACE,
638                                 "ldif_back_referrals: op=%ld target=\"%s\" matched=\"%s\"\n",
639                                 (long) op->o_tag, op->o_req_dn.bv_val, entry->e_name.bv_val );
640
641                         if ( is_entry_referral( entry ) ) {
642                                 rc = LDAP_OTHER;
643                                 rs->sr_ref = get_entry_referrals( op, entry );
644                                 if ( rs->sr_ref ) {
645                                         rs->sr_matched = ber_strdup_x(
646                                         entry->e_name.bv_val, op->o_tmpmemctx );
647                                 }
648                         }
649
650                         entry_free(entry);
651
652                 } else if ( default_referral != NULL ) {
653                         rc = LDAP_OTHER;
654                         rs->sr_ref = referral_rewrite( default_referral,
655                                 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
656                 }
657
658                 if ( rs->sr_ref != NULL ) {
659                         /* send referrals */
660                         rc = rs->sr_err = LDAP_REFERRAL;
661                         send_ldap_result( op, rs );
662                         ber_bvarray_free( rs->sr_ref );
663                         rs->sr_ref = NULL;
664
665                 } else if ( rc != LDAP_SUCCESS ) {
666                         rs->sr_err = rc;
667                         rs->sr_text = rs->sr_matched ? "bad referral object" : NULL;
668                         send_ldap_result( op, rs );
669                 }
670
671                 if ( rs->sr_matched ) {
672                         op->o_tmpfree( (char *)rs->sr_matched, op->o_tmpmemctx );
673                         rs->sr_matched = NULL;
674                 }
675
676                 return rc;
677         }
678
679         ldap_pvt_thread_mutex_unlock( &ni->li_mutex );
680
681         if ( is_entry_referral( entry ) ) {
682                 /* entry is a referral */
683                 BerVarray refs = get_entry_referrals( op, entry );
684                 rs->sr_ref = referral_rewrite(
685                         refs, &entry->e_name, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
686
687                 Debug( LDAP_DEBUG_TRACE,
688                         "ldif_back_referrals: op=%ld target=\"%s\" matched=\"%s\"\n",
689                         (long) op->o_tag, op->o_req_dn.bv_val, entry->e_name.bv_val );
690
691                 rs->sr_matched = entry->e_name.bv_val;
692                 if ( rs->sr_ref != NULL ) {
693                         rc = rs->sr_err = LDAP_REFERRAL;
694                         send_ldap_result( op, rs );
695                         ber_bvarray_free( rs->sr_ref );
696                         rs->sr_ref = NULL;
697
698                 } else {
699                         send_ldap_error( op, rs, LDAP_OTHER, "bad referral object" );
700                         rc = rs->sr_err;
701                 }
702
703                 rs->sr_matched = NULL;
704                 ber_bvarray_free( refs );
705         }
706
707         entry_free( entry );
708
709         return rc;
710 }
711
712 static int
713 ldif_back_bind( Operation *op, SlapReply *rs )
714 {
715         struct ldif_info *ni = NULL;
716         Attribute * a = NULL;
717         AttributeDescription *password = slap_schema.si_ad_userPassword;
718         int return_val = 0;
719         Entry * entry = NULL;
720
721         ni = (struct ldif_info *) op->o_bd->be_private;
722         ldap_pvt_thread_mutex_lock(&ni->li_mutex);
723         entry = (Entry *) get_entry(op, &ni->li_base_path);
724
725         /* no object is found for them */
726         if(entry == NULL) {
727                 if(be_isroot_pw(op)) {
728                         rs->sr_err = return_val = LDAP_SUCCESS;
729                 } else {
730                         rs->sr_err = return_val = LDAP_INVALID_CREDENTIALS;
731                 }
732                 goto return_result;
733         }
734
735         /* they don't have userpassword */
736         if((a = attr_find(entry->e_attrs, password)) == NULL) {
737                 rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
738                 return_val = 1;
739                 goto return_result;
740         }
741
742         /* authentication actually failed */
743         if(slap_passwd_check(op, entry, a, &op->oq_bind.rb_cred,
744                              &rs->sr_text) != 0) {
745                 rs->sr_err = LDAP_INVALID_CREDENTIALS;
746                 return_val = 1;
747                 goto return_result;
748         }
749
750         /* let the front-end send success */
751         return_val = 0;
752         goto return_result;
753
754  return_result:
755         ldap_pvt_thread_mutex_unlock(&ni->li_mutex);
756         if(return_val != 0)
757                 send_ldap_result( op, rs );
758         if(entry != NULL)
759                 entry_free(entry);
760         return return_val;
761 }
762
763 static int ldif_back_search(Operation *op, SlapReply *rs)
764 {
765         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
766         enumCookie ck = { NULL, NULL, NULL, 0, 0 };
767
768         ck.op = op;
769         ck.rs = rs;
770         ldap_pvt_thread_mutex_lock(&ni->li_mutex);
771         rs->sr_err = enum_tree( &ck );
772         ldap_pvt_thread_mutex_unlock(&ni->li_mutex);
773         send_ldap_result(op, rs);
774
775         return rs->sr_err;
776 }
777
778 static int ldif_back_add(Operation *op, SlapReply *rs) {
779         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
780         Entry * e = op->ora_e;
781         struct berval dn = e->e_nname;
782         struct berval leaf_path = BER_BVNULL;
783         struct stat stats;
784         int statres;
785         char textbuf[SLAP_TEXT_BUFLEN];
786
787         Debug( LDAP_DEBUG_TRACE, "ldif_back_add: \"%s\"\n", dn.bv_val, 0, 0);
788         slap_add_opattrs( op, &rs->sr_text, textbuf, sizeof( textbuf ), 1 );
789
790         rs->sr_err = entry_schema_check(op, e, NULL, 0,
791                 &rs->sr_text, textbuf, sizeof( textbuf ) );
792         if ( rs->sr_err != LDAP_SUCCESS ) goto send_res;
793                                 
794         ldap_pvt_thread_mutex_lock(&ni->li_mutex);
795
796         dn2path(&dn, &op->o_bd->be_nsuffix[0], &ni->li_base_path, &leaf_path);
797
798         if(leaf_path.bv_val != NULL) {
799                 struct berval base = BER_BVNULL;
800                 /* build path to container and ldif of container */
801                 get_parent_path(&leaf_path, &base);
802
803                 statres = stat(base.bv_val, &stats); /* check if container exists */
804                 if(statres == -1 && errno == ENOENT) { /* container missing */
805                         base.bv_val[base.bv_len] = '.';
806                         statres = stat(base.bv_val, &stats); /* check for leaf node */
807                         base.bv_val[base.bv_len] = '\0';
808                         if(statres == -1 && errno == ENOENT) {
809                                 rs->sr_err = LDAP_NO_SUCH_OBJECT; /* parent doesn't exist */
810                                 rs->sr_text = "Parent does not exist";
811                         }
812                         else if(statres != -1) { /* create parent */
813                                 int mkdirres = mkdir(base.bv_val, 0750);
814                                 if(mkdirres == -1) {
815                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
816                                         rs->sr_text = "Could not create parent folder";
817                                         Debug( LDAP_DEBUG_ANY, "could not create folder \"%s\": %s\n",
818                                                 base.bv_val, strerror( errno ), 0 );
819                                 }
820                         }
821                         else
822                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
823                 }/* container was possibly created, move on to add the entry */
824                 if(rs->sr_err == LDAP_SUCCESS) {
825                         statres = stat(leaf_path.bv_val, &stats);
826                         if(statres == -1 && errno == ENOENT) {
827                                 ldap_pvt_thread_mutex_lock(&entry2str_mutex);
828                                 rs->sr_err = (int) spew_entry(e, &leaf_path);
829                                 ldap_pvt_thread_mutex_unlock(&entry2str_mutex);
830                         }
831                         else if ( statres == -1 ) {
832                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
833                                 Debug( LDAP_DEBUG_ANY, "could not stat file \"%s\": %s\n",
834                                         leaf_path.bv_val, strerror( errno ), 0 );
835                         }
836                         else /* it already exists */
837                                 rs->sr_err = LDAP_ALREADY_EXISTS;
838                 }
839                 SLAP_FREE(base.bv_val);
840                 SLAP_FREE(leaf_path.bv_val);
841         }
842
843         ldap_pvt_thread_mutex_unlock(&ni->li_mutex);
844
845 send_res:
846         Debug( LDAP_DEBUG_TRACE, 
847                         "ldif_back_add: err: %d text: %s\n", rs->sr_err, rs->sr_text, 0);
848         send_ldap_result(op, rs);
849         slap_graduate_commit_csn( op );
850         return 0;
851 }
852
853 static int ldif_back_modify(Operation *op, SlapReply *rs) {
854         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
855         Modifications * modlst = op->orm_modlist;
856         struct berval path = BER_BVNULL;
857         Entry * entry = NULL;
858         int spew_res;
859
860         slap_mods_opattrs( op, &op->orm_modlist, 1 );
861
862         ldap_pvt_thread_mutex_lock(&ni->li_mutex);
863         dn2path(&op->o_req_ndn, &op->o_bd->be_nsuffix[0], &ni->li_base_path,
864                 &path);
865         entry = (Entry *) get_entry(op, &ni->li_base_path);
866
867         if(entry != NULL) {
868                 rs->sr_err = apply_modify_to_entry(entry, modlst, op, rs);
869                 if(rs->sr_err == LDAP_SUCCESS) {
870                         int save_errno;
871                         ldap_pvt_thread_mutex_lock(&entry2str_mutex);
872                         spew_res = spew_entry(entry, &path);
873                         save_errno = errno;
874                         ldap_pvt_thread_mutex_unlock(&entry2str_mutex);
875                         if(spew_res == -1) {
876                                 Debug( LDAP_DEBUG_ANY,
877                                         "%s ldif_back_modify: could not output entry \"%s\": %s\n",
878                                         op->o_log_prefix, entry->e_name.bv_val, strerror( save_errno ) );
879                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
880                         }
881                 }
882         }
883         else {
884                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
885         }
886         
887         if(entry != NULL)
888                 entry_free(entry);
889         if(path.bv_val != NULL)
890                 SLAP_FREE(path.bv_val);
891         rs->sr_text = NULL;
892         ldap_pvt_thread_mutex_unlock(&ni->li_mutex);
893         send_ldap_result(op, rs);
894         slap_graduate_commit_csn( op );
895         return 0;
896 }
897
898 static int ldif_back_delete(Operation *op, SlapReply *rs) {
899         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
900         struct berval path = BER_BVNULL;
901         int res = 0;
902
903         if ( BER_BVISEMPTY( &op->o_csn )) {
904                 struct berval csn;
905                 char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
906
907                 csn.bv_val = csnbuf;
908                 csn.bv_len = sizeof( csnbuf );
909                 slap_get_csn( op, &csn, 1 );
910         }
911
912         ldap_pvt_thread_mutex_lock(&ni->li_mutex);
913         dn2path(&op->o_req_ndn, &op->o_bd->be_nsuffix[0], &ni->li_base_path, &path);
914
915         path.bv_val[path.bv_len - STRLENOF(LDIF)] = '\0';
916         res = rmdir(path.bv_val);
917         path.bv_val[path.bv_len - STRLENOF(LDIF)] = '.';
918         if ( res && errno != ENOENT ) {
919                 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
920         } else {
921                 res = unlink(path.bv_val);
922         }
923
924         if(res == -1) {
925                 if(errno == ENOENT)
926                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
927                 else
928                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
929         }
930         else
931                 rs->sr_err = LDAP_SUCCESS;
932
933         SLAP_FREE(path.bv_val);
934         ldap_pvt_thread_mutex_unlock(&ni->li_mutex);
935         send_ldap_result(op, rs);
936         slap_graduate_commit_csn( op );
937         return 0;
938 }
939
940
941 static int move_entry(Entry * entry, struct berval * ndn,
942                            struct berval * newndn, struct berval * suffixdn,
943                            struct berval * base_path) {
944         int res;
945         int exists_res;
946         struct berval path;
947         struct berval newpath;
948
949         dn2path(ndn, suffixdn, base_path, &path);
950         dn2path(newndn, suffixdn, base_path, &newpath);
951
952         if((entry == NULL || path.bv_val == NULL) || newpath.bv_val == NULL) {
953                 /* some object doesn't exist */
954                 res = LDAP_NO_SUCH_OBJECT;
955         }
956         else { /* do the modrdn */
957                 exists_res = open(newpath.bv_val, O_RDONLY);
958                 if(exists_res == -1 && errno == ENOENT) {
959                         res = spew_entry(entry, &newpath);
960                         if(res != -1) {
961                                 /* if this fails we should log something bad */
962                                 res = unlink(path.bv_val);
963                                 res = LDAP_SUCCESS;
964                         }
965                         else {
966                                 if(errno == ENOENT)
967                                         res = LDAP_NO_SUCH_OBJECT;
968                                 else
969                                         res = LDAP_UNWILLING_TO_PERFORM;
970                                 unlink(newpath.bv_val); /* in case file was created */
971                         }
972                 }
973                 else if(exists_res) {
974                         int close_res = close(exists_res);
975                         res = LDAP_ALREADY_EXISTS;
976                         if(close_res == -1) {
977                         /* log heinous error */
978                         }
979                 }
980                 else {
981                         res = LDAP_UNWILLING_TO_PERFORM;
982                 }
983         }
984
985         if(newpath.bv_val != NULL)
986                 SLAP_FREE(newpath.bv_val);
987         if(path.bv_val != NULL)
988                 SLAP_FREE(path.bv_val);
989         return res;
990 }
991
992 static int
993 ldif_back_modrdn(Operation *op, SlapReply *rs)
994 {
995         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
996         struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
997         struct berval p_dn;
998         Entry * entry = NULL;
999         int res;
1000
1001         slap_mods_opattrs( op, &op->orr_modlist, 1 );
1002
1003         ldap_pvt_thread_mutex_lock( &ni->li_mutex );
1004         ldap_pvt_thread_mutex_lock( &entry2str_mutex );
1005         entry = (Entry *) get_entry( op, &ni->li_base_path );
1006
1007         /* build the mods to the entry */
1008         if ( entry != NULL ) {
1009                 /* build new dn, and new ndn for the entry */
1010                 if ( op->oq_modrdn.rs_newSup != NULL ) {
1011                         /* new superior */
1012                         p_dn = *op->oq_modrdn.rs_newSup;
1013                 } else {
1014                         p_dn = slap_empty_bv;
1015                 }
1016                 dnParent( &entry->e_name, &p_dn );
1017                 build_new_dn( &new_dn, &p_dn, &op->oq_modrdn.rs_newrdn, NULL ); 
1018                 dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, NULL );
1019                 ber_memfree_x( entry->e_name.bv_val, NULL );
1020                 ber_memfree_x( entry->e_nname.bv_val, NULL );
1021                 entry->e_name = new_dn;
1022                 entry->e_nname = new_ndn;
1023
1024                 /* perform the modifications */
1025                 res = apply_modify_to_entry( entry, op->orr_modlist, op, rs );
1026                 if ( res == LDAP_SUCCESS ) {
1027                         rs->sr_err = move_entry( entry, &op->o_req_ndn,
1028                                                 &new_ndn,
1029                                                 &op->o_bd->be_nsuffix[0],
1030                                                 &ni->li_base_path );
1031                 } else {
1032                         rs->sr_err = res;
1033                 }
1034         } else {
1035                 /* entry was null */
1036                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1037         }
1038
1039         if ( entry != NULL ) {
1040                 entry_free( entry );
1041         }
1042         rs->sr_text = "";
1043         ldap_pvt_thread_mutex_unlock( &ni->li_mutex );
1044         ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
1045         send_ldap_result( op, rs );
1046         slap_graduate_commit_csn( op );
1047         return 0;
1048 }
1049
1050 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
1051  */
1052 int ldif_back_entry_get(
1053         Operation *op,
1054         struct berval *ndn,
1055         ObjectClass *oc,
1056         AttributeDescription *at,
1057         int rw,
1058         Entry **ent )
1059 {
1060         struct ldif_info *ni = (struct ldif_info *) op->o_bd->be_private;
1061         struct berval op_dn = op->o_req_dn, op_ndn = op->o_req_ndn;
1062
1063         assert( ndn != NULL );
1064         assert( !BER_BVISNULL( ndn ) );
1065
1066         ldap_pvt_thread_mutex_lock( &ni->li_mutex );
1067         op->o_req_dn = *ndn;
1068         op->o_req_ndn = *ndn;
1069         *ent = (Entry *) get_entry( op, &ni->li_base_path );
1070         op->o_req_dn = op_dn;
1071         op->o_req_ndn = op_ndn;
1072         ldap_pvt_thread_mutex_unlock( &ni->li_mutex );
1073
1074         if ( *ent && oc && !is_entry_objectclass_or_sub( *ent, oc ) ) {
1075                 entry_free( *ent );
1076                 *ent = NULL;
1077         }
1078
1079         return ( *ent == NULL ? 1 : 0 );
1080 }
1081
1082 static int ldif_tool_entry_open(BackendDB * be, int mode) {
1083         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1084         ni->li_tool_current = 0;
1085         return 0;
1086 }                                       
1087
1088 static int ldif_tool_entry_close(BackendDB * be) {
1089         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1090
1091         SLAP_FREE(ni->li_tool_cookie.entries);
1092         return 0;
1093 }
1094
1095 static ID
1096 ldif_tool_entry_first(BackendDB *be)
1097 {
1098         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1099         ID id = 1; /* first entry in the array of entries shifted by one */
1100
1101         ni->li_tool_current = 1;
1102         if(ni->li_tool_cookie.entries == NULL) {
1103                 Operation op = {0};
1104
1105                 op.o_bd = be;
1106                 op.o_req_dn = *be->be_suffix;
1107                 op.o_req_ndn = *be->be_nsuffix;
1108                 op.ors_scope = LDAP_SCOPE_SUBTREE;
1109                 ni->li_tool_cookie.op = &op;
1110                 (void)enum_tree( &ni->li_tool_cookie );
1111                 ni->li_tool_cookie.op = NULL;
1112         }
1113         return id;
1114 }
1115
1116 static ID ldif_tool_entry_next(BackendDB *be)
1117 {
1118         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1119         ni->li_tool_current += 1;
1120         if(ni->li_tool_current > ni->li_tool_cookie.eind)
1121                 return NOID;
1122         else
1123                 return ni->li_tool_current;
1124 }
1125
1126 static Entry * ldif_tool_entry_get(BackendDB * be, ID id) {
1127         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1128         Entry * e;
1129
1130         if(id > ni->li_tool_cookie.eind || id < 1)
1131                 return NULL;
1132         else {
1133                 e = ni->li_tool_cookie.entries[id - 1];
1134                 ni->li_tool_cookie.entries[id - 1] = NULL;
1135                 return e;
1136         }
1137 }
1138
1139 static ID ldif_tool_entry_put(BackendDB * be, Entry * e, struct berval *text) {
1140         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1141         struct berval dn = e->e_nname;
1142         struct berval leaf_path = BER_BVNULL;
1143         struct stat stats;
1144         int statres;
1145         int res = LDAP_SUCCESS;
1146
1147         dn2path(&dn, &be->be_nsuffix[0], &ni->li_base_path, &leaf_path);
1148
1149         if(leaf_path.bv_val != NULL) {
1150                 struct berval base = BER_BVNULL;
1151                 /* build path to container, and path to ldif of container */
1152                 get_parent_path(&leaf_path, &base);
1153
1154                 statres = stat(base.bv_val, &stats); /* check if container exists */
1155                 if(statres == -1 && errno == ENOENT) { /* container missing */
1156                         base.bv_val[base.bv_len] = '.';
1157                         statres = stat(base.bv_val, &stats); /* check for leaf node */
1158                         base.bv_val[base.bv_len] = '\0';
1159                         if(statres == -1 && errno == ENOENT) {
1160                                 res = LDAP_NO_SUCH_OBJECT; /* parent doesn't exist */
1161                         }
1162                         else if(statres != -1) { /* create parent */
1163                                 int mkdirres = mkdir(base.bv_val, 0750);
1164                                 if(mkdirres == -1) {
1165                                         res = LDAP_UNWILLING_TO_PERFORM;
1166                                 }
1167                         }
1168                         else
1169                                 res = LDAP_UNWILLING_TO_PERFORM;
1170                 }/* container was possibly created, move on to add the entry */
1171                 if(res == LDAP_SUCCESS) {
1172                         statres = stat(leaf_path.bv_val, &stats);
1173                         if(statres == -1 && errno == ENOENT) {
1174                                 res = (int) spew_entry(e, &leaf_path);
1175                         }
1176                         else /* it already exists */
1177                                 res = LDAP_ALREADY_EXISTS;
1178                 }
1179                 SLAP_FREE(base.bv_val);
1180                 SLAP_FREE(leaf_path.bv_val);
1181         }
1182
1183         if(res == LDAP_SUCCESS) {
1184                 return 1;
1185         }
1186         else
1187                 return NOID;
1188 }
1189
1190 static int
1191 ldif_back_db_init( BackendDB *be )
1192 {
1193         struct ldif_info *ni;
1194
1195         ni = ch_calloc( 1, sizeof(struct ldif_info) );
1196         be->be_private = ni;
1197         be->be_cf_ocs = ldifocs;
1198         ldap_pvt_thread_mutex_init(&ni->li_mutex);
1199         return 0;
1200 }
1201
1202 static int
1203 ldif_back_db_destroy(
1204                            Backend      *be
1205                            )
1206 {
1207         struct ldif_info *ni = be->be_private;
1208
1209         ch_free(ni->li_base_path.bv_val);
1210         ldap_pvt_thread_mutex_destroy(&ni->li_mutex);
1211         free( be->be_private );
1212         return 0;
1213 }
1214
1215 static int
1216 ldif_back_db_open(
1217                         Backend *be
1218                         )
1219 {
1220         struct ldif_info *ni = (struct ldif_info *) be->be_private;
1221         if( BER_BVISEMPTY(&ni->li_base_path)) {/* missing base path */
1222                 Debug( LDAP_DEBUG_ANY, "missing base path for back-ldif\n", 0, 0, 0);
1223                 return 1;
1224         }
1225         return 0;
1226 }
1227
1228 int
1229 ldif_back_initialize(
1230                            BackendInfo  *bi
1231                            )
1232 {
1233         static char *controls[] = {
1234                 LDAP_CONTROL_MANAGEDSAIT,
1235                 NULL
1236         };
1237         int rc;
1238
1239         bi->bi_flags |=
1240                 SLAP_BFLAG_INCREMENT |
1241                 SLAP_BFLAG_REFERRALS;
1242
1243         bi->bi_controls = controls;
1244
1245         bi->bi_open = 0;
1246         bi->bi_close = 0;
1247         bi->bi_config = 0;
1248         bi->bi_destroy = 0;
1249
1250         bi->bi_db_init = ldif_back_db_init;
1251         bi->bi_db_config = config_generic_wrapper;
1252         bi->bi_db_open = ldif_back_db_open;
1253         bi->bi_db_close = 0;
1254         bi->bi_db_destroy = ldif_back_db_destroy;
1255
1256         bi->bi_op_bind = ldif_back_bind;
1257         bi->bi_op_unbind = 0;
1258         bi->bi_op_search = ldif_back_search;
1259         bi->bi_op_compare = 0;
1260         bi->bi_op_modify = ldif_back_modify;
1261         bi->bi_op_modrdn = ldif_back_modrdn;
1262         bi->bi_op_add = ldif_back_add;
1263         bi->bi_op_delete = ldif_back_delete;
1264         bi->bi_op_abandon = 0;
1265
1266         bi->bi_extended = 0;
1267
1268         bi->bi_chk_referrals = ldif_back_referrals;
1269
1270         bi->bi_connection_init = 0;
1271         bi->bi_connection_destroy = 0;
1272
1273         bi->bi_entry_get_rw = ldif_back_entry_get;
1274
1275 #if 0   /* NOTE: uncomment to completely disable access control */
1276         bi->bi_access_allowed = slap_access_always_allowed;
1277 #endif
1278
1279         bi->bi_tool_entry_open = ldif_tool_entry_open;
1280         bi->bi_tool_entry_close = ldif_tool_entry_close;
1281         bi->bi_tool_entry_first = ldif_tool_entry_first;
1282         bi->bi_tool_entry_next = ldif_tool_entry_next;
1283         bi->bi_tool_entry_get = ldif_tool_entry_get;
1284         bi->bi_tool_entry_put = ldif_tool_entry_put;
1285         bi->bi_tool_entry_reindex = 0;
1286         bi->bi_tool_sync = 0;
1287         
1288         bi->bi_tool_dn2id_get = 0;
1289         bi->bi_tool_id2entry_get = 0;
1290         bi->bi_tool_entry_modify = 0;
1291
1292         bi->bi_cf_ocs = ldifocs;
1293
1294         rc = config_register_schema( ldifcfg, ldifocs );
1295         if ( rc ) return rc;
1296         return 0;
1297 }