]> git.sur5r.net Git - openldap/blob - servers/slapd/tools/centipede.c
very bad typo.
[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 #ifdef LDBM_USE_DB2
560         DBC     *ocursorp;
561         DBC     *ncursorp;
562 #endif /* LDBM_USE_DB2 */
563
564         if ( verbose ) {
565                 printf( "Generating mods for differential %s centroid...", attr );
566                 fflush( stdout );
567         }
568
569         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 4 )) == NULL ||
570              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
571              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
572              (mods[2] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
573              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
574                  (vals[0] = (char *) malloc( 20 )) == NULL )
575         {
576                 perror( "malloc" );
577                 exit( -1 );
578         }
579         /* add values in mods[0] */
580         mods[0]->mod_op = LDAP_MOD_ADD;
581         mods[0]->mod_type = attr;
582         mods[0]->mod_values = NULL;
583         avals = NULL;
584         acur = amax = 0;
585         /* delete values in mods[1] */
586         mods[1]->mod_op = LDAP_MOD_DELETE;
587         mods[1]->mod_type = attr;
588         mods[1]->mod_values = NULL;
589         dvals = NULL;
590         dcur = dmax = 0;
591         /* number of entries in mods[2] */
592         sprintf( vals[0], "%d", nentries );
593         vals[1] = NULL;
594         mods[2]->mod_op = LDAP_MOD_REPLACE;
595         mods[2]->mod_type = "nentries";
596         mods[2]->mod_values = vals;
597         /* null terminate list of mods */
598         mods[3] = NULL;
599
600 #ifdef LDBM_ORDERED
601         /*
602          * if the underlying database is ordered, we can do a more efficient
603          * dual traversal, yielding O(N) performance.
604          */
605
606         olast.dptr = NULL;
607         nlast.dptr = NULL;
608 #ifdef LDBM_USE_DB2
609         for ( okey = ldbm_firstkey( oldbm, &ocursorp ),
610                         nkey = ldbm_firstkey( nldbm, &ncursorp );
611               okey.dptr != NULL && nkey.dptr != NULL; )
612 #else
613         for ( okey = ldbm_firstkey( oldbm ), nkey = ldbm_firstkey( nldbm );
614               okey.dptr != NULL && nkey.dptr != NULL; )
615 #endif
616         {
617                 rc = strcmp( okey.dptr, nkey.dptr );
618
619                 if ( rc == 0 ) {
620                         /* value is in both places - leave it */
621                         if ( olast.dptr != NULL ) {
622                                 ldbm_datum_free( oldbm, olast );
623                         }
624                         olast = okey;
625                         if ( nlast.dptr != NULL ) {
626                                 ldbm_datum_free( nldbm, nlast );
627                         }
628                         nlast = nkey;
629
630 #ifdef LDBM_USE_DB2
631                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
632                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
633 #else
634                         okey = ldbm_nextkey( oldbm, olast );
635                         nkey = ldbm_nextkey( nldbm, nlast );
636 #endif
637                 } else if ( rc > 0 ) {
638                         /* new value is not in old centroid - add it */
639                         if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
640                                 ldap_mods_free( mods, 1 );
641                                 return( NULL );
642                         }
643
644                         if ( nlast.dptr != NULL ) {
645                                 ldbm_datum_free( nldbm, nlast );
646                         }
647                         nlast = nkey;
648
649 #ifdef LDBM_USE_DB2
650                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
651 #else
652                         nkey = ldbm_nextkey( nldbm, nlast );
653 #endif
654                 } else {
655                         /* old value is not in new centroid - delete it */
656                         if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
657                                 ldap_mods_free( mods, 1 );
658                                 return( NULL );
659                         }
660
661                         if ( olast.dptr != NULL ) {
662                                 ldbm_datum_free( oldbm, olast );
663                         }
664                         olast = okey;
665
666 #ifdef LDBM_USE_DB2
667                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
668 #else
669                         okey = ldbm_nextkey( oldbm, olast );
670 #endif
671                 }
672         }
673
674         while ( okey.dptr != NULL ) {
675                 if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
676                         ldap_mods_free( mods, 1 );
677                         return( NULL );
678                 }
679
680 #ifdef LDBM_USE_DB2
681                 okey = ldbm_nextkey( oldbm, olast, ocursorp );
682 #else
683                 okey = ldbm_nextkey( oldbm, olast );
684 #endif
685                 if ( olast.dptr != NULL ) {
686                         ldbm_datum_free( oldbm, olast );
687                 }
688                 olast = okey;
689         }
690         if ( olast.dptr != NULL ) {
691                 ldbm_datum_free( oldbm, olast );
692         }
693         while ( nkey.dptr != NULL ) {
694                 if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
695                         ldap_mods_free( mods, 1 );
696                         return( NULL );
697                 }
698
699 #ifdef LDBM_USE_DB2
700                 nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
701 #else
702                 nkey = ldbm_nextkey( nldbm, nlast );
703 #endif
704                 if ( nlast.dptr != NULL ) {
705                         ldbm_datum_free( nldbm, nlast );
706                 }
707                 nlast = nkey;
708         }
709         if ( nlast.dptr != NULL ) {
710                 ldbm_datum_free( nldbm, nlast );
711         }
712 #else
713         /*
714          * if the underlying database is not ordered, we have to
715          * generate list of values to add by stepping through all new
716          * values and looking them up in the old centroid (not there => add),
717          * then stepping through all old values and looking them up in the
718          * new centroid (not there => delete). this yields O(Nf(N)) performance,
719          * where f(N) is the order to retrieve a single item.
720          */
721
722         /* generate list of values to add */
723         lastkey.dptr = NULL;
724 #ifdef LDBM_USE_DB2
725         for ( key = ldbm_firstkey( nldbm, &ncursorp ); key.dptr != NULL;
726           key = ldbm_nextkey( nldbm, lastkey, ncursorp ) )
727 #else
728         for ( key = ldbm_firstkey( nldbm ); key.dptr != NULL;
729           key = ldbm_nextkey( nldbm, lastkey ) )
730 #endif
731         {
732                 /* see if it's in the old one */
733                 data = ldbm_fetch( oldbm, key );
734
735                 /* not there - add it */
736                 if ( data.dptr == NULL ) {
737                         if ( charray_add_dup( &avals, &acur, &amax, key.dptr ) == NULL ) {
738                                 ldap_mods_free( mods, 1 );
739                                 return( NULL );
740                         }
741                 } else {
742                         ldbm_datum_free( oldbm, data );
743                 }
744                 if ( lastkey.dptr != NULL ) {
745                         ldbm_datum_free( nldbm, lastkey );
746                 }
747                 lastkey = key;
748         }
749         if ( lastkey.dptr != NULL ) {
750                 ldbm_datum_free( nldbm, lastkey );
751         }
752
753         /* generate list of values to delete */
754         lastkey.dptr = NULL;
755 #ifdef LDBM_USE_DB2
756         for ( key = ldbm_firstkey( oldbm, &ocursorp ); key.dptr != NULL;
757           key = ldbm_nextkey( oldbm, lastkey, ocursorp ) )
758 #else
759         for ( key = ldbm_firstkey( oldbm ); key.dptr != NULL;
760           key = ldbm_nextkey( oldbm, lastkey ) )
761 #endif
762         {
763                 /* see if it's in the new one */
764                 data = ldbm_fetch( nldbm, key );
765
766                 /* not there - delete it */
767                 if ( data.dptr == NULL ) {
768                         if ( charray_add_dup( &dvals, &dcur, &dmax, key.dptr ) == NULL ) {
769                                 ldap_mods_free( mods, 1 );
770                                 return( NULL );
771                         }
772                 } else {
773                         ldbm_datum_free( nldbm, data );
774                 }
775                 if ( lastkey.dptr != NULL ) {
776                         ldbm_datum_free( oldbm, lastkey );
777                 }
778                 lastkey = key;
779         }
780         if ( lastkey.dptr != NULL ) {
781                 ldbm_datum_free( oldbm, lastkey );
782         }
783 #endif
784
785         mods[0]->mod_values = avals;
786         mods[1]->mod_values = dvals;
787
788         if ( verbose ) {
789                 printf( "\n" );
790                 fflush( stdout );
791         }
792
793         if ( mods[1]->mod_values == NULL ) {
794                 free( (char *) mods[1] );
795                 mods[1] = NULL;
796         }
797         if ( mods[0]->mod_values == NULL ) {
798                 free( (char *) mods[0] );
799                 mods[0] = mods[1];
800                 mods[1] = NULL;
801         }
802         if ( mods[0] == NULL ) {
803                 free( (char *) mods );
804                 return( NULL );
805         } else {
806                 return( mods );
807         }
808 }
809
810 static LDAPMod **
811 full_centroid(
812         char    *attr,
813         LDBM    ldbm,
814     int         nentries
815 )
816 {
817         Datum   key, lastkey;
818         LDAPMod **mods;
819         char    **vals;
820         int             vcur, vmax;
821
822 #ifdef LDBM_USE_DB2
823         DBC *cursorp;
824 #endif
825
826         if ( verbose ) {
827                 printf( "Generating mods for full %s centroid...", attr );
828                 fflush( stdout );
829         }
830
831         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 3 )) == NULL ||
832              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
833              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
834              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
835              (vals[0] = (char *) malloc( 20 )) == NULL )
836         {
837                 perror( "malloc" );
838                 exit( -1 );
839         }
840         mods[0]->mod_op = LDAP_MOD_REPLACE;
841         mods[0]->mod_type = attr;
842         mods[0]->mod_values = NULL;
843         sprintf( vals[0], "%d", nentries );
844         vals[1] = NULL;
845         mods[1]->mod_op = LDAP_MOD_REPLACE;
846         mods[1]->mod_type = "nentries";
847         mods[1]->mod_values = vals;
848         mods[2] = NULL;
849
850         lastkey.dptr = NULL;
851         vals = NULL;
852         vcur = vmax = 0;
853 #ifdef LDBM_USE_DB2
854         for ( key = ldbm_firstkey( ldbm, &cursorp ); key.dptr != NULL;
855           key = ldbm_nextkey( ldbm, lastkey, cursorp ) )
856 #else
857         for ( key = ldbm_firstkey( ldbm ); key.dptr != NULL;
858           key = ldbm_nextkey( ldbm, lastkey ) )
859 #endif
860         {
861                 if ( charray_add_dup( &vals, &vcur, &vmax, key.dptr ) == NULL ) {
862                         ldap_mods_free( mods, 1 );
863                         return( NULL );
864                 }
865
866                 if ( lastkey.dptr != NULL ) {
867                         ldbm_datum_free( ldbm, lastkey );
868                 }
869                 lastkey = key;
870         }
871         if ( lastkey.dptr != NULL ) {
872                 ldbm_datum_free( ldbm, lastkey );
873         }
874         mods[0]->mod_values = vals;
875
876         if ( verbose ) {
877                 printf( "\n" );
878                 fflush( stdout );
879         }
880
881         if ( mods[0]->mod_values == NULL ) {
882                 free( (char *) mods[0] );
883                 free( (char *) mods );
884                 return( NULL );
885         } else {
886                 return( mods );
887         }
888 }
889
890 /*
891  * extract the destination ldap host, port, and base object for the
892  * server to receive the index information. then, open a connection,
893  * bind, and see if the entry exists. if not, create it and set things
894  * up so the centroid full and diff routines can modify it to contain
895  * the new centroid information.
896  */
897
898 static LDAP *
899 bind_to_destination_ldap(
900         char    *ldapsrcurl,
901         char    *ldapdesturl
902 )
903 {
904         LDAP            *ld;
905         LDAPMessage     *res;
906         int                     rc;
907         char            *s, *s2, *d;
908         char            *attrs[2], *refvalues[2], *ocvalues[2];
909         LDAPMod         *mp[3];
910         LDAPMod         m[2];
911         char            buf[BUFSIZ];
912
913         if ( verbose ) {
914                 printf( "Binding to destination LDAP server..." );
915                 fflush( stdout );
916         }
917
918         /* first, pick out the destination ldap server info */
919         if ( ldapbase != NULL ) {
920                 free( ldapbase );
921         }
922         if ( strncmp( ldapdesturl, "ldap://", 7 ) == 0 ) {
923                 s = ldapdesturl + 7;
924         }
925         if ( (s2 = strchr( s, '/' )) == NULL ) {
926                 ldapbase = strdup( s );
927         } else {
928                 if ( *s != '/' ) {
929                         *s2 = '\0';
930                         if ( ldaphost != NULL )
931                                 free( ldaphost );
932                         ldaphost = strdup( s );
933                         *s2 = '/';
934                 }
935                 ldapbase = strdup( s2 + 1 );
936         }
937         strcpy( buf, "ref=" );
938         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
939                 strcat( buf, "\"" );
940         }
941         for ( s = d = ldapsrcurl; *s; s++ ) {
942                 if ( *s != '"' ) {
943                         *d++ = *s;
944                 }
945         }
946         *d = '\0';
947         strcat( buf, ldapsrcurl );
948         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
949                 strcat( buf, "\"" );
950         }
951         strcat( buf, ", " );
952         strcat( buf, ldapbase );
953         free( ldapbase );
954         ldapbase = strdup( buf );
955
956         if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
957                 perror( "ldap_open" );
958                 return( NULL );
959         }
960
961         if ( ldap_bind_s( ld, destldapbinddn, destldappasswd, destldapauthmethod )
962           != LDAP_SUCCESS) {
963                 ldap_perror( ld, "ldap_bind_s" );
964                 ldap_unbind( ld );
965                 return( NULL );
966         }
967         if ( verbose ) {
968                 printf( "\n" );
969         }
970
971         attrs[0] = "c";
972         attrs[1] = NULL;
973         rc = ldap_search_s( ld, ldapbase, LDAP_SCOPE_BASE, "(objectclass=*)",
974           attrs, 0, &res );
975         ldap_msgfree( res );
976
977         if ( rc == LDAP_NO_SUCH_OBJECT ) {
978                 if ( verbose ) {
979                         printf( "%sCreating centroid entry...", not ? "Not " : "" );
980                         fflush( stdout );
981                 }
982
983                 /* create the centroid index entry */
984                 m[0].mod_op = 0;
985                 m[0].mod_type = "ref";
986                 refvalues[0] = ldapsrcurl;
987                 refvalues[1] = NULL;
988                 m[0].mod_values = refvalues;
989                 m[1].mod_op = 0;
990                 m[1].mod_type = "objectclass";
991                 ocvalues[0] = "indexentry";
992                 ocvalues[1] = NULL;
993                 m[1].mod_values = ocvalues;
994                 mp[0] = &m[0];
995                 mp[1] = &m[1];
996                 mp[2] = NULL;
997
998                 if ( !not && ldap_add_s( ld, ldapbase, mp ) != LDAP_SUCCESS ) {
999                         ldap_perror( ld, ldapbase );
1000                         ldap_unbind( ld );
1001                         return( NULL );
1002                 }
1003
1004                 if ( verbose ) {
1005                         printf( "\n" );
1006                         fflush( stdout );
1007                 }
1008         } else if ( rc != LDAP_SUCCESS ) {
1009                 ldap_perror( ld, "ldap_search_s" );
1010                 ldap_unbind( ld );
1011                 return( NULL );
1012         }
1013
1014         return( ld );
1015 }
1016
1017 static char **
1018 charray_add_dup(
1019         char    ***a,
1020     int         *cur,
1021     int         *max,
1022         char    *s
1023 )
1024 {
1025         int n;
1026  
1027         if ( *a == NULL ) {
1028                 *a = (char **) malloc( (BUFSIZ + 1) * sizeof(char *) );
1029                 *cur = 0;
1030                 *max = BUFSIZ;
1031         } else if ( *cur >= *max ) {
1032                 *max += BUFSIZ;
1033                 *a = (char **) realloc( *a, (*max + 1) * sizeof(char *) );
1034         }
1035         if ( *a == NULL ) {
1036                 return( NULL );
1037         }
1038
1039         (*a)[(*cur)++] = strdup( s );
1040         (*a)[*cur] = NULL;
1041         return( *a );
1042 }