Unity 8
AccountsServiceDBusAdaptor.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 "AccountsServiceDBusAdaptor.h"
18 #include <QDBusConnection>
19 #include <QDBusConnectionInterface>
20 #include <QDBusMessage>
21 #include <QDBusVariant>
22 #include <QDebug>
23 
24 AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
25  : QObject(parent),
26  m_accountsManager(nullptr),
27  m_ignoreNextChanged(false)
28 {
29  QDBusConnection connection = QDBusConnection::SM_BUSNAME();
30  QDBusConnectionInterface *interface = connection.interface();
31  interface->startService(QStringLiteral("org.freedesktop.Accounts"));
32  m_accountsManager = new QDBusInterface(QStringLiteral("org.freedesktop.Accounts"),
33  QStringLiteral("/org/freedesktop/Accounts"),
34  QStringLiteral("org.freedesktop.Accounts"),
35  connection, this);
36 }
37 
38 QDBusPendingReply<QVariantMap> AccountsServiceDBusAdaptor::getAllPropertiesAsync(const QString &user, const QString &interface)
39 {
40  QDBusInterface *iface = getUserInterface(user);
41  if (iface != nullptr && iface->isValid()) {
42  return iface->asyncCall(QStringLiteral("GetAll"), interface);
43  }
44  return QDBusPendingReply<QVariantMap>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
45 }
46 
47 QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
48 {
49  QDBusInterface *iface = getUserInterface(user);
50  if (iface != nullptr && iface->isValid()) {
51  return iface->asyncCall(QStringLiteral("Get"), interface, property);
52  }
53  return QDBusPendingReply<QVariant>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
54 }
55 
56 QDBusPendingCall AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
57 {
58  QDBusInterface *iface = getUserInterface(user);
59  if (iface != nullptr && iface->isValid()) {
60  if (interface == QStringLiteral("org.freedesktop.Accounts.User")) {
61  // Standard AccountsService properties use special set methods.
62  // It will not let you use the usual DBus property setters.
63  QDBusInterface accountsIface(iface->service(),
64  iface->path(),
65  interface,
66  iface->connection());
67  return accountsIface.asyncCall(QStringLiteral("Set") + property, value);
68  } else {
69  // The value needs to be carefully wrapped
70  return iface->asyncCall(QStringLiteral("Set"), interface, property, QVariant::fromValue(QDBusVariant(value)));
71  }
72  }
73  return QDBusPendingCall::fromCompletedCall(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
74 }
75 
76 void AccountsServiceDBusAdaptor::propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
77 {
78  // Merge changed and invalidated together
79  QStringList combined;
80  combined << invalid;
81  combined << changed.keys();
82  combined.removeDuplicates();
83 
84  Q_EMIT propertiesChanged(getUserForPath(message().path()), interface, combined);
85 
86  // In case a non-builtin property changes, we're getting propertiesChanged *and* changed
87  // As the generic changed requires asking back over DBus, it's quite slow to process.
88  // We don't want to trigger that when we know it's not a built-in property change.
89  m_ignoreNextChanged = true;
90 }
91 
92 void AccountsServiceDBusAdaptor::maybeChangedSlot()
93 {
94  if (!m_ignoreNextChanged) {
95  Q_EMIT maybeChanged(getUserForPath(message().path()));
96  }
97  m_ignoreNextChanged = false;
98 }
99 
100 QString AccountsServiceDBusAdaptor::getUserForPath(const QString &path) const
101 {
102  QMap<QString, QDBusInterface *>::const_iterator i;
103  for (i = m_users.constBegin(); i != m_users.constEnd(); ++i) {
104  if (i.value()->path() == path) {
105  return i.key();
106  }
107  }
108  return QString();
109 }
110 
111 QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user)
112 {
113  QDBusInterface *iface = m_users.value(user);
114  if (iface == nullptr && m_accountsManager->isValid()) {
115  QDBusReply<QDBusObjectPath> answer = m_accountsManager->call(QStringLiteral("FindUserByName"), user);
116  if (answer.isValid()) {
117  const QString path = answer.value().path();
118 
119  iface = new QDBusInterface(QStringLiteral("org.freedesktop.Accounts"),
120  path,
121  QStringLiteral("org.freedesktop.DBus.Properties"),
122  m_accountsManager->connection(), this);
123 
124  // With its own pre-defined properties, AccountsService is oddly
125  // close-lipped. It won't send out proper DBus.Properties notices,
126  // but it does have one catch-all Changed() signal. So let's
127  // listen to that.
128  iface->connection().connect(
129  iface->service(),
130  path,
131  QStringLiteral("org.freedesktop.Accounts.User"),
132  QStringLiteral("Changed"),
133  this,
134  SLOT(maybeChangedSlot()));
135 
136  // But custom properties do send out the right notifications, so
137  // let's still listen there.
138  iface->connection().connect(
139  iface->service(),
140  path,
141  QStringLiteral("org.freedesktop.DBus.Properties"),
142  QStringLiteral("PropertiesChanged"),
143  this,
144  SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
145 
146  m_users.insert(user, iface);
147  } else {
148  qWarning() << "Couldn't get user interface" << answer.error().name() << answer.error().message();
149  }
150  }
151  return iface;
152 }