1/*	$NetBSD: ldifutil.c,v 1.2 2021/08/14 16:14:56 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* Portions Copyright (c) 1990 Regents of the University of Michigan.
18 * All rights reserved.
19 */
20
21/*
22 * This file contains public API to help with parsing LDIF
23 */
24
25#include <sys/cdefs.h>
26__RCSID("$NetBSD: ldifutil.c,v 1.2 2021/08/14 16:14:56 christos Exp $");
27
28#include "portable.h"
29
30#include <stdio.h>
31
32#include <ac/stdlib.h>
33#include <ac/ctype.h>
34#include <ac/string.h>
35#include <ac/unistd.h>
36#include <ac/socket.h>
37#include <ac/time.h>
38
39#include "ldap-int.h"
40#include "ldif.h"
41
42#define	M_SEP	0x7f
43
44/* strings found in LDIF entries */
45static struct berval BV_VERSION = BER_BVC("version");
46static struct berval BV_DN = BER_BVC("dn");
47static struct berval BV_CONTROL = BER_BVC("control");
48static struct berval BV_CHANGETYPE = BER_BVC("changetype");
49static struct berval BV_ADDCT = BER_BVC("add");
50static struct berval BV_MODIFYCT = BER_BVC("modify");
51static struct berval BV_DELETECT = BER_BVC("delete");
52static struct berval BV_MODRDNCT = BER_BVC("modrdn");
53static struct berval BV_MODDNCT = BER_BVC("moddn");
54static struct berval BV_RENAMECT = BER_BVC("rename");
55static struct berval BV_MODOPADD = BER_BVC("add");
56static struct berval BV_MODOPREPLACE = BER_BVC("replace");
57static struct berval BV_MODOPDELETE = BER_BVC("delete");
58static struct berval BV_MODOPINCREMENT = BER_BVC("increment");
59static struct berval BV_NEWRDN = BER_BVC("newrdn");
60static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn");
61static struct berval BV_NEWSUP = BER_BVC("newsuperior");
62
63#define	BV_CASEMATCH(a, b) \
64	((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val))
65
66static int parse_ldif_control LDAP_P(( struct berval *bval, LDAPControl ***ppctrls ));
67
68void
69ldap_ldif_record_done( LDIFRecord *lr )
70{
71	int i;
72
73	/* the LDAPControl stuff does not allow the use of memory contexts */
74	if (lr->lr_ctrls != NULL) {
75		ldap_controls_free( lr->lr_ctrls );
76	}
77	if ( lr->lr_lm != NULL ) {
78		ber_memfree_x( lr->lr_lm, lr->lr_ctx );
79	}
80	if ( lr->lr_mops != NULL ) {
81		ber_memfree_x( lr->lr_mops, lr->lr_ctx );
82	}
83	for (i=lr->lr_lines-1; i>=0; i--)
84		if ( lr->lr_freeval[i] ) ber_memfree_x( lr->lr_vals[i].bv_val, lr->lr_ctx );
85	ber_memfree_x( lr->lr_btype, lr->lr_ctx );
86
87	memset( lr, 0, sizeof(LDIFRecord) );
88}
89
90/*
91 * ldap_parse_ldif_record_x() will convert an LDIF record read with ldif_read_record()
92 * into an array of LDAPMod* and an array of LDAPControl*, suitable for passing
93 * directly to any other LDAP API function that takes LDAPMod** and LDAPControl**
94 * arguments, such as ldap_modify_s().
95 *
96 * rbuf - the ldif record buffer returned from ldif_read_record - rbuf.bv_val must be
97 *        writable - will use ldif_getline to read from it
98 * linenum - the ldif line number returned from ldif_read_record
99 *         - used for logging errors (e.g. error at line N)
100 * lr - holds the data to return
101 * errstr - a string used for logging (usually the program name e.g. "ldapmodify"
102 * flags - 0 or some combination of LDIF_DEFAULT_ADD LDIF_ENTRIES_ONLY LDIF_NO_CONTROLS
103 * ctx is the memory allocation context - if NULL, use the standard memory allocator
104 */
105int
106ldap_parse_ldif_record_x(
107	struct berval *rbuf,
108	unsigned long linenum,
109	LDIFRecord *lr,
110	const char *errstr,
111	unsigned int flags,
112	void *ctx )
113{
114	char	*line, *dn;
115	int		rc, modop;
116	int		expect_modop, expect_sep;
117	int		ldapadd, new_entry, delete_entry, got_all, no_dn;
118	LDAPMod	**pmods;
119	int version;
120	LDAPControl **pctrls;
121	int i, j, k, idn, nmods;
122	struct berval **bvl, bv;
123
124	assert( lr != NULL );
125	assert( rbuf != NULL );
126	memset( lr, 0, sizeof(LDIFRecord) );
127	lr->lr_ctx = ctx; /* save memory context for later */
128	ldapadd = flags & LDIF_DEFAULT_ADD;
129	no_dn = flags & LDIF_NO_DN;
130	expect_modop = flags & LDIF_MODS_ONLY;
131	new_entry = ldapadd;
132
133	rc = got_all = delete_entry = modop = 0;
134	expect_sep = 0;
135	version = 0;
136	pmods = NULL;
137	pctrls = NULL;
138	dn = NULL;
139
140	lr->lr_lines = ldif_countlines( rbuf->bv_val );
141	lr->lr_btype = ber_memcalloc_x( 1, (lr->lr_lines+1)*2*sizeof(struct berval)+lr->lr_lines, ctx );
142	if ( !lr->lr_btype )
143		return LDAP_NO_MEMORY;
144
145	lr->lr_vals = lr->lr_btype+lr->lr_lines+1;
146	lr->lr_freeval = (char *)(lr->lr_vals+lr->lr_lines+1);
147	i = -1;
148
149	while ( rc == 0 && ( line = ldif_getline( &rbuf->bv_val )) != NULL ) {
150		int freev;
151
152		if ( *line == '\n' || *line == '\0' ) {
153			break;
154		}
155
156		++i;
157
158		if ( line[0] == '-' && !line[1] ) {
159			BER_BVZERO( lr->lr_btype+i );
160			lr->lr_freeval[i] = 0;
161			continue;
162		}
163
164		if ( ( rc = ldif_parse_line2( line, lr->lr_btype+i, lr->lr_vals+i, &freev ) ) < 0 ) {
165			fprintf( stderr, _("%s: invalid format (line %lu) entry: \"%s\"\n"),
166				errstr, linenum+i, dn == NULL ? "" : dn );
167			rc = LDAP_PARAM_ERROR;
168			goto leave;
169		}
170		lr->lr_freeval[i] = freev;
171
172		if ( dn == NULL && !no_dn ) {
173			if ( linenum+i == 1 && BV_CASEMATCH( lr->lr_btype+i, &BV_VERSION )) {
174				/* lutil_atoi() introduces a dependence of libldap
175				 * on liblutil; we only allow version 1 by now (ITS#6654)
176				 */
177#if 0
178				int	v;
179				if( lr->lr_vals[i].bv_len == 0 || lutil_atoi( &v, lr->lr_vals[i].bv_val) != 0 || v != 1 )
180#endif
181				static const struct berval version1 = { 1, "1" };
182				if ( lr->lr_vals[i].bv_len != version1.bv_len || strncmp( lr->lr_vals[i].bv_val, version1.bv_val, version1.bv_len ) != 0 )
183				{
184					fprintf( stderr,
185						_("%s: invalid version %s, line %lu (ignored)\n"),
186						errstr, lr->lr_vals[i].bv_val, linenum );
187				}
188				version++;
189
190			} else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) {
191				lr->lr_dn = lr->lr_vals[i];
192				dn = lr->lr_dn.bv_val; /* primarily for logging */
193				idn = i;
194			}
195			/* skip all lines until we see "dn:" */
196		}
197	}
198
199	/* check to make sure there was a dn: line */
200	if ( !dn && !no_dn ) {
201		rc = 0;
202		goto leave;
203	}
204
205	lr->lr_lines = i+1;
206
207	if( lr->lr_lines == 0 ) {
208		rc = 0;
209		goto leave;
210	}
211
212	if( version && lr->lr_lines == 1 ) {
213		rc = 0;
214		goto leave;
215	}
216
217	if ( no_dn ) {
218		i = 0;
219	} else {
220		i = idn+1;
221		/* Check for "control" tag after dn and before changetype. */
222		if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CONTROL )) {
223			/* Parse and add it to the list of controls */
224			if ( !( flags & LDIF_NO_CONTROLS ) ) {
225				rc = parse_ldif_control( lr->lr_vals+i, &pctrls );
226				if (rc != 0) {
227					fprintf( stderr,
228							 _("%s: Error processing %s line, line %lu: %s\n"),
229							 errstr, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) );
230				}
231			}
232			i++;
233			if ( i>= lr->lr_lines ) {
234short_input:
235				fprintf( stderr,
236					_("%s: Expecting more input after %s line, line %lu\n"),
237					errstr, lr->lr_btype[i-1].bv_val, linenum+i );
238
239				rc = LDAP_PARAM_ERROR;
240				goto leave;
241			}
242		}
243	}
244
245	/* Check for changetype */
246	if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CHANGETYPE )) {
247#ifdef LIBERAL_CHANGETYPE_MODOP
248		/* trim trailing spaces (and log warning ...) */
249		int icnt;
250		for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) {
251			if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) {
252				break;
253			}
254		}
255
256		if ( ++icnt != lr->lr_vals[i].bv_len ) {
257			fprintf( stderr, _("%s: illegal trailing space after"
258				" \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"),
259				errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn );
260			lr->lr_vals[i].bv_val[icnt] = '\0';
261		}
262#endif /* LIBERAL_CHANGETYPE_MODOP */
263
264		/* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or
265		   there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */
266		if ( flags & LDIF_ENTRIES_ONLY ) {
267			if ( !( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) ) {
268				ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
269									_("%s: skipping LDIF record beginning at line %lu: "
270									  "changetype '%.*s' found but entries only was requested\n"),
271									errstr, linenum,
272									(int)lr->lr_vals[i].bv_len,
273									(const char *)lr->lr_vals[i].bv_val );
274				goto leave;
275			}
276		}
277
278		if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODIFYCT )) {
279			new_entry = 0;
280			expect_modop = 1;
281		} else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) {
282			new_entry = 1;
283			modop = LDAP_MOD_ADD;
284		} else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODRDNCT )
285			|| BV_CASEMATCH( lr->lr_vals+i, &BV_MODDNCT )
286			|| BV_CASEMATCH( lr->lr_vals+i, &BV_RENAMECT ))
287		{
288			i++;
289			if ( i >= lr->lr_lines )
290				goto short_input;
291			if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWRDN )) {
292				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
293					" \"%s:\" (line %lu, entry \"%s\")\n"),
294					errstr, BV_NEWRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
295				rc = LDAP_PARAM_ERROR;
296				goto leave;
297			}
298			lr->lrop_newrdn = lr->lr_vals[i];
299			i++;
300			if ( i >= lr->lr_lines )
301				goto short_input;
302			if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_DELETEOLDRDN )) {
303				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
304					" \"%s:\" (line %lu, entry \"%s\")\n"),
305					errstr, BV_DELETEOLDRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
306				rc = LDAP_PARAM_ERROR;
307				goto leave;
308			}
309			lr->lrop_delold = ( lr->lr_vals[i].bv_val[0] == '0' ) ? 0 : 1;
310			i++;
311			if ( i < lr->lr_lines ) {
312				if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWSUP )) {
313					fprintf( stderr, _("%s: expecting \"%s:\" but saw"
314						" \"%s:\" (line %lu, entry \"%s\")\n"),
315						errstr, BV_NEWSUP.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
316					rc = LDAP_PARAM_ERROR;
317					goto leave;
318				}
319				lr->lrop_newsup = lr->lr_vals[i];
320				i++;
321			}
322			got_all = 1;
323		} else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_DELETECT )) {
324			got_all = delete_entry = 1;
325		} else {
326			fprintf( stderr,
327				_("%s:  unknown %s \"%s\" (line %lu, entry \"%s\")\n"),
328				errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn );
329			rc = LDAP_PARAM_ERROR;
330			goto leave;
331		}
332		i++;
333	} else if ( ldapadd ) {		/*  missing changetype => add */
334		new_entry = 1;
335		modop = LDAP_MOD_ADD;
336	} else {
337		/* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or
338		   there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */
339		if ( flags & LDIF_ENTRIES_ONLY ) {
340			ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
341								_("%s: skipping LDIF record beginning at line %lu: "
342								  "no changetype found but entries only was requested and "
343								  "the default setting for missing changetype is modify\n"),
344								errstr, linenum );
345			goto leave;
346		}
347		expect_modop = 1;	/* missing changetype => modify */
348	}
349
350	if ( got_all ) {
351		if ( i < lr->lr_lines ) {
352			fprintf( stderr,
353				_("%s: extra lines at end (line %lu, entry \"%s\")\n"),
354				errstr, linenum+i, dn );
355			rc = LDAP_PARAM_ERROR;
356			goto leave;
357		}
358		goto doit;
359	}
360
361	nmods = lr->lr_lines - i;
362	idn = i;
363
364	if ( new_entry ) {
365		int fv;
366
367		/* Make sure all attributes with multiple values are contiguous */
368		for (; i<lr->lr_lines; i++) {
369			for (j=i+1; j<lr->lr_lines; j++) {
370				if ( !lr->lr_btype[j].bv_val ) {
371					fprintf( stderr,
372						_("%s: missing attributeDescription (line %lu, entry \"%s\")\n"),
373						errstr, linenum+j, dn );
374					rc = LDAP_PARAM_ERROR;
375					goto leave;
376				}
377				if ( BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+j )) {
378					nmods--;
379					/* out of order, move intervening attributes down */
380					if ( j != i+1 ) {
381						bv = lr->lr_vals[j];
382						fv = lr->lr_freeval[j];
383						for (k=j; k>i; k--) {
384							lr->lr_btype[k] = lr->lr_btype[k-1];
385							lr->lr_vals[k] = lr->lr_vals[k-1];
386							lr->lr_freeval[k] = lr->lr_freeval[k-1];
387						}
388						k++;
389						lr->lr_btype[k] = lr->lr_btype[i];
390						lr->lr_vals[k] = bv;
391						lr->lr_freeval[k] = fv;
392					}
393					i++;
394				}
395			}
396		}
397		/* Allocate space for array of mods, array of pointers to mods,
398		 * and array of pointers to values, allowing for NULL terminators
399		 * for the pointer arrays...
400		 */
401		lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) +
402			(nmods+1) * sizeof(LDAPMod*) +
403			(lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx );
404		if ( lr->lr_lm == NULL ) {
405			rc = LDAP_NO_MEMORY;
406			goto leave;
407		}
408
409		pmods = (LDAPMod **)(lr->lr_lm+nmods);
410		bvl = (struct berval **)(pmods+nmods+1);
411
412		j = 0;
413		k = -1;
414		BER_BVZERO(&bv);
415		for (i=idn; i<lr->lr_lines; i++) {
416			if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) {
417				fprintf( stderr, _("%s: attributeDescription \"%s\":"
418					" (possible missing newline"
419						" after line %lu, entry \"%s\"?)\n"),
420					errstr, lr->lr_btype[i].bv_val, linenum+i - 1, dn );
421			}
422			if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
423				bvl[k++] = NULL;
424				bv = lr->lr_btype[i];
425				lr->lr_lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
426				lr->lr_lm[j].mod_type = bv.bv_val;
427				lr->lr_lm[j].mod_bvalues = bvl+k;
428				pmods[j] = lr->lr_lm+j;
429				j++;
430			}
431			bvl[k++] = lr->lr_vals+i;
432		}
433		bvl[k] = NULL;
434		pmods[j] = NULL;
435		goto doit;
436	}
437
438	lr->lr_mops = ber_memalloc_x( lr->lr_lines+1, ctx );
439	if ( lr->lr_mops == NULL ) {
440		rc = LDAP_NO_MEMORY;
441		goto leave;
442	}
443
444	lr->lr_mops[lr->lr_lines] = M_SEP;
445	if ( i > 0 )
446		lr->lr_mops[i-1] = M_SEP;
447
448	for ( ; i<lr->lr_lines; i++ ) {
449		if ( expect_modop ) {
450#ifdef LIBERAL_CHANGETYPE_MODOP
451			/* trim trailing spaces (and log warning ...) */
452		    int icnt;
453		    for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) {
454				if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) break;
455			}
456
457			if ( ++icnt != lr->lr_vals[i].bv_len ) {
458				fprintf( stderr, _("%s: illegal trailing space after"
459					" \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"),
460					errstr, type, lr->lr_vals[i].bv_val, linenum+i, dn );
461				lr->lr_vals[i].bv_val[icnt] = '\0';
462			}
463#endif /* LIBERAL_CHANGETYPE_MODOP */
464
465			expect_modop = 0;
466			expect_sep = 1;
467			if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPADD )) {
468				modop = LDAP_MOD_ADD;
469				lr->lr_mops[i] = M_SEP;
470				nmods--;
471			} else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPREPLACE )) {
472			/* defer handling these since they might have no values.
473			 * Use the BVALUES flag to signal that these were
474			 * deferred. If values are provided later, this
475			 * flag will be switched off.
476			 */
477				modop = LDAP_MOD_REPLACE;
478				lr->lr_mops[i] = modop | LDAP_MOD_BVALUES;
479				lr->lr_btype[i] = lr->lr_vals[i];
480			} else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPDELETE )) {
481				modop = LDAP_MOD_DELETE;
482				lr->lr_mops[i] = modop | LDAP_MOD_BVALUES;
483				lr->lr_btype[i] = lr->lr_vals[i];
484			} else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPINCREMENT )) {
485				modop = LDAP_MOD_INCREMENT;
486				lr->lr_mops[i] = M_SEP;
487				nmods--;
488			} else {	/* no modify op: invalid LDIF */
489				fprintf( stderr, _("%s: modify operation type is missing at"
490					" line %lu, entry \"%s\"\n"),
491					errstr, linenum+i, dn );
492				rc = LDAP_PARAM_ERROR;
493				goto leave;
494			}
495			bv = lr->lr_vals[i];
496		} else if ( expect_sep && BER_BVISEMPTY( lr->lr_btype+i )) {
497			lr->lr_mops[i] = M_SEP;
498			expect_sep = 0;
499			expect_modop = 1;
500			nmods--;
501		} else {
502			if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
503				fprintf( stderr, _("%s: wrong attributeType at"
504					" line %lu, entry \"%s\"\n"),
505					errstr, linenum+i, dn );
506				rc = LDAP_PARAM_ERROR;
507				goto leave;
508			}
509			lr->lr_mops[i] = modop;
510			/* If prev op was deferred and matches this type,
511			 * clear the flag
512			 */
513			if ( (lr->lr_mops[i-1] & LDAP_MOD_BVALUES)
514				&& BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+i-1 ))
515			{
516				lr->lr_mops[i-1] = M_SEP;
517				nmods--;
518			}
519		}
520	}
521
522	/* Allocate space for array of mods, array of pointers to mods,
523	 * and array of pointers to values, allowing for NULL terminators
524	 * for the pointer arrays...
525	 */
526	lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) +
527		(nmods+1) * sizeof(LDAPMod*) +
528		(lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx );
529	if ( lr->lr_lm == NULL ) {
530		rc = LDAP_NO_MEMORY;
531		goto leave;
532	}
533
534	pmods = (LDAPMod **)(lr->lr_lm+nmods);
535	bvl = (struct berval **)(pmods+nmods+1);
536
537	j = 0;
538	k = -1;
539	BER_BVZERO(&bv);
540	if ( idn > 0 )
541		lr->lr_mops[idn-1] = M_SEP;
542	for (i=idn; i<lr->lr_lines; i++) {
543		if ( lr->lr_mops[i] == M_SEP )
544			continue;
545		if ( lr->lr_mops[i] != lr->lr_mops[i-1] || !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
546			bvl[k++] = NULL;
547			bv = lr->lr_btype[i];
548			lr->lr_lm[j].mod_op = lr->lr_mops[i] | LDAP_MOD_BVALUES;
549			lr->lr_lm[j].mod_type = bv.bv_val;
550			if ( lr->lr_mops[i] & LDAP_MOD_BVALUES ) {
551				lr->lr_lm[j].mod_bvalues = NULL;
552			} else {
553				lr->lr_lm[j].mod_bvalues = bvl+k;
554			}
555			pmods[j] = lr->lr_lm+j;
556			j++;
557		}
558		bvl[k++] = lr->lr_vals+i;
559	}
560	bvl[k] = NULL;
561	pmods[j] = NULL;
562
563doit:
564	/* first, set the common fields */
565	lr->lr_ctrls = pctrls;
566	/* next, set the op */
567	if ( delete_entry ) {
568		lr->lr_op = LDAP_REQ_DELETE;
569	} else if ( lr->lrop_newrdn.bv_val != NULL ) {
570		lr->lr_op = LDAP_REQ_MODDN;
571	} else {
572		/* for now, either add or modify */
573		lr->lrop_mods = pmods;
574		if ( new_entry ) {
575			lr->lr_op = LDAP_REQ_ADD;
576		} else {
577			lr->lr_op = LDAP_REQ_MODIFY;
578		}
579	}
580
581leave:
582	if ( rc != LDAP_SUCCESS ) {
583		ldap_ldif_record_done( lr );
584	}
585
586	return( rc );
587}
588
589/* Same as ldap_parse_ldif_record_x()
590 * public API does not expose memory context
591 */
592int
593ldap_parse_ldif_record(
594	struct berval *rbuf,
595	unsigned long linenum,
596	LDIFRecord *lr,
597	const char *errstr,
598	unsigned int flags )
599{
600	return ldap_parse_ldif_record_x( rbuf, linenum, lr, errstr, flags, NULL );
601}
602
603/* Parse an LDIF control line of the form
604      control:  oid  [true/false]  [: value]              or
605      control:  oid  [true/false]  [:: base64-value]      or
606      control:  oid  [true/false]  [:< url]
607   The control is added to the list of controls in *ppctrls.
608*/
609static int
610parse_ldif_control(
611	struct berval *bval,
612	LDAPControl ***ppctrls)
613{
614	char *oid = NULL;
615	int criticality = 0;   /* Default is false if not present */
616	int i, rc=0;
617	char *s, *oidStart;
618	LDAPControl *newctrl = NULL;
619	LDAPControl **pctrls = NULL;
620	struct berval type, bv = BER_BVNULL;
621	int freeval = 0;
622
623	if (ppctrls) pctrls = *ppctrls;
624	/* OID should come first. Validate and extract it. */
625	s = bval->bv_val;
626	if (*s == 0) return ( LDAP_PARAM_ERROR );
627	oidStart = s;
628	while (isdigit((unsigned char)*s) || *s == '.') {
629		s++;                           /* OID should be digits or . */
630	}
631	if (s == oidStart) {
632		return ( LDAP_PARAM_ERROR );   /* OID was not present */
633	}
634	if (*s) {                          /* End of OID should be space or NULL */
635		if (!isspace((unsigned char)*s)) {
636			return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */
637		}
638		*s++ = 0;                    /* Replace space with null to terminate */
639	}
640
641	oid = ber_strdup(oidStart);
642	if (oid == NULL) return ( LDAP_NO_MEMORY );
643
644	/* Optional Criticality field is next. */
645	while (*s && isspace((unsigned char)*s)) {
646		s++;                         /* Skip white space before criticality */
647	}
648	if (strncasecmp(s, "true", 4) == 0) {
649		criticality = 1;
650		s += 4;
651	}
652	else if (strncasecmp(s, "false", 5) == 0) {
653		criticality = 0;
654		s += 5;
655	}
656
657	/* Optional value field is next */
658	while (*s && isspace((unsigned char)*s)) {
659		s++;                         /* Skip white space before value */
660	}
661	if (*s) {
662		if (*s != ':') {           /* If value is present, must start with : */
663			rc = LDAP_PARAM_ERROR;
664			goto cleanup;
665		}
666
667		/* Back up so value is in the form
668		     a: value
669		     a:: base64-value
670		     a:< url
671		   Then we can use ldif_parse_line2 to extract and decode the value
672		*/
673		s--;
674		*s = 'a';
675
676		rc = ldif_parse_line2(s, &type, &bv, &freeval);
677		if (rc < 0) {
678			rc = LDAP_PARAM_ERROR;
679			goto cleanup;
680		}
681    }
682
683	/* Create a new LDAPControl structure. */
684	newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl));
685	if ( newctrl == NULL ) {
686		rc = LDAP_NO_MEMORY;
687		goto cleanup;
688	}
689	newctrl->ldctl_oid = oid;
690	oid = NULL;
691	newctrl->ldctl_iscritical = criticality;
692	if ( freeval )
693		newctrl->ldctl_value = bv;
694	else
695		ber_dupbv( &newctrl->ldctl_value, &bv );
696
697	/* Add the new control to the passed-in list of controls. */
698	i = 0;
699	if (pctrls) {
700		while ( pctrls[i] ) {    /* Count the # of controls passed in */
701			i++;
702		}
703	}
704	/* Allocate 1 more slot for the new control and 1 for the NULL. */
705	pctrls = (LDAPControl **) ber_memrealloc(pctrls,
706		(i+2)*(sizeof(LDAPControl *)));
707	if (pctrls == NULL) {
708		rc = LDAP_NO_MEMORY;
709		goto cleanup;
710	}
711	pctrls[i] = newctrl;
712	newctrl = NULL;
713	pctrls[i+1] = NULL;
714	*ppctrls = pctrls;
715
716cleanup:
717	if (newctrl) {
718		if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid);
719		if (newctrl->ldctl_value.bv_val) {
720			ber_memfree(newctrl->ldctl_value.bv_val);
721		}
722		ber_memfree(newctrl);
723	}
724	if (oid) ber_memfree(oid);
725
726	return( rc );
727}
728
729
730