Unity 8
AccountsService.cpp
1 /*
2  * Copyright (C) 2013, 2015 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 #define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
26 #define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService")
27 #define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
28 #define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
29 #define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
30 #define IFACE_UNITY QStringLiteral("com.canonical.unity.AccountsService")
31 #define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
32 
33 #define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
34 #define PROP_DEMO_EDGES QStringLiteral("demo-edges")
35 #define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted")
36 #define PROP_EMAIL QStringLiteral("Email")
37 #define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
38 #define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
39 #define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
40 #define PROP_INPUT_SOURCES QStringLiteral("InputSources")
41 #define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
42 #define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
43 #define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
44 #define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
45 #define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
46 #define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
47 #define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
48 #define PROP_REAL_NAME QStringLiteral("RealName")
49 #define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
50 #define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
51 #define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
52 #define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
53 #define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
54 #define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
55 #define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
56 #define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
57 #define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
58 
59 using StringMap = QMap<QString,QString>;
60 using StringMapList = QList<StringMap>;
61 Q_DECLARE_METATYPE(StringMapList)
62 
63 
64 QVariant primaryButtonConverter(const QVariant &value)
65 {
66  QString stringValue = value.toString();
67  if (stringValue == "left") {
68  return QVariant::fromValue(0);
69  } else if (stringValue == "right") {
70  return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
71  } else {
72  return QVariant::fromValue(0); // default to left
73  }
74 }
75 
76 AccountsService::AccountsService(QObject* parent, const QString &user)
77  : QObject(parent)
78  , m_service(new AccountsServiceDBusAdaptor(this))
79 {
80  m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
81  QStringLiteral("/com/canonical/Unity/Input"),
82  QStringLiteral("com.canonical.Unity.Input"),
83  QDBusConnection::SM_BUSNAME(), this);
84 
85  connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
86  connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
87 
88  registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
89  registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
90  registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
91  registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
92  registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
93  registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
94  registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
95  registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
96  registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
97  registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
98  registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
99  registerProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, QStringLiteral("demoEdgesCompletedChanged"));
100  registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
101 
102  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
103  m_unityInput, QStringLiteral("setMouseCursorSpeed"));
104  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
105  m_unityInput, QStringLiteral("setMouseDoubleClickSpeed"));
106  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
107  m_unityInput, QStringLiteral("setMousePrimaryButton"),
108  primaryButtonConverter);
109  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
110  m_unityInput, QStringLiteral("setMouseScrollSpeed"));
111  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
112  m_unityInput, QStringLiteral("setTouchpadCursorSpeed"));
113  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
114  m_unityInput, QStringLiteral("setTouchpadScrollSpeed"));
115  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
116  m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping"));
117  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
118  m_unityInput, QStringLiteral("setTouchpadDisableWithMouse"));
119  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
120  m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
121  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
122  m_unityInput, QStringLiteral("setTouchpadPrimaryButton"),
123  primaryButtonConverter);
124  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
125  m_unityInput, QStringLiteral("setTouchpadTapToClick"));
126  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
127  m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll"));
128 
129  setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
130 }
131 
132 QString AccountsService::user() const
133 {
134  return m_user;
135 }
136 
137 void AccountsService::setUser(const QString &user)
138 {
139  if (user.isEmpty() || m_user == user)
140  return;
141 
142  bool wasEmpty = m_user.isEmpty();
143 
144  m_user = user;
145  Q_EMIT userChanged();
146 
147  // Do the first update synchronously, as a cheap way to block rendering
148  // until we have the right values on bootup.
149  refresh(!wasEmpty);
150 }
151 
152 bool AccountsService::demoEdges() const
153 {
154  auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
155  return value.toBool();
156 }
157 
158 void AccountsService::setDemoEdges(bool demoEdges)
159 {
160  setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
161 }
162 
163 QStringList AccountsService::demoEdgesCompleted() const
164 {
165  auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED);
166  return value.toStringList();
167 }
168 
169 void AccountsService::markDemoEdgeCompleted(const QString &edge)
170 {
171  auto currentList = demoEdgesCompleted();
172  if (!currentList.contains(edge)) {
173  setProperty(IFACE_UNITY, PROP_DEMO_EDGES_COMPLETED, currentList << edge);
174  }
175 }
176 
177 bool AccountsService::enableLauncherWhileLocked() const
178 {
179  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
180  return value.toBool();
181 }
182 
183 bool AccountsService::enableIndicatorsWhileLocked() const
184 {
185  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
186  return value.toBool();
187 }
188 
189 QString AccountsService::backgroundFile() const
190 {
191  auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
192  return value.toString();
193 }
194 
195 bool AccountsService::statsWelcomeScreen() const
196 {
197  auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
198  return value.toBool();
199 }
200 
201 AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
202 {
203  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
204  return (PasswordDisplayHint)value.toInt();
205 }
206 
207 bool AccountsService::hereEnabled() const
208 {
209  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
210  return value.toBool();
211 }
212 
213 void AccountsService::setHereEnabled(bool enabled)
214 {
215  setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
216 }
217 
218 QString AccountsService::hereLicensePath() const
219 {
220  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
221  QString hereLicensePath = value.toString();
222  if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
223  hereLicensePath = QStringLiteral("");
224  return hereLicensePath;
225 }
226 
227 bool AccountsService::hereLicensePathValid() const
228 {
229  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
230  return !value.toString().isNull();
231 }
232 
233 QString AccountsService::realName() const
234 {
235  auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
236  return value.toString();
237 }
238 
239 void AccountsService::setRealName(const QString &realName)
240 {
241  setProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, realName);
242 }
243 
244 QString AccountsService::email() const
245 {
246  auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL);
247  return value.toString();
248 }
249 
250 void AccountsService::setEmail(const QString &email)
251 {
252  setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
253 }
254 
255 QStringList AccountsService::keymaps() const
256 {
257  auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES);
258  QDBusArgument arg = value.value<QDBusArgument>();
259  StringMapList maps = qdbus_cast<StringMapList>(arg);
260  QStringList simplifiedMaps;
261 
262  Q_FOREACH(const StringMap &map, maps) {
263  Q_FOREACH(const QString &entry, map) {
264  simplifiedMaps.append(entry);
265  }
266  }
267 
268  if (!simplifiedMaps.isEmpty()) {
269  return simplifiedMaps;
270  }
271 
272  return {QStringLiteral("us")};
273 }
274 
275 uint AccountsService::failedLogins() const
276 {
277  return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
278 }
279 
280 void AccountsService::setFailedLogins(uint failedLogins)
281 {
282  setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
283 }
284 
285 // ====================================================
286 // Everything below this line is generic helper methods
287 // ====================================================
288 
289 void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
290 {
291  QString signalName = m_properties[interface][property].signal;
292  QMetaObject::invokeMethod(this, signalName.toUtf8().data());
293 }
294 
295 QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
296 {
297  return m_properties[interface][property].value;
298 }
299 
300 void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
301 {
302  if (m_properties[interface][property].value != value) {
303  m_properties[interface][property].value = value;
304  m_service->setUserPropertyAsync(m_user, interface, property, value);
305  emitChangedForProperty(interface, property);
306  }
307 }
308 
309 void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
310 {
311  PropertyInfo &info = m_properties[interface][property];
312 
313  if (info.proxyInterface) {
314  QVariant finalValue;
315  if (info.proxyConverter) {
316  finalValue = info.proxyConverter(value);
317  } else {
318  finalValue = value;
319  }
320  info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
321  return; // don't bother saving a copy
322  }
323 
324  if (info.value != value) {
325  info.value = value;
326  emitChangedForProperty(interface, property);
327  }
328 }
329 
330 void AccountsService::updateProperty(const QString &interface, const QString &property)
331 {
332  QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
333  interface,
334  property);
335  QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
336 
337  connect(watcher, &QDBusPendingCallWatcher::finished,
338  this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
339 
340  QDBusPendingReply<QVariant> reply = *watcher;
341  watcher->deleteLater();
342  if (reply.isError()) {
343  qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
344  return;
345  }
346 
347  updateCache(interface, property, reply.value());
348  });
349 }
350 
351 void AccountsService::updateAllProperties(const QString &interface, bool async)
352 {
353  QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
354  interface);
355  QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
356 
357  connect(watcher, &QDBusPendingCallWatcher::finished,
358  this, [this, interface](QDBusPendingCallWatcher* watcher) {
359 
360  QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
361  watcher->deleteLater();
362  if (reply.isError()) {
363  qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
364  return;
365  }
366 
367  auto valueHash = reply.value();
368  auto i = valueHash.constBegin();
369  while (i != valueHash.constEnd()) {
370  updateCache(interface, i.key(), i.value());
371  ++i;
372  }
373  });
374  if (!async) {
375  watcher->waitForFinished();
376  }
377 }
378 
379 void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
380 {
381  registerProperty(interface, property, nullptr);
382 
383  m_properties[interface][property].proxyInterface = iface;
384  m_properties[interface][property].proxyMethod = method;
385  m_properties[interface][property].proxyConverter = converter;
386 }
387 
388 void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
389 {
390  m_properties[interface][property] = PropertyInfo();
391  m_properties[interface][property].signal = signal;
392 }
393 
394 void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
395 {
396  if (m_user != user) {
397  return;
398  }
399 
400  auto propHash = m_properties.value(interface);
401  auto i = propHash.constBegin();
402  while (i != propHash.constEnd()) {
403  if (changed.contains(i.key())) {
404  updateProperty(interface, i.key());
405  }
406  ++i;
407  }
408 }
409 
410 void AccountsService::onMaybeChanged(const QString &user)
411 {
412  if (m_user != user) {
413  return;
414  }
415 
416  // Any of the standard properties might have changed!
417  auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
418  auto i = propHash.constBegin();
419  while (i != propHash.constEnd()) {
420  updateProperty(IFACE_ACCOUNTS_USER, i.key());
421  ++i;
422  }
423 }
424 
425 void AccountsService::refresh(bool async)
426 {
427  auto i = m_properties.constBegin();
428  while (i != m_properties.constEnd()) {
429  updateAllProperties(i.key(), async);
430  ++i;
431  }
432 }