/* * Copyright (C) 2013-2016 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "AccountsService.h" #include "AccountsServiceDBusAdaptor.h" #include #include #include #include #include #define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User") #define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService") #define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input") #define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy") #define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy") #define IFACE_UNITY QStringLiteral("com.canonical.unity.AccountsService") #define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private") #define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile") #define PROP_DEMO_EDGES QStringLiteral("demo-edges") #define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted") #define PROP_EMAIL QStringLiteral("Email") #define PROP_ENABLE_FINGERPRINT_IDENTIFICATION QStringLiteral("EnableFingerprintIdentification") #define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked") #define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked") #define PROP_FAILED_FINGERPRINT_LOGINS QStringLiteral("FailedFingerprintLogins") #define PROP_FAILED_LOGINS QStringLiteral("FailedLogins") #define PROP_INPUT_SOURCES QStringLiteral("InputSources") #define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted") #define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath") #define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed") #define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed") #define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton") #define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed") #define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint") #define PROP_REAL_NAME QStringLiteral("RealName") #define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen") #define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed") #define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping") #define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse") #define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed") #define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton") #define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed") #define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick") #define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll") using StringMap = QMap; using StringMapList = QList; Q_DECLARE_METATYPE(StringMapList) QVariant primaryButtonConverter(const QVariant &value) { QString stringValue = value.toString(); if (stringValue == QLatin1String("left")) { return QVariant::fromValue(0); } else if (stringValue == QLatin1String("right")) { return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same } else { return QVariant::fromValue(0); // default to left } } AccountsService::AccountsService(QObject* parent, const QString &user) : QObject(parent) , m_service(new AccountsServiceDBusAdaptor(this)) { m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"), QStringLiteral("/com/canonical/Unity/Input"), QStringLiteral("com.canonical.Unity.Input"), QDBusConnection::SM_BUSNAME(), this); connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged); connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged); registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged")); registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged")); registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged")); registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged")); registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged")); registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged")); registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION, QStringLiteral("enableFingerprintIdentificationChanged")); registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged")); registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged")); registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged")); registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged")); registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged")); registerProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, QStringLiteral("demoEdgesCompletedChanged")); registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, QStringLiteral("failedFingerprintLoginsChanged")); registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged")); registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED, m_unityInput, QStringLiteral("setMouseCursorSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED, m_unityInput, QStringLiteral("setMouseDoubleClickSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON, m_unityInput, QStringLiteral("setMousePrimaryButton"), primaryButtonConverter); registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED, m_unityInput, QStringLiteral("setMouseScrollSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED, m_unityInput, QStringLiteral("setTouchpadCursorSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED, m_unityInput, QStringLiteral("setTouchpadScrollSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING, m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE, m_unityInput, QStringLiteral("setTouchpadDisableWithMouse")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED, m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON, m_unityInput, QStringLiteral("setTouchpadPrimaryButton"), primaryButtonConverter); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK, m_unityInput, QStringLiteral("setTouchpadTapToClick")); registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL, m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll")); setUser(!user.isEmpty() ? user : QString::fromUtf8(g_get_user_name())); } QString AccountsService::user() const { return m_user; } void AccountsService::setUser(const QString &user) { if (user.isEmpty() || m_user == user) return; bool wasEmpty = m_user.isEmpty(); m_user = user; Q_EMIT userChanged(); // Do the first update synchronously, as a cheap way to block rendering // until we have the right values on bootup. refresh(!wasEmpty); } bool AccountsService::demoEdges() const { auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES); return value.toBool(); } void AccountsService::setDemoEdges(bool demoEdges) { setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges); } QStringList AccountsService::demoEdgesCompleted() const { auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED); return value.toStringList(); } void AccountsService::markDemoEdgeCompleted(const QString &edge) { auto currentList = demoEdgesCompleted(); if (!currentList.contains(edge)) { setProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, currentList << edge); } } bool AccountsService::enableFingerprintIdentification() const { auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION); return value.toBool(); } bool AccountsService::enableLauncherWhileLocked() const { auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED); return value.toBool(); } bool AccountsService::enableIndicatorsWhileLocked() const { auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED); return value.toBool(); } QString AccountsService::backgroundFile() const { auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE); return value.toString(); } bool AccountsService::statsWelcomeScreen() const { auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN); return value.toBool(); } AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const { auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT); return (PasswordDisplayHint)value.toInt(); } bool AccountsService::hereEnabled() const { auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED); return value.toBool(); } void AccountsService::setHereEnabled(bool enabled) { setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled); } QString AccountsService::hereLicensePath() const { auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH); QString hereLicensePath = value.toString(); if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath)) hereLicensePath = QStringLiteral(""); return hereLicensePath; } bool AccountsService::hereLicensePathValid() const { auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH); return !value.toString().isNull(); } QString AccountsService::realName() const { auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME); return value.toString(); } void AccountsService::setRealName(const QString &realName) { setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName); } QString AccountsService::email() const { auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL); return value.toString(); } void AccountsService::setEmail(const QString &email) { setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email); } QStringList AccountsService::keymaps() const { auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES); QDBusArgument arg = value.value(); StringMapList maps = qdbus_cast(arg); QStringList simplifiedMaps; Q_FOREACH(const StringMap &map, maps) { Q_FOREACH(const QString &entry, map) { simplifiedMaps.append(entry); } } if (!simplifiedMaps.isEmpty()) { return simplifiedMaps; } return {QStringLiteral("us")}; } void AccountsService::setKeymaps(const QStringList &keymaps) { if (keymaps.isEmpty()) { qWarning() << "Setting empty keymaps is not supported"; return; } StringMapList result; Q_FOREACH(const QString &keymap, keymaps) { StringMap map; map.insert(QStringLiteral("xkb"), keymap); result.append(map); } setProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QVariant::fromValue(result)); Q_EMIT keymapsChanged(); } uint AccountsService::failedFingerprintLogins() const { return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS).toUInt(); } void AccountsService::setFailedFingerprintLogins(uint failedFingerprintLogins) { setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS, failedFingerprintLogins); } uint AccountsService::failedLogins() const { return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt(); } void AccountsService::setFailedLogins(uint failedLogins) { setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins); } // ==================================================== // Everything below this line is generic helper methods // ==================================================== void AccountsService::emitChangedForProperty(const QString &interface, const QString &property) { QString signalName = m_properties[interface][property].signal; QMetaObject::invokeMethod(this, signalName.toUtf8().data()); } QVariant AccountsService::getProperty(const QString &interface, const QString &property) const { return m_properties[interface][property].value; } void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value) { if (m_properties[interface][property].value != value) { m_properties[interface][property].value = value; m_service->setUserPropertyAsync(m_user, interface, property, value); emitChangedForProperty(interface, property); } } void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value) { PropertyInfo &info = m_properties[interface][property]; if (info.proxyInterface) { QVariant finalValue; if (info.proxyConverter) { finalValue = info.proxyConverter(value); } else { finalValue = value; } info.proxyInterface->asyncCall(info.proxyMethod, finalValue); return; // don't bother saving a copy } if (info.value != value) { info.value = value; emitChangedForProperty(interface, property); } } void AccountsService::updateProperty(const QString &interface, const QString &property) { QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user, interface, property); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, interface, property](QDBusPendingCallWatcher* watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qWarning() << "Failed to get '" << property << "' property:" << reply.error().message(); return; } updateCache(interface, property, reply.value()); }); } void AccountsService::updateAllProperties(const QString &interface, bool async) { QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user, interface); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, interface](QDBusPendingCallWatcher* watcher) { QDBusPendingReply< QHash > reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message(); return; } auto valueHash = reply.value(); auto i = valueHash.constBegin(); while (i != valueHash.constEnd()) { updateCache(interface, i.key(), i.value()); ++i; } }); if (!async) { watcher->waitForFinished(); } } void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter) { registerProperty(interface, property, nullptr); m_properties[interface][property].proxyInterface = iface; m_properties[interface][property].proxyMethod = method; m_properties[interface][property].proxyConverter = converter; } void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal) { m_properties[interface][property] = PropertyInfo(); m_properties[interface][property].signal = signal; } void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed) { if (m_user != user) { return; } auto propHash = m_properties.value(interface); auto i = propHash.constBegin(); while (i != propHash.constEnd()) { if (changed.contains(i.key())) { updateProperty(interface, i.key()); } ++i; } } void AccountsService::onMaybeChanged(const QString &user) { if (m_user != user) { return; } // Any of the standard properties might have changed! auto propHash = m_properties.value(IFACE_ACCOUNTS_USER); auto i = propHash.constBegin(); while (i != propHash.constEnd()) { updateProperty(IFACE_ACCOUNTS_USER, i.key()); ++i; } } void AccountsService::refresh(bool async) { auto i = m_properties.constBegin(); while (i != m_properties.constEnd()) { updateAllProperties(i.key(), async); ++i; } }