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