/*
 * Copyright (C) 2003 Mathias Brossard <mathias.brossard@idealx.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *  
 * This program 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
 * General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301,
 * USA.
 *
 * In addition, as two special exceptions:
 *
 * 1) IDEALX S.A.S gives permission to:
 *  * link the code of portions of his program with the OpenSSL library under
 *    certain conditions described in each source file
 *  * distribute linked combinations including the two, with respect to the
 *    OpenSSL license and with the GPL
 *
 * You must obey the GNU General Public License in all respects for all of the
 * code used other than OpenSSL. If you modify file(s) with this exception,
 * you may extend this exception to your version of the file(s), but you are
 * not obligated to do so. If you do not wish to do so, delete this exception
 * statement from your version, in all files (this very one along with all
 * source files).

 * 2) IDEALX S.A.S acknowledges that portions of his sourcecode uses (by the
 * way of headers inclusion) some work published by 'RSA Security Inc.'. Those
 * portions are "derived from the RSA Security Inc. PKCS #11Cryptographic
 * Token Interface (Cryptoki)" as described in each individual source file.
 */

#include <string.h>
#include <memory.h>

#ifndef WIN32
#ifdef __APPLE__
#include <Carbon/Carbon.h>
#endif

#include <sys/types.h>
#include <unistd.h>

#ifndef PKCS11_STATIC_LINKING
#ifndef __APPLE__
#include <dlfcn.h>
#endif
#endif

#define __USE_BSD 1

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#else
#include <windows.h>
#endif

#include "pkcs11_util.h"

#ifndef WIN32
#define DEFAULT_PKCSLIB "/usr/local/lib/pkcs11/opensc-pkcs11.so"
#else
#define DEFAULT_PKCSLIB "aetpkss1.dll"
#endif

