Пример #1
0
int
ldap_set_option(
	LDAP	*ld,
	int		option,
	LDAP_CONST void	*invalue)
{
	struct ldapoptions *lo;
	int *dbglvl = NULL;
	int rc = LDAP_OPT_ERROR;

	/* Get pointer to global option structure */
	lo = LDAP_INT_GLOBAL_OPT();
	if (lo == NULL)	{
		return LDAP_NO_MEMORY;
	}

	/*
	 * The architecture to turn on debugging has a chicken and egg
	 * problem. Thus, we introduce a fix here.
	 */

	if (option == LDAP_OPT_DEBUG_LEVEL) {
		dbglvl = (int *) invalue;
	}

	if( lo->ldo_valid != LDAP_INITIALIZED ) {
		ldap_int_initialize(lo, dbglvl);
		if ( lo->ldo_valid != LDAP_INITIALIZED )
			return LDAP_LOCAL_ERROR;
	}

	if(ld != NULL) {
		assert( LDAP_VALID( ld ) );

		if( !LDAP_VALID( ld ) ) {
			return LDAP_OPT_ERROR;
		}

		lo = &ld->ld_options;
	}

	LDAP_MUTEX_LOCK( &lo->ldo_mutex );

	switch ( option ) {

	/* options with boolean values */
	case LDAP_OPT_REFERRALS:
		if(invalue == LDAP_OPT_OFF) {
			LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS);
		} else {
			LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS);
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_RESTART:
		if(invalue == LDAP_OPT_OFF) {
			LDAP_BOOL_CLR(lo, LDAP_BOOL_RESTART);
		} else {
			LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART);
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_CONNECT_ASYNC:
		if(invalue == LDAP_OPT_OFF) {
			LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC);
		} else {
			LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC);
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	/* options which can withstand invalue == NULL */
	case LDAP_OPT_SERVER_CONTROLS: {
			LDAPControl *const *controls =
				(LDAPControl *const *) invalue;

			if( lo->ldo_sctrls )
				ldap_controls_free( lo->ldo_sctrls );

			if( controls == NULL || *controls == NULL ) {
				lo->ldo_sctrls = NULL;
				rc = LDAP_OPT_SUCCESS;
				break;
			}
				
			lo->ldo_sctrls = ldap_controls_dup( controls );

			if(lo->ldo_sctrls == NULL) {
				/* memory allocation error ? */
				break;	/* LDAP_OPT_ERROR */
			}
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_CLIENT_CONTROLS: {
			LDAPControl *const *controls =
				(LDAPControl *const *) invalue;

			if( lo->ldo_cctrls )
				ldap_controls_free( lo->ldo_cctrls );

			if( controls == NULL || *controls == NULL ) {
				lo->ldo_cctrls = NULL;
				rc = LDAP_OPT_SUCCESS;
				break;
			}
				
			lo->ldo_cctrls = ldap_controls_dup( controls );

			if(lo->ldo_cctrls == NULL) {
				/* memory allocation error ? */
				break;	/* LDAP_OPT_ERROR */
			}
		}
		rc = LDAP_OPT_SUCCESS;
		break;


	case LDAP_OPT_HOST_NAME: {
			const char *host = (const char *) invalue;
			LDAPURLDesc *ludlist = NULL;
			rc = LDAP_OPT_SUCCESS;

			if(host != NULL) {
				rc = ldap_url_parsehosts( &ludlist, host,
					lo->ldo_defport ? lo->ldo_defport : LDAP_PORT );

			} else if(ld == NULL) {
				/*
				 * must want global default returned
				 * to initial condition.
				 */
				rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
					LDAP_PVT_URL_PARSE_NOEMPTY_HOST
					| LDAP_PVT_URL_PARSE_DEF_PORT );

			} else {
				/*
				 * must want the session default
				 *   updated to the current global default
				 */
				ludlist = ldap_url_duplist(
					ldap_int_global_options.ldo_defludp);
				if (ludlist == NULL)
					rc = LDAP_NO_MEMORY;
			}

			if (rc == LDAP_OPT_SUCCESS) {
				if (lo->ldo_defludp != NULL)
					ldap_free_urllist(lo->ldo_defludp);
				lo->ldo_defludp = ludlist;
			}
			break;
		}

	case LDAP_OPT_URI: {
			const char *urls = (const char *) invalue;
			LDAPURLDesc *ludlist = NULL;
			rc = LDAP_OPT_SUCCESS;

			if(urls != NULL) {
				rc = ldap_url_parselist_ext(&ludlist, urls, NULL,
					LDAP_PVT_URL_PARSE_NOEMPTY_HOST
					| LDAP_PVT_URL_PARSE_DEF_PORT );
			} else if(ld == NULL) {
				/*
				 * must want global default returned
				 * to initial condition.
				 */
				rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
					LDAP_PVT_URL_PARSE_NOEMPTY_HOST
					| LDAP_PVT_URL_PARSE_DEF_PORT );

			} else {
				/*
				 * must want the session default
				 *   updated to the current global default
				 */
				ludlist = ldap_url_duplist(
					ldap_int_global_options.ldo_defludp);
				if (ludlist == NULL)
					rc = LDAP_URL_ERR_MEM;
			}

			switch (rc) {
			case LDAP_URL_SUCCESS:		/* Success */
				rc = LDAP_SUCCESS;
				break;

			case LDAP_URL_ERR_MEM:		/* can't allocate memory space */
				rc = LDAP_NO_MEMORY;
				break;

			case LDAP_URL_ERR_PARAM:	/* parameter is bad */
			case LDAP_URL_ERR_BADSCHEME:	/* URL doesn't begin with "ldap[si]://" */
			case LDAP_URL_ERR_BADENCLOSURE:	/* URL is missing trailing ">" */
			case LDAP_URL_ERR_BADURL:	/* URL is bad */
			case LDAP_URL_ERR_BADHOST:	/* host port is bad */
			case LDAP_URL_ERR_BADATTRS:	/* bad (or missing) attributes */
			case LDAP_URL_ERR_BADSCOPE:	/* scope string is invalid (or missing) */
			case LDAP_URL_ERR_BADFILTER:	/* bad or missing filter */
			case LDAP_URL_ERR_BADEXTS:	/* bad or missing extensions */
				rc = LDAP_PARAM_ERROR;
				break;
			}

			if (rc == LDAP_SUCCESS) {
				if (lo->ldo_defludp != NULL)
					ldap_free_urllist(lo->ldo_defludp);
				lo->ldo_defludp = ludlist;
			}
			break;
		}

	case LDAP_OPT_DEFBASE: {
			const char *newbase = (const char *) invalue;
			char *defbase = NULL;

			if ( newbase != NULL ) {
				defbase = LDAP_STRDUP( newbase );
				if ( defbase == NULL ) {
					rc = LDAP_NO_MEMORY;
					break;
				}

			} else if ( ld != NULL ) {
				defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase );
				if ( defbase == NULL ) {
					rc = LDAP_NO_MEMORY;
					break;
				}
			}
			
			if ( lo->ldo_defbase != NULL )
				LDAP_FREE( lo->ldo_defbase );
			lo->ldo_defbase = defbase;
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_DIAGNOSTIC_MESSAGE: {
			const char *err = (const char *) invalue;

			if(ld == NULL) {
				/* need a struct ldap */
				break;	/* LDAP_OPT_ERROR */
			}

			if( ld->ld_error ) {
				LDAP_FREE(ld->ld_error);
				ld->ld_error = NULL;
			}

			if ( err ) {
				ld->ld_error = LDAP_STRDUP(err);
			}
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_MATCHED_DN: {
			const char *matched = (const char *) invalue;

			if (ld == NULL) {
				/* need a struct ldap */
				break;	/* LDAP_OPT_ERROR */
			}

			if( ld->ld_matched ) {
				LDAP_FREE(ld->ld_matched);
				ld->ld_matched = NULL;
			}

			if ( matched ) {
				ld->ld_matched = LDAP_STRDUP( matched );
			}
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_REFERRAL_URLS: {
			char *const *referrals = (char *const *) invalue;
			
			if(ld == NULL) {
				/* need a struct ldap */
				break;	/* LDAP_OPT_ERROR */
			}

			if( ld->ld_referrals ) {
				LDAP_VFREE(ld->ld_referrals);
			}

			if ( referrals ) {
				ld->ld_referrals = ldap_value_dup(referrals);
			}
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	/* Only accessed from inside this function by ldap_set_rebind_proc() */
	case LDAP_OPT_REBIND_PROC: {
			lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_REBIND_PARAMS: {
			lo->ldo_rebind_params = (void *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	/* Only accessed from inside this function by ldap_set_nextref_proc() */
	case LDAP_OPT_NEXTREF_PROC: {
			lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_NEXTREF_PARAMS: {
			lo->ldo_nextref_params = (void *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	/* Only accessed from inside this function by ldap_set_urllist_proc() */
	case LDAP_OPT_URLLIST_PROC: {
			lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_URLLIST_PARAMS: {
			lo->ldo_urllist_params = (void *)invalue;		
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	/* read-only options */
	case LDAP_OPT_API_INFO:
	case LDAP_OPT_DESC:
	case LDAP_OPT_SOCKBUF:
	case LDAP_OPT_API_FEATURE_INFO:
		break;	/* LDAP_OPT_ERROR */

	/* options which cannot withstand invalue == NULL */
	case LDAP_OPT_DEREF:
	case LDAP_OPT_SIZELIMIT:
	case LDAP_OPT_TIMELIMIT:
	case LDAP_OPT_PROTOCOL_VERSION:
	case LDAP_OPT_RESULT_CODE:
	case LDAP_OPT_DEBUG_LEVEL:
	case LDAP_OPT_TIMEOUT:
	case LDAP_OPT_NETWORK_TIMEOUT:
	case LDAP_OPT_CONNECT_CB:
	case LDAP_OPT_X_KEEPALIVE_IDLE:
	case LDAP_OPT_X_KEEPALIVE_PROBES :
	case LDAP_OPT_X_KEEPALIVE_INTERVAL :
		if(invalue == NULL) {
			/* no place to set from */
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
			return ( LDAP_OPT_ERROR );
		}
		break;

	default:
#ifdef HAVE_TLS
		if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) {
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
			return ( LDAP_OPT_SUCCESS );
		}
#endif
#ifdef HAVE_CYRUS_SASL
		if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) {
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
			return ( LDAP_OPT_SUCCESS );
		}
#endif
#ifdef HAVE_GSSAPI
		if ( ldap_int_gssapi_set_option( ld, option, (void *)invalue ) == 0 ) {
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
			return ( LDAP_OPT_SUCCESS );
		}
#endif
		/* bad param */
		break;	/* LDAP_OPT_ERROR */
	}

	/* options which cannot withstand invalue == NULL */

	switch(option) {
	case LDAP_OPT_DEREF:
		/* FIXME: check value for protocol compliance? */
		lo->ldo_deref = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_SIZELIMIT:
		/* FIXME: check value for protocol compliance? */
		lo->ldo_sizelimit = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_TIMELIMIT:
		/* FIXME: check value for protocol compliance? */
		lo->ldo_timelimit = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_TIMEOUT: {
			const struct timeval *tv = 
				(const struct timeval *) invalue;

			lo->ldo_tm_api = *tv;
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_NETWORK_TIMEOUT: {
			const struct timeval *tv = 
				(const struct timeval *) invalue;

			lo->ldo_tm_net = *tv;
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_PROTOCOL_VERSION: {
			int vers = * (const int *) invalue;
			if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) {
				/* not supported */
				break;
			}
			lo->ldo_version = vers;
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_RESULT_CODE: {
			int err = * (const int *) invalue;

			if(ld == NULL) {
				/* need a struct ldap */
				break;
			}

			ld->ld_errno = err;
		}
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_DEBUG_LEVEL:
		lo->ldo_debug = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;

	case LDAP_OPT_CONNECT_CB:
		{
			/* setting pushes the callback */
			ldaplist *ll;
			ll = LDAP_MALLOC( sizeof( *ll ));
			ll->ll_data = (void *)invalue;
			ll->ll_next = lo->ldo_conn_cbs;
			lo->ldo_conn_cbs = ll;
		}
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_X_KEEPALIVE_IDLE:
		lo->ldo_keepalive_idle = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_X_KEEPALIVE_PROBES :
		lo->ldo_keepalive_probes = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;
	case LDAP_OPT_X_KEEPALIVE_INTERVAL :
		lo->ldo_keepalive_interval = * (const int *) invalue;
		rc = LDAP_OPT_SUCCESS;
		break;
	
	}
	LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
	return ( rc );
}
Пример #2
0
int
main( int argc, char **argv )
{
	LDAP		*ld = NULL;
	int		i, c, port, errflg, method, id, msgtype;
	char		line[256], command1, command2, command3;
	char		passwd[64], dn[256], rdn[64], attr[64], value[256];
	char		filter[256], *host, **types;
	char		**exdn;
	static const char usage[] =
		"usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file]\n";
	int		bound, all, scope, attrsonly;
	LDAPMessage	*res;
	LDAPMod		**mods, **attrs;
	struct timeval	timeout;
	char		*copyfname = NULL;
	int		copyoptions = 0;
	LDAPURLDesc	*ludp;

	host = NULL;
	port = LDAP_PORT;
	dnsuffix = "";
	errflg = 0;

	while (( c = getopt( argc, argv, "h:d:s:p:t:T:" )) != -1 ) {
		switch( c ) {
		case 'd':
#ifdef LDAP_DEBUG
			ldap_debug = atoi( optarg );
#ifdef LBER_DEBUG
			if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
				ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
			}
#endif
#else
			printf( "Compile with -DLDAP_DEBUG for debugging\n" );
#endif
			break;

		case 'h':
			host = optarg;
			break;

		case 's':
			dnsuffix = optarg;
			break;

		case 'p':
			port = atoi( optarg );
			break;

		case 't':	/* copy ber's to given file */
			copyfname = strdup( optarg );
/*			copyoptions = LBER_TO_FILE; */
			break;

		case 'T':	/* only output ber's to given file */
			copyfname = strdup( optarg );
/*			copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY); */
			break;

		default:
		    ++errflg;
		}
	}

	if ( host == NULL && optind == argc - 1 ) {
		host = argv[ optind ];
		++optind;
	}

	if ( errflg || optind < argc - 1 ) {
		fprintf( stderr, usage, argv[ 0 ] );
		exit( EXIT_FAILURE );
	}
	
	printf( "ldap_init( %s, %d )\n",
		host == NULL ? "(null)" : host, port );

	ld = ldap_init( host, port );

	if ( ld == NULL ) {
		perror( "ldap_init" );
		exit( EXIT_FAILURE );
	}

	if ( copyfname != NULL ) {
		if ( ( ld->ld_sb->sb_fd = open( copyfname, O_WRONLY|O_CREAT|O_EXCL,
		    0600 ))  == -1 ) {
			perror( copyfname );
			exit ( EXIT_FAILURE );
		}
		ld->ld_sb->sb_options = copyoptions;
	}

	bound = 0;
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;

	(void) memset( line, '\0', sizeof(line) );
	while ( get_line( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) {
		command1 = line[0];
		command2 = line[1];
		command3 = line[2];

		switch ( command1 ) {
		case 'a':	/* add or abandon */
			switch ( command2 ) {
			case 'd':	/* add */
				get_line( dn, sizeof(dn), stdin, "dn? " );
				strcat( dn, dnsuffix );
				if ( (attrs = get_modlist( NULL, "attr? ",
				    "value? " )) == NULL )
					break;
				if ( (id = ldap_add( ld, dn, attrs )) == -1 )
					ldap_perror( ld, "ldap_add" );
				else
					printf( "Add initiated with id %d\n",
					    id );
				break;

			case 'b':	/* abandon */
				get_line( line, sizeof(line), stdin, "msgid? " );
				id = atoi( line );
				if ( ldap_abandon( ld, id ) != 0 )
					ldap_perror( ld, "ldap_abandon" );
				else
					printf( "Abandon successful\n" );
				break;
			default:
				printf( "Possibilities: [ad]d, [ab]ort\n" );
			}
			break;

		case 'b':	/* asynch bind */
			method = LDAP_AUTH_SIMPLE;
			get_line( dn, sizeof(dn), stdin, "dn? " );
			strcat( dn, dnsuffix );

			if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' )
				get_line( passwd, sizeof(passwd), stdin,
				    "password? " );
			else
				passwd[0] = '\0';

			if ( ldap_bind( ld, dn, passwd, method ) == -1 ) {
				fprintf( stderr, "ldap_bind failed\n" );
				ldap_perror( ld, "ldap_bind" );
			} else {
				printf( "Bind initiated\n" );
				bound = 1;
			}
			break;

		case 'B':	/* synch bind */
			method = LDAP_AUTH_SIMPLE;
			get_line( dn, sizeof(dn), stdin, "dn? " );
			strcat( dn, dnsuffix );

			if ( dn[0] != '\0' )
				get_line( passwd, sizeof(passwd), stdin,
				    "password? " );
			else
				passwd[0] = '\0';

			if ( ldap_bind_s( ld, dn, passwd, method ) !=
			    LDAP_SUCCESS ) {
				fprintf( stderr, "ldap_bind_s failed\n" );
				ldap_perror( ld, "ldap_bind_s" );
			} else {
				printf( "Bind successful\n" );
				bound = 1;
			}
			break;

		case 'c':	/* compare */
			get_line( dn, sizeof(dn), stdin, "dn? " );
			strcat( dn, dnsuffix );
			get_line( attr, sizeof(attr), stdin, "attr? " );
			get_line( value, sizeof(value), stdin, "value? " );

			if ( (id = ldap_compare( ld, dn, attr, value )) == -1 )
				ldap_perror( ld, "ldap_compare" );
			else
				printf( "Compare initiated with id %d\n", id );
			break;

		case 'd':	/* turn on debugging */
#ifdef LDAP_DEBUG
			get_line( line, sizeof(line), stdin, "debug level? " );
			ldap_debug = atoi( line );
#ifdef LBER_DEBUG
			if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
				ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
			}
#endif
#else
			printf( "Compile with -DLDAP_DEBUG for debugging\n" );
#endif
			break;

		case 'E':	/* explode a dn */
			get_line( line, sizeof(line), stdin, "dn? " );
			exdn = ldap_explode_dn( line, 0 );
			for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) {
				printf( "\t%s\n", exdn[i] );
			}
			break;

		case 'g':	/* set next msgid */
			get_line( line, sizeof(line), stdin, "msgid? " );
			ld->ld_msgid = atoi( line );
			break;

		case 'v':	/* set version number */
			get_line( line, sizeof(line), stdin, "version? " );
			ld->ld_version = atoi( line );
			break;

		case 'm':	/* modify or modifyrdn */
			if ( strncmp( line, "modify", 4 ) == 0 ) {
				get_line( dn, sizeof(dn), stdin, "dn? " );
				strcat( dn, dnsuffix );
				if ( (mods = get_modlist(
				    "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ",
				    "attribute type? ", "attribute value? " ))
				    == NULL )
					break;
				if ( (id = ldap_modify( ld, dn, mods )) == -1 )
					ldap_perror( ld, "ldap_modify" );
				else
					printf( "Modify initiated with id %d\n",
					    id );
			} else if ( strncmp( line, "modrdn", 4 ) == 0 ) {
				get_line( dn, sizeof(dn), stdin, "dn? " );
				strcat( dn, dnsuffix );
				get_line( rdn, sizeof(rdn), stdin, "newrdn? " );
				if ( (id = ldap_modrdn( ld, dn, rdn )) == -1 )
					ldap_perror( ld, "ldap_modrdn" );
				else
					printf( "Modrdn initiated with id %d\n",
					    id );
			} else {
				printf( "Possibilities: [modi]fy, [modr]dn\n" );
			}
			break;

		case 'q':	/* quit */
			ldap_unbind( ld );
			exit( EXIT_SUCCESS );
			break;

		case 'r':	/* result or remove */
			switch ( command3 ) {
			case 's':	/* result */
				get_line( line, sizeof(line), stdin,
				    "msgid (-1=>any)? " );
				if ( line[0] == '\0' )
					id = -1;
				else
					id = atoi( line );
				get_line( line, sizeof(line), stdin,
				    "all (0=>any, 1=>all)? " );
				if ( line[0] == '\0' )
					all = 1;
				else
					all = atoi( line );
				if (( msgtype = ldap_result( ld, id, all,
				    &timeout, &res )) < 1 ) {
					ldap_perror( ld, "ldap_result" );
					break;
				}
				printf( "\nresult: msgtype %d msgid %d\n",
				    msgtype, res->lm_msgid );
				handle_result( ld, res );
				res = NULL;
				break;

			case 'm':	/* remove */
				get_line( dn, sizeof(dn), stdin, "dn? " );
				strcat( dn, dnsuffix );
				if ( (id = ldap_delete( ld, dn )) == -1 )
					ldap_perror( ld, "ldap_delete" );
				else
					printf( "Remove initiated with id %d\n",
					    id );
				break;

			default:
				printf( "Possibilities: [rem]ove, [res]ult\n" );
				break;
			}
			break;

		case 's':	/* search */
			get_line( dn, sizeof(dn), stdin, "searchbase? " );
			strcat( dn, dnsuffix );
			get_line( line, sizeof(line), stdin,
			    "scope (0=baseObject, 1=oneLevel, 2=subtree, 3=children)? " );
			scope = atoi( line );
			get_line( filter, sizeof(filter), stdin,
			    "search filter (e.g. sn=jones)? " );
			types = get_list( "attrs to return? " );
			get_line( line, sizeof(line), stdin,
			    "attrsonly (0=attrs&values, 1=attrs only)? " );
			attrsonly = atoi( line );

			    if (( id = ldap_search( ld, dn, scope, filter,
				    types, attrsonly  )) == -1 ) {
				ldap_perror( ld, "ldap_search" );
			    } else {
				printf( "Search initiated with id %d\n", id );
			    }
			free_list( types );
			break;

		case 't':	/* set timeout value */
			get_line( line, sizeof(line), stdin, "timeout? " );
			timeout.tv_sec = atoi( line );
			break;

		case 'p':	/* parse LDAP URL */
			get_line( line, sizeof(line), stdin, "LDAP URL? " );
			if (( i = ldap_url_parse( line, &ludp )) != 0 ) {
			    fprintf( stderr, "ldap_url_parse: error %d\n", i );
			} else {
			    printf( "\t  host: " );
			    if ( ludp->lud_host == NULL ) {
				printf( "DEFAULT\n" );
			    } else {
				printf( "<%s>\n", ludp->lud_host );
			    }
			    printf( "\t  port: " );
			    if ( ludp->lud_port == 0 ) {
				printf( "DEFAULT\n" );
			    } else {
				printf( "%d\n", ludp->lud_port );
			    }
			    printf( "\t    dn: <%s>\n", ludp->lud_dn );
			    printf( "\t attrs:" );
			    if ( ludp->lud_attrs == NULL ) {
				printf( " ALL" );
			    } else {
				for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) {
				    printf( " <%s>", ludp->lud_attrs[ i ] );
				}
			    }
			    printf( "\n\t scope: %s\n",
					ludp->lud_scope == LDAP_SCOPE_BASE ? "baseObject"
					: ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? "oneLevel"
					: ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "subtree"
#ifdef LDAP_SCOPE_SUBORDINATE
					: ludp->lud_scope == LDAP_SCOPE_SUBORDINATE ? "children"
#endif
					: "**invalid**" );
			    printf( "\tfilter: <%s>\n", ludp->lud_filter );
			    ldap_free_urldesc( ludp );
			}
			    break;

		case 'n':	/* set dn suffix, for convenience */
			get_line( line, sizeof(line), stdin, "DN suffix? " );
			strcpy( dnsuffix, line );
			break;

		case 'o':	/* set ldap options */
			get_line( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" );
			ld->ld_deref = atoi( line );
			get_line( line, sizeof(line), stdin, "timelimit?" );
			ld->ld_timelimit = atoi( line );
			get_line( line, sizeof(line), stdin, "sizelimit?" );
			ld->ld_sizelimit = atoi( line );

			LDAP_BOOL_ZERO(&ld->ld_options);

			get_line( line, sizeof(line), stdin,
				"Recognize and chase referrals (0=no, 1=yes)?" );
			if ( atoi( line ) != 0 ) {
				LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_REFERRALS);
				get_line( line, sizeof(line), stdin,
					"Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" );
				if ( atoi( line ) != 0 ) {
					ldap_set_rebind_proc( ld, bind_prompt, NULL );
				}
			}
			break;

		case '?':	/* help */
			printf(
"Commands: [ad]d         [ab]andon         [b]ind\n"
"          [B]ind async  [c]ompare\n"
"          [modi]fy      [modr]dn          [rem]ove\n"
"          [res]ult      [s]earch          [q]uit/unbind\n\n"
"          [d]ebug       set ms[g]id\n"
"          d[n]suffix    [t]imeout         [v]ersion\n"
"          [?]help       [o]ptions"
"          [E]xplode dn  [p]arse LDAP URL\n" );
			break;

		default:
			printf( "Invalid command.  Type ? for help.\n" );
			break;
		}

		(void) memset( line, '\0', sizeof(line) );
	}

	return( 0 );
}