/***************************************************************************
%name: NdsSearch.java
%version: 1.0
%date_modified: 101598
%dependencies: none

Copyright (c) 1998 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.
****************************************************************************/

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.directory.InvalidSearchControlsException;
import javax.naming.directory.InvalidSearchFilterException;

import com.novell.utility.naming.Environment;
import com.novell.service.nds.NdsObject;
import com.novell.service.nds.NdsObjectACL;
import com.novell.service.nds.NdsObjectRights;

/*   NdsSearch -- an NJCL sample program
 *
 *   Designed to allow users to search an NDS tree using the search filter syntax
 *   specified in RFC 1960. This is the syntax built into JNDI. This program is
 *   useful as a utility as well as as a sample.
 */

class NdsSearch
{
   static boolean           SHOULD_PRINT_STRING_VALUES, SUCCESSFUL, VERBOSE;
   static int               ENTRY_RIGHTS;
   static SearchControls    CONTROLS;
   static StringBuffer      FILTER, NAME, PROVIDER_URL, SCOPE;
   static Throwable         EXCEPTION;

   static
   {
      SUCCESSFUL        = false;
      FILTER            = new StringBuffer("");
      NAME              = new StringBuffer("");
      PROVIDER_URL      = new StringBuffer("");
   }

   public static void main(String[] args)
   {
      /*
         The main() method provides a kind of high-level overview of
         what this application does.

         First, it parses the arguments passed into the app in the method
         parseArgs(args). This method parses the args and sets up the
         appropriate class variables--controls, filter, name, provider url,
         whether the app prints stack traces upon throwing exceptions.

         Second, it creates an initial DirContext using the provider url
         passed into the app as a parameter.

         Third, it performs a search on the initial DirContext.
      */

      try
      {
         // parse the args
         parseArgs(args);

         // create the initial context
         DirContext target = createInitialContext(PROVIDER_URL.toString());

         // perform the search
         NamingEnumeration search_result = target.search(
            NAME.toString(),
            FILTER.toString(),
            CONTROLS);

         System.out.println("\nExamining the search results");

         if (search_result.hasMore()) // we perform this check to print out
                                      // whether we should print out a
                                      // "no results" message
             while (search_result.hasMore())
                // print out the search result
                printSearchResults((SearchResult)search_result.next());
         else
             System.out.println("The search returned no results");
      }
      catch (InvalidSearchFilterException isfe)
      {
         System.out.println("\nThe search filter you specified was invalid");
         EXCEPTION = isfe;
      }
      catch (InvalidSearchControlsException isce)
      {
         System.out.println("\nThe search controls you specified were invalid");
         EXCEPTION = isce;
      }
      catch (NamingException ne)
      {
         System.out.println("\nReceived a general naming exception");
         EXCEPTION = ne;
      }
      catch (NullPointerException npe)
      {
         System.out.println("\nReceived a null pointer exception");
         EXCEPTION = npe;
      }
      finally
      {
        System.out.println("");
         if ((VERBOSE) && (EXCEPTION != null))  // only works when debugging
            EXCEPTION.printStackTrace();
      }
   }

   private static void printSearchResults(SearchResult search_result)
   {
        if (search_result != null)
        {
            // you can get the bound object
            //    using the following line of code 
            // SearchResult theBoundObject = (SearchResult)search_result.getObject();
            System.out.println(search_result.getName());
        }
        else
            System.out.println("No search results.");
   }

   private static DirContext createInitialContext(String _providerURL)
   {
      System.out.println("\nLooking up "
           + _providerURL
         );
      try
      {
         Hashtable hash = new Hashtable(11); // eleven is enough space
         hash.put(Context.INITIAL_CONTEXT_FACTORY,
            Environment.NDS_INITIAL_CONTEXT_FACTORY);
         hash.put(Context.PROVIDER_URL, _providerURL);
         InitialDirContext initDirCtx = new InitialDirContext(hash);
         DirContext theReturnableObject = ((DirContext)initDirCtx.lookup(""));
         System.out.println("Successfully looked it up");
         return theReturnableObject;
      }
      catch (NamingException ne)
      {
         System.out.println("\nCouldn't lookup "
              + _providerURL);
         System.out.println("\nOperation failed");
         if (VERBOSE) // only works when debugging
            ne.printStackTrace();
         System.exit(-1);
      }
      return null; // compiler gets upset if we leave this out
   }

