
/* ****************************************************************************

 * eID Middleware Project.
 * Copyright (C) 2008-2017 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see
 * http://www.gnu.org/licenses/.

**************************************************************************** */
#include "PinpadLibOldBeid.h"
#include "../CardLayerConst.h"
#include "../common/MWException.h"
#include "../common/eidErrors.h"
#include "../common/Util.h"

using namespace eIDMW;

static char *tcsLangs[4] = { "en", "nl", "fr", "de" };

static char *tcsUsageSig[4] = {
	"Signature",
	"Handtekening",
	"Signature",
	"Unterschrift"
};

static char *tcsUsageAuth[4] = {
	"Authentication",
	"Authentificatie",
	"Authentification",
	"Authentifizierung"
};

static unsigned char aid_belpic[] =
	{ 0xA0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31,
0x35 };

static SCR_Application xApp = {
	{aid_belpic, sizeof(aid_belpic)}
	,
	"ID",
	NULL
};

static int LangToId(unsigned long ulLanguage)
{
	switch (ulLanguage)
	{
		case 0x0813:
			return 1;	//LANG_NL
		case 0x080c:
			return 2;	//LANG_FR
		case 0x0407:
			return 3;	//LANG_DE
	}
	return 0;
}

/////////////////////////////////////////////////////////

CPinpadLibOldBeid::CPinpadLibOldBeid()
{
	m_pVerifyPin = NULL;
	m_pChangePin = NULL;
	m_iLangIdx = 0;
}

CPinpadLibOldBeid::~CPinpadLibOldBeid()
{
}

bool CPinpadLibOldBeid::Load(unsigned long hContext, SCARDHANDLE hCard,
			     const std::string & strReader,
			     const std::string & strPinpadPrefix,
			     unsigned long ulLanguage)
{
	bool bPinpadLibFound = false;
	char csSystemDir[_MAX_PATH];

	GetSystemDirectoryA(csSystemDir, sizeof(csSystemDir) - 50);

	// E.g. "C:\WINDOWS\System32\beidpp\" or "/usr/local/lib/pteidpp/"
	std::string csPinpadDir = csSystemDir + std::string("\\") +
		strPinpadPrefix + "\\";

	const char *csReader = strReader.c_str();
	const char *csPinpadPrefix = strPinpadPrefix.c_str();

	std::string strSearchFor = csPinpadDir + "*.dll";
	const char *csSearchFor = strSearchFor.c_str();

	// Search for files in csPinpadDir that are candidate pinpad lib,
	// load them and ask them if they support this reader + pinpad lib version
	struct _finddata_t c_file;
	intptr_t hFile = _findfirst(csSearchFor, &c_file);

	if (hFile != -1)
	{
		int iFindRes;

		do
		{
			// Old pinpad lib are not assumed to start with "beidpp"
			if (!StartsWithCI(c_file.name, csPinpadPrefix))
			{
				bPinpadLibFound =
					CheckLib(csPinpadDir, c_file.name,
						 ulLanguage, hContext, hCard,
						 csReader);
				if (bPinpadLibFound)
					break;	// OK: a good pinpad lib was found and loaded
			}

			iFindRes = _findnext(hFile, &c_file);
		}
		while (iFindRes == 0);

		_findclose(hFile);
	}

	if (!bPinpadLibFound)
	{
		// Try the old locations (in the system dir)

		// Append a \ to the system dir name
		size_t len = strlen(csSystemDir);

		csSystemDir[len++] = '\\';
		csSystemDir[len] = '\0';
		struct
		{
			char *name;
			char *dll;
		} libs[] =
		{
			{
			"Xiring X", "xireid.dll"},
			{
			"Cherry ", "ch44xxeid.dll"},
			{
		"SCM Microsystems Inc. SPRx32 USB Smart Card Reader",
					"sprx32eid.dll"},};

		for (int i = 0;
		     i < sizeof(libs) / sizeof(libs[0]) && !bPinpadLibFound;
		     i++)
		{
			if (!strncmp
			    (csReader, libs[i].name, strlen(libs[i].name)))
			{
				bPinpadLibFound = CheckLib(csSystemDir,
							   libs[i].dll,
							   ulLanguage,
							   hContext, hCard,
							   csReader);
			}
		}
	}

	if (bPinpadLibFound)
		m_iLangIdx = LangToId(ulLanguage);

	return bPinpadLibFound;
}

void CPinpadLibOldBeid::UnLoad()
{
	m_oPinpadLib.Close();
}

static unsigned char tucErrTimeout[] = { 0x64, 0x00 };
static unsigned char tucErrCancel[] = { 0x64, 0x01 };
static unsigned char tucErrPinsDiffer[] = { 0x64, 0x02 };
static unsigned char tucErrGeneral[] = { 0x6B, 0x80 };

