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

  $Archive: /njcl/src/com/novell/service/session/nds/NDSTreeSessionState.java $
  $Revision: 16 $
  $Modtime: 4/07/98 5:45p $

  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.

****************************************************************************/
//#define DEBUG_OUT
//#define DEBUG_SEM

#include <sys_api.h>
#include <ntypes.h>
#include <nwbindry.h>
#include <nwserver.h>
#include <nwapidef.h>
#include <nwclxcon.h>
#include <nwconnec.h>
#include <nwmsg.h>
#include <nwcalls.h>
#include <nwnet.h>
#include <nwerror.h>
#include <nwlocale.h>

#include "loginAsService.h"

#ifdef N_PLAT_NLM

#include <nwadv.h>
#include <nwenvrn.h>
#include <nwdsdc.h>
#include <nwdsasa.h>
#include <nwdserr.h>
#include <nlm\nwconn.h>
#include <nlm\nds.h>

#include <string.h>

#include <stdio.h>
#include <nwconio.h>

#include <assert.h>

#include <signal.h>

#include <nwdebug.h>
#include <nwthread.h>
#include <errno.h>

void sysThreadGCOK(int);

#endif

#include "jncp.h"
#include "nlmsem.h"
#include "xjni.h"

#ifdef N_PLAT_NLM
#define PRINTF ConsolePrintf
#define THREADSWITCH ThreadSwitch()
#else
#ifdef DEBUG_OUT
#include <stdio.h>
#endif
#define PRINTF printf
#define THREADSWITCH
#endif

#define PWD_UTF8_STRING 2
const char* extCharFailMessage = "extCharPassFuncTable = NULL";
char *ExtCharPassSymbols[4] = {"NWDSChangePwdEx", "NWDSGenerateKeyPairEx", "NWDSLoginEx", "NWDSVerifyPwdEx"};
ExtCharPassFunctionTable extCharPassFuncTable;

#ifdef N_PLAT_NLM
char *NASSymbols[2] = {"NASLoginWithContext", "NASLogoutWithContext"};
NASFunctionTable nasFuncTable;
nint32 NLMHandle;
#else
HINSTANCE inst;
#endif

void __InitNASAndExFunctions(void)
{
   #ifdef N_PLAT_NLM
   NLMHandle = GetNLMHandle();
   nasFuncTable.login = (NASLoginWithContext_fp)ImportSymbol(NLMHandle, NASSymbols[0]);
   nasFuncTable.logout = (NASLogout_fp)ImportSymbol(NLMHandle, NASSymbols[1]);
   extCharPassFuncTable.change = (NWDSChangePwdEx_fp)ImportSymbol(NLMHandle, ExtCharPassSymbols[0]);
   extCharPassFuncTable.generate = (NWDSGenerateKeyPairEx_fp)ImportSymbol(NLMHandle, ExtCharPassSymbols[1]);
   extCharPassFuncTable.login = (NWDSLoginEx_fp)ImportSymbol(NLMHandle, ExtCharPassSymbols[2]);
   extCharPassFuncTable.verify = (NWDSVerifyPwdEx_fp)ImportSymbol(NLMHandle, ExtCharPassSymbols[3]);
   #else
   inst = LoadLibrary("netwin32.dll");
   extCharPassFuncTable.change = (NWDSChangePwdEx_fp)GetProcAddress(inst, ExtCharPassSymbols[0]);
   extCharPassFuncTable.generate = (NWDSGenerateKeyPairEx_fp)GetProcAddress(inst, ExtCharPassSymbols[1]);
   extCharPassFuncTable.login = (NWDSLoginEx_fp)GetProcAddress(inst, ExtCharPassSymbols[2]);
   extCharPassFuncTable.verify = (NWDSVerifyPwdEx_fp)GetProcAddress(inst, ExtCharPassSymbols[3]);
   #endif
}

