Select Git revision
pluginMediaHandler.h
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
credentialmodel.cpp 22.11 KiB
/****************************************************************************
* Copyright (C) 2012-2015 by Savoir-Faire Linux *
* Author : Emmanuel Lepage Vallee <emmanuel.lepage@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 "credentialmodel.h"
//Qt
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtCore/QItemSelectionModel>
//Ring
#include <account.h>
#include <private/matrixutils.h>
#include <private/account_p.h>
//Dring
#include "dbus/configurationmanager.h"
#include <account_const.h>
typedef void (CredentialModelPrivate::*CredModelFct)();
struct CredentialNode final
{
union {
struct {
Credential* m_pCredential = {nullptr};
CredentialNode* m_pParent = {nullptr};
} m_Cred;
struct {
QString* m_CategoryName = {nullptr};
QVector<CredentialNode*>* m_lChildren;
} m_Category;
} m_uContent;
enum class Level {
CATEGORY,
CREDENTIAL
};
short int m_Index = {-1};
Level m_Level;
};
void free_node(CredentialNode* n);
/**
* Display the types of credentials that can be added
* This depend on the account type and settings
*/
class NewCredentialTypeModel final : public QAbstractListModel
{
Q_OBJECT
friend class CredentialModel;
public:
explicit NewCredentialTypeModel(Account* a);
virtual ~NewCredentialTypeModel();
virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
virtual Qt::ItemFlags flags ( const QModelIndex& index ) const override;
virtual bool setData ( const QModelIndex& index, const QVariant &value, int role ) override;
private:
Account* m_pAccount;
QItemSelectionModel* m_pSelectionModel {nullptr};
QStringList m_lValues { QStringLiteral("SIP"),QStringLiteral("STUN"),QStringLiteral("TURN")};
static const Matrix1D<Credential::Type, int> m_smMaximumCount;
static const Matrix2D<Credential::Type, Account::Protocol, bool> m_smAvailableInProtocol;
};
class CredentialModelPrivate final
{
public:
//Attributes
QList<CredentialNode*> m_lCategories ;
Account* m_pAccount ;
CredentialModel::EditState m_EditState ;
CredentialModel* q_ptr ;
uint m_TopLevelCount = {3};
static Matrix2D<CredentialModel::EditState, CredentialModel::EditAction,CredModelFct> m_mStateMachine;
CredentialNode* m_pSipCat = {nullptr};
CredentialNode* m_pTurnCat = {nullptr};
CredentialNode* m_pStunCat = {nullptr};
NewCredentialTypeModel* m_pTypeModel {nullptr};
//Callbacks
void clear ();
void save ();
void reload ();
void nothing();
void modify ();
//Helper
inline void performAction(const CredentialModel::EditAction action);
CredentialNode* getCategory(Credential::Type type);
CredentialNode* createCat(const QString& name);
};
#define CMP &CredentialModelPrivate
Matrix2D<CredentialModel::EditState, CredentialModel::EditAction,CredModelFct> CredentialModelPrivate::m_mStateMachine ={{
/* SAVE MODIFY RELOAD CLEAR */
{ CredentialModel::EditState::LOADING , {{ CMP::nothing, CMP::nothing, CMP::reload, CMP::nothing }}},
{ CredentialModel::EditState::READY , {{ CMP::nothing, CMP::modify , CMP::reload, CMP::clear }}},
{ CredentialModel::EditState::MODIFIED , {{ CMP::save , CMP::nothing, CMP::reload, CMP::clear }}},
{ CredentialModel::EditState::OUTDATED , {{ CMP::save , CMP::nothing, CMP::reload, CMP::clear }}},
}};
#undef CMP
///Constructor
CredentialModel::CredentialModel(Account* acc) : QAbstractItemModel(acc),
d_ptr(new CredentialModelPrivate())
{
Q_ASSERT(acc);
d_ptr->m_EditState = CredentialModel::EditState::LOADING;
d_ptr->m_pAccount = acc;
d_ptr->q_ptr = this;
QHash<int, QByteArray> roles = roleNames();
this << EditAction::RELOAD;
d_ptr->m_EditState = CredentialModel::EditState::READY;
}
CredentialModel::~CredentialModel()
{
foreach (CredentialNode* data, d_ptr->m_lCategories) {
free(data); //delete doesn't support non-trivial structures
}
}
QHash<int,QByteArray> CredentialModel::roleNames() const
{
static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
static bool initRoles = false;
if (!initRoles) {
initRoles = true;
roles.insert(CredentialModel::Role::NAME ,QByteArray("name"));
roles.insert(CredentialModel::Role::PASSWORD,QByteArray("password"));
roles.insert(CredentialModel::Role::REALM ,QByteArray("realm"));
}
return roles;
}
///Get the parent index
QModelIndex CredentialModel::parent( const QModelIndex& index) const
{
if (!index.isValid())
return QModelIndex();
const CredentialNode* node = static_cast<CredentialNode*>(index.internalPointer());
if ((!node) || node->m_Level == CredentialNode::Level::CATEGORY)
return QModelIndex();
return createIndex(node->m_uContent.m_Cred.m_pParent->m_Index,0,node->m_uContent.m_Cred.m_pParent);
}
///Get the column count
int CredentialModel::columnCount( const QModelIndex& parent) const
{
Q_UNUSED(parent)
return 1;
}
///Create the indexes
QModelIndex CredentialModel::index( int row, int column, const QModelIndex& parent) const
{
if (column)
return QModelIndex();
if (parent.isValid()) {
const CredentialNode* node = static_cast<CredentialNode*>(parent.internalPointer());
if (node->m_Level != CredentialNode::Level::CATEGORY
|| row >= node->m_uContent.m_Category.m_lChildren->size())
return QModelIndex();
return createIndex(row, column, node->m_uContent.m_Category.m_lChildren->at(row));
}
if (row >= d_ptr->m_lCategories.size())
return QModelIndex();
return createIndex(row, column, d_ptr->m_lCategories[row]);
}
///Model data
QVariant CredentialModel::data(const QModelIndex& idx, int role) const {
if (!idx.isValid())
return QVariant();
const CredentialNode* node = static_cast<CredentialNode*>(idx.internalPointer());
switch (node->m_Level) {
case CredentialNode::Level::CATEGORY:
switch(role) {
case Qt::DisplayRole:
return *node->m_uContent.m_Category.m_CategoryName;
}
break;
case CredentialNode::Level::CREDENTIAL:
switch (role) {
case Qt::DisplayRole:
case CredentialModel::Role::NAME:
return node->m_uContent.m_Cred.m_pCredential->username();
case CredentialModel::Role::PASSWORD:
return node->m_uContent.m_Cred.m_pCredential->password();
case CredentialModel::Role::REALM:
return node->m_uContent.m_Cred.m_pCredential->realm();
default:
break;
}
break;
}
return QVariant();
}
///Number of credentials
int CredentialModel::rowCount(const QModelIndex& par) const {
if (!par.isValid())
return d_ptr->m_lCategories.size();
const CredentialNode* node = static_cast<CredentialNode*>(par.internalPointer());
if (node->m_Level == CredentialNode::Level::CREDENTIAL)
return 0;
return d_ptr->m_lCategories[par.row()]->m_uContent.m_Category.m_lChildren->size();
}
///Model flags
Qt::ItemFlags CredentialModel::flags(const QModelIndex& idx) const {
if (!idx.isValid())
return Qt::NoItemFlags;
const CredentialNode* node = static_cast<CredentialNode*>(idx.internalPointer());
//Categories cannot be selected
switch (node->m_Level) {
case CredentialNode::Level::CATEGORY:
return Qt::ItemIsEnabled;
case CredentialNode::Level::CREDENTIAL:
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
//TODO make turn/stun disabled is account doesn't support/disable it
}
return Qt::NoItemFlags;
}
///Set credential data
bool CredentialModel::setData( const QModelIndex& idx, const QVariant &value, int role) {
if (!idx.isValid())
return false;
const CredentialNode* node = static_cast<CredentialNode*>(idx.internalPointer());
if (node->m_Level == CredentialNode::Level::CATEGORY)
return false;
if (idx.column() == 0 && role == CredentialModel::Role::NAME) {
node->m_uContent.m_Cred.m_pCredential->setUsername(value.toString());
emit dataChanged(idx, idx);
this << EditAction::MODIFY;
return true;
}
else if (idx.column() == 0 && role == CredentialModel::Role::PASSWORD) {
if (node->m_uContent.m_Cred.m_pCredential->password() != value.toString()) {
node->m_uContent.m_Cred.m_pCredential->setPassword(value.toString());
emit dataChanged(idx, idx);
this << EditAction::MODIFY;
return true;
}
}
else if (idx.column() == 0 && role == CredentialModel::Role::REALM) {
node->m_uContent.m_Cred.m_pCredential->setRealm(value.toString());
emit dataChanged(idx, idx);
this << EditAction::MODIFY;
return true;
}
return false;
}
CredentialNode* CredentialModelPrivate::createCat(const QString& name)
{
CredentialNode* n = (CredentialNode*) malloc(sizeof(CredentialNode));
n->m_Level = CredentialNode::Level::CATEGORY;
n->m_Index = q_ptr->rowCount();
n->m_uContent.m_Category.m_CategoryName = new QString(name);
n->m_uContent.m_Category.m_lChildren = new QVector<CredentialNode*>();
q_ptr->beginInsertRows(QModelIndex(),m_lCategories.size(),0);
m_lCategories << n;
q_ptr->endInsertRows();
return n;
}
void free_node(CredentialNode* n)
{
switch (n->m_Level) {
case CredentialNode::Level::CATEGORY:
delete n->m_uContent.m_Category.m_CategoryName;
delete n->m_uContent.m_Category.m_lChildren;
break;
case CredentialNode::Level::CREDENTIAL:
delete n->m_uContent.m_Cred.m_pCredential;
break;
}
free(n);
}
CredentialNode* CredentialModelPrivate::getCategory(Credential::Type type)
{
switch (type) {
case Credential::Type::SIP:
if (!m_pSipCat)
m_pSipCat = createCat(QStringLiteral("SIP"));
return m_pSipCat;
case Credential::Type::TURN:
if (!m_pTurnCat)
m_pTurnCat = createCat(QStringLiteral("TURN"));
return m_pTurnCat;
case Credential::Type::STUN:
if (!m_pStunCat)
m_pStunCat = createCat(QStringLiteral("STUN"));
return m_pStunCat;
case Credential::Type::COUNT__:
break;
}
return nullptr;
}
///Use the selectionmodel to get the type
QModelIndex CredentialModel::addCredentials()
{
Credential::Type type = Credential::Type::SIP;
if (d_ptr->m_pTypeModel && d_ptr->m_pTypeModel->m_pSelectionModel
&& d_ptr->m_pTypeModel->m_pSelectionModel->currentIndex().isValid())
type = static_cast<Credential::Type>(
d_ptr->m_pTypeModel->m_pSelectionModel->currentIndex().row()
);
return addCredentials(type);
}
///Add a new credential
QModelIndex CredentialModel::addCredentials(Credential::Type type)
{
CredentialNode* par = d_ptr->getCategory(type);
const int count = par->m_uContent.m_Category.m_lChildren->size();
const QModelIndex parIdx = index(par->m_Index,0);
beginInsertRows(parIdx, count, count);
CredentialNode* node = (CredentialNode*) malloc(sizeof(CredentialNode));
node->m_Level = CredentialNode::Level::CREDENTIAL;
node->m_uContent.m_Cred.m_pCredential = new Credential(type);
node->m_uContent.m_Cred.m_pParent = par;
node->m_Index = par->m_uContent.m_Category.m_lChildren->size();
par->m_uContent.m_Category.m_lChildren->append(node);
connect(node->m_uContent.m_Cred.m_pCredential, &Credential::changed,[this, node, par, type]() {
const QModelIndex parIdx = index(par->m_Index,0);
const QModelIndex idx = index(node->m_Index,0,parIdx);
emit dataChanged(idx, idx);
if (!node->m_Index) {
Credential* c = node->m_uContent.m_Cred.m_pCredential;
emit primaryCredentialChanged(type, c);
}
});
endInsertRows();
this << EditAction::MODIFY;
return index(count, 0, parIdx);
}
///Remove credential at 'idx'
void CredentialModel::removeCredentials(const QModelIndex& idx)
{
if (idx.isValid() && idx.parent().isValid()) {
beginRemoveRows(idx.parent(), idx.row(), idx.row());
CredentialNode* node = static_cast<CredentialNode*>(idx.internalPointer());
for (int i = node->m_Index+1; i < node->m_uContent.m_Cred.m_pParent->m_uContent.m_Category.m_lChildren->size();i++) {
node->m_uContent.m_Cred.m_pParent->m_uContent.m_Category.m_lChildren->at(i)->m_Index--;
}
node->m_uContent.m_Cred.m_pParent->m_uContent.m_Category.m_lChildren->removeAt(node->m_Index);
free_node(node);
endRemoveRows();
this << EditAction::MODIFY;
}
else {
qDebug() << "Failed to remove an invalid credential";
}
}
///Remove everything
void CredentialModelPrivate::clear()
{
q_ptr->beginRemoveRows(QModelIndex(),0,q_ptr->rowCount());
foreach(CredentialNode* data2, m_lCategories) {
free_node(data2);
}
m_lCategories.clear();
q_ptr->endRemoveRows();
m_EditState = CredentialModel::EditState::READY;
}
///Save all credentials
void CredentialModelPrivate::save()
{
ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
//SIP creds
if (m_pSipCat) {
VectorMapStringString toReturn;
foreach (CredentialNode* n, *m_pSipCat->m_uContent.m_Category.m_lChildren) {
Credential* cred = n->m_uContent.m_Cred.m_pCredential;
if (cred->username().isEmpty())
cred->setUsername(m_pAccount->username());
if (cred->realm().isEmpty())
cred->setRealm(QStringLiteral("*"));
toReturn << MapStringString {
{ DRing::Account::ConfProperties::USERNAME, cred->username() },
{ DRing::Account::ConfProperties::PASSWORD, cred->password() },
{ DRing::Account::ConfProperties::REALM , cred->realm () },
};
}
configurationManager.setCredentials(m_pAccount->id(),toReturn);
}
//TURN creds
if (m_pTurnCat) {
foreach (CredentialNode* n, *m_pSipCat->m_uContent.m_Category.m_lChildren) {
Credential* cred = n->m_uContent.m_Cred.m_pCredential;
m_pAccount->d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_UNAME , cred->username());
m_pAccount->d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_PWD , cred->password());
m_pAccount->d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_REALM , cred->realm ());
}
}
m_EditState = CredentialModel::EditState::READY;
}
///Reload credentials from DBUS
void CredentialModelPrivate::reload()
{
if (!m_pAccount->isNew()) {
clear();
m_EditState = CredentialModel::EditState::LOADING;
ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
//SIP
const VectorMapStringString credentials = configurationManager.getCredentials(m_pAccount->id());
for (int i=0; i < credentials.size(); i++) {
const QModelIndex& idx = q_ptr->addCredentials(Credential::Type::SIP);
q_ptr->setData(idx,credentials[i][ DRing::Account::ConfProperties::USERNAME ],CredentialModel::Role::NAME );
q_ptr->setData(idx,credentials[i][ DRing::Account::ConfProperties::PASSWORD ],CredentialModel::Role::PASSWORD);
q_ptr->setData(idx,credentials[i][ DRing::Account::ConfProperties::REALM ],CredentialModel::Role::REALM );
}
//TURN
const QModelIndex& idx = q_ptr->addCredentials(Credential::Type::TURN);
const QString usern = m_pAccount->d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_UNAME);
const QString passw = m_pAccount->d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_PWD );
const QString realm = m_pAccount->d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_REALM);
if (!(usern.isEmpty() && passw.isEmpty() && realm.isEmpty())) {
q_ptr->setData(idx, usern, CredentialModel::Role::NAME );
q_ptr->setData(idx, passw, CredentialModel::Role::PASSWORD);
q_ptr->setData(idx, realm, CredentialModel::Role::REALM );
}
}
m_EditState = CredentialModel::EditState::READY;
}
void CredentialModelPrivate::nothing()
{
//nothing
}
void CredentialModelPrivate::modify()
{
m_EditState = CredentialModel::EditState::MODIFIED;
m_pAccount << Account::EditAction::MODIFY;
}
void CredentialModelPrivate::performAction(const CredentialModel::EditAction action)
{
(this->*(m_mStateMachine[m_EditState][action]))();
}
/// anAccount << Call::EditAction::SAVE
CredentialModel* CredentialModel::operator<<(CredentialModel::EditAction& action)
{
performAction(action);
return this;
}
CredentialModel* operator<<(CredentialModel* a, CredentialModel::EditAction action)
{
return (!a)?nullptr : (*a) << action;
}
///Change the current edition state
bool CredentialModel::performAction(const CredentialModel::EditAction action)
{
CredentialModel::EditState curState = d_ptr->m_EditState;
d_ptr->performAction(action);
return curState != d_ptr->m_EditState;
}
Credential* CredentialModel::primaryCredential(Credential::Type type) const
{
switch(type) {
case Credential::Type::STUN:
if (d_ptr->m_pStunCat && d_ptr->m_pStunCat->m_uContent.m_Category.m_lChildren->size())
return d_ptr->m_pStunCat->m_uContent.m_Category.m_lChildren->first()->m_uContent.m_Cred.m_pCredential;
break;
case Credential::Type::TURN:
if (d_ptr->m_pTurnCat && d_ptr->m_pTurnCat->m_uContent.m_Category.m_lChildren->size())
return d_ptr->m_pTurnCat->m_uContent.m_Category.m_lChildren->first()->m_uContent.m_Cred.m_pCredential;
break;
case Credential::Type::SIP:
if (d_ptr->m_pSipCat && d_ptr->m_pSipCat->m_uContent.m_Category.m_lChildren->size())
return d_ptr->m_pSipCat->m_uContent.m_Category.m_lChildren->first()->m_uContent.m_Cred.m_pCredential;
break;
case Credential::Type::COUNT__:
break;
}
return nullptr;
}
/**
* NewCredentialTypeModel
*/
///Is a credential type available for an account protocol
const Matrix2D<Credential::Type, Account::Protocol, bool> NewCredentialTypeModel::m_smAvailableInProtocol = {
/* SIP IAX RING */
{ Credential::Type::SIP , {{true, false, false}}},
{ Credential::Type::STUN, {{true, false, true }}},
{ Credential::Type::TURN, {{true, false, true }}},
};
///The maximum number of credentials (-1 = inf), this could be protocol dependent
const Matrix1D<Credential::Type, int> NewCredentialTypeModel::m_smMaximumCount = {
{ Credential::Type::SIP , -1 },
{ Credential::Type::STUN, 1 },
{ Credential::Type::TURN, 1 },
};
NewCredentialTypeModel::NewCredentialTypeModel(Account* a) : m_pAccount(a)
{
}
NewCredentialTypeModel::~NewCredentialTypeModel()
{}
QVariant NewCredentialTypeModel::data( const QModelIndex& index, int role ) const
{
if (!index.isValid())
return QVariant();
switch (role) {
case Qt::DisplayRole:
return m_lValues[index.row()];
}
return QVariant();
}
int NewCredentialTypeModel::rowCount( const QModelIndex& parent ) const
{
return parent.isValid() ? 0 : m_lValues.size();
}
///Only enabled the available types
Qt::ItemFlags NewCredentialTypeModel::flags( const QModelIndex& index ) const
{
if (!index.isValid())
return Qt::NoItemFlags;
const Credential::Type t = static_cast<Credential::Type>(index.row());
bool avail = m_smAvailableInProtocol[t][m_pAccount->protocol()];
#if 0 //TODO it mostly work, but make developement/testing harder, to enable in the last patch
switch(t) {
case Credential::Type::STUN:
avail &= m_pAccount->isSipStunEnabled();
break;
case Credential::Type::TURN:
avail &= m_pAccount->isTurnEnabled();
break;
case Credential::Type::SIP:
case Credential::Type::COUNT__:
break;
}
#endif
return avail ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::NoItemFlags;
}
bool NewCredentialTypeModel::setData( const QModelIndex& index, const QVariant &value, int role )
{
Q_UNUSED(index)
Q_UNUSED(value)
Q_UNUSED(role)
return false;
}
QAbstractItemModel* CredentialModel::availableTypeModel() const
{
if (!d_ptr->m_pTypeModel)
d_ptr->m_pTypeModel = new NewCredentialTypeModel(d_ptr->m_pAccount);
return d_ptr->m_pTypeModel;
}
QItemSelectionModel* CredentialModel::availableTypeSelectionModel() const
{
if (!static_cast<NewCredentialTypeModel*>(availableTypeModel())->m_pSelectionModel) {
d_ptr->m_pTypeModel->m_pSelectionModel = new QItemSelectionModel(d_ptr->m_pTypeModel);
for (int i=0; i < d_ptr->m_pTypeModel->rowCount(); i++) {
const QModelIndex idx = d_ptr->m_pTypeModel->index(i,0);
if (idx.flags() & Qt::ItemIsSelectable) {
d_ptr->m_pTypeModel->m_pSelectionModel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
break;
}
}
}
return d_ptr->m_pTypeModel->m_pSelectionModel;
}
#include <credentialmodel.moc>