ldapmodify.c 30.6 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* ldapmodify.c - generic program to modify or add entries using LDAP */
2
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 1998-2011 The OpenLDAP Foundation.
6
 * Portions Copyright 2006 Howard Chu.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 * Portions Copyright 1998-2003 Kurt D. Zeilenga.
 * Portions Copyright 1998-2001 Net Boolean Incorporated.
 * Portions Copyright 2001-2003 IBM Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.  This
 * software is provided ``as is'' without express or implied warranty.
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
30
31
/* ACKNOWLEDGEMENTS:
 * This work was originally developed by the University of Michigan
Kurt Zeilenga's avatar
Kurt Zeilenga committed
32
33
34
 * (as part of U-MICH LDAP).  Additional significant contributors
 * include:
 *   Kurt D. Zeilenga
35
 *   Norbert Klasen
36
 *   Howard Chu
Kurt Zeilenga's avatar
Kurt Zeilenga committed
37
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
38

Kurt Zeilenga's avatar
Kurt Zeilenga committed
39
40
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
41
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
42
43

#include <ac/stdlib.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
44
#include <ac/ctype.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
45
46
#include <ac/string.h>
#include <ac/unistd.h>
Howard Chu's avatar
Howard Chu committed
47
#include <ac/socket.h>
48
#include <ac/time.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
49

50
#ifdef HAVE_SYS_STAT_H
Kurt Zeilenga's avatar
Kurt Zeilenga committed
51
#include <sys/stat.h>
52
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
53
54

#ifdef HAVE_SYS_FILE_H
Kurt Zeilenga's avatar
Kurt Zeilenga committed
55
#include <sys/file.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
57
#endif
#ifdef HAVE_FCNTL_H
Kurt Zeilenga's avatar
Kurt Zeilenga committed
58
#include <fcntl.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
59
60
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
61
62
#include <ldap.h>

63
#include "lutil.h"
64
#include "lutil_ldap.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
65
66
#include "ldif.h"
#include "ldap_defaults.h"
67
#include "ldap_pvt.h"
Howard Chu's avatar
Howard Chu committed
68
#include "lber_pvt.h"
69

70
71
#include "common.h"

72
static int	ldapadd;
73
static char *rejfile = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
74
static LDAP	*ld = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
75

Howard Chu's avatar
Howard Chu committed
76
#define	M_SEP	0x7f
Kurt Zeilenga's avatar
Kurt Zeilenga committed
77

78
/* strings found in LDIF entries */
Howard Chu's avatar
Howard Chu committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
static struct berval BV_VERSION = BER_BVC("version");
static struct berval BV_DN = BER_BVC("dn");
static struct berval BV_CONTROL = BER_BVC("control");
static struct berval BV_CHANGETYPE = BER_BVC("changetype");
static struct berval BV_ADDCT = BER_BVC("add");
static struct berval BV_MODIFYCT = BER_BVC("modify");
static struct berval BV_DELETECT = BER_BVC("delete");
static struct berval BV_MODRDNCT = BER_BVC("modrdn");
static struct berval BV_MODDNCT = BER_BVC("moddn");
static struct berval BV_RENAMECT = BER_BVC("rename");
static struct berval BV_MODOPADD = BER_BVC("add");
static struct berval BV_MODOPREPLACE = BER_BVC("replace");
static struct berval BV_MODOPDELETE = BER_BVC("delete");
static struct berval BV_MODOPINCREMENT = BER_BVC("increment");
static struct berval BV_NEWRDN = BER_BVC("newrdn");
static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn");
static struct berval BV_NEWSUP = BER_BVC("newsuperior");

97
98
#define	BV_CASEMATCH(a, b) \
	((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val))
Howard Chu's avatar
Howard Chu committed
99
100
101

static int process_ldif_rec LDAP_P(( char *rbuf, int lineno ));
static int parse_ldif_control LDAP_P(( struct berval *val, LDAPControl ***pctrls ));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
102
103
104
static int domodify LDAP_P((
	const char *dn,
	LDAPMod **pmods,
105
	LDAPControl **pctrls,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
106
107
	int newentry ));
static int dodelete LDAP_P((
108
	const char *dn,
109
	LDAPControl **pctrls ));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
111
112
113
static int dorename LDAP_P((
	const char *dn,
	const char *newrdn,
	const char *newsup,
114
	int deleteoldrdn,
115
	LDAPControl **pctrls ));
