Keyple Plugin PC/SC C++ Library - 2.5.2
Component of the Keyple C++ middleware
CardTerminal.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/plugin/pcsc/cpp/CardTerminal.hpp"
15
16#include <chrono>
17#include <cstdint>
18#include <string>
19
20#include "PcscUtils.hpp"
21
22#include "keyple/core/util/cpp/KeypleStd.hpp"
23#include "keyple/core/util/cpp/StringUtils.hpp"
24#include "keyple/core/util/cpp/System.hpp"
25#include "keyple/core/util/cpp/Thread.hpp"
26#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
27#include "keyple/core/util/cpp/exception/RuntimeException.hpp"
28#include "keyple/plugin/pcsc/cpp/CardTerminals.hpp"
29#include "keyple/plugin/pcsc/cpp/exception/CardException.hpp"
30#include "keyple/plugin/pcsc/cpp/exception/CardNotPresentException.hpp"
31#include "keyple/plugin/pcsc/cpp/exception/CardTerminalException.hpp"
32
33
34namespace keyple {
35namespace plugin {
36namespace pcsc {
37namespace cpp {
38
39using keyple::core::util::cpp::StringUtils;
40using keyple::core::util::cpp::System;
41using keyple::core::util::cpp::Thread;
42using keyple::core::util::cpp::exception::IllegalArgumentException;
43using keyple::core::util::cpp::exception::RuntimeException;
44using keyple::plugin::pcsc::cpp::exception::CardException;
45using keyple::plugin::pcsc::cpp::exception::CardNotPresentException;
46using keyple::plugin::pcsc::cpp::exception::CardTerminalException;
47
49
50CardTerminal::CardTerminal(
51 const std::shared_ptr<CardTerminals> cardTerminals
52, const std::string& name)
53: mName(name)
54, mCardTerminals(cardTerminals)
55{
56}
57
58const std::string&
59CardTerminal::getName() const
60{
61 return mName;
62}
63
64std::shared_ptr<Card>
65CardTerminal::connect(const std::string& protocol)
66{
67 int dwPreferredProtocols;
68 int dwShareMode = SCARD_SHARE_SHARED;
69
70 std::string _protocol = StringUtils::toupper(protocol);
71
72 /* Proprietary extension */
73 if (StringUtils::startsWith(protocol, "EXCLUSIVE;")) {
74 dwShareMode = SCARD_SHARE_EXCLUSIVE;
75 const size_t prefixSize = std::string("EXCLUSIVE;").size();
76 _protocol = _protocol.substr(prefixSize, protocol.size() - prefixSize);
77 }
78
79 if ("T=0" == _protocol) {
80 dwPreferredProtocols = SCARD_PROTOCOL_T0;
81 } else if ("T=1" == _protocol) {
82 dwPreferredProtocols = SCARD_PROTOCOL_T1;
83 } else if ("*" == _protocol) {
84 dwPreferredProtocols = SCARD_PROTOCOL_ANY;
85 } else if ("DIRECT" == _protocol) {
86 /* Connect directly to reader to send control commands. */
87 dwPreferredProtocols = 0;
88 // /* OSX 10.11 would otherwise fail with SCARD_E_INVALID_VALUE
89 // if (Platform.isMac()) {
90 // dwPreferredProtocols = SCARD_PROTOCOL_ANY;
91 // }
92 dwShareMode = SCARD_SHARE_DIRECT;
93 } else {
94 throw IllegalArgumentException(
95 "Protocol should be one of (prepended with EXCLUSIVE;) T=0, T=1," \
96 " *, DIRECT. Got " + protocol);
97 }
98
99 DWORD dwProtocol;
100 SCARDHANDLE handle;
101 SCARD_IO_REQUEST ioRequest;
102
103 LONG rv = SCardConnect(
104 mCardTerminals->mContext,
105 (LPCSTR)mName.c_str(),
106 (DWORD)dwShareMode,
107 (DWORD)dwPreferredProtocols,
108 &handle,
109 &dwProtocol);
110
111 if (rv == SCARD_S_SUCCESS) {
112 switch (dwProtocol) {
113 case SCARD_PROTOCOL_T0:
114 ioRequest = *SCARD_PCI_T0;
115 break;
116 case SCARD_PROTOCOL_T1:
117 ioRequest = *SCARD_PCI_T1;
118 break;
119 }
120
121 DWORD readerLength;
122 BYTE _atr[33];
123 DWORD atrLen = sizeof(_atr);
124 DWORD state;
125
126 rv = SCardStatus(
127 handle,
128 NULL,
129 &readerLength,
130 &state,
131 &dwProtocol,
132 _atr,
133 &atrLen);
134
135 std::vector<uint8_t> atr(_atr, _atr + atrLen);
136
137 return std::make_shared<Card>(
138 shared_from_this(), handle, atr, dwProtocol, ioRequest);
139
140 } else if (isCardCommunicationError(rv)) {
141 throw CardNotPresentException(
142 "Card not present: error " + std::string(pcsc_stringify_error(rv)));
143
144 } else {
145 mLogger->error(
146 "SCardConnect failed with error: %\n",
147 std::string(pcsc_stringify_error(rv)));
148 throw CardException(
149 "Failed to connect to card: error " +
150 std::string(pcsc_stringify_error(rv)));
151 }
152}
153
154bool
155CardTerminal::isCardPresent()
156{
157 SCARD_READERSTATE states[1] = {};
158 states[0].szReader = mName.c_str();
159
160 LONG rv = SCardGetStatusChange(mCardTerminals->mContext, 0, states, 1);
161 if (rv != SCARD_S_SUCCESS) {
162 mLogger->error(
163 "SCardGetStatusChange failed with error: %\n",
164 std::string(pcsc_stringify_error(rv)));
165 throw CardException(
166 "Failed to get reader status: error " +
167 std::string(pcsc_stringify_error(rv)));
168 }
169
170 return 0 != (states[0].dwEventState & SCARD_STATE_PRESENT);
171}
172
173bool
174CardTerminal::waitForCardAbsent(uint64_t timeout)
175{
176 uint64_t newMs = 0;
177 uint64_t currentMs
178 = std::chrono::duration_cast<std::chrono::milliseconds>(
179 std::chrono::system_clock::now().time_since_epoch())
180 .count();
181
182 do {
183 bool isCardAbsent = !this->isCardPresent();
184 if (isCardAbsent) {
185 return true;
186 }
187
188 /*
189 * isCardPresent() polls SCardGetStatusChange with a zero timeout,
190 * Handle pause here.
191 */
192 Thread::sleep(50);
193
194 newMs = std::chrono::duration_cast<std::chrono::milliseconds>(
195 std::chrono::system_clock::now().time_since_epoch())
196 .count();
197
198 } while (newMs <= (currentMs + timeout));
199
200 return false;
201}
202
203bool
204CardTerminal::waitForCardPresent(uint64_t timeout)
205{
206 uint64_t newMs = 0;
207 uint64_t currentMs
208 = std::chrono::duration_cast<std::chrono::milliseconds>(
209 std::chrono::system_clock::now().time_since_epoch())
210 .count();
211
212 do {
213 bool isCardPresent = this->isCardPresent();
214 if (isCardPresent) {
215 return true;
216 }
217
218 /*
219 * isCardPresent() polls SCardGetStatusChange with a zero timeout,
220 * Handle pause here.
221 */
222 Thread::sleep(50);
223
224 newMs = std::chrono::duration_cast<std::chrono::milliseconds>(
225 std::chrono::system_clock::now().time_since_epoch())
226 .count();
227
228 } while (newMs <= (currentMs + timeout));
229
230 return false;
231}
232
233bool
234CardTerminal::operator==(const CardTerminal& o) const
235{
236 return !mName.compare(o.mName);
237}
238
239bool
240CardTerminal::operator!=(const CardTerminal& o) const
241{
242 return !(*this == o);
243}
244
245std::ostream&
246operator<<(std::ostream& os, const CardTerminal& t)
247{
248 os << "CardTerminal: {"
249 << "NAME = " << t.mName << "}";
250
251 return os;
252}
253
254std::ostream&
255operator<<(std::ostream& os, const std::vector<CardTerminal>& vt)
256{
257 os << "CardTerminalS: {";
258 for (const auto& t : vt) {
259 os << t;
260 if (t != vt.back())
261 os << ", ";
262 }
263 os << "}";
264
265 return os;
266}
267
268} /* namespace cpp */
269} /* namespace pcsc */
270} /* namespace plugin */
271} /* namespace keyple */
#define SCARD_PROTOCOL_ANY
Definition: PcscUtils.hpp:26
PcscReader::DisconnectionMode DisconnectionMode
bool isCardCommunicationError(uint64_t rv)
Definition: PcscUtils.hpp:95
std::ostream & operator<<(std::ostream &os, const PcscReader::IsoProtocol &ip)
Definition: PcscReader.cpp:40
Definition: Card.cpp:20