xref: /netbsd-src/external/bsd/openldap/dist/clients/tools/ldapdelete.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* ldapdelete.c - simple program to delete an entry using LDAP */
2 /* $OpenLDAP: pkg/ldap/clients/tools/ldapdelete.c,v 1.118.2.7 2008/02/12 00:32:01 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * Portions Copyright 1998-2003 Kurt D. Zeilenga.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms are permitted
21  * provided that this notice is preserved and that due credit is given
22  * to the University of Michigan at Ann Arbor.  The name of the
23  * University may not be used to endorse or promote products derived
24  * from this software without specific prior written permission.  This
25  * software is provided ``as is'' without express or implied warranty.
26  */
27 /* ACKNOWLEDGEMENTS:
28  * This work was originally developed by the University of Michigan
29  * (as part of U-MICH LDAP).  Additional significant contributors
30  * include:
31  *   Kurt D. Zeilenga
32  */
33 
34 #include "portable.h"
35 
36 #include <stdio.h>
37 
38 #include <ac/stdlib.h>
39 #include <ac/ctype.h>
40 #include <ac/string.h>
41 #include <ac/unistd.h>
42 #include <ac/socket.h>
43 #include <ac/time.h>
44 
45 #include <ldap.h>
46 #include "lutil.h"
47 #include "lutil_ldap.h"
48 #include "ldap_defaults.h"
49 
50 #include "common.h"
51 
52 
53 static int	prune = 0;
54 static int sizelimit = -1;
55 
56 
57 static int dodelete LDAP_P((
58     LDAP *ld,
59     const char *dn));
60 
61 static int deletechildren LDAP_P((
62 	LDAP *ld,
63 	const char *dn,
64 	int subentries ));
65 
66 void
67 usage( void )
68 {
69 	fprintf( stderr, _("Delete entries from an LDAP server\n\n"));
70 	fprintf( stderr, _("usage: %s [options] [dn]...\n"), prog);
71 	fprintf( stderr, _("	dn: list of DNs to delete. If not given, it will be readed from stdin\n"));
72 	fprintf( stderr, _("	    or from the file specified with \"-f file\".\n"));
73 	fprintf( stderr, _("Delete Options:\n"));
74 	fprintf( stderr, _("  -r         delete recursively\n"));
75 	tool_common_usage();
76 	exit( EXIT_FAILURE );
77 }
78 
79 
80 const char options[] = "r"
81 	"cd:D:e:f:h:H:IMnO:o:p:P:QR:U:vVw:WxX:y:Y:z:Z";
82 
83 int
84 handle_private_option( int i )
85 {
86 	int ival;
87 	char *next;
88 	switch ( i ) {
89 #if 0
90 		int crit;
91 		char *control, *cvalue;
92 	case 'E': /* delete extensions */
93 		if( protocol == LDAP_VERSION2 ) {
94 			fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
95 				prog, protocol );
96 			exit( EXIT_FAILURE );
97 		}
98 
99 		/* should be extended to support comma separated list of
100 		 *	[!]key[=value] parameters, e.g.  -E !foo,bar=567
101 		 */
102 
103 		crit = 0;
104 		cvalue = NULL;
105 		if( optarg[0] == '!' ) {
106 			crit = 1;
107 			optarg++;
108 		}
109 
110 		control = strdup( optarg );
111 		if ( (cvalue = strchr( control, '=' )) != NULL ) {
112 			*cvalue++ = '\0';
113 		}
114 		fprintf( stderr, _("Invalid delete extension name: %s\n"), control );
115 		usage();
116 #endif
117 
118 	case 'r':
119 		prune = 1;
120 		break;
121 
122 	case 'z':	/* size limit */
123 		if ( strcasecmp( optarg, "none" ) == 0 ) {
124 			sizelimit = 0;
125 
126 		} else if ( strcasecmp( optarg, "max" ) == 0 ) {
127 			sizelimit = LDAP_MAXINT;
128 
129 		} else {
130 			ival = strtol( optarg, &next, 10 );
131 			if ( next == NULL || next[0] != '\0' ) {
132 				fprintf( stderr,
133 					_("Unable to parse size limit \"%s\"\n"), optarg );
134 				exit( EXIT_FAILURE );
135 			}
136 			sizelimit = ival;
137 		}
138 		if( sizelimit < 0 || sizelimit > LDAP_MAXINT ) {
139 			fprintf( stderr, _("%s: invalid sizelimit (%d) specified\n"),
140 				prog, sizelimit );
141 			exit( EXIT_FAILURE );
142 		}
143 		break;
144 
145 	default:
146 		return 0;
147 	}
148 	return 1;
149 }
150 
151 
152 static void
153 private_conn_setup( LDAP *ld )
154 {
155 	/* this seems prudent for searches below */
156 	int deref = LDAP_DEREF_NEVER;
157 	ldap_set_option( ld, LDAP_OPT_DEREF, &deref );
158 }
159 
160 
161 int
162 main( int argc, char **argv )
163 {
164 	char		buf[ 4096 ];
165 	FILE		*fp;
166 	LDAP		*ld;
167 	int		rc, retval;
168 
169     fp = NULL;
170 
171 	tool_init( TOOL_DELETE );
172     prog = lutil_progname( "ldapdelete", argc, argv );
173 
174 	tool_args( argc, argv );
175 
176 	if ( infile != NULL ) {
177 		if (( fp = fopen( infile, "r" )) == NULL ) {
178 			perror( optarg );
179 			exit( EXIT_FAILURE );
180 	    }
181 	} else {
182 	if ( optind >= argc ) {
183 	    fp = stdin;
184 	}
185     }
186 
187 	ld = tool_conn_setup( 0, &private_conn_setup );
188 
189 	if ( pw_file || want_bindpw ) {
190 		if ( pw_file ) {
191 			rc = lutil_get_filed_password( pw_file, &passwd );
192 			if( rc ) return EXIT_FAILURE;
193 		} else {
194 			passwd.bv_val = getpassphrase( _("Enter LDAP Password: ") );
195 			passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
196 		}
197 	}
198 
199 	tool_bind( ld );
200 
201 	tool_server_controls( ld, NULL, 0 );
202 
203 	retval = rc = 0;
204 
205 	if ( fp == NULL ) {
206 		for ( ; optind < argc; ++optind ) {
207 			rc = dodelete( ld, argv[ optind ] );
208 
209 			/* Stop on error and no -c option */
210 			if( rc != 0 ) {
211 				retval = rc;
212 				if( contoper == 0 ) break;
213 			}
214 		}
215 	} else {
216 		while ((rc == 0 || contoper) && fgets(buf, sizeof(buf), fp) != NULL) {
217 			buf[ strlen( buf ) - 1 ] = '\0'; /* remove trailing newline */
218 
219 			if ( *buf != '\0' ) {
220 				rc = dodelete( ld, buf );
221 				if ( rc != 0 )
222 					retval = rc;
223 			}
224 		}
225 	}
226 
227 	tool_unbind( ld );
228 	tool_destroy();
229     return retval;
230 }
231 
232 
233 static int dodelete(
234     LDAP	*ld,
235     const char	*dn)
236 {
237 	int id;
238 	int	rc, code;
239 	char *matcheddn = NULL, *text = NULL, **refs = NULL;
240 	LDAPControl **ctrls = NULL;
241 	LDAPMessage *res;
242 	int subentries = 0;
243 
244 	if ( verbose ) {
245 		printf( _("%sdeleting entry \"%s\"\n"),
246 			(dont ? "!" : ""), dn );
247 	}
248 
249 	if ( dont ) {
250 		return LDAP_SUCCESS;
251 	}
252 
253 	/* If prune is on, remove a whole subtree.  Delete the children of the
254 	 * DN recursively, then the DN requested.
255 	 */
256 	if ( prune ) {
257 retry:;
258 		deletechildren( ld, dn, subentries );
259 	}
260 
261 	rc = ldap_delete_ext( ld, dn, NULL, NULL, &id );
262 	if ( rc != LDAP_SUCCESS ) {
263 		fprintf( stderr, "%s: ldap_delete_ext: %s (%d)\n",
264 			prog, ldap_err2string( rc ), rc );
265 		return rc;
266 	}
267 
268 	for ( ; ; ) {
269 		struct timeval tv;
270 
271 		if ( tool_check_abandon( ld, id ) ) {
272 			return LDAP_CANCELLED;
273 		}
274 
275 		tv.tv_sec = 0;
276 		tv.tv_usec = 100000;
277 
278 		rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
279 		if ( rc < 0 ) {
280 			tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
281 			return rc;
282 		}
283 
284 		if ( rc != 0 ) {
285 			break;
286 		}
287 	}
288 
289 	rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 );
290 
291 	switch ( rc ) {
292 	case LDAP_SUCCESS:
293 		break;
294 
295 	case LDAP_NOT_ALLOWED_ON_NONLEAF:
296 		if ( prune && !subentries ) {
297 			subentries = 1;
298 			goto retry;
299 		}
300 		/* fallthru */
301 
302 	default:
303 		fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n",
304 			prog, ldap_err2string( rc ), rc );
305 		return rc;
306 	}
307 
308 	if( code != LDAP_SUCCESS ) {
309 		tool_perror( "ldap_delete", code, NULL, matcheddn, text, refs );
310 	} else if ( verbose &&
311 		((matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ))
312 	{
313 		printf( _("Delete Result: %s (%d)\n"),
314 			ldap_err2string( code ), code );
315 
316 		if( text && *text ) {
317 			printf( _("Additional info: %s\n"), text );
318 		}
319 
320 		if( matcheddn && *matcheddn ) {
321 			printf( _("Matched DN: %s\n"), matcheddn );
322 		}
323 
324 		if( refs ) {
325 			int i;
326 			for( i=0; refs[i]; i++ ) {
327 				printf(_("Referral: %s\n"), refs[i] );
328 			}
329 		}
330 	}
331 
332 	if (ctrls) {
333 		tool_print_ctrls( ld, ctrls );
334 		ldap_controls_free( ctrls );
335 	}
336 
337 	ber_memfree( text );
338 	ber_memfree( matcheddn );
339 	ber_memvfree( (void **) refs );
340 
341 	return code;
342 }
343 
344 /*
345  * Delete all the children of an entry recursively until leaf nodes are reached.
346  */
347 static int deletechildren(
348 	LDAP *ld,
349 	const char *base,
350 	int subentries )
351 {
352 	LDAPMessage *res, *e;
353 	int entries;
354 	int rc = LDAP_SUCCESS, srch_rc;
355 	static char *attrs[] = { LDAP_NO_ATTRS, NULL };
356 	LDAPControl c, *ctrls[2], **ctrlsp = NULL;
357 	BerElement *ber = NULL;
358 
359 	if ( verbose ) printf ( _("deleting children of: %s\n"), base );
360 
361 	if ( subentries ) {
362 		/*
363 		 * Do a one level search at base for subentry children.
364 		 */
365 
366 		if ((ber = ber_alloc_t(LBER_USE_DER)) == NULL) {
367 			return EXIT_FAILURE;
368 		}
369 		rc = ber_printf( ber, "b", 1 );
370 		if ( rc == -1 ) {
371 			ber_free( ber, 1 );
372 			fprintf( stderr, _("Subentries control encoding error!\n"));
373 			return EXIT_FAILURE;
374 		}
375 		if ( ber_flatten2( ber, &c.ldctl_value, 0 ) == -1 ) {
376 			return EXIT_FAILURE;
377 		}
378 		c.ldctl_oid = LDAP_CONTROL_SUBENTRIES;
379 		c.ldctl_iscritical = 1;
380 		ctrls[0] = &c;
381 		ctrls[1] = NULL;
382 		ctrlsp = ctrls;
383 	}
384 
385 	/*
386 	 * Do a one level search at base for children.  For each, delete its children.
387 	 */
388 more:;
389 	srch_rc = ldap_search_ext_s( ld, base, LDAP_SCOPE_ONELEVEL, NULL, attrs, 1,
390 		ctrlsp, NULL, NULL, sizelimit, &res );
391 	switch ( srch_rc ) {
392 	case LDAP_SUCCESS:
393 	case LDAP_SIZELIMIT_EXCEEDED:
394 		break;
395 	default:
396 		tool_perror( "ldap_search", srch_rc, NULL, NULL, NULL, NULL );
397 		return( srch_rc );
398 	}
399 
400 	entries = ldap_count_entries( ld, res );
401 
402 	if ( entries > 0 ) {
403 		int i;
404 
405 		for (e = ldap_first_entry( ld, res ), i = 0; e != NULL;
406 			e = ldap_next_entry( ld, e ), i++ )
407 		{
408 			char *dn = ldap_get_dn( ld, e );
409 
410 			if( dn == NULL ) {
411 				ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc );
412 				tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL );
413 				ber_memfree( dn );
414 				return rc;
415 			}
416 
417 			rc = deletechildren( ld, dn, 0 );
418 			if ( rc != LDAP_SUCCESS ) {
419 				tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL );
420 				ber_memfree( dn );
421 				return rc;
422 			}
423 
424 			if ( verbose ) {
425 				printf( _("\tremoving %s\n"), dn );
426 			}
427 
428 			rc = ldap_delete_ext_s( ld, dn, NULL, NULL );
429 			if ( rc != LDAP_SUCCESS ) {
430 				tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL );
431 				ber_memfree( dn );
432 				return rc;
433 
434 			}
435 
436 			if ( verbose ) {
437 				printf( _("\t%s removed\n"), dn );
438 			}
439 
440 			ber_memfree( dn );
441 		}
442 	}
443 
444 	ldap_msgfree( res );
445 
446 	if ( srch_rc == LDAP_SIZELIMIT_EXCEEDED ) {
447 		goto more;
448 	}
449 
450 	return rc;
451 }
452