116
117
118
static int process_response(
	LDAP *ld,
	int msgid,
119
	int res,
120
	const char *dn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
121

122
#ifdef LDAP_X_TXN
123
124
static int txn = 0;
static int txnabort = 0;
125
struct berval *txn_id = NULL;
126
127
#endif

128
129
void
usage( void )
130
{
131
132
133
134
135
136
137
	fprintf( stderr, _("Add or modify entries from an LDAP server\n\n"));
	fprintf( stderr, _("usage: %s [options]\n"), prog);
	fprintf( stderr, _("	The list of desired operations are read from stdin"
		" or from the file\n"));
	fprintf( stderr, _("	specified by \"-f file\".\n"));
	fprintf( stderr, _("Add or modify options:\n"));
	fprintf( stderr, _("  -a         add values (%s)\n"),
Kurt Zeilenga's avatar
Kurt Zeilenga committed
138
		(ldapadd ? _("default") : _("default is to replace")));
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
139
	fprintf( stderr, _("  -c         continuous operation mode (do not stop on errors)\n"));
140
141
	fprintf( stderr, _("  -E [!]ext=extparam	modify extensions"
		" (! indicate s criticality)\n"));
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
142
143
144
	fprintf( stderr, _("  -f file    read operations from `file'\n"));
	fprintf( stderr, _("  -M         enable Manage DSA IT control (-MM to make critical)\n"));
	fprintf( stderr, _("  -P version protocol version (default: 3)\n"));
145
#ifdef LDAP_X_TXN
146
 	fprintf( stderr,
147
		_("             [!]txn=<commit|abort>         (transaction)\n"));
148
149
150
151
152
#endif
	fprintf( stderr, _("  -S file    write skipped modifications to `file'\n"));

	tool_common_usage();
	exit( EXIT_FAILURE );
153
154
}

155

156
const char options[] = "aE:rS:"
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
157
	"cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
158

Hallvard Furuseth's avatar
Hallvard Furuseth committed
159
int
160
handle_private_option( int i )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
161
{
162
163
164
	char	*control, *cvalue;
	int		crit;

165
	switch ( i ) {
166
	case 'E': /* modify extensions */
167
		if( protocol == LDAP_VERSION2 ) {
168
			fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
169
				prog, protocol );
170
			exit( EXIT_FAILURE );
171
172
173
		}

		/* should be extended to support comma separated list of
174
		 *	[!]key[=value] parameters, e.g.  -E !foo,bar=567
175
176
		 */

177
178
179
180
181
		crit = 0;
		cvalue = NULL;
		if( optarg[0] == '!' ) {
			crit = 1;
			optarg++;
182
183
		}

184
		control = ber_strdup( optarg );
185
186
		if ( (cvalue = strchr( control, '=' )) != NULL ) {
			*cvalue++ = '\0';
187
		}
188

189
#ifdef LDAP_X_TXN
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
		if( strcasecmp( control, "txn" ) == 0 ) {
			/* Transaction */
			if( txn ) {
				fprintf( stderr,
					_("txn control previously specified\n"));
				exit( EXIT_FAILURE );
			}
			if( cvalue != NULL ) {
				if( strcasecmp( cvalue, "abort" ) == 0 ) {
					txnabort=1;
				} else if( strcasecmp( cvalue, "commit" ) != 0 ) {
					fprintf( stderr, _("Invalid value for txn control, %s\n"),
						cvalue );
					exit( EXIT_FAILURE );
				}
			}

			txn = 1 + crit;
		} else
209
#endif
210
211
212
213
214
		{
			fprintf( stderr, _("Invalid modify extension name: %s\n"),
				control );
			usage();
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
215
		break;
216

217
	case 'a':	/* add */
218
219
		ldapadd = 1;
		break;
220

221
222
223
	case 'r':	/* replace (obsolete) */
		break;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
224
225
	case 'S':	/* skipped modifications to file */
		if( rejfile != NULL ) {
226
			fprintf( stderr, _("%s: -S previously specified\n"), prog );
227
			exit( EXIT_FAILURE );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
228
		}
229
		rejfile = ber_strdup( optarg );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
230
		break;
231

232
	default:
233
		return 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
234
	}
235
236
	return 1;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
237

238
239
240
241

int
main( int argc, char **argv )
{
Howard Chu's avatar
Howard Chu committed
242
243
244
	char		*rbuf = NULL, *rejbuf = NULL;
	FILE		*rejfp;
	struct LDIFFP *ldiffp, ldifdummy = {0};
245
	char		*matched_msg, *error_msg;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
246
	int		rc, retval, ldifrc;
Howard Chu's avatar
Howard Chu committed
247
	int		len;
248
	int		i = 0;
Howard Chu's avatar
Howard Chu committed
249
	int		lineno, nextline = 0, lmax = 0;
250
251
	LDAPControl	c[1];

252
	prog = lutil_progname( "ldapmodify", argc, argv );
253
254

	/* strncmp instead of strcmp since NT binaries carry .exe extension */
255
	ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 );
256

257
258
	tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY );

259
	tool_args( argc, argv );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
260

261
	if ( argc != optind ) usage();
262

263
264
265
266
267
268
269
	if ( rejfile != NULL ) {
		if (( rejfp = fopen( rejfile, "w" )) == NULL ) {
			perror( rejfile );
			return( EXIT_FAILURE );
		}
	} else {
		rejfp = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
270
271
	}

272
	if ( infile != NULL ) {
Howard Chu's avatar
Howard Chu committed
273
		if (( ldiffp = ldif_open( infile, "r" )) == NULL ) {
274
275
276
277
			perror( infile );
			return( EXIT_FAILURE );
		}
	} else {
Howard Chu's avatar
Howard Chu committed
278
279
		ldifdummy.fp = stdin;
		ldiffp = &ldifdummy;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
280
281
	}

282
	if ( debug ) ldif_debug = debug;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
283

284
	ld = tool_conn_setup( dont, 0 );
285

286
	if ( !dont ) {
287
		tool_bind( ld );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
288
	}
289

290
#ifdef LDAP_X_TXN
291
	if( txn ) {
292
293
		/* start transaction */
		rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id );
294
		if( rc != LDAP_SUCCESS ) {
295
296
			tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL );
			if( txn > 1 ) return EXIT_FAILURE;
297
298
299
300
301
			txn = 0;
		}
	}
