slap_parseURI(
	Operation	*op,
	struct berval	*uri,
	struct berval	*base,
	struct berval	*nbase,
	int		*scope,
	Filter		**filter,
	struct berval	*fstr,
	int		normalize )
{
	struct berval	bv;
	int		rc;
	LDAPURLDesc	*ludp;

	struct berval	idx;

	assert( uri != NULL && !BER_BVISNULL( uri ) );
	BER_BVZERO( base );
	BER_BVZERO( nbase );
	BER_BVZERO( fstr );
	*scope = -1;
	*filter = NULL;

	Debug( LDAP_DEBUG_TRACE,
		"slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );

	rc = LDAP_PROTOCOL_ERROR;

	idx = *uri;
	if ( idx.bv_val[ 0 ] == '{' ) {
		char	*ptr;

		ptr = ber_bvchr( &idx, '}' ) + 1;

		assert( ptr != (void *)1 );

		idx.bv_len -= ptr - idx.bv_val;
		idx.bv_val = ptr;
		uri = &idx;
	}

	/*
	 * dn[.<dnstyle>]:<dnpattern>
	 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
	 *
	 * <dnstyle> defaults to "exact"
	 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
	 */
	if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
		bv.bv_val = uri->bv_val + STRLENOF( "dn" );

		if ( bv.bv_val[ 0 ] == '.' ) {
			bv.bv_val++;

			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
				bv.bv_val += STRLENOF( "exact:" );
				*scope = LDAP_X_SCOPE_EXACT;

			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
				bv.bv_val += STRLENOF( "regex:" );
				*scope = LDAP_X_SCOPE_REGEX;

			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
				bv.bv_val += STRLENOF( "children:" );
				*scope = LDAP_X_SCOPE_CHILDREN;

			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
				bv.bv_val += STRLENOF( "subtree:" );
				*scope = LDAP_X_SCOPE_SUBTREE;

			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
				bv.bv_val += STRLENOF( "onelevel:" );
				*scope = LDAP_X_SCOPE_ONELEVEL;

			} else {
				return LDAP_PROTOCOL_ERROR;
			}

		} else {
			if ( bv.bv_val[ 0 ] != ':' ) {
				return LDAP_PROTOCOL_ERROR;
			}
			*scope = LDAP_X_SCOPE_EXACT;
			bv.bv_val++;
		}

		bv.bv_val += strspn( bv.bv_val, " " );
		/* jump here in case no type specification was present
		 * and uri was not an URI... HEADS-UP: assuming EXACT */
is_dn:		bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);

		/* a single '*' means any DN without using regexes */
		if ( ber_bvccmp( &bv, '*' ) ) {
			*scope = LDAP_X_SCOPE_USERS;
		}

		switch ( *scope ) {
		case LDAP_X_SCOPE_EXACT:
		case LDAP_X_SCOPE_CHILDREN:
		case LDAP_X_SCOPE_SUBTREE:
		case LDAP_X_SCOPE_ONELEVEL:
			if ( normalize ) {
				rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
				if( rc != LDAP_SUCCESS ) {
					*scope = -1;
				}
			} else {
				ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
				rc = LDAP_SUCCESS;
			}
			break;

		case LDAP_X_SCOPE_REGEX:
			ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );

		case LDAP_X_SCOPE_USERS:
			rc = LDAP_SUCCESS;
			break;

		default:
			*scope = -1;
			break;
		}

		return rc;

	/*
	 * u:<uid>
	 */
	} else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
			&& ( uri->bv_val[ 1 ] == ':' 
				|| uri->bv_val[ 1 ] == '/' 
				|| uri->bv_val[ 1 ] == '.' ) )
	{
		Connection	c = *op->o_conn;
		char		buf[ SLAP_LDAPDN_MAXLEN ];
		struct berval	id,
				user = BER_BVNULL,
				realm = BER_BVNULL,
				mech = BER_BVNULL;

		if ( sizeof( buf ) <= uri->bv_len ) {
			return LDAP_INVALID_SYNTAX;
		}

		id.bv_len = uri->bv_len;
		id.bv_val = buf;
		strncpy( buf, uri->bv_val, sizeof( buf ) );

		rc = slap_parse_user( &id, &user, &realm, &mech );
		if ( rc != LDAP_SUCCESS ) {
			return rc;
		}

		if ( !BER_BVISNULL( &mech ) ) {
			c.c_sasl_bind_mech = mech;
		} else {
			BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
		}
		
		rc = slap_sasl_getdn( &c, op, &user,
				realm.bv_val, nbase, SLAP_GETDN_AUTHZID );

		if ( rc == LDAP_SUCCESS ) {
			*scope = LDAP_X_SCOPE_EXACT;
		}

		return rc;

	/*
	 * group[/<groupoc>[/<groupat>]]:<groupdn>
	 *
	 * groupoc defaults to "groupOfNames"
	 * groupat defaults to "member"
	 * 
	 * <groupdn> must pass DN normalization
	 */
	} else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
	{
		struct berval	group_dn = BER_BVNULL,
				group_oc = BER_BVNULL,
				member_at = BER_BVNULL;
		char		*tmp;

		bv.bv_val = uri->bv_val + STRLENOF( "group" );
		bv.bv_len = uri->bv_len - STRLENOF( "group" );
		group_dn.bv_val = ber_bvchr( &bv, ':' );
		if ( group_dn.bv_val == NULL ) {
			/* last chance: assume it's a(n exact) DN ... */
			bv.bv_val = uri->bv_val;
			*scope = LDAP_X_SCOPE_EXACT;
			goto is_dn;
		}
		
		if ( bv.bv_val[ 0 ] == '/' ) {
			group_oc.bv_val = &bv.bv_val[ 1 ];
			group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;

			member_at.bv_val = ber_bvchr( &group_oc, '/' );
			if ( member_at.bv_val ) {
				group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
				member_at.bv_val++;
				member_at.bv_len = group_dn.bv_val - member_at.bv_val;

			} else {
				BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
			}

		} else {
			BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
			BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
		}
		group_dn.bv_val++;
		group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );

		if ( normalize ) {
			rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
			if ( rc != LDAP_SUCCESS ) {
				*scope = -1;
				return rc;
			}
		} else {
			ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
			rc = LDAP_SUCCESS;
		}
		*scope = LDAP_X_SCOPE_GROUP;

		/* FIXME: caller needs to add value of member attribute
		 * and close brackets twice */
		fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
			+ group_oc.bv_len + member_at.bv_len;
		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );

		tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
				STRLENOF( "(&(objectClass=" /* )) */ ) );
		tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
		tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
				STRLENOF( /* ( */ ")(" /* ) */ ) );
		tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
		tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );

		return rc;
	}

	/*
	 * ldap:///<base>??<scope>?<filter>
	 * <scope> ::= {base|one|subtree}
	 *
	 * <scope> defaults to "base"
	 * <base> must pass DN normalization
	 * <filter> must pass str2filter()
	 */
	rc = ldap_url_parse( uri->bv_val, &ludp );
	switch ( rc ) {
	case LDAP_URL_SUCCESS:
		/* FIXME: the check is pedantic, but I think it's necessary,
		 * because people tend to use things like ldaps:// which
		 * gives the idea SSL is being used.  Maybe we could
		 * accept ldapi:// as well, but the point is that we use
		 * an URL as an easy means to define bits of a search with
		 * little parsing.
		 */
		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
			/*
			 * must be ldap:///
			 */
			rc = LDAP_PROTOCOL_ERROR;
			goto done;
		}
		break;

	case LDAP_URL_ERR_BADSCHEME:
		/*
		 * last chance: assume it's a(n exact) DN ...
		 *
		 * NOTE: must pass DN normalization
		 */
		ldap_free_urldesc( ludp );
		bv.bv_val = uri->bv_val;
		*scope = LDAP_X_SCOPE_EXACT;
		goto is_dn;

	default:
		rc = LDAP_PROTOCOL_ERROR;
		goto done;
	}

	if ( ( ludp->lud_host && *ludp->lud_host )
		|| ludp->lud_attrs || ludp->lud_exts )
	{
		/* host part must be empty */
		/* attrs and extensions parts must be empty */
		rc = LDAP_PROTOCOL_ERROR;
		goto done;
	}

	/* Grab the scope */
	*scope = ludp->lud_scope;

	/* Grab the filter */
	if ( ludp->lud_filter ) {
		*filter = str2filter_x( op, ludp->lud_filter );
		if ( *filter == NULL ) {
			rc = LDAP_PROTOCOL_ERROR;
			goto done;
		}
		ber_str2bv( ludp->lud_filter, 0, 0, fstr );
	}

	/* Grab the searchbase */
	ber_str2bv( ludp->lud_dn, 0, 0, base );
	if ( normalize ) {
		rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
	} else {
		ber_dupbv_x( nbase, base, op->o_tmpmemctx );
		rc = LDAP_SUCCESS;
	}

done:
	if( rc != LDAP_SUCCESS ) {
		if( *filter ) {
			filter_free_x( op, *filter, 1 );
			*filter = NULL;
		}
		BER_BVZERO( base );
		BER_BVZERO( fstr );
	} else {
		/* Don't free these, return them to caller */
		ludp->lud_filter = NULL;
		ludp->lud_dn = NULL;
	}

	ldap_free_urldesc( ludp );
	return( rc );
}