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

  $Archive: /njcl_v2/src/com/novell/service/session/xplat/Natives.java $
  $Revision: 29 $
  $Modtime: 3/24/03 2:42p $

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/
package com.novell.service.session.xplat;

import com.novell.service.session.util.Debug;
import com.novell.service.security.NdsIdentity;
import com.novell.service.jncp.NSIException;
import com.novell.service.jncp.NSIExceptionBuilder;
import com.novell.service.jncpv2.clx.ClxJNI;
import com.novell.service.jncpv2.cal.CalJNI;
import com.novell.service.jncpv2.net.NetJNI;
import com.novell.service.session.SessionAttr;
import com.novell.service.session.SessionAttrEnumerator;
import com.novell.service.session.SessionAttrs;
import com.novell.service.session.SessionEnv;
import com.novell.service.session.SessionException;
import com.novell.service.session.spi.SessionManagerImpl;
import com.novell.service.session.SessionManagerFactory;
import com.novell.service.session.SessionRuntimeException;
import com.novell.service.session.InvalidDomainNameException;
import com.novell.service.session.InvalidUserNameException;
import com.novell.service.session.util.TwoWayHashtable;
import com.novell.service.toolkit.jcl.NWLong;
import com.novell.service.toolkit.jcl.NWVersion;
import com.novell.java.lang.IntegerBuffer;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.io.Serializable;

/** @internal
 * Utility class shared by NDS and Bindery Session providers.
 */
public class Natives
{
   // Always access debug via final static...hopefully a final static of
   // false will be optimized out by the compiler
   final static private boolean  DEBUG = false;
   final static private boolean  ERRORS_DEBUG = false;
   final static private boolean  I_DEBUG = false; // Ignore exception
   final static private boolean  S_DEBUG = false; // May have side effects
   final static private boolean  CONNECTION_DEBUG = false;
   final static private boolean  OPEN_CLOSE_CONN_DEBUG = false;
   final static private boolean  CONTEXT_DEBUG = false;
   final static private boolean  OPEN_CLOSE_CONTEXT_DEBUG = false;
   final static private boolean  LOGIN_LOGOUT_DEBUG = false;
   final static private boolean  PASSWORD_DEBUG = false;
   final static private boolean  SCAN_DEBUG = false;  // scan occured

   // Print message if deprecated API is used
   final static private boolean ERROR_ON_DEPRECATED = false;

   // Can't get valid tree name from reference
   static private boolean NLM_KLUDGE_NO_TREE_FROM_REF = true;
   // Can't get valid user id from reference
   static private boolean NLM_KLUDGE_NO_USER_FROM_REF = true;
   // Can't get valid user id from reference
   static protected boolean NLM_KLUDGE_INVALID_CACHE = false;
   // NT requires us to force licenses...they only keep the license per
   // handle
   static private boolean NT_KLUDGE = true;
   // May open successfully w/bad tree name
   static private boolean NLM_KLUDGE_BADTREE = true;
   // May open successfully w/bad server name
   static private boolean NLM_KLUDGE_BADSERVER = true;


   // For RequesterConnectionSearchEnumerator
   static final int TYPE_STRING = 0;
   static final int TYPE_VALUE = 1;
   static final int TYPE_VERSION = 2;

   public static final int OWNER_NONE = 0;
   public static final int OWNER_BINDERY = 1;
   public static final int OWNER_NDS = 2;

   public static final String OWNER_NONE_STRING = "NONE";
   public static final String OWNER_BINDERY_STRING = "BINDERY";
   public static final String OWNER_NDS_STRING = "NDS";

   public static final int CONN_INFO_NONE = 0x00;
   public static final int CONN_INFO_AUTHENT_STATE = 0x01;
   public static final int CONN_INFO_BCAST_STATE = 0x02;
   public static final int CONN_INFO_CONN_REF = 0x03;
   public static final int CONN_INFO_TREE_NAME = 0x04;
   public static final int CONN_INFO_CONN_NUMBER = 0x05;
   public static final int CONN_INFO_USER_ID = 0x06;
   public static final int CONN_INFO_SERVER_NAME = 0x07;
   public static final int CONN_INFO_NDS_STATE = 0x08;
   public static final int CONN_INFO_MAX_PACKET_SIZE = 0x09;
   public static final int CONN_INFO_LICENSE_STATE = 0x0a;
   public static final int CONN_INFO_DISTANCE = 0x0b;
   public static final int CONN_INFO_SERVER_VERSION = 0x0c;
   public static final int CONN_INFO_SERVER_ADDRESS = 0xff01;

   public static final int AUTH_TYPE_NONE = 0;
   public static final int AUTH_TYPE_BIND = 1;
   public static final int AUTH_TYPE_NDS = 2;
   public static final int MATCH_NOT_EQUALS = 0;
   public static final int MATCH_EQUALS = 1;
   public static final int NDS_NOT_CAPABLE = 0;
   public static final int NDS_CAPABLE = 1;
   public static final int NAME_FORMAT_NDS = 0x0001;
   public static final int NAME_FORMAT_BIND = 0x0002;
   public static final int NAME_FORMAT_NDS_TREE = 0x0008;
   public static final int NAME_FORMAT_WILD = 0x8000;
   public static final int OPEN_LICENSED = 0x0001;
   public static final int OPEN_UNLICENSED = 0x0002;
   public static final int OPEN_PRIVATE = 0x0004;
   public static final int OPEN_PUBLIC = 0x0008;

   public static final int OT_USER = 0x0100;

   public final static int  DCK_FLAGS               =  1;
   public final static int  DCK_CONFIDENCE          =  2;
   public final static int  DCK_NAME_CONTEXT        =  3;
   public final static int  DCK_TRANSPORT_TYPE      =  4;
   public final static int  DCK_REFERRAL_SCOPE      =  5;
   public final static int  DCK_LAST_CONNECTION     =  8;
   public final static int  DCK_TREE_NAME           = 11;
   public final static int  DCK_DSI_FLAGS           = 12;
   public final static int  DCK_NAME_CACHE_DEPTH    = 15;
   /* NLM only--see NWDSIPXNetworkAddr */
   public final static int  DCK_LAST_SERVER_ADDRESS =  9;
   public final static int  DCK_LAST_ADDRESS_USED   = 10;

