]> git.sur5r.net Git - openldap/blob - servers/slapd/tools/centipede.c
Added support for ldap.conf file. See ldap.conf(5) for details.
[openldap] / servers / slapd / tools / centipede.c
1 /* centipede.c - generate and install indexing information (view w/tabstop=4) */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/ctype.h>
8 #include <ac/string.h>
9 #include <ac/time.h>
10
11 #include <lber.h>
12 #include <ldap.h>
13
14 #include <ldbm.h>
15
16 #define DEFAULT_LDAPFILTER      "(objectclass=*)"
17
18 #define CENTROID_VALUE  1
19 #define CENTROID_WORD   2
20
21 #define CENTROID_RELATIVE       1
22 #define CENTROID_FULL           2
23
24 #define WORD_BREAKS     " -',.()!;:&$%*\"/\\+_<>=?[]|^~"
25
26 char    *centdir;
27 int             ldbmcachesize;
28 int             centroidvalues;
29 int             centroidtype;
30 int             doweights;
31 char    *ldaphost;
32 char    *srcldapbinddn;
33 char    *srcldappasswd;
34 char    *destldapbinddn;
35 char    *destldappasswd;
36 char    *ldapbase;
37 int             srcldapauthmethod;
38 int             destldapauthmethod;
39 int             verbose;
40 int             not;
41
42 static LDAP             *start_ldap_search();
43 static LDAP             *bind_to_destination_ldap();
44 static int              create_tmp_files();
45 static int              generate_new_centroids();
46 static LDAPMod  **diff_centroids();
47 static LDAPMod  **full_centroid();
48 static char             **charray_add_dup();
49
50 static void usage( char *name )
51 {
52         fprintf( stderr, "usage: %s [options] -s url -d url attributes\n", name );
53         fprintf( stderr, "where:\n" );
54         fprintf( stderr, "\t-s url\t\t[[ldap://][host[:port]]/]searchbasedn\n");
55         fprintf( stderr, "\t-d url\t\t[[ldap://][host[:port]]/]centroidentrydn\n");
56         fprintf( stderr, "options:\n" );
57         fprintf( stderr, "\t-v \t\tturn on verbose mode\n" );
58         fprintf( stderr, "\t-n \t\tgenerate, but do not install index info\n" );
59         fprintf( stderr, "\t-f filter\tentry selection filter\n" );
60         fprintf( stderr, "\t-F \t\tgenerate a full centroid\n" );
61         fprintf( stderr, "\t-R \t\tgenerate a relative centroid\n" );
62         fprintf( stderr, "\t-w \t\tgenerate a word-based centroid\n" );
63         fprintf( stderr, "\t-t directory\tcentroid directory\n" );
64         fprintf( stderr, "\t-b binddn\tsource bind dn\n" );
65         fprintf( stderr, "\t-p passwd\tsource bind passwd (for simple auth)\n" );
66         fprintf( stderr, "\t-m authmethod\tsource authmethod \"simple\" or \"kerberos\"\n" );
67         fprintf( stderr, "\t-B binddn\tdestination bind dn\n" );
68         fprintf( stderr, "\t-P passwd\tdestination bind passwd (for simple auth)\n" );
69         fprintf( stderr, "\t-M authmethod\tdestination authmethod \"simple\" or \"kerberos\"\n" );
70         fprintf( stderr, "\t-c size\t\tldbm cache size\n" );
71 }
72
73 main( int argc, char **argv )
74 {
75         char            *ldapfilter;
76         char            *ldapsrcurl, *ldapdesturl;
77         LDAP            *ld;
78         LDAPMod         **mods;
79         char            **attrs;
80         char            **tmpfile;
81         LDBM            *ldbm;
82         LDBM            oldbm;
83         char            buf[BUFSIZ];
84         int                     i, j, k, count;
85         char            *s;
86         extern int      optind;
87         extern char     *optarg;
88
89         ldapsrcurl = NULL;
90         ldapdesturl = NULL;
91         ldaphost = NULL;
92         ldapbase = NULL;
93         srcldapauthmethod = LDAP_AUTH_SIMPLE;
94         destldapauthmethod = LDAP_AUTH_SIMPLE;
95         srcldapbinddn = NULL;
96         srcldappasswd = NULL;
97         destldapbinddn = NULL;
98         destldappasswd = NULL;
99         ldapfilter = DEFAULT_LDAPFILTER;
100         centroidvalues = CENTROID_VALUE;
101         centroidtype = CENTROID_RELATIVE;
102         centdir = NULL;
103         tmpfile = NULL;
104         ldbmcachesize = 0;
105
106         while ( (i = getopt( argc, argv, "s:d:c:b:B:f:FRWp:P:m:M:t:vwn" ))
107             != EOF ) {
108                 switch ( i ) {
109                 case 's':       /* source url [[ldap://][host[:port]]/]basedn */
110                         ldapsrcurl = strdup( optarg );
111                         break;
112
113                 case 'd':       /* destination url [[ldap://][host[:port]]/]entrydn */
114                         ldapdesturl = strdup( optarg );
115                         break;
116
117                 case 'f':       /* specify a filter */
118                         ldapfilter = strdup( optarg );
119                         break;
120
121                 case 'F':       /* generate full centroid */
122                         centroidtype = CENTROID_FULL;
123                         break;
124
125                 case 'R':       /* generate relative centroid */
126                         centroidtype = CENTROID_RELATIVE;
127                         break;
128
129                 case 'w':       /* generate word centroid */
130                         centroidvalues = CENTROID_WORD;
131                         break;
132
133                 case 'W':       /* generate weights */
134                         doweights = 1;
135                         break;
136
137                 case 't':       /* temp file directory */
138                         centdir = strdup( optarg );
139                         break;
140
141                 case 'b':       /* src bind dn */
142                         srcldapbinddn = strdup( optarg );
143                         break;
144
145                 case 'p':       /* src bind password */
146                         srcldappasswd = strdup( optarg );
147                         while ( *optarg )
148                                 *optarg++ = 'x';
149                         break;
150
151                 case 'B':       /* dest bind dn */
152                         destldapbinddn = strdup( optarg );
153                         break;
154
155                 case 'P':       /* dest bind password */
156                         destldappasswd = strdup( optarg );
157                         while ( *optarg )
158                                 *optarg++ = 'x';
159                         break;
160
161                 case 'm':       /* src bind method */
162                         if ( strcasecmp( optarg, "simple" ) == 0 ) {
163                                 srcldapauthmethod = LDAP_AUTH_SIMPLE;
164                         } else if ( strcasecmp( optarg, "kerberos" ) == 0 ) {
165                                 srcldapauthmethod = LDAP_AUTH_KRBV4;
166                         } else {
167                                 fprintf( stderr, "%s: unknown auth method\n", optarg );
168                                 fputs( "expecting \"simple\" or \"kerberos\"\n", stderr );
169                                 exit( 1 );
170                         }
171                         break;
172
173                 case 'M':       /* dest bind method */
174                         if ( strcasecmp( optarg, "simple" ) == 0 ) {
175                                 destldapauthmethod = LDAP_AUTH_SIMPLE;
176                         } else if ( strcasecmp( optarg, "kerberos" ) == 0 ) {
177                                 destldapauthmethod = LDAP_AUTH_KRBV4;
178                         } else {
179                                 fprintf( stderr, "%s: unknown auth method\n", optarg );
180                                 fputs( "expecting \"simple\" or \"kerberos\"\n", stderr );
181                                 exit( 1 );
182                         }
183                         break;
184
185                 case 'c':       /* ldbm cache size */
186                         ldbmcachesize = atoi( optarg );
187                         break;
188
189                 case 'v':       /* turn on verbose mode */
190                         verbose++;
191                         break;
192
193                 case 'n':       /* don't actually install index info */
194                         not++;
195                         break;
196
197                 default:
198                         usage( argv[0] );
199                         exit( 1 );
200                 }
201         }
202         if ( optind == argc || ldapsrcurl == NULL || ldapdesturl == NULL ) {
203                 usage( argv[0] );
204                 exit( 1 );
205         }
206         attrs = &argv[optind];
207
208         /*
209          * open the ldap connection and start searching for the entries
210          * we will use to generate the centroids.
211          */
212
213         if ( (ld = start_ldap_search( ldapsrcurl, ldapfilter, attrs )) == NULL ) {
214                 fprintf( stderr, "could not initiate ldap search\n" );
215                 exit( 1 );
216         }
217
218         if ( create_tmp_files( attrs, &tmpfile, &ldbm ) != 0 ) {
219                 fprintf( stderr, "could not create temp files\n" );
220                 exit( 1 );
221         }
222
223         /*
224          * go through the entries returned, building a centroid for each
225          * attribute as we go.
226          */
227
228         if ( (count = generate_new_centroids( ld, attrs, ldbm )) < 1 ) {
229                 if ( count == 0 ) {
230                     fprintf( stderr, "no entries matched\n" );
231                     exit( 0 );
232                 } else {
233                     fprintf( stderr, "could not generate new centroid\n" );
234                     exit( 1 );
235                 }
236         }
237
238         /*
239          * for each centroid we generated above, compare to the existing
240          * centroid, if any, and produce adds and deletes, or produce
241          * an entirely new centroid. in either case, update the "current"
242          * centroid version with the new one we just generated.
243          */
244
245         if ( (ld = bind_to_destination_ldap( ldapsrcurl, ldapdesturl )) == NULL ) {
246                 fprintf( stderr,
247                   "could not bind to index server, or could not create index entry\n" );
248                 exit( 1 );
249         }
250
251         for ( i = 0; ldbm[i] != NULL; i++ ) {
252                 /* generate the name of the existing centroid, if any */
253                 s = strrchr( tmpfile[i], '/' );
254                 *s = '\0';
255                 sprintf( buf, "%s/cent.%s", tmpfile[i], attrs[i] );
256                 *s = '/';
257
258                 /* generate the full centroid changes */
259                 if ( centroidtype == CENTROID_FULL || (oldbm = ldbm_open( buf,
260                   LDBM_WRITER, 0, ldbmcachesize )) == NULL ) {
261                         if ( (mods = full_centroid( attrs[i], ldbm[i], count )) == NULL ) {
262                                 fprintf( stderr, "could not produce full centroid for %s\n",
263                                   attrs[i] );
264                                 continue;
265                         }
266
267                 /* generate the differential centroid changes */
268                 } else {
269                         if ( (mods = diff_centroids( attrs[i], oldbm, ldbm[i], count ))
270                           == NULL ) {
271                                 fprintf( stderr, "could not diff centroids\n" );
272                                 ldbm_close( oldbm );
273                                 continue;
274                         }
275                         ldbm_close( oldbm );
276                 }
277
278                 if ( verbose > 1 ) {
279                         printf("changes:\n");
280                         for ( j = 0; mods[j] != NULL; j++ ) {
281                                 switch( mods[j]->mod_op ) {
282                                 case LDAP_MOD_ADD:
283                                         printf( "\tadd: %s\n",mods[j]->mod_type );
284                                         break;
285                                 case LDAP_MOD_DELETE:
286                                         printf( "\tdelete: %s\n",mods[j]->mod_type );
287                                         break;
288                                 case LDAP_MOD_REPLACE:
289                                         printf( "\treplace: %s\n",mods[j]->mod_type );
290                                         break;
291                                 }
292                                 if ( mods[j]->mod_values != NULL ) {
293                                         for ( k = 0; mods[j]->mod_values[k] != NULL; k++ ) {
294                                                 printf( "\t\t%s\n", mods[j]->mod_values[k] );
295                                         }
296                                 }
297                         }
298                         printf("end changes:\n");
299                 }
300
301                 if ( verbose ) {
302                         printf( "%sModifying centroid...", not ? "Not " : "" );
303                         fflush( stdout );
304                 }
305
306                 /* attempt to make the changes to the index server entry */
307                 if ( !not && ldap_modify_s( ld, ldapbase, mods ) != LDAP_SUCCESS ) {
308                         fprintf( stderr, "could not apply centroid modification for %s\n",
309                           attrs[i] );
310                         ldap_perror( ld, ldapbase );
311                 }
312                 ldap_mods_free( mods, 1 );
313
314                 if ( verbose ) {
315                         printf( "\n" );
316                         fflush( stdout );
317                 }
318
319                 /* move the new centroid into the old one's place */
320                 if ( ! not ) {
321                         (void) unlink( buf );
322                         if ( link( tmpfile[i], buf ) != 0 ) {
323                                 perror( "link" );
324                                 fprintf( stderr, "could not rename %s to %s\n", buf,
325                                   tmpfile[i] );
326                                 continue;
327                         }
328                 }
329                 (void) unlink( tmpfile[i] );
330         }
331
332         /* clean up */
333         for ( i = 0; attrs[i] != NULL; i++ ) {
334                 ldbm_close( ldbm[i] );
335                 free( tmpfile[i] );
336         }
337         free( ldbm );
338         free( tmpfile );
339
340         exit( 0 );
341 }
342
343 /*
344  * open an ldap connection, bind, and initiate the search
345  */
346
347 static LDAP *
348 start_ldap_search(
349         char    *ldapsrcurl,
350         char    *ldapfilter,
351         char    **attrs
352 )
353 {
354         LDAP    *ld;
355         char    *s, *s2;
356         int             i;
357
358         if ( strncmp( ldapsrcurl, "ldap://", 7 ) != 0 ) {
359                 fputs( "Not an LDAP URL", stderr ); /* Should be smarter? */
360                 return( NULL );
361         }
362         s = ldapsrcurl + 7;
363         if ( (s2 = strchr( s, '/' )) == NULL ) {
364                 ldapbase = strdup( s );
365         } else {
366                 if ( *s != '/' ) {
367                         *s2 = '\0';
368                         ldaphost = strdup( s );
369                         *s2 = '/';
370                 }
371                 ldapbase = strdup( s2 + 1 );
372         }
373
374         if ( verbose ) {
375                 printf( "Base: %s\n", ldapbase );
376                 printf( "Attributes:" );
377                 for ( i = 0; attrs[i] != NULL; i++ ) {
378                         printf( " %s", attrs[i] );
379                 }
380                 printf( "\n" );
381                 printf( "Binding to source LDAP server..." );
382                 fflush( stdout );
383         }
384
385         if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
386                 perror( "ldap_open" );
387                 return( NULL );
388         }
389
390         if ( ldap_bind_s( ld, srcldapbinddn, srcldappasswd, srcldapauthmethod )
391           != LDAP_SUCCESS) {
392                 ldap_perror( ld, "ldap_bind_s" );
393                 ldap_unbind( ld );
394                 return( NULL );
395         }
396
397         printf( "\nInitiating search..." );
398         if ( ldap_search( ld, ldapbase, LDAP_SCOPE_SUBTREE, ldapfilter, attrs, 0 )
399           == -1 ) {
400                 ldap_perror( ld, "ldap_search" );
401                 ldap_unbind( ld );
402                 return( NULL );
403         }
404
405         if ( verbose ) {
406                 printf( "\n" );
407         }
408
409         return( ld );
410 }
411
412 /*
413  * create the temporary ldbm files we will use to hold the new centroids
414  */
415
416 static int
417 create_tmp_files(
418         char    **attrs,
419         char    ***tmpfile,
420         LDBM    **ldbm
421 )
422 {
423         int     i;
424
425         for ( i = 0; attrs[i] != NULL; i++ )
426                 ;       /* NULL */
427         i++;
428
429         if ( (*tmpfile = (char **) malloc( i * sizeof(char *) )) == NULL ) {
430                 perror( "malloc" );
431                 return( -1 );
432         }
433         if ( (*ldbm = (LDBM *) malloc( i * sizeof(LDBM) )) == NULL ) {
434                 perror( "malloc" );
435                 return( -1 );
436         }
437         for ( i = 0; attrs[i] != NULL; i++ ) {
438                 if ( ((*tmpfile)[i] = tempnam( centdir, NULL )) == NULL ) {
439                         perror( "tmpnam" );
440                         return( -1 );
441                 }
442
443                 if ( ((*ldbm)[i] = ldbm_open( (*tmpfile)[i], LDBM_WRCREAT, 0600,
444                   ldbmcachesize )) == NULL ) {
445                         fprintf( stderr, "ldbm_open of \"%s\" failed\n", (*tmpfile)[i] );
446                         perror( "ldbm_open" );
447                         return( -1 );
448                 }
449         }
450         (*tmpfile)[i] = NULL;
451         (*ldbm)[i] = NULL;
452
453         return( 0 );
454 }
455
456 /*
457  * step through each entry returned from the search and generate
458  * the appropriate centroid values.
459  */
460
461 static int
462 generate_new_centroids(
463         LDAP    *ld,
464         char    **attrs,
465         LDBM    *ldbm
466 )
467 {
468         Datum           key, data;
469         int                     rc, i, j, count;
470         LDAPMessage     *res, *e;
471         char            *dn, *s, *w;
472         char            **val;
473         char            last;
474
475         if ( verbose ) {
476                 printf( "Generating new centroids for..." );
477                 fflush( stdout );
478         }
479
480         data.dptr = "";
481         data.dsize = 1;
482         count = 0;
483         while ( (rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ))
484           == LDAP_RES_SEARCH_ENTRY ) {
485                 count++;
486                 e = ldap_first_entry( ld, res );
487                 dn = ldap_get_dn( ld, e );
488
489                 /* for each attr we want to generate a centroid for */
490                 for ( i = 0; attrs[i] != NULL; i++ ) {
491                         if ( (val = ldap_get_values( ld, e, attrs[i] )) == NULL ) {
492                                 continue;
493                         }
494
495                         /* for each value */
496                         for ( j = 0; val[j] != NULL; j++ ) {
497                                 /* normalize the value */
498                                 for ( s = val[j]; *s; s++ ) {
499                                         if ( isascii( *s ) ) {
500                                                 *s = TOLOWER( *s );
501                                         }
502                                         last = *s;
503                                 }
504                                 if ( isascii( last ) && isdigit( last ) ) {
505                                         continue;
506                                 }
507
508                                 /* generate a value-based centroid */
509                                 if ( centroidvalues == CENTROID_VALUE ) {
510                                         key.dptr = val[j];
511                                         key.dsize = strlen( key.dptr ) + 1;
512                                         (void) ldbm_store( ldbm[i], key, data, LDBM_INSERT );
513
514                                 /* generate a word-based centroid */
515                                 } else {
516                                         for ( w = strtok( val[j], WORD_BREAKS ); w != NULL;
517                                           w = strtok( NULL, WORD_BREAKS ) ) {
518                                                 key.dptr = w;
519                                                 key.dsize = strlen( key.dptr ) + 1;
520                                                 (void) ldbm_store( ldbm[i], key, data, LDBM_INSERT );
521                                         }
522                                 }
523                         }
524                         ldap_value_free( val );
525                 }
526                 free( dn );
527                 ldap_msgfree( res );
528         }
529         ldap_msgfree( res );
530         ldap_unbind( ld );
531
532         if ( verbose ) {
533                 printf( "%d entries\n", count );
534         }
535
536         return( count );
537 }
538
539 /*
540  * compare the old and new centroids, generating the appropriate add
541  * and delete operations. if the underlying database is ordered, we
542  * can do this more efficiently.
543  */
544
545 static LDAPMod **
546 diff_centroids(
547         char    *attr,
548         LDBM    oldbm,
549         LDBM    nldbm,
550     int         nentries
551 )
552 {
553         Datum   okey, nkey;
554         Datum   olast, nlast;
555         Datum   lastkey, key;
556         Datum   data;
557         int             rc;
558         LDAPMod **mods;
559         char    **avals, **dvals;
560         int             amax, acur, dmax, dcur;
561         char    **vals;
562
563 #ifdef HAVE_BERKELEY_DB2
564         DBC     *ocursorp;
565         DBC     *ncursorp;
566 #endif /* HAVE_BERKELEY_DB2 */
567
568         if ( verbose ) {
569                 printf( "Generating mods for differential %s centroid...", attr );
570                 fflush( stdout );
571         }
572
573         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 4 )) == NULL ||
574              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
575              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
576              (mods[2] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
577              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
578                  (vals[0] = (char *) malloc( 20 )) == NULL )
579         {
580                 perror( "malloc" );
581                 exit( -1 );
582         }
583         /* add values in mods[0] */
584         mods[0]->mod_op = LDAP_MOD_ADD;
585         mods[0]->mod_type = attr;
586         mods[0]->mod_values = NULL;
587         avals = NULL;
588         acur = amax = 0;
589         /* delete values in mods[1] */
590         mods[1]->mod_op = LDAP_MOD_DELETE;
591         mods[1]->mod_type = attr;
592         mods[1]->mod_values = NULL;
593         dvals = NULL;
594         dcur = dmax = 0;
595         /* number of entries in mods[2] */
596         sprintf( vals[0], "%d", nentries );
597         vals[1] = NULL;
598         mods[2]->mod_op = LDAP_MOD_REPLACE;
599         mods[2]->mod_type = "nentries";
600         mods[2]->mod_values = vals;
601         /* null terminate list of mods */
602         mods[3] = NULL;
603
604 #ifdef LDBM_ORDERED
605         /*
606          * if the underlying database is ordered, we can do a more efficient
607          * dual traversal, yielding O(N) performance.
608          */
609
610         olast.dptr = NULL;
611         nlast.dptr = NULL;
612 #ifdef HAVE_BERKELEY_DB2
613         for ( okey = ldbm_firstkey( oldbm, &ocursorp ),
614                         nkey = ldbm_firstkey( nldbm, &ncursorp );
615               okey.dptr != NULL && nkey.dptr != NULL; )
616 #else
617         for ( okey = ldbm_firstkey( oldbm ), nkey = ldbm_firstkey( nldbm );
618               okey.dptr != NULL && nkey.dptr != NULL; )
619 #endif
620         {
621                 rc = strcmp( okey.dptr, nkey.dptr );
622
623                 if ( rc == 0 ) {
624                         /* value is in both places - leave it */
625                         if ( olast.dptr != NULL ) {
626                                 ldbm_datum_free( oldbm, olast );
627                         }
628                         olast = okey;
629                         if ( nlast.dptr != NULL ) {
630                                 ldbm_datum_free( nldbm, nlast );
631                         }
632                         nlast = nkey;
633
634 #ifdef HAVE_BERKELEY_DB2
635                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
636                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
637 #else
638                         okey = ldbm_nextkey( oldbm, olast );
639                         nkey = ldbm_nextkey( nldbm, nlast );
640 #endif
641                 } else if ( rc > 0 ) {
642                         /* new value is not in old centroid - add it */
643                         if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
644                                 ldap_mods_free( mods, 1 );
645                                 return( NULL );
646                         }
647
648                         if ( nlast.dptr != NULL ) {
649                                 ldbm_datum_free( nldbm, nlast );
650                         }
651                         nlast = nkey;
652
653 #ifdef HAVE_BERKELEY_DB2
654                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
655 #else
656                         nkey = ldbm_nextkey( nldbm, nlast );
657 #endif
658                 } else {
659                         /* old value is not in new centroid - delete it */
660                         if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
661                                 ldap_mods_free( mods, 1 );
662                                 return( NULL );
663                         }
664
665                         if ( olast.dptr != NULL ) {
666                                 ldbm_datum_free( oldbm, olast );
667                         }
668                         olast = okey;
669
670 #ifdef HAVE_BERKELEY_DB2
671                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
672 #else
673                         okey = ldbm_nextkey( oldbm, olast );
674 #endif
675                 }
676         }
677
678         while ( okey.dptr != NULL ) {
679                 if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
680                         ldap_mods_free( mods, 1 );
681                         return( NULL );
682                 }
683
684 #ifdef HAVE_BERKELEY_DB2
685                 okey = ldbm_nextkey( oldbm, olast, ocursorp );
686 #else
687                 okey = ldbm_nextkey( oldbm, olast );
688 #endif
689                 if ( olast.dptr != NULL ) {
690                         ldbm_datum_free( oldbm, olast );
691                 }
692                 olast = okey;
693         }
694         if ( olast.dptr != NULL ) {
695                 ldbm_datum_free( oldbm, olast );
696         }
697         while ( nkey.dptr != NULL ) {
698                 if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
699                         ldap_mods_free( mods, 1 );
700                         return( NULL );
701                 }
702
703 #ifdef HAVE_BERKELEY_DB2
704                 nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
705 #else
706                 nkey = ldbm_nextkey( nldbm, nlast );
707 #endif
708                 if ( nlast.dptr != NULL ) {
709                         ldbm_datum_free( nldbm, nlast );
710                 }
711                 nlast = nkey;
712         }
713         if ( nlast.dptr != NULL ) {
714                 ldbm_datum_free( nldbm, nlast );
715         }
716 #else
717         /*
718          * if the underlying database is not ordered, we have to
719          * generate list of values to add by stepping through all new
720          * values and looking them up in the old centroid (not there => add),
721          * then stepping through all old values and looking them up in the
722          * new centroid (not there => delete). this yields O(Nf(N)) performance,
723          * where f(N) is the order to retrieve a single item.
724          */
725
726         /* generate list of values to add */
727         lastkey.dptr = NULL;
728 #ifdef HAVE_BERKELEY_DB2
729         for ( key = ldbm_firstkey( nldbm, &ncursorp ); key.dptr != NULL;
730           key = ldbm_nextkey( nldbm, lastkey, ncursorp ) )
731 #else
732         for ( key = ldbm_firstkey( nldbm ); key.dptr != NULL;
733           key = ldbm_nextkey( nldbm, lastkey ) )
734 #endif
735         {
736                 /* see if it's in the old one */
737                 data = ldbm_fetch( oldbm, key );
738
739                 /* not there - add it */
740                 if ( data.dptr == NULL ) {
741                         if ( charray_add_dup( &avals, &acur, &amax, key.dptr ) == NULL ) {
742                                 ldap_mods_free( mods, 1 );
743                                 return( NULL );
744                         }
745                 } else {
746                         ldbm_datum_free( oldbm, data );
747                 }
748                 if ( lastkey.dptr != NULL ) {
749                         ldbm_datum_free( nldbm, lastkey );
750                 }
751                 lastkey = key;
752         }
753         if ( lastkey.dptr != NULL ) {
754                 ldbm_datum_free( nldbm, lastkey );
755         }
756
757         /* generate list of values to delete */
758         lastkey.dptr = NULL;
759 #ifdef HAVE_BERKELEY_DB2
760         for ( key = ldbm_firstkey( oldbm, &ocursorp ); key.dptr != NULL;
761           key = ldbm_nextkey( oldbm, lastkey, ocursorp ) )
762 #else
763         for ( key = ldbm_firstkey( oldbm ); key.dptr != NULL;
764           key = ldbm_nextkey( oldbm, lastkey ) )
765 #endif
766         {
767                 /* see if it's in the new one */
768                 data = ldbm_fetch( nldbm, key );
769
770                 /* not there - delete it */
771                 if ( data.dptr == NULL ) {
772                         if ( charray_add_dup( &dvals, &dcur, &dmax, key.dptr ) == NULL ) {
773                                 ldap_mods_free( mods, 1 );
774                                 return( NULL );
775                         }
776                 } else {
777                         ldbm_datum_free( nldbm, data );
778                 }
779                 if ( lastkey.dptr != NULL ) {
780                         ldbm_datum_free( oldbm, lastkey );
781                 }
782                 lastkey = key;
783         }
784         if ( lastkey.dptr != NULL ) {
785                 ldbm_datum_free( oldbm, lastkey );
786         }
787 #endif
788
789         mods[0]->mod_values = avals;
790         mods[1]->mod_values = dvals;
791
792         if ( verbose ) {
793                 printf( "\n" );
794                 fflush( stdout );
795         }
796
797         if ( mods[1]->mod_values == NULL ) {
798                 free( (char *) mods[1] );
799                 mods[1] = NULL;
800         }
801         if ( mods[0]->mod_values == NULL ) {
802                 free( (char *) mods[0] );
803                 mods[0] = mods[1];
804                 mods[1] = NULL;
805         }
806         if ( mods[0] == NULL ) {
807                 free( (char *) mods );
808                 return( NULL );
809         } else {
810                 return( mods );
811         }
812 }
813
814 static LDAPMod **
815 full_centroid(
816         char    *attr,
817         LDBM    ldbm,
818     int         nentries
819 )
820 {
821         Datum   key, lastkey;
822         LDAPMod **mods;
823         char    **vals;
824         int             vcur, vmax;
825
826 #ifdef HAVE_BERKELEY_DB2
827         DBC *cursorp;
828 #endif
829
830         if ( verbose ) {
831                 printf( "Generating mods for full %s centroid...", attr );
832                 fflush( stdout );
833         }
834
835         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 3 )) == NULL ||
836              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
837              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
838              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
839              (vals[0] = (char *) malloc( 20 )) == NULL )
840         {
841                 perror( "malloc" );
842                 exit( -1 );
843         }
844         mods[0]->mod_op = LDAP_MOD_REPLACE;
845         mods[0]->mod_type = attr;
846         mods[0]->mod_values = NULL;
847         sprintf( vals[0], "%d", nentries );
848         vals[1] = NULL;
849         mods[1]->mod_op = LDAP_MOD_REPLACE;
850         mods[1]->mod_type = "nentries";
851         mods[1]->mod_values = vals;
852         mods[2] = NULL;
853
854         lastkey.dptr = NULL;
855         vals = NULL;
856         vcur = vmax = 0;
857 #ifdef HAVE_BERKELEY_DB2
858         for ( key = ldbm_firstkey( ldbm, &cursorp ); key.dptr != NULL;
859           key = ldbm_nextkey( ldbm, lastkey, cursorp ) )
860 #else
861         for ( key = ldbm_firstkey( ldbm ); key.dptr != NULL;
862           key = ldbm_nextkey( ldbm, lastkey ) )
863 #endif
864         {
865                 if ( charray_add_dup( &vals, &vcur, &vmax, key.dptr ) == NULL ) {
866                         ldap_mods_free( mods, 1 );
867                         return( NULL );
868                 }
869
870                 if ( lastkey.dptr != NULL ) {
871                         ldbm_datum_free( ldbm, lastkey );
872                 }
873                 lastkey = key;
874         }
875         if ( lastkey.dptr != NULL ) {
876                 ldbm_datum_free( ldbm, lastkey );
877         }
878         mods[0]->mod_values = vals;
879
880         if ( verbose ) {
881                 printf( "\n" );
882                 fflush( stdout );
883         }
884
885         if ( mods[0]->mod_values == NULL ) {
886                 free( (char *) mods[0] );
887                 free( (char *) mods );
888                 return( NULL );
889         } else {
890                 return( mods );
891         }
892 }
893
894 /*
895  * extract the destination ldap host, port, and base object for the
896  * server to receive the index information. then, open a connection,
897  * bind, and see if the entry exists. if not, create it and set things
898  * up so the centroid full and diff routines can modify it to contain
899  * the new centroid information.
900  */
901
902 static LDAP *
903 bind_to_destination_ldap(
904         char    *ldapsrcurl,
905         char    *ldapdesturl
906 )
907 {
908         LDAP            *ld;
909         LDAPMessage     *res;
910         int                     rc;
911         char            *s, *s2, *d;
912         char            *attrs[2], *refvalues[2], *ocvalues[2];
913         LDAPMod         *mp[3];
914         LDAPMod         m[2];
915         char            buf[BUFSIZ];
916
917         if ( verbose ) {
918                 printf( "Binding to destination LDAP server..." );
919                 fflush( stdout );
920         }
921
922         /* first, pick out the destination ldap server info */
923         if ( ldapbase != NULL ) {
924                 free( ldapbase );
925                 ldapbase = NULL;
926         }
927         if ( strncmp( ldapdesturl, "ldap://", 7 ) != 0 ) {
928                 fputs( "Not an LDAP URL", stderr ); /* Should be smarter? */
929                 return( NULL );
930         }
931         s = ldapdesturl + 7;
932         if ( (s2 = strchr( s, '/' )) == NULL ) {
933                 ldapbase = strdup( s );
934         } else {
935                 if ( *s != '/' ) {
936                         *s2 = '\0';
937                         if ( ldaphost != NULL )
938                                 free( ldaphost );
939                         ldaphost = strdup( s );
940                         *s2 = '/';
941                 }
942                 ldapbase = strdup( s2 + 1 );
943         }
944         strcpy( buf, "ref=" );
945         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
946                 strcat( buf, "\"" );
947         }
948         for ( s = d = ldapsrcurl; *s; s++ ) {
949                 if ( *s != '"' ) {
950                         *d++ = *s;
951                 }
952         }
953         *d = '\0';
954         strcat( buf, ldapsrcurl );
955         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
956                 strcat( buf, "\"" );
957         }
958         strcat( buf, ", " );
959         strcat( buf, ldapbase );
960         free( ldapbase );
961         ldapbase = strdup( buf );
962
963         if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
964                 perror( "ldap_open" );
965                 return( NULL );
966         }
967
968         if ( ldap_bind_s( ld, destldapbinddn, destldappasswd, destldapauthmethod )
969           != LDAP_SUCCESS) {
970                 ldap_perror( ld, "ldap_bind_s" );
971                 ldap_unbind( ld );
972                 return( NULL );
973         }
974         if ( verbose ) {
975                 printf( "\n" );
976         }
977
978         attrs[0] = "c";
979         attrs[1] = NULL;
980         rc = ldap_search_s( ld, ldapbase, LDAP_SCOPE_BASE, "(objectclass=*)",
981           attrs, 0, &res );
982         ldap_msgfree( res );
983
984         if ( rc == LDAP_NO_SUCH_OBJECT ) {
985                 if ( verbose ) {
986                         printf( "%sCreating centroid entry...", not ? "Not " : "" );
987                         fflush( stdout );
988                 }
989
990                 /* create the centroid index entry */
991                 m[0].mod_op = 0;
992                 m[0].mod_type = "ref";
993                 refvalues[0] = ldapsrcurl;
994                 refvalues[1] = NULL;
995                 m[0].mod_values = refvalues;
996                 m[1].mod_op = 0;
997                 m[1].mod_type = "objectclass";
998                 ocvalues[0] = "indexentry";
999                 ocvalues[1] = NULL;
1000                 m[1].mod_values = ocvalues;
1001                 mp[0] = &m[0];
1002                 mp[1] = &m[1];
1003                 mp[2] = NULL;
1004
1005                 if ( !not && ldap_add_s( ld, ldapbase, mp ) != LDAP_SUCCESS ) {
1006                         ldap_perror( ld, ldapbase );
1007                         ldap_unbind( ld );
1008                         return( NULL );
1009                 }
1010
1011                 if ( verbose ) {
1012                         printf( "\n" );
1013                         fflush( stdout );
1014                 }
1015         } else if ( rc != LDAP_SUCCESS ) {
1016                 ldap_perror( ld, "ldap_search_s" );
1017                 ldap_unbind( ld );
1018                 return( NULL );
1019         }
1020
1021         return( ld );
1022 }
1023
1024 static char **
1025 charray_add_dup(
1026         char    ***a,
1027     int         *cur,
1028     int         *max,
1029         char    *s
1030 )
1031 {
1032         int n;
1033  
1034         if ( *a == NULL ) {
1035                 *a = (char **) malloc( (BUFSIZ + 1) * sizeof(char *) );
1036                 *cur = 0;
1037                 *max = BUFSIZ;
1038         } else if ( *cur >= *max ) {
1039                 *max += BUFSIZ;
1040                 *a = (char **) realloc( *a, (*max + 1) * sizeof(char *) );
1041         }
1042         if ( *a == NULL ) {
1043                 return( NULL );
1044         }
1045
1046         (*a)[(*cur)++] = strdup( s );
1047         (*a)[*cur] = NULL;
1048         return( *a );
1049 }