1 /*!
2  * \copyright Copyright (c) 2017-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "RemoteServiceModel.h"
6 
7 #include "ApplicationModel.h"
8 #include "AppSettings.h"
9 #include "EstablishPaceChannel.h"
10 #include "NumberModel.h"
11 #include "RemoteClientImpl.h"
12 #include "RemoteServiceSettings.h"
13 
14 #ifdef Q_OS_IOS
15 #include <QOperatingSystemVersion>
16 #endif
17 
18 using namespace governikus;
19 
RemoteServiceModel()20 RemoteServiceModel::RemoteServiceModel()
21 	: WorkflowModel()
22 	, mContext()
23 	, mRunnable(false)
24 	, mIsStarting(false)
25 	, mCanEnableNfc(false)
26 	, mPairingRequested(false)
27 	, mRequestTransportPin(false)
28 	, mErrorMessage()
29 	, mPsk()
30 	, mAvailableRemoteDevices(this, false, true)
31 	, mKnownDevices(this, true, false)
32 	, mCombinedDevices(this, true, true)
33 	, mConnectionInfo()
34 	, mConnectedServerDeviceNames()
35 	, mRememberedServerEntry()
36 #ifdef Q_OS_IOS
37 	// iOS 14 introduced a local network permission, so we need to handle it.
38 	, mRequiresLocalNetworkPermission(QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 14))
39 #else
40 	, mRequiresLocalNetworkPermission(false)
41 #endif
42 {
43 	const auto readerManager = Env::getSingleton<ReaderManager>();
44 	connect(readerManager, &ReaderManager::firePluginAdded, this, &RemoteServiceModel::onEnvironmentChanged);
45 	connect(readerManager, &ReaderManager::fireStatusChanged, this, &RemoteServiceModel::onEnvironmentChanged);
46 	connect(readerManager, &ReaderManager::fireReaderAdded, this, &RemoteServiceModel::onEnvironmentChanged);
47 	connect(readerManager, &ReaderManager::fireReaderRemoved, this, &RemoteServiceModel::onEnvironmentChanged);
48 	const auto applicationModel = Env::getSingleton<ApplicationModel>();
49 	connect(applicationModel, &ApplicationModel::fireWifiEnabledChanged, this, &RemoteServiceModel::onEnvironmentChanged);
50 
51 	const auto* const remoteClient = Env::getSingleton<RemoteClient>();
52 	connect(remoteClient, &RemoteClient::fireDetectionChanged, this, &RemoteServiceModel::fireDetectionChanged);
53 	connect(remoteClient, &RemoteClient::fireNewRemoteDispatcher, this, &RemoteServiceModel::onConnectedDevicesChanged);
54 	connect(remoteClient, &RemoteClient::fireDispatcherDestroyed, this, &RemoteServiceModel::onConnectedDevicesChanged);
55 	connect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteServiceModel::fireRemoteReaderVisibleChanged);
56 	connect(remoteClient, &RemoteClient::fireDeviceVanished, this, &RemoteServiceModel::fireRemoteReaderVisibleChanged);
57 
58 	const auto& settings = Env::getSingleton<AppSettings>()->getGeneralSettings();
59 	connect(&settings, &GeneralSettings::fireLanguageChanged, this, &RemoteServiceModel::onEnvironmentChanged);
60 
61 	QMetaObject::invokeMethod(this, &RemoteServiceModel::onEnvironmentChanged, Qt::QueuedConnection);
62 }
63 
64 
onEnvironmentChanged()65 void RemoteServiceModel::onEnvironmentChanged()
66 {
67 	bool nfcPluginAvailable = false;
68 	bool nfcPluginEnabled = false;
69 	const auto& allPlugins = Env::getSingleton<ReaderManager>()->getPlugInInfos();
70 	for (const auto& pluginInfo : allPlugins)
71 	{
72 		if (pluginInfo.getPlugInType() != ReaderManagerPlugInType::NFC)
73 		{
74 			// At this time no basic reader available so we can skip
75 			continue;
76 		}
77 
78 		nfcPluginAvailable |= pluginInfo.isAvailable();
79 		nfcPluginEnabled |= pluginInfo.isEnabled();
80 	}
81 
82 	bool readerAvailable = !(Env::getSingleton<ReaderManager>()->getReaderInfos(ReaderFilter({ReaderManagerPlugInType::NFC})).isEmpty());
83 	const bool wifiEnabled = Env::getSingleton<ApplicationModel>()->isWifiEnabled();
84 
85 	const bool runnable = readerAvailable && wifiEnabled;
86 	const bool canEnableNfc = nfcPluginAvailable && !nfcPluginEnabled;
87 	const QString errorMessage = getErrorMessage(nfcPluginAvailable, nfcPluginEnabled, wifiEnabled);
88 	if (mRunnable != runnable || mCanEnableNfc != canEnableNfc || mErrorMessage != errorMessage)
89 	{
90 		mRunnable = runnable;
91 		mCanEnableNfc = canEnableNfc;
92 		mErrorMessage = errorMessage;
93 
94 		Q_EMIT fireEnvironmentChanged();
95 	}
96 
97 	if (!runnable && isRunning())
98 	{
99 		setRunning(false);
100 	}
101 }
102 
103 
isRunning() const104 bool RemoteServiceModel::isRunning() const
105 {
106 	return mContext ? mContext->isRunning() : false;
107 }
108 
109 
setRunning(bool pState,bool pEnablePairing)110 void RemoteServiceModel::setRunning(bool pState, bool pEnablePairing)
111 {
112 	bool enablePairing = pEnablePairing && pState;
113 	if (mContext)
114 	{
115 		setPairing(enablePairing);
116 	}
117 	else
118 	{
119 		mPairingRequested = enablePairing;
120 	}
121 
122 	if (isRunning() == pState)
123 	{
124 		return;
125 	}
126 
127 	if (isRunning() && mContext)
128 	{
129 		Q_EMIT mContext->fireCancelWorkflow();
130 	}
131 	else
132 	{
133 		setStarting(true);
134 		Q_EMIT fireStartWorkflow();
135 	}
136 }
137 
138 
setStarting(bool pStarting)139 void RemoteServiceModel::setStarting(bool pStarting)
140 {
141 	mIsStarting = pStarting;
142 	Q_EMIT fireIsStartingChanged();
143 }
144 
145 
isStarting() const146 bool RemoteServiceModel::isStarting() const
147 {
148 	return mIsStarting;
149 }
150 
151 
getAvailableRemoteDevices()152 RemoteDeviceModel* RemoteServiceModel::getAvailableRemoteDevices()
153 {
154 	return &mAvailableRemoteDevices;
155 }
156 
157 
getKnownDevices()158 RemoteDeviceModel* RemoteServiceModel::getKnownDevices()
159 {
160 	return &mKnownDevices;
161 }
162 
163 
getCombinedDevices()164 RemoteDeviceModel* RemoteServiceModel::getCombinedDevices()
165 {
166 	return &mCombinedDevices;
167 }
168 
169 
setDetectRemoteDevices(bool pNewStatus)170 void RemoteServiceModel::setDetectRemoteDevices(bool pNewStatus)
171 {
172 	if (pNewStatus == Env::getSingleton<RemoteClient>()->isDetecting())
173 	{
174 		return;
175 	}
176 
177 	if (pNewStatus)
178 	{
179 		mAvailableRemoteDevices.onUiShown();
180 		mKnownDevices.onUiShown();
181 	}
182 	else
183 	{
184 		mAvailableRemoteDevices.onUiHidden();
185 		mKnownDevices.onUiHidden();
186 	}
187 }
188 
189 
detectRemoteDevices() const190 bool RemoteServiceModel::detectRemoteDevices() const
191 {
192 	return Env::getSingleton<RemoteClient>()->isDetecting();
193 }
194 
195 
connectToRememberedServer(const QString & pServerPsk)196 void RemoteServiceModel::connectToRememberedServer(const QString& pServerPsk)
197 {
198 	if (!pServerPsk.isEmpty() && !mRememberedServerEntry.isNull())
199 	{
200 		auto* const remoteClient = Env::getSingleton<RemoteClient>();
201 		connect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone);
202 
203 		qDebug() << "Starting to pair.";
204 		remoteClient->establishConnection(mRememberedServerEntry, pServerPsk);
205 	}
206 }
207 
208 
rememberServer(const QString & pDeviceId)209 bool RemoteServiceModel::rememberServer(const QString& pDeviceId)
210 {
211 	mRememberedServerEntry = mAvailableRemoteDevices.getRemoteDeviceListEntry(pDeviceId);
212 	return !mRememberedServerEntry.isNull();
213 }
214 
215 
onEstablishConnectionDone(const QSharedPointer<RemoteDeviceListEntry> & pEntry,const GlobalStatus & pStatus)216 void RemoteServiceModel::onEstablishConnectionDone(const QSharedPointer<RemoteDeviceListEntry>& pEntry, const GlobalStatus& pStatus)
217 {
218 	const auto* const remoteClient = Env::getSingleton<RemoteClient>();
219 	disconnect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone);
220 	qDebug() << "Pairing finished:" << pStatus;
221 	const auto deviceName = RemoteServiceSettings::escapeDeviceName(pEntry->getRemoteDeviceDescriptor().getIfdName());
222 	if (pStatus.isError())
223 	{
224 		Q_EMIT firePairingFailed(deviceName, pStatus.toErrorDescription());
225 	}
226 	else
227 	{
228 		Q_EMIT firePairingSuccess(deviceName);
229 	}
230 }
231 
232 
onConnectionInfoChanged(bool pConnected)233 void RemoteServiceModel::onConnectionInfoChanged(bool pConnected)
234 {
235 	if (mContext && pConnected)
236 	{
237 		const RemoteServiceSettings& settings = Env::getSingleton<AppSettings>()->getRemoteServiceSettings();
238 		const QString peerName = settings.getRemoteInfo(mContext->getRemoteServer()->getCurrentCertificate()).getNameEscaped();
239 		//: INFO ANDROID IOS The smartphone is connected as card reader (SaK) and currently processing an authentication request. The user is asked to pay attention the its screen.
240 		mConnectionInfo = tr("Please pay attention to the display on your other device \"%1\".").arg(peerName);
241 		Q_EMIT fireConnectionInfoChanged();
242 	}
243 	Q_EMIT fireConnectedChanged();
244 }
245 
246 
onCardConnectionEstablished(const QSharedPointer<CardConnection> & pConnection)247 void RemoteServiceModel::onCardConnectionEstablished(const QSharedPointer<CardConnection>& pConnection)
248 {
249 	pConnection->setProgressMessage(mConnectionInfo);
250 }
251 
252 
resetRemoteServiceContext(const QSharedPointer<RemoteServiceContext> & pContext)253 void RemoteServiceModel::resetRemoteServiceContext(const QSharedPointer<RemoteServiceContext>& pContext)
254 {
255 	mContext = pContext;
256 	WorkflowModel::resetWorkflowContext(pContext);
257 
258 	mPsk.clear();
259 	onEstablishPaceChannelUpdated();
260 
261 	if (mContext)
262 	{
263 		connect(mContext.data(), &RemoteServiceContext::fireIsRunningChanged, this, [this](){
264 					setStarting(false);
265 				});
266 		connect(mContext.data(), &RemoteServiceContext::fireIsRunningChanged, this, &RemoteServiceModel::fireIsRunningChanged);
267 		connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, [this](const QByteArray& pPsk){
268 					mPsk = pPsk;
269 				});
270 		connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, &RemoteServiceModel::firePskChanged);
271 		connect(mContext->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &RemoteServiceModel::onConnectionInfoChanged);
272 		connect(mContext->getRemoteServer().data(), &RemoteServer::firePairingCompleted, this, &RemoteServiceModel::firePairingCompleted);
273 		connect(mContext.data(), &RemoteServiceContext::fireCardConnectionEstablished, this, &RemoteServiceModel::onCardConnectionEstablished);
274 		connect(mContext.data(), &RemoteServiceContext::fireEstablishPaceChannelUpdated, this, &RemoteServiceModel::onEstablishPaceChannelUpdated);
275 
276 		setPairing(mPairingRequested);
277 	}
278 	else
279 	{
280 		setStarting(false);
281 	}
282 
283 	mPairingRequested = false;
284 
285 	Q_EMIT fireIsRunningChanged();
286 	Q_EMIT fireConnectedChanged();
287 }
288 
289 
setPairing(bool pEnabled)290 void RemoteServiceModel::setPairing(bool pEnabled)
291 {
292 	if (mContext)
293 	{
294 		mContext->getRemoteServer()->setPairing(pEnabled);
295 	}
296 }
297 
298 
isPairing()299 bool RemoteServiceModel::isPairing()
300 {
301 	if (!mContext)
302 	{
303 		return false;
304 	}
305 
306 	return !getPsk().isEmpty();
307 }
308 
309 
isConnectedToPairedDevice() const310 bool RemoteServiceModel::isConnectedToPairedDevice() const
311 {
312 	if (mContext)
313 	{
314 		return mContext->getRemoteServer()->isConnected() && !mContext->getRemoteServer()->isPairingConnection();
315 	}
316 
317 	return false;
318 }
319 
320 
enableTransportPinLink() const321 bool RemoteServiceModel::enableTransportPinLink() const
322 {
323 	return mContext && mContext->isPinChangeWorkflow() && mContext->getPreferredPinLength() == 0;
324 }
325 
326 
isRequestTransportPin() const327 bool RemoteServiceModel::isRequestTransportPin() const
328 {
329 	return mRequestTransportPin;
330 }
331 
332 
isRunnable() const333 bool RemoteServiceModel::isRunnable() const
334 {
335 	return mRunnable;
336 }
337 
338 
isCanEnableNfc() const339 bool RemoteServiceModel::isCanEnableNfc() const
340 {
341 	return mCanEnableNfc;
342 }
343 
344 
getErrorMessage() const345 QString RemoteServiceModel::getErrorMessage() const
346 {
347 	return mErrorMessage;
348 }
349 
350 
getPsk() const351 QByteArray RemoteServiceModel::getPsk() const
352 {
353 	return mPsk;
354 }
355 
356 
getConnectionInfo() const357 QString RemoteServiceModel::getConnectionInfo() const
358 {
359 	return mConnectionInfo;
360 }
361 
362 
getConnectedServerDeviceNames() const363 QString RemoteServiceModel::getConnectedServerDeviceNames() const
364 {
365 	return mConnectedServerDeviceNames;
366 }
367 
368 
getRemoteReaderVisible() const369 bool RemoteServiceModel::getRemoteReaderVisible() const
370 {
371 	return Env::getSingleton<RemoteClient>()->hasAnnouncingRemoteDevices();
372 }
373 
374 
pinPadModeOn() const375 bool RemoteServiceModel::pinPadModeOn() const
376 {
377 	return Env::getSingleton<AppSettings>()->getRemoteServiceSettings().getPinPadMode();
378 }
379 
380 
getPasswordType() const381 QString RemoteServiceModel::getPasswordType() const
382 {
383 	if (!mContext)
384 	{
385 		return QString();
386 	}
387 
388 	switch (mContext->getEstablishPaceChannel().getPasswordId())
389 	{
390 		case PacePasswordId::PACE_CAN:
391 			return QStringLiteral("CAN");
392 
393 		case PacePasswordId::PACE_PIN:
394 			return QStringLiteral("PIN");
395 
396 		case PacePasswordId::PACE_PUK:
397 			return QStringLiteral("PUK");
398 
399 		default:
400 			return QString();
401 	}
402 }
403 
404 
getErrorMessage(bool pNfcPluginAvailable,bool pNfcPluginEnabled,bool pWifiEnabled) const405 QString RemoteServiceModel::getErrorMessage(bool pNfcPluginAvailable, bool pNfcPluginEnabled, bool pWifiEnabled) const
406 {
407 	if (!pNfcPluginAvailable)
408 	{
409 		//: INFO ALL_PLATFORMS The device does not offer NFC.
410 		return tr("NFC is not available on your device.");
411 	}
412 	if (!pNfcPluginEnabled)
413 	{
414 		//: INFO ALL_PLATFORMS NFC is available but not active.
415 		return tr("Please enable NFC to use the remote service.");
416 	}
417 	if (!pWifiEnabled)
418 	{
419 		//: INFO ALL_PLATFORMS The WiFi feature is not enabled but required to use the smartphone as a card reader (SaK).
420 		return tr("Please connect your WiFi to use the remote service.");
421 	}
422 
423 	return QString();
424 }
425 
426 
forgetDevice(const QString & pId)427 void RemoteServiceModel::forgetDevice(const QString& pId)
428 {
429 	mKnownDevices.forgetDevice(pId);
430 }
431 
432 
cancelPasswordRequest()433 void RemoteServiceModel::cancelPasswordRequest()
434 {
435 	if (mContext)
436 	{
437 		Q_EMIT mContext->fireCancelPasswordRequest();
438 	}
439 }
440 
441 
changePinLength()442 void RemoteServiceModel::changePinLength()
443 {
444 	mRequestTransportPin = !mRequestTransportPin;
445 	Q_EMIT fireEstablishPaceChannelUpdated();
446 }
447 
448 
onConnectedDevicesChanged()449 void RemoteServiceModel::onConnectedDevicesChanged()
450 {
451 	auto* const remoteClient = Env::getSingleton<RemoteClient>();
452 	const auto deviceInfos = remoteClient->getConnectedDeviceInfos();
453 	QStringList deviceNames;
454 	for (const auto& info : deviceInfos)
455 	{
456 		deviceNames.append(QLatin1Char('"') + info.getNameEscaped() + QLatin1Char('"'));
457 	}
458 	mConnectedServerDeviceNames = deviceNames.join(QLatin1String(", "));
459 	Q_EMIT fireConnectedServerDeviceNamesChanged();
460 }
461 
462 
onEstablishPaceChannelUpdated()463 void RemoteServiceModel::onEstablishPaceChannelUpdated()
464 {
465 	mRequestTransportPin = (mContext && mContext->isPinChangeWorkflow() && mContext->getPreferredPinLength() == 5);
466 	Q_EMIT fireEstablishPaceChannelUpdated();
467 }
468