void __CleanupNASAndExFunctions(void)
{
   #ifdef N_PLAT_NLM
   if(nasFuncTable.login != NULL)
      UnimportSymbol(NLMHandle, NASSymbols[0]);
   if(nasFuncTable.logout != NULL)
      UnimportSymbol(NLMHandle, NASSymbols[1]);
   if(extCharPassFuncTable.change != NULL)
      UnimportSymbol(NLMHandle, ExtCharPassSymbols[0]);
   if(extCharPassFuncTable.generate != NULL)
      UnimportSymbol(NLMHandle, ExtCharPassSymbols[1]);
   if(extCharPassFuncTable.login != NULL)
      UnimportSymbol(NLMHandle, ExtCharPassSymbols[2]);
   if(extCharPassFuncTable.verify != NULL)
      UnimportSymbol(NLMHandle, ExtCharPassSymbols[3]);
   #else
   FreeLibrary(inst);
   #endif
}

#define XPLATPKG         "com/novell/service/session/xplat/"
#define PWD_UTF8_STRING 2

#define __MAX(a,b) ((a) > (b) ? (a): (b))
#define MAX_PASSWORD_LEN   129

#ifdef N_PLAT_NLM

N_GLOBAL_FUNC_C(nint) JNCPprintf
(
   const nstr N_FAR *fmt,
   ...
)
{
   int res;
   va_list ap;
   FILE N_FAR *fp;

   va_start(ap, fmt);
   fp = fopen ("SYS:\JNCP.LOG", "a+");

   res = vfprintf (fp, fmt, ap);

   fclose (fp);

   va_end(ap);
   return (res);

} /* JNCPprintf () */

#else

#  define JNCPprintf   printf

#endif


N_GLOBAL_FUNC(void)
 __InitNWCalls(void)
{

   #ifdef DEBUG_OUT
      #ifdef N_PLAT_NLM
//         EnterDebugger();
      #endif
      PRINTF("NWCallsInit(), compiled: %s %s\n", __DATE__, __TIME__);
      THREADSWITCH;
   #endif
   NWCallsInit(NULL, NULL);
}

N_GLOBAL_FUNC(void)
__CleanupNWCalls(void)
{
   #ifdef DEBUG_OUT
      PRINTF("NWCallsTerm()\n");
      THREADSWITCH;
   #endif

   NWCallsTerm(NULL);
}

#ifdef N_PLAT_NLM

N_GLOBAL_VAR _globalThreadGroup;

N_GLOBAL_FUNC(NWDSCCODE)
wrapContext
(
   pnint saveThreadGroup,
   nint  threadGroup,
   NWDSContextHandle context
)
{
   nint temp = SetThreadGroupID(threadGroup);

   if (-1 == temp)
      return (-1);

#ifdef DEBUG_OUT
   PRINTF("thread group: %d <-> %d\n", temp, threadGroup);
   THREADSWITCH;
#endif

   *saveThreadGroup = temp;

   return (0);
}

N_GLOBAL_FUNC(NWDSCCODE) unwrapContext(nint saveThreadGroup)
{
   SetThreadGroupID(saveThreadGroup);

   return (0);
}

N_GLOBAL_FUNC(void) __NewThreadGroupStarter(void *ptr)
{
   pStarterStruct st = (pStarterStruct) ptr;

   SetThreadGroupID(st->threadGroup);
   SignalLocalSemaphore(st->sem);
}

N_GLOBAL_FUNC(void)
 __InitGlobalThreadGroup(void)
{
   _globalThreadGroup = GimmeNewThreadGroupAndUserPlease();
}

N_GLOBAL_FUNC(int)
GimmeNewThreadGroupAndUserPlease(void)
{
   StarterStruct starter;
   nint newThreadGroup;
   starter.sem = OpenLocalSemaphore(0);
   starter.threadGroup = GetThreadGroupID();

   newThreadGroup = BeginThreadGroup(__NewThreadGroupStarter, NULL, 0,
         &starter);

   WaitOnLocalSemaphore(starter.sem);
   CloseLocalSemaphore(starter.sem);

   SetThreadGroupID(newThreadGroup);
   NWDSCreateUser();
   SetThreadGroupID(starter.threadGroup);
   return newThreadGroup;
}

N_GLOBAL_FUNC(void)
__CleanupGlobalThreadGroup(void)
{
   DoneWithThisThreadGroupAndUserThankYou(_globalThreadGroup);
}

N_GLOBAL_FUNC(void)
DoneWithThisThreadGroupAndUserThankYou(nint threadGroup)
{
   shutDownThreadGroup(threadGroup);
}

