ldapmodify.c 31.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-2007 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"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67
#include "ldap_log.h"
68
#include "ldap_pvt.h"
Howard Chu's avatar
Howard Chu committed
69
#include "lber_pvt.h"
70

71
72
73
74
#include "common.h"

static int	ldapadd, force = 0;
static char *rejfile = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
75
static LDAP	*ld = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
76

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

/* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
Howard Chu's avatar
Howard Chu committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
static struct berval BV_VERSION = BER_BVC("version");
static struct berval BV_REPLICA = BER_BVC("replica");
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");

#define	BVICMP(a,b)	((a)->bv_len != (b)->bv_len ? \
	(a)->bv_len - (b)->bv_len : strcasecmp((a)->bv_val, (b)->bv_val))

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
104
105
106
static int domodify LDAP_P((
	const char *dn,
	LDAPMod **pmods,
107
	LDAPControl **pctrls,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
108
109
	int newentry ));
static int dodelete LDAP_P((
110
	const char *dn,
111
	LDAPControl **pctrls ));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
112
113
114
115
static int dorename LDAP_P((
	const char *dn,
	const char *newrdn,
	const char *newsup,
116
	int deleteoldrdn,
117
	LDAPControl **pctrls ));
118
119
120
static int process_response(
	LDAP *ld,
	int msgid,
121
	int res,
122
	const char *dn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
123

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

130
131
void
usage( void )
132
{
133
134
135
136
137
138
139
	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
140
		(ldapadd ? _("default") : _("default is to replace")));
141
142
	fprintf( stderr, _("  -E [!]ext=extparam	modify extensions"
		" (! indicate s criticality)\n"));
143
#ifdef LDAP_X_TXN
144
 	fprintf( stderr,
145
		_("             [!]txn=<commit|abort>         (transaction)\n"));
146
147
148
149
150
151
#endif
	fprintf( stderr, _("  -F         force all changes records to be used\n"));
	fprintf( stderr, _("  -S file    write skipped modifications to `file'\n"));

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

154

155
const char options[] = "aE:FrS:"
Howard Chu's avatar
Howard Chu committed
156
	"cd:D:e:f:h:H:IMnO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
157

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

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

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

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

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

188
#ifdef LDAP_X_TXN
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
		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
208
#endif
209
210
211
212
213
		{
			fprintf( stderr, _("Invalid modify extension name: %s\n"),
				control );
			usage();
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
214
		break;
215

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

220
	case 'F':	/* force all changes records to be used */
221
222
		force = 1;
		break;
223

224
225
226
	case 'r':	/* replace (obsolete) */
		break;

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

235
	default:
236
		return 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
237
	}
238
239
	return 1;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
240

241
242
243
244

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

255
	prog = lutil_progname( "ldapmodify", argc, argv );
256
257

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

260
261
	tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY );

262
	tool_args( argc, argv );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
263

264
	if ( argc != optind ) usage();
265

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

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

285
	if ( debug ) ldif_debug = debug;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
286

287
	ld = tool_conn_setup( dont, 0 );
288

289
	if ( !dont ) {
290
291
292
293
294
295
296
297
		if ( pw_file || want_bindpw ) {
			if ( pw_file ) {
				rc = lutil_get_filed_password( pw_file, &passwd );
				if( rc ) return EXIT_FAILURE;
			} else {
				passwd.bv_val = getpassphrase( _("Enter LDAP Password: ") );
				passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
			}
298
		}
299
		tool_bind( ld );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
300
	}
301

302
#ifdef LDAP_X_TXN
303
	if( txn ) {
304
305
		/* start transaction */
		rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id );
306
		if( rc != LDAP_SUCCESS ) {
307
308
			tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL );
			if( txn > 1 ) return EXIT_FAILURE;
309
310
311
312
313
			txn = 0;
		}
	}
#endif

314
	if ( 0
315
#ifdef LDAP_X_TXN
316
317
		|| txn
#endif
318
		)
319
	{
320
#ifdef LDAP_X_TXN
321
		if( txn ) {
322
323
			c[i].ldctl_oid = LDAP_CONTROL_X_TXN_SPEC;
			c[i].ldctl_value = *txn_id;
324
325
326
327
			c[i].ldctl_iscritical = 1;
			i++;
		}
#endif
328
	}
