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