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