Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: ldif.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* ldif.c - routines for dealing with LDIF files */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1998-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 /* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
     19  * All rights reserved.
     20  *
     21  * Redistribution and use in source and binary forms are permitted
     22  * provided that this notice is preserved and that due credit is given
     23  * to the University of Michigan at Ann Arbor.  The name of the
     24  * University may not be used to endorse or promote products derived
     25  * from this software without specific prior written permission.  This
     26  * software is provided ``as is'' without express or implied warranty.
     27  */
     28 /* This work was originally developed by the University of Michigan
     29  * and distributed as part of U-MICH LDAP.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: ldif.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     34 
     35 #include "portable.h"
     36 
     37 #include <stdio.h>
     38 
     39 #include <ac/stdlib.h>
     40 #include <ac/ctype.h>
     41 
     42 #include <ac/string.h>
     43 #include <ac/socket.h>
     44 #include <ac/time.h>
     45 
     46 int ldif_debug = 0;
     47 
     48 #include "ldap-int.h"
     49 #include "ldif.h"
     50 
     51 #define CONTINUED_LINE_MARKER	'\r'
     52 
     53 #ifdef CSRIMALLOC
     54 #define ber_memalloc malloc
     55 #define ber_memcalloc calloc
     56 #define ber_memrealloc realloc
     57 #define ber_strdup strdup
     58 #endif
     59 
     60 static const char nib2b64[0x40] =
     61         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     62 
     63 /*
     64  * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
     65  * into components "type" and "value".  if a double colon separates type from
     66  * value, then value is encoded in base 64, and parse_line un-decodes it
     67  * (in place) before returning. The type and value are stored in malloc'd
     68  * memory which must be freed by the caller.
     69  *
     70  * ldif_parse_line2 - operates in-place on input buffer, returning type
     71  * in-place. Will return value in-place if possible, (must malloc for
     72  * fetched URLs). If freeval is NULL, all return data will be malloc'd
     73  * and the input line will be unmodified. Otherwise freeval is set to
     74  * True if the value was malloc'd.
     75  */
     76 
     77 int
     78 ldif_parse_line(
     79     LDAP_CONST char	*line,
     80     char	**typep,
     81     char	**valuep,
     82     ber_len_t *vlenp
     83 )
     84 {
     85 	struct berval type, value;
     86 	int rc = ldif_parse_line2( (char *)line, &type, &value, NULL );
     87 
     88 	*typep = type.bv_val;
     89 	*valuep = value.bv_val;
     90 	*vlenp = value.bv_len;
     91 	return rc;
     92 }
     93 
     94 int
     95 ldif_parse_line2(
     96     char	*line,
     97 	struct berval *type,
     98 	struct berval *value,
     99 	int		*freeval
    100 )
    101 {
    102 	char	*s, *p, *d;
    103 	int	b64, url;
    104 
    105 	BER_BVZERO( type );
    106 	BER_BVZERO( value );
    107 
    108 	/* skip any leading space */
    109 	while ( isspace( (unsigned char) *line ) ) {
    110 		line++;
    111 	}
    112 
    113 	if ( freeval ) {
    114 		*freeval = 0;
    115 	} else {
    116 		line = ber_strdup( line );
    117 
    118 		if( line == NULL ) {
    119 			ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    120 				_("ldif_parse_line: line malloc failed\n"));
    121 			return( -1 );
    122 		}
    123 	}
    124 
    125 	type->bv_val = line;
    126 
    127 	s = strchr( type->bv_val, ':' );
    128 
    129 	if ( s == NULL ) {
    130 		ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
    131 			_("ldif_parse_line: missing ':' after %s\n"),
    132 			type->bv_val );
    133 		if ( !freeval ) ber_memfree( line );
    134 		return( -1 );
    135 	}
    136 
    137 	/* trim any space between type and : */
    138 	for ( p = &s[-1]; p > type->bv_val && isspace( * (unsigned char *) p ); p-- ) {
    139 		*p = '\0';
    140 	}
    141 	*s++ = '\0';
    142 	type->bv_len = s - type->bv_val - 1;
    143 
    144 	url = 0;
    145 	b64 = 0;
    146 
    147 	if ( *s == '<' ) {
    148 		s++;
    149 		url = 1;
    150 
    151 	} else if ( *s == ':' ) {
    152 		/* base 64 encoded value */
    153 		s++;
    154 		b64 = 1;
    155 	}
    156 
    157 	/* skip space between : and value */
    158 	while ( isspace( (unsigned char) *s ) ) {
    159 		s++;
    160 	}
    161 
    162 	/* check for continued line markers that should be deleted */
    163 	for ( p = s, d = s; *p; p++ ) {
    164 		if ( *p != CONTINUED_LINE_MARKER )
    165 			*d++ = *p;
    166 	}
    167 	*d = '\0';
    168 
    169 	if ( b64 ) {
    170 		char *byte = s;
    171 
    172 		if ( *s == '\0' ) {
    173 			/* no value is present, error out */
    174 			ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
    175 				_("ldif_parse_line: %s missing base64 value\n"),
    176 				type->bv_val );
    177 			if ( !freeval ) ber_memfree( line );
    178 			return( -1 );
    179 		}
    180 
    181 		value->bv_val = s;
    182 		value->bv_len = d - s;
    183 		if ( ldap_int_decode_b64_inplace( value ) != LDAP_SUCCESS ) {
    184 			ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
    185 				_("ldif_parse_line: %s base64 decode failed\n"),
    186 				type->bv_val );
    187 			if ( !freeval ) ber_memfree( line );
    188 			return( -1 );
    189 		}
    190 	} else if ( url ) {
    191 		if ( *s == '\0' ) {
    192 			/* no value is present, error out */
    193 			ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
    194 				_("ldif_parse_line: %s missing URL value\n"),
    195 				type->bv_val );
    196 			if ( !freeval ) ber_memfree( line );
    197 			return( -1 );
    198 		}
    199 
    200 		if( ldif_fetch_url( s, &value->bv_val, &value->bv_len ) ) {
    201 			ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    202 				_("ldif_parse_line: %s: URL \"%s\" fetch failed\n"),
    203 				type->bv_val, s );
    204 			if ( !freeval ) ber_memfree( line );
    205 			return( -1 );
    206 		}
    207 		if ( freeval ) *freeval = 1;
    208 
    209 	} else {
    210 		value->bv_val = s;
    211 		value->bv_len = (int) (d - s);
    212 	}
    213 
    214 	if ( !freeval ) {
    215 		struct berval bv = *type;
    216 
    217 		ber_dupbv( type, &bv );
    218 
    219 		if( BER_BVISNULL( type )) {
    220 			ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    221 				_("ldif_parse_line: type malloc failed\n"));
    222 			if( url ) ber_memfree( value->bv_val );
    223 			ber_memfree( line );
    224 			return( -1 );
    225 		}
    226 
    227 		if( !url ) {
    228 			bv = *value;
    229 			ber_dupbv( value, &bv );
    230 			if( BER_BVISNULL( value )) {
    231 				ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    232 					_("ldif_parse_line: value malloc failed\n"));
    233 				ber_memfree( type->bv_val );
    234 				ber_memfree( line );
    235 				return( -1 );
    236 			}
    237 		}
    238 
    239 		ber_memfree( line );
    240 	}
    241 
    242 	return( 0 );
    243 }
    244 
    245 /*
    246  * ldif_getline - return the next "line" (minus newline) of input from a
    247  * string buffer of lines separated by newlines, terminated by \n\n
    248  * or \0.  this routine handles continued lines, bundling them into
    249  * a single big line before returning.  if a line begins with a white
    250  * space character, it is a continuation of the previous line. the white
    251  * space character (nb: only one char), and preceding newline are changed
    252  * into CONTINUED_LINE_MARKER chars, to be deleted later by the
    253  * ldif_parse_line() routine above.
    254  *
    255  * ldif_getline will skip over any line which starts '#'.
    256  *
    257  * ldif_getline takes a pointer to a pointer to the buffer on the first call,
    258  * which it updates and must be supplied on subsequent calls.
    259  */
    260 
    261 int
    262 ldif_countlines( LDAP_CONST char *buf )
    263 {
    264 	char *nl;
    265 	int ret = 0;
    266 
    267 	if ( !buf ) return ret;
    268 
    269 	for ( nl = strchr(buf, '\n'); nl; nl = strchr(nl, '\n') ) {
    270 		nl++;
    271 		if ( *nl != ' ' ) ret++;
    272 	}
    273 	return ret;
    274 }
    275 
    276 char *
    277 ldif_getline( char **next )
    278 {
    279 	char *line;
    280 
    281 	do {
    282 		if ( *next == NULL || **next == '\n' || **next == '\0' ) {
    283 			return( NULL );
    284 		}
    285 
    286 		line = *next;
    287 
    288 		while ( (*next = strchr( *next, '\n' )) != NULL ) {
    289 #if CONTINUED_LINE_MARKER != '\r'
    290 			if ( (*next)[-1] == '\r' ) {
    291 				(*next)[-1] = CONTINUED_LINE_MARKER;
    292 			}
    293 #endif
    294 
    295 			if ( (*next)[1] != ' ' ) {
    296 				if ( (*next)[1] == '\r' && (*next)[2] == '\n' ) {
    297 					*(*next)++ = '\0';
    298 				}
    299 				*(*next)++ = '\0';
    300 				break;
    301 			}
    302 
    303 			**next = CONTINUED_LINE_MARKER;
    304 			(*next)[1] = CONTINUED_LINE_MARKER;
    305 			(*next)++;
    306 		}
    307 	} while( *line == '#' );
    308 
    309 	return( line );
    310 }
    311 
    312 /*
    313  * name and OID of attributeTypes that must be base64 encoded in any case
    314  */
    315 typedef struct must_b64_encode_s {
    316 	struct berval	name;
    317 	struct berval	oid;
    318 } must_b64_encode_s;
    319 
    320 static must_b64_encode_s	default_must_b64_encode[] = {
    321 	{ BER_BVC( "userPassword" ), BER_BVC( "2.5.4.35" ) },
    322 	{ BER_BVNULL, BER_BVNULL }
    323 };
    324 
    325 static must_b64_encode_s	*must_b64_encode = default_must_b64_encode;
    326 
    327 /*
    328  * register name and OID of attributeTypes that must always be base64
    329  * encoded
    330  *
    331  * NOTE: this routine mallocs memory in a static struct which must
    332  * be explicitly freed when no longer required
    333  */
    334 int
    335 ldif_must_b64_encode_register( LDAP_CONST char *name, LDAP_CONST char *oid )
    336 {
    337 	int		i;
    338 	ber_len_t	len;
    339 
    340 	assert( must_b64_encode != NULL );
    341 	assert( name != NULL );
    342 	assert( oid != NULL );
    343 
    344 	len = strlen( name );
    345 
    346 	for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
    347 		if ( len != must_b64_encode[i].name.bv_len ) {
    348 			continue;
    349 		}
    350 
    351 		if ( strcasecmp( name, must_b64_encode[i].name.bv_val ) == 0 ) {
    352 			break;
    353 		}
    354 	}
    355 
    356 	if ( !BER_BVISNULL( &must_b64_encode[i].name ) ) {
    357 		return 1;
    358 	}
    359 
    360 	for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ )
    361 		/* just count */ ;
    362 
    363 	if ( must_b64_encode == default_must_b64_encode ) {
    364 		must_b64_encode = ber_memalloc( sizeof( must_b64_encode_s ) * ( i + 2 ) );
    365 		if ( must_b64_encode == NULL ) {
    366 		    return 1;
    367 		}
    368 
    369 		for ( i = 0; !BER_BVISNULL( &default_must_b64_encode[i].name ); i++ ) {
    370 			ber_dupbv( &must_b64_encode[i].name, &default_must_b64_encode[i].name );
    371 			ber_dupbv( &must_b64_encode[i].oid, &default_must_b64_encode[i].oid );
    372 		}
    373 
    374 	} else {
    375 		must_b64_encode_s	*tmp;
    376 
    377 		tmp = ber_memrealloc( must_b64_encode,
    378 			sizeof( must_b64_encode_s ) * ( i + 2 ) );
    379 		if ( tmp == NULL ) {
    380 			return 1;
    381 		}
    382 		must_b64_encode = tmp;
    383 	}
    384 
    385 	ber_str2bv( name, len, 1, &must_b64_encode[i].name );
    386 	ber_str2bv( oid, 0, 1, &must_b64_encode[i].oid );
    387 
    388 	BER_BVZERO( &must_b64_encode[i + 1].name );
    389 
    390 	return 0;
    391 }
    392 
    393 void
    394 ldif_must_b64_encode_release( void )
    395 {
    396 	int	i;
    397 
    398 	assert( must_b64_encode != NULL );
    399 
    400 	if ( must_b64_encode == default_must_b64_encode ) {
    401 		return;
    402 	}
    403 
    404 	for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
    405 		ber_memfree( must_b64_encode[i].name.bv_val );
    406 		ber_memfree( must_b64_encode[i].oid.bv_val );
    407 	}
    408 
    409 	ber_memfree( must_b64_encode );
    410 
    411 	must_b64_encode = default_must_b64_encode;
    412 }
    413 
    414 /*
    415  * returns 1 iff the string corresponds to the name or the OID of any
    416  * of the attributeTypes listed in must_b64_encode
    417  */
    418 static int
    419 ldif_must_b64_encode( LDAP_CONST char *s )
    420 {
    421 	int		i;
    422 	struct berval	bv;
    423 
    424 	assert( must_b64_encode != NULL );
    425 	assert( s != NULL );
    426 
    427 	ber_str2bv( s, 0, 0, &bv );
    428 
    429 	for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
    430 		if ( ber_bvstrcasecmp( &must_b64_encode[i].name, &bv ) == 0
    431 			|| ber_bvcmp( &must_b64_encode[i].oid, &bv ) == 0 )
    432 		{
    433 			return 1;
    434 		}
    435 	}
    436 
    437 	return 0;
    438 }
    439 
    440 /* NOTE: only preserved for binary compatibility */
    441 void
    442 ldif_sput(
    443 	char **out,
    444 	int type,
    445 	LDAP_CONST char *name,
    446 	LDAP_CONST char *val,
    447 	ber_len_t vlen )
    448 {
    449 	ldif_sput_wrap( out, type, name, val, vlen, 0 );
    450 }
    451 
    452 void
    453 ldif_sput_wrap(
    454 	char **out,
    455 	int type,
    456 	LDAP_CONST char *name,
    457 	LDAP_CONST char *val,
    458 	ber_len_t vlen,
    459         ber_len_t wrap )
    460 {
    461 	const unsigned char *byte, *stop;
    462 	unsigned char	buf[3];
    463 	unsigned long	bits;
    464 	char		*save;
    465 	int		pad;
    466 	int		namelen = 0;
    467 
    468 	ber_len_t savelen;
    469 	ber_len_t len=0;
    470 	ber_len_t i;
    471 
    472 	if ( !wrap )
    473 		wrap = LDIF_LINE_WIDTH;
    474 
    475 	/* prefix */
    476 	switch( type ) {
    477 	case LDIF_PUT_COMMENT:
    478 		*(*out)++ = '#';
    479 		len++;
    480 
    481 		if( vlen ) {
    482 			*(*out)++ = ' ';
    483 			len++;
    484 		}
    485 
    486 		break;
    487 
    488 	case LDIF_PUT_SEP:
    489 		*(*out)++ = '\n';
    490 		return;
    491 	}
    492 
    493 	/* name (attribute type) */
    494 	if( name != NULL ) {
    495 		/* put the name + ":" */
    496 		namelen = strlen(name);
    497 		strcpy(*out, name);
    498 		*out += namelen;
    499 		len += namelen;
    500 
    501 		if( type != LDIF_PUT_COMMENT ) {
    502 			*(*out)++ = ':';
    503 			len++;
    504 		}
    505 
    506 	}
    507 #ifdef LDAP_DEBUG
    508 	else {
    509 		assert( type == LDIF_PUT_COMMENT );
    510 	}
    511 #endif
    512 
    513 	if( vlen == 0 ) {
    514 		*(*out)++ = '\n';
    515 		return;
    516 	}
    517 
    518 	switch( type ) {
    519 	case LDIF_PUT_NOVALUE:
    520 		*(*out)++ = '\n';
    521 		return;
    522 
    523 	case LDIF_PUT_URL: /* url value */
    524 		*(*out)++ = '<';
    525 		len++;
    526 		break;
    527 
    528 	case LDIF_PUT_B64: /* base64 value */
    529 		*(*out)++ = ':';
    530 		len++;
    531 		break;
    532 	}
    533 
    534 	switch( type ) {
    535 	case LDIF_PUT_TEXT:
    536 	case LDIF_PUT_URL:
    537 	case LDIF_PUT_B64:
    538 		*(*out)++ = ' ';
    539 		len++;
    540 		/* fall-thru */
    541 
    542 	case LDIF_PUT_COMMENT:
    543 		/* pre-encoded names */
    544 		for ( i=0; i < vlen; i++ ) {
    545 			if ( len > wrap ) {
    546 				*(*out)++ = '\n';
    547 				*(*out)++ = ' ';
    548 				len = 1;
    549 			}
    550 
    551 			*(*out)++ = val[i];
    552 			len++;
    553 		}
    554 		*(*out)++ = '\n';
    555 		return;
    556 	}
    557 
    558 	save = *out;
    559 	savelen = len;
    560 
    561 	*(*out)++ = ' ';
    562 	len++;
    563 
    564 	stop = (const unsigned char *) (val + vlen);
    565 
    566 	if ( type == LDIF_PUT_VALUE
    567 		&& isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<'
    568 		&& isgraph( (unsigned char) val[vlen-1] )
    569 #ifndef LDAP_BINARY_DEBUG
    570 		&& strstr( name, ";binary" ) == NULL
    571 #endif
    572 #ifndef LDAP_PASSWD_DEBUG
    573 		&& !ldif_must_b64_encode( name )
    574 #endif
    575 	) {
    576 		int b64 = 0;
    577 
    578 		for ( byte = (const unsigned char *) val; byte < stop;
    579 		    byte++, len++ )
    580 		{
    581 			if ( !isascii( *byte ) || !isprint( *byte ) ) {
    582 				b64 = 1;
    583 				break;
    584 			}
    585 			if ( len >= wrap ) {
    586 				*(*out)++ = '\n';
    587 				*(*out)++ = ' ';
    588 				len = 1;
    589 			}
    590 			*(*out)++ = *byte;
    591 		}
    592 
    593 		if( !b64 ) {
    594 			*(*out)++ = '\n';
    595 			return;
    596 		}
    597 	}
    598 
    599 	*out = save;
    600 	*(*out)++ = ':';
    601 	*(*out)++ = ' ';
    602 	len = savelen + 2;
    603 
    604 	/* convert to base 64 (3 bytes => 4 base 64 digits) */
    605 	for ( byte = (const unsigned char *) val;
    606 		byte < stop - 2;
    607 	    byte += 3 )
    608 	{
    609 		bits = (byte[0] & 0xff) << 16;
    610 		bits |= (byte[1] & 0xff) << 8;
    611 		bits |= (byte[2] & 0xff);
    612 
    613 		for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
    614 			if ( len >= wrap ) {
    615 				*(*out)++ = '\n';
    616 				*(*out)++ = ' ';
    617 				len = 1;
    618 			}
    619 
    620 			/* get b64 digit from high order 6 bits */
    621 			*(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
    622 		}
    623 	}
    624 
    625 	/* add padding if necessary */
    626 	if ( byte < stop ) {
    627 		for ( i = 0; byte + i < stop; i++ ) {
    628 			buf[i] = byte[i];
    629 		}
    630 		for ( pad = 0; i < 3; i++, pad++ ) {
    631 			buf[i] = '\0';
    632 		}
    633 		byte = buf;
    634 		bits = (byte[0] & 0xff) << 16;
    635 		bits |= (byte[1] & 0xff) << 8;
    636 		bits |= (byte[2] & 0xff);
    637 
    638 		for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
    639 			if ( len >= wrap ) {
    640 				*(*out)++ = '\n';
    641 				*(*out)++ = ' ';
    642 				len = 1;
    643 			}
    644 
    645 			if( i + pad < 4 ) {
    646 				/* get b64 digit from low order 6 bits */
    647 				*(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
    648 			} else {
    649 				*(*out)++ = '=';
    650 			}
    651 		}
    652 	}
    653 	*(*out)++ = '\n';
    654 }
    655 
    656 
    657 /*
    658  * ldif_type_and_value return BER malloc'd, zero-terminated LDIF line
    659  */
    660 
    661 /* NOTE: only preserved for binary compatibility */
    662 char *
    663 ldif_put(
    664 	int type,
    665 	LDAP_CONST char *name,
    666 	LDAP_CONST char *val,
    667 	ber_len_t vlen )
    668 {
    669 	return ldif_put_wrap( type, name, val, vlen, 0 );
    670 }
    671 
    672 char *
    673 ldif_put_wrap(
    674 	int type,
    675 	LDAP_CONST char *name,
    676 	LDAP_CONST char *val,
    677 	ber_len_t vlen,
    678 	ber_len_t wrap )
    679 {
    680     char	*buf, *p;
    681     ber_len_t nlen;
    682 
    683     nlen = ( name != NULL ) ? strlen( name ) : 0;
    684 
    685 	buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED_WRAP( nlen, vlen, wrap ) + 1 );
    686 
    687     if ( buf == NULL ) {
    688 		ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    689 			_("ldif_type_and_value: malloc failed!"));
    690 		return NULL;
    691     }
    692 
    693     p = buf;
    694     ldif_sput_wrap( &p, type, name, val, vlen, wrap );
    695     *p = '\0';
    696 
    697     return( buf );
    698 }
    699 
    700 int ldif_is_not_printable(
    701 	LDAP_CONST char *val,
    702 	ber_len_t vlen )
    703 {
    704 	if( vlen == 0 || val == NULL  ) {
    705 		return -1;
    706 	}
    707 
    708 	if( isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' &&
    709 		isgraph( (unsigned char) val[vlen-1] ) )
    710 	{
    711 		ber_len_t i;
    712 
    713 		for ( i = 0; val[i]; i++ ) {
    714 			if ( !isascii( val[i] ) || !isprint( (unsigned char) val[i] ) ) {
    715 				return 1;
    716 			}
    717 		}
    718 
    719 		return 0;
    720 	}
    721 
    722 	return 1;
    723 }
    724 
    725 LDIFFP *
    726 ldif_open(
    727 	LDAP_CONST char *file,
    728 	LDAP_CONST char *mode
    729 )
    730 {
    731 	FILE *fp = fopen( file, mode );
    732 	LDIFFP *lfp = NULL;
    733 
    734 	if ( fp ) {
    735 		lfp = ber_memalloc( sizeof( LDIFFP ));
    736 		if ( lfp == NULL ) {
    737 			fclose( fp );
    738 			return NULL;
    739 		}
    740 		lfp->fp = fp;
    741 		lfp->prev = NULL;
    742 	}
    743 	return lfp;
    744 }
    745 
    746 LDIFFP *
    747 ldif_open_mem(
    748 	char *ldif,
    749 	size_t size,
    750 	LDAP_CONST char *mode
    751 )
    752 {
    753 #ifdef HAVE_FMEMOPEN
    754 	FILE *fp = fmemopen( ldif, size, mode );
    755 	LDIFFP *lfp = NULL;
    756 
    757 	if ( fp ) {
    758 		lfp = ber_memalloc( sizeof( LDIFFP ));
    759 		lfp->fp = fp;
    760 		lfp->prev = NULL;
    761 	}
    762 	return lfp;
    763 #else /* !HAVE_FMEMOPEN */
    764 	return NULL;
    765 #endif /* !HAVE_FMEMOPEN */
    766 }
    767 
    768 void
    769 ldif_close(
    770 	LDIFFP *lfp
    771 )
    772 {
    773 	LDIFFP *prev;
    774 
    775 	while ( lfp ) {
    776 		fclose( lfp->fp );
    777 		prev = lfp->prev;
    778 		ber_memfree( lfp );
    779 		lfp = prev;
    780 	}
    781 }
    782 
    783 #define	LDIF_MAXLINE	4096
    784 
    785 /*
    786  * ldif_read_record - read an ldif record.  Return 1 for success, 0 for EOF,
    787  * -1 for error.
    788  */
    789 int
    790 ldif_read_record(
    791 	LDIFFP      *lfp,
    792 	unsigned long *lno,		/* ptr to line number counter              */
    793 	char        **bufp,     /* ptr to malloced output buffer           */
    794 	int         *buflenp )  /* ptr to length of *bufp                  */
    795 {
    796 	char        line[LDIF_MAXLINE], *nbufp;
    797 	ber_len_t   lcur = 0, len;
    798 	int         last_ch = '\n', found_entry = 0, stop, top_comment = 0;
    799 
    800 	for ( stop = 0;  !stop;  last_ch = line[len-1] ) {
    801 		/* If we're at the end of this file, see if we should pop
    802 		 * back to a previous file. (return from an include)
    803 		 */
    804 		while ( feof( lfp->fp )) {
    805 pop:
    806 			if ( lfp->prev ) {
    807 				LDIFFP *tmp = lfp->prev;
    808 				fclose( lfp->fp );
    809 				*lfp = *tmp;
    810 				ber_memfree( tmp );
    811 			} else {
    812 				stop = 1;
    813 				break;
    814 			}
    815 		}
    816 		if ( !stop ) {
    817 			if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) {
    818 				if ( !found_entry && !ferror( lfp->fp ) ) {
    819 					/* ITS#9811 Reached the end looking for an entry, try again */
    820 					goto pop;
    821 				}
    822 				stop = 1;
    823 				len = 0;
    824 			} else {
    825 				len = strlen( line );
    826 			}
    827 		}
    828 
    829 		if ( stop ) {
    830 			/* Add \n in case the file does not end with newline */
    831 			if (last_ch != '\n') {
    832 				len = 1;
    833 				line[0] = '\n';
    834 				line[1] = '\0';
    835 				goto last;
    836 			}
    837 			break;
    838 		}
    839 
    840 		/* Squash \r\n to \n */
    841 		if ( len > 1 && line[len-2] == '\r' ) {
    842 			len--;
    843 			line[len]   = '\0';
    844 			line[len-1] = '\n';
    845 		}
    846 
    847 		if ( last_ch == '\n' ) {
    848 			(*lno)++;
    849 
    850 			if ( line[0] == '\n' ) {
    851 				if ( !found_entry ) {
    852 					lcur = 0;
    853 					top_comment = 0;
    854 					continue;
    855 				}
    856 				break;
    857 			}
    858 
    859 			if ( !found_entry ) {
    860 				if ( line[0] == '#' ) {
    861 					top_comment = 1;
    862 				} else if ( ! ( top_comment && line[0] == ' ' ) ) {
    863 					/* Found a new entry */
    864 					found_entry = 1;
    865 
    866 					if ( isdigit( (unsigned char) line[0] ) ) {
    867 						/* skip index */
    868 						continue;
    869 					}
    870 					if ( !strncasecmp( line, "include:",
    871 						STRLENOF("include:"))) {
    872 						FILE *fp2;
    873 						char *ptr;
    874 						found_entry = 0;
    875 
    876 						if ( line[len-1] == '\n' ) {
    877 							len--;
    878 							line[len] = '\0';
    879 						}
    880 
    881 						ptr = line + STRLENOF("include:");
    882 						while (isspace((unsigned char) *ptr)) ptr++;
    883 						fp2 = ldif_open_url( ptr );
    884 						if ( fp2 ) {
    885 							LDIFFP *lnew = ber_memalloc( sizeof( LDIFFP ));
    886 							if ( lnew == NULL ) {
    887 								fclose( fp2 );
    888 								return 0;
    889 							}
    890 							lnew->prev = lfp->prev;
    891 							lnew->fp = lfp->fp;
    892 							lfp->prev = lnew;
    893 							lfp->fp = fp2;
    894 							line[len] = '\n';
    895 							len++;
    896 							continue;
    897 						} else {
    898 							/* We failed to open the file, this should
    899 							 * be reported as an error somehow.
    900 							 */
    901 							ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
    902 								_("ldif_read_record: include %s failed\n"), ptr );
    903 							return -1;
    904 						}
    905 					}
    906 				}
    907 			}
    908 		}
    909 
    910 last:
    911 		if ( *buflenp - lcur <= len ) {
    912 			*buflenp += len + LDIF_MAXLINE;
    913 			nbufp = ber_memrealloc( *bufp, *buflenp );
    914 			if( nbufp == NULL ) {
    915 				return 0;
    916 			}
    917 			*bufp = nbufp;
    918 		}
    919 		strcpy( *bufp + lcur, line );
    920 		lcur += len;
    921 	}
    922 
    923 	return( found_entry );
    924 }
    925