3 * Copyright (c) 1995 Regents of the University of Michigan.
6 * Redistribution and use in source and binary forms are permitted
7 * provided that this notice is preserved and that due credit is given
8 * to the University of Michigan at Ann Arbor. The name of the University
9 * may not be used to endorse or promote products derived from this
10 * software without specific prior written permission. This software
11 * is provided ``as is'' without express or implied warranty.
18 #include <ac/stdlib.h>
22 #include <ac/dirent.h>
23 #include <ac/string.h>
24 #include <ac/unistd.h>
28 #include <quipu/config.h>
29 #include <quipu/entry.h>
30 #include <quipu/commonarg.h>
31 #include <quipu/attrvalue.h>
34 #define HAVE_FILE_ATTR_DIR
38 #define HAVE_PARSE_ENTRY
41 #define DEF_EDBFILENAME "EDB"
42 #define EDB_ROOT_FILENAME "EDB.root"
44 #define EDBMAP_FILENAME "EDB.map"
45 #define ADDVALS_FILENAME ".add"
47 #define MAX_LINE_SIZE 2048
49 #define VERBOSE_ENTRY_REPORT_THRESHOLD 250
56 struct edbmap *edbm_next;
60 static int edb2ldif( FILE *outfp, char *edbfile, char *basedn, int recurse );
61 static int convert_entry( FILE *fp, char *edbname, FILE *outfp,
62 char *basedn, char *loc_addvals, int loc_addlen, char *linebuf );
63 static int add_rdn_values (Attr_Sequence entryas, RDN rdn);
64 static int read_edbmap( char *mapfile, struct edbmap **edbmapp );
65 static char *file2rdn( struct edbmap *edbmap, char *filename );
66 static void free_edbmap( struct edbmap *edbmap );
67 static char *read_file( char *filename, int *lenp );
68 static void print_err( char *msg );
75 static int verboseflg;
76 static int override_add = 0;
77 static int entrycount;
78 static char **ignore_attr = NULL;
79 static char *always_addvals = NULL;
80 static int always_addlen;
82 static char *edb_home = ".";
85 int ldap_syslog_level = 0;
89 main( int argc, char **argv )
91 char *usage = "usage: %s [-d] [-o] [-r] [-v] [-b basedn] [-a addvalsfile] [-f fileattrdir] [-i ignoreattr...] [edbfile...]\n";
92 char edbfile[ MAXNAMLEN ], *basedn;
93 int c, rc, errflg, ignore_count, recurse;
95 #ifdef HAVE_FILE_ATTR_DIR
96 extern char *file_attr_directory;
99 if (( progname = strrchr( argv[ 0 ], '/' )) == NULL ) {
100 progname = argv[ 0 ];
105 errflg = recurse = 0;
110 always_addvals = NULL;
113 while (( c = getopt( argc, argv, "dorva:b:f:h:i:" )) != EOF ) {
119 fprintf( stderr, "Ignoring -d: compile with -DLDAP_DEBUG to enable this option.\n" );
136 if ( always_addvals != NULL ) {
138 } else if (( always_addvals = read_file( optarg, &always_addlen ))
141 exit( EXIT_FAILURE );
146 if ( basedn != NULL ) {
154 #ifdef HAVE_FILE_ATTR_DIR
155 /* add trailing slash to directory name if missing */
156 if ( *( optarg + strlen( optarg ) - 1 ) == '/' ) {
157 file_attr_directory = strdup( optarg );
158 } else if (( file_attr_directory = (char *)malloc( strlen( optarg )
160 sprintf( file_attr_directory, "%s/", optarg );
163 if ( file_attr_directory == NULL ) {
164 print_err( "malloc" );
165 exit( EXIT_FAILURE );
167 #else /* HAVE_FILE_ATTR_DIR */
168 fprintf( stderr, "Ignoring -f: this option requires a newer version of ISODE.\n" );
169 #endif /* HAVE_FILE_ATTR_DIR */
177 if ( ignore_count == 0 ) {
178 ignore_attr = (char **)malloc( 2 * sizeof( char * ));
180 ignore_attr = (char **)realloc( ignore_attr,
181 ( ignore_count + 2 ) * sizeof( char * ));
183 if ( ignore_attr == NULL ) {
184 print_err( "malloc/realloc" );
185 exit( EXIT_FAILURE );
187 ignore_attr[ ignore_count ] = optarg;
188 ignore_attr[ ++ignore_count ] = NULL;
197 fprintf( stderr, usage, progname );
198 exit( EXIT_FAILURE );
201 if ( basedn == NULL ) {
205 /* load & initialize quipu syntax handlers */
208 pp_quipu_init( progname );
210 dsap_init( NULL, NULL );
212 dsa_mode = 1; /* so {CRYPT} is accepted by EDB parse routines */
214 if ( init_syntaxes() < 0 ) {
215 fprintf( stderr, "%s: init_syntaxes failed -- check your oid tables \n",
217 exit( EXIT_FAILURE );
223 /* process EDB file(s) */
224 if ( optind >= argc ) {
226 rc = edb2ldif( stdout, edbfile, basedn, recurse );
228 for ( rc = 0; rc >= 0 && optind < argc; ++optind ) {
229 if ( argv[ optind ][ 0 ] == '/' ) {
230 strcpy( edbfile, argv[ optind ] );
232 sprintf( edbfile, "%s/%s", edb_home, argv[ optind ] );
234 rc = edb2ldif( stdout, edbfile, basedn, recurse );
238 if ( last_dn != NULL ) {
243 fprintf( stderr, "edb2ldif: exit( %d )\n", ( rc < 0 ) ? 1 : 0 );
246 exit( ( rc < 0 ) ? EXIT_FAILURE : EXIT_SUCCESS );
251 edb2ldif( FILE *outfp, char *edbfile, char *basedn, int recurse )
254 char *addvals, *p, *rdn, line[ MAX_LINE_SIZE + 1 ];
255 char dirname[ MAXNAMLEN ], filename[ MAXNAMLEN ];
256 int err, startcount, addvals_len;
261 fprintf( stderr, "edb2ldif( 0x%X, \"%s\", \"%s\", %d)\n",
262 outfp, edbfile, basedn, recurse );
266 if ( *edbfile == '\0' ) {
267 sprintf( filename, "%s/%s", edb_home, EDB_ROOT_FILENAME );
268 if ( stat( filename, &st ) == 0 ) {
269 if (( err = edb2ldif( outfp, filename, basedn, 0 )) < 0 ) {
272 fprintf( stderr, "edb2ldif: 0 return( %d )\n", err );
277 if (( basedn = strdup( last_dn )) == NULL ) {
278 print_err( "strdup" );
281 fprintf( stderr, "edb2ldif: 1 return( -1 )\n" );
287 sprintf( edbfile, "%s/%s", edb_home, DEF_EDBFILENAME );
291 fprintf( stderr, "%s: converting EDB file: \"%s\"\n\tbasedn: \"%s\"\n",
292 progname, edbfile, basedn );
295 startcount = entrycount;
299 /* construct name of directory we are working in */
300 if (( p = strrchr( edbfile, '/' )) == NULL ) {
304 strncpy( dirname, edbfile, p - edbfile );
305 dirname[ p - edbfile ] = '\0';
308 /* load local ".add" file (if any) */
309 sprintf( filename, "%s/%s", dirname, ADDVALS_FILENAME );
311 addvals = read_file( filename, &addvals_len );
313 /* read and convert this EDB file */
314 if (( fp = fopen( edbfile, "r" )) == NULL ) {
315 print_err( edbfile );
316 if ( addvals != NULL ) {
321 fprintf( stderr, "edb2ldif: 2 return( -1 )\n" );
327 /* skip first two lines (type and timestamp) if they are present */
328 if ( fgets( line, MAX_LINE_SIZE, fp ) == NULL ) {
331 line[ strlen( line ) - 1 ] = '\0';
332 if ( strcmp( line, "MASTER" ) == 0 || strcmp( line, "SLAVE" ) == 0 ||
333 strcmp( line, "CACHE" ) == 0 ) {
334 if ( fgets( line, MAX_LINE_SIZE, fp ) == NULL ) {
343 fprintf( stderr, "%s: skipping empty EDB file %s\n", progname,
345 err = 0; /* treat as a non-fatal error */
347 while ( !feof( fp ) && ( err = convert_entry( fp, edbfile, outfp,
348 basedn, addvals, addvals_len, line )) > 0 ) {
349 if ( verboseflg && (( entrycount - startcount ) %
350 VERBOSE_ENTRY_REPORT_THRESHOLD ) == 0 ) {
351 fprintf( stderr, "\tworking... %d entries done...\n",
352 entrycount - startcount );
358 if ( addvals != NULL ) {
365 fprintf( stderr, "edb2ldif: 3 return( %d )\n", err );
372 fprintf( stderr, "\t%d entries converted\n\n",
373 entrycount - startcount );
376 /* optionally convert EDB file within sub-directories */
381 struct edbmap *edbmap;
383 /* open this directory */
384 if (( dp = opendir( dirname )) == NULL ) {
385 print_err( dirname );
388 fprintf( stderr, "edb2ldif: 4 return( -1 )\n" );
394 /* check for EDB.map file and record contents for future reference */
395 sprintf( filename, "%s/%s", dirname, EDBMAP_FILENAME );
396 if ( read_edbmap( filename, &edbmap ) < 0 ) {
397 print_err( "read_edbmap" );
401 fprintf( stderr, "edb2ldif: 5 return( -1 )\n" );
407 p = dirname + strlen( dirname );
411 /* scan looking for sub-directories w/EDB files in them */
413 while ( err >= 0 && ( dep = readdir( dp )) != NULL ) {
414 if ( dep->d_name[ 0 ] == '.' && ( dep->d_name[ 1 ] == '\0' ||
415 ( dep->d_name[ 1 ] == '.' && dep->d_name[ 2 ] == '\0' ))) {
416 continue; /* skip "." and ".." */
419 strcpy( p, dep->d_name );
422 fprintf( stderr, "edb2ldif: checking directory \"%s\"\n",
427 if ( stat( dirname, &st ) != 0 ) {
428 print_err( dirname );
429 } else if ( S_ISDIR( st.st_mode )) {
430 sprintf( filename, "%s/%s", dirname, DEF_EDBFILENAME );
432 if ( stat( filename, &st ) == 0 && S_ISREG( st.st_mode )) {
433 if (( newbase = malloc( strlen( basedn ) +
434 strlen( dep->d_name ) + 3 )) == NULL ) {
435 print_err( "malloc" );
440 sprintf( newbase, "%s@%s", basedn,
441 file2rdn( edbmap, dep->d_name ));
444 err = edb2ldif( outfp, filename, newbase, recurse );
451 free_edbmap( edbmap );
455 fprintf( stderr, "%s: %d total entries converted under \"%s\"\n\n",
456 progname, entrycount - startcount, basedn );
462 fprintf( stderr, "edb2ldif: 6 return( %d )\n", err );
470 * read one entry from fp and write to outfp.
471 * return > 0 if entry converted, 0 if end of file, < 0 if error occurs
484 Attr_Sequence as, tmpas;
486 PS attrtype_ps, val_ps;
491 extern int parse_status;
492 extern char *parse_file;
493 extern RDN parse_rdn;
494 #ifdef HAVE_PARSE_ENTRY
495 extern char *parse_entry;
496 extern Attr_Sequence fget_attributes();
497 #else /* HAVE_PARSE_ENTRY */
498 extern Attr_Sequence get_attributes();
499 #endif /* HAVE_PARSE_ENTRY */
503 fprintf( stderr, "convert_entry( 0x%X, \"%s\", 0x%X, \"%s\", ...)\n",
504 fp, edbname, outfp, basedn );
508 while (( dnstr = fgets( linebuf, MAX_LINE_SIZE, fp )) != NULL &&
513 if ( dnstr == NULL ) {
514 return( feof( fp ) ? 0 : -1 ); /* end of file or error */
517 linebuf[ strlen( linebuf ) - 1 ] = '\0';
519 if (( dnstr = malloc( strlen( basedn ) + strlen( linebuf ) + 2 ))
521 print_err( "convert_entry" );
524 sprintf( dnstr, "%s@%s", basedn, linebuf );
525 if ( last_dn != NULL ) {
530 if ( entrycount > 0 ) {
531 fputc( '\n', outfp );
535 * parse_entry, parse_file and parse_rdn are needed inside the
536 * libisode decoding routines, so we set it here.
538 parse_file = edbname;
539 #ifdef HAVE_PARSE_ENTRY
542 parse_rdn = rdn = str2rdn( linebuf );
544 if (( val_ps = ps_alloc( str_open )) == NULLPS ||
545 str_setup( val_ps, NULLCP, 0, 0 ) == NOTOK ) {
546 fprintf( stderr, "%s: ps_alloc/setup failed (EDB file %s)\n", progname,
548 if ( rdn != NULLRDN ) {
554 if (( dn = str2dn( dnstr )) == NULLDN || av2ldif( outfp, NULL, dn,
555 0, "dn", val_ps ) < 0 ) {
557 "str2dn or av2ldif of DN failed (EDB file %s, entry %s)\n",
559 print_err( linebuf );
560 if ( dn != NULLDN ) {
564 if ( rdn != NULLRDN ) {
573 if ( always_addvals != NULL && ( loc_addvals == NULL || !override_add )
574 && fwrite( always_addvals, always_addlen, 1, outfp ) != 1 ) {
576 "write of additional values failed (EDB file %s, entry %s)\n",
578 print_err( linebuf );
580 if ( rdn != NULLRDN ) {
586 if ( loc_addvals != NULL && fwrite( loc_addvals, loc_addlen, 1,
589 "write of additional values failed (EDB file %s, entry %s)\n",
591 print_err( linebuf );
593 if ( rdn != NULLRDN ) {
600 #ifdef HAVE_PARSE_ENTRY
601 as = fget_attributes( fp );
602 #else /* HAVE_PARSE_ENTRY */
603 as = get_attributes( fp );
604 #endif /* HAVE_PARSE_ENTRY */
606 if ( parse_status != 0 ) {
607 fprintf( stderr, "%s: problem parsing entry (EDB file %s)\n", progname,
610 if ( as != NULLATTR ) {
613 if ( rdn != NULLRDN ) {
619 if ( add_rdn_values( as, rdn ) != 0 ) {
621 "adding RDN values(s) failed (EDB file %s, entry %s)\n",
623 print_err( linebuf );
624 if ( as != NULLATTR ) {
627 if ( rdn != NULLRDN ) {
633 if (( attrtype_ps = ps_alloc( str_open )) == NULLPS ||
634 str_setup( attrtype_ps, NULLCP, 0, 0 ) == NOTOK ) {
635 fprintf( stderr, "%s: ps_alloc/setup failed (EDB file %s)\n", progname,
637 if ( as != NULLATTR ) {
640 if ( rdn != NULLRDN ) {
646 for ( tmpas = as; tmpas != NULLATTR; tmpas = tmpas->attr_link ) {
647 attrtype_ps->ps_ptr = attrtype_ps->ps_base;
648 AttrT_print( attrtype_ps, tmpas->attr_type, EDBOUT );
649 *attrtype_ps->ps_ptr = '\0';
651 if ( ignore_attr != NULL ) {
654 for ( i = 0; ignore_attr[ i ] != NULL; ++i ) {
655 if ( strcasecmp( attrtype_ps->ps_base, ignore_attr[ i ] )
660 if ( ignore_attr[ i ] != NULL ) {
661 continue; /* skip this attribute */
666 for ( av = tmpas->attr_value; av != NULLAV; av = av->avseq_next ) {
668 if ( av2ldif( outfp, av, NULL, tmpas->attr_type->oa_syntax,
669 attrtype_ps->ps_base, val_ps ) < 0 ) {
671 "av2ldif failed (EDB file %s, entry %s, attribute %s, value no. %d)\n",
672 edbname, dnstr, attrtype_ps->ps_base, valcnt );
673 print_err( linebuf );
674 ps_free( attrtype_ps );
677 if ( rdn != NULLRDN ) {
685 ps_free( attrtype_ps );
688 if ( rdn != NULLRDN ) {
697 add_rdn_values( Attr_Sequence entryas, RDN rdn )
700 * this routine is based on code from the real_unravel_attribute() routine
701 * found in isode-8.0/.dsap/common/attribute.c
707 for (; rdn != NULLRDN; rdn = rdn->rdn_next ) {
708 if (( as = as_find_type( entryas, rdn->rdn_at )) == NULLATTR ) {
709 at = AttrT_cpy( rdn->rdn_at );
710 avs = avs_comp_new( AttrV_cpy(&rdn->rdn_av ));
711 as = as_comp_new( at, avs, NULLACL_INFO );
712 entryas = as_merge( entryas, as );
714 for ( avs = as->attr_value; avs != NULLAV; avs = avs->avseq_next ) {
715 if ( AttrV_cmp( &rdn->rdn_av, &avs->avseq_av ) == 0 ) {
720 if ( avs == NULLAV ) {
721 avs = avs_comp_new( AttrV_cpy( &rdn->rdn_av ));
722 as->attr_value = avs_merge( as->attr_value, avs );
731 /* read the EDB.map file and return a linked list of translations */
733 read_edbmap( char *mapfile, struct edbmap **edbmapp )
736 char *p, *filename, *rdn, line[ MAX_LINE_SIZE + 1 ];
738 struct edbmap *emp, *tmpemp;
742 fprintf( stderr, "read_edbmap( \"%s\", ...)\n", mapfile );
746 if (( fp = fopen( mapfile, "r" )) == NULL ) {
748 return( 0 ); /* soft error -- no EDB.map file */
754 * read all the lines in the file, looking for lines of the form:
758 while ( err == 0 && fgets( line, MAX_LINE_SIZE, fp ) != NULL ) {
759 line[ strlen( line ) - 1 ] = '\0'; /* remove trailing newline */
760 if (( filename = strchr( line, '#' )) == NULL ) {
765 while ( isspace((unsigned char) *filename) ) { /* strip leading whitespace */
769 if ( *filename == '\0' ) {
773 p = filename + strlen( filename ) - 1;
774 while ( isspace((unsigned char) *p) ) { /* strip trailing whitespace */
779 while ( isspace((unsigned char) *rdn)) { /* strip leading whitespace */
783 if ( *rdn == '\0' ) {
787 p = rdn + strlen( rdn ) - 1;
788 while ( isspace((unsigned char) *p)) { /* strip trailing whitespace */
792 if (( tmpemp = (struct edbmap *)calloc( 1, sizeof( struct edbmap )))
794 ( tmpemp->edbm_filename = strdup( filename )) == NULL ||
795 ( tmpemp->edbm_rdn = strdup( rdn )) == NULL ) {
798 tmpemp->edbm_next = emp;
816 file2rdn( struct edbmap *edbmap, char *filename )
820 fprintf( stderr, "file2rdn( 0x%X, \"%s\" )\n", edbmap, filename );
824 while ( edbmap != NULL ) {
825 if ( strcmp( filename, edbmap->edbm_filename ) == 0 ) {
828 edbmap = edbmap->edbm_next;
831 return(( edbmap == NULL ) ? filename : edbmap->edbm_rdn );
835 /* free the edbmap list */
837 free_edbmap( struct edbmap *edbmap )
843 fprintf( stderr, "free_edbmap( 0x%X )\n", edbmap );
847 while ( edbmap != NULL ) {
848 if ( edbmap->edbm_filename != NULL ) free( edbmap->edbm_filename );
849 if ( edbmap->edbm_rdn != NULL ) free( edbmap->edbm_rdn );
851 edbmap = edbmap->edbm_next;
858 print_err( char *msg )
862 fprintf( stderr, "print_err( \"%s\" )\n", msg );
866 if ( errno > sys_nerr ) {
867 fprintf( stderr, "%s: %s: errno=%d\n", progname, msg, errno );
869 fprintf( stderr, "%s: %s: %s\n", progname, msg, sys_errlist[ errno ] );
875 read_file( char *filename, int *lenp )
883 fprintf( stderr, "read_file( \"%s\", 0x%X )\n", filename, lenp );
887 if ( stat( filename, &st ) != 0 || !S_ISREG( st.st_mode ) ||
888 ( fp = fopen( filename, "r" )) == NULL ) {
892 if (( buf = (char *)malloc( st.st_size )) == NULL ) {
897 if ( fread( buf, st.st_size, 1, fp ) != 1 ) {