CK_FUNCTION_LIST  *pkcs11_get_function_list( const char *param )
{
  CK_FUNCTION_LIST  *funcs;
  CK_RV            rc;
#ifndef PKCS11_STATIC_LINKING
  CK_RV       (*pfoo)();
  void        *d;
  const char  *e;
  char        *z = DEFAULT_PKCSLIB;

#ifdef __APPLE__
  return NULL;
#else

  if( param ) {
    e = param;
  } else {
    e = getenv("PKCS11_LIBRARY");
    if ( e == NULL) {
    e = z;
    }
  }
#ifdef WIN32
  d = LoadLibrary(e);
 
  if ( d == NULL ) {
    printf("LoadLibrary Failed\n");
    return NULL;
  }
  pfoo = (CK_RV (*)())GetProcAddress(d, "C_GetFunctionList");
#else
  d = dlopen(e, RTLD_NOW);
  if ( d == NULL ) {
    d = dlopen(e, RTLD_LAZY);
    if (d == NULL ) {
	 	printf("LoadLibrary Failed\n");
      return NULL;
    }
  }

  pfoo = (CK_RV (*)())dlsym(d,"C_GetFunctionList");
#endif
  if (pfoo == NULL ) {
    printf("Symbol lookup failed\n");
    return NULL;
  }
#ifndef WIN32
  rc = pfoo(&funcs);
#else
  funcs = (CK_FUNCTION_LIST_PTR) malloc(sizeof(CK_FUNCTION_LIST));
  if(funcs) {
#undef CK_NEED_ARG_LIST
#undef CK_PKCS11_FUNCTION_INFO
#define CK_PKCS11_FUNCTION_INFO(name) \
    funcs->name = (CK_RV (*)())GetProcAddress(d, #name);

#include "pkcs11f.h"
    rc = CKR_OK;
  }
#endif

#endif

#else
  rc = C_GetFunctionList( &funcs ) ;
#endif


  if (rc != CKR_OK) {
    printf("Call to C_GetFunctionList failed\n");
    funcs = NULL;
  }
    printf("C_GetFunctionList returned %p\n", funcs);

  return funcs;
}

#ifndef PKCS11_STATIC_LINKING
static int find_mozilla_callbacks(CK_CREATEMUTEX  *create,
				  CK_DESTROYMUTEX *destroy,
				  CK_LOCKMUTEX    *lock,
				  CK_UNLOCKMUTEX  *unlock)
#if ( ( ! defined ( WIN32 ) && ( ! defined (__APPLE__) ) ) )
{ 
  CK_RV rv = CKR_MUTEX_BAD;
  CK_LOCKMUTEX l;
  CK_UNLOCKMUTEX u;
  CK_CREATEMUTEX c;
  CK_DESTROYMUTEX d;
  char *e, *z = "libnssckbi.so";
  void *dl;

  e = getenv("MOZILLA_LOCKLIB");
  if ( e == NULL) {
    e = z;
  }

  if ( (dl = dlopen(e, RTLD_NOW)) ) {
    l = (CK_LOCKMUTEX)    dlsym(dl,"nssCKFWMutex_Lock");
    u = (CK_UNLOCKMUTEX)  dlsym(dl,"nssCKFWMutex_Unlock");
    c = (CK_CREATEMUTEX)  dlsym(dl,"nssCKFWMutex_Create");
    d = (CK_DESTROYMUTEX) dlsym(dl,"nssCKFWMutex_Destroy");
    
    if((c != NULL) && (d != NULL) && (l != NULL) && (u != NULL)) {
      *lock    = l;
      *unlock  = u;
      *create  = c;
      *destroy = d;
      rv = CKR_OK;
    }
  }
  return rv;
}
#else
{
  return 0;
}
#endif
#endif

int search_file(char *buffer, int size, char *key)
#if ( ( ! defined ( WIN32 ) && ( ! defined (__APPLE__) ) ) )
{
  DIR *dir = opendir(buffer);
  struct dirent *ent;
  int found = 0;

  while((found == 0) && (ent = readdir(dir))) {
    if((ent->d_type & DT_DIR)) {
      int len = strlen(buffer);
      if((ent->d_name[0] != '.') && (len < size - 8)) {
	buffer[len] = '/';
	strncpy(buffer + len + 1, ent->d_name, size - len - 1);
	found = search_file(buffer, size, key);
	if(found == 0) {
	  buffer[len] = '\0';
	}
      }
    }
    if((ent->d_type & DT_REG)) {
      if(strcmp(key, ent->d_name) == 0) {
	found = 1;
      }
    }
  }
  closedir(dir);
  return found;
}
#else
{
  return 0;
}
#endif


CK_RV pkcs11_initialize(CK_FUNCTION_LIST_PTR funcs)
{
  CK_RV rc = CKR_HOST_MEMORY;
  if(funcs) {
	printf("Going to initialize %p\n", funcs);
    rc = funcs->C_Initialize( NULL );
	printf("Success!\n");
#if ( ( ! defined ( WIN32 ) ) && ( ! defined (__APPLE__) ) && ( ! defined (PKCS11_STATIC_LINKING) ) )
    if (rc == CKR_ARGUMENTS_BAD) {
      char buffer[256];
      char *z;
      CK_C_INITIALIZE_ARGS *iap = NULL;
      struct {
	CK_CREATEMUTEX CreateMutex;
	CK_DESTROYMUTEX DestroyMutex;
	CK_LOCKMUTEX LockMutex;
	CK_UNLOCKMUTEX UnlockMutex;
	CK_FLAGS flags;
	CK_CHAR_PTR LibraryParameters;
	CK_VOID_PTR pReserved;
      } ia;
	
      (void)memset(&ia, 0, sizeof(ia));
      z = getenv("MOZILLA_INIT");
      if (z) {
	snprintf(buffer, 256, "%s", z);
      } else {
	char *l = "configdir='%s' certPrefix='' keyPrefix='' secmod='secmod.db'";
	char path[256];
	snprintf(path, 256, "%s/.mozilla", getenv("HOME"));
	search_file(path, 256, "secmod.db");
	snprintf(buffer, 256, l, path);
      }

      iap = (CK_C_INITIALIZE_ARGS *)&ia;
      ia.flags = CKF_LIBRARY_CANT_CREATE_OS_THREADS | CKF_OS_LOCKING_OK;
      ia.LibraryParameters = (CK_CHAR_PTR)buffer;
      ia.pReserved = NULL_PTR;
      find_mozilla_callbacks (&ia.CreateMutex, &ia.DestroyMutex,
			      &ia.LockMutex, &ia.UnlockMutex);
      rc = funcs->C_Initialize( (CK_VOID_PTR)iap );
    }
#endif
  }
  return rc;
}



void pkcs11_print_error( CK_RV error )
{
    switch( error ){
    case CKR_OK: printf("SUCCESS\n"); break;
    case CKR_ARGUMENTS_BAD: printf("BAD ARGUMENTS\n"); break;

    case CKR_USER_ALREADY_LOGGED_IN: printf("Already logged in!\n"); break;
    case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: printf("Another user already logged in!\n"); break;
    case CKR_USER_PIN_NOT_INITIALIZED: printf("User pin not initialized\n");break;

    case CKR_PIN_LOCKED: printf("Pin locked\n"); break;
	
    case CKR_SESSION_CLOSED: printf("Session closed\n"); break;
    case CKR_SESSION_HANDLE_INVALID: printf("Session handle invalid\n");break;
    case CKR_SESSION_READ_ONLY_EXISTS: printf("Read Only Session exists\n");break;


	//OpenSession
    case CKR_TOKEN_NOT_RECOGNIZED: printf("Token not recognized"); break;

    default: printf("[Error Code: %lu]\n", error); break;
	
    }
 
}