N_GLOBAL_FUNC(int)
shutDownThreadGroup(int threadGroup)
{
   StarterStruct starter;
   nint tID;

   if (_usingNetWareFunctions)
   {
      // kEnterNetWare()
      (*_netwareFunctions[0])();
   }

   starter.sem = OpenLocalSemaphore(0);
   starter.threadGroup = (nint) threadGroup;

   tID = BeginThread(__NewThreadGroupStarter, NULL, 0, &starter);

   WaitOnLocalSemaphore(starter.sem);
   CloseLocalSemaphore(starter.sem);

   if (_usingNetWareFunctions)
   {
      // kExitNetWare
      (*_netwareFunctions[1])();
   }

   if (-1 == tID)
      return (-1);
   else
      return 0;
}
#endif

static NWDSCCODE throwException(JNIEnv *env, char* exceptionClass, char* message)
{
   jclass class = (*env)->FindClass(env, exceptionClass);
   if(NULL == class)
      goto FATAL_ERROR;
   if(!(*env)->ThrowNew(env, class, message))
      return -1; //Exception was thrown so return code is irrelevant.
         
   FATAL_ERROR:
   (*env)->FatalError(env, "FATAL");
   return 0; //This line will never be hit. FatalError() does not return.
}

/* *
 * NWDSCreateContextHandle.
 *
 * Differs from NWDSCreateContext in that the return code may now specify
 * codes indicative of the reason for the failure.
 */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSCreateContextHandle
(
   nint *newThreadGroup,
   NWDSContextHandle N_FAR *newHandle
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   StarterStruct starter;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSCreateContextHandle\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM

   starter.sem = OpenLocalSemaphore(0);
   starter.threadGroup = GetThreadGroupID();

   *newThreadGroup = BeginThreadGroup(__NewThreadGroupStarter, NULL, 0,
         &starter);

   WaitOnLocalSemaphore(starter.sem);
   CloseLocalSemaphore(starter.sem);

   if (-1 == *newThreadGroup)
      return (errno);

#ifdef DEBUG_OUT
   PRINTF("new thread group: %d <-> %d\n", starter.threadGroup,
         *newThreadGroup);

   THREADSWITCH;
#endif

   SetThreadGroupID(*newThreadGroup);
   NWDSSetCurrentUser(0);
#endif

   ccode = NWDSCreateContextHandle(newHandle);

   #ifdef DEBUG_OUT
      PRINTF("XX: 0x%X = xNWDSCreateContextHandle; new context = 0x%X\n",
         ccode,
         *newHandle);
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   unwrapContext(starter.threadGroup);
#endif

   return ccode;

} /* xNWDSCreateContextHandle () */

/* *
 * NWDSDuplicateContextHandle
 *
 * Differs from  NWDSDuplicateContext in that the return code may now
 * specify codes indicative of the reason for the failure.
 */
N_GLOBAL_FUNC (NWDSCCODE)
xNWDSDuplicateContextHandle
(
   nint                    threadGroup,
   NWDSContextHandle       srcContextHandle,
   NWDSContextHandle N_FAR *destContextHandle
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSDuplicateContextHandle; original context = 0x%X\n",
         srcContextHandle);
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, srcContextHandle);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSDuplicateContextHandle (
               srcContextHandle,
               destContextHandle);

   #ifdef DEBUG_OUT
      PRINTF("XX: 0x%X = xNWDSDuplicateContextHandle; oc = 0x%X, "
         "new context = 0x%X\n",
         ccode,
         srcContextHandle,
         *destContextHandle);
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;

} /* xNWDSDuplicateContextHandle () */

/* *
 * NWDSFreeContext
 *
 * Frees a previously allocated Directory context .
 */
N_GLOBAL_FUNC (NWDSCCODE)
xNWDSFreeContext
(
   nint threadGroup,
   NWDSContextHandle context
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSFreeContext; ctx = 0x%X\n", context);
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSFreeContext (context);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;
} /* NWDSFreeContext () */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSLogin
(
   JNIEnv            *env,
   nint              threadGroup,
   NWDSContextHandle context,
   nflag32           optionsFlag,
   pnstr8            objectName,
   pnstr8            password,
   nuint32           validityPeriod,
   nuint32           extendedCharPasswordFlag
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSLogin\n");
      THREADSWITCH;
   #endif

   //make check here before we wrapContext.
   if(extendedCharPasswordFlag && extCharPassFuncTable.login == NULL)
      return throwException(env, "java/lang/LinkageError", ExtCharPassSymbols[2]);

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   if(extendedCharPasswordFlag)
      ccode = (extCharPassFuncTable.login)(context, objectName, PWD_UTF8_STRING, password);
   else
      ccode = NWDSLogin (
               context,
               optionsFlag,
               objectName,
               password,
               validityPeriod);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;
}

