Lomiri
Loading...
Searching...
No Matches
AccountsService.cpp
1/*
2 * Copyright (C) 2013-2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "AccountsService.h"
18#include "AccountsServiceDBusAdaptor.h"
19
20#include <QDBusInterface>
21#include <QFile>
22#include <QStringList>
23#include <QDebug>
24
25#include <glib.h>
26#include <paths.h>
27
28#define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
29#define IFACE_UBUNTU_INPUT QStringLiteral("com.lomiri.AccountsService.Input")
30#define IFACE_UBUNTU_SECURITY QStringLiteral("com.lomiri.AccountsService.SecurityPrivacy")
31#define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.lomiri.touch.AccountsService.SecurityPrivacy")
32#define IFACE_LOMIRI QStringLiteral("com.lomiri.shell.AccountsService")
33#define IFACE_LOMIRI_PRIVATE QStringLiteral("com.lomiri.shell.AccountsService.Private")
34
35#define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
36#define PROP_DEMO_EDGES QStringLiteral("DemoEdges2")
37#define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted")
38#define PROP_EMAIL QStringLiteral("Email")
39#define PROP_ENABLE_FINGERPRINT_IDENTIFICATION QStringLiteral("EnableFingerprintIdentification")
40#define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
41#define PROP_HIDE_NOTIFICATION_CONTENT_WHILE_LOCKED QStringLiteral("HideNotificationContentWhileLocked")
42#define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
43#define PROP_FAILED_FINGERPRINT_LOGINS QStringLiteral("FailedFingerprintLogins")
44#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
45#define PROP_INPUT_SOURCES QStringLiteral("InputSources")
46#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
47#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
48#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
49#define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
50#define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
51#define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
52#define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
53#define PROP_PINCODE_PROMPT_MANAGER QStringLiteral("PinCodePromptManager")
54#define PROP_PINCODE_LENGTH QStringLiteral("PinCodeLength")
55#define PROP_REAL_NAME QStringLiteral("RealName")
56#define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
57#define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
58#define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
59#define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
60#define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
61#define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
62#define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
63#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
64#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
65
66using StringMap = QMap<QString,QString>;
67using StringMapList = QList<StringMap>;
68Q_DECLARE_METATYPE(StringMapList)
69
70
71QVariant primaryButtonConverter(const QVariant &value)
72{
73 QString stringValue = value.toString();
74 if (stringValue == QLatin1String("left")) {
75 return QVariant::fromValue(0);
76 } else if (stringValue == QLatin1String("right")) {
77 return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
78 } else {
79 return QVariant::fromValue(0); // default to left
80 }
81}
82
83AccountsService::AccountsService(QObject* parent, const QString &user)
84 : QObject(parent)
85 , m_defaultPinPromptManager("PinPrompt.qml")
86 , m_service(new AccountsServiceDBusAdaptor(this))
87{
88 m_syscompInput = new QDBusInterface(QStringLiteral("com.lomiri.SystemCompositor.Input"),
89 QStringLiteral("/com/lomiri/SystemCompositor/Input"),
90 QStringLiteral("com.lomiri.SystemCompositor.Input"),
91 QDBusConnection::SM_BUSNAME(), this);
92
93 connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
94 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
95
96 #ifdef ENABLE_UBUNTU_ACCOUNTSSERVICE
97 registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
98 registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
99 #else
100 registerProperty(IFACE_LOMIRI, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
101 registerProperty(IFACE_LOMIRI, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
102 #endif
103
104 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
105 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
106 registerProperty(IFACE_UBUNTU_SECURITY, PROP_PINCODE_PROMPT_MANAGER, QStringLiteral("pinCodePromptManagerChanged"));
107 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION, QStringLiteral("enableFingerprintIdentificationChanged"));
108 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
109 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
110 registerProperty(IFACE_UBUNTU_SECURITY, PROP_HIDE_NOTIFICATION_CONTENT_WHILE_LOCKED, QStringLiteral("hideNotificationContentWhileLocked"));
111 registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
112 registerProperty(IFACE_UBUNTU_SECURITY, PROP_PINCODE_LENGTH, QStringLiteral("pincodeLengthChanged"));
113 registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
114 registerProperty(IFACE_LOMIRI, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
115 registerProperty(IFACE_LOMIRI, PROP_DEMO_EDGES_COMPLETED, QStringLiteral("demoEdgesCompletedChanged"));
116 registerProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, QStringLiteral("failedFingerprintLoginsChanged"));
117 registerProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
118
119 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
120 m_syscompInput, QStringLiteral("setMouseCursorSpeed"));
121 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
122 m_syscompInput, QStringLiteral("setMouseDoubleClickSpeed"));
123 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
124 m_syscompInput, QStringLiteral("setMousePrimaryButton"),
125 primaryButtonConverter);
126 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
127 m_syscompInput, QStringLiteral("setMouseScrollSpeed"));
128 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
129 m_syscompInput, QStringLiteral("setTouchpadCursorSpeed"));
130 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
131 m_syscompInput, QStringLiteral("setTouchpadScrollSpeed"));
132 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
133 m_syscompInput, QStringLiteral("setTouchpadDisableWhileTyping"));
134 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
135 m_syscompInput, QStringLiteral("setTouchpadDisableWithMouse"));
136 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
137 m_syscompInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
138 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
139 m_syscompInput, QStringLiteral("setTouchpadPrimaryButton"),
140 primaryButtonConverter);
141 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
142 m_syscompInput, QStringLiteral("setTouchpadTapToClick"));
143 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
144 m_syscompInput, QStringLiteral("setTouchpadTwoFingerScroll"));
145
146 setUser(!user.isEmpty() ? user : QString::fromUtf8(g_get_user_name()));
147}
148
149QString AccountsService::user() const
150{
151 return m_user;
152}
153
154void AccountsService::setUser(const QString &user)
155{
156 if (user.isEmpty() || m_user == user)
157 return;
158
159 bool wasEmpty = m_user.isEmpty();
160
161 m_user = user;
162 Q_EMIT userChanged();
163
164 // Do the first update synchronously, as a cheap way to block rendering
165 // until we have the right values on bootup.
166 refresh(!wasEmpty);
167}
168
169bool AccountsService::demoEdges() const
170{
171 auto value = getProperty(IFACE_LOMIRI, PROP_DEMO_EDGES);
172 return value.toBool();
173}
174
175void AccountsService::setDemoEdges(bool demoEdges)
176{
177 setProperty(IFACE_LOMIRI, PROP_DEMO_EDGES, demoEdges);
178}
179
180QStringList AccountsService::demoEdgesCompleted() const
181{
182 auto value = getProperty(IFACE_LOMIRI, PROP_DEMO_EDGES_COMPLETED);
183 return value.toStringList();
184}
185
186void AccountsService::markDemoEdgeCompleted(const QString &edge)
187{
188 auto currentList = demoEdgesCompleted();
189 if (!currentList.contains(edge)) {
190 setProperty(IFACE_LOMIRI, PROP_DEMO_EDGES_COMPLETED, currentList << edge);
191 }
192}
193
194bool AccountsService::enableFingerprintIdentification() const
195{
196 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION);
197 return value.toBool();
198}
199
200bool AccountsService::enableLauncherWhileLocked() const
201{
202 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
203 return value.toBool();
204}
205
206bool AccountsService::enableIndicatorsWhileLocked() const
207{
208 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
209 return value.toBool();
210}
211
212bool AccountsService::hideNotificationContentWhileLocked() const
213{
214 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_HIDE_NOTIFICATION_CONTENT_WHILE_LOCKED);
215 return value.toBool();
216}
217
218QString AccountsService::backgroundFile() const
219{
220 #ifdef ENABLE_UBUNTU_ACCOUNTSSERVICE
221 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
222 #else
223 auto value = getProperty(IFACE_LOMIRI, PROP_BACKGROUND_FILE);
224 #endif
225
226 return value.toString();
227}
228
229bool AccountsService::statsWelcomeScreen() const
230{
231 auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
232 return value.toBool();
233}
234
235AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
236{
237 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
238 return (PasswordDisplayHint)value.toInt();
239}
240
241QString AccountsService::pinCodePromptManager() const
242{
243
244 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PINCODE_PROMPT_MANAGER);
245 if (!value.isValid()) {
246 return m_defaultPinPromptManager;
247 } else {
248 QString file = value.toString() + ".qml";
249 if (file == m_defaultPinPromptManager) {
250 return m_defaultPinPromptManager;
251 } else if (!QFile::exists(qmlDirectory() + "/Greeter/" + file)) {
252 qWarning() << "failed to load pinCodePromptManager " << file << ", fallback to " << m_defaultPinPromptManager;
253 return m_defaultPinPromptManager;
254 } else {
255 return file;
256 }
257 }
258}
259
260QString AccountsService::defaultPinCodePromptManager() const
261{
262 return m_defaultPinPromptManager;
263}
264
265uint AccountsService::pincodeLength() const
266{
267 return getProperty(IFACE_UBUNTU_SECURITY, PROP_PINCODE_LENGTH).toUInt();
268}
269
270QString AccountsService::realName() const
271{
272 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
273 return value.toString();
274}
275
276void AccountsService::setRealName(const QString &realName)
277{
278 setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
279}
280
281QString AccountsService::email() const
282{
283 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
284 return value.toString();
285}
286
287void AccountsService::setEmail(const QString &email)
288{
289 setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
290}
291
292QStringList AccountsService::keymaps() const
293{
294 #ifdef ENABLE_UBUNTU_ACCOUNTSSERVICE
295 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES);
296 #else
297 auto value = getProperty(IFACE_LOMIRI, PROP_INPUT_SOURCES);
298 #endif
299
300 QDBusArgument arg = value.value<QDBusArgument>();
301 StringMapList maps = qdbus_cast<StringMapList>(arg);
302 QStringList simplifiedMaps;
303
304 Q_FOREACH(const StringMap &map, maps) {
305 Q_FOREACH(const QString &entry, map) {
306 simplifiedMaps.append(entry);
307 }
308 }
309
310 if (!simplifiedMaps.isEmpty()) {
311 return simplifiedMaps;
312 }
313
314 return {QStringLiteral("us")};
315}
316
317void AccountsService::setKeymaps(const QStringList &keymaps)
318{
319 if (keymaps.isEmpty()) {
320 qWarning() << "Setting empty keymaps is not supported";
321 return;
322 }
323
324 StringMapList result;
325 Q_FOREACH(const QString &keymap, keymaps) {
326 StringMap map;
327 map.insert(QStringLiteral("xkb"), keymap);
328 result.append(map);
329 }
330
331 #ifdef ENABLE_UBUNTU_ACCOUNTSSERVICE
332 setProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QVariant::fromValue(result));
333 #else
334 setProperty(IFACE_LOMIRI, PROP_INPUT_SOURCES, QVariant::fromValue(result));
335 #endif
336
337 Q_EMIT keymapsChanged();
338}
339
340uint AccountsService::failedFingerprintLogins() const
341{
342 return getProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS).toUInt();
343}
344
345void AccountsService::setFailedFingerprintLogins(uint failedFingerprintLogins)
346{
347 setProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, failedFingerprintLogins);
348}
349
350uint AccountsService::failedLogins() const
351{
352 return getProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_LOGINS).toUInt();
353}
354
355void AccountsService::setFailedLogins(uint failedLogins)
356{
357 setProperty(IFACE_LOMIRI_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
358}
359
360// ====================================================
361// Everything below this line is generic helper methods
362// ====================================================
363
364void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
365{
366 QString signalName = m_properties[interface][property].signal;
367 QMetaObject::invokeMethod(this, signalName.toUtf8().data());
368}
369
370QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
371{
372 return m_properties[interface][property].value;
373}
374
375void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
376{
377 if (m_properties[interface][property].value != value) {
378 m_properties[interface][property].value = value;
379 m_service->setUserPropertyAsync(m_user, interface, property, value);
380 emitChangedForProperty(interface, property);
381 }
382}
383
384void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
385{
386 PropertyInfo &info = m_properties[interface][property];
387
388 if (info.proxyInterface) {
389 QVariant finalValue;
390 if (info.proxyConverter) {
391 finalValue = info.proxyConverter(value);
392 } else {
393 finalValue = value;
394 }
395 info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
396 return; // don't bother saving a copy
397 }
398
399 if (info.value != value) {
400 info.value = value;
401 emitChangedForProperty(interface, property);
402 }
403}
404
405void AccountsService::updateProperty(const QString &interface, const QString &property)
406{
407 QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
408 interface,
409 property);
410 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
411
412 connect(watcher, &QDBusPendingCallWatcher::finished,
413 this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
414
415 QDBusPendingReply<QVariant> reply = *watcher;
416 watcher->deleteLater();
417 if (reply.isError()) {
418 qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
419 return;
420 }
421
422 updateCache(interface, property, reply.value());
423 });
424}
425
426void AccountsService::updateAllProperties(const QString &interface, bool async)
427{
428 QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
429 interface);
430 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
431
432 connect(watcher, &QDBusPendingCallWatcher::finished,
433 this, [this, interface](QDBusPendingCallWatcher* watcher) {
434
435 QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
436 watcher->deleteLater();
437 if (reply.isError()) {
438 qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
439 return;
440 }
441
442 auto valueHash = reply.value();
443 auto i = valueHash.constBegin();
444 while (i != valueHash.constEnd()) {
445 updateCache(interface, i.key(), i.value());
446 ++i;
447 }
448 });
449 if (!async) {
450 watcher->waitForFinished();
451 }
452}
453
454void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
455{
456 registerProperty(interface, property, nullptr);
457
458 m_properties[interface][property].proxyInterface = iface;
459 m_properties[interface][property].proxyMethod = method;
460 m_properties[interface][property].proxyConverter = converter;
461}
462
463void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
464{
465 m_properties[interface][property] = PropertyInfo();
466 m_properties[interface][property].signal = signal;
467}
468
469void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
470{
471 if (m_user != user) {
472 return;
473 }
474
475 auto propHash = m_properties.value(interface);
476 auto i = propHash.constBegin();
477 while (i != propHash.constEnd()) {
478 if (changed.contains(i.key())) {
479 updateProperty(interface, i.key());
480 }
481 ++i;
482 }
483}
484
485void AccountsService::onMaybeChanged(const QString &user)
486{
487 if (m_user != user) {
488 return;
489 }
490
491 // Any of the standard properties might have changed!
492 auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
493 auto i = propHash.constBegin();
494 while (i != propHash.constEnd()) {
495 updateProperty(IFACE_ACCOUNTS_USER, i.key());
496 ++i;
497 }
498}
499
500void AccountsService::refresh(bool async)
501{
502 auto i = m_properties.constBegin();
503 while (i != m_properties.constEnd()) {
504 updateAllProperties(i.key(), async);
505 ++i;
506 }
507}