329

330
331
	tool_server_controls( ld, c, i );

332
	rc = 0;
333
	retval = 0;
Howard Chu's avatar
Howard Chu committed
334
335
336
	lineno = 1;
	while (( rc == 0 || contoper ) && ldif_read_record( ldiffp, &nextline,
		&rbuf, &lmax ))
337
338
	{
		if ( rejfp ) {
339
			len = strlen( rbuf );
340
341
342
343
344
			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
345
346
		}

Howard Chu's avatar
Howard Chu committed
347
348
		rc = process_ldif_rec( rbuf, lineno );
		lineno = nextline+1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
349

350
351
352
		if ( rc ) retval = rc;
		if ( rc && rejfp ) {
			fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
353

354
355
356
357
358
359
360
361
			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
362

363
			error_msg = NULL;
364
			ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg);
365
366
367
368
369
370
371
			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
372
		}
373

374
		if (rejfp) ber_memfree( rejbuf );
375
	}
376
	ber_memfree( rbuf );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
377

378
#ifdef LDAP_X_TXN
379
	if( retval == 0 && txn ) {
380
381
382
383
384
		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");
		}

385
		/* create transaction */
386
		rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL );
387
		if( rc != LDAP_SUCCESS ) {
388
			tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL );
389
			retval = rc;
390
391
392
393
		}
	}
#endif

