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