1
/****************************************************************************
2
* Copyright (C) 2009-2014 by Savoir-Faire Linux *
3
* Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> *
4
* Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Lesser General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2.1 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14
* Lesser General Public License for more details. *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18
***************************************************************************/
21
#include "accountlistmodel.h"
24
#include "sflphone_const.h"
27
#include <QtCore/QObject>
30
#include "dbus/configurationmanager.h"
31
#include "dbus/callmanager.h"
32
#include "visitors/accountlistcolorvisitor.h"
34
AccountListModel* AccountListModel::m_spAccountList = nullptr;
35
Account* AccountListModel::m_spPriorAccount = nullptr ;
37
QVariant AccountListNoCheckProxyModel::data(const QModelIndex& idx,int role ) const
39
if (role == Qt::CheckStateRole) {
42
return AccountListModel::instance()->data(idx,role);
45
bool AccountListNoCheckProxyModel::setData( const QModelIndex& idx, const QVariant &value, int role)
47
return AccountListModel::instance()->setData(idx,value,role);
50
Qt::ItemFlags AccountListNoCheckProxyModel::flags (const QModelIndex& idx) const
52
const QModelIndex& src = AccountListModel::instance()->index(idx.row(),idx.column());
53
if (!idx.row() || AccountListModel::instance()->data(src,Qt::CheckStateRole) == Qt::Unchecked)
54
return Qt::NoItemFlags;
55
return AccountListModel::instance()->flags(idx);
58
int AccountListNoCheckProxyModel::rowCount(const QModelIndex& parentIdx ) const
60
return AccountListModel::instance()->rowCount(parentIdx);
64
///@param fill Whether to fill the list with accounts from configurationManager or not.
65
AccountListModel::AccountListModel() : QAbstractListModel(QCoreApplication::instance()),m_pColorVisitor(nullptr),m_pDefaultAccount(nullptr)
70
///Prevent constructor loop
71
void AccountListModel::init()
74
CallManagerInterface& callManager = DBus::CallManager::instance();
75
ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance();
77
connect(&configurationManager, SIGNAL(sipRegistrationStateChanged(QString,QString,int)),this,SLOT(accountChanged(QString,QString,int)));
78
connect(&configurationManager, SIGNAL(accountsChanged()) ,this,SLOT(updateAccounts()) );
79
connect(&callManager , SIGNAL(voiceMailNotify(QString,int)) ,this, SLOT(slotVoiceMailNotify(QString,int)) );
84
AccountListModel::~AccountListModel()
86
while(m_lAccounts.size()) {
87
Account* a = m_lAccounts[0];
88
m_lAccounts.remove(0);
93
void AccountListModel::setupRoleName()
95
QHash<int, QByteArray> roles = roleNames();
96
roles.insert(Account::Role::Alias ,QByteArray("alias" ));
97
roles.insert(Account::Role::Proto ,QByteArray("protocol" ));
98
roles.insert(Account::Role::Hostname ,QByteArray("hostname" ));
99
roles.insert(Account::Role::Username ,QByteArray("username" ));
100
roles.insert(Account::Role::Mailbox ,QByteArray("mailbox" ));
101
roles.insert(Account::Role::Proxy ,QByteArray("proxy" ));
102
roles.insert(Account::Role::TlsPassword ,QByteArray("tlsPassword" ));
103
roles.insert(Account::Role::TlsCaListFile ,QByteArray("tlsCaListFile" ));
104
roles.insert(Account::Role::TlsCertificateFile ,QByteArray("tlsCertificateFile" ));
105
roles.insert(Account::Role::TlsPrivateKeyFile ,QByteArray("tlsPrivateKeyFile" ));
106
roles.insert(Account::Role::TlsCiphers ,QByteArray("tlsCiphers" ));
107
roles.insert(Account::Role::TlsServerName ,QByteArray("tlsServerName" ));
108
roles.insert(Account::Role::SipStunServer ,QByteArray("sipStunServer" ));
109
roles.insert(Account::Role::PublishedAddress ,QByteArray("publishedAddress" ));
110
roles.insert(Account::Role::LocalInterface ,QByteArray("localInterface" ));
111
roles.insert(Account::Role::RingtonePath ,QByteArray("ringtonePath" ));
112
roles.insert(Account::Role::TlsMethod ,QByteArray("tlsMethod" ));
113
roles.insert(Account::Role::RegistrationExpire ,QByteArray("registrationExpire" ));
114
roles.insert(Account::Role::TlsNegotiationTimeoutSec ,QByteArray("tlsNegotiationTimeoutSec" ));
115
roles.insert(Account::Role::TlsNegotiationTimeoutMsec,QByteArray("tlsNegotiationTimeoutMsec" ));
116
roles.insert(Account::Role::LocalPort ,QByteArray("localPort" ));
117
roles.insert(Account::Role::TlsListenerPort ,QByteArray("tlsListenerPort" ));
118
roles.insert(Account::Role::PublishedPort ,QByteArray("publishedPort" ));
119
roles.insert(Account::Role::Enabled ,QByteArray("enabled" ));
120
roles.insert(Account::Role::AutoAnswer ,QByteArray("autoAnswer" ));
121
roles.insert(Account::Role::TlsVerifyServer ,QByteArray("tlsVerifyServer" ));
122
roles.insert(Account::Role::TlsVerifyClient ,QByteArray("tlsVerifyClient" ));
123
roles.insert(Account::Role::TlsRequireClientCertificate,QByteArray("tlsRequireClientCertificate" ));
124
roles.insert(Account::Role::TlsEnable ,QByteArray("tlsEnable" ));
125
roles.insert(Account::Role::DisplaySasOnce ,QByteArray("displaySasOnce" ));
126
roles.insert(Account::Role::SrtpRtpFallback ,QByteArray("srtpRtpFallback" ));
127
roles.insert(Account::Role::ZrtpDisplaySas ,QByteArray("zrtpDisplaySas" ));
128
roles.insert(Account::Role::ZrtpNotSuppWarning ,QByteArray("zrtpNotSuppWarning" ));
129
roles.insert(Account::Role::ZrtpHelloHash ,QByteArray("zrtpHelloHash" ));
130
roles.insert(Account::Role::SipStunEnabled ,QByteArray("sipStunEnabled" ));
131
roles.insert(Account::Role::PublishedSameAsLocal ,QByteArray("publishedSameAsLocal" ));
132
roles.insert(Account::Role::RingtoneEnabled ,QByteArray("ringtoneEnabled" ));
133
roles.insert(Account::Role::dTMFType ,QByteArray("dTMFType" ));
134
roles.insert(Account::Role::Id ,QByteArray("id" ));
135
roles.insert(Account::Role::Object ,QByteArray("object" ));
136
roles.insert(Account::Role::TypeName ,QByteArray("typeName" ));
137
roles.insert(Account::Role::PresenceStatus ,QByteArray("presenceStatus" ));
138
roles.insert(Account::Role::PresenceMessage ,QByteArray("presenceMessage" ));
144
AccountListModel* AccountListModel::instance()
146
if (not m_spAccountList) {
147
m_spAccountList = new AccountListModel();
148
m_spAccountList->init();
150
return m_spAccountList;
154
void AccountListModel::destroy()
157
delete m_spAccountList;
158
m_spAccountList = nullptr;
161
///Account status changed
162
void AccountListModel::accountChanged(const QString& account,const QString& state, int code)
164
Account* a = getAccountById(account);
166
if (!a || (a && a->registrationStatus() != state )) {
167
if (state != "OK") //Do not polute the log
168
qDebug() << "Account" << account << "status changed to" << state;
171
ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance();
172
QStringList accountIds = configurationManager.getAccountList().value();
173
for (int i = 0; i < accountIds.size(); ++i) {
174
if ((!getAccountById(accountIds[i])) && m_lDeletedAccounts.indexOf(accountIds[i]) == -1) {
175
Account* acc = Account::buildExistingAccountFromId(accountIds[i]);
176
m_lAccounts.insert(i, acc);
177
connect(acc,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*)));
178
emit dataChanged(index(i,0),index(size()-1));
179
emit layoutChanged();
182
foreach (Account* acc, m_lAccounts) {
183
const int idx =accountIds.indexOf(acc->id());
184
if (idx == -1 && (acc->state() == Account::AccountEditState::READY || acc->state() == Account::AccountEditState::REMOVED)) {
185
m_lAccounts.remove(idx);
186
emit dataChanged(index(idx - 1, 0), index(m_lAccounts.size()-1, 0));
187
emit layoutChanged();
192
const bool isRegistered = a->isRegistered();
194
emit a->stateChanged(a->toHumanStateName());
195
const QModelIndex idx = a->index();
196
emit dataChanged(idx, idx);
197
const bool regStateChanged = isRegistered != a->isRegistered();
198
if (regStateChanged && (code == 502 || code == 503)) {
201
else if (regStateChanged)
202
emit accountRegistrationChanged(a,a->isRegistered());
204
//Keep the error message
205
a->setLastErrorMessage(state);
206
a->setLastErrorCode(code);
208
emit accountStateChanged(a,a->toHumanStateName());
213
///Tell the model something changed
214
void AccountListModel::accountChanged(Account* a)
216
int idx = m_lAccounts.indexOf(a);
218
emit dataChanged(index(idx, 0), index(idx, 0));
223
/*****************************************************************************
227
****************************************************************************/
230
///When a new voice mail is available
231
void AccountListModel::slotVoiceMailNotify(const QString &accountID, int count)
233
Account* a = getAccountById(accountID);
235
a->setVoiceMailCount(count);
236
emit voiceMailNotify(a,count);
241
void AccountListModel::update()
243
ConfigurationManagerInterface & configurationManager = DBus::ConfigurationManager::instance();
245
for (int i = 0; i < m_lAccounts.size(); i++)
246
tmp << m_lAccounts[i];
248
for (int i = 0; i < tmp.size(); i++) {
249
Account* current = tmp[i];
250
if (!current->isNew() && (current->state() != Account::AccountEditState::NEW
251
&& current->state() != Account::AccountEditState::MODIFIED
252
&& current->state() != Account::AccountEditState::OUTDATED))
253
removeAccount(current);
255
//ask for the list of accounts ids to the configurationManager
256
const QStringList accountIds = configurationManager.getAccountList().value();
257
for (int i = 0; i < accountIds.size(); ++i) {
258
if (m_lDeletedAccounts.indexOf(accountIds[i]) == -1) {
259
Account* a = Account::buildExistingAccountFromId(accountIds[i]);
260
m_lAccounts.insert(i, a);
261
emit dataChanged(index(i,0),index(size()-1,0));
262
connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*)));
263
emit layoutChanged();
269
void AccountListModel::updateAccounts()
271
qDebug() << "Updating all accounts";
272
ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance();
273
QStringList accountIds = configurationManager.getAccountList().value();
274
//m_lAccounts.clear();
275
for (int i = 0; i < accountIds.size(); ++i) {
276
Account* acc = getAccountById(accountIds[i]);
278
Account* a = Account::buildExistingAccountFromId(accountIds[i]);
280
connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*)));
281
emit dataChanged(index(size()-1,0),index(size()-1,0));
284
acc->performAction(Account::AccountEditAction::RELOAD);
287
emit accountListUpdated();
290
///Save accounts details and reload it
291
void AccountListModel::save()
293
ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance();
294
const QStringList accountIds = QStringList(configurationManager.getAccountList().value());
296
//create or update each account from accountList
297
for (int i = 0; i < size(); i++) {
298
Account* current = (*this)[i];
299
current->performAction(Account::AccountEditAction::SAVE);
302
//remove accounts that are in the configurationManager but not in the client
303
for (int i = 0; i < accountIds.size(); i++) {
304
if(!getAccountById(accountIds[i])) {
305
configurationManager.removeAccount(accountIds[i]);
311
for( int i = 0 ; i < size() ; i++)
312
order += m_lAccounts[i]->id() + '/';
313
configurationManager.setAccountsOrder(order);
314
m_lDeletedAccounts.clear();
318
bool AccountListModel::accountUp( int idx )
320
if(idx > 0 && idx <= rowCount()) {
321
Account* account = m_lAccounts[idx];
322
m_lAccounts.remove(idx);
323
m_lAccounts.insert(idx - 1, account);
324
emit dataChanged(this->index(idx - 1, 0, QModelIndex()), this->index(idx, 0, QModelIndex()));
325
emit layoutChanged();
332
bool AccountListModel::accountDown( int idx )
334
if(idx >= 0 && idx < rowCount()) {
335
Account* account = m_lAccounts[idx];
336
m_lAccounts.remove(idx);
337
m_lAccounts.insert(idx + 1, account);
338
emit dataChanged(this->index(idx, 0, QModelIndex()), this->index(idx + 1, 0, QModelIndex()));
339
emit layoutChanged();
345
///Try to register all enabled accounts
346
void AccountListModel::registerAllAccounts()
348
ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance();
349
configurationManager.registerAllAccounts();
352
///Cancel all modifications
353
void AccountListModel::cancel() {
354
foreach (Account* a, getAccounts()) {
355
if (a->state() == Account::AccountEditState::MODIFIED || a->state() == Account::AccountEditState::OUTDATED)
356
a->performAction(Account::AccountEditAction::CANCEL);
358
m_lDeletedAccounts.clear();
362
/*****************************************************************************
366
****************************************************************************/
369
const QVector<Account*>& AccountListModel::getAccounts()
374
///Sometime, it may be useful to reverse map a phone number to an account using the hostname
375
QList<Account*> AccountListModel::getAccountsByHostNames ( const QString& hostname ) const
377
QList<Account*> toReturn;
378
for (int i = 0; i < m_lAccounts.size(); i++) {
379
Account* acc = m_lAccounts[i];
380
if (acc->hostname() == hostname)
386
///Get account using its ID
387
Account* AccountListModel::getAccountById(const QString& id) const
389
Q_ASSERT(!id.isEmpty());
390
for (int i = 0; i < m_lAccounts.size(); i++) {
391
Account* acc = m_lAccounts[i];
392
if (acc && !acc->isNew() && acc->id() == id)
398
///Get account with a specific state
399
QVector<Account*> AccountListModel::getAccountsByState(const QString& state)
401
QVector<Account *> v;
402
for (int i = 0; i < m_lAccounts.size(); i++) {
403
Account* acc = m_lAccounts[i];
404
if (acc->registrationStatus() == state)
410
///Get the first registerred account (default account)
411
Account* AccountListModel::firstRegisteredAccount() const
413
for (int i = 0; i < m_lAccounts.count(); ++i) {
414
Account* current = m_lAccounts[i];
415
if(current && current->registrationStatus() == Account::State::REGISTERED && current->isEnabled())
417
else if (current && (current->registrationStatus() == Account::State::READY) && m_lAccounts.count() == 1)
419
// else if (current && !(current->accountRegistrationStatus()() == ACCOUNT_STATE_READY)) {
420
// qDebug() << "Account " << ((current)?current->accountId():"") << " is not registered ("
421
// << ((current)?current->accountRegistrationStatus()():"") << ") State:"
422
// << ((current)?current->accountRegistrationStatus()():"");
428
///Get the account size
429
int AccountListModel::size() const
431
return m_lAccounts.size();
434
///Return the current account
435
Account* AccountListModel::currentAccount()
437
Account* priorAccount = m_spPriorAccount;
438
if(priorAccount && priorAccount->registrationStatus() == Account::State::REGISTERED && priorAccount->isEnabled() ) {
442
Account* a = instance()->firstRegisteredAccount();
444
a = instance()->getAccountById("IP2IP");
445
instance()->setPriorAccount(a);
448
} //getCurrentAccount
450
///Get data from the model
451
QVariant AccountListModel::data ( const QModelIndex& idx, int role) const
453
if (!idx.isValid() || idx.row() < 0 || idx.row() >= rowCount())
456
const Account* account = m_lAccounts[idx.row()];
457
if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
458
return QVariant(account->alias());
459
else if(idx.column() == 0 && role == Qt::CheckStateRole)
460
return QVariant(account->isEnabled() ? Qt::Checked : Qt::Unchecked);
461
else if (role == Qt::BackgroundRole)
462
return (m_pColorVisitor)?m_pColorVisitor->getColor(account):account->stateColor();
463
else if(idx.column() == 0 && role == Qt::DecorationRole && m_pColorVisitor)
464
return m_pColorVisitor->getIcon(account);
466
return account->roleData(role);
470
Qt::ItemFlags AccountListModel::flags(const QModelIndex& idx) const
472
if (idx.column() == 0)
473
return QAbstractItemModel::flags(idx) | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
474
return QAbstractItemModel::flags(idx);
478
int AccountListModel::rowCount(const QModelIndex& parentIdx) const
481
return m_lAccounts.size();
484
Account* AccountListModel::getAccountByModelIndex(const QModelIndex& item) const
488
return m_lAccounts[item.row()];
491
AccountListColorVisitor* AccountListModel::colorVisitor()
493
return m_pColorVisitor;
496
///Return the default account (used for contact lookup)
497
Account* AccountListModel::getDefaultAccount() const
499
return m_pDefaultAccount;
502
///Generate an unique suffix to prevent multiple account from sharing alias
503
QString AccountListModel::getSimilarAliasIndex(const QString& alias)
506
foreach (Account* a, instance()->getAccounts()) {
507
if (a->alias().left(alias.size()) == alias)
513
foreach (Account* a, instance()->getAccounts()) {
514
if (a->alias() == alias+QString(" (%1)").arg(count)) {
522
return QString(" (%1)").arg(count);
527
/*****************************************************************************
531
****************************************************************************/
534
Account* AccountListModel::addAccount(const QString& alias)
536
Account* a = Account::buildNewAccountFromAlias(alias);
537
connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*)));
540
emit dataChanged(index(m_lAccounts.size()-1,0), index(m_lAccounts.size()-1,0));
545
void AccountListModel::removeAccount(Account* account)
547
if (not account) return;
548
qDebug() << "Removing" << account->alias() << account->id();
549
const int aindex = m_lAccounts.indexOf(account);
550
m_lAccounts.remove(aindex);
551
m_lDeletedAccounts << account->id();
552
if (currentAccount() == account)
553
setPriorAccount(getAccountById("IP2IP"));
554
emit dataChanged(index(aindex,0), index(m_lAccounts.size()-1,0));
555
emit layoutChanged();
559
void AccountListModel::removeAccount( QModelIndex idx )
561
removeAccount(getAccountByModelIndex(idx));
564
///Set the previous account used
565
void AccountListModel::setPriorAccount(const Account* account) {
566
const bool changed = (account && m_spPriorAccount != account) || (!account && m_spPriorAccount);
567
m_spPriorAccount = const_cast<Account*>(account);
569
emit priorAccountChanged(currentAccount());
573
bool AccountListModel::setData(const QModelIndex& idx, const QVariant& value, int role)
575
if (idx.isValid() && idx.column() == 0 && role == Qt::CheckStateRole) {
576
const bool prevEnabled = m_lAccounts[idx.row()]->isEnabled();
577
m_lAccounts[idx.row()]->setEnabled(value.toBool());
578
emit dataChanged(idx, idx);
579
if (prevEnabled != value.toBool())
580
emit accountEnabledChanged(m_lAccounts[idx.row()]);
581
emit dataChanged(idx, idx);
584
else if ( role == Qt::EditRole ) {
585
if (value.toString() != data(idx,Qt::EditRole)) {
586
m_lAccounts[idx.row()]->setAlias(value.toString());
587
emit dataChanged(idx, idx);
593
///Set QAbstractItemModel BackgroundRole visitor
594
void AccountListModel::setColorVisitor(AccountListColorVisitor* visitor)
596
m_pColorVisitor = visitor;
599
///Set the default account (used for contact lookup)
600
void AccountListModel::setDefaultAccount(Account* a)
602
if (a != m_pDefaultAccount)
603
emit defaultAccountChanged(a);
604
m_pDefaultAccount = a;
608
/*****************************************************************************
612
****************************************************************************/
614
///Get the account from its index
615
const Account* AccountListModel::operator[] (int i) const
617
return m_lAccounts[i];
620
///Get the account from its index
621
Account* AccountListModel::operator[] (int i)
623
return m_lAccounts[i];
627
Account* AccountListModel::operator[] (const QString& i) {
628
return getAccountById(i);