N_GLOBAL_FUNC (NWDSCCODE) xNASLogin
(
   nint              threadGroup,
   NWDSContextHandle context,
   unicode *         objectName,
   pnstr8            password,
   nint32 *          nmasID,
   unicode *         authenticatedFDN
)
{
   #ifndef N_PLAT_NLM
   return 0xABCD; //not supported platform
   #else
   
   NWDSCCODE ccode;
   nint saveThreadGroup;

   if(nasFuncTable.login == NULL || nasFuncTable.logout == NULL) //Could not import symbol
      return 0xBCDE;
      
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);

   ccode = (nasFuncTable.login)(objectName, password, -1, context, nmasID, authenticatedFDN);

   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);

   return ccode;
   #endif
}

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSLoginAsService
(
   nint              threadGroup,
   NWDSContextHandle context,
   nflag32           optionsFlag,
   pnstr8            objectName,
   nuint32           validityPeriod
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSLoginAsService\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSLoginAsService (
               context,
               optionsFlag,
               objectName,
               validityPeriod);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;
}


N_GLOBAL_FUNC (NWDSCCODE)
xNWDSWhoAmI
(
   nint              threadGroup,
   NWDSContextHandle context,
   pnstr8            objectName
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSWhoAmI\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSWhoAmI (
               context,
               objectName);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;
}


N_GLOBAL_FUNC (NWDSCCODE)
xNWDSChangeObjectPassword
(
   JNIEnv            *env,
   nint              threadGroup,
   NWDSContextHandle context,
   nflag32           optionsFlag,
   pnstr8            objectName,
   pnstr8            oldPassword,
   pnstr8            newPassword,
   nuint32           extendedCharPasswordFlag
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSChangeObjectPassword\n");
      THREADSWITCH;
   #endif

   //make check here before we wrapContext.
   if(extendedCharPasswordFlag && extCharPassFuncTable.change == NULL)
      return throwException(env, "java/lang/LinkageError", ExtCharPassSymbols[0]);

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   if(extendedCharPasswordFlag)
      ccode = (extCharPassFuncTable.change)(context, objectName, PWD_UTF8_STRING, oldPassword, newPassword, optionsFlag);
   else
      ccode = NWDSChangeObjectPassword (
               context,
               optionsFlag,
               objectName,
               oldPassword,
               newPassword);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;
}

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSGenerateObjectKeyPair
(
   JNIEnv            *env,
   nint              threadGroup,
   NWDSContextHandle context,
   pnstr8            objectName,
   pnstr8            objectPassword,
   nflag32           optionsFlag,
   nuint32           extendedCharPasswordFlag
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSGenerateObjectKeyPair\n");
      THREADSWITCH;
   #endif

   //make check here before we wrapContext.
   if(extendedCharPasswordFlag && extCharPassFuncTable.generate == NULL)
      return throwException(env, "java/lang/LinkageError", ExtCharPassSymbols[1]);

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   if(extendedCharPasswordFlag)
      ccode = (extCharPassFuncTable.generate)(context, objectName, PWD_UTF8_STRING, objectPassword, optionsFlag);
   else
      ccode = NWDSGenerateObjectKeyPair(
               context,
               objectName,
               objectPassword,
               optionsFlag);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;

} /* xNWDSGenerateObjectKeyPair () */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSVerifyObjectPassword
(
   JNIEnv            *env,
   nint              threadGroup,
   NWDSContextHandle context,
   nflag32           optionsFlag,
   pnstr8            objectName,
   pnstr8            objectPassword,
   nuint32           extendedCharPasswordFlag
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSVerifyObjectPassword\n");
      THREADSWITCH;
   #endif

   //make check here before we wrapContext.
   if(extendedCharPasswordFlag && extCharPassFuncTable.verify == NULL)
      return throwException(env, "java/lang/LinkageError", ExtCharPassSymbols[3]);

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   if(extendedCharPasswordFlag)
      ccode = (extCharPassFuncTable.verify)(context, objectName, PWD_UTF8_STRING, objectPassword);
   else
      ccode = NWDSVerifyObjectPassword(
               context,
               optionsFlag,
               objectName,
               objectPassword);

#ifdef N_PLAT_NLM
   unwrapContext(saveThreadGroup);
   sysThreadGCOK(0);
#endif

   return ccode;

} /* NWDSVerifyObjectPassword () */


