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