/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/

//===[ Include files ]=====================================================

#include "internal.h"

//===[ Type definitions ]==================================================

//
// Identity Token Provider Interface instance data
// 
typedef struct _IdenTokenProviderIfInstance
{
   int                  refCount;
   IdenTokenProviderIf  idenTokenProviderIf;

} IdenTokenProviderIfInstance, *PIdenTokenProviderIfInstance;

//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// IdenTokenProviderIf variables
static
int   g_numIdenTokenProviderIfObjs = 0;

// Debug Level
int   DebugLevel = 0;

//
// Initialization variables
//
static
bool  g_moduleInitialized = false;

// Synchronization mutex
static
HANDLE   g_idenTokenProviderIfMutex = NULL;


//++=======================================================================
static
int SSCS_CALL
AddReference(
   IN       const void  *pIfInstance)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
// Returns:
//    Interface reference count.
//                           
// Description:
//    Increases interface reference count.
//
// L2
//=======================================================================--
{
   int                           refCount;
   IdenTokenProviderIfInstance   *pIdenTokenProviderIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenProviderIfInstance, idenTokenProviderIf);

   DbgTrace(2, "-AddReference- Start\n", 0);

   // Increment the reference count on the object
   PlatAcquireMutex(g_idenTokenProviderIfMutex);
   pIdenTokenProviderIfInstance->refCount ++;
   refCount = pIdenTokenProviderIfInstance->refCount;
   PlatReleaseMutex(g_idenTokenProviderIfMutex);

   DbgTrace(2, "-AddReference- End, refCount = %0X\n", refCount);

   return refCount;
}


//++=======================================================================
static
void SSCS_CALL
ReleaseReference(
   IN       const void  *pIfInstance)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
// Returns:
//    Nothing.
//                           
// Description:
//    Decreases interface reference count. The interface is deallocated if
//    the reference count becomes zero.
//
// L2
//=======================================================================--
{
   bool                          freeObj = false;
   IdenTokenProviderIfInstance   *pIdenTokenProviderIfInstance = CONTAINING_RECORD(pIfInstance, IdenTokenProviderIfInstance, idenTokenProviderIf);

   DbgTrace(2, "-ReleaseReference- Start\n", 0);

   // Decrement the reference count on the object and determine if it needs to
   // be released.
   PlatAcquireMutex(g_idenTokenProviderIfMutex);
   pIdenTokenProviderIfInstance->refCount --;
   if (pIdenTokenProviderIfInstance->refCount == 0)
   {
      // The object needs to be released, forget about it.
      freeObj = true;
      g_numIdenTokenProviderIfObjs --;
   }
   PlatReleaseMutex(g_idenTokenProviderIfMutex);

   // Free object if necessary
   if (freeObj)
      free(pIdenTokenProviderIfInstance);

   DbgTrace(2, "-ReleaseReference- End\n", 0);
}


//++=======================================================================
static
CasaStatus 
GetIdentityTokenIf(
   IN       const void        *pIfInstance,
   IN       const char        *pTokenBuf,
   IN       const size_t      tokenLen,
   INOUT    IdenTokenIf       **ppIdenTokenIf)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pTokenBuf -
