diff --git a/config.w32 b/config.w32 index 788e138..34c264c 100755 --- a/config.w32 +++ b/config.w32 @@ -1,4 +1,3 @@ - ARG_WITH("pcsc", "PC/SC Support", "no"); if (PHP_PCSC != "no") { diff --git a/package.xml b/package.xml index 70f7cd5..af8136f 100644 --- a/package.xml +++ b/package.xml @@ -22,8 +22,8 @@ environments. 2013-04-11 - 0.2 - 0.2 + 0.3 + 0.3 alpha diff --git a/pcsc.c b/pcsc.c index dfc37d2..0b62e1b 100644 --- a/pcsc.c +++ b/pcsc.c @@ -35,6 +35,14 @@ #define TRUE 1 #endif +/* arg info for scard_connect (accepts an optional compile-time reference parameter) */ +ZEND_BEGIN_ARG_INFO_EX(php_pcsc_scard_connect_arginfo,0,0,2) + ZEND_ARG_INFO(0,context) + ZEND_ARG_INFO(0,reader_name) + ZEND_ARG_INFO(0,preferred_protocol) + ZEND_ARG_INFO(1,active_protocol) +ZEND_END_ARG_INFO() + /* {{{ pcsc_functions[] * * Every user visible function must have an entry in pcsc_functions[]. @@ -44,7 +52,7 @@ zend_function_entry pcsc_functions[] = { 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_connect, php_pcsc_scard_connect_arginfo) //PHP_FE(scard_reconnect, NULL) PHP_FE(scard_disconnect, NULL) PHP_FE(scard_transmit, NULL) @@ -80,6 +88,44 @@ ZEND_GET_MODULE(pcsc) #define REGISTER_PCSC_CONSTANT(__c) REGISTER_LONG_CONSTANT(#__c, __c, CONST_CS | CONST_PERSISTENT) +/* resource container: context */ +static int le_pcsc_ctx_res; +static void php_pcsc_ctx_res_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { + SCARDCONTEXT context; + LONG rc=0; + + context=(SCARDCONTEXT)rsrc->ptr; + rc = SCardIsValidContext(context); + if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PC/SC context dtor: SCardIsValidContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); + return; + } + + rc = SCardReleaseContext(context); + if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PC/SC context dtor: SCardReleaseContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); + return; + } + return; +} + +/* resource container: connection */ +static int le_pcsc_conn_res; +static void php_pcsc_conn_res_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { + SCARDHANDLE hCard; + LONG rc; + + //TODO: actually use this + return; + php_printf("conn dtor\n"); + hCard=(SCARDHANDLE)rsrc->ptr; + + rc = SCardDisconnect(hCard, SCARD_LEAVE_CARD); + if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PC/SC connection dtor: SCardDisconnect returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); + } +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(pcsc) @@ -169,7 +215,11 @@ PHP_MINIT_FUNCTION(pcsc) REGISTER_PCSC_CONSTANT(SCARD_PRESENT); REGISTER_PCSC_CONSTANT(SCARD_SPECIFIC); REGISTER_PCSC_CONSTANT(SCARD_SWALLOWED); - + + /* resource containers */ + le_pcsc_ctx_res=zend_register_list_destructors_ex(php_pcsc_ctx_res_dtor, NULL, PHP_PCSC_CTX_RES_NAME,module_number); + le_pcsc_conn_res=zend_register_list_destructors_ex(php_pcsc_conn_res_dtor, NULL, PHP_PCSC_CONN_RES_NAME,module_number); + return SUCCESS; } /* }}} */ @@ -192,6 +242,77 @@ PHP_MINFO_FUNCTION(pcsc) } /* }}} */ +/* map error codes to string */ +static char* php_pcsc_error_to_string(LONG dwRC) { + switch(dwRC) { + case SCARD_E_BAD_SEEK: return "SCARD_E_BAD_SEEK"; + case SCARD_E_CANCELLED: return "SCARD_E_CANCELLED"; + case SCARD_E_CANT_DISPOSE: return "SCARD_E_CANT_DISPOSE"; + case SCARD_E_CARD_UNSUPPORTED: return "SCARD_E_CARD_UNSUPPORTED"; + case SCARD_E_CERTIFICATE_UNAVAILABLE: return "SCARD_E_CERTIFICATE_UNAVAILABLE"; + case SCARD_E_COMM_DATA_LOST: return "SCARD_E_COMM_DATA_LOST"; + case SCARD_E_DIR_NOT_FOUND: return "SCARD_E_DIR_NOT_FOUND"; + case SCARD_E_DUPLICATE_READER: return "SCARD_E_DUPLICATE_READER"; + case SCARD_E_FILE_NOT_FOUND: return "SCARD_E_FILE_NOT_FOUND"; + case SCARD_E_ICC_CREATEORDER: return "SCARD_E_ICC_CREATEORDER"; + case SCARD_E_ICC_INSTALLATION: return "SCARD_E_ICC_INSTALLATION"; + case SCARD_E_INSUFFICIENT_BUFFER: return "SCARD_E_INSUFFICIENT_BUFFER"; + case SCARD_E_INVALID_ATR: return "SCARD_E_INVALID_ATR"; + case SCARD_E_INVALID_CHV: return "SCARD_E_INVALID_CHV"; + case SCARD_E_INVALID_HANDLE: return "SCARD_E_INVALID_HANDLE"; + case SCARD_E_INVALID_PARAMETER: return "SCARD_E_INVALID_PARAMETER"; + case SCARD_E_INVALID_TARGET: return "SCARD_E_INVALID_TARGET"; + case SCARD_E_INVALID_VALUE: return "SCARD_E_INVALID_VALUE"; + case SCARD_E_NO_ACCESS: return "SCARD_E_NO_ACCESS"; + case SCARD_E_NO_DIR: return "SCARD_E_NO_DIR"; + case SCARD_E_NO_FILE: return "SCARD_E_NO_FILE"; + case SCARD_E_NO_KEY_CONTAINER: return "SCARD_E_NO_KEY_CONTAINER"; + case SCARD_E_NO_MEMORY: return "SCARD_E_NO_MEMORY"; + case SCARD_E_NO_READERS_AVAILABLE: return "SCARD_E_NO_READERS_AVAILABLE"; + case SCARD_E_NO_SERVICE: return "SCARD_E_NO_SERVICE"; + case SCARD_E_NO_SMARTCARD: return "SCARD_E_NO_SMARTCARD"; + case SCARD_E_NO_SUCH_CERTIFICATE: return "SCARD_E_NO_SUCH_CERTIFICATE"; + case SCARD_E_NOT_READY: return "SCARD_E_NOT_READY"; + case SCARD_E_NOT_TRANSACTED: return "SCARD_E_NOT_TRANSACTED"; + case SCARD_E_PCI_TOO_SMALL: return "SCARD_E_PCI_TOO_SMALL"; + case SCARD_E_PROTO_MISMATCH: return "SCARD_E_PROTO_MISMATCH"; + case SCARD_E_READER_UNAVAILABLE: return "SCARD_E_READER_UNAVAILABLE"; + case SCARD_E_READER_UNSUPPORTED: return "SCARD_E_READER_UNSUPPORTED"; + case SCARD_E_SERVER_TOO_BUSY: return "SCARD_E_SERVER_TOO_BUSY"; + case SCARD_E_SERVICE_STOPPED: return "SCARD_E_SERVICE_STOPPED"; + case SCARD_E_SHARING_VIOLATION: return "SCARD_E_SHARING_VIOLATION"; + case SCARD_E_SYSTEM_CANCELLED: return "SCARD_E_SYSTEM_CANCELLED"; + case SCARD_E_TIMEOUT: return "SCARD_E_TIMEOUT"; + case SCARD_E_UNEXPECTED: return "SCARD_E_UNEXPECTED"; + case SCARD_E_UNKNOWN_CARD: return "SCARD_E_UNKNOWN_CARD"; + case SCARD_E_UNKNOWN_READER: return "SCARD_E_UNKNOWN_READER"; + case SCARD_E_UNKNOWN_RES_MNG: return "SCARD_E_UNKNOWN_RES_MNG"; + case SCARD_E_UNSUPPORTED_FEATURE: return "SCARD_E_UNSUPPORTED_FEATURE"; + case SCARD_E_WRITE_TOO_MANY: return "SCARD_E_WRITE_TOO_MANY"; + case SCARD_F_COMM_ERROR: return "SCARD_E_WRITE_TOO_MANY"; + case SCARD_F_INTERNAL_ERROR: return "SCARD_F_INTERNAL_ERROR"; + case SCARD_F_UNKNOWN_ERROR: return "SCARD_F_UNKNOWN_ERROR"; + case SCARD_F_WAITED_TOO_LONG: return "SCARD_F_WAITED_TOO_LONG"; + case SCARD_P_SHUTDOWN: return "SCARD_P_SHUTDOWN"; + case SCARD_S_SUCCESS: return "SCARD_S_SUCCESS"; + case SCARD_W_CANCELLED_BY_USER: return "SCARD_W_CANCELLED_BY_USER"; + case SCARD_W_CACHE_ITEM_NOT_FOUND: return "SCARD_W_CACHE_ITEM_NOT_FOUND"; + case SCARD_W_CACHE_ITEM_STALE: return "SCARD_W_CACHE_ITEM_STALE"; + case SCARD_W_CACHE_ITEM_TOO_BIG: return "SCARD_W_CACHE_ITEM_TOO_BIG"; + case SCARD_W_CARD_NOT_AUTHENTICATED: return "SCARD_W_CARD_NOT_AUTHENTICATED"; + case SCARD_W_CHV_BLOCKED: return "SCARD_W_CHV_BLOCKED"; + case SCARD_W_EOF: return "SCARD_W_EOF"; + case SCARD_W_REMOVED_CARD: return "SCARD_W_REMOVED_CARD"; + case SCARD_W_RESET_CARD: return "SCARD_W_RESET_CARD"; + case SCARD_W_SECURITY_VIOLATION: return "SCARD_W_SECURITY_VIOLATION"; + case SCARD_W_UNPOWERED_CARD: return "SCARD_W_UNPOWERED_CARD"; + case SCARD_W_UNRESPONSIVE_CARD: return "SCARD_W_UNRESPONSIVE_CARD"; + case SCARD_W_UNSUPPORTED_CARD: return "SCARD_W_UNSUPPORTED_CARD"; + case SCARD_W_WRONG_CHV: return "SCARD_W_WRONG_CHV"; + default: return "Unknown"; + } +} + static char *e_bytes_to_hex(BYTE *buffer, DWORD length) { DWORD i; @@ -249,87 +370,89 @@ static BYTE *e_hex_to_bytes(char *str, DWORD *length) return buf; } -/* {{{ proto array pcsc_establishcontext() +/* {{{ proto resource scard_establish_context() 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) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardEstablishContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } - RETURN_LONG(scard_context); + + ZEND_REGISTER_RESOURCE(return_value,(void*)scard_context,le_pcsc_ctx_res); } /* }}} */ -/* {{{ proto boolean scard_is_valid_context(long context) - Return a a context to the PC/SC layer */ +/* {{{ proto boolean scard_is_valid_context(resource context) + Checks if the PC/SC context is valid */ PHP_FUNCTION(scard_is_valid_context) { LONG rc = 0; - LONG *context; + zval* ctx_res; + SCARDCONTEXT context; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &ctx_res) == FAILURE) { + RETURN_NULL(); } + ZEND_FETCH_RESOURCE(context,SCARDCONTEXT,&ctx_res,-1,PHP_PCSC_CTX_RES_NAME,le_pcsc_ctx_res); rc = SCardIsValidContext(context); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardIsValidContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ -/* {{{ proto array pcsc_establishcontext(long context) - Return a a context to the PC/SC layer */ +/* {{{ proto boolean scard_release_context(long context) + Invalidate the PC/SC context */ PHP_FUNCTION(scard_release_context) { LONG rc = 0; - LONG *context; + zval* ctx_res; + SCARDCONTEXT context; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &ctx_res) == FAILURE) { + RETURN_NULL(); } + ZEND_FETCH_RESOURCE(context,SCARDCONTEXT,&ctx_res,-1,PHP_PCSC_CTX_RES_NAME,le_pcsc_ctx_res); - rc = SCardIsValidContext(context); - if (rc != SCARD_S_SUCCESS) { - RETURN_FALSE; - } - - rc = SCardReleaseContext(context); - if (rc != SCARD_S_SUCCESS) { - RETURN_FALSE; - } - RETURN_LONG(context); + zend_hash_index_del(&EG(regular_list),Z_RESVAL_P(ctx_res)); + RETURN_TRUE; } /* }}} */ -/* {{{ proto array pcsc_listreaders(long context) - Return a an array of available PC/SC readers */ +/* {{{ proto array scard_list_readers(resource context) + Return 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; + zval* ctx_res; + SCARDCONTEXT context; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &ctx_res) == FAILURE) { + RETURN_NULL(); } + ZEND_FETCH_RESOURCE(context,SCARDCONTEXT,&ctx_res,-1,PHP_PCSC_CTX_RES_NAME,le_pcsc_ctx_res); rc = SCardIsValidContext(context); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardIsValidContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } rc = SCardListReaders(context, NULL, (char *) &strReaders, &dwLen); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardListReaders returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } @@ -350,68 +473,74 @@ PHP_FUNCTION(scard_list_readers) } /* }}} */ -/* {{{ proto long scardconnect(long context, string reader_name, [long preferred_protocol], [long ¤t_protocol]) +/* {{{ proto resource scard_connect(resource 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; + zval* ctx_res; + zval* current_protocol; + SCARDCONTEXT context; char *strReaderName; int strReaderNameLen; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &context, &strReaderName, &strReaderNameLen) == FAILURE) { - return; + MAKE_STD_ZVAL(current_protocol); + ZVAL_LONG(current_protocol,0); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lz", &ctx_res, &strReaderName, &strReaderNameLen, &dwPreferredProtocol,¤t_protocol) == FAILURE) { + RETURN_NULL(); } + ZEND_FETCH_RESOURCE(context,SCARDCONTEXT,&ctx_res,-1,PHP_PCSC_CTX_RES_NAME,le_pcsc_ctx_res); rc = SCardIsValidContext(context); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardIsValidContext returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } rc = SCardConnect(context, strReaderName, SCARD_SHARE_SHARED, dwPreferredProtocol, &hCard, &dwCurrentProtocol); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardConnect returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } - - RETURN_LONG(hCard); + ZVAL_LONG(current_protocol,dwCurrentProtocol); + + ZEND_REGISTER_RESOURCE(return_value,(void*)hCard,le_pcsc_conn_res); } /* }}} */ -/* {{{ proto boolean scarddisconnect(long card_handle, [long disposition]) - Close a card connection obtained with scardconnect */ - /* TODO: Add support for disposition. */ +/* {{{ proto boolean scard_disconnect(resource card_handle, [long disposition = SCARD_EJECT_CARD]) + Close a card connection obtained with scard_connect */ PHP_FUNCTION(scard_disconnect) { DWORD dwDisposition = SCARD_EJECT_CARD; LONG rc; - SCARDHANDLE *hCard; + zval* conn_res; + SCARDHANDLE hCard; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &conn_res, &dwDisposition) == FAILURE) { return; } - - /*if (argc >= 2) { - convert_to_long(ARG(1)); - dwDisposition = ARG(1)->value.lval; - }*/ + ZEND_FETCH_RESOURCE(hCard,SCARDHANDLE,&conn_res,-1,PHP_PCSC_CONN_RES_NAME,le_pcsc_conn_res); rc = SCardDisconnect(hCard, dwDisposition); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardDisconnect returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ -/* {{{ proto string scardtransmit(long card_handle, string send_command) +/* {{{ proto string scard_transmit(resource card_handle, string send_command) Transmit (and receive) data to (and from) the card */ PHP_FUNCTION(scard_transmit) { SCARDHANDLE hCard; + zval* conn_res; DWORD dwProtocol; DWORD dwState; const SCARD_IO_REQUEST *sendPci = NULL; @@ -423,13 +552,15 @@ PHP_FUNCTION(scard_transmit) char *apdu; int apduLen; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &hCard, &apdu, &apduLen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_res, &apdu, &apduLen) == FAILURE) { return; } + ZEND_FETCH_RESOURCE(hCard,SCARDHANDLE,&conn_res,-1,PHP_PCSC_CONN_RES_NAME,le_pcsc_conn_res); /* Are we in T=0 or T=1 ? */ rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, NULL, NULL); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardStatus returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } @@ -457,6 +588,7 @@ PHP_FUNCTION(scard_transmit) rc = SCardTransmit(hCard, sendPci, sendBuffer, sendLen, recvPci, recvBuffer, &recvLen); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardTransmit returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); efree(recvPci); efree(recvBuffer); efree(sendBuffer); @@ -473,10 +605,11 @@ PHP_FUNCTION(scard_transmit) } /* }}} */ -/* {{{ proto array scardstatus([long card_handle]) +/* {{{ proto array scard_status([resource card_handle]) Retrieve reader's or card's status */ PHP_FUNCTION(scard_status) { + zval* conn_res; SCARDHANDLE hCard = 0; char *strReader = NULL; DWORD dwProtocol, dwState; @@ -484,13 +617,15 @@ PHP_FUNCTION(scard_status) DWORD atrLen; LONG rc; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_res) == FAILURE) { return; } - + ZEND_FETCH_RESOURCE(hCard,SCARDHANDLE,&conn_res,-1,PHP_PCSC_CONN_RES_NAME,le_pcsc_conn_res); + atrLen = sizeof(atrBuffer); rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, atrBuffer, &atrLen); if (rc != SCARD_S_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SCardStatus returned %s (0x%x)",php_pcsc_error_to_string(rc),rc); RETURN_FALSE; } diff --git a/php_pcsc.h b/php_pcsc.h index ffabad4..b973b45 100644 --- a/php_pcsc.h +++ b/php_pcsc.h @@ -4,7 +4,7 @@ extern zend_module_entry pcsc_module_entry; #define phpext_pcsc_ptr &pcsc_module_entry -#define PHP_PCSC_VERSION "0.0.1" +#define PHP_PCSC_VERSION "0.3" #ifdef PHP_WIN32 #define PHP_PCSC_API __declspec(dllexport) @@ -31,4 +31,9 @@ PHP_FUNCTION(scard_status); PHP_FUNCTION(scard_transmit); //PHP_FUNCTION(scard_cancel); +#define PHP_PCSC_CTX_RES_NAME "PC/SC Context" +#define PHP_PCSC_CONN_RES_NAME "PC/SC Connection" + +static char* php_pcsc_error_to_string(LONG dwRC); + #endif /* PHP_PCSC_H */