1/* ldapmodify.c - generic program to modify or add entries using LDAP */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2006 Howard Chu.
7 * Portions Copyright 1998-2003 Kurt D. Zeilenga.
8 * Portions Copyright 1998-2001 Net Boolean Incorporated.
9 * Portions Copyright 2001-2003 IBM Corporation.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted only as authorized by the OpenLDAP
14 * Public License.
15 *
16 * A copy of this license is available in the file LICENSE in the
17 * top-level directory of the distribution or, alternatively, at
18 * <http://www.OpenLDAP.org/license.html>.
19 */
20/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms are permitted
24 * provided that this notice is preserved and that due credit is given
25 * to the University of Michigan at Ann Arbor.  The name of the
26 * University may not be used to endorse or promote products derived
27 * from this software without specific prior written permission.  This
28 * software is provided ``as is'' without express or implied warranty.
29 */
30/* ACKNOWLEDGEMENTS:
31 * This work was originally developed by the University of Michigan
32 * (as part of U-MICH LDAP).  Additional significant contributors
33 * include:
34 *   Kurt D. Zeilenga
35 *   Norbert Klasen
36 *   Howard Chu
37 */
38
39#include "portable.h"
40
41#include <stdio.h>
42
43#include <ac/stdlib.h>
44#include <ac/ctype.h>
45#include <ac/string.h>
46#include <ac/unistd.h>
47#include <ac/socket.h>
48#include <ac/time.h>
49
50#ifdef HAVE_SYS_STAT_H
51#include <sys/stat.h>
52#endif
53
54#ifdef HAVE_SYS_FILE_H
55#include <sys/file.h>
56#endif
57#ifdef HAVE_FCNTL_H
58#include <fcntl.h>
59#endif
60
61#include <ldap.h>
62
63#include "lutil.h"
64#include "lutil_ldap.h"
65#include "ldif.h"
66#include "ldap_defaults.h"
67#include "ldap_pvt.h"
68#include "lber_pvt.h"
69
70#include "common.h"
71
72static int	ldapadd;
73static char *rejfile = NULL;
74static LDAP	*ld = NULL;
75
76#define	M_SEP	0x7f
77
78/* strings found in LDIF entries */
79static struct berval BV_VERSION = BER_BVC("version");
80static struct berval BV_DN = BER_BVC("dn");
81static struct berval BV_CONTROL = BER_BVC("control");
82static struct berval BV_CHANGETYPE = BER_BVC("changetype");
83static struct berval BV_ADDCT = BER_BVC("add");
84static struct berval BV_MODIFYCT = BER_BVC("modify");
85static struct berval BV_DELETECT = BER_BVC("delete");
86static struct berval BV_MODRDNCT = BER_BVC("modrdn");
87static struct berval BV_MODDNCT = BER_BVC("moddn");
88static struct berval BV_RENAMECT = BER_BVC("rename");
89static struct berval BV_MODOPADD = BER_BVC("add");
90static struct berval BV_MODOPREPLACE = BER_BVC("replace");
91static struct berval BV_MODOPDELETE = BER_BVC("delete");
92static struct berval BV_MODOPINCREMENT = BER_BVC("increment");
93static struct berval BV_NEWRDN = BER_BVC("newrdn");
94static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn");
95static struct berval BV_NEWSUP = BER_BVC("newsuperior");
96
97#define	BV_CASEMATCH(a, b) \
98	((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val))
99
100static int process_ldif_rec LDAP_P(( char *rbuf, int lineno ));
101static int parse_ldif_control LDAP_P(( struct berval *val, LDAPControl ***pctrls ));
102static int domodify LDAP_P((
103	const char *dn,
104	LDAPMod **pmods,
105	LDAPControl **pctrls,
106	int newentry ));
107static int dodelete LDAP_P((
108	const char *dn,
109	LDAPControl **pctrls ));
110static int dorename LDAP_P((
111	const char *dn,
112	const char *newrdn,
113	const char *newsup,
114	int deleteoldrdn,
115	LDAPControl **pctrls ));
116static int process_response(
117	LDAP *ld,
118	int msgid,
119	int res,
120	const char *dn );
121
122#ifdef LDAP_X_TXN
123static int txn = 0;
124static int txnabort = 0;
125struct berval *txn_id = NULL;
126#endif
127
128void
129usage( void )
130{
131	fprintf( stderr, _("Add or modify entries from an LDAP server\n\n"));
132	fprintf( stderr, _("usage: %s [options]\n"), prog);
133	fprintf( stderr, _("	The list of desired operations are read from stdin"
134		" or from the file\n"));
135	fprintf( stderr, _("	specified by \"-f file\".\n"));
136	fprintf( stderr, _("Add or modify options:\n"));
137	fprintf( stderr, _("  -a         add values (%s)\n"),
138		(ldapadd ? _("default") : _("default is to replace")));
139	fprintf( stderr, _("  -c         continuous operation mode (do not stop on errors)\n"));
140	fprintf( stderr, _("  -E [!]ext=extparam	modify extensions"
141		" (! indicate s criticality)\n"));
142	fprintf( stderr, _("  -f file    read operations from `file'\n"));
143	fprintf( stderr, _("  -M         enable Manage DSA IT control (-MM to make critical)\n"));
144	fprintf( stderr, _("  -P version protocol version (default: 3)\n"));
145#ifdef LDAP_X_TXN
146 	fprintf( stderr,
147		_("             [!]txn=<commit|abort>         (transaction)\n"));
148#endif
149	fprintf( stderr, _("  -S file    write skipped modifications to `file'\n"));
150
151	tool_common_usage();
152	exit( EXIT_FAILURE );
153}
154
155
156const char options[] = "aE:rS:"
157	"cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
158
159int
160handle_private_option( int i )
161{
162	char	*control, *cvalue;
163	int		crit;
164
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		crit = 0;
178		cvalue = NULL;
179		if( optarg[0] == '!' ) {
180			crit = 1;
181			optarg++;
182		}
183
184		control = ber_strdup( optarg );
185		if ( (cvalue = strchr( control, '=' )) != NULL ) {
186			*cvalue++ = '\0';
187		}
188
189#ifdef LDAP_X_TXN
190		if( strcasecmp( control, "txn" ) == 0 ) {
191			/* Transaction */
192			if( txn ) {
193				fprintf( stderr,
194					_("txn control previously specified\n"));
195				exit( EXIT_FAILURE );
196			}
197			if( cvalue != NULL ) {
198				if( strcasecmp( cvalue, "abort" ) == 0 ) {
199					txnabort=1;
200				} else if( strcasecmp( cvalue, "commit" ) != 0 ) {
201					fprintf( stderr, _("Invalid value for txn control, %s\n"),
202						cvalue );
203					exit( EXIT_FAILURE );
204				}
205			}
206
207			txn = 1 + crit;
208		} else
209#endif
210		{
211			fprintf( stderr, _("Invalid modify extension name: %s\n"),
212				control );
213			usage();
214		}
215		break;
216
217	case 'a':	/* add */
218		ldapadd = 1;
219		break;
220
221	case 'r':	/* replace (obsolete) */
222		break;
223
224	case 'S':	/* skipped modifications to file */
225		if( rejfile != NULL ) {
226			fprintf( stderr, _("%s: -S previously specified\n"), prog );
227			exit( EXIT_FAILURE );
228		}
229		rejfile = ber_strdup( optarg );
230		break;
231
232	default:
233		return 0;
234	}
235	return 1;
236}
237
238
239int
240main( int argc, char **argv )
241{
242	char		*rbuf = NULL, *rejbuf = NULL;
243	FILE		*rejfp;
244	struct LDIFFP *ldiffp = NULL, ldifdummy = {0};
245	char		*matched_msg, *error_msg;
246	int		rc, retval, ldifrc;
247	int		len;
248	int		i = 0;
249	int		lineno, nextline = 0, lmax = 0;
250	LDAPControl	c[1];
251
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	tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY );
258
259	tool_args( argc, argv );
260
261	if ( argc != optind ) usage();
262
263	if ( rejfile != NULL ) {
264		if (( rejfp = fopen( rejfile, "w" )) == NULL ) {
265			perror( rejfile );
266			retval = EXIT_FAILURE;
267			goto fail;
268		}
269	} else {
270		rejfp = NULL;
271	}
272
273	if ( infile != NULL ) {
274		if (( ldiffp = ldif_open( infile, "r" )) == NULL ) {
275			perror( infile );
276			retval = EXIT_FAILURE;
277			goto fail;
278		}
279	} else {
280		ldifdummy.fp = stdin;
281		ldiffp = &ldifdummy;
282	}
283
284	if ( debug ) ldif_debug = debug;
285
286	ld = tool_conn_setup( dont, 0 );
287
288	if ( !dont ) {
289		tool_bind( ld );
290	}
291
292#ifdef LDAP_X_TXN
293	if( txn ) {
294		/* start transaction */
295		rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id );
296		if( rc != LDAP_SUCCESS ) {
297			tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL );
298			if( txn > 1 ) {
299				retval = EXIT_FAILURE;
300				goto fail;
301			}
302			txn = 0;
303		}
304	}
305#endif
306
307	if ( 0
308#ifdef LDAP_X_TXN
309		|| txn
310#endif
311		)
312	{
313#ifdef LDAP_X_TXN
314		if( txn ) {
315			c[i].ldctl_oid = LDAP_CONTROL_X_TXN_SPEC;
316			c[i].ldctl_value = *txn_id;
317			c[i].ldctl_iscritical = 1;
318			i++;
319		}
320#endif
321	}
322
323	tool_server_controls( ld, c, i );
324
325	rc = 0;
326	retval = 0;
327	lineno = 1;
328	while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline,
329		&rbuf, &lmax )) > 0 )
330	{
331		if ( rejfp ) {
332			len = strlen( rbuf );
333			if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) {
334				perror( "malloc" );
335				retval = EXIT_FAILURE;
336				goto fail;
337			}
338			memcpy( rejbuf, rbuf, len+1 );
339		}
340
341		rc = process_ldif_rec( rbuf, lineno );
342		lineno = nextline+1;
343
344		if ( rc ) retval = rc;
345		if ( rc && rejfp ) {
346			fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc);
347
348			matched_msg = NULL;
349			ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg);
350			if ( matched_msg != NULL ) {
351				if ( *matched_msg != '\0' ) {
352					fprintf( rejfp, _(", matched DN: %s"), matched_msg );
353				}
354				ldap_memfree( matched_msg );
355			}
356
357			error_msg = NULL;
358			ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg);
359			if ( error_msg != NULL ) {
360				if ( *error_msg != '\0' ) {
361					fprintf( rejfp, _(", additional info: %s"), error_msg );
362				}
363				ldap_memfree( error_msg );
364			}
365			fprintf( rejfp, "\n%s\n", rejbuf );
366		}
367
368		if (rejfp) ber_memfree( rejbuf );
369	}
370	ber_memfree( rbuf );
371
372	if ( ldifrc < 0 )
373		retval = LDAP_OTHER;
374
375#ifdef LDAP_X_TXN
376	if( retval == 0 && txn ) {
377		rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
378		if ( rc != LDAP_OPT_SUCCESS ) {
379			fprintf( stderr, "Could not unset controls for ldap_txn_end\n");
380		}
381
382		/* create transaction */
383		rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL );
384		if( rc != LDAP_SUCCESS ) {
385			tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL );
386			retval = rc;
387		}
388	}
389#endif
390
391fail:;
392	if ( rejfp != NULL ) {
393		fclose( rejfp );
394	}
395
396	if ( ldiffp != NULL && ldiffp != &ldifdummy ) {
397		ldif_close( ldiffp );
398	}
399
400	tool_exit( ld, retval );
401}
402
403
404static int
405process_ldif_rec( char *rbuf, int linenum )
406{
407	char	*line, *dn, *newrdn, *newsup;
408	int		rc, modop;
409	int		expect_modop, expect_sep;
410	int		deleteoldrdn;
411	int		new_entry, delete_entry, got_all;
412	LDAPMod	**pmods, *lm = NULL;
413	int version;
414	LDAPControl **pctrls;
415	int i, j, k, lines, idn, nmods;
416	struct berval *btype, *vals, **bvl, bv;
417	char *freeval;
418	unsigned char *mops = NULL;
419
420	new_entry = ldapadd;
421
422	rc = got_all = delete_entry = modop = expect_modop = 0;
423	expect_sep = 0;
424	version = 0;
425	deleteoldrdn = 1;
426	pmods = NULL;
427	pctrls = NULL;
428	dn = newrdn = newsup = NULL;
429
430	lines = ldif_countlines( rbuf );
431	btype = ber_memcalloc( 1, (lines+1)*2*sizeof(struct berval)+lines );
432	if ( !btype )
433		return LDAP_NO_MEMORY;
434
435	vals = btype+lines+1;
436	freeval = (char *)(vals+lines+1);
437	i = -1;
438
439	while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
440		int freev;
441
442		if ( *line == '\n' || *line == '\0' ) {
443			break;
444		}
445
446		++i;
447
448		if ( line[0] == '-' && !line[1] ) {
449			BER_BVZERO( btype+i );
450			freeval[i] = 0;
451			continue;
452		}
453
454		if ( ( rc = ldif_parse_line2( line, btype+i, vals+i, &freev ) ) < 0 ) {
455			fprintf( stderr, _("%s: invalid format (line %d) entry: \"%s\"\n"),
456				prog, linenum+i, dn == NULL ? "" : dn );
457			rc = LDAP_PARAM_ERROR;
458			goto leave;
459		}
460		freeval[i] = freev;
461
462		if ( dn == NULL ) {
463			if ( linenum+i == 1 && BV_CASEMATCH( btype+i, &BV_VERSION )) {
464				int	v;
465				if( vals[i].bv_len == 0 || lutil_atoi( &v, vals[i].bv_val) != 0 || v != 1 ) {
466					fprintf( stderr,
467						_("%s: invalid version %s, line %d (ignored)\n"),
468						prog, vals[i].bv_val, linenum );
469				}
470				version++;
471
472			} else if ( BV_CASEMATCH( btype+i, &BV_DN )) {
473				dn = vals[i].bv_val;
474				idn = i;
475			}
476			/* skip all lines until we see "dn:" */
477		}
478	}
479
480	/* check to make sure there was a dn: line */
481	if ( !dn ) {
482		rc = 0;
483		goto leave;
484	}
485
486	lines = i+1;
487
488	if( lines == 0 ) {
489		rc = 0;
490		goto leave;
491	}
492
493	if( version && lines == 1 ) {
494		rc = 0;
495		goto leave;
496	}
497
498	i = idn+1;
499	/* Check for "control" tag after dn and before changetype. */
500	if ( BV_CASEMATCH( btype+i, &BV_CONTROL )) {
501		/* Parse and add it to the list of controls */
502		rc = parse_ldif_control( vals+i, &pctrls );
503		if (rc != 0) {
504			fprintf( stderr,
505				_("%s: Error processing %s line, line %d: %s\n"),
506				prog, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) );
507		}
508		i++;
509		if ( i>= lines ) {
510short_input:
511			fprintf( stderr,
512				_("%s: Expecting more input after %s line, line %d\n"),
513				prog, btype[i-1].bv_val, linenum+i );
514
515			rc = LDAP_PARAM_ERROR;
516			goto leave;
517		}
518	}
519
520	/* Check for changetype */
521	if ( BV_CASEMATCH( btype+i, &BV_CHANGETYPE )) {
522#ifdef LIBERAL_CHANGETYPE_MODOP
523		/* trim trailing spaces (and log warning ...) */
524		int icnt;
525		for ( icnt = vals[i].bv_len; --icnt > 0; ) {
526			if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) {
527				break;
528			}
529		}
530
531		if ( ++icnt != vals[i].bv_len ) {
532			fprintf( stderr, _("%s: illegal trailing space after"
533				" \"%s: %s\" trimmed (line %d, entry \"%s\")\n"),
534				prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn );
535			vals[i].bv_val[icnt] = '\0';
536		}
537#endif /* LIBERAL_CHANGETYPE_MODOP */
538
539		if ( BV_CASEMATCH( vals+i, &BV_MODIFYCT )) {
540			new_entry = 0;
541			expect_modop = 1;
542		} else if ( BV_CASEMATCH( vals+i, &BV_ADDCT )) {
543			new_entry = 1;
544			modop = LDAP_MOD_ADD;
545		} else if ( BV_CASEMATCH( vals+i, &BV_MODRDNCT )
546			|| BV_CASEMATCH( vals+i, &BV_MODDNCT )
547			|| BV_CASEMATCH( vals+i, &BV_RENAMECT ))
548		{
549			i++;
550			if ( i >= lines )
551				goto short_input;
552			if ( !BV_CASEMATCH( btype+i, &BV_NEWRDN )) {
553				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
554					" \"%s:\" (line %d, entry \"%s\")\n"),
555					prog, BV_NEWRDN.bv_val, btype[i].bv_val, linenum+i, dn );
556				rc = LDAP_PARAM_ERROR;
557				goto leave;
558			}
559			newrdn = vals[i].bv_val;
560			i++;
561			if ( i >= lines )
562				goto short_input;
563			if ( !BV_CASEMATCH( btype+i, &BV_DELETEOLDRDN )) {
564				fprintf( stderr, _("%s: expecting \"%s:\" but saw"
565					" \"%s:\" (line %d, entry \"%s\")\n"),
566					prog, BV_DELETEOLDRDN.bv_val, btype[i].bv_val, linenum+i, dn );
567				rc = LDAP_PARAM_ERROR;
568				goto leave;
569			}
570			deleteoldrdn = ( vals[i].bv_val[0] == '0' ) ? 0 : 1;
571			i++;
572			if ( i < lines ) {
573				if ( !BV_CASEMATCH( btype+i, &BV_NEWSUP )) {
574					fprintf( stderr, _("%s: expecting \"%s:\" but saw"
575						" \"%s:\" (line %d, entry \"%s\")\n"),
576						prog, BV_NEWSUP.bv_val, btype[i].bv_val, linenum+i, dn );
577					rc = LDAP_PARAM_ERROR;
578					goto leave;
579				}
580				newsup = vals[i].bv_val;
581				i++;
582			}
583			got_all = 1;
584		} else if ( BV_CASEMATCH( vals+i, &BV_DELETECT )) {
585			got_all = delete_entry = 1;
586		} else {
587			fprintf( stderr,
588				_("%s:  unknown %s \"%s\" (line %d, entry \"%s\")\n"),
589				prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn );
590			rc = LDAP_PARAM_ERROR;
591			goto leave;
592		}
593		i++;
594	} else if ( ldapadd ) {		/*  missing changetype => add */
595		new_entry = 1;
596		modop = LDAP_MOD_ADD;
597	} else {
598		expect_modop = 1;	/* missing changetype => modify */
599	}
600
601	if ( got_all ) {
602		if ( i < lines ) {
603			fprintf( stderr,
604				_("%s: extra lines at end (line %d, entry \"%s\")\n"),
605				prog, linenum+i, dn );
606			rc = LDAP_PARAM_ERROR;
607			goto leave;
608		}
609		goto doit;
610	}
611
612	nmods = lines - i;
613	idn = i;
614
615	if ( new_entry ) {
616		int fv;
617
618		/* Make sure all attributes with multiple values are contiguous */
619		for (; i<lines; i++) {
620			for (j=i+1; j<lines; j++) {
621				if ( BV_CASEMATCH( btype+i, btype+j )) {
622					nmods--;
623					/* out of order, move intervening attributes down */
624					if ( j != i+1 ) {
625						bv = vals[j];
626						fv = freeval[j];
627						for (k=j; k>i; k--) {
628							btype[k] = btype[k-1];
629							vals[k] = vals[k-1];
630							freeval[k] = freeval[k-1];
631						}
632						k++;
633						btype[k] = btype[i];
634						vals[k] = bv;
635						freeval[k] = fv;
636					}
637					i++;
638				}
639			}
640		}
641		/* Allocate space for array of mods, array of pointers to mods,
642		 * and array of pointers to values, allowing for NULL terminators
643		 * for the pointer arrays...
644		 */
645		lm = ber_memalloc( nmods * sizeof(LDAPMod) +
646			(nmods+1) * sizeof(LDAPMod*) +
647			(lines + nmods - idn) * sizeof(struct berval *));
648		pmods = (LDAPMod **)(lm+nmods);
649		bvl = (struct berval **)(pmods+nmods+1);
650
651		j = 0;
652		k = -1;
653		BER_BVZERO(&bv);
654		for (i=idn; i<lines; i++) {
655			if ( BV_CASEMATCH( btype+i, &BV_DN )) {
656				fprintf( stderr, _("%s: attributeDescription \"%s\":"
657					" (possible missing newline"
658						" after line %d, entry \"%s\"?)\n"),
659					prog, btype[i].bv_val, linenum+i - 1, dn );
660			}
661			if ( !BV_CASEMATCH( btype+i, &bv )) {
662				bvl[k++] = NULL;
663				bv = btype[i];
664				lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
665				lm[j].mod_type = bv.bv_val;
666				lm[j].mod_bvalues = bvl+k;
667				pmods[j] = lm+j;
668				j++;
669			}
670			bvl[k++] = vals+i;
671		}
672		bvl[k] = NULL;
673		pmods[j] = NULL;
674		goto doit;
675	}
676
677	mops = ber_memalloc( lines+1 );
678	mops[lines] = M_SEP;
679	mops[i-1] = M_SEP;
680
681	for ( ; i<lines; i++ ) {
682		if ( expect_modop ) {
683#ifdef LIBERAL_CHANGETYPE_MODOP
684			/* trim trailing spaces (and log warning ...) */
685		    int icnt;
686		    for ( icnt = vals[i].bv_len; --icnt > 0; ) {
687				if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) break;
688			}
689
690			if ( ++icnt != vals[i].bv_len ) {
691				fprintf( stderr, _("%s: illegal trailing space after"
692					" \"%s: %s\" trimmed (line %d, entry \"%s\")\n"),
693					prog, type, vals[i].bv_val, linenum+i, dn );
694				vals[i].bv_val[icnt] = '\0';
695			}
696#endif /* LIBERAL_CHANGETYPE_MODOP */
697
698			expect_modop = 0;
699			expect_sep = 1;
700			if ( BV_CASEMATCH( btype+i, &BV_MODOPADD )) {
701				modop = LDAP_MOD_ADD;
702				mops[i] = M_SEP;
703				nmods--;
704			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPREPLACE )) {
705			/* defer handling these since they might have no values.
706			 * Use the BVALUES flag to signal that these were
707			 * deferred. If values are provided later, this
708			 * flag will be switched off.
709			 */
710				modop = LDAP_MOD_REPLACE;
711				mops[i] = modop | LDAP_MOD_BVALUES;
712				btype[i] = vals[i];
713			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPDELETE )) {
714				modop = LDAP_MOD_DELETE;
715				mops[i] = modop | LDAP_MOD_BVALUES;
716				btype[i] = vals[i];
717			} else if ( BV_CASEMATCH( btype+i, &BV_MODOPINCREMENT )) {
718				modop = LDAP_MOD_INCREMENT;
719				mops[i] = M_SEP;
720				nmods--;
721			} else {	/* no modify op: invalid LDIF */
722				fprintf( stderr, _("%s: modify operation type is missing at"
723					" line %d, entry \"%s\"\n"),
724					prog, linenum+i, dn );
725				rc = LDAP_PARAM_ERROR;
726				goto leave;
727			}
728			bv = vals[i];
729		} else if ( expect_sep && BER_BVISEMPTY( btype+i )) {
730			mops[i] = M_SEP;
731			expect_sep = 0;
732			expect_modop = 1;
733			nmods--;
734		} else {
735			if ( !BV_CASEMATCH( btype+i, &bv )) {
736				fprintf( stderr, _("%s: wrong attributeType at"
737					" line %d, entry \"%s\"\n"),
738					prog, linenum+i, dn );
739				rc = LDAP_PARAM_ERROR;
740				goto leave;
741			}
742			mops[i] = modop;
743			/* If prev op was deferred and matches this type,
744			 * clear the flag
745			 */
746			if ( (mops[i-1] & LDAP_MOD_BVALUES)
747				&& BV_CASEMATCH( btype+i, btype+i-1 ))
748			{
749				mops[i-1] = M_SEP;
750				nmods--;
751			}
752		}
753	}
754
755#if 0	/* we should faithfully encode the LDIF, not combine */
756	/* Make sure all modops with multiple values are contiguous */
757	for (i=idn; i<lines; i++) {
758		if ( mops[i] == M_SEP )
759			continue;
760		for (j=i+1; j<lines; j++) {
761			if ( mops[j] == M_SEP || mops[i] != mops[j] )
762				continue;
763			if ( BV_CASEMATCH( btype+i, btype+j )) {
764				nmods--;
765				/* out of order, move intervening attributes down */
766				if ( j != i+1 ) {
767					int c;
768					struct berval bv;
769					char fv;
770
771					c = mops[j];
772					bv = vals[j];
773					fv = freeval[j];
774					for (k=j; k>i; k--) {
775						btype[k] = btype[k-1];
776						vals[k] = vals[k-1];
777						freeval[k] = freeval[k-1];
778						mops[k] = mops[k-1];
779					}
780					k++;
781					btype[k] = btype[i];
782					vals[k] = bv;
783					freeval[k] = fv;
784					mops[k] = c;
785				}
786				i++;
787			}
788		}
789	}
790#endif
791
792	/* Allocate space for array of mods, array of pointers to mods,
793	 * and array of pointers to values, allowing for NULL terminators
794	 * for the pointer arrays...
795	 */
796	lm = ber_memalloc( nmods * sizeof(LDAPMod) +
797		(nmods+1) * sizeof(LDAPMod*) +
798		(lines + nmods - idn) * sizeof(struct berval *));
799	pmods = (LDAPMod **)(lm+nmods);
800	bvl = (struct berval **)(pmods+nmods+1);
801
802	j = 0;
803	k = -1;
804	BER_BVZERO(&bv);
805	mops[idn-1] = M_SEP;
806	for (i=idn; i<lines; i++) {
807		if ( mops[i] == M_SEP )
808			continue;
809		if ( mops[i] != mops[i-1] || !BV_CASEMATCH( btype+i, &bv )) {
810			bvl[k++] = NULL;
811			bv = btype[i];
812			lm[j].mod_op = mops[i] | LDAP_MOD_BVALUES;
813			lm[j].mod_type = bv.bv_val;
814			if ( mops[i] & LDAP_MOD_BVALUES ) {
815				lm[j].mod_bvalues = NULL;
816			} else {
817				lm[j].mod_bvalues = bvl+k;
818			}
819			pmods[j] = lm+j;
820			j++;
821		}
822		bvl[k++] = vals+i;
823	}
824	bvl[k] = NULL;
825	pmods[j] = NULL;
826
827doit:
828	/* If default controls are set (as with -M option) and controls are
829	   specified in the LDIF file, we must add the default controls to
830	   the list of controls sent with the ldap operation.
831	*/
832	if ( rc == 0 ) {
833		if (pctrls) {
834			LDAPControl **defctrls = NULL;   /* Default server controls */
835			LDAPControl **newctrls = NULL;
836			ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls);
837			if (defctrls) {
838				int npc=0;                       /* Num of LDIF controls */
839				int ndefc=0;                     /* Num of default controls */
840				while (pctrls[npc]) npc++;       /* Count LDIF controls */
841				while (defctrls[ndefc]) ndefc++; /* Count default controls */
842				newctrls = ber_memrealloc(pctrls,
843					(npc+ndefc+1)*sizeof(LDAPControl*));
844
845				if (newctrls == NULL) {
846					rc = LDAP_NO_MEMORY;
847				} else {
848					int i;
849					pctrls = newctrls;
850					for (i=npc; i<npc+ndefc; i++) {
851						pctrls[i] = ldap_control_dup(defctrls[i-npc]);
852						if (pctrls[i] == NULL) {
853							rc = LDAP_NO_MEMORY;
854							break;
855						}
856					}
857					pctrls[npc+ndefc] = NULL;
858				}
859				ldap_controls_free(defctrls);  /* Must be freed by library */
860			}
861		}
862	}
863
864	if ( rc == 0 ) {
865		if ( delete_entry ) {
866			rc = dodelete( dn, pctrls );
867		} else if ( newrdn != NULL ) {
868			rc = dorename( dn, newrdn, newsup, deleteoldrdn, pctrls );
869		} else {
870			rc = domodify( dn, pmods, pctrls, new_entry );
871		}
872
873		if ( rc == LDAP_SUCCESS ) {
874			rc = 0;
875		}
876	}
877
878leave:
879    if (pctrls != NULL) {
880    	ldap_controls_free( pctrls );
881	}
882	if ( lm != NULL ) {
883		ber_memfree( lm );
884	}
885	if ( mops != NULL ) {
886		ber_memfree( mops );
887	}
888	for (i=lines-1; i>=0; i--)
889		if ( freeval[i] ) ber_memfree( vals[i].bv_val );
890	ber_memfree( btype );
891
892	return( rc );
893}
894
895/* Parse an LDIF control line of the form
896      control:  oid  [true/false]  [: value]              or
897      control:  oid  [true/false]  [:: base64-value]      or
898      control:  oid  [true/false]  [:< url]
899   The control is added to the list of controls in *ppctrls.
900*/
901static int
902parse_ldif_control(
903	struct berval *bval,
904	LDAPControl ***ppctrls )
905{
906	char *oid = NULL;
907	int criticality = 0;   /* Default is false if not present */
908	int i, rc=0;
909	char *s, *oidStart;
910	LDAPControl *newctrl = NULL;
911	LDAPControl **pctrls = NULL;
912	struct berval type, bv = BER_BVNULL;
913	int freeval = 0;
914
915	if (ppctrls) pctrls = *ppctrls;
916	/* OID should come first. Validate and extract it. */
917	s = bval->bv_val;
918	if (*s == 0) return ( LDAP_PARAM_ERROR );
919	oidStart = s;
920	while (isdigit((unsigned char)*s) || *s == '.') {
921		s++;                           /* OID should be digits or . */
922	}
923	if (s == oidStart) {
924		return ( LDAP_PARAM_ERROR );   /* OID was not present */
925	}
926	if (*s) {                          /* End of OID should be space or NULL */
927		if (!isspace((unsigned char)*s)) {
928			return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */
929		}
930		*s++ = 0;                    /* Replace space with null to terminate */
931	}
932
933	oid = ber_strdup(oidStart);
934	if (oid == NULL) return ( LDAP_NO_MEMORY );
935
936	/* Optional Criticality field is next. */
937	while (*s && isspace((unsigned char)*s)) {
938		s++;                         /* Skip white space before criticality */
939	}
940	if (strncasecmp(s, "true", 4) == 0) {
941		criticality = 1;
942		s += 4;
943	}
944	else if (strncasecmp(s, "false", 5) == 0) {
945		criticality = 0;
946		s += 5;
947	}
948
949	/* Optional value field is next */
950	while (*s && isspace((unsigned char)*s)) {
951		s++;                         /* Skip white space before value */
952	}
953	if (*s) {
954		if (*s != ':') {           /* If value is present, must start with : */
955			rc = LDAP_PARAM_ERROR;
956			goto cleanup;
957		}
958
959		/* Back up so value is in the form
960		     a: value
961		     a:: base64-value
962		     a:< url
963		   Then we can use ldif_parse_line2 to extract and decode the value
964		*/
965		s--;
966		*s = 'a';
967
968		rc = ldif_parse_line2(s, &type, &bv, &freeval);
969		if (rc < 0) {
970			rc = LDAP_PARAM_ERROR;
971			goto cleanup;
972		}
973    }
974
975	/* Create a new LDAPControl structure. */
976	newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl));
977	if ( newctrl == NULL ) {
978		rc = LDAP_NO_MEMORY;
979		goto cleanup;
980	}
981	newctrl->ldctl_oid = oid;
982	oid = NULL;
983	newctrl->ldctl_iscritical = criticality;
984	if ( freeval )
985		newctrl->ldctl_value = bv;
986	else
987		ber_dupbv( &newctrl->ldctl_value, &bv );
988
989	/* Add the new control to the passed-in list of controls. */
990	i = 0;
991	if (pctrls) {
992		while ( pctrls[i] ) {    /* Count the # of controls passed in */
993			i++;
994		}
995	}
996	/* Allocate 1 more slot for the new control and 1 for the NULL. */
997	pctrls = (LDAPControl **) ber_memrealloc(pctrls,
998		(i+2)*(sizeof(LDAPControl *)));
999	if (pctrls == NULL) {
1000		rc = LDAP_NO_MEMORY;
1001		goto cleanup;
1002	}
1003	pctrls[i] = newctrl;
1004	newctrl = NULL;
1005	pctrls[i+1] = NULL;
1006	*ppctrls = pctrls;
1007
1008cleanup:
1009	if (newctrl) {
1010		if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid);
1011		if (newctrl->ldctl_value.bv_val) {
1012			ber_memfree(newctrl->ldctl_value.bv_val);
1013		}
1014		ber_memfree(newctrl);
1015	}
1016	if (oid) ber_memfree(oid);
1017
1018	return( rc );
1019}
1020
1021
1022static int
1023domodify(
1024	const char *dn,
1025	LDAPMod **pmods,
1026	LDAPControl **pctrls,
1027	int newentry )
1028{
1029	int			rc, i, j, k, notascii, op;
1030	struct berval	*bvp;
1031
1032	if ( dn == NULL ) {
1033		fprintf( stderr, _("%s: no DN specified\n"), prog );
1034		return( LDAP_PARAM_ERROR );
1035	}
1036
1037	if ( pmods == NULL ) {
1038		/* implement "touch" (empty sequence)
1039		 * modify operation (note that there
1040		 * is no symmetry with the UNIX command,
1041		 * since \"touch\" on a non-existent entry
1042		 * will fail)*/
1043		printf( "warning: no attributes to %sadd (entry=\"%s\")\n",
1044			newentry ? "" : "change or ", dn );
1045
1046	} else {
1047		for ( i = 0; pmods[ i ] != NULL; ++i ) {
1048			op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1049			if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
1050				fprintf( stderr,
1051					_("%s: attribute \"%s\" has no values (entry=\"%s\")\n"),
1052					prog, pmods[i]->mod_type, dn );
1053				return LDAP_PARAM_ERROR;
1054			}
1055		}
1056
1057		if ( verbose ) {
1058			for ( i = 0; pmods[ i ] != NULL; ++i ) {
1059				op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1060				printf( "%s %s:\n",
1061					op == LDAP_MOD_REPLACE ? _("replace") :
1062						op == LDAP_MOD_ADD ?  _("add") :
1063							op == LDAP_MOD_INCREMENT ?  _("increment") :
1064								op == LDAP_MOD_DELETE ?  _("delete") :
1065									_("unknown"),
1066					pmods[ i ]->mod_type );
1067
1068				if ( pmods[ i ]->mod_bvalues != NULL ) {
1069					for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
1070						bvp = pmods[ i ]->mod_bvalues[ j ];
1071						notascii = 0;
1072						for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
1073							if ( !isascii( bvp->bv_val[ k ] )) {
1074								notascii = 1;
1075								break;
1076							}
1077						}
1078						if ( notascii ) {
1079							printf( _("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len );
1080						} else {
1081							printf( "\t%s\n", bvp->bv_val );
1082						}
1083					}
1084				}
1085			}
1086		}
1087	}
1088
1089	if ( newentry ) {
1090		printf( "%sadding new entry \"%s\"\n", dont ? "!" : "", dn );
1091	} else {
1092		printf( "%smodifying entry \"%s\"\n", dont ? "!" : "", dn );
1093	}
1094
1095	if ( !dont ) {
1096		int	msgid;
1097		if ( newentry ) {
1098			rc = ldap_add_ext( ld, dn, pmods, pctrls, NULL, &msgid );
1099		} else {
1100			rc = ldap_modify_ext( ld, dn, pmods, pctrls, NULL, &msgid );
1101		}
1102
1103		if ( rc != LDAP_SUCCESS ) {
1104			/* print error message about failed update including DN */
1105			fprintf( stderr, _("%s: update failed: %s\n"), prog, dn );
1106			tool_perror( newentry ? "ldap_add" : "ldap_modify",
1107				rc, NULL, NULL, NULL, NULL );
1108			goto done;
1109		}
1110		rc = process_response( ld, msgid,
1111			newentry ? LDAP_RES_ADD : LDAP_RES_MODIFY, dn );
1112
1113		if ( verbose && rc == LDAP_SUCCESS ) {
1114			printf( _("modify complete\n") );
1115		}
1116
1117	} else {
1118		rc = LDAP_SUCCESS;
1119	}
1120
1121done:
1122	putchar( '\n' );
1123	return rc;
1124}
1125
1126
1127static int
1128dodelete(
1129	const char *dn,
1130	LDAPControl **pctrls )
1131{
1132	int	rc;
1133	int msgid;
1134
1135	printf( _("%sdeleting entry \"%s\"\n"), dont ? "!" : "", dn );
1136	if ( !dont ) {
1137		rc = ldap_delete_ext( ld, dn, pctrls, NULL, &msgid );
1138		if ( rc != LDAP_SUCCESS ) {
1139			fprintf( stderr, _("%s: delete failed: %s\n"), prog, dn );
1140			tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL );
1141			goto done;
1142		}
1143		rc = process_response( ld, msgid, LDAP_RES_DELETE, dn );
1144
1145		if ( verbose && rc == LDAP_SUCCESS ) {
1146			printf( _("delete complete\n") );
1147		}
1148	} else {
1149		rc = LDAP_SUCCESS;
1150	}
1151
1152done:
1153	putchar( '\n' );
1154	return( rc );
1155}
1156
1157
1158static int
1159dorename(
1160	const char *dn,
1161	const char *newrdn,
1162	const char* newsup,
1163	int deleteoldrdn,
1164	LDAPControl **pctrls )
1165{
1166	int	rc;
1167	int msgid;
1168
1169	printf( _("%smodifying rdn of entry \"%s\"\n"), dont ? "!" : "", dn );
1170	if ( verbose ) {
1171		printf( _("\tnew RDN: \"%s\" (%skeep existing values)\n"),
1172			newrdn, deleteoldrdn ? _("do not ") : "" );
1173	}
1174	if ( !dont ) {
1175		rc = ldap_rename( ld, dn, newrdn, newsup, deleteoldrdn,
1176			pctrls, NULL, &msgid );
1177		if ( rc != LDAP_SUCCESS ) {
1178			fprintf( stderr, _("%s: rename failed: %s\n"), prog, dn );
1179			tool_perror( "ldap_rename", rc, NULL, NULL, NULL, NULL );
1180			goto done;
1181		}
1182		rc = process_response( ld, msgid, LDAP_RES_RENAME, dn );
1183
1184		if ( verbose && rc == LDAP_SUCCESS ) {
1185			printf( _("rename complete\n") );
1186		}
1187	} else {
1188		rc = LDAP_SUCCESS;
1189	}
1190
1191done:
1192	putchar( '\n' );
1193	return( rc );
1194}
1195
1196static const char *
1197res2str( int res ) {
1198	switch ( res ) {
1199	case LDAP_RES_ADD:
1200		return "ldap_add";
1201	case LDAP_RES_DELETE:
1202		return "ldap_delete";
1203	case LDAP_RES_MODIFY:
1204		return "ldap_modify";
1205	case LDAP_RES_MODRDN:
1206		return "ldap_rename";
1207	default:
1208		assert( 0 );
1209	}
1210
1211	return "ldap_unknown";
1212}
1213
1214static int process_response(
1215	LDAP *ld,
1216	int msgid,
1217	int op,
1218	const char *dn )
1219{
1220	LDAPMessage	*res;
1221	int		rc = LDAP_OTHER, msgtype;
1222	struct timeval	tv = { 0, 0 };
1223	int		err;
1224	char		*text = NULL, *matched = NULL, **refs = NULL;
1225	LDAPControl	**ctrls = NULL;
1226
1227	for ( ; ; ) {
1228		tv.tv_sec = 0;
1229		tv.tv_usec = 100000;
1230
1231		rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
1232		if ( tool_check_abandon( ld, msgid ) ) {
1233			return LDAP_CANCELLED;
1234		}
1235
1236		if ( rc == -1 ) {
1237			ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc );
1238			tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
1239			return rc;
1240		}
1241
1242		if ( rc != 0 ) {
1243			break;
1244		}
1245	}
1246
1247	msgtype = ldap_msgtype( res );
1248
1249	rc = ldap_parse_result( ld, res, &err, &matched, &text, &refs, &ctrls, 1 );
1250	if ( rc == LDAP_SUCCESS ) rc = err;
1251
1252#ifdef LDAP_X_TXN
1253	if ( rc == LDAP_X_TXN_SPECIFY_OKAY ) {
1254		rc = LDAP_SUCCESS;
1255	} else
1256#endif
1257	if ( rc != LDAP_SUCCESS ) {
1258		tool_perror( res2str( op ), rc, NULL, matched, text, refs );
1259	} else if ( msgtype != op ) {
1260		fprintf( stderr, "%s: msgtype: expected %d got %d\n",
1261			res2str( op ), op, msgtype );
1262		rc = LDAP_OTHER;
1263	}
1264
1265	if ( text ) ldap_memfree( text );
1266	if ( matched ) ldap_memfree( matched );
1267	if ( refs ) ber_memvfree( (void **)refs );
1268
1269	if ( ctrls ) {
1270		tool_print_ctrls( ld, ctrls );
1271		ldap_controls_free( ctrls );
1272	}
1273
1274	return rc;
1275}
1276