parent
923dfdb328
commit
d8bc9c49c2
2 changed files with 430 additions and 0 deletions
@ -0,0 +1,25 @@ |
|||||||
|
//g++ -I ../svn -I /usr/include/PCSC test.cpp -lpcsclite -ggdb3
|
||||||
|
#include "pcscpp/pcsc.hxx" |
||||||
|
#include "pcscpp/cardos.hxx" |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
int main(int, char const*const*const argv) try { |
||||||
|
pcsc::Connection c; |
||||||
|
pcsc::Connection::Strings reader(c.scan()); |
||||||
|
std::cout<<"Suche PCSC-Reader ..."<<std::endl; |
||||||
|
if (!reader.size()) std::cout<<"Keine gefunden."<<std::endl; |
||||||
|
for (pcsc::Connection::Strings::const_iterator it(reader.begin()); |
||||||
|
it!=reader.end(); ++it) { |
||||||
|
std::cout<<"Reader: "<<*it<<std::endl; |
||||||
|
pcsc::Connection::Reader::Status s(c.reader(*it).status()); |
||||||
|
std::cout<<"Status = "<<s.state<<std::endl; |
||||||
|
std::cout<<"Attribute = "; |
||||||
|
for (std::vector<unsigned char>::const_iterator it(s.attr.begin()); |
||||||
|
it!=s.attr.end(); ++it) |
||||||
|
std::cout<<std::hex<<(int)*it<<" "; |
||||||
|
std::cout<<std::endl; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} catch (std::exception& x) { |
||||||
|
std::cerr<<"**** FEHLER in "<<*argv<<": "<<x.what()<<std::endl; |
||||||
|
} |
@ -0,0 +1,405 @@ |
|||||||
|
/*! @file
|
||||||
|
|
||||||
|
This file offers a C++ access to the PCSC library. |
||||||
|
|
||||||
|
@id $Id$ |
||||||
|
*/ |
||||||
|
// 1 2 3 4 5 6 7 8
|
||||||
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
|
||||||
|
#ifndef PCSC_HXX |
||||||
|
#define PCSC_HXX |
||||||
|
|
||||||
|
#include <PCSC/wintypes.h> |
||||||
|
#include <PCSC/pcsclite.h> |
||||||
|
#include <PCSC/winscard.h> |
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
#include <map> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <iomanip> |
||||||
|
|
||||||
|
//! This library is a C++ wrapper to the awful pcsc-lite interface.
|
||||||
|
/*! The reason for this wrapper is to get a nice object oriented
|
||||||
|
interface written in C++ manner and using standard types and so to |
||||||
|
avoid the ugly M$-C-quirks. This interface is memory clean. |
||||||
|
@todo: Not implemented, not supported: |
||||||
|
- SCardGetStatusChange(hContext, 0, rgReaderStates, 1); |
||||||
|
- SCardReconnect(hCard, SCARD_SHARE_SHARED, |
||||||
|
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, |
||||||
|
SCARD_LEAVE_CARD, |
||||||
|
&dwActiveProtocol); |
||||||
|
- several other |
||||||
|
I do not need these, and I don't know the usecase where they are |
||||||
|
required. If you need something that is not yet supported, please |
||||||
|
let me know, what you need and why. Then I'll add it so that it |
||||||
|
best fits the common needs. */ |
||||||
|
namespace pcsc { |
||||||
|
|
||||||
|
std::string hex(const std::string& data) { |
||||||
|
std::stringstream res; |
||||||
|
for (std::string::const_iterator it(data.begin()); it!=data.end(); ++it) |
||||||
|
res<<std::hex<<std::setfill('0')<<std::setw(2)<<(int)*it; |
||||||
|
return res.str(); |
||||||
|
} |
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
class exception: public std::exception { |
||||||
|
public: |
||||||
|
exception(const std::string& reason) throw(): _what("pcsc: "+reason) {} |
||||||
|
~exception() throw() {} |
||||||
|
const char* what() const throw() { |
||||||
|
return _what.c_str(); |
||||||
|
} |
||||||
|
private: |
||||||
|
std::string _what; |
||||||
|
}; |
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
class not_implemented: exception { |
||||||
|
public: |
||||||
|
not_implemented(const std::string& reason) throw(): |
||||||
|
exception("feature is not implemented:\n"+reason) { |
||||||
|
} |
||||||
|
}; |
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
class access_error: exception { |
||||||
|
public: |
||||||
|
access_error(const std::string& reason) throw(): |
||||||
|
exception("smardcard access error:\n"+reason) { |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
class Connection { |
||||||
|
|
||||||
|
//...............................................................typedefs
|
||||||
|
public: |
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------Reader
|
||||||
|
friend class Reader; |
||||||
|
class Reader { |
||||||
|
|
||||||
|
//............................................................typedefs
|
||||||
|
public: |
||||||
|
|
||||||
|
friend class Transaction; |
||||||
|
//! Scoped transaction.
|
||||||
|
/*! This is a scoped transaction. That means, the
|
||||||
|
transaction is started in the constructor and cancelled |
||||||
|
in the destructor, unless it is committed before. That |
||||||
|
means: If you commit the transaction, it is executed. If |
||||||
|
you leave the code block unexpectedly, i.e. through an |
||||||
|
exception, the transaction is cancelled. |
||||||
|
|
||||||
|
@code |
||||||
|
pcsc::Connection c; |
||||||
|
{ |
||||||
|
Transaction t(c.reader("name")); |
||||||
|
[...] // do some stuff, possible exceptions thrown
|
||||||
|
if (problem) return; // automatically cancelled
|
||||||
|
[...] |
||||||
|
t.commit(); // commit if we reach this line
|
||||||
|
} // cancelled, unless commit reaced
|
||||||
|
@endcode */ |
||||||
|
class Transaction { |
||||||
|
public: |
||||||
|
//! Begins a transaction.
|
||||||
|
/*! @note Please note that the Reader is required in the
|
||||||
|
destructor und must therefore live longer than the |
||||||
|
Transaction instance. */ |
||||||
|
Transaction(Reader& r): _reader(r), _running(true) { |
||||||
|
_reader.beginTransaction(); |
||||||
|
} |
||||||
|
//! Cancels the transaction if not yet finished.
|
||||||
|
~Transaction() try { |
||||||
|
if (_running) cancel(); |
||||||
|
} catch (...) { |
||||||
|
if (!std::uncaught_exception()) throw; |
||||||
|
} |
||||||
|
//! Ends (commits) the running transaction.
|
||||||
|
void commit() { |
||||||
|
if (_running) _reader.commitTransaction(); |
||||||
|
_running = false; |
||||||
|
} |
||||||
|
//! Cancels the running transaction.
|
||||||
|
void cancel() { |
||||||
|
if (_running) _reader.cancelTransaction(); |
||||||
|
_running = false; |
||||||
|
} |
||||||
|
private: |
||||||
|
Reader& _reader; |
||||||
|
bool _running; |
||||||
|
}; |
||||||
|
|
||||||
|
//! State and attribute list of a reader.
|
||||||
|
class Status { |
||||||
|
public: |
||||||
|
Status(unsigned long s, const std::vector<unsigned char>& attrs): |
||||||
|
state(s), attr(attrs) { |
||||||
|
} |
||||||
|
Status(unsigned long s, const std::string& attrs): |
||||||
|
state(s), attr(convert(attrs)) { |
||||||
|
} |
||||||
|
const unsigned long state; |
||||||
|
const std::vector<unsigned char> attr; |
||||||
|
private: |
||||||
|
static std::vector<unsigned char> convert(const std::string& a) { |
||||||
|
std::vector<unsigned char> res; |
||||||
|
for (std::string::const_iterator it(a.begin()); |
||||||
|
it!=a.end(); ++it) |
||||||
|
res.push_back(*it); |
||||||
|
return res; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//.............................................................methods
|
||||||
|
public: |
||||||
|
|
||||||
|
//! Disconnects connection.
|
||||||
|
~Reader() { |
||||||
|
_state = SCardDisconnect(_id, SCARD_LEAVE_CARD); |
||||||
|
if (!std::uncaught_exception()) _connection.check(); |
||||||
|
} |
||||||
|
|
||||||
|
//! Get reader status.
|
||||||
|
Status status() { |
||||||
|
unsigned long dummy(0); |
||||||
|
unsigned long s; |
||||||
|
unsigned long len(MAX_ATR_SIZE); |
||||||
|
unsigned char a[len]; |
||||||
|
check(SCardStatus(_id, 0, &dummy, &s, &_protocol, a, &len)); |
||||||
|
return Status(s, std::string((char*)a, len)); |
||||||
|
} |
||||||
|
|
||||||
|
//! Transmit data to reader.
|
||||||
|
std::string transmit(std::string in) { |
||||||
|
unsigned long len(1024); // arbitrary
|
||||||
|
unsigned char buff[len]; |
||||||
|
SCARD_IO_REQUEST rPci; |
||||||
|
check(SCardTransmit(_id, pci(), |
||||||
|
(unsigned char*)in.c_str(), in.size(), |
||||||
|
&rPci, buff, &len)); |
||||||
|
return std::string((char*)buff, len); |
||||||
|
} |
||||||
|
|
||||||
|
//! @c false if last operation was not successful
|
||||||
|
operator bool() const { |
||||||
|
return _state==SCARD_S_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
//...........................................................variables
|
||||||
|
public: |
||||||
|
|
||||||
|
const std::string name; |
||||||
|
|
||||||
|
//.............................................................methods
|
||||||
|
private: |
||||||
|
|
||||||
|
//! Use scoped transactions with @ref Transaction
|
||||||
|
void beginTransaction() { |
||||||
|
check(SCardBeginTransaction(_id)); |
||||||
|
} |
||||||
|
|
||||||
|
//! Use scoped transactions with @ref Transaction
|
||||||
|
void commitTransaction() { |
||||||
|
check(SCardEndTransaction(_id, SCARD_LEAVE_CARD)); |
||||||
|
} |
||||||
|
|
||||||
|
//! Use scoped transactions with @ref Transaction
|
||||||
|
void cancelTransaction() { |
||||||
|
check(SCardCancelTransaction(_id)); |
||||||
|
} |
||||||
|
|
||||||
|
/*! @throw not_implemented if _protocol is unknown. */ |
||||||
|
SCARD_IO_REQUEST* pci() { |
||||||
|
switch(_protocol) { |
||||||
|
case SCARD_PROTOCOL_T0: return SCARD_PCI_T0; |
||||||
|
case SCARD_PROTOCOL_T1: return SCARD_PCI_T1; |
||||||
|
} |
||||||
|
if (_connection._exc) throw not_implemented("unknown protocol"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
//! Sets state and throws an exception if neccessary.
|
||||||
|
/*! @throw access_error if it is instanciated for exceptions and
|
||||||
|
an error occured in the last command. */ |
||||||
|
bool check(long state) { |
||||||
|
_state = state; |
||||||
|
return _connection.check(state); |
||||||
|
} |
||||||
|
|
||||||
|
//! Only Connection is allowed to instanciate.
|
||||||
|
friend class Connection; |
||||||
|
|
||||||
|
//! Establishes a connection to the given named cardreader
|
||||||
|
Reader(const std::string& nm, Connection& c): |
||||||
|
name(nm), _connection(c) { |
||||||
|
check(SCardConnect(_connection._id, name.c_str(), |
||||||
|
SCARD_SHARE_SHARED, |
||||||
|
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, |
||||||
|
&_id, &_protocol)); |
||||||
|
} |
||||||
|
|
||||||
|
//! forbidden
|
||||||
|
Reader(); |
||||||
|
|
||||||
|
//! forbidden
|
||||||
|
Reader(const Reader&); |
||||||
|
|
||||||
|
//...........................................................variables
|
||||||
|
private: |
||||||
|
|
||||||
|
Connection& _connection; |
||||||
|
long _id; |
||||||
|
long _state; |
||||||
|
unsigned long _protocol; |
||||||
|
|
||||||
|
}; |
||||||
|
//------------------------------------------------------------------Reader
|
||||||
|
|
||||||
|
enum Scope { |
||||||
|
USER = SCARD_SCOPE_USER, |
||||||
|
TERMINAL = SCARD_SCOPE_TERMINAL, |
||||||
|
SYSTEM = SCARD_SCOPE_SYSTEM |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::vector<std::string> Strings; |
||||||
|
|
||||||
|
//................................................................methods
|
||||||
|
public: |
||||||
|
|
||||||
|
//! Opens a connection (establishes a smartcard context)
|
||||||
|
/*! The errorhandling is defined with the @c exceptions flag:
|
||||||
|
|
||||||
|
Using exceptions: |
||||||
|
@code |
||||||
|
try { |
||||||
|
pcsc::Connection c; // standard with exceptions
|
||||||
|
pcsc::Connection::Strings r(c.scan()); |
||||||
|
} catch (std::exception& x) { |
||||||
|
std::cout<<"Error with message: "<<x.what()<<std::endl; |
||||||
|
} |
||||||
|
@endcode |
||||||
|
|
||||||
|
Without exceptions: |
||||||
|
@code |
||||||
|
pcsc::Connection c(SYSTEM, false); |
||||||
|
if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl; |
||||||
|
pcsc::Connection::Strings r(c.scan()); |
||||||
|
if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl; |
||||||
|
@endcode |
||||||
|
|
||||||
|
Recommendation: Use exceptions! |
||||||
|
|
||||||
|
@param s defines the scope of the connection |
||||||
|
@param exceptions |
||||||
|
- @c true: class throws exceptions in case of an error |
||||||
|
- @c false: no exceptions, check your instance after each |
||||||
|
operation */ |
||||||
|
Connection(Scope s=SYSTEM, bool exceptions=true): |
||||||
|
_exc(exceptions), _id(0), |
||||||
|
_state(SCardEstablishContext(s, 0, 0, &_id)) { |
||||||
|
check(); |
||||||
|
} |
||||||
|
|
||||||
|
//! Closes the connection (releases the smartcard context)
|
||||||
|
~Connection() { |
||||||
|
_state = SCardReleaseContext(_id); |
||||||
|
if (!std::uncaught_exception()) check(); |
||||||
|
} |
||||||
|
|
||||||
|
//! Scans for available readers from a specified list of groups.
|
||||||
|
/*! Defaults to all groups. */ |
||||||
|
Strings scan(const Strings& groups = Strings()) { |
||||||
|
Strings res; |
||||||
|
std::string grp(join(groups)); |
||||||
|
unsigned long num(0); |
||||||
|
if (!check(SCardListReaders(_id, grp.size()?grp.data():0, 0, &num))) |
||||||
|
return res; |
||||||
|
std::auto_ptr<char> nm(new char[num]); |
||||||
|
if (!check(SCardListReaders(_id, grp.size()?grp.data():0, |
||||||
|
nm.get(), &num))) |
||||||
|
return res; |
||||||
|
return res = split(std::string(nm.get(), num-1)); |
||||||
|
} |
||||||
|
|
||||||
|
//! Get a reader, open a connection if not already open.
|
||||||
|
/*! First use scan() to get a list of readers, then open a
|
||||||
|
connection to the reader, then access it. */ |
||||||
|
Reader& reader(const std::string& name) { |
||||||
|
if (_reader.find(name)==_reader.end()) |
||||||
|
_reader.insert(std::make_pair(name, new Reader(name, *this))); |
||||||
|
return *_reader.find(name)->second; |
||||||
|
} |
||||||
|
|
||||||
|
//! Close connection of a named reader.
|
||||||
|
/*! If you access the same reader through raeder() later, the
|
||||||
|
connection will be reestablished. */ |
||||||
|
void close(const std::string& s) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
//! @c false if last operation was not successful
|
||||||
|
operator bool() const { |
||||||
|
return _state==SCARD_S_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
//! Get the describing text of the last error
|
||||||
|
std::string error() const { |
||||||
|
return pcsc_stringify_error(_state); |
||||||
|
} |
||||||
|
|
||||||
|
//................................................................methods
|
||||||
|
private: |
||||||
|
|
||||||
|
//! Sets state and throws an exception if neccessary.
|
||||||
|
/*! @throw access_error if it is instanciated for exceptions and
|
||||||
|
an error occured in the last command. */ |
||||||
|
bool check(long state) { |
||||||
|
_state = state; |
||||||
|
return check(); |
||||||
|
} |
||||||
|
|
||||||
|
//! Throws an exception if neccessary.
|
||||||
|
/*! @throw access_error if it is instanciated for exceptions and
|
||||||
|
an error occured in the last command. */ |
||||||
|
bool check() { |
||||||
|
if (_exc&&_state!=SCARD_S_SUCCESS) throw access_error(error()); |
||||||
|
return _state==SCARD_S_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
//! Splits a buffer with 0 separators into a vector of strings.
|
||||||
|
Strings split(const std::string& in) { |
||||||
|
Strings res; |
||||||
|
for (std::string::size_type pos(0); pos<in.size(); |
||||||
|
pos+=res.rbegin()->size()+1) |
||||||
|
res.push_back(in.substr(pos).c_str()); |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
//! Joins a vector of strings into a buffer with 0 separators.
|
||||||
|
std::string join(const Strings& in) { |
||||||
|
std::string res; |
||||||
|
for (Strings::const_iterator it(in.begin()); |
||||||
|
it!=in.end(); ++it) |
||||||
|
res+=*it+'\0'; |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
//..............................................................variables
|
||||||
|
private: |
||||||
|
|
||||||
|
bool _exc; |
||||||
|
long _id; |
||||||
|
long _state; |
||||||
|
std::map<std::string, boost::shared_ptr<Reader> > _reader; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
#endif |
Loading…
Reference in new issue