#endif

302
	if ( 0
303
#ifdef LDAP_X_TXN
304
305
		|| txn
#endif
306
		)
307
	{
308
#ifdef LDAP_X_TXN
309
		if( txn ) {
310
311
			c[i].ldctl_oid = LDAP_CONTROL_X_TXN_SPEC;
			c[i].ldctl_value = *txn_id;
312
313
314
315
			c[i].ldctl_iscritical = 1;
			i++;
		}
#endif
316
	}
317

318
319
	tool_server_controls( ld, c, i );

320
	rc = 0;
321
	retval = 0;
Howard Chu's avatar
Howard Chu committed
322
	lineno = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
323
324
	while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline,
		&rbuf, &lmax )) > 0 )
325
326
	{
		if ( rejfp ) {
327
			len = strlen( rbuf );
328
329
330
331
332
			if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) {
				perror( "malloc" );
				exit( EXIT_FAILURE );
			}
			memcpy( rejbuf, rbuf, len+1 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
333
334
		}

Howard Chu's avatar
Howard Chu committed
335
336
		rc = process_ldif_rec( rbuf, lineno );
		lineno = nextline+1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
337

338
339
340
		if ( rc ) retval = rc;
		if ( rc && rejfp ) {
			fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
341

342
343
344
345
346
347
348
349
			matched_msg = NULL;
			ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg);
			if ( matched_msg != NULL ) {
				if ( *matched_msg != '\0' ) {
					fprintf( rejfp, _(", matched DN: %s"), matched_msg );
				}
				ldap_memfree( matched_msg );
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
350

351
			error_msg = NULL;
352
			ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg);
353
354
355
356
357
358
359
			if ( error_msg != NULL ) {
				if ( *error_msg != '\0' ) {
					fprintf( rejfp, _(", additional info: %s"), error_msg );
				}
				ldap_memfree( error_msg );
			}
			fprintf( rejfp, "\n%s\n", rejbuf );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
360
		}
361

362
		if (rejfp) ber_memfree( rejbuf );
363
	}
364
	ber_memfree( rbuf );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
365

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
366
367
368
	if ( ldifrc < 0 )
		retval = LDAP_OTHER;

369
#ifdef LDAP_X_TXN
370
	if( retval == 0 && txn ) {
371
372
373
374
375
		rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
		if ( rc != LDAP_OPT_SUCCESS ) {
			fprintf( stderr, "Could not unset controls for ldap_txn_end\n");
		}

376
		/* create transaction */
377
		rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL );
378
		if( rc != LDAP_SUCCESS ) {
379
			tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL );
380
			retval = rc;
381
382
383
384
		}
	}
#endif