   // Special value for handling NLM-based CurrentUser
   public final static int  DCK_SPECIAL             = 16;

   // values for DCK_FLAGS
   public final static int  DCV_DEREF_ALIASES        = 0x0001;
   public final static int  DCV_XLATE_STRINGS        = 0x0002;
   public final static int  DCV_TYPELESS_NAMES       = 0x0004;
   public final static int  DCV_ASYNC_MODE           = 0x0008;
   public final static int  DCV_CANONICALIZE_NAMES   = 0x0010;
   public final static int  DCV_DEREF_BASE_CLASS     = 0x0040;
   public final static int  DCV_DISALLOW_REFERRALS   = 0x0080;

   // values for DCK_CONFIDENCE
   public final static int  DCV_LOW_CONF             = 0;
   public final static int  DCV_MED_CONF             = 1;
   public final static int  DCV_HIGH_CONF            = 2;

   // values for DCK_REFERRAL_SCOPE
   public final static int  DCV_ANY_SCOPE            = 0;
   public final static int  DCV_COUNTRY_SCOPE        = 1;
   public final static int  DCV_ORGANIZATION_SCOPE   = 2;
   public final static int  DCV_LOCAL_SCOPE          = 3;

   // values for DSI_FLAGS key
   public final static int DSI_OUTPUT_FIELDS           = 0x00000001;
   public final static int DSI_ENTRY_ID                = 0x00000002;
   public final static int DSI_ENTRY_FLAGS             = 0x00000004;
   public final static int DSI_SUBORDINATE_COUNT       = 0x00000008;
   public final static int DSI_MODIFICATION_TIME       = 0x00000010;
   public final static int DSI_MODIFICATION_TIMESTAMP  = 0x00000020;
   public final static int DSI_CREATION_TIMESTAMP      = 0x00000040;
   public final static int DSI_PARTITION_ROOT_ID       = 0x00000080;
   public final static int DSI_PARENT_ID               = 0x00000100;
   public final static int DSI_REVISION_COUNT          = 0x00000200;
   public final static int DSI_REPLICA_TYPE            = 0x00000400;
   public final static int DSI_BASE_CLASS              = 0x00000800;
   public final static int DSI_ENTRY_RDN               = 0x00001000;
   public final static int DSI_ENTRY_DN                = 0x00002000;
   public final static int DSI_PARTITION_ROOT_DN       = 0x00004000;
   public final static int DSI_PARENT_DN               = 0x00008000;
   public final static int DSI_PURGE_TIME              = 0x00010000;
   public final static int DSI_DEREFERENCE_BASE_CLASS  = 0x00020000;
   public final static int DSI_REPLICA_NUMBER          = 0x00040000;
   public final static int DSI_REPLICA_STATE           = 0x00080000;
   public final static int DSI_GUID                    = 0x00100000;
   public final static int DSI_COUNT                   = 21;

   /* ERROR: directory services client library */
   public final static int  ERR_CONTEXT_CREATION     = 0xFFFFFEB8;

   // Used for translating attribute ids to native equivalents
   private static TwoWayHashtable connInfo = null;
   private static TwoWayHashtable contextInfo = null;

   private static NWVersion nwnetVersion = new NWVersion();
   private static boolean isNetWareNLM = false;

   static
   {
      try
      {
         if(System.getProperty("os.name", "").toLowerCase().startsWith("netware"))
         {
            isNetWareNLM = true;
            String version = System.getProperty("os.version");
            int majorVersion = Integer.parseInt(version.substring(0,version.indexOf(".")));
            if(majorVersion < 5)
            {
               try
               {
                  Runtime runtime = Runtime.getRuntime();
                  Process process = runtime.exec("pftdlbs");
                  process.waitFor();
               }
               catch (Throwable t)
               {
                  // ignore pftdlbs load failure
               }
            }
         }
         System.loadLibrary ("jncpv2");
      }
      catch (Exception e)
      {
         e.printStackTrace ();
         System.exit (1);
      }

      connInfo = new TwoWayHashtable(13, 1);
      connInfo.put(Xplat.CONN_AUTHENTICATION_STATE_ATTR_ID,  new Integer(CONN_INFO_AUTHENT_STATE)     );
      connInfo.put(Xplat.CONN_BROADCAST_STATE_ATTR_ID,       new Integer(CONN_INFO_BCAST_STATE)       );
      connInfo.put(Xplat.CONN_CONNECTION_REFERENCE_ATTR_ID,  new Integer(CONN_INFO_CONN_REF)          );
      connInfo.put(Xplat.CONN_TREE_NAME_ATTR_ID,             new Integer(CONN_INFO_TREE_NAME)         );
      connInfo.put(Xplat.CONN_CONNECTION_NUMBER_ATTR_ID,     new Integer(CONN_INFO_CONN_NUMBER)       );
      connInfo.put(Xplat.CONN_USER_ID_ATTR_ID,               new Integer(CONN_INFO_USER_ID)           );
      connInfo.put(Xplat.CONN_SERVER_NAME_ATTR_ID,           new Integer(CONN_INFO_SERVER_NAME)       );
      connInfo.put(Xplat.CONN_NDS_STATE_ATTR_ID,             new Integer(CONN_INFO_NDS_STATE)         );
      connInfo.put(Xplat.CONN_MAX_PACKET_SIZE_ATTR_ID,       new Integer(CONN_INFO_MAX_PACKET_SIZE)   );
      connInfo.put(Xplat.CONN_LICENSE_STATE_ATTR_ID,         new Integer(CONN_INFO_LICENSE_STATE)     );
      connInfo.put(Xplat.CONN_DISTANCE_ATTR_ID,              new Integer(CONN_INFO_DISTANCE)          );
      connInfo.put(Xplat.CONN_SERVER_VERSION_ATTR_ID,        new Integer(CONN_INFO_SERVER_VERSION)    );
      connInfo.put(Xplat.CONN_SERVER_ADDRESS_ATTR_ID,        new Integer(CONN_INFO_SERVER_ADDRESS)    );

      contextInfo = new TwoWayHashtable(7, 1);
      contextInfo.put(Xplat.DCK_FLAGS_ATTR_ID,            new Integer(DCK_FLAGS)           );
      contextInfo.put(Xplat.DCK_CONFIDENCE_ATTR_ID,       new Integer(DCK_CONFIDENCE)      );
      contextInfo.put(Xplat.DCK_NAME_CONTEXT_ATTR_ID,     new Integer(DCK_NAME_CONTEXT)    );
      contextInfo.put(Xplat.DCK_TRANSPORT_TYPE_ATTR_ID,   new Integer(DCK_TRANSPORT_TYPE)  );
      contextInfo.put(Xplat.DCK_REFERRAL_SCOPE_ATTR_ID,   new Integer(DCK_REFERRAL_SCOPE)  );
      contextInfo.put(Xplat.DCK_LAST_CONNECTION_ATTR_ID,  new Integer(DCK_LAST_CONNECTION) );
      contextInfo.put(Xplat.DCK_TREE_NAME_ATTR_ID,        new Integer(DCK_TREE_NAME)       );

      System.runFinalizersOnExit(true);

      // Based on platform we're running on, turn on certain kludges
      // that are platform-specific.
      // NOTE: This needs to be expanded to all other kludges currently
      // implemented...but the changes must be tested!
      if (isNetWareNLM)
      {
         int ccode = ClxJNI.NWGetNWNetVersion(nwnetVersion);
         if (DEBUG || (ERRORS_DEBUG && ccode != 0))
         {
            Debug.println(
               (ccode == 0 ? "" : "?FAIL? ") +
               "0x" + Integer.toHexString(ccode) +
               " = ClxJNI.NWGetNWNetVersion(" +
               nwnetVersion.toString() +
               ")");
         }
         if (0 != ccode)
            throw NSIExceptionBuilder.build(ccode);

         int libVersion = nwnetVersion.intValue();
         if (DEBUG)
         {
            Debug.println("Version String = " + nwnetVersion);
            Debug.println("Version Value = 0x" + Integer.toHexString(libVersion));
         }

         // Context NAME_CACHE was fixed as of NWNet v5.3.6
         if (libVersion < new NWVersion("5.3.6").intValue())
         {
            NLM_KLUDGE_INVALID_CACHE = true;
            if (DEBUG)
               Debug.println("KLUDGE ON");
         }
      }
   }

