/*! @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 #ifdef WIN32 #include "WinSCard.h" #ifndef MAX_ATR_SIZE #define MAX_ATR_SIZE 33 #endif namespace pcsc { //! stupid windows needs std::wstring std::wstring strconv(std::string s) { return std::wstring(s.begin(), s.end()); } std::string strconv(std::wstring s) { return std::string(s.begin(), s.end()); } typedef wchar_t char_t; typedef std::wstring string; } #else #include #include #include #include namespace pcsc { const std::string& strconv(const std::string& s) { return s; } typedef char char_t; typedef std::string string; } #endif #include #include #include #include #include #include #include //! 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<<"(0x"<& 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 attr; private: static std::vector convert(const std::string& a) { std::vector 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() { DWORD dummy(0); DWORD s; DWORD 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) { DWORD 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. */ const 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, strconv(name).c_str(), SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, &_id, &_protocol)); } //! forbidden Reader(); //! forbidden Reader(const Reader&); //...........................................................variables private: Connection& _connection; SCARDHANDLE _id; long _state; DWORD _protocol; }; //------------------------------------------------------------------Reader enum Scope { USER = SCARD_SCOPE_USER, TERMINAL = SCARD_SCOPE_TERMINAL, SYSTEM = SCARD_SCOPE_SYSTEM }; typedef std::vector 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: "< nm(new char_t[num]); if (!check(SCardListReaders(_id, grp.size()?strconv(grp).data():0, nm.get(), &num))) return res; return res = split(strconv(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 { #ifdef WIN32 std::stringstream ss; ss<<"PCSC state: "<<_state; return ss.str(); #else return pcsc_stringify_error(_state); #endif } //................................................................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); possize()+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; SCARDCONTEXT _id; long _state; std::map > _reader; }; } #endif