//       Pointer to null terminated string containing an identity token.
//
//    tokenLen -
//       Length of the token contained in the token buffer.
//
//    ppIdenTokenIf -
//       Pointer to variable that will receive pointer to identity
//       token interface.
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get identity token interface instance for the specified token.
// 
// L2
//=======================================================================--
{
   CasaStatus  retStatus;
   char        *pDecodedTokenBuf;
   uint32_t    decodedTokenBufLen;

   DbgTrace(2, "-GetIdentityTokenIf- Start\n", 0);

   // Validate input parameters
   if (pIfInstance == NULL
       || pTokenBuf == NULL
       || tokenLen > UINT32_MAX
       || tokenLen == 0
       || ppIdenTokenIf == NULL)
   {
      DbgTrace(0, "-GetIdentityTokenIf- Invalid input parameter\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // First decode the token string
   retStatus = DecodeData(pTokenBuf,
                          tokenLen,
                          (void**) &pDecodedTokenBuf,
                          &decodedTokenBufLen);
   if (CASA_SUCCESS(retStatus))
   {
      // Get the identity token interface
      retStatus = GetIdenTokenInterface(pDecodedTokenBuf,
                                        decodedTokenBufLen,
                                        ppIdenTokenIf);

      // Free the decoded token buffer
      free(pDecodedTokenBuf);
   }
   else
   {
      DbgTrace(0, "-GetIdentityTokenIf- Token decode failure\n", 0);
   }

exit:

   DbgTrace(2, "-GetIdentityTokenIf- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
CasaStatus SSCS_CALL
GET_IDEN_TOKEN_PROVIDER_INTERFACE_RTN(
   IN       const ConfigIf    *pModuleConfigIf,
   INOUT    IdenTokenProviderIf  **ppIdenTokenProviderIf)
//
// Arguments:  
//    pModuleConfigIf -
//       Pointer to configuration interface instance for the module.
//               
//    ppIdenTokenProviderIf -
//       Pointer to variable that will receive pointer to
//       IdentityTokenProviderIf instance.
//
// Returns:
//    Casa Status
//                           
// Description:
//    Gets identity token provider interface instance.
//
// L2
//=======================================================================--
{
   CasaStatus                    retStatus;
   IdenTokenProviderIfInstance   *pIdenTokenProviderIfInstance;

   DbgTrace(1, "-GetIdenTokenProviderInterface- Start\n", 0);

   // Validate input parameters
   if (pModuleConfigIf == NULL
       || ppIdenTokenProviderIf == NULL)
   {
      DbgTrace(0, "-GetIdenTokenProviderInterface- Invalid input parameter\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_KRB5TOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

    // Make sure that the module has been initialized
   if (g_moduleInitialized == false)
   {
      // The module has not been initialized, synchronize access thought this section
      // to avoid having two threads performing initialization.
      AcquireModuleMutex;

      // Assume success
      retStatus = CASA_STATUS_SUCCESS;

      // Check again in case another thread pre-empted us.
      if (g_moduleInitialized == false)
      {
         // Initialize the IdenTokenIf complex
         retStatus = IdenTokenIfInit();
         if (CASA_SUCCESS(retStatus))
         {
            // Allocate mutex
            if ((g_idenTokenProviderIfMutex = PlatAllocMutex()) != NULL)
            {
               // Success
               g_moduleInitialized = true;
            }
            else
            {
               IdenTokenIfUninit();
               retStatus = CasaStatusBuild(CASA_SEVERITY_INFORMATIONAL,
                                           CASA_FACILITY_AUTHTOKEN,
                                           CASA_STATUS_INSUFFICIENT_RESOURCES);
            }
         }
      }

      // Stop synchronization
      ReleaseModuleMutex;

      // Exit if we failed
      if (g_moduleInitialized == false)
         goto exit;
   }

   // Allocate space for the interface instance
   pIdenTokenProviderIfInstance = malloc(sizeof(*pIdenTokenProviderIfInstance));
   if (pIdenTokenProviderIfInstance)
   {
      // Initialize the interface instance data
      pIdenTokenProviderIfInstance->refCount = 1;
      pIdenTokenProviderIfInstance->idenTokenProviderIf.addReference = AddReference;
      pIdenTokenProviderIfInstance->idenTokenProviderIf.releaseReference = ReleaseReference;
      pIdenTokenProviderIfInstance->idenTokenProviderIf.getIdentityTokenIf = GetIdentityTokenIf;

      // Keep track of this object
      PlatAcquireMutex(g_idenTokenProviderIfMutex);
      g_numIdenTokenProviderIfObjs ++;
      PlatReleaseMutex(g_idenTokenProviderIfMutex);

      // Return the interface to the caller
      *ppIdenTokenProviderIf = &pIdenTokenProviderIfInstance->idenTokenProviderIf;

      // Success
      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      DbgTrace(0, "-GetIdenTokenProviderInterface- Buffer allocation failure\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_KRB5TOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

exit:

   DbgTrace(1, "-GetIdenTokenProviderInterface- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
//++=======================================================================
//++=======================================================================