   static public boolean isNetWareNLM()
   {
      return isNetWareNLM;
   }

   /**
    * Return integer value for conn info attrId.
    */
   static public int connIntForAttr
   (
      String infoString
   )
   {
      Object o = Natives.connInfo.get(infoString);
      if (null != o)
         return((Integer)o).intValue();
      return (-1);
   }

   /**
    * Return conn info attrId for integer value.
    */
   static public String connAttrForInt
   (
      int value
   )
   {
      Object o = Natives.connInfo.getKey(
         new Integer(value));
      if (null != o)
         return(String)o;
      return ("CONN_UNKNOWN_ATTR_ID");
   }

   /**
    * Return integer value for nativeContext info attrId.
    */
   static public int contextIntForAttr(
      String infoString)
   {
      Object o = Natives.contextInfo.get(infoString);
      if (null != o)
         return((Integer)o).intValue();
      return (-1);
   }

   /**
    * Return context info attrId for integer value.
    */
   static public String contextAttrForInt(
      int value)
   {
      Object o = Natives.contextInfo.getKey(
         new Integer(value));
      if (null != o)
         return(String)o;
      return ("DCK_UNKNOWN_ATTR_ID");
   }

   /**
    * Returns the preferred tree name.
    */
   static public String getPreferredTree()
   {
      int ccode;
      StringBuffer tree = new StringBuffer();

      ccode = ClxJNI.NWCGetPrefDsTreeName(tree);
      if (DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetPrefDsTreeName(" +
            tree.toString() +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return tree.toString();
   }

   /**
    * Returns the preferred server name.
    */
   static public String getPreferredServer()
   {
      int ccode;
      StringBuffer server = new StringBuffer();

      ccode = ClxJNI.NWCGetPrefServerName(server);
      if (DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetPrefDsServerName(" +
            server.toString() +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return server.toString();
   }

   /**
    * Creates an NDS nativeContext handle.
    */
   static public NativeContextMaster createNativeContext(
      String tree)
   throws SessionException
   {
      int ccode;
      Integer flags;
      int[] threadGroup = new int[1];
      int[] contextHandle = new int[1];

      ccode = ClxJNI.NWDSCreateContextHandle(threadGroup, contextHandle);
      if (DEBUG || OPEN_CLOSE_CONTEXT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSCreateContextHandle(" +
            "0x" + Integer.toHexString(
            threadGroup[0]) + ", " +
            "0x" + Integer.toHexString(
            contextHandle[0]) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      NativeContextMaster master = new NativeContextMaster(
         threadGroup[0],
         contextHandle[0],
         tree);

      return master;
   }

   /**
    * Duplicates an NDS nativeContext handle.
    */
   static public NativeContextDuplicate duplicateNativeContext(
      NativeContext context)
   throws SessionException
   {
      int ccode;
      int[] newContextHandle = new int[1];

      ccode = ClxJNI.NWDSDuplicateContextHandle (
         context.threadGroup,
         context.contextHandle,
         newContextHandle);
      if (DEBUG || OPEN_CLOSE_CONTEXT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSDuplicateContextHandle(" +
            "0x" + Integer.toHexString(context.threadGroup) + ", " +
            "0x" + Integer.toHexString(context.contextHandle) + ", " +
            "0x" + Integer.toHexString(
            newContextHandle[0]) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      while (context instanceof NativeContextDuplicate)
         context = ((NativeContextDuplicate)context).master;

      return(new NativeContextDuplicate(
         (NativeContextMaster)context,
         newContextHandle[0]));
   }

   /**
    * Frees an NDS nativeContext handle.
    */
   static public void freeNativeContext(
      NativeContext nativeContext)
   throws SessionException
   {
      int ccode = 0;

      synchronized (nativeContext)
      {
         try
         {
            if (-1 != nativeContext.contextHandle)
            {
      ccode = ClxJNI.NWDSFreeContext (nativeContext.threadGroup,
         nativeContext.contextHandle);
      if (DEBUG || OPEN_CLOSE_CONTEXT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSFreeContext(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) +
            ")");
      }
            }
         }
         finally
         {
            nativeContext.contextHandle = -1;
         }
      }

      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      // Free up thread group here...NativeContext...objects don't make
      // native calls
      if (nativeContext instanceof NativeContextMaster)
      {
         synchronized (nativeContext)
         {
            if (-1 != nativeContext.threadGroup)
            {
         ClxJNI.shutDownThreadGroup(nativeContext.threadGroup);
               nativeContext.threadGroup = -1;
            }
         }
      }
   }

   /**
    * Gets information from an NDS nativeContext handle.
    */
   static public Object getContextInfo(
      NativeContext nativeContext,
      int key)
   throws SessionException
   {
      int ccode;
      NDSContextInfo contextInfo = new NDSContextInfo();

      ccode = ClxJNI.NWDSGetContext (nativeContext.threadGroup,
         nativeContext.contextHandle, key, contextInfo);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGetContext(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            contextAttrForInt(key) + ", " +
            getString(contextInfo.getValue()) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return(contextInfo.getValue ());
   }

   /**
    * Sets information in an NDS nativeContext handle.
    */
   static public void setContextInfo(
      NativeContext nativeContext,
      int key,
      Object value)
   throws SessionException
   {
      int ccode;

      ccode = ClxJNI.NWDSSetContext (nativeContext.threadGroup,
         nativeContext.contextHandle, key, value);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSSetContext(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            contextAttrForInt(key) + ", " +
            getString(value) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Login on an NDS nativeContext handle.
    */
   static public void loginContext(
      NativeContext nativeContext,
      String userName,
      String password,
      boolean extendedCharPasswordFlag)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSLogin(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         0,
         userName,
         password,
         0,
         extendedCharPasswordFlag);

      if (DEBUG || PASSWORD_DEBUG || LOGIN_LOGOUT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSLogin(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0, " +
            userName + ", " +
            password + ", " +
            "0," +
            extendedCharPasswordFlag +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   static public void loginContextNAS(
      NativeContext nativeContext,
      NdsIdentity ident,
      String userName,
      String password,
      boolean extendedCharPasswordFlag)
   throws SessionException
   {
      int ccode = ClxJNI.NASLogin(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         userName,
         password,
         ident,
         extendedCharPasswordFlag);

      if (DEBUG || PASSWORD_DEBUG || LOGIN_LOGOUT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NASLogin(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            userName + ", " +
            password + ", " +
            ident + ", " +
            extendedCharPasswordFlag +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Login on an NDS nativeContext handle.
    */
   static public void loginAsServiceContext(
      NativeContext nativeContext,
      String serviceName)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSLoginAsService(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         0,
         serviceName,
         0);

      if (DEBUG || PASSWORD_DEBUG || LOGIN_LOGOUT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSLoginAsService(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0, " +
            serviceName + ", " +
            "0" +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }


   /**
    * Logout on an NDS nativeContext handle.
    */
   static public void logoutContext(
      NativeContext nativeContext)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSLogout(
         nativeContext.threadGroup,
         nativeContext.contextHandle);

      if (DEBUG || LOGIN_LOGOUT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSLogout(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   static public void logoutContextNAS(NativeContext nativeContext, NdsIdentity ident)
   throws SessionException
   {
      int ccode = ClxJNI.NASLogout(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         ident.getNMASID());

      if (DEBUG || LOGIN_LOGOUT_DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NASLogout(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            ident.getNMASID() +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Authenticate a connection using a nativeContext handle.
    */
   static public void authenticateConn(
      NativeContext nativeContext,
      int connHandle)
   throws SessionException
   {
      if (DEBUG)
      {
         Debug.println("Natives.authenticateConn(): Logging in DS connection");
      }
      int ccode = ClxJNI.NWDSAuthenticateConn(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         connHandle);

      if (DEBUG || CONTEXT_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSAuthenticateConn(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0x" + Integer.toHexString(connHandle) + ", " +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Unauthenticate a connection using a nativeContext handle.
    */
   static public void unauthenticateConn(
      NativeContext nativeContext,
      int connHandle)
   throws SessionException
   {
      if (DEBUG)
      {
         Debug.println(
            "Natives.unauthenticateConn(): Logging out DS connection");
      }
      // NOTE: Need new NDS function here...
      closeConnection(connHandle);
   }

   /**
    * Returns true if the nativeContext handle can be used to authenticate a
    * connection.
    */
   static public boolean canDSAuthenticate(
      NativeContext nativeContext)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSCanDSAuthenticate(
         nativeContext.threadGroup,
         nativeContext.contextHandle);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSCanDSAuthenticate(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            ")");
      }

      if (1 == ccode)
         return (true);

      return (false);
   }

   /**
    * Returns true if the nativeContext handle can be used to authenticate a
    * connection.
    */
   static public boolean specialIsDSAuthenticated(
      NativeContext nativeContext)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSSpecialIsDSAuthenticated(
         nativeContext.threadGroup,
         nativeContext.contextHandle);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSSpecialIsDSAuthenticated(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) +
            ")");
      }

      if (1 == ccode)
         return (true);

      return (false);
   }

   /*
    * Try open conn by address.  Return 0 if address invalid.
    * Throw exception if domains don't match.
    */
   static public void verifyConnectionDomain(
      int handle,
      String domainName,
      int nameFormat,
      Address addr)
   throws SessionException
   {
      int domainType;

      if (NAME_FORMAT_NDS_TREE == nameFormat)
         domainType = CONN_INFO_TREE_NAME;
      else
         domainType = CONN_INFO_SERVER_NAME;

      String newName = getConnInfoString(
         handle,
         0,
         domainType);

      if(DEBUG)
         Debug.println("Comparing: " + domainName + " to: " + newName);
      if (!newName.equals(DomainName.stripQualifiers(domainName)))
      {
         if (null == addr)
            throw new DomainNameMismatchException(
               domainName,
               newName);
         else
            throw new DomainNameMismatchException(
               domainName,
               newName,
               addr);
      }
   }

   /**
    * Open a connection by address, returns handle.
    */
   static public int openConnection(
      Address addr,
      String domainName,
      int nameFormat,
      int mode)
   throws SessionException
   {
      NWLong lHandle = new NWLong(0);
      int ccode = ClxJNI.NWCOpenConnByAddress(
         mode,
         addr,
         lHandle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCOpenConnByAddress(" +
            "0x" + Integer.toHexString(lHandle.getIntValue()) +
            ")");
      }
      //Debug.dumpException(new Exception("BOGUS TRACKING EXCEPTION").fillInStackTrace());
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      int handle = lHandle.getIntValue();

      try
      {
         verifyConnectionDomain(
            handle,
            domainName,
            nameFormat,
            null);
      }
      catch (DomainNameMismatchException e)
      {
         closeRefByHandle(handle);
         throw e;
      }

      // Try to keep things licensed...kludge required for NT requester
      // Okay to ignore ccode
      if (NT_KLUDGE)
      {
         if ((mode & OPEN_LICENSED) > 0)
         {
            ClxJNI.NWCLicenseConn(handle);
            if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
            {
               Debug.println(
                  (ccode == 0 ? "" : "?FAIL? ") +
                  "0x" + Integer.toHexString(ccode) +
                  " = ClxJNI.NWCLicenseConn(" +
                  "0x" + Integer.toHexString(handle) +
                  ")");
            }
         }
      }

      return (handle);
   }

   /**
    * Open a default connection, returns handle.
    */
   static public int openConnection()
   throws SessionException
   {
      NWLong lHandle = new NWLong(0);
      int ccode = ClxJNI.NWCOpenConnDefault(
         lHandle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCOpenConnDefault(" +
            "0x" + Integer.toHexString(lHandle.getIntValue()) +
            ")");
      }
      //Debug.dumpException(new Exception("BOGUS TRACKING EXCEPTION").fillInStackTrace());
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      int handle = lHandle.getIntValue();

      // Try to keep things licensed...kludge required for NT requester
      // Okay to ignore ccode
      if (NT_KLUDGE)
      {
         ClxJNI.NWCLicenseConn(handle);
         if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
         {
            Debug.println(
               (ccode == 0 ? "" : "?FAIL? ") +
               "0x" + Integer.toHexString(ccode) +
               " = ClxJNI.NWCLicenseConn(" +
               "0x" + Integer.toHexString(handle) +
               ")");
         }
      }

      return (handle);
   }

   /**
    * Open a connection using a name.
    */
   static public int openConnection(
      Address addr,
      int startConnHandle,
      String domainName,
      int nameFormat,
      int mode)
   throws SessionException
   {
      int handle = -1;
      if (null != addr)
      {
         handle = Natives.openConnection(
            addr,
            domainName,
            nameFormat,
            mode);
      }
      if (-1 == handle)
      {
         // Address failed, try by name
         handle = openConnection(
            startConnHandle,
            domainName,
            nameFormat,
            mode);
      }

      return handle;
   }

   /**
    * Open a connection using a name.
    */
   static public int openConnection(
      int startConnHandle,
      String domainName,
      int nameFormat,
      int mode)
   throws SessionException
   {
      NWLong connHandle = new NWLong(0);
      int ccode = ClxJNI.NWCOpenConnByName(
         startConnHandle,
         domainName,
         nameFormat,
         mode,
         connHandle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCOpenConnByName(" +
            "0x" + Integer.toHexString(startConnHandle) + ", " +
            domainName + ", " +
            nameFormat + ", " +
            mode + ", " +
            "0x" + Integer.toHexString(
            connHandle.getIntValue()) +
            ")");
      }
      //Debug.dumpException(new Exception("BOGUS TRACKING EXCEPTION").fillInStackTrace());
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      int handle = connHandle.getIntValue();

      if (NLM_KLUDGE_BADTREE || NLM_KLUDGE_BADSERVER)
      {
         try
         {
            verifyConnectionDomain(
               handle,
               domainName,
               nameFormat,
               null);
         }
         catch (DomainNameMismatchException e)
         {
            closeRefByHandle(handle);
            throw e;
         }
      }

      // Try to keep things licensed...kludge required for NT requester
      // Okay to ignore ccode
      if (NT_KLUDGE)
      {
         if ((mode & OPEN_LICENSED) > 0)
         {
            ClxJNI.NWCLicenseConn(handle);
            if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
            {
               Debug.println(
                  (ccode == 0 ? "" : "?FAIL? ") +
                  "0x" + Integer.toHexString(ccode) +
                  " = ClxJNI.NWCLicenseConn(" +
                  "0x" + Integer.toHexString(handle) +
                  ")");
            }
         }
      }

      return (handle);
   }

   /**
    * Open a connection using an NDS name.
    */
   static public int openConnection(
      NativeContext nativeContext,
      String domainName)
   throws SessionException
   {
      NWLong connHandle = new NWLong(0);
      int ccode = ClxJNI.NWDSOpenConnToNDSServer(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         domainName,
         connHandle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSOpenConnToNDSServer(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            domainName + ", " +
            "0x" + Integer.toHexString(
            connHandle.getIntValue()) +
            ")");
      }
      //Debug.dumpException(new Exception("BOGUS TRACKING EXCEPTION").fillInStackTrace());
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      int handle = connHandle.getIntValue();

      // Try to keep things licensed...kludge required for NT requester
      // Okay to ignore ccode
      if (NT_KLUDGE)
      {
         ClxJNI.NWCLicenseConn(handle);
         if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
         {
            Debug.println(
               (ccode == 0 ? "" : "?FAIL? ") +
               "0x" + Integer.toHexString(ccode) +
               " = ClxJNI.NWCLicenseConn(" +
               "0x" + Integer.toHexString(handle) +
               ")");
         }
      }

      return (handle);
   }

   /**
    * Open a connection using a connection reference.
    */
   static public int openConnection(
      int reference)
   throws SessionException
   {
      NWLong lHandle = new NWLong(0);
      int ccode = ClxJNI.NWCOpenConnByReference(
         reference,
         OPEN_LICENSED,
         lHandle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCOpenConnByReference(" +
            "0x" + Integer.toHexString(reference) + ", " +
            "LICENSED, " +
            "0x" + Integer.toHexString(
            lHandle.getIntValue()) +
            ")");
      }
      //Debug.dumpException(new Exception("BOGUS TRACKING EXCEPTION").fillInStackTrace());
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      int handle = lHandle.getIntValue();

      // Try to keep things licensed...kludge required for NT requester
      // Okay to ignore ccode
      if (NT_KLUDGE)
      {
         ClxJNI.NWCLicenseConn(handle);
         if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
         {
            Debug.println(
               (ccode == 0 ? "" : "?FAIL? ") +
               "0x" + Integer.toHexString(ccode) +
               " = ClxJNI.NWCLicenseConn(" +
               "0x" + Integer.toHexString(handle) +
               ")");
         }
      }

      return (handle);
   }

   /**
    */
   static public int getConnectionReference(
      int handle)
   throws SessionException
   {
      int reference;
      reference = getConnInfoLong(
         handle,
         0,
         Natives.CONN_INFO_CONN_REF);

      return (reference);
   }

   /**
    * Close a connection handle.
    */
   static public void closeConnection(
      int handle)
   throws SessionException
   {
      int ccode = ClxJNI.NWCCloseConn(handle);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCCloseConn(" +
            "0x" + Integer.toHexString(handle) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Makes a connection stick around.
    */
   static public void keep(
      int handle)
   throws SessionException
   {
      int ccode = ClxJNI.NWCMakeConnPermanent(handle);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCMakeConnPermanent(" +
            "0x" + Integer.toHexString(handle) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Close a connection reference (system-level close).
    */
   static public void closeRef(
      int ref)
   throws SessionException
   {
      int ccode = ClxJNI.NWCSysCloseConnRef(ref);
      if (DEBUG || OPEN_CLOSE_CONN_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCSysCloseConnRef(" +
            "0x" + Integer.toHexString(ref) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Close a connection reference (system-level close).
    */
   static public void closeRefByHandle(
      int handle)
   throws SessionException
   {
      closeRef(
         getConnectionReference(handle));
   }

   /**
    * Get connection info in Long format.
    */
   static public int getConnInfoLong(
      int handle,
      int reference,
      int level)
   throws SessionException
   {
      boolean kludge = false;
      if (NLM_KLUDGE_NO_USER_FROM_REF)
      {
         if (CONN_INFO_USER_ID == level)
         {
            if (0 == handle)
            {
               if (DEBUG)
               {
                  Debug.println("Natives.getConnInfoLong(): Kludge started...");
               }
               kludge = true;
               handle = openConnection(reference);
            }
         }
      }
      NWLong lValue = new NWLong();
      int ccode = ClxJNI.NWCGetConnInfoLong(
         handle,
         reference,
         level,
         lValue);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetConnInfoLong(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(reference) + ", " +
            connAttrForInt(level) + ", " +
            "0x" + Integer.toHexString(
            lValue.getIntValue()) +
            ")");
      }
      if (NLM_KLUDGE_NO_USER_FROM_REF)
      {
         if (kludge)
         {
            closeConnection(handle);
            if (DEBUG)
            {
               Debug.println("Natives.getConnInfoLong(): ...Kludge ended.");
            }
         }
      }
      if (0 != ccode)
      {
         throw NSIExceptionBuilder.build(ccode);
      }

      return (lValue.getIntValue());
   }

   /**
    * Get connection info in String format.
    */
   static public String getConnInfoString(
      int handle,
      int reference,
      int level)
   throws SessionException
   {
      boolean kludge = false;
      if (CONN_INFO_TREE_NAME == level)
      {
         if (NLM_KLUDGE_NO_TREE_FROM_REF)
         {
            if (0 == handle)
            {
               if (DEBUG)
               {
                  Debug.println("Natives.getConnInfoString(): Kludge started...");
               }
               kludge = true;
               handle = openConnection(reference);
            }
         }
      }
      StringBuffer sValue = new StringBuffer();
      int ccode = ClxJNI.NWCGetConnInfoString(
         handle,
         reference,
         level,
         sValue);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetConnInfoString(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(reference) + ", " +
            connAttrForInt(level) + ", " +
            sValue +
            ")");
      }
      if (CONN_INFO_TREE_NAME == level ||
          CONN_INFO_SERVER_NAME == level)
      {
         // Always upper cased
         sValue = new StringBuffer(sValue.toString().toUpperCase());
         if (CONN_INFO_TREE_NAME == level)
         {
            sValue = new StringBuffer(DomainName.strip(sValue.toString()));
            if (NLM_KLUDGE_NO_TREE_FROM_REF)
            {
               if (kludge)
               {
                  closeConnection(handle);
                  if (DEBUG)
                  {
                     Debug.println("Natives.getConnInfoString(): ...Kludge ended.");
                  }
               }
            }
         }
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return (sValue.toString());
   }

   /**
    * Get connection info in Address format.
    */
   static public Address getConnInfoAddress(
      int handle,
      int reference)
   throws SessionException
   {
      return (getConnInfoAddress(handle, reference, Address.TYPE_NONE));
   }

   /**
    * Get connection info in Address format.
    */
   static public Address getConnInfoAddress(
      int handle,
      int reference,
      int type)
   throws SessionException
   {
      Address aValue = new Address(type, null);
      int ccode = ClxJNI.NWCGetConnInfoAddress(
         handle,
         reference,
         aValue);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetConnInfoAddress(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(reference) + ", " +
            aValue.toString() + ", " +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return (new Address(
         aValue.getType(),
         aValue.getAddress()));
   }

   /**
    * Get connection info in Version format.
    */
   static public Version getConnInfoVersion(
      int handle,
      int reference,
      int level)
   throws SessionException
   {
      NWVersion v = new NWVersion();
      int ccode = ClxJNI.NWCGetConnInfoVersion(
         handle,
         reference,
         level,
         v);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCGetConnInfoVersion(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(reference) + ", " +
            connAttrForInt(level) + ", " +
            Long.toString (
            v.getMajor()) + "." +
            Long.toString (
            v.getMinor()) + "." +
            Long.toString (
            v.getRevision()) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return (new Version(v.majorVersion, v.minorVersion, v.revision));
   }

   static public int scanConnInfoString(
      NWLong   scanIterator,
      int      scanLevel,
      String   scanString,
      int      scanMode)
   {
      NWLong connRef = new NWLong(-1);

      int ccode = ClxJNI.NWCScanConnInfoString(
         scanIterator,
         scanLevel,
         scanString,
         scanMode,
         connRef);
      if (SCAN_DEBUG || DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCScanConnInfoString(" +
            "0x" + Integer.toHexString(
            scanIterator.getIntValue()) + ", " +
            connAttrForInt(scanLevel) +
            "(" + Integer.toString(scanLevel) + "), " +
            scanString + ", " +
            scanMode + ", " +
            "0x" + Integer.toHexString(
            connRef.getIntValue()) +
            ")");
      }
      if (0 != ccode)
      {
         switch (ccode)
         {
            case 0:
            case com.novell.service.jncp.ClientErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_INVALID_CONNECTION:
               break;
            default:
               throw NSIExceptionBuilder.build(ccode);
         }
      }
      return connRef.getIntValue();
   }

   static public int scanConnInfoLong(
      NWLong   scanIterator,
      int      scanLevel,
      int      scanValue,
      int      scanMode)
   {
      NWLong connRef = new NWLong(-1);

      int ccode = ClxJNI.NWCScanConnInfoLong(
         scanIterator,
         scanLevel,
         new NWLong(scanValue),
         scanMode,
         connRef);
      if (SCAN_DEBUG || DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCScanConnInfoLong(" +
            "0x" + Integer.toHexString(
            scanIterator.getIntValue()) + ", " +
            connAttrForInt(scanLevel) +
            "(" + Integer.toString(scanLevel) + "), " +
            scanValue + ", " +
            scanMode + ", " +
            "0x" + Integer.toHexString(
            connRef.getIntValue()) +
            ")");
      }
      if (0 != ccode)
      {
         switch (ccode)
         {
            case 0:
            case com.novell.service.jncp.ClientErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_INVALID_CONNECTION:
               break;
            default:
               throw NSIExceptionBuilder.build(ccode);
         }
      }
      return connRef.getIntValue();
   }

   static public int scanConnInfoVersion(
      NWLong      scanIterator,
      int         scanLevel,
      NWVersion   scanVersion,
      int         scanMode)
   {
      NWLong connRef = new NWLong(-1);

      int ccode = ClxJNI.NWCScanConnInfoVersion(
         scanIterator,
         scanLevel,
         scanVersion,
         scanMode,
         connRef);
      if (SCAN_DEBUG || DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWCScanConnInfoVersion(" +
            "0x" + Integer.toHexString(
            scanIterator.getIntValue()) + ", " +
            connAttrForInt(scanLevel) +
            "(" + Integer.toString(scanLevel) + "), " +
            scanVersion + ", " +
            scanMode + ", " +
            "0x" + Integer.toHexString(
            connRef.getIntValue()) +
            ")");
      }
      if (0 != ccode)
      {
         switch (ccode)
         {
            case 0:
            case com.novell.service.jncp.ClientErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_NO_MORE_ENTRIES:
            case com.novell.service.jncp.ServerErrors.NWE_INVALID_CONNECTION:
               break;
            default:
               throw NSIExceptionBuilder.build(ccode);
         }
      }
      return connRef.getIntValue();
   }


   /**
    * Login on a Bindery connection handle.
    */
   static public void loginConnection(
      int handle,
      String userName,
      String password)
   throws SessionException
   {
      int ccode = ClxJNI.NWLoginToFileServer(
         handle,
         userName,
         Natives.OT_USER,
         password);
      if (DEBUG || LOGIN_LOGOUT_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWLoginToFileServer(" +
            "0x" + Integer.toHexString(handle) + ", " +
            userName + ", " +
            "OT_USER, " +
            password +
            ")");
      }
      if (0 != ccode)
      {
         NSIException e = NSIExceptionBuilder.build(ccode);

         if (0x89fc == ccode)
            throw new InvalidUserNameException(userName, e);

         throw e;
      }

   }

   /**
    * Logout from a Bindery connection handle.
    */
   static public void logoutConnection(
      int handle)
   throws SessionException
   {
      int ccode = ClxJNI.NWLogoutFromFileServer(handle);
      if (DEBUG || LOGIN_LOGOUT_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWLogoutFromFileServer(" +
            "0x" + Integer.toHexString(handle) +
            ")");
      }

      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    * Get a user name from a user id via a connection handle.
    */
   static public String getUserName(
      int handle,
      int userId)
   throws SessionException
   {
      StringBuffer name = new StringBuffer();
      int ccode = CalJNI.NWGetObjectName(
         handle,
         userId,
         name,
         new IntegerBuffer(OT_USER));
      if (DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = CalJNI.NWGetObjectName(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(userId) + ", " +
            name + ", " +
            "OT_USER" +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return (name.toString());
   }

   static public String getContextName(
      NativeContext nativeContext)
   {
      StringBuffer name = new StringBuffer();
      int ccode = ClxJNI.NWDSWhoami(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         name);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSWhoami(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            name +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);

      return (name.toString());

   }

   /**
    */
   static public void changePasswordConnection(
      int handle,
      String userName,
      String oldPassword,
      String newPassword)
   throws SessionException
   {
      int ccode = ClxJNI.NWChangeObjectPassword(
         handle,
         userName,
         Natives.OT_USER,
         oldPassword,
         newPassword);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWChangeObjectPassword(" +
            "0x" + Integer.toHexString(handle) + ", " +
            userName + ", " +
            "OT_USER, " +
            oldPassword + ", " +
            newPassword +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    */
   static public void setPasswordConnection(
      int handle,
      String objName,
      int objType,
      String password)
   throws SessionException
   {
      int ccode = ClxJNI.NWChangeObjectPassword(
         handle,
         objName,
         objType,
         "",
         password);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWChangeObjectPassword(" +
            "0x" + Integer.toHexString(handle) + ", " +
            objName + ", " +
            objType + ", " +
            " , " +
            password +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }


   /**
    */
   static public void verifyPasswordConnection(
      int handle,
      String objName,
      int objType,
      String password)
   throws SessionException
   {
      int ccode = ClxJNI.NWVerifyObjectPassword(
         handle,
         objName,
         objType,
         password);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWVerifyObjectPassword(" +
            "0x" + Integer.toHexString(handle) + ", " +
            objName + ", " +
            objType + ", " +
            password +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    */
   static public void changePasswordContext(
      NativeContext nativeContext,
      String userName,
      String oldPassword,
      String newPassword,
      boolean extendedCharPasswordFlag)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSChangeObjectPassword(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         0,
         userName,
         oldPassword,
         newPassword,
         extendedCharPasswordFlag);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSChangeObjectPassword(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0, " +
            userName + ", " +
            oldPassword + ", " +
            newPassword + ", " +
            extendedCharPasswordFlag +
            ")");
      }

      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    */
   static public void setPasswordContext(
      NativeContext nativeContext,
      String objName,
      String password,
      boolean extendedCharPasswordFlag)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSGenerateObjectKeyPair(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         objName,
         password,
         0,
         extendedCharPasswordFlag);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGenerateObjectKeyPair(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            objName + ", " +
            password + ", " +
            "0, " +
            extendedCharPasswordFlag +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   /**
    */
   static public void verifyPasswordContext(
      NativeContext nativeContext,
      String objName,
      String password,
      boolean extendedCharPasswordFlag)
   throws SessionException
   {
      int ccode = ClxJNI.NWDSVerifyObjectPassword(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         0,
         objName,
         password,
         extendedCharPasswordFlag);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSVerifyObjectPassword(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0" +
            objName + ", " +
            password + ", " +
            extendedCharPasswordFlag +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }


   static public void contextPing(
      NativeContext nativeContext)
   {
      int ccode = NetJNI.NWDSResolveName(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         "[root]",
         null,
         null);

      if (DEBUG || CONTEXT_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = NetJNI.NWDSResolveName(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) +
            ")");
      }
      if (0!= ccode)
         throw NSIExceptionBuilder.build(ccode);
   }

   static private String getString(
      Object object)
   {
      if (object instanceof Integer)
      {
         int i = ((Integer)object).intValue();
         return ("0x" + Integer.toHexString(i) + "(" + i + ")");
      }
      if (object instanceof Version)
      {
         Version v = (Version)object;
         return
            (Long.toString (v.majorVersion) + "." +
            Long.toString (v.minorVersion) + "." +
            Long.toString (v.revision));
      }
      return (object.toString());
   }

   static protected String getOwnerString(
      int owner)
   {
      switch (owner)
      {
         case OWNER_NONE:
            return (OWNER_NONE_STRING);
         case OWNER_BINDERY:
            return (OWNER_BINDERY_STRING);
         case OWNER_NDS:
            return (OWNER_NDS_STRING);
         default:
            return ("UNKNOWN");
      }
   }

   static public Integer getDSNLMBuild(
      int handle)
   throws SessionException
   {
      IntegerBuffer ver = new IntegerBuffer();
      int ccode = ClxJNI.NWDSGetDSVerInfo(
         handle,
         null,
         ver,
         null);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGetDSVerInfo(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "(null)" + ", " +
            "0x" + Integer.toHexString(ver.intValue()) + ", " +
            "(null)" +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
      return new Integer(ver.intValue());
   }

   static public Integer getDSServerRootDepth(
      int handle)
   throws SessionException
   {
      IntegerBuffer depth = new IntegerBuffer();
      int ccode = ClxJNI.NWDSGetDSVerInfo(
         handle,
         depth,
         null,
         null);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGetDSVerInfo(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "0x" + Integer.toHexString(depth.intValue()) + ", " +
            "(null)" + ", " +
            "(null)" +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
      return new Integer(depth.intValue());
   }

   static public Boolean isMasterReplica(
      int handle)
   throws SessionException
   {
      IntegerBuffer flags = new IntegerBuffer();
      int ccode = ClxJNI.NWDSGetDSVerInfo(
         handle,
         null,
         null,
         flags);
      if (DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGetDSVerInfo(" +
            "0x" + Integer.toHexString(handle) + ", " +
            "(null)" + ", " +
            "(null)" + ", " +
            "0x" + Integer.toHexString(flags.intValue()) +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
      return new Boolean(0 != (flags.intValue() & 0x00000001));
   }

   static public String getServerDN(
      NativeContext nativeContext,
      int handle)
   throws SessionException
   {
      StringBuffer serverDN = new StringBuffer();
      int ccode = ClxJNI.NWDSGetServerDN(
         nativeContext.threadGroup,
         nativeContext.contextHandle,
         handle,
         serverDN);
      if (DEBUG || CONTEXT_DEBUG || CONNECTION_DEBUG || (ERRORS_DEBUG && ccode != 0))
      {
         Debug.println(
            (ccode == 0 ? "" : "?FAIL? ") +
            "0x" + Integer.toHexString(ccode) +
            " = ClxJNI.NWDSGetServerDN(" +
            "0x" + Integer.toHexString(nativeContext.threadGroup) + ", " +
            "0x" + Integer.toHexString(nativeContext.contextHandle) + ", " +
            "0x" + Integer.toHexString(handle) + ", " +
            serverDN +
            ")");
      }
      if (0 != ccode)
         throw NSIExceptionBuilder.build(ccode);
      return serverDN.toString();
   }
}

