/* $OpenLDAP: pkg/ldap/libraries/liblutil/sasl.c,v 1.10.2.2 2003/02/21 19:55:45 kurt Exp $ */
/*
 * Copyright 2000-2002 The OpenLDAP Foundation, All Rights Reserved.
 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
 */

#include "portable.h"

#ifdef HAVE_CYRUS_SASL

#include <stdio.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <ac/unistd.h>

#ifdef HAVE_SASL_SASL_H
#include <sasl/sasl.h>
#else
#include <sasl.h>
#endif

#include <ldap.h>
#include "lutil_ldap.h"


typedef struct lutil_sasl_defaults_s {
	char *mech;
	char *realm;
	char *authcid;
	char *passwd;
	char *authzid;
} lutilSASLdefaults;


void *
lutil_sasl_defaults(
	LDAP *ld,
	char *mech,
	char *realm,
	char *authcid,
	char *passwd,
	char *authzid )
{
	lutilSASLdefaults *defaults;
	
	defaults = ber_memalloc( sizeof( lutilSASLdefaults ) );

	if( defaults == NULL ) return NULL;

	defaults->mech = mech;
	defaults->realm = realm;
	defaults->authcid = authcid;
	defaults->passwd = passwd;
	defaults->authzid = authzid;

	if( defaults->mech == NULL ) {
		ldap_get_option( ld, LDAP_OPT_X_SASL_MECH, &defaults->mech );
	}
	if( defaults->realm == NULL ) {
		ldap_get_option( ld, LDAP_OPT_X_SASL_REALM, &defaults->realm );
	}
	if( defaults->authcid == NULL ) {
		ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid );
	}
	if( defaults->authzid == NULL ) {
		ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid );
	}

	return defaults;
}

static int interaction(
	unsigned flags,
	sasl_interact_t *interact,
	lutilSASLdefaults *defaults )
{
	const char *dflt = interact->defresult;
	char input[1024];

	int noecho=0;
	int challenge=0;

	switch( interact->id ) {
	case SASL_CB_GETREALM:
		if( defaults ) dflt = defaults->realm;
		break;
	case SASL_CB_AUTHNAME:
		if( defaults ) dflt = defaults->authcid;
		break;
	case SASL_CB_PASS:
		if( defaults ) dflt = defaults->passwd;
		noecho = 1;
		break;
	case SASL_CB_USER:
		if( defaults ) dflt = defaults->authzid;
		break;
	case SASL_CB_NOECHOPROMPT:
		noecho = 1;
		challenge = 1;
		break;
	case SASL_CB_ECHOPROMPT:
		challenge = 1;
		break;
	}

	if( dflt && !*dflt ) dflt = NULL;

	if( flags != LDAP_SASL_INTERACTIVE &&
		( dflt || interact->id == SASL_CB_USER ) )
	{
		goto use_default;
	}

	if( flags == LDAP_SASL_QUIET ) {
		/* don't prompt */
		return LDAP_OTHER;
	}

	if( challenge ) {
		if( interact->challenge ) {
			fprintf( stderr, "Challenge: %s\n", interact->challenge );
		}
	}

	if( dflt ) {
		fprintf( stderr, "Default: %s\n", dflt );
	}

	snprintf( input, sizeof input, "%s: ",
		interact->prompt ? interact->prompt : "Interact" );

	if( noecho ) {
		interact->result = (char *) getpassphrase( input );
		interact->len = interact->result
			? strlen( interact->result ) : 0;

	} else {
		/* prompt user */
		fputs( input, stderr );

		/* get input */
		interact->result = fgets( input, sizeof(input), stdin );

		if( interact->result == NULL ) {
			interact->len = 0;
			return LDAP_UNAVAILABLE;
		}

		/* len of input */
		interact->len = strlen(input); 

		if( interact->len > 0 && input[interact->len - 1] == '\n' ) {
			/* input includes '\n', trim it */
			interact->len--;
			input[interact->len] = '\0';
		}
	}


	if( interact->len > 0 ) {
		/* duplicate */
		char *p = (char *)interact->result;
		interact->result = strdup( p );

		/* zap */
		memset( p, '\0', interact->len );

	} else {
use_default:
		/* input must be empty */
		interact->result = strdup( (dflt && *dflt) ? dflt : "" );
		interact->len = interact->result
			? strlen( interact->result ) : 0;
	}

	if( defaults && defaults->passwd && interact->id == SASL_CB_PASS ) {
		/* zap password after first use */
		memset( defaults->passwd, '\0', strlen(defaults->passwd) );
		defaults->passwd = NULL;
	}

	return LDAP_SUCCESS;
}

int lutil_sasl_interact(
	LDAP *ld,
	unsigned flags,
	void *defaults,
	void *in )
{
	sasl_interact_t *interact = in;

	if( interact->result ) {
		/* we have results from a previous interaction */
		free( (void *)interact->result );
		interact->result = NULL;
	}

	if( ld == NULL ) return LDAP_PARAM_ERROR;

	if( flags == LDAP_SASL_INTERACTIVE ) {
		fputs( "SASL Interaction\n", stderr );
	}

	while( interact->id != SASL_CB_LIST_END ) {
		int rc = interaction( flags, interact, defaults );

		if( rc )  return rc;
		interact++;
	}
	
	return LDAP_SUCCESS;
}
#endif
