/*

The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is a portion of the Path Development API developed
by CygnaCom Solutions, Inc.

Portions created by CygnaCom Solutions, Inc. are Copyright (C) CygnaCom 
Solutions, Inc. All Rights Reserved.

Contributor(s): ______________________________________.

*/

/*------------------------------------------------------------------------*\
|  Project Name  :   Path Development API
|  File Name     :   CMAPIInterface.cpp
|  Creation Date :   May 17, 1999
|  Author        :   Matt Cooper (mcooper@cygnacom.com)
|  Last Mode Date:   May 17, 1999
|  Modification History:
|--------------------------------------------------------------------------
|
|     Known Bugs:   None
|                   
|--------------------------------------------------------------------------
| Cryptographic devices and technical data regarding them are subject to
| Federal Government export controls as specified in Title 22, Code of
| Federal Regulations, Parts 121 through 128.  Cryptographic devices
| implementing the Data Encryption Standard (DES) and technical data
| regarding them must comply with these Federal regulations
|--------------------------------------------------------------------------
| Copyright (c) 1999, CygnaCom Solutions, Inc.
| All Rights Reserved.
\*------------------------------------------------------------------------*/
// CMAPIInterface.cpp: implementation of the CCMAPIInterface class.
//
//////////////////////////////////////////////////////////////////////

#if defined(_WIN32)
	#if defined(_AFXDLL)
		#include "stdafx.h"
	#else
		#include "windows.h"
	#endif
#else
#ifndef VX /* Added by Masanori Satake. */
#include "dlfcn.h"
#define GetProcAddress dlsym
#else
#include "vxWorks.h"
#include "string.h"
#include "vxdbg.h"
#endif /* #ifndef VX */
#endif

#include "CMAPIInterface.h"
#include "Defines.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//#define _DEBUG_VERBOSE


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CCMAPIInterface::CCMAPIInterface()
{
	// Strings, etc
	m_lpszLibraryName = NULL;
	memset(m_lpszErrorString,0,CCMAPIINTERFACE_MAX_ERR_SIZE);

	// Clear Function pointers
	ZeroAllProcs();
	
	// Dll Handle
#ifndef VX /* Added by Masanori Satake. */
	m_hLibrary = NULL;
#endif /* #ifndef VX */

	// CMAPI values
	m_sessionHandle = 0;

#ifdef _DEBUG
	m_decodeCount = 0;
	m_freeCount = 0;
#endif
}


CCMAPIInterface::~CCMAPIInterface()
{
	// delete library string
	if(NULL != m_lpszLibraryName)
	{
		delete []m_lpszLibraryName;
		m_lpszLibraryName = NULL;
	}

	// free the CMAPI session
	if( 0 != m_sessionHandle )
	{
		DestroySession();
	}

#ifndef VX /* Added by Masanori Satake. */
	// Unload the CMAPI library
	if( NULL != m_hLibrary )
	{
#ifdef _WINDOWS
		FreeLibrary(m_hLibrary);
#else
		dlclose(m_hLibrary);
#endif
		m_hLibrary = NULL;
	}
#endif /* #ifdef VX */

}

/*-------------------------------------------------------------------------*\
|  Function Name :   LoadCMAPILibrary(LPCSTR libraryName)
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Destroys existing CMAPI session and unloads the library if needed
|	        Calls LoadLibrary, the GetProcAddress for each required function
|	        
|  Exception Handling: None
|---------------------------------------------------------------------------
|  Inputs Params:   The dll name.  May be the full path or just the name
|	                of the dll (e.g. "cmapi.dll") if it is within the
|	                execution search path
|  Pre-Conditions:  None
|  Post-Conditions: Library loaded on 0 return
|  Return values:   0 or error.  Fail description in m_lpszErrorString
|	                
\*-------------------------------------------------------------------------*/
#ifndef VX /* Added by Masanori Satake. */

