Commit 7f198af5 authored by Tomi Leppänen's avatar Tomi Leppänen

Add users manipulation capabilites

Add createUser method that uses user-managerd to create a new user.
Similarly add setData and removeUser to change user information and
remove users. Use the manager also to watch for changes in users.
Add signals for error cases.
parent f2a55583
......@@ -12,6 +12,7 @@ Requires: connman
Requires: mce >= 1.83.0
Requires: libsailfishkeyprovider >= 0.0.14
Requires: connman-qt5 >= 1.2.21
Requires: user-managerd
Requires(post): coreutils
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5SystemInfo)
......
......@@ -31,21 +31,60 @@
#include "usermodel.h"
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMetaType>
#include <QDBusPendingCall>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
#include <QDebug>
#include <functional>
#include <grp.h>
#include <sys/types.h>
// Currently some copy-paste from user-managerd since it doesn't have devel subpackage
const auto UserManagerService = QStringLiteral("org.sailfishos.usermanager");
const auto UserManagerPath = QStringLiteral("/");
struct SailfishUser {
QString user;
QString name;
uint uid;
};
inline QDBusArgument &operator<<(QDBusArgument &argument, const SailfishUser &user)
{
argument.beginStructure();
argument << user.user << user.name << user.uid;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, SailfishUser &user)
{
argument.beginStructure();
argument >> user.user >> user.name >> user.uid;
argument.endStructure();
return argument;
}
Q_DECLARE_METATYPE(SailfishUser)
UserModel::UserModel(QObject *parent)
: QAbstractListModel(parent)
, m_currentUser(-1)
, m_dBusInterface(nullptr)
, m_dBusWatcher(new QDBusServiceWatcher(UserManagerService, QDBusConnection::systemBus(),
QDBusServiceWatcher::WatchForRegistration, this))
{
qDBusRegisterMetaType<SailfishUser>();
connect(m_dBusWatcher, &QDBusServiceWatcher::serviceRegistered,
this, &UserModel::connectSignals);
struct group *grp = getgrnam("users");
for (int i = 0; grp->gr_mem[i] != nullptr; ++i) {
UserInfo user(QString(grp->gr_mem[i]));
if (user.isValid()) // Skip invalid users here
m_users.append(user);
// Store index of current user for later use
if (user.current())
m_currentUser = m_users.count()-1;
}
// grp must not be free'd
}
......@@ -54,18 +93,17 @@ UserModel::~UserModel()
{
}
const QHash<int, QByteArray> UserModel::s_roles = {
{ Qt::DisplayRole, "displayName" },
{ UsernameRole, "username" },
{ NameRole, "name" },
{ TypeRole, "type" },
{ UidRole, "uid" },
{ CurrentRole, "current" },
};
QHash<int, QByteArray> UserModel::roleNames() const
{
return s_roles;
static const QHash<int, QByteArray> roles = {
{ Qt::DisplayRole, "displayName" },
{ UsernameRole, "username" },
{ NameRole, "name" },
{ TypeRole, "type" },
{ UidRole, "uid" },
{ CurrentRole, "current" },
};
return roles;
}
int UserModel::rowCount(const QModelIndex &parent) const
......@@ -98,10 +136,163 @@ QVariant UserModel::data(const QModelIndex &index, int role) const
}
}
bool UserModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= m_users.count() || index.column() != 0)
return false;
UserInfo &user = m_users[index.row()];
switch (role) {
case NameRole: {
QString name = value.toString();
if (!name.isEmpty()) {
user.setName(name);
createInterface();
auto username = user.username();
auto call = m_dBusInterface->asyncCall(QStringLiteral("modifyUser"), username, username, name);
auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished,
this, std::bind(&UserModel::userModifyFinished, this, std::placeholders::_1, index.row()));
emit dataChanged(index, index, QVector<int>() << role);
return true;
} else {
return false;
}
}
case Qt::DisplayRole:
case UsernameRole:
case TypeRole:
case UidRole:
case CurrentRole:
default:
return false;
}
}
QModelIndex UserModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
if (row < 0 || row >= m_users.count() || column != 0)
return QModelIndex();
// create index
return createIndex(row, 0, row);
}
void UserModel::createUser(const QString &name)
{
createInterface();
auto username = QLocale().toLower(name.simplified()).remove(" ");
auto call = m_dBusInterface->asyncCall(QStringLiteral("addUser"), username, name);
auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, &UserModel::userAddFinished);
}
void UserModel::removeUser(int row)
{
createInterface();
QString username(m_users.at(row).username());
auto call = m_dBusInterface->asyncCall(QStringLiteral("removeUser"), username);
auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished,
this, std::bind(&UserModel::userModifyFinished, this, std::placeholders::_1, row));
}
UserInfo * UserModel::getCurrentUser() const
{
if (m_currentUser < 0 || m_currentUser >= m_users.count())
return nullptr;
return new UserInfo();
}
void UserModel::userAdded(const SailfishUser &user)
{
UserInfo newUser(user.uid);
if (newUser.isValid()) {
beginInsertRows(QModelIndex(), m_users.count(), m_users.count());
m_users.append(newUser);
endInsertRows();
}
}
void UserModel::userModified(const QString &oldUsername, const QString &newUsername, const QString &newName)
{
for (auto iter = m_users.begin(); iter != m_users.end(); ++iter) {
if (iter->username() == oldUsername) {
QVector<int> changed;
auto idx = index(iter-m_users.begin(), 0);
if (iter->username() != newUsername) {
iter->setUsername(newUsername);
changed << UsernameRole;
}
if (iter->name() != newName) {
iter->setName(newName);
changed << NameRole;
}
if (!changed.empty())
dataChanged(idx, idx, changed);
break;
}
}
}
void UserModel::userRemoved(const QString &username)
{
for (auto iter = m_users.begin(); iter != m_users.end(); ++iter) {
if (iter->username() == username) {
auto index = iter-m_users.begin();
beginRemoveRows(QModelIndex(), index, index);
m_users.erase(iter);
endRemoveRows();
break;
}
}
}
void UserModel::userAddFinished(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<void> reply = *call;
if (reply.isError()) {
emit userAddFailed();
qWarning() << "Adding user with usermanager failed:" << reply.error();
} // else awesome! (waiting for signal to alter data)
call->deleteLater();
}
void UserModel::userModifyFinished(QDBusPendingCallWatcher *call, int row)
{
QDBusPendingReply<void> reply = *call;
if (reply.isError()) {
emit userModifyFailed(row, m_users.at(row).name());
qWarning() << "Modifying user with usermanager failed:" << reply.error();
m_users[row].reset();
emit dataChanged(index(row, 0), index(row, 0), QVector<int>());
} // else awesome! (data was changed already)
call->deleteLater();
}
return new UserInfo(m_users.at(m_currentUser));
void UserModel::userRemoveFinished(QDBusPendingCallWatcher *call, int row)
{
QDBusPendingReply<void> reply = *call;
if (reply.isError()) {
emit userRemoveFailed(row, m_users.at(row).name());
qWarning() << "Removing user with usermanager failed:" << reply.error();
} // else awesome! (waiting for signal to alter data)
call->deleteLater();
}
void UserModel::connectSignals()
{
m_dBusInterface->connection().connect(UserManagerService, UserManagerPath, UserManagerService, "userAdded",
this, SLOT(userAdded(const SailfishUser &)));
connect(m_dBusInterface, SIGNAL(userModified(const QString &, const QString &, const QString &)),
this, SLOT(userModified(const QString &, const QString &, const QString &)));
connect(m_dBusInterface, SIGNAL(userRemoved(const QString &)),
this, SLOT(userRemoved(const QString &)));
}
void UserModel::createInterface()
{
if (!m_dBusInterface) {
m_dBusInterface = new QDBusInterface(UserManagerService, UserManagerPath, UserManagerService,
QDBusConnection::systemBus(), this);
}
}
......@@ -37,13 +37,18 @@
#include "systemsettingsglobal.h"
#include "userinfo.h"
class QDBusInterface;
class QDBusPendingCallWatcher;
class QDBusServiceWatcher;
struct SailfishUser;
class SYSTEMSETTINGS_EXPORT UserModel: public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
UsernameRole = Qt::UserRole+1,
UsernameRole = Qt::UserRole,
NameRole,
TypeRole,
UidRole,
......@@ -64,12 +69,31 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE void createUser(const QString &name);
Q_INVOKABLE void removeUser(int row);
Q_INVOKABLE UserInfo * getCurrentUser() const;
signals:
void userAddFailed();
void userModifyFailed(int row, const QString &name);
void userRemoveFailed(int row, const QString &name);
private slots:
void userAdded(const SailfishUser &user);
void userModified(const QString &oldUsername, const QString &newUsername, const QString &newName);
void userRemoved(const QString &username);
void userAddFinished(QDBusPendingCallWatcher *call);
void userModifyFinished(QDBusPendingCallWatcher *call, int row);
void userRemoveFinished(QDBusPendingCallWatcher *call, int row);
void connectSignals();
private:
static const QHash<int, QByteArray> s_roles;
void createInterface();
QVector<UserInfo> m_users;
int m_currentUser;
QDBusInterface *m_dBusInterface;
QDBusServiceWatcher *m_dBusWatcher;
};
#endif /* USERMODEL_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment