Keyple Plugin PC/SC C++ Library - 2.6.3
Component of the Keyple C++ middleware
PcscPluginAdapter.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/PcscPluginAdapter.hpp"
15
16#include "keyple/core/plugin/PluginIOException.hpp"
17#include "keyple/core/util/cpp/KeypleStd.hpp"
18#include "keyple/core/util/cpp/StringUtils.hpp"
19#include "keyple/core/util/cpp/exception/Exception.hpp"
20#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
21#include "keyple/plugin/pcsc/PcscPluginFactoryAdapter.hpp"
22#include "keyple/plugin/pcsc/PcscReaderAdapter.hpp"
23#include "keyple/plugin/pcsc/PcscCardCommunicationProtocol.hpp"
24#include "keyple/plugin/pcsc/PcscSupportedContactProtocol.hpp"
25#include "keyple/plugin/pcsc/PcscSupportedContactlessProtocol.hpp"
26#include "keyple/plugin/pcsc/cpp/CardTerminal.hpp"
27#include "keyple/plugin/pcsc/cpp/CardTerminals.hpp"
28#include "keyple/plugin/pcsc/cpp/TerminalFactory.hpp"
29#include "keyple/plugin/pcsc/cpp/exception/CardTerminalException.hpp"
30
31namespace keyple {
32namespace plugin {
33namespace pcsc {
34
35using keyple::core::plugin::PluginIOException;
36using keyple::core::util::cpp::StringUtils;
37using keyple::core::util::cpp::exception::Exception;
38using keyple::core::util::cpp::exception::IllegalArgumentException;
39using keyple::plugin::pcsc::cpp::CardTerminal;
40using keyple::plugin::pcsc::cpp::CardTerminals;
41using keyple::plugin::pcsc::cpp::TerminalFactory;
42using keyple::plugin::pcsc::cpp::exception::CardTerminalException;
43
44std::shared_ptr<PcscPluginAdapter> PcscPluginAdapter::INSTANCE;
45
46const int PcscPluginAdapter::MONITORING_CYCLE_DURATION_MS = 1000;
47
48PcscPluginAdapter::PcscPluginAdapter()
49: PcscPlugin()
50, ObservablePluginSpi()
51, mIsCardTerminalsInitialized(false)
52{
53 /* Initializes the protocol rules map with default values. */
54 mProtocolRulesMap = {
55
56 /* Contactless protocols */
57 {PcscCardCommunicationProtocol::ISO_14443_4.getName(),
58 PcscCardCommunicationProtocol::ISO_14443_4.getDefaultRule()},
59 {PcscCardCommunicationProtocol::INNOVATRON_B_PRIME.getName(),
60 PcscCardCommunicationProtocol::INNOVATRON_B_PRIME.getDefaultRule()},
61 {PcscSupportedContactlessProtocol::INNOVATRON_B_PRIME_CARD.getName(),
62 PcscSupportedContactlessProtocol::INNOVATRON_B_PRIME_CARD.getDefaultRule()},
63 {PcscCardCommunicationProtocol::MIFARE_ULTRALIGHT.getName(),
64 PcscCardCommunicationProtocol::MIFARE_ULTRALIGHT.getDefaultRule()},
65 {PcscSupportedContactlessProtocol::MIFARE_ULTRA_LIGHT.getName(),
66 PcscSupportedContactlessProtocol::MIFARE_ULTRA_LIGHT.getDefaultRule()},
67 {PcscSupportedContactlessProtocol::MIFARE_CLASSIC.getName(),
68 PcscSupportedContactlessProtocol::MIFARE_CLASSIC.getDefaultRule()},
69 {PcscCardCommunicationProtocol::MIFARE_CLASSIC_1K.getName(),
70 PcscCardCommunicationProtocol::MIFARE_CLASSIC_1K.getDefaultRule()},
71 {PcscCardCommunicationProtocol::MIFARE_CLASSIC_4K.getName(),
72 PcscCardCommunicationProtocol::MIFARE_CLASSIC_4K.getDefaultRule()},
73 {PcscSupportedContactlessProtocol::MIFARE_DESFIRE.getName(),
74 PcscSupportedContactlessProtocol::MIFARE_DESFIRE.getDefaultRule()},
75 {PcscCardCommunicationProtocol::ST25_SRT512.getName(),
76 PcscCardCommunicationProtocol::ST25_SRT512.getDefaultRule()},
77 {PcscSupportedContactlessProtocol::MEMORY_ST25.getName(),
78 PcscSupportedContactlessProtocol::MEMORY_ST25.getDefaultRule()},
79
80 /* Contact protocols */
81 {PcscCardCommunicationProtocol::ISO_7816_3.getName(),
82 PcscCardCommunicationProtocol::ISO_7816_3.getDefaultRule()},
83
84 /* Legacy protocols for compatibility */
85 {PcscSupportedContactProtocol::ISO_7816_3_T0.getName(),
86 PcscSupportedContactProtocol::ISO_7816_3_T0.getDefaultRule()},
87 {PcscSupportedContactProtocol::ISO_7816_3_T1.getName(),
88 PcscSupportedContactProtocol::ISO_7816_3_T1.getDefaultRule()}};
89
90}
91
92std::shared_ptr<PcscPluginAdapter>
93PcscPluginAdapter::getInstance()
94{
95 if (!INSTANCE) {
96 INSTANCE = std::make_shared<PcscPluginAdapter>();
97 }
98
99 return INSTANCE;
100}
101
102std::unique_ptr<PcscReaderAdapter>
103PcscPluginAdapter::createReader(std::shared_ptr<CardTerminal> terminal)
104{
105 return std::unique_ptr<PcscReaderAdapter>(new PcscReaderAdapter(
106 terminal, shared_from_this(), mCardMonitoringCycleDuration));
107}
108
109int
110PcscPluginAdapter::getMonitoringCycleDuration() const
111{
112 return MONITORING_CYCLE_DURATION_MS;
113}
114
115const std::vector<std::string>
116PcscPluginAdapter::searchAvailableReaderNames()
117{
118 std::vector<std::string> readerNames;
119
120 mLogger->trace("Searching available reader names\n");
121
122 for (const auto& terminal : getCardTerminalList()) {
123 readerNames.push_back(terminal->getName());
124 }
125
126 mLogger->trace("Readers found [names=%]\n", readerNames);
127
128 return readerNames;
129}
130
131const std::string&
132PcscPluginAdapter::getName() const
133{
134 return PcscPluginFactoryAdapter::PLUGIN_NAME;
135}
136
137const std::vector<std::shared_ptr<ReaderSpi>>
138PcscPluginAdapter::searchAvailableReaders()
139{
140 std::vector<std::shared_ptr<ReaderSpi>> readerSpis;
141
142 mLogger->trace("Searching available readers\n");
143
144 for (const auto& terminal : getCardTerminalList()) {
145 readerSpis.push_back(createReader(terminal));
146 }
147
148 for (const auto& readerSpi : readerSpis) {
149 mLogger->trace(
150 "Reader found [name=%]\n", readerSpi->getName());
151 }
152
153 return readerSpis;
154}
155
156void
157PcscPluginAdapter::onUnregister()
158{
159 /* Nothing to do here in this plugin */
160}
161
162const std::vector<std::shared_ptr<CardTerminal>>
163PcscPluginAdapter::getCardTerminalList()
164{
165 /*
166 * Parse the current readers list to create the ReaderSpi(s) associated with
167 * new reader(s).
168 */
169
170 try {
171 if (!mIsCardTerminalsInitialized) {
172 mTerminals = TerminalFactory::getDefault()->terminals();
173 mIsCardTerminalsInitialized = true;
174 }
175
176 return mTerminals->list();
177
178 } catch (const Exception& e) {
179 const auto msg = e.getMessage();
180
181 if (StringUtils::contains(msg, "SCARD_E_NO_READERS_AVAILABLE")) {
182 mLogger->error("No reader available\n");
183
184 } else if (
185 StringUtils::contains(msg, "SCARD_E_NO_SERVICE")
186 || StringUtils::contains(msg, "SCARD_E_SERVICE_STOPPED")) {
187 mLogger->error(
188 "No running smart card service\n");
189 mIsCardTerminalsInitialized = false;
190
191 } else if (StringUtils::contains(msg, "SCARD_F_COMM_ERROR")) {
192 mLogger->error(
193 "Reader communication error occurred\n");
194
195 } else {
196 throw PluginIOException(
197 "Could not access terminals list",
198 CardTerminalException(msg));
199 }
200 }
201
202 return std::vector<std::shared_ptr<CardTerminal>>(0);
203}
204
205std::shared_ptr<ReaderSpi>
206PcscPluginAdapter::searchReader(const std::string& readerName)
207{
208 mLogger->trace("Searching reader [reader=%]\n", readerName);
209
210 const auto terminals = getCardTerminalList();
211 auto it = std::find_if(
212 terminals.begin(),
213 terminals.end(),
214 [readerName](const std::shared_ptr<CardTerminal>& terminal) {
215 return terminal->getName() == readerName;
216 });
217
218 if (it != terminals.end()) {
219 mLogger->trace("Reader found\n");
220 return createReader(*it);
221 }
222
223 mLogger->trace("Reader not found\n");
224
225 return nullptr;
226}
227
228const std::string&
229PcscPluginAdapter::getProtocolRule(const std::string& readerProtocol) const
230{
231 const auto it = mProtocolRulesMap.find(readerProtocol);
232 if (it != mProtocolRulesMap.end()) {
233 return it->second;
234
235 } else {
236 /*
237 * C++ specific, cannot return null here, to check if not a problem
238 * with the flow somewhere...
239 */
240 throw IllegalArgumentException("bad protocol");
241 }
242}
243
244bool
245PcscPluginAdapter::isContactless(const std::string& readerName)
246{
247 return mContactlessReaderIdentificationFilterPattern->matcher(readerName)
248 ->matches();
249}
250
251PcscPluginAdapter&
252PcscPluginAdapter::setContactlessReaderIdentificationFilterPattern(
253 const std::shared_ptr<Pattern> contactlessReaderIdentificationFilter)
254{
255 mContactlessReaderIdentificationFilterPattern
256 = contactlessReaderIdentificationFilter;
257
258 return *this;
259}
260
261PcscPluginAdapter&
262PcscPluginAdapter::addProtocolRulesMap(
263 const std::map<std::string, std::string> protocolRulesMap)
264{
265 if (!protocolRulesMap.empty()) {
266 mLogger->info(
267 "Adding protocol identification rules [rules=%]\n",
268 protocolRulesMap);
269 } else {
270 mLogger->info(
271 "Using default protocol identification rules\n");
272 }
273
274 mProtocolRulesMap.insert(protocolRulesMap.begin(), protocolRulesMap.end());
275
276 return *this;
277}
278
279PcscPluginAdapter&
280PcscPluginAdapter::setCardMonitoringCycleDuration(
281 const int cardMonitoringCycleDuration)
282{
283 mCardMonitoringCycleDuration = cardMonitoringCycleDuration;
284
285 return *this;
286}
287
288} /* namespace pcsc */
289} /* namespace plugin */
290} /* namespace keyple */
Definition: Card.cpp:20