on add3e60 help sign in
unity8/plugins/AccountsService/AccountsService.cpp
498 lines 20 kB history raw
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 */
17#include "AccountsService.h"
18#include "AccountsServiceDBusAdaptor.h"
20#include <QDBusInterface>
21#include <QFile>
22#include <QStringList>
23#include <QDebug>
25#include <glib.h>
27#define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
28#define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService")
29#define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
30#define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
31#define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
32#define IFACE_UNITY QStringLiteral("com.canonical.unity.AccountsService")
33#define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
35#define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
36#define PROP_DEMO_EDGES QStringLiteral("demo-edges")
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_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
42#define PROP_FAILED_FINGERPRINT_LOGINS QStringLiteral("FailedFingerprintLogins")
43#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
44#define PROP_INPUT_SOURCES QStringLiteral("InputSources")
45#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
46#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
47#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
48#define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
49#define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
50#define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
51#define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
52#define PROP_REAL_NAME QStringLiteral("RealName")
53#define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
54#define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
55#define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
56#define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
57#define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
58#define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
59#define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
60#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
61#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
63using StringMap = QMap<QString,QString>;
64using StringMapList = QList<StringMap>;
65Q_DECLARE_METATYPE(StringMapList)
68QVariant primaryButtonConverter(const QVariant &value)
70 QString stringValue = value.toString();
71 if (stringValue == QLatin1String("left")) {
72 return QVariant::fromValue(0);
73 } else if (stringValue == QLatin1String("right")) {
74 return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
75 } else {
76 return QVariant::fromValue(0); // default to left
77 }
80AccountsService::AccountsService(QObject* parent, const QString &user)
81 : QObject(parent)
82 , m_service(new AccountsServiceDBusAdaptor(this))
83 , m_greeterMode(true)
85 m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
86 QStringLiteral("/com/canonical/Unity/Input"),
87 QStringLiteral("com.canonical.Unity.Input"),
88 QDBusConnection::SM_BUSNAME(), this);
90 connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
91 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
93 registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
94 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
95 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
96 registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
97 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
98 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
99 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION, QStringLiteral("enableFingerprintIdentificationChanged"));
100 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
101 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
102 registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
103 registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
104 registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
105 registerProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, QStringLiteral("demoEdgesCompletedChanged"));
106 registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, QStringLiteral("failedFingerprintLoginsChanged"));
107 registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
109 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
110 m_unityInput, QStringLiteral("setMouseCursorSpeed"));
111 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
112 m_unityInput, QStringLiteral("setMouseDoubleClickSpeed"));
113 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
114 m_unityInput, QStringLiteral("setMousePrimaryButton"),
115 primaryButtonConverter);
116 registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
117 m_unityInput, QStringLiteral("setMouseScrollSpeed"));
118 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
119 m_unityInput, QStringLiteral("setTouchpadCursorSpeed"));
120 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
121 m_unityInput, QStringLiteral("setTouchpadScrollSpeed"));
122 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
123 m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping"));
124 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
125 m_unityInput, QStringLiteral("setTouchpadDisableWithMouse"));
126 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
127 m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
128 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
129 m_unityInput, QStringLiteral("setTouchpadPrimaryButton"),
130 primaryButtonConverter);
131 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
132 m_unityInput, QStringLiteral("setTouchpadTapToClick"));
133 registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
134 m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll"));
136 setUser(!user.isEmpty() ? user : QString::fromUtf8(g_get_user_name()));
139QString AccountsService::user() const
141 return m_user;
144void AccountsService::setUser(const QString &user)
146 if (user.isEmpty() || m_user == user)
147 return;
149 bool wasEmpty = m_user.isEmpty();
151 m_user = user;
152 Q_EMIT userChanged();
154 // Do the first update synchronously, as a cheap way to block rendering
155 // until we have the right values on bootup.
156 refresh(!wasEmpty);
159bool AccountsService::greeterMode() const
161 return m_greeterMode;
164void AccountsService::setGreeterMode(bool greeterMode)
166 if (m_greeterMode != greeterMode) {
167 m_greeterMode = greeterMode;
168 Q_EMIT greeterModeChanged();
169 }
172bool AccountsService::demoEdges() const
174 auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
175 return value.toBool();
178void AccountsService::setDemoEdges(bool demoEdges)
180 setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
183QStringList AccountsService::demoEdgesCompleted() const
185 auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED);
186 return value.toStringList();
189void AccountsService::markDemoEdgeCompleted(const QString &edge)
191 auto currentList = demoEdgesCompleted();
192 if (!currentList.contains(edge)) {
193 setProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, currentList << edge);
194 }
197bool AccountsService::enableFingerprintIdentification() const
199 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION);
200 return value.toBool();
203bool AccountsService::enableLauncherWhileLocked() const
205 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
206 return value.toBool();
209bool AccountsService::enableIndicatorsWhileLocked() const
211 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
212 return value.toBool();
215QString AccountsService::backgroundFile() const
217 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
218 return value.toString();
221bool AccountsService::statsWelcomeScreen() const
223 auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
224 return value.toBool();
227AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
229 auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
230 return (PasswordDisplayHint)value.toInt();
233bool AccountsService::hereEnabled() const
235 auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
236 return value.toBool();
239void AccountsService::setHereEnabled(bool enabled)
241 setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
244QString AccountsService::hereLicensePath() const
246 auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
247 QString hereLicensePath = value.toString();
248 if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
249 hereLicensePath = QStringLiteral("");
250 return hereLicensePath;
253bool AccountsService::hereLicensePathValid() const
255 auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
256 return !value.toString().isNull();
259QString AccountsService::realName() const
261 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
262 return value.toString();
265void AccountsService::setRealName(const QString &realName)
267 setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
270QString AccountsService::email() const
272 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
273 return value.toString();
276void AccountsService::setEmail(const QString &email)
278 setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
281QStringList AccountsService::keymaps() const
283 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES);
284 QDBusArgument arg = value.value<QDBusArgument>();
285 StringMapList maps = qdbus_cast<StringMapList>(arg);
286 QStringList simplifiedMaps;
288 Q_FOREACH(const StringMap &map, maps) {
289 Q_FOREACH(const QString &entry, map) {
290 simplifiedMaps.append(entry);
291 }
292 }
294 if (!simplifiedMaps.isEmpty()) {
295 return simplifiedMaps;
296 }
298 return {QStringLiteral("us")};
301void AccountsService::setKeymaps(const QStringList &keymaps)
303 if (keymaps.isEmpty()) {
304 qWarning() << "Setting empty keymaps is not supported";
305 return;
306 }
308 StringMapList result;
309 Q_FOREACH(const QString &keymap, keymaps) {
310 StringMap map;
311 map.insert(QStringLiteral("xkb"), keymap);
312 result.append(map);
313 }
315 setProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QVariant::fromValue(result));
316 Q_EMIT keymapsChanged();
319uint AccountsService::failedFingerprintLogins() const
321 return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS).toUInt();
324void AccountsService::setFailedFingerprintLogins(uint failedFingerprintLogins)
326 setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, failedFingerprintLogins);
329uint AccountsService::failedLogins() const
331 return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
334void AccountsService::setFailedLogins(uint failedLogins)
336 setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
339// ====================================================
340// Everything below this line is generic helper methods
341// ====================================================
343void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
345 QString signalName = m_properties[interface][property].signal;
346 QMetaObject::invokeMethod(this, signalName.toUtf8().data());
349QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
351 return m_properties[interface][property].value;
354void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
356 if (m_properties[interface][property].value != value) {
357 m_properties[interface][property].value = value;
358 if (m_greeterMode || m_user == g_get_user_name()) {
359 // Only write any changes back to AccountsService if we are running
360 // as a proper LightDM greeter (or are writing values for our own
361 // user).
362 // (A) Only the lightdm user has permission to set everyone's
363 // properties.
364 // (B) We don't want to have policykit throw up a password dialog
365 // when we are running in the user session.
366 // (C) We only write things to AS that are safe to ignore for other
367 // users anyway (like failedLogins count, which does not NEED
368 // to be persistent).
369 m_service->setUserPropertyAsync(m_user, interface, property, value);
370 }
371 emitChangedForProperty(interface, property);
372 }
375void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
377 PropertyInfo &info = m_properties[interface][property];
379 if (info.proxyInterface) {
380 QVariant finalValue;
381 if (info.proxyConverter) {
382 finalValue = info.proxyConverter(value);
383 } else {
384 finalValue = value;
385 }
386 info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
387 return; // don't bother saving a copy
388 }
390 if (info.value != value) {
391 info.value = value;
392 emitChangedForProperty(interface, property);
393 }
396void AccountsService::updateProperty(const QString &interface, const QString &property)
398 QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
399 interface,
400 property);
401 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
403 connect(watcher, &QDBusPendingCallWatcher::finished,
404 this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
406 QDBusPendingReply<QVariant> reply = *watcher;
407 watcher->deleteLater();
408 if (reply.isError()) {
409 qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
410 return;
411 }
413 updateCache(interface, property, reply.value());
414 });
417void AccountsService::updateAllProperties(const QString &interface, bool async)
419 QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
420 interface);
421 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
423 connect(watcher, &QDBusPendingCallWatcher::finished,
424 this, [this, interface](QDBusPendingCallWatcher* watcher) {
426 QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
427 watcher->deleteLater();
428 if (reply.isError()) {
429 qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
430 return;
431 }
433 auto valueHash = reply.value();
434 auto i = valueHash.constBegin();
435 while (i != valueHash.constEnd()) {
436 updateCache(interface, i.key(), i.value());
437 ++i;
438 }
439 });
440 if (!async) {
441 watcher->waitForFinished();
442 }
445void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
447 registerProperty(interface, property, nullptr);
449 m_properties[interface][property].proxyInterface = iface;
450 m_properties[interface][property].proxyMethod = method;
451 m_properties[interface][property].proxyConverter = converter;
454void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
456 m_properties[interface][property] = PropertyInfo();
457 m_properties[interface][property].signal = signal;
460void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
462 if (m_user != user) {
463 return;
464 }
466 auto propHash = m_properties.value(interface);
467 auto i = propHash.constBegin();
468 while (i != propHash.constEnd()) {
469 if (changed.contains(i.key())) {
470 updateProperty(interface, i.key());
471 }
472 ++i;
473 }
476void AccountsService::onMaybeChanged(const QString &user)
478 if (m_user != user) {
479 return;
480 }
482 // Any of the standard properties might have changed!
483 auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
484 auto i = propHash.constBegin();
485 while (i != propHash.constEnd()) {
486 updateProperty(IFACE_ACCOUNTS_USER, i.key());
487 ++i;
488 }
491void AccountsService::refresh(bool async)
493 auto i = m_properties.constBegin();
494 while (i != m_properties.constEnd()) {
495 updateAllProperties(i.key(), async);
496 ++i;
497 }