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; +} +/* }}} */