/***********************************************************************
 * 
 *  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 ]==================================================

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

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

// Number of static locks required by OpenSSL
static
int   g_numStaticLocks = 0;

// Mutex array for OpenSSL static locks
static
pthread_mutex_t   *g_staticLocks = NULL;


//++=======================================================================
static void
StaticLockFunction(
   IN    int mode,
   IN    int n,
   IN    const char *file,
   IN    int line)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(3, "-StaticLockFunction- Start\n", 0);

   // Verify that the lock number is within range
   if (n < g_numStaticLocks
       && n >= 0)
   {
      // Either set or release the nth lock
      if (mode & CRYPTO_LOCK)
      {
         // Set the lock
         pthread_mutex_lock(&g_staticLocks[n]);
      }
      else
      {
         // Release the lock
         pthread_mutex_unlock(&g_staticLocks[n]);
      }
   }
   else
   {
      DbgTrace(0, "-StaticLockFunction- n out of range\n", 0);
   }

   DbgTrace(3, "-StaticLockFunction- End\n", 0);
}


//++=======================================================================
static void
DynLockFunction(
   IN    int mode,
   IN    struct CRYPTO_dynlock_value *l,
   IN    const char *file,
   IN    int line)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(3, "-DynLockFunction- Start\n", 0);

   if (l)
   {
      // Either set or release the lock
      if (mode & CRYPTO_LOCK)
      {
         // Set the lock
         pthread_mutex_lock((pthread_mutex_t*) l);
      }
      else
      {
         // Release the lock
         pthread_mutex_unlock((pthread_mutex_t*) l);
      }
   }
   else
   {
      DbgTrace(0, "-DynLockFunction- Invalid parameter\n", 0);
   }

   DbgTrace(3, "-DynLockFunction- End\n", 0);
}


//++=======================================================================
static struct CRYPTO_dynlock_value*
CreateDynLockFunction(
   IN    const char *file,
   IN    int line)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   struct CRYPTO_dynlock_value   *l;

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

   // Allocate space for the lock
   l = (struct CRYPTO_dynlock_value*) malloc(sizeof(pthread_mutex_t));
   if (l)
   {
      pthread_mutex_init((pthread_mutex_t*) l, NULL);
   }
   else
   {
      DbgTrace(0, "-CreateDynLockFunction- Buffer allocation failure\n", 0);
   }

   DbgTrace(1, "-CreateDynLockFunction- End, l = %0lX\n", (long) l);

   return l;
}


//++=======================================================================
static void
DestroyDynLockFunction(
   IN    struct CRYPTO_dynlock_value *l,
   IN    const char *file,
   IN    int line)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "-DestroyDynLockFunction- Start, l = %0lX\n", (long) l);

   if (l)
   {
      pthread_mutex_destroy((pthread_mutex_t*) l);
      free(l);
   }

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


//++=======================================================================
static unsigned long
ThreadIdFunction(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   unsigned long  threadId;

   DbgTrace(3, "-ThreadIdFunction- Start\n", 0);

   threadId = (unsigned long) pthread_self();

   DbgTrace(3, "-ThreadIdFunction- End, id = %0lX\n", threadId);

   return threadId;
}


//++=======================================================================
int
SetupOSSLSupport(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   retStatus = -1;
   int   i;

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

   // Determine how many static locks are needed
   g_numStaticLocks = CRYPTO_num_locks();

   // Allocate space to hold the needed mutexes
   g_staticLocks = malloc(sizeof(pthread_mutex_t) * g_numStaticLocks);
   if (g_staticLocks)
   {
      for (i = 0; i < g_numStaticLocks; i++)
         pthread_mutex_init(&g_staticLocks[i], NULL);

      // Set callback functions
      CRYPTO_set_id_callback(ThreadIdFunction);
      CRYPTO_set_locking_callback(StaticLockFunction);
      CRYPTO_set_dynlock_create_callback(CreateDynLockFunction);
      CRYPTO_set_dynlock_destroy_callback(DestroyDynLockFunction);
      CRYPTO_set_dynlock_lock_callback(DynLockFunction);

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

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

   return retStatus;
}


//++=======================================================================
void
CleanupOSSLSupport(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   i;

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

   // Clear our callback functions
   CRYPTO_set_id_callback(NULL);
   CRYPTO_set_locking_callback(NULL);
   CRYPTO_set_dynlock_create_callback(NULL);
   CRYPTO_set_dynlock_destroy_callback(NULL);
   CRYPTO_set_dynlock_lock_callback(NULL);

   // Now, cleanup the resources allocated for static locks
   if (g_staticLocks)
   {
      for (i = 0; i < g_numStaticLocks; i++)
         pthread_mutex_destroy(&g_staticLocks[i]);

      free(g_staticLocks);
   }

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


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