newdevicemodel: deprecate ringdevicemodel in favor of newdevicemodel

Ring devices are currently managed via a Qt model. With this patch,
this class is now deprecated in favor of NewDeviceModel linked to
the AccountModel used by clients.

This patch add the ability to revoke devices and rename current used
device for LRC based clients.

Add some unit-tests in test/newdevicemodeltester.*

Change-Id: Ifcec4e573f92967d7e0ee8759f1159328957c7ba
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent a2c6e242
......@@ -327,6 +327,7 @@ SET( libringclient_LIB_SRCS
src/bannedcontactmodel.cpp
src/contactmodel.cpp
src/newcallmodel.cpp
src/newdevicemodel.cpp
src/conversationmodel.cpp
src/database.cpp
src/authority/daemon.cpp
......@@ -498,6 +499,7 @@ SET(libringclient_api_LIB_HDRS
src/api/lrc.h
src/api/newaccountmodel.h
src/api/newcallmodel.h
src/api/newdevicemodel.h
src/api/contactmodel.h
src/api/conversationmodel.h
src/api/profile.h
......
......@@ -35,6 +35,7 @@ class NewCallModel;
class ContactModel;
class ConversationModel;
class NewAccountModel;
class NewDeviceModel;
namespace account
{
......@@ -85,6 +86,7 @@ struct Info
std::unique_ptr<lrc::api::NewCallModel> callModel;
std::unique_ptr<lrc::api::ContactModel> contactModel;
std::unique_ptr<lrc::api::ConversationModel> conversationModel;
std::unique_ptr<lrc::api::NewDeviceModel> deviceModel;
NewAccountModel* accountModel {nullptr};
};
......
/****************************************************************************
* Copyright (C) 2017-2018 Savoir-faire Linux *
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#pragma once
// Std
#include <memory>
#include <string>
#include <list>
// Qt
#include <qobject.h>
#include <QObject>
// Lrc
#include "api/account.h"
#include "typedefs.h"
namespace lrc
{
class CallbacksHandler;
class NewDeviceModelPimpl;
namespace api
{
namespace account { struct Info; }
struct Device
{
std::string id = "";
std::string name = "";
bool isCurrent = false;
};
/**
* @brief Class that manages ring devices for an account
*/
class LIB_EXPORT NewDeviceModel : public QObject {
Q_OBJECT
public:
/**
* Used by deviceRevoked's status
*/
enum class Status {
SUCCESS = 0,
WRONG_PASSWORD = 1,
UNKNOWN_DEVICE = 2
};
const account::Info& owner;
NewDeviceModel(const account::Info& owner, const CallbacksHandler& callbacksHandler);
~NewDeviceModel();
/**
* Get ring devices of an account
* @return a copy of current devices
*/
std::list<Device> getAllDevices() const;
/**
* Retrieve a device by its id
* @param id of the device
* @return the device if found else a device with a null id
*/
Device getDevice(const std::string& id) const;
/**
* Revoke a ring device
* @param id of the device to revoke
* @param password of the account's archive
* @note will emit deviceRevoked when finished
*/
void revokeDevice(const std::string& id, const std::string& password);
/**
* Change the name of the current device
* @param newName
* @note will emit deviceUpdated when finished
* @note ring can't change the name of another device
*/
void setCurrentDeviceName(const std::string& newName);
Q_SIGNALS:
/**
* Link to this signal to know when a new device is added
* @param id added device
*/
void deviceAdded(const std::string& id) const;
/**
* Link to this signal to know when a device is removed
* @param id removed device
* @param Status (SUCCESS, WRONG_PASSWORD, UNKNOWN_DEVICE)
*/
void deviceRevoked(const std::string& id, const Status status) const;
/**
* Link to this signal when a device get a new name
* @param id
*/
void deviceUpdated(const std::string& id) const;
private:
std::unique_ptr<NewDeviceModelPimpl> pimpl_;
};
} // namespace api
} // namespace lrc
......@@ -118,6 +118,16 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
&ConfigurationManagerInterface::dataTransferEvent,
this,
&CallbacksHandler::slotDataTransferEvent);
connect(&ConfigurationManager::instance(),
&ConfigurationManagerInterface::knownDevicesChanged,
this,
&CallbacksHandler::slotKnownDevicesChanged);
connect(&ConfigurationManager::instance(),
&ConfigurationManagerInterface::deviceRevocationEnded,
this,
&CallbacksHandler::slotDeviceRevokationEnded);
}
CallbacksHandler::~CallbacksHandler()
......@@ -327,4 +337,23 @@ CallbacksHandler::slotDataTransferEvent(qulonglong dringId, uint codeStatus)
}
}
void
CallbacksHandler::slotKnownDevicesChanged(const QString& accountId,
const QMap<QString, QString>& devices)
{
std::map<std::string, std::string> stdDevices;
for (auto item : devices.keys())
stdDevices[item.toStdString()] = devices.value(item).toStdString();
auto accountId2 = accountId.toStdString();
emit knownDevicesChanged(accountId2, stdDevices);
}
void
CallbacksHandler::slotDeviceRevokationEnded(const QString& accountId,
const QString& deviceId,
const int status)
{
emit deviceRevocationEnded(accountId.toStdString(), deviceId.toStdString(), status);
}
} // namespace lrc
......@@ -20,6 +20,8 @@
// Std
#include <memory>
#include <string>
#include <map>
// Qt
#include <qobject.h>
......@@ -171,6 +173,25 @@ Q_SIGNALS:
void transferStatusTimeoutExpired(long long dringId, api::datatransfer::Info info);
void transferStatusUnjoinable(long long dringId, api::datatransfer::Info info);
/**
* Connect this signal to get when a device name changed or a device is added
* @param accountId interaction receiver.
* @param devices A map of device IDs with corresponding labels.
*/
void knownDevicesChanged(std::string& accountId,
std::map<std::string,std::string> devices);
/**
* Emit deviceRevocationEnded
* @param accountId
* @param deviceId
* @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2
*/
void deviceRevocationEnded(const std::string& accountId,
const std::string& deviceId,
const int status);
private Q_SLOTS:
/**
* Emit newAccountMessage
......@@ -291,6 +312,24 @@ private Q_SLOTS:
void slotDataTransferEvent(qulonglong id, uint code);
/**
* Emit knownDevicesChanged
* @param accountId
* @param devices A map of device IDs and corresponding labels
*/
void slotKnownDevicesChanged(const QString& accountId,
const QMap<QString, QString>& devices);
/**
* Emit deviceRevocationEnded
* @param accountId
* @param deviceId
* @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2
*/
void slotDeviceRevokationEnded(const QString& accountId,
const QString& deviceId,
const int status);
private:
const api::Lrc& parent;
};
......
......@@ -20,12 +20,13 @@
// LRC
#include "api/lrc.h"
#include "api/newcallmodel.h"
#include "api/contactmodel.h"
#include "api/conversationmodel.h"
#include "api/account.h"
#include "api/behaviorcontroller.h"
#include "api/contactmodel.h"
#include "api/conversationmodel.h"
#include "api/lrc.h"
#include "api/newcallmodel.h"
#include "api/newdevicemodel.h"
#include "authority/databasehelper.h"
#include "callbackshandler.h"
#include "database.h"
......@@ -219,6 +220,7 @@ NewAccountModelPimpl::addToAccounts(const std::string& accountId)
owner.callModel = std::make_unique<NewCallModel>(owner, callbacksHandler);
owner.contactModel = std::make_unique<ContactModel>(owner, database, callbacksHandler);
owner.conversationModel = std::make_unique<ConversationModel>(owner, lrc, database, callbacksHandler, behaviorController);
owner.deviceModel = std::make_unique<NewDeviceModel>(owner, callbacksHandler);
owner.accountModel = &linked;
}
......
/****************************************************************************
* Copyright (C) 2017-2018 Savoir-faire Linux *
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "api/newdevicemodel.h"
// std
#include <list>
#include <mutex>
// LRC
#include "callbackshandler.h"
#include "dbus/configurationmanager.h"
// Daemon
#include <account_const.h>
// Qt
#include <QObject>
namespace lrc
{
using namespace api;
class NewDeviceModelPimpl: public QObject
{
Q_OBJECT
public:
NewDeviceModelPimpl(const NewDeviceModel& linked, const CallbacksHandler& callbacksHandler);
~NewDeviceModelPimpl();
const CallbacksHandler& callbacksHandler;
const NewDeviceModel& linked;
std::mutex devicesMtx_;
std::string currentDeviceId_;
std::list<Device> devices_;
public Q_SLOTS:
/**
* Listen from CallbacksHandler to get when a device name changed or a device is added
* @param accountId interaction receiver.
* @param devices A map of device IDs with corresponding labels.
*/
void slotKnownDevicesChanged(const std::string& accountId,
const std::map<std::string, std::string> devices);
/**
* update devices_ when a device is revoked
* @param accountId
* @param deviceId
* @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2
*/
void slotDeviceRevocationEnded(const std::string& accountId,
const std::string& deviceId,
const int status);
};
NewDeviceModel::NewDeviceModel(const account::Info& owner, const CallbacksHandler& callbacksHandler)
: owner(owner)
, pimpl_(std::make_unique<NewDeviceModelPimpl>(*this, callbacksHandler))
{ }
NewDeviceModel::~NewDeviceModel() {}
std::list<Device>
NewDeviceModel::getAllDevices() const
{
return pimpl_->devices_;
}
Device
NewDeviceModel::getDevice(const std::string& id) const
{
std::lock_guard<std::mutex> lock(pimpl_->devicesMtx_);
auto i = std::find_if(
pimpl_->devices_.begin(), pimpl_->devices_.end(),
[id](const Device& d) {
return d.id == id;
});
if (i == pimpl_->devices_.end()) return {};
return *i;
}
void
NewDeviceModel::revokeDevice(const std::string& id, const std::string& password)
{
ConfigurationManager::instance().revokeDevice(owner.id.c_str(), password.c_str(), id.c_str());
}
void
NewDeviceModel::setCurrentDeviceName(const std::string& newName)
{
MapStringString details = {};
details[DRing::Account::ConfProperties::RING_DEVICE_NAME] = newName.c_str();
ConfigurationManager::instance().setAccountDetails(owner.id.c_str(), details);
}
NewDeviceModelPimpl::NewDeviceModelPimpl(const NewDeviceModel& linked, const CallbacksHandler& callbacksHandler)
: linked(linked)
, callbacksHandler(callbacksHandler)
, devices_({})
{
const MapStringString aDetails = ConfigurationManager::instance().getAccountDetails(linked.owner.id.c_str());
currentDeviceId_ = aDetails.value(DRing::Account::ConfProperties::RING_DEVICE_ID).toStdString();
const MapStringString accountDevices = ConfigurationManager::instance().getKnownRingDevices(linked.owner.id.c_str());
auto it = accountDevices.begin();
while (it != accountDevices.end()) {
{
std::lock_guard<std::mutex> lock(devicesMtx_);
auto device = Device {
/* id= */it.key().toStdString(),
/* name= */it.value().toStdString(),
/* isCurrent= */it.key().toStdString() == currentDeviceId_
};
if (device.isCurrent) {
devices_.emplace_front(device);
} else {
devices_.emplace_back(device);
}
}
++it;
}
connect(&callbacksHandler, &CallbacksHandler::knownDevicesChanged, this,
&NewDeviceModelPimpl::slotKnownDevicesChanged);
connect(&callbacksHandler, &CallbacksHandler::deviceRevocationEnded, this,
&NewDeviceModelPimpl::slotDeviceRevocationEnded);
}
NewDeviceModelPimpl::~NewDeviceModelPimpl()
{
disconnect(&callbacksHandler, &CallbacksHandler::knownDevicesChanged, this,
&NewDeviceModelPimpl::slotKnownDevicesChanged);
disconnect(&callbacksHandler, &CallbacksHandler::deviceRevocationEnded, this,
&NewDeviceModelPimpl::slotDeviceRevocationEnded);
}
void
NewDeviceModelPimpl::slotKnownDevicesChanged(const std::string& accountId,
const std::map<std::string, std::string> devices)
{
if (accountId != linked.owner.id) return;
auto devicesMap = devices;
// Update current devices
std::list<std::string> updatedDevices;
{
std::lock_guard<std::mutex> lock(devicesMtx_);
for (auto& device : devices_) {
if (devicesMap.find(device.id) != devicesMap.end()) {
if (device.name != devicesMap[device.id]) {
updatedDevices.emplace_back(device.id);
device.name = devicesMap[device.id];
}
devicesMap.erase(device.id);
}
}
}
for (const auto& device : updatedDevices)
emit linked.deviceUpdated(device);
// Add new devices
std::list<std::string> addedDevices;
{
std::lock_guard<std::mutex> lock(devicesMtx_);
auto it = devicesMap.begin();
while (it != devicesMap.end()) {
devices_.emplace_back(Device {
/* id= */it->first,
/* name= */it->second,
/* isCurrent= */false
});
addedDevices.emplace_back(it->first);
++it;
}
}
for (const auto& device : addedDevices)
emit linked.deviceAdded(device);
}
void
NewDeviceModelPimpl::slotDeviceRevocationEnded(const std::string& accountId,
const std::string& deviceId,
const int status)
{
if (accountId != linked.owner.id) return;
if (status == 0) {
std::lock_guard<std::mutex> lock(devicesMtx_);
auto it = std::find_if(
devices_.begin(), devices_.end(),
[deviceId](const Device& d) {
return d.id == deviceId;
});
if (it != devices_.end())
devices_.erase(it);
}
switch (status) {
case 0:
emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::SUCCESS);
break;
case 1:
emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::WRONG_PASSWORD);
break;
case 2:
emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::UNKNOWN_DEVICE);
break;
default:
break;
}
}
} // namespace lrc
#include "newdevicemodel.moc"
#include "api/moc_newdevicemodel.cpp"
......@@ -566,6 +566,10 @@ public Q_SLOTS: // METHODS
DRing::removeContact(accountId.toStdString(), uri.toStdString(), ban);
}
void revokeDevice(const QString &accountId, const QString &password, const QString &deviceId) {
DRing::revokeDevice(accountId.toStdString(), password.toStdString(), deviceId.toStdString());
}
void addContact(const QString &accountId, const QString &uri) {
DRing::addContact(accountId.toStdString(), uri.toStdString());
}
......@@ -676,6 +680,7 @@ Q_SIGNALS: // SIGNALS
void contactAdded(const QString &accountID, const QString &uri, bool banned);
void contactRemoved(const QString &accountID, const QString &uri, bool banned);
void dataTransferEvent(qulonglong transfer_id, uint code);
void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status);
};
namespace org { namespace ring { namespace Ring {
......
......@@ -53,6 +53,7 @@ private:
QMap<QString, VectorMapStringString> accountToContactsMap;
QStringList availableContacts_;
std::mutex contactsMtx_;
QMap<QString, QMap<QString, QString>> devices;
public:
......@@ -86,11 +87,22 @@ public:
}
}
accountToContactsMap.insert(account, contacts);
// Init devices
MapStringString devicesForAccount;
devicesForAccount["device0"] = "pc";
if (account.toStdString() == "ring3")
devicesForAccount["device1"] = "tel";
devices[account] = devicesForAccount;
}
}
~ConfigurationManagerInterface() {}
void addNewDevice(const QString& accountId, const QString& deviceId, const QString& name) {
devices[accountId][deviceId] = name;
emit knownDevicesChanged(accountId, devices[accountId]);
}
void emitIncomingAccountMessage(const QString& accountId, const QString& from, const QMap<QString,QString>& payloads)
{
emit incomingAccountMessage(accountId, from, payloads);
......@@ -137,9 +149,7 @@ public Q_SLOTS: // METHODS
MapStringString getKnownRingDevices(const QString& accountId)
{
Q_UNUSED(accountId)
MapStringString temp;
return temp;
return devices[accountId];
}
bool lookupName(const QString& accountId, const QString& nameServiceURL, const QString& name)
......@@ -174,6 +184,7 @@ public Q_SLOTS: // METHODS
} else {
result.insert("Account.type", "SIP");
}
result.insert("Account.deviceID", "device0");
return result;
}
......@@ -183,6 +194,7 @@ public Q_SLOTS: // METHODS
accountList << QString("ring0"); // Used in conversationmodeltester
accountList << QString("ring1"); // Used in contactmodeltester
accountList << QString("ring2"); // Used in newcallmodeltester
accountList << QString("ring3"); // Used in newdevicemodeltester
accountList << QString("sip0");
accountList << QString("sip1");
return accountList;
......@@ -448,8 +460,13 @@ public Q_SLOTS: // METHODS
void setAccountDetails(const QString& accountId, MapStringString details)
{
Q_UNUSED(accountId)
Q_UNUSED(details)
if (accountId.toStdString() == "ring3") {
// testSetCurrentDeviceName
if (details.contains(DRing::Account::ConfProperties::RING_DEVICE_NAME)) {
devices["ring3"]["device0"] = details[DRing::Account::ConfProperties::RING_DEVICE_NAME];
emit knownDevicesChanged(accountId, devices[accountId]);
}
}
}
void setAccountsOrder(const QString& order)
......@@ -648,6 +665,19 @@ public Q_SLOTS: // METHODS
emit contactRemoved(accountId, uri, ban);
}
void revokeDevice(const QString &accountId, const QString &password, const QString &deviceId) {
if (password == "") {
if (devices[accountId].contains(deviceId)) {
devices[accountId].remove(deviceId);
emit deviceRevocationEnded(accountId, deviceId, 0);
} else {
emit deviceRevocationEnded(accountId, deviceId, 2);
}
} else {
emit deviceRevocationEnded(accountId, deviceId, 1);
}
}
void addContact(const QString &accountId, const QString &uri)
{
if (getAccountList().indexOf(accountId) == -1) return;
......@@ -803,6 +833,7 @@ Q_SIGNALS: // SIGNALS
void contactAdded(const QString &accountId, const QString &uri, bool banned);
void contactRemoved(const QString &accountId, const QString &uri, bool banned);
void dataTransferEvent(uint64_t transfer_id, uint32_t code);
void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status);
};
namespace org {
......
/*
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*