   private static void parseArgs(String[] args)
   {
      /*
          This is a complicated and unwieldy beast which, unfortunately,
          can only be partly tamed.
          
          This method catches args and, if appropriate, 
          sets class variables with the values that follow the args.
          
          The help() method is a catch-all for when we choke on an arg.
      
          First, we check if the class was invoked without any args,
          or if it was invoked with a "/?" or a "-h," which are well-known
          ways to indicate "help me!"
          
          Second, we check to see if we should do verbose ("-v") stack traces.
          
          Third, we set the URL ("-u").
          
          Fourth, we set the search filter ("-f").
      
          Fifth, we set the search controls ("-s").
          
          Last, we set the name we want JNDI to search for ("-n").      
      */
      
      if (args.length < 1)
         help();

      if ((args[0].equals("/?")) || (args[0].equals("-h")))
         help();

      for (int i=0; i < args.length; i++)
      {
         if (args[i].startsWith("-", 0))
         {
            if (args[i].equalsIgnoreCase("-v"))
                VERBOSE = true;
            if (args[i].equalsIgnoreCase("-u"))
            {
               if (i == args.length -1)
                  help();  // we are at the end of the args and didn't get a url
                           // so we need to punt
               i++;
               PROVIDER_URL = new StringBuffer(args[i]);
               System.out.println("Provider URL "
                 + PROVIDER_URL);
            }
            else if (args[i].equalsIgnoreCase("-f"))
            {
               if (i == args.length -1)
                  help();
               i++;
               FILTER = new StringBuffer(args[i]);
               System.out.println("Filter "
                  + FILTER);
            }
            else if (args[i].equalsIgnoreCase("-s"))
            {
               if (i == args.length -1)
                  help();
               i++;

               CONTROLS = new SearchControls();

               // check to see if the next arg is a scope
               String scope = args[i];
               if (scope.equalsIgnoreCase("OBJECT"))
                  CONTROLS.setSearchScope(SearchControls.OBJECT_SCOPE);
               else if (scope.equalsIgnoreCase("ONELEVEL"))
                  CONTROLS.setSearchScope(SearchControls.ONELEVEL_SCOPE);
               else if (scope.equalsIgnoreCase("SUBTREE"))
                  CONTROLS.setSearchScope(SearchControls.SUBTREE_SCOPE);
               else 
               {
                  System.out.println("Bad search control specified.");
                  help();
               }
               System.out.println("Scope "
                  + scope);
            }
            else if (args[i].equalsIgnoreCase("-n"))
            {
                if (i == args.length -1)
                   help();
                i++;
                NAME = new StringBuffer(args[i]);
                System.out.println("Name "
                   + NAME);
            }
         }
      }
   }

   private static void help()
   {
      System.out.println("NdsSearch -- a search utility for NDS using Java");
      System.out.println("usage: java NdsSearch [-u url] [-s scope] [-v] [-f filter_string] [-n name] ");
      System.out.println("\nOPTIONS\n");
      System.out.println("-u\tspecifies the NDS URL as a search base");
      System.out.println("-s\tspecifies the search scope");
      System.out.println("-v\tspecifies verbose errors (default is quiet)");
      System.out.println("\nPARAMETERS\n");
      System.out.println("url\tthe URL of the tree to search");
      System.out.println("\ttakes the form of nds://[treename]/");
      System.out.println("scope\tthe scope of the search to perform");
      System.out.println("\tcan be ONELEVEL, SUBTREE, or OBJECT");
      System.out.println("filter_string\tthe RFC 2254 search filter syntax to use to search");
      System.out.println("name\tthe name of the object to search");
      System.out.println("\nCAVEATS\n");
      System.out.println(" * Requires an authenticated connection as a user with sufficient rights to search.");
      System.out.println(" * Parameters can be specified in any order.");
      System.out.println("\nEXAMPLES\n");
      System.out.println("java NdsSearch -u nds://superstring/ -f (cn=kris)");
      System.out.println("\tSearches the tree superstring for an object with");
      System.out.println("\rattribute ID 'cn' with value 'kris'");
      System.out.println("java NdsSearch -u nds://superstring/ -f (surname=magnusson) -n kris");
      System.out.println("\tSearches the tree superstring for an object with");
      System.out.println("\rattribute ID 'surname' with value 'magnusson', with name 'kris'");
      System.exit(1);
   }
}