xref: /titanic_52/usr/src/cmd/ldap/common/ldapsearch.c (revision 0eb822a1c0c2bea495647510b75f77f0e57633eb)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * The contents of this file are subject to the Netscape Public
10  * License Version 1.1 (the "License"); you may not use this file
11  * except in compliance with the License. You may obtain a copy of
12  * the License at http://www.mozilla.org/NPL/
13  *
14  * Software distributed under the License is distributed on an "AS
15  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16  * implied. See the License for the specific language governing
17  * rights and limitations under the License.
18  *
19  * The Original Code is Mozilla Communicator client code, released
20  * March 31, 1998.
21  *
22  * The Initial Developer of the Original Code is Netscape
23  * Communications Corporation. Portions created by Netscape are
24  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
25  * Rights Reserved.
26  *
27  * Contributor(s):
28  */
29 
30 /* ldapsearch.c - generic program to search LDAP */
31 
32 #include "ldaptool.h"
33 #include "fileurl.h"
34 #ifdef SOLARIS_LDAP_CMD
35 #include <locale.h>
36 #include "solaris-int.h"
37 #endif	/* SOLARIS_LDAP_CMD */
38 
39 #define VLV_PARAM_SEP ':'
40 
41 #ifndef SOLARIS_LDAP_CMD
42 #define gettext(s) s
43 #endif
44 
45 static void usage( void );
46 static int dosearch( LDAP *ld, char *base, int scope, char **attrs,
47 			 int attrsonly, char *filtpatt, char *value);
48 static void write_string_attr_value( char *attrname, char *strval,
49 	unsigned long opts );
50 #define LDAPTOOL_WRITEVALOPT_SUPPRESS_NAME	0x01
51 static int write_ldif_value( char *type, char *value, unsigned long vallen,
52 	unsigned long ldifoptions );
53 static void print_entry( LDAP *ld, LDAPMessage *entry, int attrsonly );
54 static void options_callback( int option, char *optarg );
55 static void parse_and_display_reference( LDAP *ld, LDAPMessage *ref );
56 static char *sortresult2string(unsigned long result);
57 static char *changetype_num2string( int chgtype );
58 static char *msgtype2str( int msgtype );
59 static char  **get_effectiverights_attrlist(char * optarg);
60 
61 #ifdef SOLARIS_LDAP_CMD
62 static void fill_ldapsearch_msgtypes( void );
63 #endif	/* SOLARIS_LDAP_CMD */
64 
65 /*
66  * Prefix used in names of pseudo attributes added to the entry LDIF
67  * output if we receive an entryChangeNotification control with an entry
68  * (requested using persistent search).
69  */
70 #define LDAPTOOL_PSEARCH_ATTR_PREFIX	"persistentSearch-"
71 
72 
73 static void
74 usage( void )
75 {
76     fprintf( stderr, gettext("usage: %s -b basedn [options] filter [attributes...]\n"), ldaptool_progname );
77     fprintf( stderr, gettext("       %s -b basedn [options] -f file [attributes...]\nwhere:\n"), ldaptool_progname );
78     fprintf( stderr, gettext("    basedn\tbase dn for search\n") );
79     fprintf( stderr, gettext("\t\t(if the environment variable LDAP_BASEDN is set,\n") );
80     fprintf( stderr, gettext("\t\tthen the -b flag is not required)\n") );
81     fprintf( stderr, gettext("    filter\tRFC-2254 compliant LDAP search filter\n") );
82     fprintf( stderr, gettext("    file\tfile containing a sequence of LDAP search filters to use\n") );
83     fprintf( stderr, gettext("    attributes\twhitespace-separated list of attributes to retrieve\n") );
84     fprintf( stderr, gettext("\t\t(if no attribute list is given, all are retrieved)\n") );
85     fprintf( stderr, gettext("options:\n") );
86     ldaptool_common_usage( 0 );
87 #if defined( XP_WIN32 )
88     fprintf( stderr, gettext("    -t\t\twrite values to files in temp directory.\n") );
89 #else
90     fprintf( stderr, gettext("    -t\t\twrite values to files in /tmp\n") );
91 #endif
92     fprintf( stderr, gettext("    -U\t\tproduce file URLs in conjunction with -t\n") );
93     fprintf( stderr, gettext("    -e\t\tminimize base-64 encoding of values\n") );
94     fprintf( stderr, gettext("    -u\t\tinclude User Friendly entry names in the output\n") );
95 #ifndef HAVE_SASL_OPTIONS
96     fprintf( stderr, gettext("    -o\t\tprint entries using old format (default is LDIF)\n") );
97 #endif
98     fprintf( stderr, gettext("    -T\t\tdon't fold (wrap) long lines (default is to fold)\n") );
99     fprintf( stderr, gettext("    -1\t\tomit leading \"version: %d\" line in LDIF output\n"), LDIF_VERSION_ONE );
100     fprintf( stderr, gettext("    -A\t\tretrieve attribute names only (no values)\n") );
101     fprintf( stderr, gettext("    -B\t\tprint non-ASCII values and use old output format (attr=value)\n") );
102     fprintf( stderr, gettext("    -x\t\tperforming sorting on server\n") );
103 #ifdef SOLARIS_LDAP_CMD
104     fprintf( stderr, gettext("    -r\t\tprint entries using old format (default is LDIF)\n") );
105 #endif	/* SOLARIS_LDAP_CMD */
106     fprintf( stderr, gettext("    -F sep\tprint `sep' instead of `%s' between attribute names\n"), LDAPTOOL_DEFSEP );
107     fprintf( stderr, gettext("          \tand values\n") );
108     fprintf( stderr, gettext("    -S attr\tsort the results by attribute `attr'\n") );
109     fprintf( stderr, gettext("    -s scope\tone of base, one, or sub (default is sub)\n") );
110     fprintf( stderr, gettext("    -a deref\tone of never, always, search, or find (default: never)\n") );
111     fprintf( stderr, gettext("            \t(alias dereferencing)\n") );
112     fprintf( stderr, gettext("    -l timelim\ttime limit (in seconds) for search (default is no limit)\n") );
113     fprintf( stderr, gettext("    -z sizelim\tsize limit (in entries) for search (default is no limit)\n") );
114     fprintf( stderr, gettext("    -C ps:changetype[:changesonly[:entrychgcontrols]]\n") );
115     fprintf( stderr, gettext("\t\tchangetypes are add,delete,modify,moddn,any\n") );
116     fprintf( stderr, gettext("\t\tchangesonly and  entrychgcontrols are boolean values\n") );
117     fprintf( stderr, gettext("\t\t(default is 1)\n") );
118     fprintf( stderr, gettext("    -G before%cafter%cindex%ccount | before%cafter%cvalue where 'before' and\n"), VLV_PARAM_SEP, VLV_PARAM_SEP, VLV_PARAM_SEP, VLV_PARAM_SEP, VLV_PARAM_SEP );
119     fprintf( stderr, gettext("\t\t'after' are the number of entries surrounding 'index.'\n"));
120     fprintf( stderr, gettext("\t\t'count' is the content count, 'value' is the search value.\n"));
121 #ifndef SOLARIS_LDAP_CMD
122     fprintf( stderr, gettext("    -c authzid\tspecifies the getEffectiveRights control authzid\n"));
123 	fprintf( stderr, gettext("\t\t eg. dn:uid=bjensen,dc=example,dc=com\n"));
124     fprintf( stderr, gettext("\t\t A value of \"\" means \"the authorization id for the operation\".\n"));
125     fprintf( stderr, gettext("\t\t A value of \"dn:\" means \"anonymous\"\n"));
126     fprintf( stderr, gettext("\t\t (The aclRights operational attribute must be requested)\n"));
127 	fprintf( stderr, gettext("    -X attrlist\tspecifies the getEffectiveRights control specific attribute list.\n"));
128     fprintf( stderr, gettext("\t\t eg. \"nsroledn userPassword\"\n"));
129 #endif	/* SOLARIS_LDAP_CMD */
130 
131     exit( LDAP_PARAM_ERROR );
132 }
133 
134 static char	*base = NULL;
135 static char	*sep = LDAPTOOL_DEFSEP;
136 static char	**sortattr = NULL;
137 static char     *vlv_value = NULL;
138 static int	sortsize = 0;
139 static int	*skipsortattr = NULL;
140 static int	includeufn, allow_binary, vals2tmp, ldif, scope, deref;
141 static int	attrsonly, timelimit, sizelimit, server_sort, fold;
142 static int	minimize_base64, produce_file_urls;
143 static int	use_vlv = 0, vlv_before, vlv_after, vlv_index, vlv_count;
144 static int	use_psearch=0;
145 static int	write_ldif_version = 1;
146 #ifndef SOLARIS_LDAP_CMD
147 static char *get_effectiverights_control_target_dn = NULL; /* -c */
148 static char **get_effectiverights_control_attrlist = NULL;  /* -X */
149 static int	do_effective_rights_control = 0;
150 #endif	/* SOLARIS_LDAP_CMD */
151 
152 /* Persistent search variables */
153 static int	chgtype=0, changesonly=1, return_echg_ctls=1;
154 
155 
156 int
157 main( int argc, char **argv )
158 {
159     char		*filtpattern, **attrs;
160     int			rc, optind, i, first, free_filtpattern;
161     LDAP		*ld;
162 
163 #ifdef SOLARIS_LDAP_CMD
164     char *locale = setlocale(LC_ALL, "");
165     textdomain(TEXT_DOMAIN);
166     ldaptool_require_binddn = 0;
167 #endif	/* SOLARIS_LDAP_CMD */
168 
169     free_filtpattern = 0;
170     deref = LDAP_DEREF_NEVER;
171     allow_binary = vals2tmp = attrsonly = 0;
172     minimize_base64 = produce_file_urls = 0;
173     ldif = 1;
174     fold = 1;
175     sizelimit = timelimit = 0;
176     scope = LDAP_SCOPE_SUBTREE;
177 	server_sort = 0;
178 
179 
180 #ifdef notdef
181 #ifdef HPUX11
182 #ifndef __LP64__
183 	_main( argc, argv);
184 #endif /* __LP64_ */
185 #endif /* HPUX11 */
186 #endif
187 
188 
189     ldaptool_reset_control_array( ldaptool_request_ctrls );
190 #ifdef HAVE_SASL_OPTIONS
191 #ifdef SOLARIS_LDAP_CMD
192     optind = ldaptool_process_args( argc, argv, "ABLTU1etuxra:b:F:G:l:S:s:z:C:",
193         0, options_callback );
194 #else
195     optind = ldaptool_process_args( argc, argv, "ABLTU1etuxa:b:F:G:l:S:s:z:C:c:",
196         0, options_callback );
197 #endif	/* SOLARIS_LDAP_CMD */
198 #else
199     optind = ldaptool_process_args( argc, argv, "ABLTU1eotuxa:b:F:G:l:S:s:z:C:c:",
200         0, options_callback );
201 #endif	/* HAVE_SASL_OPTIONS */
202 
203     if ( optind == -1 ) {
204 	usage();
205     }
206 
207     if ( base == NULL ) {
208 	if (( base = getenv( "LDAP_BASEDN" )) == NULL ) {
209 	    usage();
210 	}
211     }
212     if ( sortattr ) {
213 	for ( sortsize = 0; sortattr[sortsize] != NULL; sortsize++ ) {
214 	    ;       /* NULL */
215 	}
216 	sortsize++;             /* add in the final NULL field */
217 	skipsortattr = (int *) malloc( sortsize * sizeof(int *) );
218 	if ( skipsortattr == NULL ) {
219     		fprintf( stderr, gettext("Out of memory\n") );
220 		exit( LDAP_NO_MEMORY );
221 	}
222 	memset( (char *) skipsortattr, 0, sortsize * sizeof(int *) );
223     } else if ( server_sort ) {
224 	server_sort = 0;   /* ignore this option if no sortattrs were given */
225     }
226 
227     if ( argc - optind < 1 ) {
228 	if ( ldaptool_fp == NULL ) {
229 	    usage();
230 	}
231 	attrs = NULL;
232 	filtpattern = "%s";
233     } else {	/* there are additional args (filter + attrs) */
234 	if ( ldaptool_fp == NULL || strstr( argv[ optind ], "%s" ) != NULL ) {
235 	    filtpattern = ldaptool_local2UTF8( argv[ optind ] );
236 	    /* since local2UTF8 always allocates something, we should free it */
237 	    free_filtpattern = 1;
238 	    ++optind;
239 	} else {
240 	    filtpattern = "%s";
241 	}
242 
243 	if ( argv[ optind ] == NULL ) {
244 	    attrs = NULL;
245 	} else if ( sortattr == NULL || *sortattr == '\0' || server_sort) {
246 	    attrs = &argv[ optind ];
247 	} else {
248 	    attrs = ldap_charray_dup( &argv[ optind ] );
249 	    if ( attrs == NULL ) {
250 		fprintf( stderr, gettext("Out of memory\n") );
251 		exit( LDAP_NO_MEMORY );
252 	    }
253 	    for ( i = 0; i < (sortsize - 1); i++ ) {
254                 if ( !ldap_charray_inlist( attrs, sortattr[i] ) ) {
255                     if ( ldap_charray_add( &attrs, sortattr[i] ) != 0 ) {
256     			fprintf( stderr, gettext("Out of memory\n") );
257 			exit( LDAP_NO_MEMORY );
258 		    }
259                     /*
260                      * attribute in the search list only for the purpose of
261                      * sorting
262                      */
263                     skipsortattr[i] = 1;
264                 }
265 	    }
266         }
267     }
268 
269     ld = ldaptool_ldap_init( 0 );
270 
271     if ( !ldaptool_not ) {
272 	ldap_set_option( ld, LDAP_OPT_DEREF, &deref );
273 	ldap_set_option( ld, LDAP_OPT_TIMELIMIT, &timelimit );
274 	ldap_set_option( ld, LDAP_OPT_SIZELIMIT, &sizelimit );
275     }
276 
277     ldaptool_bind( ld );
278 
279     if ( ldaptool_verbose ) {
280 	printf( gettext("filter pattern: %s\nreturning: "), filtpattern );
281 	if ( attrs == NULL ) {
282 	    printf( gettext("ALL") );
283 	} else {
284 	    for ( i = 0; attrs[ i ] != NULL; ++i ) {
285 		printf( "%s ", attrs[ i ] );
286 	    }
287 	}
288 	putchar( '\n' );
289     }
290 
291     if ( ldaptool_fp == NULL ) {
292 	char *conv;
293 
294 	conv = ldaptool_local2UTF8( base );
295 	rc = dosearch( ld, conv, scope, attrs, attrsonly, filtpattern, "" );
296 	if( conv != NULL )
297             free( conv );
298     } else {
299 	int done = 0;
300 
301 	rc = LDAP_SUCCESS;
302 	first = 1;
303 	while ( rc == LDAP_SUCCESS && !done ) {
304 	    char *linep = NULL;
305 	    int   increment = 0;
306 	    int	  c, index;
307 
308 	    /* allocate initial block of memory */
309 	    if ((linep = (char *)malloc(BUFSIZ)) == NULL) {
310 	        fprintf( stderr, gettext("Out of memory\n") );
311 		exit( LDAP_NO_MEMORY );
312 	    }
313 	    increment++;
314 	    index = 0;
315 	    while ((c = fgetc( ldaptool_fp )) != '\n' && c != EOF) {
316 
317 	        /* check if we will overflow the buffer */
318 	        if ((c != EOF) && (index == ((increment * BUFSIZ) -1))) {
319 
320 		    /* if we did, add another BUFSIZ worth of bytes */
321 	   	    if ((linep = (char *)
322 			realloc(linep, (increment + 1) * BUFSIZ)) == NULL) {
323 			fprintf( stderr, gettext("Out of memory\n") );
324 			exit( LDAP_NO_MEMORY );
325 		    }
326 		    increment++;
327 		}
328 		linep[index++] = c;
329 	    }
330 
331 	    if (c == EOF) {
332 		done = 1;
333 		break;
334 	    }
335 
336 	    linep[index] = '\0';
337 
338 	    if ( !first ) {
339 		putchar( '\n' );
340 	    } else {
341 		first = 0;
342 	    }
343 	    rc = dosearch( ld, base, scope, attrs, attrsonly, filtpattern,
344 		    linep );
345 	    free (linep);
346 	}
347     }
348 
349     ldaptool_cleanup( ld );
350     if (free_filtpattern != 0 && filtpattern != NULL) {
351 	free (filtpattern);
352     }
353     return( rc );
354 }
355 
356 
357 static void
358 options_callback( int option, char *optarg )
359 {
360     char *s, *temp_arg, *ps_ptr, *ps_arg;
361 
362     switch( option ) {
363     case 'u':	/* include UFN */
364 	++includeufn;
365 	break;
366     case 't':	/* write attribute values to /tmp files */
367 	++vals2tmp;
368 	break;
369     case 'U':	/* produce file URLs in conjunction with -t */
370 	++produce_file_urls;
371 	break;
372     case 'e':	/* minimize base-64 encoding of values */
373 	++minimize_base64;
374 	break;
375     case 'A':	/* retrieve attribute names only -- no values */
376 	++attrsonly;
377 	break;
378     case 'L':       /* print entries in LDIF format -- now the default */
379 	break;
380 #ifdef SOLARIS_LDAP_CMD
381     case 'r':	/* print entries in the old format */
382 	ldif = 0;
383 	break;
384 #endif	/* SOLARIS_LDAP_CMD */
385 #ifdef HAVE_SASL_OPTIONS
386 #ifdef HAVE_SASL_OPTIONS_2
387     case 'o':	/* print entries using old ldapsearch format */
388 	ldif = 0;
389 	break;
390 #endif
391 #else
392     case 'o':	/* print entries using old ldapsearch format */
393 	ldif = 0;
394 	break;
395 #endif
396     case 'B':	/* allow binary values to be printed, use old format */
397 	++allow_binary;
398 	ldif = 0;
399 	break;
400     case '1':	/* omit leading "version: #" line from LDIF output */
401 	write_ldif_version = 0;
402 	break;
403     case 's':	/* search scope */
404 	if ( strncasecmp( optarg, "base", 4 ) == 0 ) {
405 	    scope = LDAP_SCOPE_BASE;
406 	} else if ( strncasecmp( optarg, "one", 3 ) == 0 ) {
407 	    scope = LDAP_SCOPE_ONELEVEL;
408 	} else if ( strncasecmp( optarg, "sub", 3 ) == 0 ) {
409 	    scope = LDAP_SCOPE_SUBTREE;
410 	} else {
411 	    fprintf( stderr, gettext("scope should be base, one, or sub\n") );
412 	    usage();
413 	}
414 	break;
415 
416     case 'a':	/* set alias deref option */
417 	if ( strncasecmp( optarg, "never", 5 ) == 0 ) {
418 	    deref = LDAP_DEREF_NEVER;
419 	} else if ( strncasecmp( optarg, "search", 5 ) == 0 ) {
420 	    deref = LDAP_DEREF_SEARCHING;
421 	} else if ( strncasecmp( optarg, "find", 4 ) == 0 ) {
422 	    deref = LDAP_DEREF_FINDING;
423 	} else if ( strncasecmp( optarg, "always", 6 ) == 0 ) {
424 	    deref = LDAP_DEREF_ALWAYS;
425 	} else {
426 	    fprintf( stderr, gettext("alias deref should be never, search, find, or always\n") );
427 	    usage();
428 	}
429 	break;
430 
431     case 'F':	/* field separator */
432 	sep = strdup( optarg );
433 	ldif = 0;
434 	break;
435 #ifndef SOLARIS_LDAP_CMD
436 	case 'c':
437 		if ( optarg && optarg[0] == '\0' ) {
438 			/* -c ""
439 				means "This user"
440 			*/
441 			get_effectiverights_control_target_dn = NULL;
442 			do_effective_rights_control = 1;
443 		}else if ( strlen(optarg) < 3 || (strncasecmp(optarg, "dn:", 3) != 0) ) {
444 			fprintf(stderr, gettext("-c wrong format--should be \"\" or \"dn:...\".\n"
445 				"\"dn:\" means anonymous user."));
446 			usage();
447 		} else {
448 			get_effectiverights_control_target_dn = strdup(optarg);
449 			do_effective_rights_control = 1;
450 		}
451 	break;
452 	case 'X':
453 		get_effectiverights_control_attrlist = get_effectiverights_attrlist(optarg);
454 		do_effective_rights_control = 1;
455 	break;
456 #endif	/* SOLARIS_LDAP_CMD */
457     case 'b':	/* searchbase */
458 	base = strdup( optarg );
459 	break;
460     case 'l':	/* time limit */
461 	timelimit = atoi( optarg );
462 	break;
463     case 'x':	/* server sorting requested */
464 	server_sort = 1;
465 	break;
466     case 'z':	/* size limit */
467 	sizelimit = atoi( optarg );
468 	break;
469     case 'S':	/* sort attribute */
470 	ldap_charray_add( &sortattr, strdup( optarg ) );
471 	break;
472     case 'T':	/* don't fold lines */
473 	fold = 0;
474 	break;
475     case 'G':  /* do the virtual list setup */
476 	use_vlv++;
477 	s = strchr(optarg, VLV_PARAM_SEP );
478 
479 	if (s != NULL)
480 	{
481 	    vlv_before = atoi(optarg);
482 	    s++;
483 	    vlv_after = atoi( s );
484 	    s = strchr(s, VLV_PARAM_SEP );
485 	    if (s != NULL)
486 	    {
487 		s++;
488 		/* below is a small set of logic to implement the following cases
489 		 * -G23:23:wilber
490 		 * -G23:23:"wilber:wright"
491 		 * -G23:23:'wilber'
492 		 * -G23:23:wilber wright
493 		 * all of the above are before, after, value -  NOTE: a colon not in a quoted
494 		 * string will break the parser!!!!
495 		 * -G23:23:45:600
496 		 * above is index, count encoding
497 		 */
498 
499 		if (*s == '\'' || *s == '"')
500 		{
501 		    vlv_value = strdup( s );
502 		}
503 		else
504 		{
505 		    if (strchr( s, VLV_PARAM_SEP ))
506 		    {
507 			/* we have an index + count option */
508 			vlv_index = atoi( s );
509 			vlv_count = atoi( strchr( s, VLV_PARAM_SEP) + 1);
510 		    }
511 		    else
512 		    {
513 			/* we don't have a quote surrounding the assertion value
514 			 * do we need to???
515 			 */
516 			vlv_value = strdup( s );
517 		    }
518 		}
519 	    }
520 	    else
521 	    {
522 		fprintf( stderr,gettext("Illegal 'after' paramater for virtual list\n") );
523 		exit( LDAP_PARAM_ERROR );
524 	    }
525 
526 	}
527 	else
528 	{
529 	    fprintf( stderr,gettext("Illegal 'before' paramater for virtual list\n") );
530 	    exit( LDAP_PARAM_ERROR );
531 	}
532 	break;
533     case 'C':
534 	use_psearch++;
535 	if ( (ps_arg = strdup( optarg)) == NULL ) {
536 	    perror ("strdup");
537 	    exit (LDAP_NO_MEMORY);
538 	}
539 
540 	ps_ptr=strtok(ps_arg, ":");
541 	if (ps_ptr == NULL || (strcasecmp(ps_ptr, "ps")) ) {
542 	    fprintf (stderr, gettext("Invalid argument for -C\n"));
543 	    usage();
544 	}
545 	if (NULL != (ps_ptr=strtok(NULL, ":"))) {
546 	    if ( (temp_arg = strdup( ps_ptr )) == NULL ) {
547 	        perror ("strdup");
548 	    	exit (LDAP_NO_MEMORY);
549 	    }
550 	} else {
551 	    fprintf (stderr, gettext("Invalid argument for -C\n"));
552 	    usage();
553 	}
554 	if (NULL != (ps_ptr=strtok(NULL, ":"))) {
555 	    if ( (changesonly = ldaptool_boolean_str2value(ps_ptr, 0)) == -1) {
556 		fprintf(stderr, gettext("Invalid option value: %s\n"), ps_ptr);
557 		usage();
558 	    }
559 	}
560 	if (NULL != (ps_ptr=strtok(NULL, ":"))) {
561 	    if ( (return_echg_ctls = ldaptool_boolean_str2value(ps_ptr, 0)) == -1) {
562 		fprintf(stderr, gettext("Invalid option value: %s\n"), ps_ptr);
563 		usage();
564 	    }
565 	}
566 
567 	/* Now parse the temp_arg and build chgtype as
568 	 * the changetypes are encountered */
569 
570 	if ((ps_ptr = strtok( temp_arg, "," )) == NULL) {
571 		usage();
572 	} else {
573 	    while ( ps_ptr ) {
574 		if ((strcasecmp(ps_ptr, "add"))==0)
575 		    chgtype |= LDAP_CHANGETYPE_ADD;
576 		else if ((strcasecmp(ps_ptr, "delete"))==0)
577 		    chgtype |= LDAP_CHANGETYPE_DELETE;
578 		else if ((strcasecmp(ps_ptr, "modify"))==0)
579 		    chgtype |= LDAP_CHANGETYPE_MODIFY;
580 		else if ((strcasecmp(ps_ptr, "moddn"))==0)
581 		    chgtype |= LDAP_CHANGETYPE_MODDN;
582 		else if ((strcasecmp(ps_ptr, "any"))==0)
583 		    chgtype = LDAP_CHANGETYPE_ANY;
584 		else {
585 			fprintf(stderr, gettext("Unknown changetype: %s\n"), ps_ptr);
586 			usage();
587 		}
588 		ps_ptr = strtok( NULL, "," );
589 	    }
590 	  }
591 	break;
592     default:
593 	usage();
594 	break;
595     }
596 }
597 
598 
599 static int
600 dosearch( ld, base, scope, attrs, attrsonly, filtpatt, value )
601     LDAP	*ld;
602     char	*base;
603     int		scope;
604     char	**attrs;
605     int		attrsonly;
606     char	*filtpatt;
607     char	*value;
608 {
609     char		**refs = NULL, filter[ BUFSIZ ], *filterp = NULL;
610     int			rc, first, matches;
611     LDAPMessage		*res, *e;
612     LDAPControl		*ldctrl;
613     LDAPControl		**ctrl_response_array = NULL;
614     LDAPVirtualList	vlv_data;
615     int			msgid = 0;
616     int			length = 0;
617     int			mallocd_filter = 0;
618 
619     if ( strstr( filtpatt, "%s" ) == NULL ) {	/* no need to sprintf() */
620 	    filterp = filtpatt;
621     } else {
622 	length = strlen( filtpatt ) + strlen ( value ) +1;
623 	if ( length > BUFSIZ ) {
624 	    if ((filterp = (char *)
625 		 malloc ( length )) == NULL) {
626 		perror( gettext("filter and/or pattern too long?") );
627 		exit (LDAP_PARAM_ERROR);
628 	    }
629 	    mallocd_filter = 1;
630 	} else {
631 	    filterp = filter;
632 	}
633 
634 #ifdef HAVE_SNPRINTF
635 	if ( snprintf( filterp, length, filtpatt, value ) < 0 ) {
636 	    perror( gettext("snprintf filter (filter and/or pattern too long?)") );
637 	    exit( LDAP_PARAM_ERROR );
638 	}
639 #else
640 	sprintf( filterp, filtpatt, value );
641 #endif
642     }
643 
644     if ( *filterp == '\0' ) {	/* treat empty filter is a shortcut for oc=* */
645 	if (mallocd_filter) {
646 	    free(filterp);
647 	    mallocd_filter = 0;
648 	}
649 	filterp = "(objectclass=*)";
650     }
651 
652     if ( ldaptool_verbose ) {
653 	/*
654 	 * Display the filter that will be used.  Add surrounding parens.
655 	 * if they are missing.
656 	 */
657 	if ( '(' == *filterp ) {
658 	    printf( "filter is: %s\n", filterp );
659 	} else {
660 	    printf( "filter is: (%s)\n", filterp );
661 	}
662     }
663 
664     if ( ldaptool_not ) {
665 	if (mallocd_filter) free(filterp);
666 	return( LDAP_SUCCESS );
667     }
668 
669     if (( ldctrl = ldaptool_create_manage_dsait_control()) != NULL ) {
670 	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
671     }
672 
673     if ((ldctrl = ldaptool_create_proxyauth_control(ld)) !=NULL) {
674 	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
675     }
676 
677 #ifndef SOLARIS_LDAP_CMD
678     if ( do_effective_rights_control ) {
679         if ((ldctrl = ldaptool_create_geteffectiveRights_control(ld,
680 							       get_effectiverights_control_target_dn,
681 							       (const char**)	get_effectiverights_control_attrlist)) !=NULL) {
682 	    ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
683         }
684     }
685 #endif	/* SOLARIS_LDAP_CMD */
686 
687     if (use_psearch) {
688 	if ( ldap_create_persistentsearch_control( ld, chgtype,
689                 changesonly, return_echg_ctls,
690 		1, &ldctrl ) != LDAP_SUCCESS )
691 	{
692 		ldap_perror( ld, "ldap_create_persistentsearch_control" );
693 		return (1);
694 	}
695 	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
696     }
697 
698 
699     if (server_sort) {
700 	/* First make a sort key list from the attribute list we have */
701 	LDAPsortkey **keylist = NULL;
702 	int i = 0;
703 	char *sortattrs = NULL;
704 	char *s = NULL;
705 	int string_length = 0;
706 
707 	/* Count the sort strings */
708 	for (i = 0; i < sortsize - 1 ; i++) {
709 	    string_length += strlen(sortattr[i]) + 1;
710 	}
711 
712 	sortattrs = (char *) malloc(string_length + 1);
713 	if (NULL == sortattrs) {
714 	    fprintf( stderr, gettext("Out of memory\n") );
715 	    exit( LDAP_NO_MEMORY );
716 	}
717 
718 	s = sortattrs;
719 	for (i = 0; i < sortsize - 1 ; i++) {
720 	    memcpy(s, sortattr[i], strlen(sortattr[i]));
721 	    s += strlen(sortattr[i]);
722 	    *s++ = ' ';
723 	}
724 
725 	sortattrs[string_length] = '\0';
726 
727 	ldap_create_sort_keylist(&keylist,sortattrs);
728 	free(sortattrs);
729 	sortattrs = NULL;
730 
731 	/* Then make a control for the sort attributes we have */
732 	rc = ldap_create_sort_control(ld,keylist,0,&ldctrl);
733 	ldap_free_sort_keylist(keylist);
734 	if ( rc != LDAP_SUCCESS ) {
735 	    if (mallocd_filter) free(filterp);
736 	    return( ldaptool_print_lderror( ld, "ldap_create_sort_control",
737 		LDAPTOOL_CHECK4SSL_IF_APPROP ));
738 	}
739 
740 	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
741 
742     }
743     /* remember server side sorting must be available for vlv!!!! */
744 
745     if (use_vlv)
746     {
747 	vlv_data.ldvlist_before_count = vlv_before;
748 	vlv_data.ldvlist_after_count = vlv_after;
749 	if ( ldaptool_verbose ) {
750 	    printf( gettext("vlv data %lu, %lu, "),
751 		    vlv_data.ldvlist_before_count,
752 		    vlv_data.ldvlist_after_count
753 		);
754 	}
755 	if (vlv_value)
756 	{
757 	    vlv_data.ldvlist_attrvalue = vlv_value;
758 	    vlv_data.ldvlist_size = 0;
759 	    vlv_data.ldvlist_index = 0;
760 	    if ( ldaptool_verbose ) {
761 		printf( "%s, 0, 0\n", vlv_data.ldvlist_attrvalue);
762 	    }
763 	}
764 	else
765 	{
766 	    vlv_data.ldvlist_attrvalue = NULL;
767 	    vlv_data.ldvlist_size = vlv_count;
768 	    vlv_data.ldvlist_index = vlv_index;
769 	    if ( ldaptool_verbose ) {
770 		printf( "(null), %lu, %lu\n", vlv_data.ldvlist_size, vlv_data.ldvlist_index );
771 	    }
772 	}
773 
774 	if ( rc != LDAP_SUCCESS ) {
775 	    if (mallocd_filter) free(filterp);
776 	    return( ldaptool_print_lderror( ld, "ldap_create_sort_control",
777 		LDAPTOOL_CHECK4SSL_IF_APPROP ));
778 	}
779 	if (LDAP_SUCCESS != (rc = ldap_create_virtuallist_control(ld,
780 		&vlv_data, &ldctrl)))
781 	{
782 	    if (mallocd_filter) free(filterp);
783 	    return( ldaptool_print_lderror( ld,
784 		"ldap_create_virtuallist_control",
785 		LDAPTOOL_CHECK4SSL_IF_APPROP ));
786 	}
787 
788 	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
789 
790     }
791 
792     if ( ldap_search_ext( ld, base, scope, filterp, attrs, attrsonly,
793 	    ldaptool_request_ctrls, NULL, NULL, -1, &msgid )
794 	    != LDAP_SUCCESS ) {
795 	if (mallocd_filter) free(filterp);
796 	return( ldaptool_print_lderror( ld, "ldap_search",
797 		LDAPTOOL_CHECK4SSL_IF_APPROP ));
798     }
799 
800 
801     matches = 0;
802     first = 1;
803     if ( sortattr && !server_sort ) {
804         rc = ldap_result( ld, LDAP_RES_ANY, 1, NULL, &res );
805     } else {
806         while ( (rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res )) !=
807 		LDAP_RES_SEARCH_RESULT && rc != -1 ) {
808 	    if ( rc != LDAP_RES_SEARCH_ENTRY ) {
809 		if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
810 		    parse_and_display_reference( ld, res );
811 		} else if ( rc == LDAP_RES_EXTENDED
812 			&& ldap_msgid( res ) == LDAP_RES_UNSOLICITED ) {
813 		    ldaptool_print_extended_response( ld, res,
814 			    gettext("Unsolicited response") );
815 		} else {
816 		    fprintf( stderr, gettext("%s: ignoring LDAP response message"
817 			    " type 0x%x (%s)\n"),
818 			    ldaptool_progname, rc, msgtype2str( rc ));
819 		}
820 		ldap_msgfree( res );
821 		continue;
822 	    }
823 	    matches++;
824 	    e = ldap_first_entry( ld, res );
825 	    if ( !first ) {
826 	        putchar( '\n' );
827 	    } else {
828 	        first = 0;
829 	    }
830 	    print_entry( ld, e, attrsonly );
831 	    ldap_msgfree( res );
832         }
833     }
834     if ( rc == -1 ) {
835 	if (mallocd_filter) free(filterp);
836 	return( ldaptool_print_lderror( ld, "ldap_result",
837 		LDAPTOOL_CHECK4SSL_IF_APPROP ));
838     }
839 
840     if ( ldap_parse_result( ld, res, &rc, NULL, NULL, &refs,
841 	    &ctrl_response_array, 0 ) != LDAP_SUCCESS ) {
842 	ldaptool_print_lderror( ld, "ldap_parse_result",
843 		LDAPTOOL_CHECK4SSL_IF_APPROP );
844     } else if ( rc != LDAP_SUCCESS ) {
845 	ldaptool_print_lderror( ld, "ldap_search",
846 		LDAPTOOL_CHECK4SSL_IF_APPROP );
847     }
848     /* Parse the returned sort control */
849     if (server_sort) {
850 	unsigned long result = 0;
851 	char *attribute;
852 
853 	if ( LDAP_SUCCESS != ldap_parse_sort_control(ld,ctrl_response_array,&result,&attribute) ) {
854 	    ldaptool_print_lderror(ld, "ldap_parse_sort_control",
855 		    LDAPTOOL_CHECK4SSL_IF_APPROP );
856 	    ldap_controls_free(ctrl_response_array);
857 	    ldap_msgfree(res);
858 	    if (mallocd_filter) free(filterp);
859 	    return ( ldap_get_lderrno( ld, NULL, NULL ) );
860 	}
861 
862 	if (0 == result) {
863 	    if ( ldaptool_verbose ) {
864 		printf( gettext("Server indicated results sorted OK\n"));
865 	    }
866 	} else {
867 	    if (NULL != attribute) {
868 		printf(gettext("Server reported sorting error %ld: %s, attribute in error\"%s\"\n"),result,sortresult2string(result),attribute);
869 	    } else {
870 		printf(gettext("Server reported sorting error %ld: %s\n"),result,sortresult2string(result));
871 	    }
872 	}
873 
874     }
875 
876     if (use_vlv)
877     {
878 	unsigned long vpos, vcount;
879 	int vresult;
880 	if ( LDAP_SUCCESS != ldap_parse_virtuallist_control(ld,ctrl_response_array,&vpos, &vcount,&vresult) ) {
881 	    ldaptool_print_lderror( ld, "ldap_parse_virtuallist_control",
882 		    LDAPTOOL_CHECK4SSL_IF_APPROP );
883 	    ldap_controls_free(ctrl_response_array);
884 	    ldap_msgfree(res);
885 	    if (mallocd_filter) free(filterp);
886 	    return ( ldap_get_lderrno( ld, NULL, NULL ) );
887 	}
888 
889 	if (0 == vresult) {
890 	    if ( ldaptool_verbose ) {
891 		printf( gettext("Server indicated virtual list positioning OK\n"));
892 	    }
893 	    printf(gettext("index %lu content count %lu\n"), vpos, vcount);
894 
895 	} else {
896 	    printf(gettext("Server reported sorting error %d: %s\n"),vresult,sortresult2string(vresult));
897 
898 	}
899 
900     }
901 
902     ldap_controls_free(ctrl_response_array);
903 
904     if ( sortattr != NULL && !server_sort) {
905 
906 	(void) ldap_multisort_entries( ld, &res,
907 				       ( *sortattr == NULL ) ? NULL : sortattr,
908 				       (LDAP_CMP_CALLBACK *)strcasecmp );
909 	matches = 0;
910 	first = 1;
911 	for ( e = ldap_first_entry( ld, res ); e != NULLMSG;
912 	      e = ldap_next_entry( ld, e ) ) {
913 	    matches++;
914 	    if ( !first ) {
915 		putchar( '\n' );
916 	    } else {
917 		first = 0;
918 	    }
919 	    print_entry( ld, e, attrsonly );
920 	}
921     }
922 
923     if ( ldaptool_verbose ) {
924 	printf( gettext("%d matches\n"), matches );
925     }
926 
927     if ( refs != NULL ) {
928 	ldaptool_print_referrals( refs );
929 	ldap_value_free( refs );
930     }
931 
932     if (mallocd_filter) free(filterp);
933 
934     ldap_msgfree( res );
935     return( rc );
936 }
937 
938 
939 static void
940 print_entry( ld, entry, attrsonly )
941     LDAP	*ld;
942     LDAPMessage	*entry;
943     int		attrsonly;
944 {
945     char		*a, *dn, *ufn, tmpfname[ BUFSIZ ];
946     int			i, notascii;
947     BerElement		*ber;
948     struct berval	**bvals;
949     FILE		*tmpfp;
950 #if defined( XP_WIN32 )
951 	char	mode[20] = "w+b";
952 #else
953 	char	mode[20] = "w";
954 #endif
955 
956     dn = ldap_get_dn( ld, entry );
957     write_string_attr_value( "dn", dn, LDAPTOOL_WRITEVALOPT_SUPPRESS_NAME );
958     if ( includeufn ) {
959 	ufn = ldap_dn2ufn( dn );
960 	write_string_attr_value( "ufn", ufn,
961 			LDAPTOOL_WRITEVALOPT_SUPPRESS_NAME );
962 	free( ufn );
963     }
964     ldap_memfree( dn );
965 
966     if ( use_psearch ) {
967 	LDAPControl	**ectrls;
968 	int		chgtype, chgnumpresent;
969 #ifdef SOLARIS_LDAP_CMD
970 	ber_int_t	chgnum;
971 #else
972 	long		chgnum;
973 #endif	/* SOLARIS_LDAP_CMD */
974 	char		*prevdn, longbuf[ 128 ];
975 
976 	if ( ldap_get_entry_controls( ld, entry, &ectrls ) == LDAP_SUCCESS ) {
977 	    if ( ldap_parse_entrychange_control( ld, ectrls, &chgtype,
978 			&prevdn, &chgnumpresent, &chgnum ) == LDAP_SUCCESS ) {
979 		write_string_attr_value(
980 			LDAPTOOL_PSEARCH_ATTR_PREFIX "changeType",
981 			changetype_num2string( chgtype ), 0 );
982 		if ( chgnumpresent ) {
983 		    sprintf( longbuf, "%d", chgnum );
984 		    write_string_attr_value(
985 			    LDAPTOOL_PSEARCH_ATTR_PREFIX "changeNumber",
986 			    longbuf, 0 );
987 		}
988 		if ( NULL != prevdn ) {
989 		    write_string_attr_value(
990 			    LDAPTOOL_PSEARCH_ATTR_PREFIX "previousDN",
991 			    prevdn, 0 );
992 		    ldap_memfree( prevdn );
993 		}
994 	    }
995 	    ldap_controls_free (ectrls);
996 	}
997     }
998 
999     for ( a = ldap_first_attribute( ld, entry, &ber ); a != NULL;
1000 	    a = ldap_next_attribute( ld, entry, ber ) ) {
1001 	if ( ldap_charray_inlist(sortattr, a) &&            /* in the list*/
1002 	    skipsortattr[ldap_charray_position(sortattr, a)] ) {/* and skip it*/
1003 	    continue;                                       /* so skip it! */
1004 	}
1005 	if ( attrsonly ) {
1006 	    if ( ldif ) {
1007 		write_ldif_value( a, "", 0, 0 );
1008 	    } else {
1009 		printf( "%s\n", a );
1010 	    }
1011 	} else if (( bvals = ldap_get_values_len( ld, entry, a )) != NULL ) {
1012 	    for ( i = 0; bvals[i] != NULL; i++ ) {
1013 		if ( vals2tmp ) {
1014 #ifdef HAVE_SNPRINTF
1015 		    if ( snprintf( tmpfname, sizeof(tmpfname),
1016 			    "%s/ldapsearch-%s-XXXXXX",
1017 			    ldaptool_get_tmp_dir(), a ) < 0 ) {
1018 			perror( gettext("snprintf tmpfname (attribute name too long?)") );
1019 			exit( LDAP_PARAM_ERROR );
1020 		    }
1021 #else
1022 		    sprintf( tmpfname, "%s/ldapsearch-%s-XXXXXX",
1023 			    ldaptool_get_tmp_dir(), a );
1024 #endif
1025 		    tmpfp = NULL;
1026 
1027 		    if ( LDAPTOOL_MKTEMP( tmpfname ) == NULL ) {
1028 			perror( tmpfname );
1029 		    } else if (( tmpfp = ldaptool_open_file( tmpfname, mode)) == NULL ) {
1030 			perror( tmpfname );
1031 		    } else if ( bvals[ i ]->bv_len > 0 &&
1032 			    fwrite( bvals[ i ]->bv_val,
1033 			    bvals[ i ]->bv_len, 1, tmpfp ) == 0 ) {
1034 			perror( tmpfname );
1035 		    } else if ( ldif ) {
1036 			if ( produce_file_urls ) {
1037 			    char	*url;
1038 
1039 			    if ( ldaptool_path2fileurl( tmpfname, &url ) !=
1040 				LDAPTOOL_FILEURL_SUCCESS ) {
1041 				    perror( "ldaptool_path2fileurl" );
1042 				} else {
1043 				    write_ldif_value( a, url, strlen( url ),
1044 					    LDIF_OPT_VALUE_IS_URL );
1045 				    free( url );
1046 				}
1047 			} else {
1048 			    write_ldif_value( a, tmpfname, strlen( tmpfname ),
1049 				    0 );
1050 			}
1051 		    } else {
1052 			printf( "%s%s%s\n", a, sep, tmpfname );
1053 		    }
1054 
1055 		    if ( tmpfp != NULL ) {
1056 			fclose( tmpfp );
1057 		    }
1058 		} else {
1059 		    notascii = 0;
1060 		    if ( !ldif && !allow_binary ) {
1061 			notascii = !ldaptool_berval_is_ascii( bvals[i] );
1062 		    }
1063 
1064 		    if ( ldif ) {
1065 			write_ldif_value( a, bvals[ i ]->bv_val,
1066 				bvals[ i ]->bv_len, 0 );
1067 		    } else {
1068 			printf( "%s%s%s\n", a, sep,
1069 				notascii ? gettext("NOT ASCII") : bvals[ i ]->bv_val );
1070 		    }
1071 		}
1072 	    }
1073 	    ber_bvecfree( bvals );
1074 	}
1075 	ldap_memfree( a );
1076     }
1077 
1078     if ( ldap_get_lderrno( ld, NULL, NULL ) != LDAP_SUCCESS ) {
1079 	ldaptool_print_lderror( ld, "ldap_first_attribute/ldap_next_attribute",
1080 		LDAPTOOL_CHECK4SSL_IF_APPROP );
1081     }
1082 
1083     if ( ber != NULL ) {
1084 	ber_free( ber, 0 );
1085     }
1086 }
1087 
1088 
1089 static void
1090 write_string_attr_value( char *attrname, char *strval, unsigned long opts )
1091 {
1092     if ( strval == NULL ) {
1093 	strval = "";
1094     }
1095     if ( ldif ) {
1096 	write_ldif_value( attrname, strval, strlen( strval ), 0 );
1097     } else if ( 0 != ( opts & LDAPTOOL_WRITEVALOPT_SUPPRESS_NAME )) {
1098 	printf( "%s\n", strval );
1099     } else {
1100 	printf( "%s%s%s\n", attrname, sep, strval );
1101     }
1102 }
1103 
1104 
1105 static int
1106 write_ldif_value( char *type, char *value, unsigned long vallen,
1107 	unsigned long ldifoptions )
1108 {
1109     char	*ldif;
1110     static int	wrote_version = 0;
1111 
1112     if ( write_ldif_version && !wrote_version ) {
1113 	char	versionbuf[ 64 ];
1114 
1115 	wrote_version = 1;
1116 	sprintf( versionbuf, "%d", LDIF_VERSION_ONE );
1117 	write_ldif_value( "version", versionbuf, strlen( versionbuf ), 0 );
1118     }
1119 
1120     if ( !fold ) {
1121 	ldifoptions |= LDIF_OPT_NOWRAP;
1122     }
1123     if ( minimize_base64 ) {
1124 	ldifoptions |= LDIF_OPT_MINIMAL_ENCODING;
1125     }
1126 
1127     if (( ldif = ldif_type_and_value_with_options( type, value, (int)vallen,
1128 	    ldifoptions )) == NULL ) {
1129 	return( -1 );
1130     }
1131 
1132     fputs( ldif, stdout );
1133     free( ldif );
1134 
1135     return( 0 );
1136 }
1137 
1138 
1139 static char *
1140 sortresult2string(unsigned long result)
1141 {
1142 	/*
1143             success                   (0), -- results are sorted
1144             operationsError           (1), -- server internal failure
1145             timeLimitExceeded         (3), -- timelimit reached before
1146                                            -- sorting was completed
1147             strongAuthRequired        (8), -- refused to return sorted
1148                                            -- results via insecure
1149                                            -- protocol
1150             adminLimitExceeded       (11), -- too many matching entries
1151                                            -- for the server to sort
1152             noSuchAttribute          (16), -- unrecognized attribute
1153                                            -- type in sort key
1154             inappropriateMatching    (18), -- unrecognized or inappro-
1155                                            -- priate matching rule in
1156                                            -- sort key
1157             insufficientAccessRights (50), -- refused to return sorted
1158                                            -- results to this client
1159             busy                     (51), -- too busy to process
1160             unwillingToPerform       (53), -- unable to sort
1161             other                    (80)
1162 	*/
1163 
1164 	switch (result) {
1165 	case 0: return (gettext("success"));
1166 	case 1: return (gettext("operations error"));
1167 	case 3: return (gettext("time limit exceeded"));
1168 	case 8: return (gettext("strong auth required"));
1169 	case 11: return (gettext("admin limit exceeded"));
1170 	case 16: return (gettext("no such attribute"));
1171 	case 18: return (gettext("unrecognized or inappropriate matching rule"));
1172 	case 50: return (gettext("insufficient access rights"));
1173 	case 51: return (gettext("too busy"));
1174 	case 53: return (gettext("unable to sort"));
1175 	case 80:
1176         default: return (gettext("Er...Other ?"));
1177 	}
1178 }
1179 
1180 
1181 static void
1182 parse_and_display_reference( LDAP *ld, LDAPMessage *ref )
1183 {
1184     int		i;
1185     char	**refs;
1186 
1187     if ( ldap_parse_reference( ld, ref, &refs, NULL, 0 ) != LDAP_SUCCESS ) {
1188 	ldaptool_print_lderror( ld, "ldap_parse_reference",
1189 		LDAPTOOL_CHECK4SSL_IF_APPROP );
1190     } else if ( refs != NULL && refs[ 0 ] != NULL ) {
1191 	fputs( gettext("Unfollowed continuation reference(s):\n"), stderr );
1192 	for ( i = 0; refs[ i ] != NULL; ++i ) {
1193 	    fprintf( stderr, "    %s\n", refs[ i ] );
1194 	}
1195 	ldap_value_free( refs );
1196     }
1197 }
1198 
1199 
1200 /*possible operations a client can invoke -- copied from ldaprot.h */
1201 
1202 #ifndef LDAP_REQ_BIND
1203 #define LDAP_REQ_BIND                   0x60L   /* application + constructed */
1204 #define LDAP_REQ_UNBIND                 0x42L   /* application + primitive   */
1205 #define LDAP_REQ_SEARCH                 0x63L   /* application + constructed */
1206 #define LDAP_REQ_MODIFY                 0x66L   /* application + constructed */
1207 #define LDAP_REQ_ADD                    0x68L   /* application + constructed */
1208 #define LDAP_REQ_DELETE                 0x4aL   /* application + primitive   */
1209 #define LDAP_REQ_RENAME                 0x6cL   /* application + constructed */
1210 #define LDAP_REQ_COMPARE                0x6eL   /* application + constructed */
1211 #define LDAP_REQ_ABANDON                0x50L   /* application + primitive   */
1212 #define LDAP_REQ_EXTENDED               0x77L   /* application + constructed */
1213 #endif /* LDAP_REQ_BIND */
1214 
1215 
1216 
1217 struct ldapsearch_type2str {
1218 
1219     int         ldst2s_type;    /* message type */
1220     char        *ldst2s_string; /* descriptive string */
1221 };
1222 
1223 #ifdef SOLARIS_LDAP_CMD
1224 static struct ldapsearch_type2str ldapsearch_msgtypes[] = {
1225 
1226     /* results: */
1227     { LDAP_RES_BIND,                    NULL },
1228     { LDAP_RES_SEARCH_REFERENCE,        NULL },
1229     { LDAP_RES_SEARCH_ENTRY,            NULL },
1230     { LDAP_RES_SEARCH_RESULT,           NULL },
1231     { LDAP_RES_MODIFY,                  NULL },
1232     { LDAP_RES_ADD,                     NULL },
1233     { LDAP_RES_DELETE,                  NULL },
1234     { LDAP_RES_MODDN,                   NULL },
1235     { LDAP_RES_COMPARE,                 NULL },
1236     { LDAP_RES_EXTENDED,                NULL },
1237     /* requests: */
1238     { LDAP_REQ_BIND,                    NULL },
1239     { LDAP_REQ_UNBIND,                  NULL },
1240     { LDAP_REQ_SEARCH,                  NULL },
1241     { LDAP_REQ_MODIFY,                  NULL },
1242     { LDAP_REQ_ADD,                     NULL },
1243     { LDAP_REQ_DELETE,                  NULL },
1244     { LDAP_REQ_RENAME,                  NULL },
1245     { LDAP_REQ_COMPARE,                 NULL },
1246     { LDAP_REQ_ABANDON,                 NULL },
1247     { LDAP_REQ_EXTENDED,                NULL },
1248 
1249 };
1250 #else
1251 static struct ldapsearch_type2str ldapsearch_msgtypes[] = {
1252 
1253     /* results: */
1254     { LDAP_RES_BIND,                    "bind result" },
1255     { LDAP_RES_SEARCH_REFERENCE,        "continuation reference" },
1256     { LDAP_RES_SEARCH_ENTRY,            "entry" },
1257     { LDAP_RES_SEARCH_RESULT,           "search result" },
1258     { LDAP_RES_MODIFY,                  "modify result" },
1259     { LDAP_RES_ADD,                     "add result" },
1260     { LDAP_RES_DELETE,                  "delete result" },
1261     { LDAP_RES_MODDN,                   "rename result" },
1262     { LDAP_RES_COMPARE,                 "compare result" },
1263     { LDAP_RES_EXTENDED,                "extended operation result" },
1264     /* requests: */
1265     { LDAP_REQ_BIND,                    "bind request" },
1266     { LDAP_REQ_UNBIND,                  "unbind request" },
1267     { LDAP_REQ_SEARCH,                  "search request" },
1268     { LDAP_REQ_MODIFY,                  "modify request" },
1269     { LDAP_REQ_ADD,                     "add request" },
1270     { LDAP_REQ_DELETE,                  "delete request" },
1271     { LDAP_REQ_RENAME,                  "rename request" },
1272     { LDAP_REQ_COMPARE,                 "compare request" },
1273     { LDAP_REQ_ABANDON,                 "abandon request" },
1274     { LDAP_REQ_EXTENDED,                "extended request" },
1275 
1276 };
1277 #endif	/* SOLARIS_LDAP_CMD */
1278 
1279 
1280 #define LDAPSEARCHTOOL_NUMTYPES (sizeof(ldapsearch_msgtypes) \
1281 				 / sizeof(struct ldapsearch_type2str))
1282 
1283 #ifdef SOLARIS_LDAP_CMD
1284 static void
1285 fill_ldapsearch_msgtypes( void )
1286 {
1287     int		i = 0;
1288     if (ldapsearch_msgtypes[LDAPSEARCHTOOL_NUMTYPES - 1].ldst2s_string
1289 	!= NULL)
1290 		return;
1291 
1292     /* results: */
1293     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1294 			"bind result");
1295     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1296 			"continuation reference");
1297     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1298 			"entry");
1299     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1300     			"search result");
1301     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1302     			"modify result");
1303     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1304     			"add result");
1305     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1306     			"delete result");
1307     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1308     			"rename result");
1309     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1310     			"compare result");
1311     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1312     			"extended operation result");
1313     /* requests: */
1314     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1315     			"bind request");
1316     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1317     			"unbind request");
1318     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1319     			"search request");
1320     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1321     			"modify request");
1322     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1323     			"add request");
1324     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1325     			"delete request");
1326     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1327     			"rename request");
1328     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1329     			"compare request");
1330     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1331     			"abandon request");
1332     ldapsearch_msgtypes[i++].ldst2s_string = gettext(
1333 			"extended request");
1334 }
1335 #endif	/* SOLARIS_LDAP_CMD */
1336 
1337 /*
1338  * Return a descriptive string given an LDAP result message type (tag).
1339  */
1340 static char *
1341 msgtype2str( int msgtype )
1342 {
1343     char        *s = gettext("unknown");
1344     int         i;
1345 
1346 #ifdef SOLARIS_LDAP_CMD
1347     /* Make sure ldapsearch_msgtypes is initialized */
1348     if (ldapsearch_msgtypes[LDAPSEARCHTOOL_NUMTYPES - 1].ldst2s_string
1349 	== NULL)
1350 		(void) fill_ldapsearch_msgtypes();
1351 #endif	/* SOLARIS_LDAP_CMD */
1352 
1353     for ( i = 0; i < LDAPSEARCHTOOL_NUMTYPES; ++i ) {
1354 	if ( msgtype == ldapsearch_msgtypes[ i ].ldst2s_type ) {
1355 	    s = ldapsearch_msgtypes[ i ].ldst2s_string;
1356 	}
1357     }
1358     return( s );
1359 }
1360 
1361 
1362 /*
1363  * Return a descriptive string given a Persistent Search change type
1364  */
1365 static char *
1366 changetype_num2string( int chgtype )
1367 {
1368     char	*s = gettext("unknown");
1369 
1370     switch( chgtype ) {
1371     case LDAP_CHANGETYPE_ADD:
1372 	s = gettext("add");
1373 	break;
1374     case LDAP_CHANGETYPE_DELETE:
1375 	s = gettext("delete");
1376 	break;
1377     case LDAP_CHANGETYPE_MODIFY:
1378 	s = gettext("modify");
1379 	break;
1380     case LDAP_CHANGETYPE_MODDN:
1381 	s = gettext("moddn");
1382 	break;
1383     }
1384 
1385     return( s );
1386 }
1387 
1388 /* returns a null teminated charrary */
1389 static char  **get_effectiverights_attrlist(char * optarg) {
1390 
1391 	char * tmp_str = strdup(optarg);
1392 	char ** retArray = NULL;
1393 	int i = 0;
1394 
1395 	retArray = ldap_str2charray( tmp_str, " "); /* takes copies */
1396 
1397 	free(tmp_str);
1398 
1399 	/* Oops - somebody left this debug message in for the
1400 	   getEffectiveRights control
1401 	   fprintf(stderr, "attrlist: "); */
1402 	i = 0;
1403 	while( retArray[i] != NULL ) {
1404 
1405 		fprintf(stderr,"%s ", retArray[i]);
1406 		i++;
1407 	}
1408 	fprintf(stderr, "\n");
1409 
1410 	return(retArray);
1411 
1412 }
1413