/***********************************************************************
 * 
 *  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 Module definition
// 
typedef struct _IdenTokenProviderModule
{
   LIST_ENTRY           listEntry;
   char                 *pTypeName;
   int                  typeNameLen;
   LIB_HANDLE           libHandle;
   IdenTokenProviderIf  *pIdenTokenProviderIf;

} IdenTokenProviderModule, *PIdenTokenProviderModule;


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

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

//
// IdenTokenProviderModule list and syncronization mutex
// 
static
LIST_ENTRY        g_IdenTokenProviderModuleListHead = {&g_IdenTokenProviderModuleListHead, &g_IdenTokenProviderModuleListHead};

static
HANDLE            g_idenTokenMutex = NULL;


//++=======================================================================
CasaStatus
GetIdenTokenProviderInterface(
   IN       const char           *pIdenTokenTypeName,
   INOUT    IdenTokenProviderIf  **ppIdenTokenProviderIf)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
//  Environment:
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus;
   ConfigIf    *pModuleConfigIf;

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

   // Get the configuration for the module
   retStatus = GetConfigInterface(moduleConfigFolderPath,
                                  pIdenTokenTypeName,
                                  &pModuleConfigIf);
   if (CASA_SUCCESS(retStatus)
       && CasaStatusCode(retStatus) != CASA_STATUS_OBJECT_NOT_FOUND)
   {
      LIST_ENTRY              *pListEntry;
      IdenTokenProviderModule *pIdenTokenProviderModule = NULL;
      int32_t                 idenTokenTypeNameLen = strlen(pIdenTokenTypeName);

      // Gain exclusive access to our mutex
      PlatAcquireMutex(g_idenTokenMutex);

      // Look if we already have the module in our list
      pListEntry = g_IdenTokenProviderModuleListHead.Flink;
      while (pListEntry != &g_IdenTokenProviderModuleListHead)
      {
         // Get pointer to the current entry
         pIdenTokenProviderModule = CONTAINING_RECORD(pListEntry, IdenTokenProviderModule, listEntry);

         // Check if this is the module that we need
         if (pIdenTokenProviderModule->typeNameLen == idenTokenTypeNameLen
             && memcmp(pIdenTokenTypeName, pIdenTokenProviderModule->pTypeName, idenTokenTypeNameLen) == 0)
         {
            // This is the module that we need, stop looking.
            break;
         }
         else
         {
            // This is not the module that we are looking for
            pIdenTokenProviderModule = NULL;
         }

         // Advance to the next entry
         pListEntry = pListEntry->Flink;
      }

      // Proceed based on whether or not a module was found
      if (pIdenTokenProviderModule)
      {
         // Module found in our list, provide the caller with its IdenTokenProviderIf
         // instance after we have incremented its reference count.
         pIdenTokenProviderModule->pIdenTokenProviderIf->addReference(pIdenTokenProviderModule->pIdenTokenProviderIf);
         *ppIdenTokenProviderIf = pIdenTokenProviderModule->pIdenTokenProviderIf;

         // Success
         retStatus = CASA_STATUS_SUCCESS;
      }
      else
      {
         // Needed module not found in our list, create an entry.
         pIdenTokenProviderModule = malloc(sizeof(*pIdenTokenProviderModule));
         if (pIdenTokenProviderModule)
         {
            // Allocate buffer to contain the authentication type name within the module entry
            pIdenTokenProviderModule->pTypeName = malloc(idenTokenTypeNameLen + 1);
            if (pIdenTokenProviderModule->pTypeName)
            {
               char  *pLibraryName;

               // Initialize the library handle field
               pIdenTokenProviderModule->libHandle = NULL;

               // Save the auth type name within the entry
               strcpy(pIdenTokenProviderModule->pTypeName, pIdenTokenTypeName);
               pIdenTokenProviderModule->typeNameLen = idenTokenTypeNameLen;

               // Obtain the name of the library that we must load
               pLibraryName = pModuleConfigIf->getEntryValue(pModuleConfigIf, "LibraryName");
               if (pLibraryName)
               {
                  // Load the library
                  pIdenTokenProviderModule->libHandle = OpenLibrary(pLibraryName);
                  if (pIdenTokenProviderModule->libHandle)
                  {
                     PFN_GetIdenTokenProviderIfRtn   pGetIdenTokenProviderIfRtn;

                     // Library has been loaded, now get a pointer to its GetIdenTokenProviderProviderInterface routine
                     pGetIdenTokenProviderIfRtn = (PFN_GetIdenTokenProviderIfRtn) GetFunctionPtr(pIdenTokenProviderModule->libHandle,
                                                                                                 GET_IDEN_TOKEN_PROVIDER_INTERFACE_RTN_SYMBOL);
                     if (pGetIdenTokenProviderIfRtn)
                     {
                        // Now, obtain the modules IdenTokenProviderIf.
                        retStatus = (pGetIdenTokenProviderIfRtn)(pModuleConfigIf, &pIdenTokenProviderModule->pIdenTokenProviderIf);
                     }
                     else
                     {
                        DbgTrace(0, "-GetIdenTokenProviderInterface- GetFunctionPtr error\n", 0);
                        retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                    CASA_FACILITY_AUTHTOKEN,
                                                    CASA_STATUS_LIBRARY_LOAD_FAILURE);
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-GetIdenTokenProviderInterface- OpenLibrary error\n", 0);
                     retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                 CASA_FACILITY_AUTHTOKEN,
                                                 CASA_STATUS_LIBRARY_LOAD_FAILURE);
                  }

                  // Free the buffer holding the library name
                  free(pLibraryName);
               }
               else
               {
                  DbgTrace(0, "-GetIdenTokenProviderInterface- Library name not configured\n", 0);
                  retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                              CASA_FACILITY_AUTHTOKEN,
                                              CASA_STATUS_CONFIGURATION_ERROR);
               }

               // Check if we were successful at obtaining the IdenTokenProviderIf instance for the
               // module.
               if (CASA_SUCCESS(retStatus))
               {
                  // Insert the entry in the list, provide the caller with its IdenTokenProviderIf
                  // instance after we have incremented its reference count.
                  InsertTailList(&g_IdenTokenProviderModuleListHead, &pIdenTokenProviderModule->listEntry);
                  pIdenTokenProviderModule->pIdenTokenProviderIf->addReference(pIdenTokenProviderModule->pIdenTokenProviderIf);
                  *ppIdenTokenProviderIf = pIdenTokenProviderModule->pIdenTokenProviderIf;
               }
               else
               {
                  // Failed, free resources.
                  free(pIdenTokenProviderModule->pTypeName);
                  if (pIdenTokenProviderModule->libHandle)
                     CloseLibrary(pIdenTokenProviderModule->libHandle);
                  free(pIdenTokenProviderModule);
               }
            }
            else
            {
               DbgTrace(0, "-GetIdenTokenProviderInterface- Unable to allocate buffer\n", 0);

               // Free buffer allocated for entry
               free(pIdenTokenProviderModule);

               retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                           CASA_FACILITY_AUTHTOKEN,
                                           CASA_STATUS_INSUFFICIENT_RESOURCES);
            }
         }
         else
         {
            DbgTrace(0, "-GetIdenTokenProviderInterface- Unable to allocate buffer\n", 0);
            retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                        CASA_FACILITY_AUTHTOKEN,
                                        CASA_STATUS_INSUFFICIENT_RESOURCES);
         }
      }

      // Release exclusive access to our mutex
      PlatReleaseMutex(g_idenTokenMutex);

      // Release config interface instance
      pModuleConfigIf->releaseReference(pModuleConfigIf);
   }
   else
   {
      DbgTrace(0, "-GetIdenTokenProviderInterface- Unable to obtain config interface\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_CONFIGURATION_ERROR);
   }

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

   return retStatus;
}


//++=======================================================================
CasaStatus
IdenTokenInit(void)
//
// Arguments:  
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Initializes the identity token complex.
//
// L2
//=======================================================================--
{
   CasaStatus        retStatus;

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

   // Allocate mutex
   if ((g_idenTokenMutex = PlatAllocMutex()) != NULL)
      retStatus = CASA_STATUS_SUCCESS;
   else
      retStatus = CasaStatusBuild(CASA_SEVERITY_INFORMATIONAL,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);

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

   return retStatus;
}


//++=======================================================================
void
IdenTokenUninit(void)
//
// Arguments:  
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Uninitializes the indentity token complex.
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-IdenTokenUninit- Start\n", 0);

   // Free mutex if necessary
   if (g_idenTokenMutex)
   {
      PlatDestroyMutex(g_idenTokenMutex);
      g_idenTokenMutex = NULL;
   }

   DbgTrace(1, "-IdenTokenUninit- End\n", 0);
}


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