/*******************************************************************************

 * Copyright (C) 1999, 2000, 2001, 2002 Novell, Inc. All Rights Reserved.

 *

 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND

 * TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE

 * AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS

 * THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO

 * DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE

 * CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS

 * TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF

 * DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR

 * DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.

 *

 * $name:          ExtensibleMatch.java

 * 

 * $description:   The ExtensibleMatch.java sample constructs the extensible

 *                 match filter with the given inputs,  searches the directory

 *                 and prints the results.  Extensible match requires

 *                 eDirectory version 8.7 or higher.

 *

 * The LDAP v3 Core protocol specification requires LDAP servers to recognize a

 * search element called an extensible match. Extensible match provides

 * additional features which can be expressed in the search filter.

 *

 * Feature 1. Allow matching to components of the object's Distinguished Name

 *            as well as attributes of the object.

 *

 * Feature 2. Allow a specific matching rule to be used for an attribute-value

 *            comparison, such as a sounds-alike match.

 *

 * Feature 3. Allow a match to any attribute of an object supporting a

 *            particular matching rule.  For example, one could search for

 *            objects with any string attribute containing "Dino".

 *

 * These features may be used independently or in combination.

 *

 * All the supported matching rules are advertised through LDAP subschema

 * subentry object using the standard matchingRules and matchingRuleUse

 * schema attributes.

 *

 * It is mandatory that either dnAttribute or matching rule be present

 * as part of extensible match filter.

 *

 * NOTE:  In eDirectory 8.7, only the DN feature of extensible match

 * is supported (feature 1).

 *

 * The following examples illustrate the use of extensible match filters

 * and correspond to the features listed above.

 *

 * Example #1: (ou:dn:=Sales)

 * Same as filter "(ou=Sales)" except that DN components should be

 * considered part of the entry when doing the match.  Any object

 * with "ou=Sales" as part of its DN will match.

 *

 * Example #2: (cn:2.4.6.8:=Barney Rubble)

 * Specifies matching rule "2.4.6.8" to be used when making comparisons

 * for (cn="Barney Rubble").

 *

 * Example #2a: (cn:dn:2.4.6.8:=Barney Rubble)

 * Combines examples #1 and #2.  Specifies the matching rule to use and

 * indicates that components of entry's DN should be considered attributes of

 * the entry when evaluating the match.

 *

 * Example #3: (:2.4.6.8:=Dino)

 * Indicates this filter should be applied to all attributes supporting

 * this matching rule.

 *

 * Example #3a: (:dn:2.4.6.8:=Dino)

 * Same as example #3, but also includes components of the DN in the match.

 *

 * For further details, refer to IETF RFC 2251 and RFC 2254.

 *

 ******************************************************************************/



import com.novell.ldap.*;

import java.io.UnsupportedEncodingException;

 

public class ExtensibleMatch 

{

    public static void main( String[] args )

    {

        boolean  useDN = false, useAttribute = false, useRule = false;

        int      argc=0, counter=0;

       

        argc = args.length;

        // Determine which options are set. "-d" option to come before the parameters 

        if ( argc == 9 ) {

            // -d option inserts ":dn" in the filter 

            if (args[0].equals("-d")) {

                   useDN = true;

                   counter++;

                   argc--;

            }

        } else

        // -d option is not set

        if ( argc != 8 ) {

                usage();

                System.exit(1);

        }



        int      searchScope    = LDAPConnection.SCOPE_SUB;

        int      ldapVersion    = LDAPConnection.LDAP_V3;

        String[] attrs          = {LDAPConnection.NO_ATTRS};

        String   ldapHost       = args[counter++];

        int      ldapPort       = Integer.parseInt(args[counter++]);

        String   loginDN        = args[counter++];

        String   password       = args[counter++];

        String   searchBase     = args[counter++];

        String   matchAttribute = args[counter++];

        String   matchingRule   = args[counter++];

        String   matchValue     = args[counter];



        if (!matchAttribute.equals("")) useAttribute = true;

        if (!matchingRule.equals("")) useRule = true;



        /* Must have dn option or matching rule. */

        if ((useDN == false) && (useRule == false)) {

           System.out.println("Error: Either <attribute name> or"

                      + " <matching rule> must be present as part of an"

                      + " extensible matching fileter.");

           usage();

           System.exit(1);

        }



        // Construct extensible matching filter. Example:"(attr:dn:rule:=value)"

        String extMatchFilter = "(";

        if (useAttribute) extMatchFilter += matchAttribute;

        if (useDN) extMatchFilter += ":dn";

        if (useRule) {

            extMatchFilter += ":";

            extMatchFilter += matchingRule;

        }

        extMatchFilter += ":=";

        extMatchFilter += matchValue;

        extMatchFilter += ")";



        System.out.println("Extensible match filter: " + extMatchFilter);



        // Setting search time out to 10 seconds

        LDAPSearchConstraints cons = new LDAPSearchConstraints();

        cons.setTimeLimit( 10000 );

        LDAPConnection lc = new LDAPConnection();



        try {

            // connect to the server

            lc.connect( ldapHost, ldapPort );

            // bind to the server

            lc.bind( ldapVersion, loginDN, password.getBytes("UTF8") );



            LDAPSearchResults searchResults =

                lc.search(  searchBase,

                            searchScope,

                            extMatchFilter,

                            attrs,          

                            false,

                            cons );         // time out value



            // To print matched entry names

            int cnt = 0;

            while ( searchResults.hasMore()) {

                LDAPEntry nextEntry = null;

                try {

                    nextEntry = searchResults.next();

                    cnt += 1;

                }

                catch(LDAPException e) {

                    System.out.println("Error: " + e.toString());

                    System.exit(1);

                }

                System.out.println("\n" + nextEntry.getDN());

            }

            if( cnt == 0) {

                System.out.println("No entries returned from search.");

            }

        }

        catch( LDAPException e ) {

            System.out.println( "Error: " + e.toString() );

            System.exit(1);

        }

        catch( UnsupportedEncodingException e ) {

            System.out.println( "Error: " + e.toString() );

        }

    

        System.out.println("\nExtensible match search completed successfully.");

        return;    

    }



    public static void usage() {

           System.err.println("Usage:\njava ExtensibleMatch [-d] <host name>"

                        + " <port number> <login dn> <password>\n"

                        + "\t   <search base> <attribute name> <matching rule>"

                        + " <match value>\n"

                        + "where: \n"

                        + "    -d means consider DN components as attributes of"

                        + " the object \n"

                        + "    <search base> is the starting container for a"

                        + " subtree search \n"

                        + "    <attribute name> is the attribute to match"

                        + " against, or \"\" for all attrs \n"

                        + "    <matching rule> is an OID specifying the"

                        + " matching rule to use, or \"\"\n"

                        + "    <match value> is the value to compare against in"

                        + " the search filter\n");



           System.err.println("Examples:\n"

                        + "#1. java ExtensibleMatch -d Acme.com 389"

                        + " cn=admin,o=Acme secret o=Acme\n\t\tou \"\" Sales \n"

                        + "#2. java ExtensibleMatch Acme.com 389"

                        + " cn=admin,o=Acme secret \n\t\tou=Sales,o=Acme"

                        + " cn 2.4.6.8 \"Barney Rubble\" \n"

                        + "#3. java ExtensibleMatch Acme.com 389 "

                        + "cn=admin,o=Acme secret o=Acme\n\t\t\"\" 2.4.6.8 Dino \n\n"

                        + "NOTE: Only example 1 is supported by eDirectory"

                        + " 8.7");

        return;

    }

}

