/**************************************************************************
* Authors : Lionel VICTOR <lionel.victor@unforgettable.com>
* <lionel.victor@free.fr>
* Ludovic ROUSSEAU <ludovic.rousseau@free.fr>
* Compiler : gcc, Visual C++
* Target : Unix, Windows
*
* Description : Perl wrapper to the PCSC API
*
* Copyright (C) 2001 - Lionel VICTOR
* Copyright (c) 2003-2010 Ludovic ROUSSEAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
**************************************************************************/
/* $Id: PCSC.xs,v 1.28 2011-03-06 17:17:04 rousseau Exp $ */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include "PCSCperl.h"
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/* The following hack helps importing variables from the PCSC library.
* Those variables are dynamically imported...
* TODO: see how we can use them anyway...
*/
#ifdef SCARD_PCI_T0
# undef SCARD_PCI_T0
# define SCARD_PCI_T0 (gpioSCardT0Pci)
#endif
#ifdef SCARD_PCI_T1
# undef SCARD_PCI_T1
# define SCARD_PCI_T1 (gpioSCardT1Pci)
#endif
#ifdef SCARD_PCI_RAW
# undef SCARD_PCI_RAW
# define SCARD_PCI_RAW (gpioSCardRawPci)
#endif
#ifdef __cplusplus
}
#endif
/* InitErrorCodes () initializes and creates a few variables describing
* important values like error codes and constants. Those values are set
* read-only so the user won't be able to eventually modify them.
* Those variables are set in the XS because their numerical value is
* platform dependent.
*/
void _InitErrorCodes () {
SV * tmpSV;
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_S_SUCCESS", TRUE);
sv_setiv (tmpSV, SCARD_S_SUCCESS); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_CANCELLED", TRUE);
sv_setiv (tmpSV, SCARD_E_CANCELLED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_CANT_DISPOSE", TRUE);
sv_setiv (tmpSV, SCARD_E_CANT_DISPOSE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INSUFFICIENT_BUFFER", TRUE);
sv_setiv (tmpSV, SCARD_E_INSUFFICIENT_BUFFER); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INVALID_ATR", TRUE);
sv_setiv (tmpSV, SCARD_E_INVALID_ATR); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INVALID_HANDLE", TRUE);
sv_setiv (tmpSV, SCARD_E_INVALID_HANDLE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INVALID_PARAMETER", TRUE);
sv_setiv (tmpSV, SCARD_E_INVALID_PARAMETER); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INVALID_TARGET", TRUE);
sv_setiv (tmpSV, SCARD_E_INVALID_TARGET); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_INVALID_VALUE", TRUE);
sv_setiv (tmpSV, SCARD_E_INVALID_VALUE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_NO_MEMORY", TRUE);
sv_setiv (tmpSV, SCARD_E_NO_MEMORY); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_UNKNOWN_READER", TRUE);
sv_setiv (tmpSV, SCARD_E_UNKNOWN_READER); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_TIMEOUT", TRUE);
sv_setiv (tmpSV, SCARD_E_TIMEOUT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_SHARING_VIOLATION", TRUE);
sv_setiv (tmpSV, SCARD_E_SHARING_VIOLATION); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_NO_SMARTCARD", TRUE);
sv_setiv (tmpSV, SCARD_E_NO_SMARTCARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_UNKNOWN_CARD", TRUE);
sv_setiv (tmpSV, SCARD_E_UNKNOWN_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_PROTO_MISMATCH", TRUE);
sv_setiv (tmpSV, SCARD_E_PROTO_MISMATCH); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_NOT_READY", TRUE);
sv_setiv (tmpSV, SCARD_E_NOT_READY); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_SYSTEM_CANCELLED", TRUE);
sv_setiv (tmpSV, SCARD_E_SYSTEM_CANCELLED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_NOT_TRANSACTED", TRUE);
sv_setiv (tmpSV, SCARD_E_NOT_TRANSACTED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_READER_UNAVAILABLE", TRUE);
sv_setiv (tmpSV, SCARD_E_READER_UNAVAILABLE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_PCI_TOO_SMALL", TRUE);
sv_setiv (tmpSV, SCARD_E_PCI_TOO_SMALL); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_READER_UNSUPPORTED", TRUE);
sv_setiv (tmpSV, SCARD_E_READER_UNSUPPORTED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_DUPLICATE_READER", TRUE);
sv_setiv (tmpSV, SCARD_E_DUPLICATE_READER); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_CARD_UNSUPPORTED", TRUE);
sv_setiv (tmpSV, SCARD_E_CARD_UNSUPPORTED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_NO_SERVICE", TRUE);
sv_setiv (tmpSV, SCARD_E_NO_SERVICE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_SERVICE_STOPPED", TRUE);
sv_setiv (tmpSV, SCARD_E_SERVICE_STOPPED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_E_UNSUPPORTED_FEATURE", TRUE);
sv_setiv (tmpSV, SCARD_E_UNSUPPORTED_FEATURE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_W_UNSUPPORTED_CARD", TRUE);
sv_setiv (tmpSV, SCARD_W_UNSUPPORTED_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_W_UNRESPONSIVE_CARD", TRUE);
sv_setiv (tmpSV, SCARD_W_UNRESPONSIVE_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_W_UNPOWERED_CARD", TRUE);
sv_setiv (tmpSV, SCARD_W_UNPOWERED_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_W_RESET_CARD", TRUE);
sv_setiv (tmpSV, SCARD_W_RESET_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_W_REMOVED_CARD", TRUE);
sv_setiv (tmpSV, SCARD_W_REMOVED_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_F_COMM_ERROR", TRUE);
sv_setiv (tmpSV, SCARD_F_COMM_ERROR); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_F_INTERNAL_ERROR", TRUE);
sv_setiv (tmpSV, SCARD_F_INTERNAL_ERROR); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_F_UNKNOWN_ERROR", TRUE);
sv_setiv (tmpSV, SCARD_F_UNKNOWN_ERROR); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_F_WAITED_TOO_LONG", TRUE);
sv_setiv (tmpSV, SCARD_F_WAITED_TOO_LONG); SvREADONLY_on (tmpSV);
/* PCSC - Perl wrapper specific error codes */
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_P_ALREADY_CONNECTED", TRUE);
sv_setiv (tmpSV, SCARD_P_ALREADY_CONNECTED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_P_NOT_CONNECTED", TRUE);
sv_setiv (tmpSV, SCARD_P_NOT_CONNECTED); SvREADONLY_on (tmpSV);
/* PCSC standard constants */
/*
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_CONVENTION_DIRECT", TRUE);
sv_setiv (tmpSV, SCARD_CONVENTION_DIRECT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_CONVENTION_INVERSE", TRUE);
sv_setiv (tmpSV, SCARD_CONVENTION_INVERSE); SvREADONLY_on (tmpSV);
*/
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SCOPE_USER", TRUE);
sv_setiv (tmpSV, SCARD_SCOPE_USER); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SCOPE_TERMINAL", TRUE);
sv_setiv (tmpSV, SCARD_SCOPE_TERMINAL); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SCOPE_SYSTEM", TRUE);
sv_setiv (tmpSV, SCARD_SCOPE_SYSTEM); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_PROTOCOL_T0", TRUE);
sv_setiv (tmpSV, SCARD_PROTOCOL_T0); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_PROTOCOL_T1", TRUE);
sv_setiv (tmpSV, SCARD_PROTOCOL_T1); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_PROTOCOL_RAW", TRUE);
sv_setiv (tmpSV, SCARD_PROTOCOL_RAW); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SHARE_EXCLUSIVE", TRUE);
sv_setiv (tmpSV, SCARD_SHARE_EXCLUSIVE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SHARE_SHARED", TRUE);
sv_setiv (tmpSV, SCARD_SHARE_SHARED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SHARE_DIRECT", TRUE);
sv_setiv (tmpSV, SCARD_SHARE_DIRECT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_LEAVE_CARD", TRUE);
sv_setiv (tmpSV, SCARD_LEAVE_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_RESET_CARD", TRUE);
sv_setiv (tmpSV, SCARD_RESET_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_UNPOWER_CARD", TRUE);
sv_setiv (tmpSV, SCARD_UNPOWER_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_EJECT_CARD", TRUE);
sv_setiv (tmpSV, SCARD_EJECT_CARD); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_UNKNOWN", TRUE);
sv_setiv (tmpSV, SCARD_UNKNOWN); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_ABSENT", TRUE);
sv_setiv (tmpSV, SCARD_ABSENT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_PRESENT", TRUE);
sv_setiv (tmpSV, SCARD_PRESENT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SWALLOWED", TRUE);
sv_setiv (tmpSV, SCARD_SWALLOWED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_POWERED", TRUE);
sv_setiv (tmpSV, SCARD_POWERED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_NEGOTIABLE", TRUE);
sv_setiv (tmpSV, SCARD_NEGOTIABLE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_SPECIFIC", TRUE);
sv_setiv (tmpSV, SCARD_SPECIFIC); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_UNAWARE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_UNAWARE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_IGNORE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_IGNORE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_CHANGED", TRUE);
sv_setiv (tmpSV, SCARD_STATE_CHANGED); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_UNKNOWN", TRUE);
sv_setiv (tmpSV, SCARD_STATE_UNKNOWN); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_UNAVAILABLE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_UNAVAILABLE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_EMPTY", TRUE);
sv_setiv (tmpSV, SCARD_STATE_EMPTY); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_PRESENT", TRUE);
sv_setiv (tmpSV, SCARD_STATE_PRESENT); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_ATRMATCH", TRUE);
sv_setiv (tmpSV, SCARD_STATE_ATRMATCH); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_EXCLUSIVE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_EXCLUSIVE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_INUSE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_INUSE); SvREADONLY_on (tmpSV);
tmpSV = perl_get_sv ("Chipcard::PCSC::SCARD_STATE_MUTE", TRUE);
sv_setiv (tmpSV, SCARD_STATE_MUTE); SvREADONLY_on (tmpSV);
}
/* _StringifyError is copied from pcsc_stringify_error() which is a
* function taken from PCSClite
* It has been modified because I added a few internal errors to the
* wrapper (SCARD_P_NOT_CONNECTED for instance)
* I also feel like using strong types like const char * const and
* avoiding to use strcpy() is better from a security point of view.
*
* See file debuglog.c from your PCSClite distribution for extra
* informations
*/
const char * _StringifyError (unsigned long Error) {
switch ( Error ) {
case SCARD_S_SUCCESS: return "Command successful.";
case SCARD_E_CANCELLED: return "Command cancelled.";
case SCARD_E_CANT_DISPOSE: return "Cannot dispose handle.";
case SCARD_E_CARD_UNSUPPORTED: return "Card is unsupported.";
case SCARD_E_DUPLICATE_READER: return "Reader already exists.";
case SCARD_E_INSUFFICIENT_BUFFER: return "Insufficient buffer.";
case SCARD_E_INVALID_ATR: return "Invalid ATR.";
case SCARD_E_INVALID_HANDLE: return "Invalid handle.";
case SCARD_E_INVALID_PARAMETER: return "Invalid parameter given.";
case SCARD_E_INVALID_TARGET: return "Invalid target given.";
case SCARD_E_INVALID_VALUE: return "Invalid value given.";
case SCARD_E_NO_MEMORY: return "Not enough memory.";
case SCARD_E_NO_SERVICE: return "Service not available.";
case SCARD_E_NO_SMARTCARD: return "No smartcard inserted.";
case SCARD_E_NOT_READY: return "Subsystem not ready.";
case SCARD_E_NOT_TRANSACTED: return "Transaction failed.";
case SCARD_E_PCI_TOO_SMALL: return "PCI struct too small.";
case SCARD_E_PROTO_MISMATCH: return "Card protocol mismatch.";
case SCARD_E_READER_UNAVAILABLE: return "Reader/s is unavailable.";
case SCARD_E_READER_UNSUPPORTED: return "Reader is unsupported.";
case SCARD_E_SERVICE_STOPPED: return "Service was stopped.";
case SCARD_E_SHARING_VIOLATION: return "Sharing violation.";
case SCARD_E_SYSTEM_CANCELLED: return "System cancelled.";
case SCARD_E_TIMEOUT: return "Command timeout.";
case SCARD_E_UNKNOWN_CARD: return "Unknown card.";
case SCARD_E_UNKNOWN_READER: return "Unknown reader specified.";
case SCARD_E_UNSUPPORTED_FEATURE: return "Feature not supported.";
case SCARD_F_COMM_ERROR: return "RPC transport error.";
case SCARD_F_INTERNAL_ERROR: return "Unknown internal error.";
case SCARD_F_UNKNOWN_ERROR: return "Unknown internal error.";
case SCARD_F_WAITED_TOO_LONG: return "Waited too long.";
case SCARD_W_REMOVED_CARD: return "Card was removed.";
case SCARD_W_RESET_CARD: return "Card was reset.";
case SCARD_W_UNPOWERED_CARD: return "Card is unpowered.";
case SCARD_W_UNRESPONSIVE_CARD: return "Card is unresponsive.";
case SCARD_W_UNSUPPORTED_CARD: return "Card is not supported.";
/* The following errors are specific to the Perl wrapper */
case SCARD_P_ALREADY_CONNECTED: return "Object is already connected";
case SCARD_P_NOT_CONNECTED: return "Object is not connected";
/* We finally end with a generic error message */
default: return "Unknown (reader specific ?) error...";
};
}
/*************************************************************************/
/*************** Double Typed Magical variable PCSC::errno ***************/
/*************************************************************************/
/* This is an accessor to our internal double-typed magical variable */
I32 gnLastError_get (pTHX_ IV nID, SV *sv) {
/* We have to set both int and double values */
sv_setiv (sv, (IV)gnLastError);
sv_setnv (sv, (double)gnLastError);
/* Then we set the error message string */
sv_setpv (sv, _StringifyError(gnLastError));
/* Then, we eventually put corresponding bits of the SV flag */
SvNOK_on (sv);
SvIOK_on (sv);
/* return value should be ignored (man samples use return 1) */
return 1;
}
/* This is a modifier to our internal double-typed magical variable */
I32 gnLastError_set (pTHX_ IV nID, SV *sv) {
/* just store the value in our global variable */
gnLastError = SvIV (sv);
/* return value should be ignored (man samples use return 1) */
return 1;
}
/* Initialize the double-typed magical variable Chipcard::PCSC::errno */
void _InitMagic () {
struct ufuncs uf_errno;
SV *sv;
/* Build a new immortal scalar */
sv = perl_get_sv ("Chipcard::PCSC::errno", TRUE);
/* Construct the magic virtual table */
uf_errno.uf_val = &gnLastError_get;
uf_errno.uf_set = &gnLastError_set;
uf_errno.uf_index = 0;
/* Then apply magic to it (use uf_eerno as a callback list) */
sv_magic (sv, 0, 'U', (char *)&uf_errno, sizeof(uf_errno));
/* Let the scalar be enchanted ! */
SvMAGICAL_on (sv);
}
MODULE = Chipcard::PCSC PACKAGE = Chipcard::PCSC
PROTOTYPES: ENABLE
#///////////////////////////////////////////////////////////////////////////
bool
_LoadPCSCLibrary ()
CODE:
if (ghDll) {
/* No need to load the library twice */
RETVAL = TRUE;
} else {
/* Then loads the dynamic library */
ghDll = LOAD_LIB();
if (ghDll == NULL) {
RETVAL = FALSE;
croak ("Failed to load PCSC library");
} else {
hEstablishContext = (TSCardEstablishContext) GET_FCT (ghDll, "SCardEstablishContext");
hReleaseContext = (TSCardReleaseContext) GET_FCT (ghDll, "SCardReleaseContext");
hReconnect = (TSCardReconnect) GET_FCT (ghDll, "SCardReconnect");
hDisconnect = (TSCardDisconnect) GET_FCT (ghDll, "SCardDisconnect");
hBeginTransaction = (TSCardBeginTransaction) GET_FCT (ghDll, "SCardBeginTransaction");
hEndTransaction = (TSCardEndTransaction) GET_FCT (ghDll, "SCardEndTransaction");
hTransmit = (TSCardTransmit) GET_FCT (ghDll, "SCardTransmit");
hControl = (TSCardControl) GET_FCT (ghDll, "SCardControl");
hCancel = (TSCardCancel) GET_FCT (ghDll, "SCardCancel");
#ifdef WIN32
hListReaders = (TSCardListReaders) GET_FCT (ghDll, "SCardListReadersA");
hConnect = (TSCardConnect) GET_FCT (ghDll, "SCardConnectA");
hStatus = (TSCardStatus) GET_FCT (ghDll, "SCardStatusA");
hGetStatusChange = (TSCardGetStatusChange) GET_FCT (ghDll, "SCardGetStatusChangeA");
#else
hListReaders = (TSCardListReaders) GET_FCT (ghDll, "SCardListReaders");
hConnect = (TSCardConnect) GET_FCT (ghDll, "SCardConnect");
hStatus = (TSCardStatus) GET_FCT (ghDll, "SCardStatus");
hGetStatusChange = (TSCardGetStatusChange) GET_FCT (ghDll, "SCardGetStatusChange");
#endif
if (
!hEstablishContext || !hReleaseContext || !hListReaders || !hConnect ||
!hReconnect || ! hDisconnect || !hBeginTransaction || !hEndTransaction ||
!hTransmit || !hStatus || !hGetStatusChange || !hCancel || !hControl)
{
RETVAL = FALSE;
croak ("PCSC library does not contain all the required symbols");
} else {
RETVAL = TRUE;
}
}
/* Initialize the magical variable */
_InitMagic ();
_InitErrorCodes ();
}
OUTPUT:
RETVAL
#///////////////////////////////////////////////////////////////////////////
#// EstablishContext ()
#//
#// INPUT :
#// - $dwScope -> Scope of the establishment. This can be either a local
#// or remote connection.
#// - $pvReserved1
#// - $pwReserved2 -> as of this writting, bothe the above parameters
#// are not used by PCSC... they should be 0
#//
#// OUTPUT :
#// EstablishContext() returns the connection context or the 'undef'
#// value if something goes wrong.
SV*
_EstablishContext (dwScope, pvReserved1, pvReserved2)
unsigned long dwScope
void* pvReserved1
void* pvReserved2
PREINIT:
SCARDCONTEXT hContext = 0;
CODE:
ST(0) = sv_newmortal();
gnLastError = hEstablishContext (dwScope, pvReserved1, pvReserved2, &hContext);
/* Then we either return an explicit 'UNDEF' value or the handle */
if (gnLastError != SCARD_S_SUCCESS) {
ST(0) = &PL_sv_undef;
} else {
sv_setiv (ST(0), hContext);
}
#///////////////////////////////////////////////////////////////////////////
#// ReleaseContext ()
#//
#// INPUT :
#// - $hContext -> Connection context to be closed
#//
#// OUTPUT :
#// - ReleaseContext returns a true value on successful operation and a
#// false value otherwise.
bool
_ReleaseContext (hContext)
unsigned long hContext
CODE:
gnLastError = hReleaseContext (hContext);
/* Then returns true or false according to the return code */
if (gnLastError != SCARD_S_SUCCESS) {
RETVAL = FALSE;
} else {
RETVAL = TRUE;
}
OUTPUT:
RETVAL
#///////////////////////////////////////////////////////////////////////////
#// ListReaders ()
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC resource manager.
#// - $mszGroups -> List of groups to list readers. (as of this writing,
#// this is not used and should be 0
#//
#// OUTPUT :
#// ListReaders returns the 'undef' value if an error occurs otherwise it
#// returns the list of available readers. Note that this can be an empty
#// list...
#//
SV *
_ListReaders(hContext, svGroups)
unsigned long hContext
SV* svGroups
PREINIT:
DWORD nBufferSize = 0;
char* szBuffer = NULL;
char* szCurrentToken = NULL;
char* mszGroups;
PPCODE:
/* Before doing anything, we check that we have a valid group. */
if (SvPOK(svGroups)) {
/* TODO : see how this works... multistring stuff with time
*svGroups may become a reference to an array of groups... or undef
*/
mszGroups = SvPV (svGroups, PL_na);
} else {
mszGroups = 0;
}
/* The first call to SCardListReaders gives us the size of the
* buffer we must allocate for the list.
*/
gnLastError = hListReaders (hContext, mszGroups, 0, &nBufferSize);
/* In case of any error we immediately return an explicit UNDEF value */
if (gnLastError != SCARD_S_SUCCESS) {
XSRETURN_UNDEF;
}
if (nBufferSize > 0) {
/* At this point, nBufferSize contains the size of the buffer to
* alloate. We use New as recomended by perlguts(3pm). The
* buffer will be freed with Safefree()...
*/
New (2018, szBuffer, nBufferSize, char);
if (szBuffer == NULL) {
gnLastError = SCARD_E_NO_MEMORY;
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* The buffer is ready, so we can now retrieve the whole list. */
gnLastError = hListReaders (hContext, mszGroups, szBuffer, &nBufferSize);
/* Then check for any error */
if (gnLastError != SCARD_S_SUCCESS) {
Safefree (szBuffer);
XSRETURN_UNDEF;
}
/* The string must be NULL terminated
* May be just too much paranoid but...
*/
if (szBuffer[nBufferSize-1] != 0) {
Safefree (szBuffer);
gnLastError = SCARD_F_INTERNAL_ERROR;
warn ("PCSC did not return a NULL terminated multistring at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Now we need to push each reader separately on the stack. If
* no readers are found, the stack is left empty...
*/
szCurrentToken = szBuffer;
while (strlen(szCurrentToken)) {
XPUSHs (sv_2mortal(newSVpv(szCurrentToken,0)));
szCurrentToken = strchr (szCurrentToken, 0) + 1;
}
/* Free our buffer...
* DO NOT USE free() or delete() here ! Let Perl deal with this.
*/
Safefree (szBuffer);
} else {
gnLastError = SCARD_F_INTERNAL_ERROR;
warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
#///////////////////////////////////////////////////////////////////////////
#// Connect ()
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC Resource Manager
#// - $szReader -> ReaderName to connect to
#// - dwShareMode -> Mode of connection : exclusive or shared
#// - dwPreferredProtocols -> Desired protocol use
#//
#// OUTPUT :
#// Connect returns an array with the handle to the connection and the
#// active protocol for this connection ($hCard, $dwActiveProtocol).
#// If a problem occurs, it just return the 'undef' value.
SV*
_Connect (hContext, szReader, dwShareMode, dwPreferredProtocols)
unsigned long hContext
const char* szReader
unsigned long dwShareMode
unsigned long dwPreferredProtocols
PREINIT:
SCARDHANDLE hCard = 0;
DWORD dwActiveProtocol = 0;
PPCODE:
gnLastError = hConnect (hContext, szReader, dwShareMode, dwPreferredProtocols, &hCard, &dwActiveProtocol);
/* We return immediately in case of an error */
if (gnLastError != SCARD_S_SUCCESS)
XSRETURN_UNDEF;
/* If anything was successful, push the two scalar values */
XPUSHs (sv_2mortal(newSViv(hCard)));
XPUSHs (sv_2mortal(newSViv(dwActiveProtocol)));
#///////////////////////////////////////////////////////////////////////////
#// Reconnect ()
#//
#// INPUT :
#// - $hCard -> Handle to a previous call to connect
#// - dwShareMode -> Mode of connection : exclusive or shared
#// - dwPreferredProtocols -> Desired protocol use
#// - $dwInitialization -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// Reconnect returns the new active protocol or the 'undef' value if an
#// error occurs
SV*
_Reconnect (hCard, dwShareMode, dwPreferredProtocols, dwInitialization)
unsigned long hCard
unsigned long dwShareMode
unsigned long dwPreferredProtocols
unsigned long dwInitialization
PREINIT:
DWORD dwActiveProtocol = 0;
CODE:
ST(0) = sv_newmortal();
gnLastError = hReconnect (hCard, dwShareMode, dwPreferredProtocols, dwInitialization, &dwActiveProtocol);
/* Return either an UNDEF value if an error occurs or the current
* active protocol as returned by PCSC
*/
if (gnLastError == SCARD_S_SUCCESS)
sv_setiv (ST(0), dwActiveProtocol);
else
ST(0) = &PL_sv_undef;
#///////////////////////////////////////////////////////////////////////////
#// Disconnect ()
#//
#// INPUT :
#// - $hCard -> Connection made from Connect
#// - $dwDisposition -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// Disconnect returns TRUE upon successful result. Oppositely, it
#// returns FALSE if somthing went bang
bool
_Disconnect (hCard, dwDisposition)
unsigned long hCard;
unsigned long dwDisposition;
CODE:
gnLastError = hDisconnect (hCard, dwDisposition);
/* Then check for an error */
if (gnLastError != SCARD_S_SUCCESS)
RETVAL = FALSE;
else
RETVAL = TRUE;
OUTPUT:
RETVAL
#///////////////////////////////////////////////////////////////////////////
#// Status ()
#//
#// INPUT :
#// - $hCard -> Connection made from Connect
#//
#// OUTPUT :
#// Status returns a list holding different informations about the
#// reader : ($szReaderName, $dwState, $dwProtocol, \@bAttr). If an
#// error pops up, the 'undef' value is returned.
#//
#// Important note:
#// We return the ATR in the form of a reference to an array of bytes.
#// When we build this reference, we start to build an array wich is
#// made mortal, then we fill it with non mortal items usin av_push().
#// These items need to be immortal at this point because av_push does
#// not increase the reference count of the scalar values it pushes into
#// the array. As our array dies when it passes out of scope, perl
#// would free its content and then any attempt to use them would result
#// in an 'Attempt to free unreferenced scalar' error...
SV*
_Status (hCard)
long hCard
PREINIT:
int nCount = 0;
#ifdef WIN32
char tmpReaderName[200];
char* szReaderName = tmpReaderName;
unsigned long cchReaderLen = sizeof(tmpReaderName);
char tmpAtr[MAX_ATR_SIZE];
unsigned char* pbAtr = tmpAtr;
unsigned long cbAtrLen = sizeof(tmpAtr);
#else
char* szReaderName = NULL;
DWORD cchReaderLen = 0;
unsigned char* pbAtr = NULL;
DWORD cbAtrLen = 0;
#endif
DWORD dwState = 0;
DWORD dwProtocol = 0;
AV* aATR = 0;
PPCODE:
/* We call the function with a null cchReaderLen : this should
* gives us the length of the buffer to allocate
*/
gnLastError = hStatus (hCard, szReaderName, &cchReaderLen,
&dwState, &dwProtocol, (BYTE *)pbAtr, &cbAtrLen);
/* Behaviour differs here from PCSC and PCSClite :
* PCSC returns SUCCESS while PCSClite returns an error
*/
if (gnLastError == SCARD_E_INSUFFICIENT_BUFFER || gnLastError == SCARD_S_SUCCESS) {
/* The call was hopefuly successful, so we allocate the
* buffer for the ATR
*/
#ifndef WIN32
/* This hack should be temporary as PCSClite should eventually behave like PCSC */
cbAtrLen = MAX_ATR_SIZE;
#endif
if (cbAtrLen <= 0) {
gnLastError = SCARD_F_INTERNAL_ERROR;
warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
New (2018, pbAtr, cbAtrLen, unsigned char);
if (pbAtr == NULL) {
gnLastError = SCARD_E_NO_MEMORY;
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* we then allocate the buffer for the reader name */
if (cbAtrLen <= 0) {
gnLastError = SCARD_F_INTERNAL_ERROR;
warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
New (2018, szReaderName, cchReaderLen, char);
if (szReaderName == NULL) {
Safefree (pbAtr);
gnLastError = SCARD_E_NO_MEMORY;
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Now we perform the real call to SCardStatus */
gnLastError = hStatus (hCard, szReaderName, &cchReaderLen,
&dwState, &dwProtocol, (BYTE *)pbAtr, &cbAtrLen);
if (gnLastError != SCARD_S_SUCCESS) {
Safefree (szReaderName);
Safefree (pbAtr);
XSRETURN_UNDEF;
}
} else {
/* As our first call should trigger the SCARD_E_INSUFFICIENT_BUFFER
* error or no error, we consider any other case as a failure...
*/
XSRETURN_UNDEF;
}
/* we fill this array with every byte from the ATR
* note that we do not make this data mortal because av_push()
* does not increment the reference count. See the note in the
* function header above
*/
if (cbAtrLen > 0) {
/* We first create an array for the ATR */
aATR = (AV*) sv_2mortal((SV*)newAV());
/* Then we fill it with every byte from the ATR
* note that we do not make this data mortal because av_push()
* does not increment the reference count. See the note in the
* function header above
*/
for (nCount=0; nCount < cbAtrLen; nCount++) {
av_push (aATR, newSViv(pbAtr[nCount]));
}
}
/* In the event that no ATR is available, we used to fill aATR
* with '(AV*) &PL_sv_undef' However, pushing a reference to
* this seems is hard to handle I therefore prefer to do
* nothing and leave aATR with the default null value and
* the code below will not push the reference to the ATR array
*/
/* eventually, we end up pushing all the values */
XPUSHs (sv_2mortal(newSVpv(szReaderName,0)));
XPUSHs (sv_2mortal(newSViv(dwState)));
XPUSHs (sv_2mortal(newSViv(dwProtocol)));
/* as well as a reference to the ATR array if available */
if (aATR) {
XPUSHs (sv_2mortal(newRV((SV*)aATR)));
}
/* As a conclusion, we just free what we took */
Safefree (szReaderName);
Safefree (pbAtr);
#///////////////////////////////////////////////////////////////////////////
#// Transmit ()
#//
#// INPUT :
#// - $hCard
#// - @inBuffer = ($Protocol, \@BytesToSend)
#// $Protocol contains the protocol (T0|T1)
#// @BytesToSend contains the bytes to transmit
#// Note: please note that @inBuffer is actually appended to the
#// parameters list, therefore, the following calls are equivalent:
#// @inBuffer ($Protocol, [0x00, 0x12, 0x33]); = ;Transmit ($hCard, @inBuffer);
#// Transmit ($hCard, $Protocol, [0x00, 0x12, 0x33]);
#//
#// OUTPUT :
#// - @outBuffer = ($Protocol, \@BytesRead)
#// - $Protocol may be undef
#// - @BytesRead contains the returned bytes
#// Transmit can return the 'undef' value alone if an error occurs.
SV*
_Transmit (hCard, dwProtocol, psvSendData)
unsigned long hCard;
unsigned long dwProtocol;
SV* psvSendData;
PREINIT:
int nCount = 0;
static char* pbSendBuffer = NULL;
static unsigned char pbRecvBuffer [MAX_BUFFER_SIZE_EXTENDED];
unsigned long cbSendLength = 0;
DWORD cbRecvLength = sizeof (pbRecvBuffer);
SCARD_IO_REQUEST ioSendPci, ioRecvPci;
AV* aRecvBuffer = NULL;
PPCODE:
/* We make sure that the array is sane */
if (psvSendData == NULL) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvSendData is a NULL pointer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Should the second parameter not be a reference, we return the
* SCARD_E_INVALID_PARAMETER error code.
*/
if ((!SvROK(psvSendData))||(SvTYPE(SvRV(psvSendData)) != SVt_PVAV)) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvSendData is not a RVAV at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* We have to build up our IO_REQUEST structures according to
* $dwProtocol
*/
switch (dwProtocol) {
case SCARD_PROTOCOL_T0:
case SCARD_PROTOCOL_T1:
case SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1:
case SCARD_PROTOCOL_RAW:
ioSendPci.dwProtocol = dwProtocol;
ioSendPci.cbPciLength = sizeof(ioSendPci);
ioRecvPci.dwProtocol = dwProtocol;
ioRecvPci.cbPciLength = sizeof(ioRecvPci);
break;
default:
/* If $dwProtocol holds an invalid value, we exist reporting
* the error SCARD_E_INVALID_VALUE.
*/
gnLastError = SCARD_E_INVALID_VALUE;
warn ("unknown protocol %ld given at %s line %d\n\t",
dwProtocol, __FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Let's allocate some space for the send buffer */
cbSendLength = av_len((AV*)SvRV(psvSendData)) + 1;
if (cbSendLength <= 0) {
gnLastError = SCARD_E_INVALID_VALUE;
warn ("empty array given at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
New (2018, pbSendBuffer, cbSendLength, char);
if (pbSendBuffer == NULL) {
gnLastError = SCARD_E_NO_MEMORY;
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* We have to extract data from the array referenced by psvSendData */
for (nCount = 0; nCount < cbSendLength ; nCount++)
pbSendBuffer[nCount] = (char)SvIV(*av_fetch((AV*)SvRV(psvSendData), nCount, 0));
/* Everything is ready : call the real function... */
gnLastError = hTransmit (hCard, &ioSendPci, (BYTE *)pbSendBuffer, cbSendLength, &ioRecvPci, pbRecvBuffer, &cbRecvLength);
if (gnLastError != SCARD_S_SUCCESS) {
/* Free the buffer if something went wrong */
Safefree (pbSendBuffer);
XSRETURN_UNDEF;
}
/* At this point, the command was successful. We still need to
* return all the values from our buffer...
* so we build an array for the ATR
*/
aRecvBuffer = (AV*) sv_2mortal((SV*)newAV());
/* we fill this array with every byte from the Response
* note that we do not make this data mortal because av_push()
* does not increment the reference count. See the note in the
* function header above
*/
for (nCount = 0; nCount < cbRecvLength; nCount++)
av_push (aRecvBuffer, newSViv(pbRecvBuffer[nCount]));
XPUSHs (sv_2mortal(newSViv(ioRecvPci.dwProtocol)));
XPUSHs (sv_2mortal(newRV((SV*)aRecvBuffer)));
/* Do not forget to free the dynamically allocated buffer */
Safefree (pbSendBuffer);
#///////////////////////////////////////////////////////////////////////////
#// Control ()
#//
#// INPUT :
#// - $hCard
#// - $dwControlCode
#// - @inBuffer = (\@BytesToSend)
#// @BytesToSend contains the bytes to transmit
#//
#// OUTPUT :
#// - @outBuffer = (\@BytesRead)
#// - @BytesRead contains the returned bytes
#// Control can return the 'undef' value alone if an error occurs.
SV*
_Control (hCard, dwControlCode, psvSendData)
unsigned long hCard;
unsigned long dwControlCode;
SV* psvSendData;
PREINIT:
int nCount = 0;
static char* pbSendBuffer = NULL;
static unsigned char pbRecvBuffer [MAX_BUFFER_SIZE];
unsigned long cbSendLength = 0;
DWORD cbRecvLength = sizeof (pbRecvBuffer);
AV* aRecvBuffer = NULL;
PPCODE:
/* We make sure that the array is sane */
if (psvSendData == NULL) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvSendData is a NULL pointer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Should the second parameter not be a reference, we return the
* SCARD_E_INVALID_PARAMETER error code.
*/
if ((!SvROK(psvSendData))||(SvTYPE(SvRV(psvSendData)) != SVt_PVAV)) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvSendData is not a RVAV at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
/* Let's allocate some space for the send buffer, if needed */
cbSendLength = av_len((AV*)SvRV(psvSendData)) + 1;
if (cbSendLength <= 0) {
gnLastError = SCARD_E_INVALID_VALUE;
warn ("empty array given at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
New (2018, pbSendBuffer, cbSendLength, char);
if (pbSendBuffer == NULL) {
gnLastError = SCARD_E_NO_MEMORY;
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_UNDEF;
}
for (nCount = 0; nCount < cbSendLength ; nCount++)
pbSendBuffer[nCount] = (char)SvIV(*av_fetch((AV*)SvRV(psvSendData), nCount, 0));
/* Everything is ready : call the real function... */
gnLastError = hControl (hCard, dwControlCode,
(cbSendLength > 0 ? (BYTE *)pbSendBuffer : NULL), cbSendLength,
pbRecvBuffer, sizeof(pbRecvBuffer), &cbRecvLength);
if (gnLastError != SCARD_S_SUCCESS) {
/* Free the buffer if something went wrong */
Safefree (pbSendBuffer);
XSRETURN_UNDEF;
}
/* At this point, the command was successful. We still need to
* return all the values from our buffer...
*/
aRecvBuffer = (AV*) sv_2mortal((SV*)newAV());
/* we fill this array with every byte from the Response
* note that we do not make this data mortal because av_push()
* does not increment the reference count. See the note in the
* function header above
*/
for (nCount = 0; nCount < cbRecvLength; nCount++)
av_push (aRecvBuffer, newSViv(pbRecvBuffer[nCount]));
// XPUSHs (sv_2mortal(newSViv(ioRecvPci.dwProtocol)));
XPUSHs (sv_2mortal(newRV((SV*)aRecvBuffer)));
/* Do not forget to free the dynamically allocated buffer */
Safefree (pbSendBuffer);
#///////////////////////////////////////////////////////////////////////////
#// BeginTransaction ()
#//
#// INPUT :
#// - $hCard -> connection made from Connect()
#//
#// OUTPUT :
#// BeginTransaction returns true or false depending on its successful
#// completion
unsigned long
_BeginTransaction (hCard)
unsigned long hCard;
CODE:
gnLastError = hBeginTransaction (hCard);
/* Then we check for an error */
if (gnLastError != SCARD_S_SUCCESS)
RETVAL = FALSE;
else
RETVAL = TRUE;
OUTPUT:
RETVAL
#///////////////////////////////////////////////////////////////////////////
#// EndTransaction ()
#//
#// INPUT :
#// - $hCard -> connection made from Connect()
#// - $dwDisposition -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// EndTransaction returns true or false depending on its successful
#// completion
unsigned long
_EndTransaction (hCard, dwDisposition)
unsigned long hCard;
unsigned long dwDisposition;
CODE:
gnLastError = hEndTransaction (hCard, dwDisposition);
/* Then we check for an error */
if (gnLastError != SCARD_S_SUCCESS)
RETVAL = FALSE;
else
RETVAL = TRUE;
OUTPUT:
RETVAL
#///////////////////////////////////////////////////////////////////////////
#// GetStatusChange ()
#//
#// This
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC resource manager.
#// - $nTimeout -> Time to wait for a change (or SCARD_INFINITE)
#// - \@ReaderStates -> array of reader states
#//
#// OUTPUT :
bool
_GetStatusChange (hContext, dwTimeout, psvReaderStates)
unsigned long hContext;
unsigned long dwTimeout;
SV* psvReaderStates;
PREINIT:
static SCARD_READERSTATE *rgReaderStates_t = NULL;
unsigned int nCount = 0;
unsigned int nATRCount = 0;
unsigned int nReaders = 0;
AV* aRecvBuffer = NULL;
PPCODE:
if (psvReaderStates == NULL) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvReaderStates is a NULL pointer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
/* Should the second parameter not be a reference, we return the
* SCARD_E_INVALID_PARAMETER error code.
*/
if ((!SvROK(psvReaderStates))||(SvTYPE(SvRV(psvReaderStates)) != SVt_PVAV)) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvReaderStates is not a RVAV at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
/* Get the total number of elements in our array */
nReaders = av_len((AV*)SvRV(psvReaderStates)) + 1;
/* free the memory allocated during previous call to GetStatusChange */
if (rgReaderStates_t)
Safefree(rgReaderStates_t);
/* allocate the Reader States table */
Newz(2018, rgReaderStates_t, nReaders, SCARD_READERSTATE);
if (rgReaderStates_t == NULL)
{
warn ("Could not allocate buffer at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
for (nCount = 0; nCount < nReaders; nCount++) {
/* As long as psvReaderStates is a reference to a PVAV we
* should be able to use av_fetch() without error
*/
SV *svCurrentToken = (*av_fetch((AV*)SvRV(psvReaderStates), nCount, 0));
/* Now see if the elements in the array are reference to hasharrays */
if ((!SvROK(svCurrentToken)) || (SvTYPE(SvRV(svCurrentToken)) != SVt_PVHV)) {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("psvReaderStates[%d] is not a RVHV at %s line %d\n\t", nCount,
__FILE__, __LINE__);
XSRETURN_NO;
}
/* Checkout if the 'name' argument has been passed */
if (hv_exists((HV*)SvRV(svCurrentToken), "reader_name", 11)) {
SV** psvName = NULL;
/* fetch the value of reader_name */
psvName = hv_fetch((HV*)SvRV(svCurrentToken), "reader_name", 11, 0);
/* Link the internal structure and the fetched pointer if appropriate */
if ((psvName != NULL) && (SvTYPE(*psvName) == SVt_PV)) {
// printf ("We got a name\n");
rgReaderStates_t[nCount].szReader = SvPV (*psvName, PL_na);
// printf ("which is : %s\n", rgReaderStates_t[nCount].szReader);
} else {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("reader_name is not valid (must be ASCII) at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
}
/* Checkout if the 'current_state' argument has been passed */
if (hv_exists((HV*)SvRV(svCurrentToken), "current_state", 13)) {
SV** psvCurrentState = NULL;
/* fetch the value of current_state */
psvCurrentState = hv_fetch((HV*)SvRV(svCurrentToken), "current_state", 13, 0);
/* Copy the current status into the struct */
if (psvCurrentState != NULL) {
if (SvTYPE(*psvCurrentState) == SVt_IV
|| SvTYPE(*psvCurrentState) == SVt_PVIV) {
// printf ("We got a current_state\n");
rgReaderStates_t[nCount].dwCurrentState = SvIV (*psvCurrentState);
// printf ("which is : 0x%lX\n", rgReaderStates_t[nCount].dwCurrentState);
} else {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("current_state is not valid (must be numeric) at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
}
}
/* Checkout if the 'event_state' argument has been passed */
if (hv_exists((HV*)SvRV(svCurrentToken), "event_state", 11)) {
SV** psvEventState = NULL;
/* fetch the value of current_state */
psvEventState = hv_fetch((HV*)SvRV(svCurrentToken), "event_state", 11, 0);
/* Copy the event status into the struct */
if (psvEventState != NULL) {
if (SvTYPE(*psvEventState) == SVt_IV
|| SvTYPE(*psvEventState) == SVt_PVIV) {
rgReaderStates_t[nCount].dwEventState = SvIV (*psvEventState);
} else {
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("event_state is not valid (must be numeric) at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
}
}
/* Checkout if the 'ATR' argument has been passed */
if (hv_exists((HV*)SvRV(svCurrentToken), "ATR", 3)) {
SV** psvATR = NULL;
/* fetch the value of ATR */
psvATR = hv_fetch((HV*)SvRV(svCurrentToken), "ATR", 3, 0);
if (psvATR != NULL) {
/* Make sure we have a reference to an array */
if ((SvTYPE(*psvATR) == SVt_RV) && (SvTYPE(SvRV(*psvATR)) == SVt_PVAV)) {
int nATR = 0;
/* Fetch the ATR length */
nATR = av_len((AV*)SvRV(*psvATR)) + 1;
for (nATRCount=0; nATRCount< nATR; nATRCount++) {
/* Fetch all bytes of th ATR one by one */
SV *svCurrentATRToken = (*av_fetch((AV*)SvRV(*psvATR), nATRCount, 0));
if (SvTYPE(svCurrentATRToken) != SVt_IV) {
/* Return SCARD_E_INVALID_PARAMETER if
* the ATR is not made only of numbers
*/
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("invalid ATR (not a reference to a numerical array) at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
rgReaderStates_t[nCount].rgbAtr[nATRCount] = (char)SvIV(svCurrentATRToken);
}
} else {
/* ATR is invalid therefore we return SCARD_E_INVALID_PARAMETER */
gnLastError = SCARD_E_INVALID_PARAMETER;
warn ("invalid ATR (not a reference to an array) at %s line %d\n\t",
__FILE__, __LINE__);
XSRETURN_NO;
}
}
}
}
/* Eventually call the real PCSC function */
gnLastError = hGetStatusChange (hContext, dwTimeout, rgReaderStates_t, nReaders);
/* Stop here upon failure */
if (gnLastError != SCARD_S_SUCCESS) {
XSRETURN_NO;
}
/* Upon successful completion, we have to propagate changes from
* the internalm structs to the hash arays, creating entries if
* required
*/
for (nCount = 0; nCount < nReaders; nCount++) {
/* As long as psvReaderStates is a reference to a PVAV we
* should be able to use av_fetch() without error
*/
SV *svCurrentToken = (*av_fetch((AV*)SvRV(psvReaderStates), nCount, 0));
/* Propagates changes to the reader_name... */
/* The name was mandatory so we should have it already
* linked to our value...
*/
/* Propagate changes to the current_state */
if (hv_exists((HV*)SvRV(svCurrentToken), "current_state", 13)) {
/* If the current_state was provided we modify its
* entry...
* Most checks were performed already so this is a
* simplified run...
*/
/* Copy the struct into current_state */
sv_setiv (*hv_fetch((HV*)SvRV(svCurrentToken), "current_state", 13, 0),
rgReaderStates_t[nCount].dwCurrentState);
} else {
/* If the current_state wasn't provided we create its
* entry
*/
hv_store ((HV*)SvRV(svCurrentToken), "current_state", 13,
newSViv(rgReaderStates_t[nCount].dwCurrentState), 0);
}
/* Propagate changes to the event_state */
if (hv_exists((HV*)SvRV(svCurrentToken), "event_state", 11)) {
/* If the event_state was provided we modify its
* entry...
* Most checks were performed already so this is a
* simplified run...
*/
/* Copy the struct into event_state */
sv_setiv (*hv_fetch((HV*)SvRV(svCurrentToken), "event_state", 11, 0),
rgReaderStates_t[nCount].dwEventState);
} else {
/* If the current_state wasn't provided we create its
* entry
*/
hv_store ((HV*)SvRV(svCurrentToken), "event_state", 11,
newSViv(rgReaderStates_t[nCount].dwEventState), 0);
}
/* Build the ATR if possible */
if (rgReaderStates_t[nCount].cbAtr > 0) {
/* Create an AV* with the ATR */
aRecvBuffer = (AV*) sv_2mortal((SV*)newAV());
for (nATRCount = 0; nATRCount <rgReaderStates_t[nCount].cbAtr; nATRCount++)
av_push (aRecvBuffer, newSViv(rgReaderStates_t[nCount].rgbAtr[nATRCount]));
/* Propagates changes to the ATR */
if (hv_exists((HV*)SvRV(svCurrentToken), "ATR", 11)) {
/* If the ATR was provided we modify its entry...
* Most checks were performed already so this is a
* simplified run...
*/
/* Copy the struct into the ATR */
sv_setsv (*hv_fetch((HV*)SvRV(svCurrentToken), "ATR", 3, 0),
sv_2mortal(newRV((SV*)aRecvBuffer)));
} else {
hv_store ((HV*)SvRV(svCurrentToken), "ATR", 3,
newRV((SV*)aRecvBuffer), 0);
}
} else {
/* Deletes the variable to make sure we do not keep some
* old outdated values
*/
hv_delete((HV*)SvRV(svCurrentToken), "ATR", 3, G_DISCARD);
}
}
XSRETURN_YES;
#///////////////////////////////////////////////////////////////////////////
#// Cancel ()
#//
#// This function cancels pending blocking requests from _GetStatusChange ()
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC resource manager.
#//
#// OUTPUT :
#// Cancel returns true upon successful conmpletion or false otherwise
bool
_Cancel (hContext)
unsigned long hContext
CODE:
gnLastError = hCancel (hContext);
/* Then we check for an error */
if (gnLastError != SCARD_S_SUCCESS)
RETVAL = FALSE;
else
RETVAL = TRUE;
OUTPUT:
RETVAL
# End of File #