1 /*!
2  * \copyright Copyright (c) 2017-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "ReaderDriverModel.h"
6 
7 #include "AppSettings.h"
8 #include "Env.h"
9 #include "GeneralSettings.h"
10 #include "HelpAction.h"
11 #include "LanguageLoader.h"
12 #include "ReaderConfiguration.h"
13 #include "ReaderManager.h"
14 
15 #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
16 #include "ReaderDetector.h"
17 #endif
18 
19 using namespace governikus;
20 
21 
ReaderDriverModel(QObject * pParent)22 ReaderDriverModel::ReaderDriverModel(QObject* pParent)
23 	: QAbstractTableModel(pParent)
24 	, mKnownDrivers()
25 	, mConnectedReaders()
26 	, mConnectedReadersUpdateTime()
27 {
28 	const GeneralSettings& generalSettings = Env::getSingleton<AppSettings>()->getGeneralSettings();
29 	connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &ReaderDriverModel::fireLanguageChanged);
30 	connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &ReaderDriverModel::onUpdateContent);
31 
32 	const ReaderManager* const readerManager = Env::getSingleton<ReaderManager>();
33 
34 	connect(readerManager, &ReaderManager::fireReaderAdded, this, &ReaderDriverModel::onUpdateContent);
35 	connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ReaderDriverModel::onUpdateContent);
36 	connect(Env::getSingleton<ReaderConfiguration>(), &ReaderConfiguration::fireUpdated, this, &ReaderDriverModel::onUpdateContent);
37 	connect(Env::getSingleton<AppSettings>(), &AppSettings::fireSettingsChanged, this, &ReaderDriverModel::onUpdateContent);
38 
39 #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
40 	connect(Env::getSingleton<ReaderDetector>(), &ReaderDetector::fireReaderChangeDetected, this, &ReaderDriverModel::onUpdateContent);
41 #endif
42 
43 	onUpdateContent();
44 }
45 
46 
collectReaderData()47 void ReaderDriverModel::collectReaderData()
48 {
49 	mConnectedReaders.clear();
50 
51 	const QVector<ReaderInfo> installedReaders = Env::getSingleton<ReaderManager>()->getReaderInfos(ReaderFilter({
52 				ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::NFC
53 			}));
54 
55 	for (const auto& installedReader : installedReaders)
56 	{
57 		const auto& readerSettingsInfo = installedReader.getReaderConfigurationInfo();
58 		if (!readerSettingsInfo.getUrl().isEmpty())
59 		{
60 			mKnownDrivers += readerSettingsInfo;
61 			mConnectedReaders += readerSettingsInfo;
62 		}
63 	}
64 
65 #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
66 	QVector<ReaderConfigurationInfo> readersWithoutDriver;
67 	const auto& attachedSupportedDevices = Env::getSingleton<ReaderDetector>()->getAttachedSupportedDevices();
68 	for (const auto& info : attachedSupportedDevices)
69 	{
70 		if (!mConnectedReaders.contains(info))
71 		{
72 			readersWithoutDriver.append(info);
73 		}
74 	}
75 	mConnectedReaders += readersWithoutDriver;
76 #endif
77 }
78 
79 
indexIsValid(const QModelIndex & pIndex) const80 bool ReaderDriverModel::indexIsValid(const QModelIndex& pIndex) const
81 {
82 	if (!pIndex.isValid())
83 	{
84 		Q_ASSERT(false && "Invoked with an invalid QModelIndex.");
85 		return false;
86 	}
87 
88 	if (pIndex.row() >= rowCount() || pIndex.column() >= columnCount())
89 	{
90 		Q_ASSERT(false && "Invoked with a row or a column which is out of bounds.");
91 		return false;
92 	}
93 
94 	return true;
95 }
96 
97 
getStatus(const ReaderConfigurationInfo & pReaderConfigurationInfo) const98 QString ReaderDriverModel::getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const
99 {
100 	if (mConnectedReaders.isEmpty())
101 	{
102 		//: LABEL ALL_PLATFORMS
103 		return tr("Not connected");
104 	}
105 
106 	if (mKnownDrivers.contains(pReaderConfigurationInfo))
107 	{
108 		//: LABEL ALL_PLATFORMS
109 		return tr("Driver installed");
110 	}
111 
112 	//: LABEL ALL_PLATFORMS
113 	return tr("No driver installed");
114 }
115 
116 
onUpdateContent()117 void ReaderDriverModel::onUpdateContent()
118 {
119 	beginResetModel();
120 
121 	collectReaderData();
122 	mConnectedReadersUpdateTime = QTime::currentTime();
123 
124 	endResetModel();
125 
126 	Q_EMIT fireModelChanged();
127 }
128 
129 
headerData(int pSection,Qt::Orientation pOrientation,int pRole) const130 QVariant ReaderDriverModel::headerData(int pSection, Qt::Orientation pOrientation, int pRole) const
131 {
132 	if (pRole == Qt::DisplayRole && pOrientation == Qt::Horizontal)
133 	{
134 		switch (pSection)
135 		{
136 			case ColumnId::ReaderName:
137 				//: LABEL ALL_PLATFORMS
138 				return tr("Card reader");
139 
140 			case ColumnId::ReaderStatus:
141 				//: LABEL ALL_PLATFORMS
142 				return tr("Status");
143 
144 			default:
145 				return QVariant();
146 		}
147 	}
148 	return QVariant();
149 }
150 
151 
rowCount(const QModelIndex &) const152 int ReaderDriverModel::rowCount(const QModelIndex&) const
153 {
154 	return mConnectedReaders.size();
155 }
156 
157 
columnCount(const QModelIndex &) const158 int ReaderDriverModel::columnCount(const QModelIndex&) const
159 {
160 	return NUMBER_OF_COLUMNS;
161 }
162 
163 
data(const QModelIndex & pIndex,int pRole) const164 QVariant ReaderDriverModel::data(const QModelIndex& pIndex, int pRole) const
165 {
166 	if (!indexIsValid(pIndex))
167 	{
168 		return QVariant();
169 	}
170 
171 	const auto& reader = mConnectedReaders.at(pIndex.row());
172 	switch (pRole)
173 	{
174 		case Qt::DisplayRole:
175 			switch (pIndex.column())
176 			{
177 				case ColumnId::ReaderName:
178 					return reader.getName();
179 
180 				case ColumnId::ReaderStatus:
181 					return getStatus(reader);
182 
183 				default:
184 					return QVariant();
185 			}
186 
187 		case READER_NAME:
188 			return reader.getName();
189 
190 		case READER_STATUS:
191 			return getStatus(reader);
192 
193 		case READER_IMAGE_PATH:
194 			return getReaderImageUrl(pIndex);
195 
196 		case READER_HTML_DESCRIPTION:
197 			return getHTMLDescription(pIndex);
198 
199 		case READER_DRIVER_URL:
200 			return mConnectedReaders.at(pIndex.row()).getUrl();
201 
202 		case READER_INSTALLED_AND_SUPPORTED:
203 			return isInstalledSupportedReader(pIndex);
204 
205 		default:
206 			return QVariant();
207 	}
208 }
209 
210 
roleNames() const211 QHash<int, QByteArray> ReaderDriverModel::roleNames() const
212 {
213 	QHash<int, QByteArray> roles = QAbstractTableModel::roleNames();
214 	roles.insert(READER_NAME, "readerName");
215 	roles.insert(READER_STATUS, "readerStatus");
216 	roles.insert(READER_IMAGE_PATH, "readerImagePath");
217 	roles.insert(READER_HTML_DESCRIPTION, "readerHTMLDescription");
218 	roles.insert(READER_DRIVER_URL, "readerDriverUrl");
219 	roles.insert(READER_INSTALLED_AND_SUPPORTED, "readerInstalledAndSupported");
220 	return roles;
221 }
222 
223 
getNoReaderFoundIconPath() const224 QString ReaderDriverModel::getNoReaderFoundIconPath() const
225 {
226 	return ReaderConfiguration::getNoReaderFoundIconPath();
227 }
228 
229 
getReaderImageUrl(const QModelIndex & pIndex) const230 QUrl ReaderDriverModel::getReaderImageUrl(const QModelIndex& pIndex) const
231 {
232 	return mConnectedReaders.at(pIndex.row()).getIcon()->lookupUrl();
233 }
234 
235 
getReaderImagePath(const QModelIndex & pIndex) const236 QString ReaderDriverModel::getReaderImagePath(const QModelIndex& pIndex) const
237 {
238 	if (!indexIsValid(pIndex))
239 	{
240 		return QString();
241 	}
242 
243 	return mConnectedReaders.at(pIndex.row()).getIcon()->lookupPath();
244 }
245 
246 
getHTMLDescription(const QModelIndex & pIndex) const247 QString ReaderDriverModel::getHTMLDescription(const QModelIndex& pIndex) const
248 {
249 	if (!indexIsValid(pIndex))
250 	{
251 		return QString();
252 	}
253 
254 	if (mConnectedReaders.isEmpty())
255 	{
256 		return QString();
257 	}
258 
259 	if (mKnownDrivers.contains(mConnectedReaders.at(pIndex.row())))
260 	{
261 		//: LABEL ALL_PLATFORMS
262 		return tr("Card reader ready for use.");
263 	}
264 
265 	//: INFO ALL_PLATFORMS The driver for card reader needs to be installed, the download link is provided in the message.
266 	return tr("Please download and install the driver you can find at: %1").
267 		   arg(QStringLiteral("<a href=\"%1\">%1</a>").arg(mConnectedReaders.at(pIndex.row()).getUrl()));
268 }
269 
270 
getEmptyListDescriptionString() const271 QString ReaderDriverModel::getEmptyListDescriptionString() const
272 {
273 	const QString& onlineHelpSection = QStringLiteral("settingsPcscReader");
274 	const QString& url = HelpAction::getOnlineUrl(onlineHelpSection);
275 	//: Is embedded in a sentence.
276 	const QString& hyperlink = QStringLiteral("<a href=\"%1\">%2</a>").arg(url, tr("online help"));
277 	//: INFO ALL_PLATFORMS No card reader was found, the message contains a link to the installation section of the manual.
278 	return tr("No connected card reader found. See %1 for installation of card readers.").arg(hyperlink);
279 }
280 
281 
isInstalledSupportedReader(const QModelIndex & pIndex) const282 bool ReaderDriverModel::isInstalledSupportedReader(const QModelIndex& pIndex) const
283 {
284 	if (!indexIsValid(pIndex))
285 	{
286 		return false;
287 	}
288 
289 	const auto& readerSettingsInfo = mConnectedReaders.at(pIndex.row());
290 	const bool knownDriver = mKnownDrivers.contains(readerSettingsInfo);
291 	const bool knownReader = readerSettingsInfo.isKnownReader();
292 	return knownDriver && knownReader;
293 }
294 
295 
getLastUpdatedInformation() const296 QString ReaderDriverModel::getLastUpdatedInformation() const
297 {
298 	if (!mConnectedReadersUpdateTime.isValid())
299 	{
300 		return QString();
301 	}
302 
303 	const auto& updateTime = LanguageLoader::getInstance().getUsedLocale().toString(mConnectedReadersUpdateTime, tr("hh:mm:ss AP"));
304 	//: LABEL ALL_PLATFORMS
305 	return tr("The list of card readers was last updated at %1.").arg(updateTime);
306 }
307