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