CByteArray CPinpadLibOldBeid::PinCmd(SCARDHANDLE hCard, unsigned long ulControl, CByteArray oCmd,
				     unsigned char ucPintype, unsigned char ucOperation, unsigned long ulLangCode)
{
	if (ulControl == CCID_IOCTL_GET_FEATURE_REQUEST)
	{
		unsigned char tucFeatures[] =
			{ 0x06, 0x04, 0x00, 0x31, 0x32, 0x33, 0x07, 0x04,
	  0x00, 0x31, 0x32, 0x33 };
		return CByteArray(tucFeatures, sizeof(tucFeatures));
	}
	m_iLangIdx = LangToId(ulLangCode);

	SCR_Card xCard = {
		hCard,
		tcsLangs[m_iLangIdx],
		{NULL, 0}
		,
		NULL
	};
	unsigned char tucStatus[2];
	long lRet = SCARD_F_INTERNAL_ERROR;

	if (ucOperation == EIDMW_PP_OP_VERIFY)
	{
		SCR_PinUsage xPinUsage = {
			ucPintype ==
				EIDMW_PP_TYPE_SIGN ? SCR_USAGE_SIGN :
				SCR_USAGE_AUTH,
			ucPintype == EIDMW_PP_TYPE_SIGN ? "SIG" : "AUT",
			ucPintype ==
				EIDMW_PP_TYPE_SIGN ? tcsUsageSig[m_iLangIdx] :
				tcsUsageAuth[m_iLangIdx]
		};

		lRet = m_pVerifyPin(&xCard, oCmd.GetByte(22), &xPinUsage,
				    &xApp, tucStatus);
	} else if (ucOperation == EIDMW_PP_OP_CHANGE)
	{
		lRet = m_pChangePin(&xCard, oCmd.GetByte(27), &xApp,
				    tucStatus);
	} else
		throw CMWEXCEPTION(EIDMW_ERR_PIN_OPERATION);

	CByteArray oResp(2);

	switch (lRet)
	{
		case SCARD_S_SUCCESS:
			if (tucStatus[0] == 0xEC && tucStatus[1] == 0xD2)
				oResp.Append(tucErrTimeout,
					     sizeof(tucErrTimeout));
			else if (tucStatus[0] == 0xEC && tucStatus[1] == 0xD6)
				oResp.Append(tucErrCancel,
					     sizeof(tucErrCancel));
			else
				oResp.Append(tucStatus, 2);
			break;
		case SCARD_E_CANCELLED:
			oResp.Append(tucErrCancel, sizeof(tucErrCancel));
			break;
		case SCARD_W_REMOVED_CARD:
			throw CMWEXCEPTION(EIDMW_ERR_NO_CARD);
			break;
		case SCR_I_PIN_CHECK_FAILED:
			oResp.Append(tucErrPinsDiffer,
				     sizeof(tucErrPinsDiffer));
			break;
		default:
			oResp.Append(tucErrGeneral, sizeof(tucErrGeneral));
	}

	return oResp;
}

bool CPinpadLibOldBeid::UseOldLib()
{
	return m_pVerifyPin != NULL && m_pChangePin != NULL;
}

bool CPinpadLibOldBeid::CheckLib(const std::string & csPinpadDir,
				 const char *csFileName,
				 unsigned long ulLanguage,
				 unsigned long hContext, SCARDHANDLE hCard,
				 const char *csReader)
{
	bool bRet = false;

	// This is no pinpad DLL (it doesn't hurt to try it out, but it's faster not to)
	if (StartsWithCI(csFileName, "belpicppgui"))
		return false;

	BEID_OLD_PP_INIT pInit = NULL;
	BEID_OLD_VERIFY_PIN pVerifyPin = NULL;
	BEID_OLD_CHANGE_PIN pChangePin = NULL;

	// Load the pinpad lib
	unsigned long ulRes = m_oPinpadLib.Open(csPinpadDir + csFileName);

	if (ulRes == EIDMW_OK)
	{
		// Get the 3 functions
		pInit = (BEID_OLD_PP_INIT) m_oPinpadLib.
			GetAddress("SCR_Init");
		pVerifyPin =
			(BEID_OLD_VERIFY_PIN) m_oPinpadLib.
			GetAddress("SCR_VerifyPIN");
		pChangePin =
			(BEID_OLD_CHANGE_PIN) m_oPinpadLib.
			GetAddress("SCR_ChangePIN");

		if (pInit == NULL || pVerifyPin == NULL || pChangePin == NULL)
			m_oPinpadLib.Close();
		else
		{
			SCR_SupportConstants supported;
			long lRet = pInit(csReader, 1, &supported);

			if (lRet == SCARD_S_SUCCESS
			    && supported == SCR_SUPPORT_OK)
				bRet = true;	// OK, the pinpad lib supports this reader
			else
				m_oPinpadLib.Close();
		}
	}

	if (bRet)
	{
		m_pVerifyPin = pVerifyPin;
		m_pChangePin = pChangePin;
	}

	return bRet;
}