385
	if ( !dont ) {
386
		tool_unbind( ld );
387
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
388

389
390
391
	if ( rejfp != NULL ) {
		fclose( rejfp );
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
392

393
	tool_destroy();
394
	return( retval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
395
396
397
398
}


static int
Howard Chu's avatar
Howard Chu committed
399
process_ldif_rec( char *rbuf, int linenum )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
400
{
Howard Chu's avatar
Howard Chu committed
401
	char	*line, *dn, *newrdn, *newsup;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
402
	int		rc, modop;
Howard Chu's avatar
Howard Chu committed
403
404
	int		expect_modop, expect_sep;
	int		deleteoldrdn;
405
	int		new_entry, delete_entry, got_all;
Howard Chu's avatar
Howard Chu committed
406
	LDAPMod	**pmods, *lm = NULL;
407
	int version;
408
	LDAPControl **pctrls;
Howard Chu's avatar
Howard Chu committed
409
410
411
412
	int i, j, k, lines, idn, nmods;
	struct berval *btype, *vals, **bvl, bv;
	char *freeval;
	unsigned char *mops = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
413

414
	new_entry = ldapadd;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
415

416
	rc = got_all = delete_entry = modop = expect_modop = 0;
Howard Chu's avatar
Howard Chu committed
417
	expect_sep = 0;
418
	version = 0;
419
420
421
422
423
	deleteoldrdn = 1;
	pmods = NULL;
	pctrls = NULL;
	dn = newrdn = newsup = NULL;

Howard Chu's avatar
Howard Chu committed
424
425
	lines = ldif_countlines( rbuf );
	btype = ber_memcalloc( 1, (lines+1)*2*sizeof(struct berval)+lines );
426
427
428
	if ( !btype )
		return LDAP_NO_MEMORY;

Howard Chu's avatar
Howard Chu committed
429
430
431
432
	vals = btype+lines+1;
	freeval = (char *)(vals+lines+1);
	i = -1;

433
	while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
Howard Chu's avatar
Howard Chu committed
434
		int freev;
435

Howard Chu's avatar
Howard Chu committed
436
437
438
439
440
441
442
443
444
		if ( *line == '\n' || *line == '\0' ) {
			break;
		}

		++i;

		if ( line[0] == '-' && !line[1] ) {
			BER_BVZERO( btype+i );
			freeval[i] = 0;
445
446
			continue;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
447
	
Pierangelo Masarati's avatar
Pierangelo Masarati committed
448
		if ( ( rc = ldif_parse_line2( line, btype+i, vals+i, &freev ) ) < 0 ) {
449
			fprintf( stderr, _("%s: invalid format (line %d) entry: \"%s\"\n"),
Howard Chu's avatar
Howard Chu committed
450
				prog, linenum+i, dn == NULL ? "" : dn );
451
			rc = LDAP_PARAM_ERROR;
Howard Chu's avatar
Howard Chu committed
452
			goto leave;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
453
		}
Howard Chu's avatar
Howard Chu committed
454
		freeval[i] = freev;
455
456

		if ( dn == NULL ) {
457
			if ( linenum+i == 1 && BV_CASEMATCH( btype+i, &BV_VERSION )) {
458
				int	v;
Howard Chu's avatar
Howard Chu committed
459
				if( vals[i].bv_len == 0 || lutil_atoi( &v, vals[i].bv_val) != 0 || v != 1 ) {
460
461
					fprintf( stderr,
						_("%s: invalid version %s, line %d (ignored)\n"),
Howard Chu's avatar
Howard Chu committed
462
						prog, vals[i].bv_val, linenum );
463
464
465
				}
				version++;

466
			} else if ( BV_CASEMATCH( btype+i, &BV_DN )) {
Howard Chu's avatar
Howard Chu committed
467
468
				dn = vals[i].bv_val;
				idn = i;
469
			}
Howard Chu's avatar
Howard Chu committed
470
			/* skip all lines until we see "dn:" */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
471
		}
Howard Chu's avatar
Howard Chu committed
472
	}
473

Howard Chu's avatar
Howard Chu committed
474
475
476
477
478
	/* check to make sure there was a dn: line */
	if ( !dn ) {
		rc = 0;
		goto leave;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
479

Howard Chu's avatar
Howard Chu committed
480
481
482
483
484
485
	lines = i+1;

	if( lines == 0 ) {
		rc = 0;
		goto leave;
	}
486

Howard Chu's avatar
Howard Chu committed
487
488
489
490
491
492
493
	if( version && lines == 1 ) {
		rc = 0;
		goto leave;
	}

	i = idn+1;
	/* Check for "control" tag after dn and before changetype. */
494
	if ( BV_CASEMATCH( btype+i, &BV_CONTROL )) {
Howard Chu's avatar
Howard Chu committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
		/* Parse and add it to the list of controls */
		rc = parse_ldif_control( vals+i, &pctrls );
		if (rc != 0) {
			fprintf( stderr,
				_("%s: Error processing %s line, line %d: %s\n"),
				prog, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) );
		}
		i++;
		if ( i>= lines ) {
short_input:
			fprintf( stderr,
				_("%s: Expecting more input after %s line, line %d\n"),
				prog, btype[i-1].bv_val, linenum+i );
			
			rc = LDAP_PARAM_ERROR;
			goto leave;
		}
	}

	/* Check for changetype */
515
	if ( BV_CASEMATCH( btype+i, &BV_CHANGETYPE )) {
516
#ifdef LIBERAL_CHANGETYPE_MODOP
Howard Chu's avatar
Howard Chu committed
517
518
519
520
521
522
523
		/* trim trailing spaces (and log warning ...) */
		int icnt;
		for ( icnt = vals[i].bv_len; --icnt > 0; ) {
			if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) {
				break;
			}
		}
524

Howard Chu's avatar
Howard Chu committed
525
526
527
528
529
530
		if ( ++icnt != vals[i].bv_len ) {
			fprintf( stderr, _("%s: illegal trailing space after"
				" \"%s: %s\" trimmed (line %d, entry \"%s\")\n"),
				prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn );
			vals[i].bv_val[icnt] = '\0';
		}
531
532
#endif /* LIBERAL_CHANGETYPE_MODOP */

533
		if ( BV_CASEMATCH( vals+i, &BV_MODIFYCT )) {
Howard Chu's avatar
Howard Chu committed
534
535
			new_entry = 0;
			expect_modop = 1;
536
		} else if ( BV_CASEMATCH( vals+i, &BV_ADDCT )) {
Howard Chu's avatar
Howard Chu committed
537
538
			new_entry = 1;
			modop = LDAP_MOD_ADD;
539
540
541
		} else if ( BV_CASEMATCH( vals+i, &BV_MODRDNCT )
			|| BV_CASEMATCH( vals+i, &BV_MODDNCT )
			|| BV_CASEMATCH( vals+i, &BV_RENAMECT ))
Howard Chu's avatar
Howard Chu committed
542
543
544
545
		{
			i++;
			if ( i >= lines )
				goto short_input;
546
			if ( !BV_CASEMATCH( btype+i, &BV_NEWRDN )) {
Howard Chu's avatar
Howard Chu committed
547
				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
548
					" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
549
550
551
552
553
554
555
556
					prog, BV_NEWRDN.bv_val, btype[i].bv_val, linenum+i, dn );
				rc = LDAP_PARAM_ERROR;
				goto leave;
			}
			newrdn = vals[i].bv_val;
			i++;
			if ( i >= lines )
				goto short_input;
557
			if ( !BV_CASEMATCH( btype+i, &BV_DELETEOLDRDN )) {
Howard Chu's avatar
Howard Chu committed
558
				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
559
					" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
560
561
562
563
564
565
566
					prog, BV_DELETEOLDRDN.bv_val, btype[i].bv_val, linenum+i, dn );
				rc = LDAP_PARAM_ERROR;
				goto leave;
			}
			deleteoldrdn = ( vals[i].bv_val[0] == '0' ) ? 0 : 1;
			i++;
			if ( i < lines ) {
567
				if ( !BV_CASEMATCH( btype+i, &BV_NEWSUP )) {
Howard Chu's avatar
Howard Chu committed
568
					fprintf( stderr, _("%s: expecting \"%s:\" but saw"
569
						" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
570
						prog, BV_NEWSUP.bv_val, btype[i].bv_val, linenum+i, dn );
571
					rc = LDAP_PARAM_ERROR;
Howard Chu's avatar
Howard Chu committed
572
573
574
575
576
577
					goto leave;
				}
				newsup = vals[i].bv_val;
				i++;
			}
			got_all = 1;
578
		} else if ( BV_CASEMATCH( vals+i, &BV_DELETECT )) {
Howard Chu's avatar
Howard Chu committed
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
			got_all = delete_entry = 1;
		} else {
			fprintf( stderr,
				_("%s:  unknown %s \"%s\" (line %d, entry \"%s\")\n"),
				prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn );
			rc = LDAP_PARAM_ERROR;
			goto leave;
		}
		i++;
	} else if ( ldapadd ) {		/*  missing changetype => add */
		new_entry = 1;
		modop = LDAP_MOD_ADD;
	} else {
		expect_modop = 1;	/* missing changetype => modify */
	}

	if ( got_all ) {
		if ( i < lines ) {
			fprintf( stderr,
598
				_("%s: extra lines at end (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
				prog, linenum+i, dn );
			rc = LDAP_PARAM_ERROR;
			goto leave;
		}
		goto doit;
	}

	nmods = lines - i;
	idn = i;

	if ( new_entry ) {
		int fv;

		/* Make sure all attributes with multiple values are contiguous */
		for (; i<lines; i++) {
			for (j=i+1; j<lines; j++) {
615
				if ( BV_CASEMATCH( btype+i, btype+j )) {
Howard Chu's avatar
Howard Chu committed
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
					nmods--;
					/* out of order, move intervening attributes down */
					if ( j != i+1 ) {
						bv = vals[j];
						fv = freeval[j];
						for (k=j; k>i; k--) {
							btype[k] = btype[k-1];
							vals[k] = vals[k-1];
							freeval[k] = freeval[k-1];
						}
						k++;
						btype[k] = btype[i];
						vals[k] = bv;
						freeval[k] = fv;
					}
					i++;
632
633
				}
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
634
		}
Howard Chu's avatar
Howard Chu committed
635
636
637
638
639
640
641
642
643
		/* Allocate space for array of mods, array of pointers to mods,
		 * and array of pointers to values, allowing for NULL terminators
		 * for the pointer arrays...
		 */
		lm = ber_memalloc( nmods * sizeof(LDAPMod) +
			(nmods+1) * sizeof(LDAPMod*) +
			(lines + nmods - idn) * sizeof(struct berval *));
		pmods = (LDAPMod **)(lm+nmods);
		bvl = (struct berval **)(pmods+nmods+1);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
644

Howard Chu's avatar
Howard Chu committed
645
646
647
648
		j = 0;
		k = -1;
		BER_BVZERO(&bv);
		for (i=idn; i<lines; i++) {
649
			if ( BV_CASEMATCH( btype+i, &BV_DN )) {
Howard Chu's avatar
Howard Chu committed
650
651
652
				fprintf( stderr, _("%s: attributeDescription \"%s\":"
					" (possible missing newline"
						" after line %d, entry \"%s\"?)\n"),
Pierangelo Masarati's avatar
Pierangelo Masarati committed
653
					prog, btype[i].bv_val, linenum+i - 1, dn );
Howard Chu's avatar
Howard Chu committed
654
			}
655
			if ( !BV_CASEMATCH( btype+i, &bv )) {
Howard Chu's avatar
Howard Chu committed
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
				bvl[k++] = NULL;
				bv = btype[i];
				lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
				lm[j].mod_type = bv.bv_val;
				lm[j].mod_bvalues = bvl+k;
				pmods[j] = lm+j;
				j++;
			}
			bvl[k++] = vals+i;
		}
		bvl[k] = NULL;
		pmods[j] = NULL;
		goto doit;
	}

	mops = ber_memalloc( lines+1 );
	mops[lines] = M_SEP;
	mops[i-1] = M_SEP;

	for ( ; i<lines; i++ ) {
676
		if ( expect_modop ) {
677
#ifdef LIBERAL_CHANGETYPE_MODOP
678
679
			/* trim trailing spaces (and log warning ...) */
		    int icnt;
Howard Chu's avatar
Howard Chu committed
680
681
		    for ( icnt = vals[i].bv_len; --icnt > 0; ) {
				if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) break;
682
683
			}
    
Howard Chu's avatar
Howard Chu committed
684
			if ( ++icnt != vals[i].bv_len ) {
685
				fprintf( stderr, _("%s: illegal trailing space after"
686
687
					" \"%s: %s\" trimmed (line %d, entry \"%s\")\n"),
					prog, type, vals[i].bv_val, linenum+i, dn );
Howard Chu's avatar
Howard Chu committed
688
				vals[i].bv_val[icnt] = '\0';
689
			}
690
691
#endif /* LIBERAL_CHANGETYPE_MODOP */

692
693
			expect_modop = 0;
			expect_sep = 1;
694
			if ( BV_CASEMATCH( btype+i, &BV_MODOPADD )) {
695
				modop = LDAP_MOD_ADD;
Howard Chu's avatar
Howard Chu committed
696
697
				mops[i] = M_SEP;
				nmods--;
698
			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPREPLACE )) {
Howard Chu's avatar
Howard Chu committed
699
700
701
702
703
			/* defer handling these since they might have no values.
			 * Use the BVALUES flag to signal that these were
			 * deferred. If values are provided later, this
			 * flag will be switched off.
			 */
704
				modop = LDAP_MOD_REPLACE;
Howard Chu's avatar
Howard Chu committed
705
706
				mops[i] = modop | LDAP_MOD_BVALUES;
				btype[i] = vals[i];
707
			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPDELETE )) {
708
				modop = LDAP_MOD_DELETE;
Howard Chu's avatar
Howard Chu committed
709
710
				mops[i] = modop | LDAP_MOD_BVALUES;
				btype[i] = vals[i];
711
			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPINCREMENT )) {
712
				modop = LDAP_MOD_INCREMENT;
Howard Chu's avatar
Howard Chu committed
713
714
				mops[i] = M_SEP;
				nmods--;
715
716
717
718
719
720
			} else {	/* no modify op: invalid LDIF */
				fprintf( stderr, _("%s: modify operation type is missing at"
					" line %d, entry \"%s\"\n"),
					prog, linenum+i, dn );
				rc = LDAP_PARAM_ERROR;
				goto leave;
721
			}
722
			bv = vals[i];
Howard Chu's avatar
Howard Chu committed
723
724
725
726
727
		} else if ( expect_sep && BER_BVISEMPTY( btype+i )) {
			mops[i] = M_SEP;
			expect_sep = 0;
			expect_modop = 1;
			nmods--;
728
		} else {
729
			if ( !BV_CASEMATCH( btype+i, &bv )) {
730
731
732
733
734
735
				fprintf( stderr, _("%s: wrong attributeType at"
					" line %d, entry \"%s\"\n"),
					prog, linenum+i, dn );
				rc = LDAP_PARAM_ERROR;
				goto leave;
			}
Howard Chu's avatar
Howard Chu committed
736
737
738
739
			mops[i] = modop;
			/* If prev op was deferred and matches this type,
			 * clear the flag
			 */
740
741
742
			if ( (mops[i-1] & LDAP_MOD_BVALUES)
				&& BV_CASEMATCH( btype+i, btype+i-1 ))
			{
Howard Chu's avatar
Howard Chu committed
743
744
				mops[i-1] = M_SEP;
				nmods--;
745
			}
746
747
		}
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
748