394
	if ( !dont ) {
395
		tool_unbind( ld );
396
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
397

398
399
400
	if ( rejfp != NULL ) {
		fclose( rejfp );
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
401

402
	tool_destroy();
403
	return( retval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
404
405
406
407
}


static int
Howard Chu's avatar
Howard Chu committed
408
process_ldif_rec( char *rbuf, int linenum )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
409
{
Howard Chu's avatar
Howard Chu committed
410
411
412
413
	char	*line, *dn, *newrdn, *newsup;
	int		rc, modop, replicaport;
	int		expect_modop, expect_sep;
	int		deleteoldrdn;
414
	int		saw_replica, use_record, new_entry, delete_entry, got_all;
Howard Chu's avatar
Howard Chu committed
415
	LDAPMod	**pmods, *lm = NULL;
416
	int version;
417
	LDAPControl **pctrls;
Howard Chu's avatar
Howard Chu committed
418
419
420
421
	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
422

423
	new_entry = ldapadd;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
424

425
	rc = got_all = saw_replica = delete_entry = modop = expect_modop = 0;
Howard Chu's avatar
Howard Chu committed
426
	expect_sep = 0;
427
	version = 0;
428
429
430
431
432
433
	deleteoldrdn = 1;
	use_record = force;
	pmods = NULL;
	pctrls = NULL;
	dn = newrdn = newsup = NULL;

Howard Chu's avatar
Howard Chu committed
434
435
	lines = ldif_countlines( rbuf );
	btype = ber_memcalloc( 1, (lines+1)*2*sizeof(struct berval)+lines );
436
437
438
	if ( !btype )
		return LDAP_NO_MEMORY;

Howard Chu's avatar
Howard Chu committed
439
440
441
442
	vals = btype+lines+1;
	freeval = (char *)(vals+lines+1);
	i = -1;

443
	while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
Howard Chu's avatar
Howard Chu committed
444
		int freev;
445

Howard Chu's avatar
Howard Chu committed
446
447
448
449
450
451
452
453
454
		if ( *line == '\n' || *line == '\0' ) {
			break;
		}

		++i;

		if ( line[0] == '-' && !line[1] ) {
			BER_BVZERO( btype+i );
			freeval[i] = 0;
455
456
			continue;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
457
	
Pierangelo Masarati's avatar
Pierangelo Masarati committed
458
		if ( ( rc = ldif_parse_line2( line, btype+i, vals+i, &freev ) ) < 0 ) {
459
			fprintf( stderr, _("%s: invalid format (line %d) entry: \"%s\"\n"),
Howard Chu's avatar
Howard Chu committed
460
				prog, linenum+i, dn == NULL ? "" : dn );
461
462
			rc = LDAP_PARAM_ERROR;
			break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
463
		}
Howard Chu's avatar
Howard Chu committed
464
		freeval[i] = freev;
465
466

		if ( dn == NULL ) {
Howard Chu's avatar
Howard Chu committed
467
468
			if ( !use_record && !BVICMP( btype+i, &BV_REPLICA )) {
				char *p;
469
				++saw_replica;
Howard Chu's avatar
Howard Chu committed
470
				if (( p = strchr( vals[i].bv_val, ':' )) == NULL ) {
471
472
473
					replicaport = 0;
				} else {
					*p++ = '\0';
474
475
					if ( lutil_atoi( &replicaport, p ) != 0 ) {
						fprintf( stderr, _("%s: unable to parse replica port \"%s\" (line %d) entry: \"%s\"\n"),
Howard Chu's avatar
Howard Chu committed
476
							prog, p, linenum+i, dn == NULL ? "" : dn );
477
478
479
						rc = LDAP_PARAM_ERROR;
						break;
					}
480
481
				}
				if ( ldaphost != NULL &&
Howard Chu's avatar
Howard Chu committed
482
					strcasecmp( vals[i].bv_val, ldaphost ) == 0 &&
483
484
485
486
					replicaport == ldapport )
				{
					use_record = 1;
				}
Howard Chu's avatar
Howard Chu committed
487
			} else if ( linenum+i == 1 && !BVICMP( btype+i, &BV_VERSION )) {
488
				int	v;
Howard Chu's avatar
Howard Chu committed
489
				if( vals[i].bv_len == 0 || lutil_atoi( &v, vals[i].bv_val) != 0 || v != 1 ) {
490
491
					fprintf( stderr,
						_("%s: invalid version %s, line %d (ignored)\n"),
Howard Chu's avatar
Howard Chu committed
492
						prog, vals[i].bv_val, linenum );
493
494
495
				}
				version++;

Howard Chu's avatar
Howard Chu committed
496
497
498
499
500
501
502
503
504
			} else if ( !BVICMP( btype+i, &BV_DN )) {
				dn = vals[i].bv_val;
				idn = i;
				if ( !use_record && saw_replica ) {
					printf(_("%s: skipping change record for entry: %s at line %d\n"),
						prog, dn, linenum+i);
					printf(_("\t(LDAP host/port does not match replica: lines)\n"));
					rc = 0;
					goto leave;
505
506
				}
			}
Howard Chu's avatar
Howard Chu committed
507
			/* skip all lines until we see "dn:" */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
508
		}
Howard Chu's avatar
Howard Chu committed
509
	}
510

Howard Chu's avatar
Howard Chu committed
511
512
513
514
515
	/* check to make sure there was a dn: line */
	if ( !dn ) {
		rc = 0;
		goto leave;
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
516

Howard Chu's avatar
Howard Chu committed
517
518
519
520
521
522
	lines = i+1;

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

Howard Chu's avatar
Howard Chu committed
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
	if( version && lines == 1 ) {
		rc = 0;
		goto leave;
	}

	i = idn+1;
	/* Check for "control" tag after dn and before changetype. */
	if (!BVICMP( btype+i, &BV_CONTROL)) {
		/* 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 */
	if ( !BVICMP( btype+i, &BV_CHANGETYPE )) {
553
#ifdef LIBERAL_CHANGETYPE_MODOP
Howard Chu's avatar
Howard Chu committed
554
555
556
557
558
559
560
		/* 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;
			}
		}
561

Howard Chu's avatar
Howard Chu committed
562
563
564
565
566
567
		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';
		}
568
569
#endif /* LIBERAL_CHANGETYPE_MODOP */

Howard Chu's avatar
Howard Chu committed
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
		if ( BVICMP( vals+i, &BV_MODIFYCT ) == 0 ) {
			new_entry = 0;
			expect_modop = 1;
		} else if ( BVICMP( vals+i, &BV_ADDCT ) == 0 ) {
			new_entry = 1;
			modop = LDAP_MOD_ADD;
		} else if ( BVICMP( vals+i, &BV_MODRDNCT ) == 0
			|| BVICMP( vals+i, &BV_MODDNCT ) == 0
			|| BVICMP( vals+i, &BV_RENAMECT ) == 0)
		{
			i++;
			if ( i >= lines )
				goto short_input;
			if ( BVICMP( btype+i, &BV_NEWRDN )) {
				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
585
					" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
586
587
588
589
590
591
592
593
594
595
					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;
			if ( BVICMP( btype+i, &BV_DELETEOLDRDN )) {
				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
596
					" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
597
598
599
600
601
602
603
604
605
					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 ) {
				if ( BVICMP( btype+i, &BV_NEWSUP )) {
					fprintf( stderr, _("%s: expecting \"%s:\" but saw"
606
						" \"%s:\" (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
607
						prog, BV_NEWSUP.bv_val, btype[i].bv_val, linenum+i, dn );
608
					rc = LDAP_PARAM_ERROR;
Howard Chu's avatar
Howard Chu committed
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
					goto leave;
				}
				newsup = vals[i].bv_val;
				i++;
			}
			got_all = 1;
		} else if ( BVICMP( vals+i, &BV_DELETECT ) == 0 ) {
			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,
635
				_("%s: extra lines at end (line %d, entry \"%s\")\n"),
Howard Chu's avatar
Howard Chu committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
				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++) {
				if ( !BVICMP( btype+i, btype+j )) {
					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++;
669
670
				}
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
671
		}
Howard Chu's avatar
Howard Chu committed
672
673
674
675
676
677
678
679
680
		/* 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
681

Howard Chu's avatar
Howard Chu committed
682
683
684
685
686
687
688
689
		j = 0;
		k = -1;
		BER_BVZERO(&bv);
		for (i=idn; i<lines; i++) {
			if ( !BVICMP( btype+i, &BV_DN )) {
				fprintf( stderr, _("%s: attributeDescription \"%s\":"
					" (possible missing newline"
						" after line %d, entry \"%s\"?)\n"),
Pierangelo Masarati's avatar
Pierangelo Masarati committed
690
					prog, btype[i].bv_val, linenum+i - 1, dn );
Howard Chu's avatar
Howard Chu committed
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
			}
			if ( BVICMP(btype+i,&bv)) {
				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++ ) {
713
		if ( expect_modop ) {
714
#ifdef LIBERAL_CHANGETYPE_MODOP
715
716
			/* trim trailing spaces (and log warning ...) */
		    int icnt;
Howard Chu's avatar
Howard Chu committed
717
718
		    for ( icnt = vals[i].bv_len; --icnt > 0; ) {
				if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) break;
719
720
			}
    
Howard Chu's avatar
Howard Chu committed
721
			if ( ++icnt != vals[i].bv_len ) {
722
				fprintf( stderr, _("%s: illegal trailing space after"
723
724
					" \"%s: %s\" trimmed (line %d, entry \"%s\")\n"),
					prog, type, vals[i].bv_val, linenum+i, dn );
Howard Chu's avatar
Howard Chu committed
725
				vals[i].bv_val[icnt] = '\0';
726
			}
727
728
#endif /* LIBERAL_CHANGETYPE_MODOP */

729
730
			expect_modop = 0;
			expect_sep = 1;
Howard Chu's avatar
Howard Chu committed
731
			if ( BVICMP( btype+i, &BV_MODOPADD ) == 0 ) {
732
				modop = LDAP_MOD_ADD;
Howard Chu's avatar
Howard Chu committed
733
734
735
736
737
738
739
740
				mops[i] = M_SEP;
				nmods--;
			} else if ( BVICMP( btype+i, &BV_MODOPREPLACE ) == 0 ) {
			/* 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.
			 */
741
				modop = LDAP_MOD_REPLACE;
Howard Chu's avatar
Howard Chu committed
742
743
744
				mops[i] = modop | LDAP_MOD_BVALUES;
				btype[i] = vals[i];
			} else if ( BVICMP( btype+i, &BV_MODOPDELETE ) == 0 ) {
745
				modop = LDAP_MOD_DELETE;
Howard Chu's avatar
Howard Chu committed
746
747
748
				mops[i] = modop | LDAP_MOD_BVALUES;
				btype[i] = vals[i];
			} else if ( BVICMP( btype+i, &BV_MODOPINCREMENT ) == 0 ) {
749
				modop = LDAP_MOD_INCREMENT;
Howard Chu's avatar
Howard Chu committed
750
751
				mops[i] = M_SEP;
				nmods--;
752
753
754
755
756
757
			} 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;
758
			}
759
			bv = vals[i];
Howard Chu's avatar
Howard Chu committed
760
761
762
763
764
		} else if ( expect_sep && BER_BVISEMPTY( btype+i )) {
			mops[i] = M_SEP;
			expect_sep = 0;
			expect_modop = 1;
			nmods--;
765
		} else {
766
767
768
769
770
771
772
			if ( BVICMP( btype+i, &bv )) {
				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
773
774
775
776
777
778
779
780
			mops[i] = modop;
			/* If prev op was deferred and matches this type,
			 * clear the flag
			 */
			if ( (mops[i-1]&LDAP_MOD_BVALUES) && !BVICMP(btype+i,
				btype+i-1)) {
				mops[i-1] = M_SEP;
				nmods--;
781
			}
782
783
		}
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
784

785
#if 0	/* we should faithfully encode the LDIF, not combine */
Howard Chu's avatar
Howard Chu committed
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
	/* 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;
			if ( !BVICMP( btype+i, btype+j )) {
				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
819
	}
820
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
821

Howard Chu's avatar
Howard Chu committed
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
	/* 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;
		if ( mops[i] != mops[i-1] || BVICMP(btype+i,&bv)) {
			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;
853
	}
Howard Chu's avatar
Howard Chu committed
854
855
	bvl[k] = NULL;
	pmods[j] = NULL;
856

Howard Chu's avatar
Howard Chu committed
857
doit:
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
	/* 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 */
			}
		}
	}
893

894
895
896
897
898
899
900
901
	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
902

903
904
905
		if ( rc == LDAP_SUCCESS ) {
			rc = 0;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
906
907
	}

Howard Chu's avatar
Howard Chu committed
908
leave:
909
    if (pctrls != NULL) {
910
911
    	ldap_controls_free( pctrls );
	}
Howard Chu's avatar
Howard Chu committed
912
913
914
915
916
917
918
919
920
	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 );
921

922
	return( rc );
923
924
925
926
927
928
929
930
931
}

/* 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
932
parse_ldif_control(
Howard Chu's avatar
Howard Chu committed
933
	struct berval *bval,
934
	LDAPControl ***ppctrls )
935
{
936
937
938
	char *oid = NULL;
	int criticality = 0;   /* Default is false if not present */
	int i, rc=0;
Howard Chu's avatar
Howard Chu committed
939
	char *s, *oidStart;
940
941
	LDAPControl *newctrl = NULL;
	LDAPControl **pctrls = NULL;
Howard Chu's avatar
Howard Chu committed
942
943
	struct berval type, bv;
	int freeval;
944
945

	if (ppctrls) pctrls = *ppctrls;
Howard Chu's avatar
Howard Chu committed
946
	/* OID should come first. Validate and extract it. */
Howard Chu's avatar
Howard Chu committed
947
	s = bval->bv_val;
948
949
950
	if (*s == 0) return ( LDAP_PARAM_ERROR );
	oidStart = s;
	while (isdigit((unsigned char)*s) || *s == '.') {
951
		s++;                           /* OID should be digits or . */
952
953
954
955
956
957
958
959
960
961
	}
	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 */
	}