long CCMAPIInterface::LoadCMAPILibrary(char * libraryName)
{
	long rv = 0;

#ifdef _WINDOWS
	HINSTANCE hInstance = NULL;
#else
	void *hInstance = NULL;
#endif
	
	m_lpszErrorString[0] = '\0';

	do
	{
		// Destroy current session
		if( 0 != m_sessionHandle )
		{
			DestroySession();
		}

		// Clear any existing function pointers
		ZeroAllProcs();


		// Unload the dll if it is loaded
		if( NULL != m_hLibrary )
		{
#ifdef _WINDOWS
			FreeLibrary(m_hLibrary);
#else
			dlclose (m_hLibrary);
#endif
			m_hLibrary = NULL;
		}

		// Load the library
#ifdef _WINDOWS
		hInstance = LoadLibrary(libraryName);
#else
		hInstance = dlopen(libraryName, RTLD_NOW | RTLD_GLOBAL);
#endif
		if( NULL == hInstance )
		{
			// Probably a bad name || path or name not in path
			strcpy(m_lpszErrorString,"Could not load CMAPI library.");
			rv = CCMAPIINTERFACE_LOADLIB_FAILED;
			break;
		}
		else
		{
			// Load success, save library name
			if( NULL != m_lpszLibraryName )
			{
				// delete old name
				delete []m_lpszLibraryName;
				m_lpszLibraryName = NULL;
			}

			// allocate for string data
			m_lpszLibraryName = new char[strlen(libraryName)+4];
			if( NULL == m_lpszLibraryName )
			{
				strcpy(m_lpszErrorString,"Could not allocate string for library name.");
				rv = CCMAPIINTERFACE_ALLOC_ERR;
#ifdef _WINDOWS
				FreeLibrary(hInstance);
#else
				dlclose(hInstance);
#endif
				break;
			}

			// save dll handle and name
			m_hLibrary = hInstance;
			strcpy(m_lpszLibraryName,libraryName);
		}

		// Now retrieve the function pointers
		iDecodeCertProc = NULL;
		iDecodeCertProc = (CMAPI_DECODECERT_PROC)GetProcAddress( m_hLibrary,"CM_DecodeCert" );
		if( NULL == iDecodeCertProc )
		{
			strcpy(m_lpszErrorString,"Could not get address for CM_DecodeCert");
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

		iFreeCertProc = NULL;
		iFreeCertProc = (CMAPI_FREECERT_PROC)GetProcAddress( m_hLibrary,"CM_FreeCert");
		if( NULL == iFreeCertProc )
		{
			strcpy(m_lpszErrorString,"Could not get address for CM_FreeCert");
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

		iCreateSessionProc = NULL;
		iCreateSessionProc = (CMAPI_CREATESESSION_PROC)GetProcAddress(m_hLibrary,"CM_CreateSession");
		if( NULL == iCreateSessionProc )
		{
			strcpy(m_lpszErrorString,"Could not get address for CM_CreateSession");
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

		iDestroySessionProc = NULL;
		iDestroySessionProc = (CMAPI_DESTROYSESSION_PROC)GetProcAddress(m_hLibrary,"CM_DestroySession");
		if( NULL == iDestroySessionProc )
		{
			strcpy(m_lpszErrorString,"Could not get address for CM_DestroySession");
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

	}
	while(FALSE);

	// On error, unload and reset the handle & procs
	if( 0 != rv && NULL != m_hLibrary )
	{
#ifdef _WINDOWS
		FreeLibrary(m_hLibrary);
#else
		dlclose(m_hLibrary);
#endif
		m_hLibrary = NULL;
		ZeroAllProcs();
	}

	#if defined(_DEBUG_VERBOSE) && defined(_DEBUG)
		if( rv && m_lpszErrorString[0] )
			afxDump << m_lpszErrorString;
	#endif

	return rv;
}

#else /* Defined VX. */

long CCMAPIInterface::LoadCMAPILibrary(char * libraryName)
{
  long rv = 0;
  
  m_lpszErrorString[0] = '\0';
  
  do {
    // Destroy current session
    if( 0 != m_sessionHandle ) {
      DestroySession();
    }
    
    // Clear any existing function pointers
    ZeroAllProcs();
    
    // Load success, save library name
    if( NULL != m_lpszLibraryName ) {
      // delete old name
      delete []m_lpszLibraryName;
      m_lpszLibraryName = NULL;
    }
    
    // allocate for string data
    m_lpszLibraryName = new char[strlen(libraryName)+4];
    if( NULL == m_lpszLibraryName ) {
      strcpy(m_lpszErrorString,"Could not allocate string for library name.");
      rv = CCMAPIINTERFACE_ALLOC_ERR;
      break;
    }
    
    // save dll handle and name
    /* Save CML library name.(dummy) */
    strcpy(m_lpszLibraryName,libraryName);
    
    // Now retrieve the function pointers
    iDecodeCertProc = NULL;
    iDecodeCertProc = CM_DecodeCert;
    if( NULL == iDecodeCertProc ) {
      strcpy(m_lpszErrorString,"Could not get address for CM_DecodeCert");
      rv = CCMAPIINTERFACE_GETPROC_FAIL;
      break;
    }
    
    iFreeCertProc = NULL;
    iFreeCertProc = CM_FreeCert;
    if( NULL == iFreeCertProc ) {
      strcpy(m_lpszErrorString,"Could not get address for CM_FreeCert");
      rv = CCMAPIINTERFACE_GETPROC_FAIL;
      break;
    }
    
    iCreateSessionProc = NULL;
    iCreateSessionProc = CM_CreateSession;
    if( NULL == iCreateSessionProc ) {
      strcpy(m_lpszErrorString,"Could not get address for CM_CreateSession");
      rv = CCMAPIINTERFACE_GETPROC_FAIL;
      break;
    }
    
    iDestroySessionProc = NULL;
    iDestroySessionProc = CM_DestroySession;
    if( NULL == iDestroySessionProc ) {
      strcpy(m_lpszErrorString,"Could not get address for CM_DestroySession");
      rv = CCMAPIINTERFACE_GETPROC_FAIL;
      break;
    }
  }
  while(FALSE);

  // On error, unload and reset the handle & procs
  if( 0 != rv ) {
    ZeroAllProcs();
  }
  
#if defined(_DEBUG_VERBOSE) && defined(_DEBUG)
  if( rv && m_lpszErrorString[0] )
    PRINTERR(m_lpszErrorString);
#endif
  
  return rv;
}

#endif /* #ifndef VX */

/*-------------------------------------------------------------------------*\
|  Function Name :   DecodeCert(ASN1_Data encodedCert, Cert_struct **decCert)
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Calls CM_DecodeCert
|	        
|	        
|  Exception Handling: Around CM_DecodeCert
|---------------------------------------------------------------------------
|  Inputs Params:   binary asn1 data pointer,  Cert_struct** ( = NULL )
|	                
|	                
|  Pre-Conditions:  encodedCert points to valid der cert
|  Post-Conditions: decCert points to decoded cert
|  Return values:   0 on success, fail description in m_lpszErrorString
|	                
\*-------------------------------------------------------------------------*/
long CCMAPIInterface::DecodeCert(ASN1_Data encodedCert, Cert_struct **decCert)
{
	long rv = 0;

	m_lpszErrorString[0] = '\0';

	// Establish a session if needed
	if( 0 == m_sessionHandle )
	{
		rv = CreateSession();
		if( 0 != rv )
			return rv; 
	}

	if( NULL == iDecodeCertProc )
	{ /* Should never happen */
		ZeroAllProcs();
		rv = CCMAPIINTERFACE_GETPROC_FAIL;
		return rv;
	}

	#if defined(_WIN32)
		if( IsBadCodePtr((int (__stdcall *)(void))iDecodeCertProc) )
		{
			strcpy(m_lpszErrorString, "iDecodeCertProc ptr is not readable");
			return CCMAPIINTERFACE_CODE_PTR_INVALID;
		}
	#endif

	// using try blocks to guard against possible invalid address
#ifndef VX /* Added by Masanori Satake. */
	try
	{
#endif
		// Call CM_DecodeCert
		rv = iDecodeCertProc(m_sessionHandle,encodedCert,decCert);

		#ifdef _DEBUGCOUNT
		if( 0 == rv )
			m_decodeCount++;  // tracking for leak detection
		#endif

		if( 0 != rv )
		{
			strcpy(m_lpszErrorString,"CM_DecodeCert failed to decode the certificate");
		}


#ifndef VX /* Added by Masanori Satake. */
	}
	catch(...)
	{
		strcpy(m_lpszErrorString,"Fatal exception while calling CM_DecodeCert");
		rv = CCMAPIINTERFACE_CMAPI_EXCEPTION;
	}
#endif
	#if defined(_DEBUG_VERBOSE) && defined(_DEBUG)
		if( rv && m_lpszErrorString[0] )
#ifdef _WIN32 /* Added by Masanori Satake. */
			afxDump << m_lpszErrorString;
#else /* _UNIX & VX */
                        PRINTERR(m_lpszErrorString);
#endif /* _WIN32 */		
	#endif

	return rv;
}

/*-------------------------------------------------------------------------*\
|  Function Name :   FreeCert(Cert_struct **decCert)
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Calls CM_FreeCert
|	        
|	        
|  Exception Handling: Around CM_FreeCert call
|---------------------------------------------------------------------------
|  Inputs Params:   decoded certificate from DecodeCert
|	                
|  Pre-Conditions:  DecodeCert
|  Post-Conditions: certificate free'd
|  Return values:   0 on success, error description in m_lpszErrorString
\*-------------------------------------------------------------------------*/
long CCMAPIInterface::FreeCert(Cert_struct **decCert)
{
	long rv = 0;

	m_lpszErrorString[0] = '\0';

	// Establish a session if needed
	if( 0 == m_sessionHandle )
	{
		rv = CreateSession();
		if( 0 != rv )
			return rv; 
	}

	if( NULL == iDecodeCertProc )
	{ /* Should never happen */
		ZeroAllProcs();
		rv = CCMAPIINTERFACE_GETPROC_FAIL;
		return rv;
	}

	#if defined(_WIN32)
		if( IsBadCodePtr((int (__stdcall *)(void))iDecodeCertProc) )
		{
			strcpy(m_lpszErrorString, "iDecodeCertProc ptr is not readable");
			return CCMAPIINTERFACE_CODE_PTR_INVALID;
		}
	#endif

	// using try blocks to guard against possible invalid address
#ifndef VX /* Added by Masanori Satake. */
	try
	{
#endif
		// Call CM_FreeCert
		iFreeCertProc(m_sessionHandle,decCert);
		#ifdef _DEBUGCOUNT
			m_freeCount++;  // tracking for leak detection
		#endif

#ifndef VX /* Added by Masanori Satake. */
	}
	catch(...)
	{
		strcpy(m_lpszErrorString,"Fatal exception while calling CM_FreeCert");
		rv = CCMAPIINTERFACE_CMAPI_EXCEPTION;
	}
#endif
	#if defined(_DEBUG_VERBOSE) && defined(_DEBUG)
		if( rv && m_lpszErrorString[0] )
			afxDump << m_lpszErrorString;
	#endif

	return rv;
}

/*-------------------------------------------------------------------------*\
|  Function Name :   CreateSession()
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Calls CM_CreateSession
|	        
|	        
|  Exception Handling: around CM_CreateSession call
|---------------------------------------------------------------------------
|  Inputs Params:   None
|	                
|  Pre-Conditions:  LoadCMAPILibrary
|  Post-Conditions: Session established
|  Return values:   0 on success, error description in m_lpszErrorString
|	                
\*-------------------------------------------------------------------------*/
long CCMAPIInterface::CreateSession()
{
	long rv = 0;

	do
	{
#ifndef VX /* Added by Masanori Satake. */
			if( NULL == m_hLibrary )
			{
#endif /* #ifndef VX */
				if( NULL != m_lpszLibraryName )
				{
					ZeroAllProcs();
					rv = LoadCMAPILibrary(m_lpszLibraryName);
					if( 0 != rv  )
					{
						break;
					}
				}
				else
				{
					strcpy(m_lpszErrorString,"Call LoadCMAPILibrary first");
					rv = CCMAPIINTERFACE_LIB_NOT_LOADED;
					break;
				}
#ifndef VX /* Added by Masanori Satake. */
			}
#endif /* #ifndef VX */

		if( NULL == iCreateSessionProc )
		{ /* Should never happen */
			ZeroAllProcs();
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

		#if defined(_WIN32)
			if( IsBadCodePtr((int (__stdcall *)(void))iCreateSessionProc) )
			{
				strcpy(m_lpszErrorString, "iCreateSessionProc ptr is not readable");
				rv = CCMAPIINTERFACE_CODE_PTR_INVALID;
				break;
			}
		#endif

#ifndef VX /* Added by Masanori Satake. */
		try
		{
#endif
			// Call CM_CreateSession
			rv = iCreateSessionProc(&m_sessionHandle, m_ldap_auth_info, tib);
#ifndef VX /* Added by Masanori Satake. */
		}
		catch(...)
		{
			strcpy(m_lpszErrorString,"Fatal exception while calling CM_CreateSession");
			rv = CCMAPIINTERFACE_CMAPI_EXCEPTION;
		}
#endif
		if( 0 == m_sessionHandle || 0 != rv )
		{
			strcpy(m_lpszErrorString,"Failed to create a CMAPI session");
			rv = CCMAPIINTERFACE_SESSIONCREATE_FAIL;
			m_sessionHandle = 0;
		}

	}
	while(FALSE);

	return rv;
}

/*-------------------------------------------------------------------------*\
|  Function Name :   DestroySession()
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Calls CM_DestroySession
|	        
|	        
|  Exception Handling: around CM_DestroySession call
|---------------------------------------------------------------------------
|  Inputs Params:   None
|	                
|  Pre-Conditions:  Presumably, CreateSession
|  Post-Conditions: Session destroyed
|  Return values:   0 on success
|	                
\*-------------------------------------------------------------------------*/
long CCMAPIInterface::DestroySession()
{
	long rv = 0;

	#ifdef _DEBUGCOUNT
		ASSERT(m_freeCount == m_decodeCount);
		m_freeCount = m_decodeCount = 0;
	#endif

	do
	{
#ifndef VX /* Added by Masanori Satake. */
			if( NULL == m_hLibrary )
			{
				// Nothing to do
				ZeroAllProcs();
				break;
			}
#endif /* #ifndef VX */

		if( NULL == iDestroySessionProc )
		{
			// Should not happen
			ZeroAllProcs();
			rv = CCMAPIINTERFACE_GETPROC_FAIL;
			break;
		}

		#if defined(_WIN32)
			if( IsBadCodePtr((int(WINAPI*)(void))iDestroySessionProc) )
			{
				strcpy(m_lpszErrorString, "iDestroySessionProc ptr is not readable");
				rv = CCMAPIINTERFACE_CODE_PTR_INVALID;
				break;
			}
		#endif

#ifndef VX /* Added by Masanori Satake. */
		try
		{
#endif
			// Call CM_DestroySession
			rv = iDestroySessionProc(&m_sessionHandle);
#ifndef VX /* Added by Masanori Satake. */
		}
		catch(...)
		{
			strcpy(m_lpszErrorString,"Fatal exception while calling CM_DestroySession");
			rv = CCMAPIINTERFACE_CMAPI_EXCEPTION;
		}
#endif
		if( 0 != rv )
		{
			rv = CCMAPIINTERFACE_SESSIONDESTROY_FAIL;
		}

	}
	while(FALSE);

	// Always reset, regardless of state
	m_sessionHandle = 0;

	return rv;
}

/*-------------------------------------------------------------------------*\
|  Function Name :   ZeroAllProcs()
|  Creation Date :   March 4, 1999
|  Author        :   Matt Cooper
|  Last Mode Date:   March 4, 1999
|---------------------------------------------------------------------------
|	        
|  Purpose:	Places NULL in each function pointer
|	        
|	        
|  Exception Handling: None
|---------------------------------------------------------------------------
|  Inputs Params:   None
|	                
|	                
|  Pre-Conditions:  None
|  Post-Conditions: NULL in all function pointers
|  Return values:   void
|	                
\*-------------------------------------------------------------------------*/
void CCMAPIInterface::ZeroAllProcs()
{
	// Function pointers
	iDecodeCertProc = NULL;
	iFreeCertProc = NULL;
	iCreateSessionProc = NULL;
	iDestroySessionProc = NULL;
}

#ifdef _DEBUG_VERBOSE
#undef _DEBUG_VERBOSE
#endif