N_GLOBAL_FUNC (NWDSCCODE)
xNWDSGetContext
(
   nint              threadGroup,
   NWDSContextHandle context,
   nint              key,
   nptr              value
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

#ifdef DEBUG_OUT
   PRINTF("IN: xNWDSGetContext\n");
   THREADSWITCH;
#endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSGetContext (
               context,
               key,
               value);

#ifdef N_PLAT_NLM
   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);
#endif

#ifdef DEBUG_OUT
   PRINTF("OUT: xNWDSGetContext\n");
   THREADSWITCH;
#endif

   return ccode;

}  /* xNWDSGetContext */


N_GLOBAL_FUNC (NWDSCCODE)
xNWDSSetContext
(
   nint              threadGroup,
   NWDSContextHandle context,
   nint              key,
   nptr              value
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

#ifdef DEBUG_OUT
   PRINTF("IN: xNWDSSetContext\n");
   THREADSWITCH;
#endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSSetContext (
               context,
               key,
               value);

#ifdef N_PLAT_NLM
   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);
#endif

   return ccode;
}  /* xNWDSSetContext */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSLogout
(
   nint              threadGroup,
   NWDSContextHandle context
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

#ifdef DEBUG_OUT
   PRINTF("IN: xNWDSLogout\n");
   THREADSWITCH;
#endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSLogout (
               (NWDSContextHandle) context);

#ifdef N_PLAT_NLM
   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);
#endif

   return ccode;
} /* xNWDSLogout () */

N_GLOBAL_FUNC (NWDSCCODE) xNASLogout
(
   nint              threadGroup,
   NWDSContextHandle context,
   nint32            nmasID
)
{
   #ifndef N_PLAT_NLM
   return 0xABCD; //not supported platform
   #else

   NWDSCCODE ccode;
   nint saveThreadGroup;

   if(nasFuncTable.login == NULL || nasFuncTable.logout == NULL) //Could not import symbol
      return 0xBCDE;

   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);

   ccode = (nasFuncTable.logout)(nmasID, -1, context);

   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);

   return ccode;
   #endif
}

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSCanDSAuthenticate
(
   nint              threadGroup,
   NWDSContextHandle context
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSCanDSAuthenticate\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSCanDSAuthenticate (
               context);

#ifdef N_PLAT_NLM
   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);
#endif

   return ccode;
} /* xNWDSCanDSAuthenticate () */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSSpecialIsDSAuthenticated
(
   nint              threadGroup,
   NWDSContextHandle context
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSSpecialIsDSAuthenticated\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;

   sysThreadGCOK(1);

   ccode = NWIsDSAuthenticated ();

   sysThreadGCOK(0);

   unwrapContext(saveThreadGroup);
#else
   ccode = NWDSCanDSAuthenticate (
               (NWDSContextHandle) context);
#endif

   return ccode;
} /* xNWDSSpecialIsDSAuthenticated () */

N_GLOBAL_FUNC (NWDSCCODE)
xNWDSAuthenticateConn
(
   nint              threadGroup,
   NWDSContextHandle context,
   NWCONN_HANDLE     connHandle
)
{
   NWDSCCODE ccode;
#ifdef N_PLAT_NLM
   nint saveThreadGroup;
#endif

   #ifdef DEBUG_OUT
      PRINTF("IN: xNWDSAuthenticateConn\n");
      THREADSWITCH;
   #endif

#ifdef N_PLAT_NLM
   ccode = wrapContext(&saveThreadGroup, threadGroup, context);
   if (0 != ccode)
      return ccode;
   sysThreadGCOK(1);
#endif

   ccode = NWDSAuthenticateConn (
               context,
               connHandle);

#ifdef N_PLAT_NLM
   sysThreadGCOK(0);
   unwrapContext(saveThreadGroup);
#endif

   return ccode;
} /* xNWDSAuthenticateConn () */