962

963
964
	oid = ber_strdup(oidStart);
	if (oid == NULL) return ( LDAP_NO_MEMORY );
965

966
967
	/* Optional Criticality field is next. */
	while (*s && isspace((unsigned char)*s)) {
968
		s++;                         /* Skip white space before criticality */
969
970
971
972
973
974
975
976
977
	}
	if (strncasecmp(s, "true", 4) == 0) {
		criticality = 1;
		s += 4;
	} 
	else if (strncasecmp(s, "false", 5) == 0) {
		criticality = 0;
		s += 5;
	}
978

979
980
	/* Optional value field is next */
	while (*s && isspace((unsigned char)*s)) {
981
		s++;                         /* Skip white space before value */
982
983
984
985
986
987
	}
	if (*s) {
		if (*s != ':') {           /* If value is present, must start with : */
			rc = LDAP_PARAM_ERROR;
			goto cleanup;
		}
988

Howard Chu's avatar
Howard Chu committed
989
990
991
992
993
		/* 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
994
		*/
Howard Chu's avatar
Howard Chu committed
995
996
997
998
		s--;
		*s = 'a';

		rc = ldif_parse_line2(s, &type, &bv, &freeval);
999
1000
		if (rc < 0) {
			rc = LDAP_PARAM_ERROR;
For faster browsing, not all history is shown. View entire blame