Initial commit of the PC/SC (pcsc) extension. There is some stuff that needs to be romoved like the build.sh script and the subfolders tmp/ and php/.

I think the .m4 file has some bugs because it fails to build without my weird build.sh script. I will fix that ASAP.



git-svn-id: https://svn.php.net/repository/pecl/pcsc/trunk@320951 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Johannes Findeisen 2011-12-13 16:57:24 +00:00
commit 8db0824d92
11 changed files with 1306 additions and 0 deletions

69
COPYING Normal file
View file

@ -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
<http://www.php.net/software/>".
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 <http://www.php.net>.
PHP includes the Zend Engine, freely available at
<http://www.zend.com>.

2
CREDITS Normal file
View file

@ -0,0 +1,2 @@
pcsc
Johann Dantant, Johannes Findeisen

3
README Normal file
View file

@ -0,0 +1,3 @@
This is totally unstable! Don't use in production environments!
You have been warned... ;)

7
TODO Normal file
View file

@ -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
- ...

24
build.sh Executable file
View file

@ -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"

39
config.m4 Normal file
View file

@ -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 <pcsc-dir>/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

536
pcsc.c Normal file
View file

@ -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 <you@hanez.org> - 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 <PCSC/winscard.h>
#include <PCSC/pcsclite.h>
#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 &current_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);
}
}
/* }}} */

55
php/pcsc.class.php Normal file
View file

@ -0,0 +1,55 @@
<?php
/*
* Don't use this! this is a raw and dirty written class just for testing some
* stuff... Johannes
*/
class PCSC {
public $context;
public $card;
public $readers;
public $connection;
function __construct() {
$this->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);
}
}
?>

43
php/test.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/* Just a test script for checking the implementation... */
if (!extension_loaded('pcsc')) {
dl('pcsc.so');
}
include("./pcsc.class.php");
$foo = new PCSC();
echo "Create Context:\n";
var_dump($foo->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());
?>

32
php_pcsc.h Normal file
View file

@ -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 */

496
tmp/pcsc2.c Normal file
View file

@ -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<br/>\n", __LINE__, SCARD_S_SUCCESS);
php_printf("%d: %d<br/>\n", __LINE__, lLastError);
php_printf("%d: %d<br/>\n", __LINE__, hContext);
rc3 = SCardListReaders(hContext, NULL, (char *) &strReaders, &dwLen);
php_printf("%d: %d<br/>\n", __LINE__, hContext);
php_printf("%d: %d<br/>\n", __LINE__, lLastError);
*/
if (rc3 != SCARD_S_SUCCESS)
{
php_printf("%d: %d<br/>\n", __LINE__, 1);
lLastError = rc3;
php_printf("%d: %d<br/>\n", __LINE__, rc3);
//RETURN_FALSE;
}
if (strReaders == NULL) {
php_printf("%d: %d<br/>\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 &current_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 &current_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;
}
/* }}} */