749
#if 0	/* we should faithfully encode the LDIF, not combine */
Howard Chu's avatar
Howard Chu committed
750
751
752
753
754
755
756
	/* Make sure all modops with multiple values are contiguous */
	for (i=idn; i<lines; i++) {
		if ( mops[i] == M_SEP )
			continue;
		for (j=i+1; j<lines; j++) {
			if ( mops[j] == M_SEP || mops[i] != mops[j] )
				continue;
757
			if ( BV_CASEMATCH( btype+i, btype+j )) {
Howard Chu's avatar
Howard Chu committed
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
				nmods--;
				/* out of order, move intervening attributes down */
				if ( j != i+1 ) {
					int c;
					struct berval bv;
					char fv;

					c = mops[j];
					bv = vals[j];
					fv = freeval[j];
					for (k=j; k>i; k--) {
						btype[k] = btype[k-1];
						vals[k] = vals[k-1];
						freeval[k] = freeval[k-1];
						mops[k] = mops[k-1];
					}
					k++;
					btype[k] = btype[i];
					vals[k] = bv;
					freeval[k] = fv;
					mops[k] = c;
				}
				i++;
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
783
	}
784
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
785

Howard Chu's avatar
Howard Chu committed
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
	/* Allocate space for array of mods, array of pointers to mods,
	 * and array of pointers to values, allowing for NULL terminators
	 * for the pointer arrays...
	 */
	lm = ber_memalloc( nmods * sizeof(LDAPMod) +
		(nmods+1) * sizeof(LDAPMod*) +
		(lines + nmods - idn) * sizeof(struct berval *));
	pmods = (LDAPMod **)(lm+nmods);
	bvl = (struct berval **)(pmods+nmods+1);

	j = 0;
	k = -1;
	BER_BVZERO(&bv);
	mops[idn-1] = M_SEP;
	for (i=idn; i<lines; i++) {
		if ( mops[i] == M_SEP )
			continue;
803
		if ( mops[i] != mops[i-1] || !BV_CASEMATCH( btype+i, &bv )) {
Howard Chu's avatar
Howard Chu committed
804
805
806
807
808
809
810
811
812
813
814
815
816
			bvl[k++] = NULL;
			bv = btype[i];
			lm[j].mod_op = mops[i] | LDAP_MOD_BVALUES;
			lm[j].mod_type = bv.bv_val;
			if ( mops[i] & LDAP_MOD_BVALUES ) {
				lm[j].mod_bvalues = NULL;
			} else {
				lm[j].mod_bvalues = bvl+k;
			}
			pmods[j] = lm+j;
			j++;
		}
		bvl[k++] = vals+i;
817
	}
Howard Chu's avatar
Howard Chu committed
818
819
	bvl[k] = NULL;
	pmods[j] = NULL;
820

Howard Chu's avatar
Howard Chu committed
821
doit:
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
	/* If default controls are set (as with -M option) and controls are
	   specified in the LDIF file, we must add the default controls to
	   the list of controls sent with the ldap operation.
	*/
	if ( rc == 0 ) {
		if (pctrls) {
			LDAPControl **defctrls = NULL;   /* Default server controls */
			LDAPControl **newctrls = NULL;
			ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls);
			if (defctrls) {
				int npc=0;                       /* Num of LDIF controls */
				int ndefc=0;                     /* Num of default controls */
				while (pctrls[npc]) npc++;       /* Count LDIF controls */
				while (defctrls[ndefc]) ndefc++; /* Count default controls */
				newctrls = ber_memrealloc(pctrls,
					(npc+ndefc+1)*sizeof(LDAPControl*));

				if (newctrls == NULL) {
					rc = LDAP_NO_MEMORY;
				} else {
					int i;
					pctrls = newctrls;
					for (i=npc; i<npc+ndefc; i++) {
						pctrls[i] = ldap_control_dup(defctrls[i-npc]);
						if (pctrls[i] == NULL) {
							rc = LDAP_NO_MEMORY;
							break;
						}
					}
					pctrls[npc+ndefc] = NULL;
				}
				ldap_controls_free(defctrls);  /* Must be freed by library */
			}
		}
	}
857

858
859
860
861
862
863
864
865
	if ( rc == 0 ) {
		if ( delete_entry ) {
			rc = dodelete( dn, pctrls );
		} else if ( newrdn != NULL ) {
			rc = dorename( dn, newrdn, newsup, deleteoldrdn, pctrls );
		} else {
			rc = domodify( dn, pmods, pctrls, new_entry );
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
866

867
868
869
		if ( rc == LDAP_SUCCESS ) {
			rc = 0;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
870
871
	}

Howard Chu's avatar
Howard Chu committed
872
leave:
873
    if (pctrls != NULL) {
874
875
    	ldap_controls_free( pctrls );
	}
Howard Chu's avatar
Howard Chu committed
876
877
878
879
880
881
882
883
884
	if ( lm != NULL ) {
		ber_memfree( lm );
	}
	if ( mops != NULL ) {
		ber_memfree( mops );
	}
	for (i=lines-1; i>=0; i--)
		if ( freeval[i] ) ber_memfree( vals[i].bv_val );
	ber_memfree( btype );
885

886
	return( rc );
887
888
889
890
891
892
893
894
895
}

/* Parse an LDIF control line of the form
      control:  oid  [true/false]  [: value]              or
      control:  oid  [true/false]  [:: base64-value]      or
      control:  oid  [true/false]  [:< url]
   The control is added to the list of controls in *ppctrls.
*/      
static int
896
parse_ldif_control(
Howard Chu's avatar
Howard Chu committed
897
	struct berval *bval,
898
	LDAPControl ***ppctrls )
899
{
900
901
902
	char *oid = NULL;
	int criticality = 0;   /* Default is false if not present */
	int i, rc=0;
Howard Chu's avatar
Howard Chu committed
903
	char *s, *oidStart;
904
905
	LDAPControl *newctrl = NULL;
	LDAPControl **pctrls = NULL;
Howard Chu's avatar
Howard Chu committed
906
907
	struct berval type, bv;
	int freeval;
908
909

	if (ppctrls) pctrls = *ppctrls;
Howard Chu's avatar
Howard Chu committed
910
	/* OID should come first. Validate and extract it. */
Howard Chu's avatar
Howard Chu committed
911
	s = bval->bv_val;
912
913
914
	if (*s == 0) return ( LDAP_PARAM_ERROR );
	oidStart = s;
	while (isdigit((unsigned char)*s) || *s == '.') {
915
		s++;                           /* OID should be digits or . */
916
917
918
919
920
921
922
923
924
925
	}
	if (s == oidStart) { 
		return ( LDAP_PARAM_ERROR );   /* OID was not present */
	}
	if (*s) {                          /* End of OID should be space or NULL */
		if (!isspace((unsigned char)*s)) {
			return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */
		}
		*s++ = 0;                    /* Replace space with null to terminate */
	}
926

927
928
	oid = ber_strdup(oidStart);
	if (oid == NULL) return ( LDAP_NO_MEMORY );
929

930
931
	/* Optional Criticality field is next. */
	while (*s && isspace((unsigned char)*s)) {
932
		s++;                         /* Skip white space before criticality */
933
934
935
936
937
938
939
940
941
	}
	if (strncasecmp(s, "true", 4) == 0) {
		criticality = 1;
		s += 4;
	} 
	else if (strncasecmp(s, "false", 5) == 0) {
		criticality = 0;
		s += 5;
	}
942

943
944
	/* Optional value field is next */
	while (*s && isspace((unsigned char)*s)) {
945
		s++;                         /* Skip white space before value */
946
947
948
949
950
951
	}
	if (*s) {
		if (*s != ':') {           /* If value is present, must start with : */
			rc = LDAP_PARAM_ERROR;
			goto cleanup;
		}
952

Howard Chu's avatar
Howard Chu committed
953
954
955
956
957
		/* Back up so value is in the form
		     a: value
		     a:: base64-value
		     a:< url
		   Then we can use ldif_parse_line2 to extract and decode the value
958
		*/
Howard Chu's avatar
Howard Chu committed
959
960
961
962
		s--;
		*s = 'a';

		rc = ldif_parse_line2(s, &type, &bv, &freeval);
963
964
965
966
		if (rc < 0) {
			rc = LDAP_PARAM_ERROR;
			goto cleanup;
		}
967
    }
968
969
970
971
972
973
974
975
976
977

	/* Create a new LDAPControl structure. */
	newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl));
	if ( newctrl == NULL ) {
		rc = LDAP_NO_MEMORY;
		goto cleanup;
	}
	newctrl->ldctl_oid = oid;
	oid = NULL;
	newctrl->ldctl_iscritical = criticality;
Howard Chu's avatar
Howard Chu committed
978
979
980
981
	if ( freeval )
		newctrl->ldctl_value = bv;
	else
		ber_dupbv( &newctrl->ldctl_value, &bv );
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

	/* Add the new control to the passed-in list of controls. */
	i = 0;
	if (pctrls) {
		while ( pctrls[i] ) {    /* Count the # of controls passed in */
			i++;
		}
	}
	/* Allocate 1 more slot for the new control and 1 for the NULL. */
	pctrls = (LDAPControl **) ber_memrealloc(pctrls,
		(i+2)*(sizeof(LDAPControl *)));
	if (pctrls == NULL) {
		rc = LDAP_NO_MEMORY;
		goto cleanup;
	}
	pctrls[i] = newctrl;
	newctrl = NULL;
	pctrls[i+1] = NULL;
	*ppctrls = pctrls;
For faster browsing, not all history is shown. View entire blame