diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..68e03c7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,69 @@
+--------------------------------------------------------------------
+ The PHP License, version 3.01
+Copyright (c) 1999 - 2010 The PHP Group. All rights reserved.
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. The name "PHP" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact group@php.net.
+
+ 4. Products derived from this software may not be called "PHP", nor
+ may "PHP" appear in their name, without prior written permission
+ from group@php.net. You may indicate that your software works in
+ conjunction with PHP by saying "Foo for PHP" instead of calling
+ it "PHP Foo" or "phpfoo"
+
+ 5. The PHP Group may publish revised and/or new versions of the
+ license from time to time. Each version will be given a
+ distinguishing version number.
+ Once covered code has been published under a particular version
+ of the license, you may always continue to use it under the terms
+ of that version. You may also choose to use such covered code
+ under the terms of any subsequent version of the license
+ published by the PHP Group. No one other than the PHP Group has
+ the right to modify the terms applicable to covered code created
+ under this License.
+
+ 6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes PHP software, freely available from
+ ".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the PHP Group.
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project,
+please see .
+
+PHP includes the Zend Engine, freely available at
+.
+
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..b4953c5
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,2 @@
+pcsc
+Johann Dantant, Johannes Findeisen
diff --git a/README b/README
new file mode 100644
index 0000000..073b7d2
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+This is totally unstable! Don't use in production environments!
+
+You have been warned... ;)
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..4bc0433
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+Some things that need to be done:
+
+- Find all leaks and fix them
+- Write all function documentation
+- Write complete API documentation
+- ...
+
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..ced013e
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+#echo "Cleaning up ...\n"
+
+make clean
+phpize --clean
+
+#echo "Compile package...\n"
+
+phpize
+aclocal
+libtoolize --force
+autoheader
+autoconf
+
+#echo "Configure package...\n"
+
+./configure --enable-pcsc --prefix=/usr
+
+make
+
+#echo "Now type su and the make install...\n"
+
+
diff --git a/config.m4 b/config.m4
new file mode 100644
index 0000000..567292d
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,39 @@
+dnl $Id$
+dnl config.m4 for PC/SC extension
+
+PHP_ARG_ENABLE(pcsc, PC/SC Smart Card support for PHP,
+[ --enable-pcsc Enable PC/SC Smart Card support])
+
+dnl Check whether the extension is enabled at all
+if test "$PHP_PCSC" != "no"; then
+ if test -r $PHP_PCSC/include/PCSC/winscard.h; then
+ PCSC_DIR=$PHP_PCSC
+ else
+ AC_MSG_CHECKING(for PC/SC in default path)
+ for i in /usr/local /usr; do
+ if test -r $i/include/PCSC/winscard.h; then
+ PCSC_DIR=$i
+ AC_MSG_RESULT(found in $i)
+ break
+ fi
+ done
+ fi
+
+ if test -z "$PCSC_DIR"; then
+ AC_MSG_RESULT(not found)
+ AC_MSG_ERROR(Please reinstall the pcsc-lite distribution -
+ winscard.h should be in /include/PCSC/)
+ fi
+ AC_MSG_RESULT($PCSC_DIR)
+ AC_MSG_RESULT($PCSC_DIR)
+ AC_MSG_RESULT($PCSC_DIR)
+ PHP_ADD_INCLUDE($PCSC_DIR/include/PCSC/)
+ dnl PHP_EVAL_LIBLINE($PCSC_LIBS, PCSC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY_WITH_PATH(pcsclite, "$PCSC_DIR/lib/", PCSC_SHARED_LIBADD)
+
+ dnl Finally, tell the build system about the extension and what files are needed
+ PHP_NEW_EXTENSION(pcsc, pcsc.c, $ext_shared)
+ PHP_INSTALL_HEADERS([php_pcsc.h])
+ PHP_SUBST(PCSC_SHARED_LIBADD)
+fi
+
diff --git a/pcsc.c b/pcsc.c
new file mode 100644
index 0000000..a3f4f0c
--- /dev/null
+++ b/pcsc.c
@@ -0,0 +1,536 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2006 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Johannes Findeisen - 2010 |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_pcsc.h"
+
+#include
+#include
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+/* {{{ pcsc_functions[]
+ *
+ * Every user visible function must have an entry in pcsc_functions[].
+ */
+function_entry pcsc_functions[] = {
+ PHP_FE(scard_establish_context, NULL)
+ PHP_FE(scard_release_context, NULL)
+ PHP_FE(scard_is_valid_context, NULL)
+ PHP_FE(scard_list_readers, NULL)
+ PHP_FE(scard_connect, NULL)
+ //PHP_FE(scard_reconnect, NULL)
+ PHP_FE(scard_disconnect, NULL)
+ PHP_FE(scard_transmit, NULL)
+ PHP_FE(scard_status, NULL)
+ //PHP_FE(scard_cancel, NULL)
+ {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* {{{ pcsc_module_entry
+ */
+zend_module_entry pcsc_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "PC/SC",
+ pcsc_functions,
+ PHP_MINIT(pcsc),
+ PHP_MSHUTDOWN(pcsc),
+ NULL,
+ NULL,
+ PHP_MINFO(pcsc),
+#if ZEND_MODULE_API_NO >= 20010901
+ "0.0.1", /* Replace with version number for your extension */
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_PCSC
+ZEND_GET_MODULE(pcsc)
+#endif
+
+#define REGISTER_PCSC_CONSTANT(__c) REGISTER_LONG_CONSTANT(#__c, __c, CONST_CS | CONST_PERSISTENT)
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(pcsc)
+{
+ /* Return Codes */
+ REGISTER_PCSC_CONSTANT(SCARD_E_BAD_SEEK);
+ REGISTER_PCSC_CONSTANT(SCARD_E_CANCELLED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_CANT_DISPOSE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_CARD_UNSUPPORTED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_CERTIFICATE_UNAVAILABLE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_COMM_DATA_LOST);
+ REGISTER_PCSC_CONSTANT(SCARD_E_DIR_NOT_FOUND);
+ REGISTER_PCSC_CONSTANT(SCARD_E_DUPLICATE_READER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_FILE_NOT_FOUND);
+ REGISTER_PCSC_CONSTANT(SCARD_E_ICC_CREATEORDER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_ICC_INSTALLATION);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INSUFFICIENT_BUFFER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_ATR);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_CHV);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_HANDLE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_PARAMETER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_TARGET);
+ REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_VALUE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_ACCESS);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_DIR);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_FILE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_KEY_CONTAINER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_MEMORY);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_READERS_AVAILABLE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_SERVICE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_SMARTCARD);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NO_SUCH_CERTIFICATE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NOT_READY);
+ REGISTER_PCSC_CONSTANT(SCARD_E_NOT_TRANSACTED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_PCI_TOO_SMALL);
+ REGISTER_PCSC_CONSTANT(SCARD_E_PROTO_MISMATCH);
+ REGISTER_PCSC_CONSTANT(SCARD_E_READER_UNAVAILABLE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_READER_UNSUPPORTED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_SERVER_TOO_BUSY);
+ REGISTER_PCSC_CONSTANT(SCARD_E_SERVICE_STOPPED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_SHARING_VIOLATION);
+ REGISTER_PCSC_CONSTANT(SCARD_E_SYSTEM_CANCELLED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_TIMEOUT);
+ REGISTER_PCSC_CONSTANT(SCARD_E_UNEXPECTED);
+ REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_READER);
+ REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_RES_MNG);
+ REGISTER_PCSC_CONSTANT(SCARD_E_UNSUPPORTED_FEATURE);
+ REGISTER_PCSC_CONSTANT(SCARD_E_WRITE_TOO_MANY);
+
+ REGISTER_PCSC_CONSTANT(SCARD_F_COMM_ERROR);
+ REGISTER_PCSC_CONSTANT(SCARD_F_INTERNAL_ERROR);
+ REGISTER_PCSC_CONSTANT(SCARD_F_UNKNOWN_ERROR);
+ REGISTER_PCSC_CONSTANT(SCARD_F_WAITED_TOO_LONG);
+
+ REGISTER_PCSC_CONSTANT(SCARD_P_SHUTDOWN);
+
+ REGISTER_PCSC_CONSTANT(SCARD_S_SUCCESS);
+
+ REGISTER_PCSC_CONSTANT(SCARD_W_CANCELLED_BY_USER);
+ REGISTER_PCSC_CONSTANT(SCARD_W_CARD_NOT_AUTHENTICATED);
+ REGISTER_PCSC_CONSTANT(SCARD_W_CHV_BLOCKED);
+ REGISTER_PCSC_CONSTANT(SCARD_W_EOF);
+ REGISTER_PCSC_CONSTANT(SCARD_W_REMOVED_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_W_RESET_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_W_SECURITY_VIOLATION);
+ REGISTER_PCSC_CONSTANT(SCARD_W_UNPOWERED_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_W_UNRESPONSIVE_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_W_UNSUPPORTED_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_W_WRONG_CHV);
+
+ /* Protocols */
+ REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_RAW);
+ REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_T0);
+ REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_T1);
+
+ /* Disconnect Modes */
+ REGISTER_PCSC_CONSTANT(SCARD_EJECT_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_LEAVE_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_RESET_CARD);
+ REGISTER_PCSC_CONSTANT(SCARD_UNPOWER_CARD);
+
+ /* Status codes */
+ REGISTER_PCSC_CONSTANT(SCARD_ABSENT);
+ REGISTER_PCSC_CONSTANT(SCARD_NEGOTIABLE);
+ REGISTER_PCSC_CONSTANT(SCARD_POWERED);
+ REGISTER_PCSC_CONSTANT(SCARD_PRESENT);
+ REGISTER_PCSC_CONSTANT(SCARD_SPECIFIC);
+ REGISTER_PCSC_CONSTANT(SCARD_SWALLOWED);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(pcsc)
+{
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(pcsc)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "PC/SC support", "enabled");
+ php_info_print_table_end();
+}
+/* }}} */
+
+static char *e_bytes_to_hex(BYTE *buffer, DWORD length)
+{
+ DWORD i;
+ char tmp[3];
+ char *str;
+
+ str = emalloc(2 * length + 1);
+ str[0] = '\0';
+
+ for (i = 0; i < length; i++) {
+ sprintf(tmp, "%02X", buffer[i]);
+ strcat(str, tmp);
+ }
+ return str;
+}
+
+static BYTE hex_to_nibble(char chr)
+{
+ if ((chr >= '0') && (chr <= '9')) {
+ return chr - '0';
+ }
+ if ((chr >= 'A') && (chr <= 'F')) {
+ return chr - 'A' + 10;
+ }
+ if ((chr >= 'a') && (chr <= 'f')) {
+ return chr - 'a' + 10;
+ }
+ return 0;
+}
+
+static BYTE hex_to_byte(char *str)
+{
+ return hex_to_nibble(str[0]) * 0x10 + hex_to_nibble(str[1]);
+}
+
+static BYTE *e_hex_to_bytes(char *str, DWORD *length)
+{
+ DWORD i, l;
+ BYTE *buf;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ l = strlen(str) / 2;
+ if (length != NULL) {
+ *length = l;
+ }
+
+ buf = emalloc(l);
+
+ for (i = 0; i < l; i++) {
+ buf[i] = hex_to_byte(&str[2 * i]);
+ }
+ return buf;
+}
+
+/* {{{ proto array pcsc_establishcontext()
+ Return a a context to the PC/SC layer */
+PHP_FUNCTION(scard_establish_context)
+{
+ /* TODO: Make the use of SCARDCONTEXT for the context everywhere instead of LONG */
+ SCARDCONTEXT scard_context = 0;
+ LONG rc = 0;
+
+ rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_context);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ RETURN_FALSE;
+ }
+ RETURN_LONG(scard_context);
+}
+/* }}} */
+
+/* {{{ proto boolean scard_is_valid_context(long context)
+ Return a a context to the PC/SC layer */
+PHP_FUNCTION(scard_is_valid_context)
+{
+ LONG rc = 0;
+ LONG *context;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) {
+ return;
+ }
+
+ rc = SCardIsValidContext(context);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array pcsc_establishcontext(long context)
+ Return a a context to the PC/SC layer */
+PHP_FUNCTION(scard_release_context)
+{
+ LONG rc = 0;
+ LONG *context;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) {
+ return;
+ }
+
+ rc = SCardIsValidContext(context);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ rc = SCardReleaseContext(context);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+ RETURN_LONG(context);
+}
+/* }}} */
+
+/* {{{ proto array pcsc_listreaders(long context)
+ Return a an array of available PC/SC readers */
+PHP_FUNCTION(scard_list_readers)
+{
+ char *strReaders = NULL;
+ char *ptrReader;
+ DWORD dwLen = SCARD_AUTOALLOCATE;
+ LONG rc = 0;
+ LONG *context;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) {
+ return;
+ }
+
+ rc = SCardIsValidContext(context);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ rc = SCardListReaders(context, NULL, (char *) &strReaders, &dwLen);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ if (strReaders == NULL) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+
+ ptrReader = strReaders;
+ do {
+ add_next_index_string(return_value, ptrReader, TRUE);
+ ptrReader+= strlen(ptrReader);
+ ptrReader++;
+ } while (*ptrReader != '\0');
+
+ SCardFreeMemory(context, strReaders);
+}
+/* }}} */
+
+/* {{{ proto long scardconnect(long context, string reader_name, [long preferred_protocol], [long ¤t_protocol])
+ Return a handle to the card */
+ /* TODO: Add support for the protocol parameters; default is "T=1" now and not changeable */
+PHP_FUNCTION(scard_connect)
+{
+ DWORD dwPreferredProtocol = SCARD_PROTOCOL_T1;
+ DWORD dwCurrentProtocol;
+ SCARDHANDLE hCard = 0;
+ LONG rc = 0;
+ LONG *context;
+ char *strReaderName;
+ int strReaderNameLen;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &context, &strReaderName, &strReaderNameLen) == FAILURE) {
+ return;
+ }
+
+ rc = SCardIsValidContext(context);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ rc = SCardConnect(context, strReaderName, SCARD_SHARE_SHARED, dwPreferredProtocol, &hCard, &dwCurrentProtocol);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(hCard);
+}
+/* }}} */
+
+/* {{{ proto boolean scarddisconnect(long card_handle, [long disposition])
+ Close a card connection obtained with scardconnect */
+ /* TODO: Add support for disposition. */
+PHP_FUNCTION(scard_disconnect)
+{
+ DWORD dwDisposition = SCARD_EJECT_CARD;
+ LONG rc;
+ SCARDHANDLE *hCard;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) {
+ return;
+ }
+
+ /*if (argc >= 2) {
+ convert_to_long(ARG(1));
+ dwDisposition = ARG(1)->value.lval;
+ }*/
+
+ rc = SCardDisconnect(hCard, dwDisposition);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string scardtransmit(long card_handle, string send_command)
+ Transmit (and receive) data to (and from) the card */
+PHP_FUNCTION(scard_transmit)
+{
+ SCARDHANDLE hCard;
+ DWORD dwProtocol;
+ DWORD dwState;
+ const SCARD_IO_REQUEST *sendPci = NULL;
+ SCARD_IO_REQUEST *recvPci = NULL;
+ BYTE *sendBuffer;
+ BYTE *recvBuffer;
+ DWORD sendLen, recvLen;
+ LONG rc;
+ char *apdu;
+ int apduLen;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &hCard, &apdu, &apduLen) == FAILURE) {
+ return;
+ }
+
+ /* Are we in T=0 or T=1 ? */
+ rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, NULL, NULL);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ if (!(dwState & SCARD_POWERED)) {
+ RETURN_FALSE;
+ }
+
+ switch (dwProtocol) {
+ case SCARD_PROTOCOL_RAW : sendPci = SCARD_PCI_RAW; break;
+ case SCARD_PROTOCOL_T0 : sendPci = SCARD_PCI_T0; break;
+ case SCARD_PROTOCOL_T1 : sendPci = SCARD_PCI_T1; break;
+ }
+
+ /* Get buffer to send */
+ sendBuffer = e_hex_to_bytes(apdu, &sendLen);
+
+ /* Guess the size of the buffer to recv */
+ recvLen = 2 + 256;
+ recvBuffer = emalloc(recvLen);
+
+ /* Perform the exchange */
+ recvPci = emalloc(1024);
+ recvPci->dwProtocol = dwProtocol;
+ recvPci->cbPciLength = 1024;
+
+ rc = SCardTransmit(hCard, sendPci, sendBuffer, sendLen, recvPci, recvBuffer, &recvLen);
+ if (rc != SCARD_S_SUCCESS) {
+ efree(recvPci);
+ efree(recvBuffer);
+ efree(sendBuffer);
+ RETURN_FALSE;
+ }
+ efree(recvPci);
+
+ /* Return the response */
+ RETVAL_STRING(e_bytes_to_hex(recvBuffer, recvLen), TRUE);
+
+ /* Free local data */
+ efree(recvBuffer);
+ efree(sendBuffer);
+}
+/* }}} */
+
+/* {{{ proto array scardstatus([long card_handle])
+ Retrieve reader's or card's status */
+PHP_FUNCTION(scard_status)
+{
+ SCARDHANDLE hCard = 0;
+ char *strReader = NULL;
+ DWORD dwProtocol, dwState;
+ BYTE atrBuffer[32];
+ DWORD atrLen;
+ LONG rc;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) {
+ return;
+ }
+
+ atrLen = sizeof(atrBuffer);
+ rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, atrBuffer, &atrLen);
+ if (rc != SCARD_S_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ add_assoc_long(return_value, "state", dwState);
+ if (dwState & SCARD_ABSENT)
+ add_assoc_long(return_value, "SCARD_ABSENT", 1);
+
+ if (dwState & SCARD_PRESENT)
+ add_assoc_long(return_value, "SCARD_PRESENT", 1);
+
+ if (dwState & SCARD_SWALLOWED)
+ add_assoc_long(return_value, "SCARD_SWALLOWED", 1);
+
+ if (dwState & SCARD_POWERED)
+ add_assoc_long(return_value, "SCARD_POWERED", 1);
+
+ if (dwState & SCARD_NEGOTIABLE)
+ add_assoc_long(return_value, "SCARD_NEGOTIABLE", 1);
+
+ if (dwState & SCARD_SPECIFIC)
+ add_assoc_long(return_value, "SCARD_SPECIFIC", 1);
+
+ switch (dwProtocol) {
+ /* Maybe change to bool type here instead of add_assoc_long...? */
+ case SCARD_PROTOCOL_RAW : add_assoc_long(return_value, "SCARD_PROTOCOL_RAW", 1);
+ add_assoc_string(return_value, "PROTOCOL", "RAW", TRUE);
+ break;
+ case SCARD_PROTOCOL_T0 : add_assoc_long(return_value, "SCARD_PROTOCOL_T0", 1);
+ add_assoc_string(return_value, "PROTOCOL", "T=0", TRUE);
+ break;
+ case SCARD_PROTOCOL_T1 : add_assoc_long(return_value, "SCARD_PROTOCOL_T1", 1);
+ add_assoc_string(return_value, "PROTOCOL", "T=1", TRUE);
+ break;
+ default : add_assoc_long(return_value, "PROTOCOL", dwProtocol);
+ }
+
+ if (atrLen) {
+ char *atrString;
+ atrString = e_bytes_to_hex(atrBuffer, atrLen);
+ add_assoc_string(return_value, "ATR", atrString, TRUE);
+ efree(atrString);
+ }
+}
+/* }}} */
diff --git a/php/pcsc.class.php b/php/pcsc.class.php
new file mode 100644
index 0000000..897edda
--- /dev/null
+++ b/php/pcsc.class.php
@@ -0,0 +1,55 @@
+context = scard_establish_context();
+ }
+
+ function __destruct() {
+ $this->context = scard_release_context($this->context);
+ }
+
+ function connect($reader) {
+ return $this->connection = scard_connect($this->context, $reader);
+ }
+
+ /*function reconnect() {
+ return $this->connection = scard_reconnect($this->connection);
+ }*/
+
+ function disconnect() {
+ return scard_disconnect($this->connection);
+ }
+
+ function listreaders() {
+ return $this->readers = scard_list_readers($this->context);
+ }
+
+ function transmit($apdu) {
+ return scard_transmit($this->connection, $apdu);
+ }
+
+ function isvalidcontext() {
+ return scard_is_valid_context($this->context);
+ }
+
+ function releasecontext() {
+ return scard_release_context($this->context);
+ }
+
+ function status() {
+ return scard_status($this->connection);
+ }
+}
+
+?>
diff --git a/php/test.php b/php/test.php
new file mode 100644
index 0000000..61aa86a
--- /dev/null
+++ b/php/test.php
@@ -0,0 +1,43 @@
+context);
+
+echo "List Readers:\n";
+var_dump($foo->listreaders());
+
+echo "Connect Card:\n";
+var_dump($foo->connect("OMNIKEY CardMan 5x21 00 01"));
+
+#echo "Reconnect:\n";
+#var_dump($foo->reconnect());
+
+echo "Some Transmits:\n";
+var_dump($foo->transmit("00a4040c0cD2760001354B414E4D30310000"));
+var_dump($foo->transmit("0084000008"));
+var_dump($foo->transmit("0084000008"));
+var_dump($foo->transmit("0084000008"));
+
+echo "Is Valid Context?\n";
+var_dump($foo->isvalidcontext());
+
+echo "Status:\n";
+var_dump($foo->status());
+
+echo "Disconnect Card:\n";
+var_dump($foo->disconnect());
+
+echo "Release Context:\n";
+var_dump($foo->releasecontext());
+
+?>
diff --git a/php_pcsc.h b/php_pcsc.h
new file mode 100644
index 0000000..42ba42f
--- /dev/null
+++ b/php_pcsc.h
@@ -0,0 +1,32 @@
+#ifndef PHP_PCSC_H
+#define PHP_PCSC_H
+
+extern zend_module_entry pcsc_module_entry;
+#define phpext_pcsc_ptr &pcsc_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_PCSC_API __declspec(dllexport)
+#else
+#define PHP_PCSC_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pcsc);
+PHP_MSHUTDOWN_FUNCTION(pcsc);
+PHP_MINFO_FUNCTION(pcsc);
+
+PHP_FUNCTION(scard_establish_context);
+PHP_FUNCTION(scard_is_valid_context);
+PHP_FUNCTION(scard_release_context);
+PHP_FUNCTION(scard_list_readers);
+PHP_FUNCTION(scard_connect);
+//PHP_FUNCTION(scard_reconnect);
+PHP_FUNCTION(scard_disconnect);
+PHP_FUNCTION(scard_status);
+PHP_FUNCTION(scard_transmit);
+//PHP_FUNCTION(scard_cancel);
+
+#endif /* PHP_PCSC_H */
diff --git a/tmp/pcsc2.c b/tmp/pcsc2.c
new file mode 100644
index 0000000..1226c79
--- /dev/null
+++ b/tmp/pcsc2.c
@@ -0,0 +1,496 @@
+PHP_FUNCTION(pcsc_test)
+{
+
+ //php_printf("Huhudeldiehu: %d::\n", hContext);
+
+ char *strReaders = "";
+ char *ptrReader;
+ DWORD dwLen = SCARD_AUTOALLOCATE;
+ LONG rc3;
+
+ /*
+ LONG rc;
+ rc = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ return FAILURE;
+ }
+ */
+
+
+
+
+/* php_printf("%d: %d
\n", __LINE__, SCARD_S_SUCCESS);
+ php_printf("%d: %d
\n", __LINE__, lLastError);
+ php_printf("%d: %d
\n", __LINE__, hContext);
+ rc3 = SCardListReaders(hContext, NULL, (char *) &strReaders, &dwLen);
+ php_printf("%d: %d
\n", __LINE__, hContext);
+ php_printf("%d: %d
\n", __LINE__, lLastError);
+*/
+
+ if (rc3 != SCARD_S_SUCCESS)
+ {
+ php_printf("%d: %d
\n", __LINE__, 1);
+ lLastError = rc3;
+ php_printf("%d: %d
\n", __LINE__, rc3);
+ //RETURN_FALSE;
+ }
+
+ if (strReaders == NULL) {
+ php_printf("%d: %d
\n", __LINE__, 2);
+ //RETURN_FALSE;
+ }
+
+ array_init(return_value);
+
+ ptrReader = strReaders;
+ do
+ {
+ add_next_index_string(return_value, ptrReader, TRUE);
+ ptrReader += strlen(ptrReader); /* End of the line */
+ ptrReader ++; /* Beginning of next list */
+ } while (*ptrReader != '\0');
+
+ SCardFreeMemory(hContext, strReaders);
+
+
+
+}
+
+
+/* {{{ proto long scardconnect(string reader_name, [long preferred_protocol], [long ¤t_protocol])
+ Return a handle to the card */
+PHP_FUNCTION(scardconnect)
+{
+ DWORD dwPreferredProtocol, dwCurrentProtocol;
+ SCARDHANDLE hCard;
+ char *strReader;
+ LONG rc;
+ PARSE_ARGS(1, 3);
+
+ hLastCard = 0;
+
+ if (ARG(0)->type == IS_LONG)
+ {
+ /* Find reader by index */
+ char *strReaders = NULL;
+ char *ptrReader;
+ LONG idxReader;
+ DWORD dwLen = SCARD_AUTOALLOCATE;
+
+ /* Reader name not supplied, select it in readers list */
+ rc = SCardListReaders(hContext, NULL, (char *) &strReaders, &dwLen);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ if (strReaders == NULL)
+ RETURN_FALSE;
+
+ /* Loop */
+ idxReader = 0;
+ ptrReader = strReaders;
+ do
+ {
+ if (idxReader == ARG(0)->value.lval)
+ {
+ strReader = estrdup(ptrReader);
+ break;
+ }
+ idxReader ++;
+ ptrReader += strlen(ptrReader); /* End of the line */
+ ptrReader ++; /* Beginning of next list */
+ } while (*ptrReader != '\0');
+
+ SCardFreeMemory(hContext, strReaders);
+
+ /* Check */
+ if (strReader == NULL)
+ RETURN_FALSE; /* Reader not found ? */
+ } else
+ {
+ /* Use reader's name */
+ strReader = estrdup(ARG(0)->value.str.val);
+ }
+
+ /* Preferred protocol */
+ if (argc >= 2)
+ {
+ convert_to_long(ARG(1));
+ dwPreferredProtocol = ARG(1)->value.lval;
+ } else
+ {
+ /* dwPreferredProtocol = SCARD_PROTOCOL_DEFAULT | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; */
+ dwPreferredProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
+ }
+
+ /* Perform the function */
+ rc = SCardConnect(hContext, strReader, SCARD_SHARE_SHARED, dwPreferredProtocol, &hCard, &dwCurrentProtocol);
+
+ /* Free the string ASAP */
+ efree(strReader);
+
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ /* Current protocol */
+ if ((argc >= 3) && (PZVAL_IS_REF(ARG(2))))
+ {
+ ARG(2)->value.lval = dwCurrentProtocol;
+ ARG(2)->type = IS_LONG;
+ }
+
+ /* We store a copy of the handle in case we need it in a shortcut function */
+ hLastCard = hCard;
+
+ /* We return the handle */
+ RETURN_LONG(hCard);
+}
+/* }}} */
+
+/* {{{ proto boolean scardreconnect([long card_handle], [long preferred_protocol], [long initialisation], [long ¤t_protocol])
+ Return a handle to the card */
+PHP_FUNCTION(scardreconnect)
+{
+ DWORD dwPreferredProtocol, dwInitialization, dwCurrentProtocol;
+ SCARDHANDLE hCard;
+ LONG rc;
+ PARSE_ARGS(0, 4);
+
+ hLastCard = 0;
+
+ if (argc >= 1)
+ {
+ convert_to_long(ARG(0));
+ hCard = ARG(0)->value.lval;
+ } else
+ {
+ hCard = hLastCard;
+ }
+
+ /* Preferred protocol */
+ if (ARG(1) != NULL)
+ {
+ convert_to_long(ARG(1));
+ dwPreferredProtocol = ARG(1)->value.lval;
+ } else
+ {
+ /*
+ dwPreferredProtocol = SCARD_PROTOCOL_DEFAULT | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
+ */
+ dwPreferredProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
+ }
+
+ /* Initialisation */
+ if (ARG(2) != NULL)
+ {
+ convert_to_long(ARG(2));
+ dwInitialization = ARG(2)->value.lval;
+ } else
+ {
+ dwInitialization = SCARD_LEAVE_CARD;
+ }
+
+ /* Perform the function */
+ rc = SCardReconnect(hCard, SCARD_SHARE_SHARED, dwPreferredProtocol, dwInitialization, &dwCurrentProtocol);
+
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ /* Current protocol */
+ if ((ARG(3) != NULL) && (PZVAL_IS_REF(ARG(3))))
+ {
+ ARG(3)->value.lval = dwCurrentProtocol;
+ ARG(3)->type = IS_LONG;
+ }
+
+ /* OK */
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto boolean scarddisconnect([long card_handle], [long disposition])
+ Close a card connection obtained with scardconnect */
+PHP_FUNCTION(scarddisconnect)
+{
+ SCARDHANDLE hCard;
+ DWORD dwDisposition;
+ LONG rc;
+ PARSE_ARGS(0, 2);
+
+ /* Retrieve parameters */
+ hCard = hLastCard;
+ dwDisposition = SCARD_EJECT_CARD;
+
+ if (argc >= 1)
+ {
+ convert_to_long(ARG(0));
+ hLastCard = ARG(0)->value.lval;
+ }
+
+ if (argc >= 2)
+ {
+ convert_to_long(ARG(1));
+ dwDisposition = ARG(1)->value.lval;
+ }
+
+ /* Disconnect */
+ rc = SCardDisconnect(hCard, dwDisposition);
+
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ /* Drop current card handle */
+ if (hLastCard == hCard)
+ hLastCard = 0;
+
+ /* OK */
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array scardstatus([long card_handle])
+ Retrieve reader's or card's status */
+PHP_FUNCTION(scardstatus)
+{
+ SCARDHANDLE hCard = 0;
+ char *strReader = NULL;
+ DWORD dwProtocol, dwState;
+ BYTE atrBuffer[32];
+ DWORD atrLen;
+ LONG rc;
+ PARSE_ARGS(0, 1);
+
+ /* Retrieve parameters */
+ if (argc >= 1)
+ {
+ hCard = ARG(0)->value.lval;
+ } else
+ {
+ hCard = hLastCard;
+ }
+
+ /* Read card's status */
+ atrLen = sizeof(atrBuffer);
+ rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, atrBuffer, &atrLen);
+
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ /* Put result in an array */
+ array_init(return_value);
+
+ add_assoc_long(return_value, "state", dwState);
+
+ if (dwState & SCARD_ABSENT)
+ add_assoc_long(return_value, "SCARD_ABSENT", 1);
+
+ if (dwState & SCARD_PRESENT)
+ add_assoc_long(return_value, "SCARD_PRESENT", 1);
+
+ if (dwState & SCARD_SWALLOWED)
+ add_assoc_long(return_value, "SCARD_SWALLOWED", 1);
+
+ if (dwState & SCARD_POWERED)
+ add_assoc_long(return_value, "SCARD_POWERED", 1);
+
+ if (dwState & SCARD_NEGOTIABLE)
+ add_assoc_long(return_value, "SCARD_NEGOTIABLE", 1);
+
+ if (dwState & SCARD_SPECIFIC)
+ add_assoc_long(return_value, "SCARD_SPECIFIC", 1);
+
+ switch (dwProtocol)
+ {
+ case SCARD_PROTOCOL_RAW : add_assoc_long(return_value, "SCARD_PROTOCOL_RAW", 1);
+ add_assoc_string(return_value, "protocol", "RAW", TRUE);
+ break;
+ case SCARD_PROTOCOL_T0 : add_assoc_long(return_value, "SCARD_PROTOCOL_T0", 1);
+ add_assoc_string(return_value, "protocol", "T=0", TRUE);
+ break;
+ case SCARD_PROTOCOL_T1 : add_assoc_long(return_value, "SCARD_PROTOCOL_T1", 1);
+ add_assoc_string(return_value, "protocol", "T=1", TRUE);
+ break;
+ default : add_assoc_long(return_value, "protocol", dwProtocol);
+ }
+
+ if (atrLen)
+ {
+ char *atrString;
+ atrString = e_bytes_to_hex(atrBuffer, atrLen);
+ add_assoc_string(return_value, "atr", atrString, TRUE);
+ efree(atrString);
+ }
+}
+/* }}} */
+
+/* {{{ proto array scardgetstatuschange(string reader_name, [long current_state])
+ Retrieve the status of a reader */
+PHP_FUNCTION(scardgetstatuschange)
+{
+ SCARD_READERSTATE rgReaderState = { 0 };
+ LONG rc;
+ PARSE_ARGS(1, 2);
+
+ /* Retrieve parameters */
+ rgReaderState.szReader = ARG(0)->value.str.val;
+
+ if (argc >= 2)
+ {
+ convert_to_long(ARG(1));
+ rgReaderState.dwCurrentState = ARG(1)->value.lval;
+ } else
+ {
+ rgReaderState.dwCurrentState = SCARD_STATE_UNAWARE;
+ }
+
+ /* Read reader's status */
+ rc = SCardGetStatusChange(hContext, 0, &rgReaderState, 1);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ /* Put result in an array */
+ array_init(return_value);
+
+ add_assoc_long(return_value, "state", rgReaderState.dwEventState);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_IGNORE)
+ add_assoc_long(return_value, "SCARD_STATE_IGNORE", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_UNKNOWN)
+ add_assoc_long(return_value, "SCARD_STATE_UNKNOWN", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_UNAVAILABLE)
+ add_assoc_long(return_value, "SCARD_STATE_UNAVAILABLE", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_EMPTY)
+ add_assoc_long(return_value, "SCARD_STATE_EMPTY", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_PRESENT)
+ add_assoc_long(return_value, "SCARD_STATE_PRESENT", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_ATRMATCH)
+ add_assoc_long(return_value, "SCARD_STATE_ATRMATCH", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_EXCLUSIVE)
+ add_assoc_long(return_value, "SCARD_STATE_EXCLUSIVE", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_INUSE)
+ add_assoc_long(return_value, "SCARD_STATE_INUSE", 1);
+
+ if (rgReaderState.dwEventState & SCARD_STATE_MUTE)
+ add_assoc_long(return_value, "SCARD_STATE_MUTE", 1);
+
+ if (rgReaderState.cbAtr)
+ {
+ char *atrString;
+ atrString = e_bytes_to_hex(rgReaderState.rgbAtr, rgReaderState.cbAtr);
+ add_assoc_string(return_value, "atr", atrString, TRUE);
+ efree(atrString);
+ }
+}
+/* }}} */
+
+
+/* {{{ proto string scardtransmit([long card_handle], string send_command)
+ Transmit (and receive) data to (and from) the card */
+PHP_FUNCTION(scardtransmit)
+{
+ SCARDHANDLE hCard;
+ DWORD dwProtocol, dwState;
+ const SCARD_IO_REQUEST *sendPci = NULL;
+ SCARD_IO_REQUEST *recvPci = NULL;
+ BYTE *sendBuffer;
+ BYTE *recvBuffer;
+ DWORD sendLen, recvLen;
+ zval *argb = NULL;
+ LONG rc;
+ PARSE_ARGS(0, 3);
+
+ /* Retrieve parameters */
+ hCard = hLastCard;
+ argb = ARG(0);
+ if (argc >= 2)
+ {
+ convert_to_long(ARG(0));
+ hCard = ARG(0)->value.lval;
+ argb = ARG(1);
+ }
+
+ /* Are we in T=0 or T=1 ? */
+ rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, NULL, NULL);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+
+ if (!(dwState & SCARD_POWERED))
+ RETURN_FALSE;
+
+ switch (dwProtocol)
+ {
+ case SCARD_PROTOCOL_RAW : sendPci = SCARD_PCI_RAW; break;
+ case SCARD_PROTOCOL_T0 : sendPci = SCARD_PCI_T0; break;
+ case SCARD_PROTOCOL_T1 : sendPci = SCARD_PCI_T1; break;
+ }
+
+ /* Get buffer to send */
+ convert_to_string(argb);
+ sendBuffer = e_hex_to_bytes(argb->value.str.val, &sendLen);
+
+ /* Guess the size of the buffer to recv */
+ recvLen = 2+256;
+ recvBuffer = emalloc(recvLen);
+
+ /* Perform the exchange */
+ recvPci = emalloc(1024);
+ recvPci->dwProtocol = dwProtocol;
+ recvPci->cbPciLength = 1024;
+
+ rc = SCardTransmit(hCard, sendPci, sendBuffer, sendLen, recvPci, recvBuffer, &recvLen);
+ if (rc != SCARD_S_SUCCESS)
+ {
+ efree(recvPci);
+ efree(recvBuffer);
+ efree(sendBuffer);
+ lLastError = rc;
+ RETURN_FALSE;
+ }
+ efree(recvPci);
+
+ /* Return the response */
+ RETVAL_STRING(e_bytes_to_hex(recvBuffer, recvLen), TRUE);
+
+ /* Free local data */
+ efree(recvBuffer);
+ efree(sendBuffer);
+}
+/* }}} */
+
+/* {{{ proto long scardgetlasterror()
+ Retrieve value of last error */
+PHP_FUNCTION(scardgetlasterror)
+{
+ RETVAL_LONG(lLastError);
+ lLastError = SCARD_S_SUCCESS;
+}
+/* }}} */