Skip to content
Snippets Groups Projects
Select Git revision
  • 747cd67fa150e549280274de72627b2801bc0a1e
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

main.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    account.cpp 92.14 KiB
    /****************************************************************************
     *   Copyright (C) 2009-2016 by Savoir-faire Linux                          *
     *   Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com>          *
     *            Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
     *            Guillaume Roguez <guillaume.roguez@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/>.  *
     ***************************************************************************/
    
    //Parent
    #include "account.h"
    
    //Qt
    #include <QtCore/QDebug>
    #include <QtCore/QObject>
    #include <QtCore/QString>
    #include <QtCore/QMimeData>
    #include <QtCore/QItemSelectionModel>
    
    //Ring daemon
    #include <account_const.h>
    
    //Ring lib
    #include "dbus/configurationmanager.h"
    #include "dbus/callmanager.h"
    #include "dbus/videomanager.h"
    #include "dbus/presencemanager.h"
    #include "globalinstances.h"
    #include "interfaces/accountlistcolorizeri.h"
    #include "certificate.h"
    #include "certificatemodel.h"
    #include "accountmodel.h"
    #include "private/certificatemodel_p.h"
    #include "private/account_p.h"
    #include "private/accountmodel_p.h"
    #include "credentialmodel.h"
    #include "ciphermodel.h"
    #include "protocolmodel.h"
    #include "bootstrapmodel.h"
    #include "ringdevicemodel.h"
    #include "contactrequest.h"
    #include "person.h"
    #include "profile.h"
    #include "profilemodel.h"
    #include "pendingcontactrequestmodel.h"
    #include "private/pendingcontactrequestmodel_p.h"
    #include "accountstatusmodel.h"
    #include "codecmodel.h"
    #include "networkinterfacemodel.h"
    #include "contactmethod.h"
    #include "phonedirectorymodel.h"
    #include "presencestatusmodel.h"
    #include "uri.h"
    #include "private/vcardutils.h"
    #include "mime.h"
    #include "namedirectory.h"
    #include "securityevaluationmodel.h"
    #include "daemoncertificatecollection.h"
    #include "private/securityevaluationmodel_p.h"
    #include "extensions/securityevaluationextension.h"
    #define TO_BOOL ?"true":"false"
    #define IS_TRUE == "true"
    
    
    #define AP &AccountPrivate
    #define EA Account::EditAction
    #define ES Account::EditState
    
    static EnumClassReordering<Account::EditAction> co =
    {                                 EA::NOTHING,  EA::EDIT  , EA::RELOAD ,  EA::SAVE  , EA::REMOVE , EA::MODIFY   , EA::CANCEL     };
    const Matrix2D<Account::EditState, Account::EditAction, account_function> AccountPrivate::stateMachineActionsOnState = {
    {ES::READY               ,{{co, { AP::nothing, AP::edit   , AP::reload , AP::nothing, AP::remove , AP::modify   , AP::nothing }}}},
    {ES::EDITING             ,{{co, { AP::nothing, AP::nothing, AP::outdate, AP::nothing, AP::remove , AP::modify   , AP::cancel  }}}},
    {ES::OUTDATED            ,{{co, { AP::nothing, AP::nothing, AP::nothing, AP::nothing, AP::remove , AP::reloadMod, AP::reload  }}}},
    {ES::NEW                 ,{{co, { AP::nothing, AP::nothing, AP::nothing, AP::save   , AP::remove , AP::nothing  , AP::nothing }}}},
    {ES::MODIFIED_INCOMPLETE ,{{co, { AP::nothing, AP::nothing, AP::nothing, AP::save   , AP::remove , AP::modify   , AP::reload  }}}},
    {ES::MODIFIED_COMPLETE   ,{{co, { AP::nothing, AP::nothing, AP::nothing, AP::save   , AP::remove , AP::modify   , AP::reload  }}}},
    {ES::REMOVED             ,{{co, { AP::nothing, AP::nothing, AP::nothing, AP::nothing, AP::nothing, AP::nothing  , AP::cancel  }}}}
    };
    
    #undef ES
    #undef EA
    #undef AP
    
    //Host the current highest interal identifier. The internal id is used for some bitmasks
    //when objects have a different status for each account
    static uint p_sAutoIncrementId = 0;
    
    AccountPrivate::AccountPrivate(Account* acc) : QObject(acc),q_ptr(acc),m_pCredentials(nullptr),m_pCodecModel(nullptr),
    m_LastErrorCode(-1),m_VoiceMailCount(0),m_CurrentState(Account::EditState::READY),
    m_pAccountNumber(nullptr),m_pKeyExchangeModel(nullptr),m_pSecurityEvaluationModel(nullptr),m_pTlsMethodModel(nullptr),
    m_pCaCert(nullptr),m_pTlsCert(nullptr),m_isLoaded(true),m_pCipherModel(nullptr),
    m_pStatusModel(nullptr),m_LastTransportCode(0),m_RegistrationState(Account::RegistrationState::UNREGISTERED),
    m_UseDefaultPort(false),m_pProtocolModel(nullptr),m_pBootstrapModel(nullptr),m_RemoteEnabledState(false),
    m_pKnownCertificates(nullptr),
    m_pBannedCertificates(nullptr), m_pAllowedCertificates(nullptr),m_InternalId(++p_sAutoIncrementId),
    m_pNetworkInterfaceModel(nullptr),m_pAllowedCerts(nullptr),m_pBannedCerts(nullptr),m_pPendingContactRequestModel(nullptr),
    m_pRingDeviceModel(nullptr)
    {
    }
    
    void AccountPrivate::changeState(Account::EditState state) {
       const Account::EditState previous = m_CurrentState;
       m_CurrentState = state;
    
       if (state != previous)
          emit q_ptr->editStateChanged(state, previous);
    
       emit q_ptr->changed(q_ptr);
    }
    
    ///Constructors
    Account::Account():ItemBase(&AccountModel::instance()),d_ptr(new AccountPrivate(this))
    {
    }
    
    ///Build an account from it'id
    Account* Account::buildExistingAccountFromId(const QByteArray& _accountId)
    {
    //    qDebug() << "Building an account from id: " << _accountId;
       Account* a = new Account();
       a->d_ptr->m_AccountId = _accountId;
       a->d_ptr->setObjectName(_accountId);
       a->d_ptr->m_RemoteEnabledState = true;
    
       a->performAction(Account::EditAction::RELOAD);
    
       //If a placeholder exist for this account, upgrade it
       if (auto place_holder = AccountModel::instance().findPlaceHolder(_accountId))
           place_holder->d_ptr->merge(a);
    
       //Load the pending trust requests
       if (a->protocol() == Account::Protocol::RING) {
           const VectorMapStringString& pending_tr {ConfigurationManager::instance().getTrustRequests(a->id())};
           for (const auto& tr_info : pending_tr) {
             a->pendingContactRequestModel()->d_ptr->addRequest(new ContactRequest(a, tr_info["from"], 0));
          }
       }
    
       // Connects the account to the signal of the model.
       connect(a->pendingContactRequestModel() , &PendingContactRequestModel::requestAccepted, [a] (ContactRequest* r) {
          emit a->contactRequestAccepted(r);
       });
    
       // Load the contacts associated from the daemon and create the cms.
       const auto account_contacts = static_cast<QVector<QMap<QString, QString>>>(ConfigurationManager::instance().getContacts(a->id().data()));
    
       if (a->protocol() == Account::Protocol::RING) {
          for (auto contact_info : account_contacts) {
             auto cm = PhoneDirectoryModel::instance().getNumber(contact_info["id"], a);
             a->d_ptr->m_NumbersFromDaemon << cm;
          }
       }
    
       //Load the tracked buddies
       const VectorMapStringString subscriptions = PresenceManager::instance().getSubscriptions(a->id());
       foreach(auto subscription, subscriptions){
           ContactMethod* tracked_buddy = PhoneDirectoryModel::instance().getNumber(subscription["Buddy"], a);
           bool tracked_buddy_present = subscription["Status"].compare("Online") == 0;
           tracked_buddy->setTracked(true);
           tracked_buddy->setPresent(tracked_buddy_present);
       }
    
       return a;
    } //buildExistingAccountFromId
    
    ///Build an account from it's name / alias
    Account* Account::buildNewAccountFromAlias(Account::Protocol proto, const QString& alias)
    {
       qDebug() << "Building an account from alias: " << alias;
       ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
       Account* a = new Account();
       a->setProtocol(proto);
       a->d_ptr->m_hAccountDetails.clear();
       a->d_ptr->m_hAccountDetails[DRing::Account::ConfProperties::ENABLED] = "false";
       a->d_ptr->m_pAccountNumber = const_cast<ContactMethod*>(ContactMethod::BLANK());
       MapStringString tmp;
       switch (proto) {
          case Account::Protocol::SIP:
             tmp = configurationManager.getAccountTemplate(DRing::Account::ProtocolNames::SIP);
             break;
          case Account::Protocol::RING:
             tmp = configurationManager.getAccountTemplate(DRing::Account::ProtocolNames::RING);
             break;
          case Account::Protocol::COUNT__:
             break;
       }
       QMutableMapIterator<QString, QString> iter(tmp);
       while (iter.hasNext()) {
          iter.next();
          a->d_ptr->m_hAccountDetails[iter.key()] = iter.value();
       }
    
       if (proto == Account::Protocol::RING)
       {
           /* Set LRC-provided bootstrap servers */
           a->bootstrapModel() << BootstrapModel::EditAction::RESET;
           a->bootstrapModel() << BootstrapModel::EditAction::SAVE;
       }
       else
       {
           a->setHostname(a->d_ptr->m_hAccountDetails[DRing::Account::ConfProperties::HOSTNAME]);
       }
    
       a->d_ptr->setAccountProperty(DRing::Account::ConfProperties::ALIAS,alias);
       a->d_ptr->m_RemoteEnabledState = a->isEnabled();
       //a->setObjectName(a->id());
       return a;
    }
    
    ///Destructor
    Account::~Account()
    {
       disconnect();
       if (d_ptr->m_pCredentials) delete d_ptr->m_pCredentials ;
       if (d_ptr->m_pCodecModel) delete d_ptr->m_pCodecModel   ;
    }
    
    /*****************************************************************************
     *                                                                           *
     *                                   Slots                                   *
     *                                                                           *
     ****************************************************************************/
    
    void AccountPrivate::slotPresentChanged(bool present)
    {
       Q_UNUSED(present)
       emit q_ptr->changed(q_ptr);
    }
    
    void AccountPrivate::slotPresenceMessageChanged(const QString& message)
    {
       Q_UNUSED(message)
       emit q_ptr->changed(q_ptr);
    }
    
    void AccountPrivate::slotUpdateCertificate()
    {
       Certificate* cert = qobject_cast<Certificate*>(sender());
       if (cert) {
          switch (cert->type()) {
             case Certificate::Type::AUTHORITY:
                if (accountDetail(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) != cert->path())
                   setAccountProperty(DRing::Account::ConfProperties::TLS::CA_LIST_FILE, cert->path());
                break;
             case Certificate::Type::USER:
                if (accountDetail(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE) != cert->path())
                   setAccountProperty(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE, cert->path());
                break;
             case Certificate::Type::PRIVATE_KEY:
                if (accountDetail(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) != cert->path())
                   setAccountProperty(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE, cert->path());
                break;
             case Certificate::Type::NONE:
             case Certificate::Type::CALL:
                break;
          };
       }
    }
    
    /*****************************************************************************
     *                                                                           *
     *                                  Getters                                  *
     *                                                                           *
     ****************************************************************************/
    
    ///IS this account new
    bool Account::isNew() const
    {
       return (d_ptr->m_AccountId == nullptr) || d_ptr->m_AccountId.isEmpty();
    }
    
    ///Is this an IP2IP account
    bool Account::isIp2ip() const
    {
        return protocol() == Protocol::SIP && hostname().isEmpty();
    }
    
    ///Get this account ID
    const QByteArray Account::id() const
    {
       if (isNew()) {
          qDebug() << "Error : getting AccountId of a new account.";
          return QByteArray(); //WARNING May explode
       }
    
       return d_ptr->m_AccountId;
    }
    
    ///Get the device ID
    QString Account::deviceId() const
    {
        return d_ptr->accountDetail(DRing::Account::ConfProperties::RING_DEVICE_ID);
    }
    
    ///Get current state
    const QString Account::toHumanStateName() const
    {
       const QString s = d_ptr->m_hAccountDetails[DRing::Account::ConfProperties::Registration::STATUS];
    
                                                     //: Account state
       static const QString ready                  = tr("Ready"                    );
                                                     //: Account state
       static const QString registered             = tr("Registered"               );
                                                     //: Account state
       static const QString notRegistered          = tr("Not Registered"           );
                                                     //: Account state
       static const QString initializing           = tr("Initializing"             );
                                                     //: Account state
       static const QString trying                 = tr("Trying..."                );
                                                     //: Account state
       static const QString error                  = tr("Error"                    );
                                                     //: Account state
       static const QString authenticationFailed   = tr("Authentication Failed"    );
                                                     //: Account state
       static const QString networkUnreachable     = tr("Network unreachable"      );
                                                     //: Account state
       static const QString hostUnreachable        = tr("Host unreachable"         );
                                                     //: Account state
       static const QString stunConfigurationError = tr("Stun configuration error" );
                                                     //: Account state
       static const QString stunServerInvalid      = tr("Stun server invalid"      );
                                                     //: Account state
       static const QString serviceUnavailable     = tr("Service unavailable"      );
                                                     //: Account state
       static const QString notAcceptable          = tr("Unacceptable"             );
                                                     //: Account state
       static const QString invalid                = tr("Invalid"                  );
                                                     //: Account state
       static const QString requestTimeout         = tr("Request Timeout"          );
    
       if(s == DRing::Account::States::READY            )
          return ready                  ;
       if(s == DRing::Account::States::REGISTERED       )
          return registered             ;
       if(s == DRing::Account::States::INITIALIZING     )
          return initializing           ;
       if(s == DRing::Account::States::UNREGISTERED     )
          return notRegistered          ;
       if(s == DRing::Account::States::TRYING           )
          return trying                 ;
       if(s == DRing::Account::States::ERROR            )
          return d_ptr->m_LastErrorMessage.isEmpty()?error:d_ptr->m_LastErrorMessage;
       if(s == DRing::Account::States::ERROR_AUTH       )
          return authenticationFailed   ;
       if(s == DRing::Account::States::ERROR_NETWORK    )
          return networkUnreachable     ;
       if(s == DRing::Account::States::ERROR_HOST       )
          return hostUnreachable        ;
       if(s == DRing::Account::States::ERROR_CONF_STUN  )
          return stunConfigurationError ;
       if(s == DRing::Account::States::ERROR_EXIST_STUN )
          return stunServerInvalid      ;
       if(s == DRing::Account::States::ERROR_SERVICE_UNAVAILABLE )
          return serviceUnavailable     ;
       if(s == DRing::Account::States::ERROR_NOT_ACCEPTABLE      )
          return notAcceptable          ;
       if(s == DRing::Account::States::REQUEST_TIMEOUT           )
          return requestTimeout         ;
       return invalid                   ;
    }
    
    ///Get an account detail
    QString AccountPrivate::accountDetail(const QString& param) const
    {
       if (!m_hAccountDetails.size()) {
          qDebug() << "The account details is not set";
          return QString(); //May crash, but better than crashing now
       }
       if (m_hAccountDetails.find(param) != m_hAccountDetails.end()) {
          return m_hAccountDetails[param];
       }
       else if (m_hAccountDetails.count() > 0) {
          if (param == DRing::Account::ConfProperties::ENABLED) //If an account is invalid, at least does not try to register it
             return AccountPrivate::RegistrationEnabled::NO;
          if (param == DRing::Account::ConfProperties::Registration::STATUS) { //If an account is new, then it is unregistered
             return DRing::Account::States::UNREGISTERED;
          }
          static QHash<QString,bool> alreadyWarned;
          if (!alreadyWarned[param]) {
             alreadyWarned[param] = true;
             qDebug() << "Account parameter \"" << param << "\" not found";
          }
          return QString();
       }
       else {
          qDebug() << "Account details not found, there is " << m_hAccountDetails.count() << " details available";
          return QString();
       }
    } //accountDetail
    
    ///Get an account detail
    QString Account::accountDetail(const QString& param) const
    {
        return d_ptr->accountDetail(param);
    }
    
    ///Get the alias
    const QString Account::alias() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ALIAS);
    }
    
    ///Return the model index of this item
    QModelIndex Account::index() const
    {
       //There is usually < 5 accounts, the loop may be faster than a hash for most users
       for (int i=0;i < AccountModel::instance().size();i++) {
          if (this == AccountModel::instance()[i]) {
             return AccountModel::instance().index(i,0);
          }
       }
       return QModelIndex();
    }
    
    ///Return status color name
    QString Account::stateColorName() const
    {
       switch(registrationState()) {
          case RegistrationState::READY:
             return "darkGreen";
          case RegistrationState::UNREGISTERED:
             return "black";
          case RegistrationState::TRYING:
          case RegistrationState::INITIALIZING:
             return "orange";
          case RegistrationState::ERROR:
             return "red";
          case RegistrationState::COUNT__:
             break;
       };
       return QString();
    }
    
    ///I
    bool Account::isLoaded() const
    {
       return d_ptr->m_isLoaded;
    }
    
    ///Return status Qt color, QColor is not part of QtCore, use using the global variant
    QVariant Account::stateColor() const
    {
       return GlobalInstances::accountListColorizer().color(this);
    }
    
    ///Create and return the credential model
    CredentialModel* Account::credentialModel() const
    {
       if (!d_ptr->m_pCredentials) {
          d_ptr->m_pCredentials = new CredentialModel(const_cast<Account*>(this));
          connect(d_ptr->m_pCredentials, &CredentialModel::primaryCredentialChanged,[this]() {
             Account* a = const_cast<Account*>(this);
             emit a->changed(a);
          });
       }
       return d_ptr->m_pCredentials;
    }
    
    ///Create and return the audio codec model
    CodecModel* Account::codecModel() const
    {
       if (!d_ptr->m_pCodecModel) {
          d_ptr->m_pCodecModel = new CodecModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pCodecModel;
    }
    
    KeyExchangeModel* Account::keyExchangeModel() const
    {
       if (!d_ptr->m_pKeyExchangeModel) {
          d_ptr->m_pKeyExchangeModel = new KeyExchangeModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pKeyExchangeModel;
    }
    
    CipherModel* Account::cipherModel() const
    {
       if (!d_ptr->m_pCipherModel) {
          d_ptr->m_pCipherModel = new CipherModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pCipherModel;
    }
    
    AccountStatusModel* Account::statusModel() const
    {
       if (!d_ptr->m_pStatusModel) {
          d_ptr->m_pStatusModel = new AccountStatusModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pStatusModel;
    }
    
    SecurityEvaluationModel* Account::securityEvaluationModel() const
    {
       if (!d_ptr->m_pSecurityEvaluationModel) {
          d_ptr->m_pSecurityEvaluationModel = new SecurityEvaluationModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pSecurityEvaluationModel;
    }
    
    TlsMethodModel* Account::tlsMethodModel() const
    {
       if (!d_ptr->m_pTlsMethodModel ) {
          d_ptr->m_pTlsMethodModel  = new TlsMethodModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pTlsMethodModel;
    }
    
    ProtocolModel* Account::protocolModel() const
    {
       if (!d_ptr->m_pProtocolModel ) {
          d_ptr->m_pProtocolModel  = new ProtocolModel(const_cast<Account*>(this));
       }
       return d_ptr->m_pProtocolModel;
    }
    
    BootstrapModel* Account::bootstrapModel() const
    {
       if (protocol() != Account::Protocol::RING)
          return nullptr;
    
       if (!d_ptr->m_pBootstrapModel ) {
          d_ptr->m_pBootstrapModel  = new BootstrapModel(const_cast<Account*>(this));
       }
    
       return d_ptr->m_pBootstrapModel;
    }
    
    RingDeviceModel* Account::ringDeviceModel() const
    {
        if (!d_ptr->m_pRingDeviceModel)
          d_ptr->m_pRingDeviceModel = new RingDeviceModel(const_cast<Account*>(this));
    
       return d_ptr->m_pRingDeviceModel;
    }
    
    QAbstractItemModel* Account::knownCertificateModel() const
    {
       if (!d_ptr->m_pKnownCertificates) {
          d_ptr->m_pKnownCertificates = CertificateModel::instance().d_ptr->createKnownList(this);
       }
    
       return d_ptr->m_pKnownCertificates;
    }
    
    QAbstractItemModel* Account::bannedCertificatesModel() const
    {
       if (protocol() != Account::Protocol::RING || isNew())
          return nullptr;
    
       if (!d_ptr->m_pBannedCerts) {
          d_ptr->m_pBannedCerts = CertificateModel::instance().addCollection<DaemonCertificateCollection,Account*,DaemonCertificateCollection::Mode>(
             const_cast<Account*>(this),
             DaemonCertificateCollection::Mode::BANNED
          );
          d_ptr->m_pBannedCerts->load();
       }
    
       if (!d_ptr->m_pBannedCertificates) {
          d_ptr->m_pBannedCertificates = CertificateModel::instance().d_ptr->createBannedList(this);
       }
    
       return d_ptr->m_pBannedCertificates;
    }
    
    QAbstractItemModel* Account::allowedCertificatesModel() const
    {
       if (protocol() != Account::Protocol::RING || isNew())
          return nullptr;
    
       if (!d_ptr->m_pAllowedCerts) {
          d_ptr->m_pAllowedCerts = CertificateModel::instance().addCollection<DaemonCertificateCollection,Account*,DaemonCertificateCollection::Mode>(
             const_cast<Account*>(this),
             DaemonCertificateCollection::Mode::ALLOWED
          );
          d_ptr->m_pAllowedCerts->load();
       }
    
       if (!d_ptr->m_pAllowedCertificates) {
          d_ptr->m_pAllowedCertificates = CertificateModel::instance().d_ptr->createAllowedList(this);
       }
    
       return d_ptr->m_pAllowedCertificates;
    }
    
    PendingContactRequestModel* Account::pendingContactRequestModel() const
    {
       if (!d_ptr->m_pPendingContactRequestModel)
          d_ptr->m_pPendingContactRequestModel = new PendingContactRequestModel(const_cast<Account*>(this));
    
       return d_ptr->m_pPendingContactRequestModel;
    }
    
    NetworkInterfaceModel* Account::networkInterfaceModel() const
    {
       if (!d_ptr->m_pNetworkInterfaceModel) {
          d_ptr->m_pNetworkInterfaceModel = new NetworkInterfaceModel(const_cast<Account*>(this));
       }
    
       return d_ptr->m_pNetworkInterfaceModel;
    }
    
    bool Account::isUsedForOutgogingCall() const
    {
       return usageStats.haveCalled();
    }
    
    uint Account::totalCallCount() const
    {
       return usageStats.totalCount();
    }
    
    uint Account::weekCallCount() const
    {
       return usageStats.lastWeekCount();
    }
    
    uint Account::trimesterCallCount() const
    {
       return usageStats.lastTrimCount();
    }
    
    time_t Account::lastUsed() const
    {
       return usageStats.lastUsed();
    }
    
    /*******************************************************************************
     *                                                                             *
     *                                  Setters                                    *
     *                                                                             *
     ******************************************************************************/
    
    void Account::setAlias(const QString& detail)
    {
       const bool accChanged = detail != alias();
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ALIAS,detail);
    
       if (accChanged)
          emit aliasChanged(detail);
    }
    
    ///Return the account hostname
    QString Account::hostname() const
    {
       return d_ptr->m_HostName;
    }
    
    ///Return if the account is enabled
    bool Account::isEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ENABLED) IS_TRUE;
    }
    
    ///Return if the account should auto answer
    bool Account::isAutoAnswer() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::AUTOANSWER) IS_TRUE;
    }
    
    //Return if the accounts needs to migrate
    bool Account::needsMigration() const
    {
        const MapStringString details = ConfigurationManager::instance().getVolatileAccountDetails(id());
        const QString status = details[DRing::Account::VolatileProperties::Registration::STATUS];
        return status == DRing::Account::States::ERROR_NEED_MIGRATION;
    }
    
    /**
     * return all ContactsMethod from @this account
     * @return Account::ContactMethods a.k.a. QVector<ContactMethod*>
     */
    const Account::ContactMethods&
    Account::getContacts() const
    {
        return d_ptr->m_NumbersFromDaemon;
    }
    
    ///Return the account user name
    QString Account::username() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::USERNAME);
    }
    
    //Return the account registered name
    QString Account::registeredName() const
    {
        const MapStringString details = ConfigurationManager::instance().getVolatileAccountDetails(id());
        const QString registeredName = details[DRing::Account::VolatileProperties::REGISTERED_NAME];
        return registeredName;
    }
    
    ///Return the account mailbox address
    QString Account::mailbox() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::MAILBOX);
    }
    
    ///Return the account mailbox address
    QString Account::proxy() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ROUTE);
    }
    
    ///Return the name service URL
    QString Account::nameServiceURL() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::RingNS::URI);
    }
    
    QString Account::password() const
    {
       switch (protocol()) {
          case Account::Protocol::SIP:
             if (credentialModel()->primaryCredential(Credential::Type::SIP))
                return credentialModel()->primaryCredential(Credential::Type::SIP)->password();
             break;
          case Account::Protocol::RING:
             return tlsPassword();
          case Account::Protocol::COUNT__:
             break;
       };
       return "";
    }
    
    ///Return the account security fallback
    bool Account::isSrtpRtpFallback() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::SRTP::RTP_FALLBACK) IS_TRUE;
    }
    
    //Return if SRTP is enabled or not
    bool Account::isSrtpEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::SRTP::ENABLED) IS_TRUE;
    }
    
    ///Return if the account is using a STUN server
    bool Account::isSipStunEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::STUN::ENABLED) IS_TRUE;
    }
    
    ///Return the account STUN server
    QString Account::sipStunServer() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::STUN::SERVER);
    }
    
    ///Return when the account expire (require renewal)
    int Account::registrationExpire() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Registration::EXPIRE).toInt();
    }
    
    ///Return if the published address is the same as the local one
    bool Account::isPublishedSameAsLocal() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::PUBLISHED_SAMEAS_LOCAL) IS_TRUE;
    }
    
    ///Return the account published address
    QString Account::publishedAddress() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::PUBLISHED_ADDRESS);
    }
    
    ///Return the account published port
    int Account::publishedPort() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::PUBLISHED_PORT).toUInt();
    }
    
    ///Return the account tls password
    QString Account::tlsPassword() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::PASSWORD);
    }
    
    ///Return the account TLS port
    int Account::bootstrapPort() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::DHT::PORT).toInt();
    }
    
    ///Return the account TLS certificate authority list file
    Certificate* Account::tlsCaListCertificate() const
    {
       if (!d_ptr->m_pCaCert) {
          const QString& path = d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::CA_LIST_FILE);
          if (path.isEmpty())
             return nullptr;
          d_ptr->m_pCaCert = CertificateModel::instance().getCertificateFromPath(path,Certificate::Type::AUTHORITY);
          connect(d_ptr->m_pCaCert,SIGNAL(changed()),d_ptr.data(),SLOT(slotUpdateCertificate()));
       }
       return d_ptr->m_pCaCert;
    }
    
    ///Return the account TLS certificate
    Certificate* Account::tlsCertificate() const
    {
       if (!d_ptr->m_pTlsCert) {
          const QString& path = d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE);
          if (path.isEmpty())
             return nullptr;
          d_ptr->m_pTlsCert = CertificateModel::instance().getCertificateFromPath(path,Certificate::Type::USER);
          connect(d_ptr->m_pTlsCert,SIGNAL(changed()),d_ptr.data(),SLOT(slotUpdateCertificate()));
       }
       return d_ptr->m_pTlsCert;
    }
    
    ///Return the account private key
    QString Account::tlsPrivateKey() const
    {
       return tlsCertificate() ? tlsCertificate()->privateKeyPath() : QString();
    }
    
    ///Return the account TLS server name
    QString Account::tlsServerName() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::SERVER_NAME);
    }
    
    ///Return the account negotiation timeout in seconds
    int Account::tlsNegotiationTimeoutSec() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::NEGOTIATION_TIMEOUT_SEC).toInt();
    }
    
    ///Return the account TLS verify server
    bool Account::isTlsVerifyServer() const
    {
       return (d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::VERIFY_SERVER) IS_TRUE);
    }
    
    ///Return the account TLS verify client
    bool Account::isTlsVerifyClient() const
    {
       return (d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::VERIFY_CLIENT) IS_TRUE);
    }
    
    ///Return if it is required for the peer to have a certificate
    bool Account::isTlsRequireClientCertificate() const
    {
       return (d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::REQUIRE_CLIENT_CERTIFICATE) IS_TRUE);
    }
    
    ///Return the account TLS security is enabled
    bool Account::isTlsEnabled() const
    {
       return protocol() == Account::Protocol::RING || (d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::ENABLED) IS_TRUE);
    }
    
    ///Return if the ringtone are enabled
    bool Account::isRingtoneEnabled() const
    {
       return (d_ptr->accountDetail(DRing::Account::ConfProperties::Ringtone::ENABLED) IS_TRUE);
    }
    
    ///Return the account ringtone path
    QString Account::ringtonePath() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Ringtone::PATH);
    }
    
    ///Return the last error message received
    QString Account::lastErrorMessage() const
    {
       return statusModel()->lastErrorMessage();
    }
    
    ///Return the last error code (useful for debugging)
    int Account::lastErrorCode() const
    {
       return statusModel()->lastErrorCode();
    }
    
    ///Get the last transport error code, this is used to debug why registration failed
    int Account::lastTransportErrorCode() const
    {
       return d_ptr->m_LastTransportCode;
    }
    
    ///Get the last transport error message, this is used to debug why registration failed
    QString Account::lastTransportErrorMessage() const
    {
       return d_ptr->m_LastTransportMessage;
    }
    
    ///Return the account local port
    int Account::localPort() const
    {
       switch (protocol()) {
          case Account::Protocol::SIP:
             if (isTlsEnabled())
                return d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::LISTENER_PORT).toInt();
             else
                return d_ptr->accountDetail(DRing::Account::ConfProperties::LOCAL_PORT).toInt();
          case Account::Protocol::RING:
             return d_ptr->accountDetail(DRing::Account::ConfProperties::TLS::LISTENER_PORT).toInt();
          case Account::Protocol::COUNT__:
             break;
       };
       return 0;
    }
    
    ///Return the number of voicemails
    int Account::voiceMailCount() const
    {
       return d_ptr->m_VoiceMailCount;
    }
    
    ///Return the account registration status
    Account::RegistrationState Account::registrationState() const
    {
       return d_ptr->m_RegistrationState;
    }
    
    ///Return the last account SIP registration status
    QString Account::lastSipRegistrationStatus() const
    {
       return d_ptr->m_LastSipRegistrationStatus;
    }
    
    ///Return the account type
    Account::Protocol Account::protocol() const
    {
       const QString str = d_ptr->accountDetail(DRing::Account::ConfProperties::TYPE);
    
       if (str.isEmpty() || str == DRing::Account::ProtocolNames::SIP)
          return Account::Protocol::SIP;
       else if (str == DRing::Account::ProtocolNames::RING)
          return Account::Protocol::RING;
       qDebug() << "Warning: unhandled protocol name" << str << ", defaulting to SIP";
       return Account::Protocol::SIP;
    }
    
    ///Return the contact method associated with this account
    ContactMethod* Account::contactMethod() const
    {
       return d_ptr->m_pAccountNumber;
    }
    
    ///Return the DTMF type
    DtmfType Account::DTMFType() const
    {
       QString type = d_ptr->accountDetail(DRing::Account::ConfProperties::DTMF_TYPE);
       return (type == "overrtp" || type.isEmpty())? DtmfType::OverRtp:DtmfType::OverSip;
    }
    
    bool Account::presenceStatus() const
    {
       return d_ptr->m_pAccountNumber->isPresent();
    }
    
    QString Account::presenceMessage() const
    {
       return d_ptr->m_pAccountNumber->presenceMessage();
    }
    
    bool Account::supportPresencePublish() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Presence::SUPPORT_PUBLISH) IS_TRUE;
    }
    
    bool Account::supportPresenceSubscribe() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Presence::SUPPORT_SUBSCRIBE) IS_TRUE;
    }
    
    bool Account::presenceEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Presence::ENABLED) IS_TRUE;
    }
    
    bool Account::isVideoEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Video::ENABLED) IS_TRUE;
    }
    
    int Account::videoPortMax() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Video::PORT_MAX).toInt();
    }
    
    int Account::videoPortMin() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Video::PORT_MIN).toInt();
    }
    
    int Account::audioPortMin() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Audio::PORT_MIN).toInt();
    }
    
    int Account::audioPortMax() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::Audio::PORT_MAX).toInt();
    }
    
    bool Account::isUpnpEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::UPNP_ENABLED) IS_TRUE;
    }
    
    bool Account::hasCustomUserAgent() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::HAS_CUSTOM_USER_AGENT) IS_TRUE;
    }
    
    QString Account::userAgent() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::USER_AGENT);
    }
    
    bool Account::useDefaultPort() const
    {
       return d_ptr->m_UseDefaultPort;
    }
    
    bool Account::isTurnEnabled() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::ENABLED) IS_TRUE;
    }
    
    QString Account::turnServer() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER);
    }
    
    QString Account::turnServerUsername() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_UNAME);
    }
    
    QString Account::turnServerPassword() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_PWD);
    }
    
    QString Account::turnServerRealm() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::TURN::SERVER_REALM);
    }
    
    bool Account::hasProxy() const
    {
       return proxy().size();
    }
    
    QString Account::displayName() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::DISPLAYNAME);
    }
    
    QString Account::archivePassword() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ARCHIVE_PASSWORD);
    }
    
    QString Account::archivePin() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ARCHIVE_PIN);
    }
    
    bool Account::allowIncomingFromUnknown() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::DHT::PUBLIC_IN_CALLS) IS_TRUE;
    }
    
    bool Account::allowIncomingFromHistory() const
    {
       if (protocol() != Account::Protocol::RING)
          return false;
    
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY) IS_TRUE;
    }
    
    bool Account::allowIncomingFromContact() const
    {
       if (protocol() != Account::Protocol::RING)
          return false;
    
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT) IS_TRUE;
    }
    
    int Account::activeCallLimit() const
    {
       return d_ptr->accountDetail(DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT).toInt();
    }
    
    bool Account::hasActiveCallLimit() const
    {
       return activeCallLimit() > -1;
    }
    
    bool Account::exportOnRing(const QString& password) const
    {
        return ConfigurationManager::instance().exportOnRing(id(), password);
    }
    
    
    #define CAST(item) static_cast<int>(item)
    QVariant Account::roleData(int role) const
    {
       switch(role) {
          //Generic
          case Qt::DisplayRole:
          case Qt::EditRole:
          case CAST(Ring::Role::Name):
             return alias();
          case Qt::CheckStateRole:
             return QVariant(isEnabled() ? Qt::Checked : Qt::Unchecked);
          case Qt::BackgroundRole:
             return stateColor();
          case Qt::DecorationRole:
             return GlobalInstances::accountListColorizer().icon(this);
    
          //Specialized
          case CAST(Account::Role::Alias):
             return alias();
          case CAST(Account::Role::Proto):
             return CAST(protocol());
          case CAST(Account::Role::Hostname):
             return hostname();
          case CAST(Account::Role::Username):
             return username();
          case CAST(Account::Role::Mailbox):
             return mailbox();
          case CAST(Account::Role::Proxy):
             return proxy();
    //       case Password:
    //          return accountPassword();
          case CAST(Account::Role::TlsPassword):
             return tlsPassword();
          case CAST(Account::Role::TlsCaListCertificate):
             return tlsCaListCertificate()?tlsCaListCertificate()->path():QVariant();
          case CAST(Account::Role::TlsCertificate):
             return tlsCertificate()?tlsCertificate()->path():QVariant();
          case CAST(Account::Role::TlsPrivateKey):
            return tlsPrivateKey();
          case CAST(Account::Role::TlsServerName):
             return tlsServerName();
          case CAST(Account::Role::SipStunServer):
             return sipStunServer();
          case CAST(Account::Role::PublishedAddress):
             return publishedAddress();
          case CAST(Account::Role::RingtonePath):
             return ringtonePath();
          case CAST(Account::Role::RegistrationExpire):
             return registrationExpire();
          case CAST(Account::Role::TlsNegotiationTimeoutSec):
             return tlsNegotiationTimeoutSec();
          case CAST(Account::Role::LocalPort):
             return localPort();
          case CAST(Account::Role::BootstrapPort):
             return bootstrapPort();
          case CAST(Account::Role::PublishedPort):
             return publishedPort();
          case CAST(Account::Role::Enabled):
             return isEnabled();
          case CAST(Account::Role::AutoAnswer):
             return isAutoAnswer();
          case CAST(Account::Role::TlsVerifyServer):
             return isTlsVerifyServer();
          case CAST(Account::Role::TlsVerifyClient):
             return isTlsVerifyClient();
          case CAST(Account::Role::TlsRequireClientCertificate):
             return isTlsRequireClientCertificate();
          case CAST(Account::Role::TlsEnabled):
             return isTlsEnabled();
          case CAST(Account::Role::SrtpRtpFallback):
             return isSrtpRtpFallback();
          case CAST(Account::Role::SipStunEnabled):
             return isSipStunEnabled();
          case CAST(Account::Role::PublishedSameAsLocal):
             return isPublishedSameAsLocal();
          case CAST(Account::Role::RingtoneEnabled):
             return isRingtoneEnabled();
          case CAST(Account::Role::dTMFType):
             return DTMFType();
          case CAST(Account::Role::Id):
             return id();
          case CAST(Ring::Role::Object):
          case CAST(Account::Role::Object): {
             QVariant var;
             var.setValue(const_cast<Account*>(this));
             return var;
          }
          case CAST(Account::Role::TypeName):
             return CAST(protocol());
          case CAST(Account::Role::PresenceStatus):
             return PresenceStatusModel::instance().currentStatus();
          case CAST(Account::Role::PresenceMessage):
             return PresenceStatusModel::instance().currentMessage();
          case CAST(Account::Role::RegistrationState):
             return QVariant::fromValue(registrationState());
          case CAST(Account::Role::UsedForOutgogingCall):
             return isUsedForOutgogingCall();
          case CAST(Account::Role::TotalCallCount):
             return totalCallCount();
          case CAST(Account::Role::WeekCallCount):
             return weekCallCount();
          case CAST(Account::Role::TrimesterCallCount):
             return trimesterCallCount();
          case CAST(Ring::Role::LastUsed):
          case CAST(Account::Role::LastUsed):
             return (int)lastUsed();
          case CAST(Account::Role::UserAgent):
             return userAgent();
          case CAST(Account::Role::Password):
             return password();
          case CAST(Account::Role::SupportPresencePublish   ):
             return supportPresencePublish();
          case CAST(Account::Role::SupportPresenceSubscribe ):
             return supportPresenceSubscribe();
          case CAST(Account::Role::PresenceEnabled          ):
             return presenceEnabled();
          case CAST(Account::Role::IsVideoEnabled           ):
             return isVideoEnabled();
          case CAST(Account::Role::VideoPortMax             ):
             return videoPortMax();
          case CAST(Account::Role::VideoPortMin             ):
             return videoPortMin();
          case CAST(Account::Role::AudioPortMin             ):
             return audioPortMin();
          case CAST(Account::Role::AudioPortMax             ):
             return audioPortMax();
          case CAST(Account::Role::IsUpnpEnabled            ):
             return isUpnpEnabled();
          case CAST(Account::Role::HasCustomUserAgent       ):
             return hasCustomUserAgent();
          case CAST(Account::Role::LastTransportErrorCode   ):
             return lastTransportErrorCode();
          case CAST(Account::Role::LastTransportErrorMessage):
             return lastTransportErrorMessage();
          case CAST(Account::Role::TurnServer               ):
             return turnServer();
          case CAST(Account::Role::TurnServerEnabled        ):
             return isTurnEnabled();
          case CAST(Account::Role::TurnServerUsername       ):
             return turnServerUsername();
          case CAST(Account::Role::TurnServerPassword       ):
             return turnServerPassword();
          case CAST(Account::Role::TurnServerRealm          ):
             return turnServerRealm();
          case CAST(Account::Role::HasProxy                 ):
             return hasProxy();
          case CAST(Account::Role::DisplayName              ):
             return displayName();
          case CAST(Account::Role::SrtpEnabled              ):
             return isSrtpEnabled();
          case CAST(Account::Role::HasCustomBootstrap       ):
             //Do not create the model for nothing
             return protocol() == Account::Protocol::RING ? bootstrapModel()->isCustom() : false;
          case CAST(Account::Role::CredentialModel            ):
             return QVariant::fromValue(credentialModel());
          case CAST(Account::Role::CodecModel                 ):
             return QVariant::fromValue(codecModel());
          case CAST(Account::Role::KeyExchangeModel           ):
             return QVariant::fromValue(keyExchangeModel());
          case CAST(Account::Role::CipherModel                ):
             return QVariant::fromValue(cipherModel());
          case CAST(Account::Role::StatusModel                ):
             return QVariant::fromValue(statusModel());
          case CAST(Account::Role::SecurityEvaluationModel    ):
             return QVariant::fromValue(securityEvaluationModel());
          case CAST(Account::Role::TlsMethodModel             ):
             return QVariant::fromValue(tlsMethodModel());
          case CAST(Account::Role::ProtocolModel              ):
             return QVariant::fromValue(protocolModel());
          case CAST(Account::Role::BootstrapModel             ):
             return QVariant::fromValue(bootstrapModel());
          case CAST(Account::Role::NetworkInterfaceModel      ):
             return QVariant::fromValue(networkInterfaceModel());
          case CAST(Account::Role::KnownCertificateModel      ):
             return QVariant::fromValue(knownCertificateModel());
          case CAST(Account::Role::BannedCertificatesModel    ):
             return QVariant::fromValue(bannedCertificatesModel());
          case CAST(Account::Role::AllowedCertificatesModel   ):
             return QVariant::fromValue(allowedCertificatesModel());
          case CAST(Account::Role::AllowIncomingFromHistory ):
             return allowIncomingFromHistory();
          case CAST(Account::Role::AllowIncomingFromContact ):
             return allowIncomingFromContact();
          case CAST(Account::Role::AllowIncomingFromUnknown ):
             return allowIncomingFromUnknown();
          case CAST(Account::Role::ActiveCallLimit):
             return activeCallLimit();
          case CAST(Account::Role::HasActiveCallLimit):
             return hasActiveCallLimit();
          case CAST(Account::Role::SecurityLevel):
             if (extension<SecurityEvaluationExtension>()) {
                return QVariant::fromValue(
                   extension<SecurityEvaluationExtension>()->securityLevel(this)
                );
             };
             break;
          case CAST(Account::Role::SecurityLevelIcon):
             if (extension<SecurityEvaluationExtension>()) {
                return extension<SecurityEvaluationExtension>()->securityLevelIcon(this);
             }
             break;
          case CAST(Account::Role::LastStatusChangeTimeStamp):
             return QVariant::fromValue(statusModel()->lastTimeStamp());
          case CAST(Account::Role::RegisteredName):
             return registeredName();
          default:
             return QVariant();
       }
       return QVariant();
    }
    #undef CAST
    
    
    bool Account::supportScheme( URI::SchemeType type ) const
    {
       switch(type) {
          case URI::SchemeType::NONE :
             if (protocol() == Account::Protocol::RING)
                /* the URIs which are supported by accounts of type RING are well
                 * defined and should always be identified correctly, thus URIs
                 * which are not identified to be of a specific type cannot possibly
                 * be of type RING and thus should never be used to make a RING call
                 */
                return false;
             return true;
             break;
          case URI::SchemeType::SIP  :
          case URI::SchemeType::SIPS :
             if (protocol() == Account::Protocol::SIP)
                return true;
             break;
          case URI::SchemeType::RING :
             if (protocol() == Account::Protocol::RING)
                return true;
             break;
          case URI::SchemeType::COUNT__:
             break;
       }
       return false;
    }
    
    bool Account::allowCertificate(Certificate* c)
    {
       if (protocol() != Account::Protocol::RING)
          return false;
    
       return CertificateModel::instance().d_ptr->allowCertificate(c, this);
    }
    
    bool Account::banCertificate(Certificate* c)
    {
       if (protocol() != Account::Protocol::RING)
          return false;
    
       return CertificateModel::instance().d_ptr->banCertificate(c, this);
    }
    
    ///Ask the certificate owner (peer) to trust you
    bool Account::sendContactRequest( const URI& uri )
    {
       if (uri.isEmpty())
           return false;
    
       QByteArray payload;
    
       // Send our VCard as payload
       if (contactMethod() && contactMethod()->contact()) {
          payload = contactMethod()->contact()->toVCard();
       }
    
       ConfigurationManager::instance().sendTrustRequest(id(), uri, payload);
    
       return true;
    }
    
    bool Account::sendContactRequest(const ContactMethod* c)
    {
        if (!c)
            return false;
    
        return sendContactRequest(c->uri());
    }
    
    bool Account::sendContactRequest( Certificate* c )
    {
       if ((!c) || (c->remoteId().isEmpty()))
          return false;
    
       QByteArray payload;
    
       if (contactMethod() && contactMethod()->contact()) {
          payload = contactMethod()->contact()->toVCard();
       }
    
       ConfigurationManager::instance().sendTrustRequest(id(),c->remoteId(), payload);
    
       return true;
    }
    
    uint Account::internalId() const
    {
       return d_ptr->m_InternalId;
    }
    
    
    /*****************************************************************************
     *                                                                           *
     *                                  Setters                                  *
     *                                                                           *
     ****************************************************************************/
    
    ///Set account details
    void AccountPrivate::setAccountProperties(const QHash<QString,QString>& m)
    {
       m_hAccountDetails.clear();
       m_hAccountDetails = m;
       m_HostName = m[DRing::Account::ConfProperties::HOSTNAME];
    }
    
    ///Set a specific detail
    bool AccountPrivate::setAccountProperty(const QString& param, const QString& val)
    {
       const QString buf = m_hAccountDetails[param];
       const bool accChanged = buf != val;
       //Status can be changed regardless of the EditState
       //TODO make this more generic for volatile properties
       if (param == DRing::Account::ConfProperties::Registration::STATUS) {
          m_hAccountDetails[param] = val;
          if (accChanged) {
             emit q_ptr->changed(q_ptr);
             emit q_ptr->propertyChanged(q_ptr,param,val,buf);
          }
       }
       else if (accChanged) {
    
          m_hAccountDetails[param] = val;
          emit q_ptr->changed(q_ptr);
          emit q_ptr->propertyChanged(q_ptr,param,val,buf);
    
          q_ptr->performAction(Account::EditAction::MODIFY);
       }
       return m_CurrentState == Account::EditState::MODIFIED_COMPLETE
        || m_CurrentState == Account::EditState::MODIFIED_INCOMPLETE
        || m_CurrentState == Account::EditState::NEW;
    }
    
    bool Account::setAccountProperty(const QString& param, const QString& val)
    {
        return d_ptr->setAccountProperty(param, val);
    }
    
    ///Set the account id
    void Account::setId(const QByteArray& id)
    {
       if (! isNew())
          qDebug() << "Error : setting AccountId of an existing account" << d_ptr->m_AccountId;
       d_ptr->m_AccountId = id;
    }
    
    ///Set the account type, SIP or RING
    void Account::setProtocol(Account::Protocol proto)
    {
       //TODO prevent this if the protocol has been saved
       switch (proto) {
          case Account::Protocol::SIP:
             d_ptr->setAccountProperty(DRing::Account::ConfProperties::TYPE ,DRing::Account::ProtocolNames::SIP );
             break;
          case Account::Protocol::RING:
             d_ptr->setAccountProperty(DRing::Account::ConfProperties::TYPE ,DRing::Account::ProtocolNames::RING);
             break;
          case Account::Protocol::COUNT__:
             break;
       };
    }
    
    ///The set account hostname, it can be an hostname or an IP address
    void Account::setHostname(const QString& detail)
    {
       if (d_ptr->m_HostName != detail) {
          d_ptr->m_HostName = detail;
          if (protocol() == Account::Protocol::RING)
          {
              bootstrapModel() << BootstrapModel::EditAction::RELOAD;
          }
          d_ptr->setAccountProperty(DRing::Account::ConfProperties::HOSTNAME, detail);
       }
    }
    
    
    ///Set the account registeredName
    bool Account::registerName(const QString& password, const QString& name) const
    {
        return NameDirectory::instance().registerName(this, password, name);
    }
    
    //Lookup a name
    bool Account::lookupName(const QString& name) const
    {
        return NameDirectory::instance().lookupName(this, QString(), name);
    }
    
    //Lookup an address
    bool Account::lookupAddress(const QString& address) const
    {
        return NameDirectory::instance().lookupAddress(this, QString(), address);
    }
    
    ///Set the account username, everything is valid, some might be rejected by the PBX server
    void Account::setUsername(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::USERNAME, detail);
       switch (protocol()) {
          case Account::Protocol::RING:
          case Account::Protocol::COUNT__:
             //nothing to do
             break;
          case Account::Protocol::SIP:
             if (credentialModel()->primaryCredential(Credential::Type::SIP)) {
                credentialModel()->primaryCredential(Credential::Type::SIP)->setUsername(detail);
                credentialModel() << CredentialModel::EditAction::MODIFY;
             }
             else {
                const QModelIndex idx = credentialModel()->addCredentials(Credential::Type::SIP);
                credentialModel()->setData(idx,detail,CredentialModel::Role::NAME);
             }
             break;
       };
    }
    
    ///Set the account mailbox, usually a number, but can be anything
    void Account::setMailbox(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::MAILBOX, detail);
    }
    
    ///Set the account mailbox, usually a number, but can be anything
    void Account::setProxy(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ROUTE, detail);
    }
    
    //Set the name service URL
    void Account::setNameServiceURL(const QString& detail)
    {
        d_ptr->setAccountProperty(DRing::Account::ConfProperties::RingNS::URI, detail);
    }
    
    ///Set the main credential password
    void Account::setPassword(const QString& detail)
    {
       switch (protocol()) {
          case Account::Protocol::SIP:
             if (credentialModel()->primaryCredential(Credential::Type::SIP)) {
                credentialModel()->primaryCredential(Credential::Type::SIP)->setPassword(detail);
                credentialModel() << CredentialModel::EditAction::MODIFY;
             }
             else {
                const QModelIndex idx = credentialModel()->addCredentials(Credential::Type::SIP);
                credentialModel()->setData(idx,detail,CredentialModel::Role::PASSWORD);
             }
             break;
          case Account::Protocol::RING:
             setTlsPassword(detail);
             break;
          case Account::Protocol::COUNT__:
             break;
       };
    }
    
    ///Set the TLS (encryption) password
    void Account::setTlsPassword(const QString& detail)
    {
       auto cert = tlsCertificate();
       if (!cert)
          return;
       cert->setPrivateKeyPassword(detail);
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::PASSWORD, detail);
       d_ptr->regenSecurityValidation();
    }
    
    ///Set the certificate authority list file
    void Account::setTlsCaListCertificate(const QString& path)
    {
       Certificate* cert = CertificateModel::instance().getCertificateFromPath(path);
       setTlsCaListCertificate(cert);
    }
    
    ///Set the certificate
    void Account::setTlsCertificate(const QString& path)
    {
       Certificate* cert = CertificateModel::instance().getCertificateFromPath(path);
       setTlsCertificate(cert);
    }
    
    ///Set the private key
    void Account::setTlsPrivateKey(const QString& path)
    {
        auto cert = tlsCertificate();
        if (!cert)
            return;
    
        cert->setPrivateKeyPath(path);
        d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE, cert?path:QString());
        d_ptr->regenSecurityValidation();
    }
    
    ///Set the certificate authority list file
    void Account::setTlsCaListCertificate(Certificate* cert)
    {
       //FIXME it can be a list of multiple certificates
       //this code currently only handle the case where is there is exactly one
    
       cert->setRequireStrictPermission(false);
    
       //All calls from the same top level CA are always accepted
       allowCertificate(cert);
    
       d_ptr->m_pCaCert = cert;
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::CA_LIST_FILE, cert?cert->path():QString());
       d_ptr->regenSecurityValidation();
    
       if (d_ptr->m_cTlsCaCert)
          disconnect(d_ptr->m_cTlsCaCert);
    
       if (cert) {
          d_ptr->m_cTlsCaCert = connect(cert, &Certificate::changed,[this]() {
             d_ptr->regenSecurityValidation();
          });
       }
    
    }
    
    ///Set the certificate
    void Account::setTlsCertificate(Certificate* cert)
    {
       //The private key will be required for this certificate
       cert->setRequirePrivateKey(true);
    
       d_ptr->m_pTlsCert = cert;
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE, cert?cert->path():QString());
       d_ptr->regenSecurityValidation();
    }
    
    ///Set the TLS server
    void Account::setTlsServerName(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::SERVER_NAME, detail);
       d_ptr->regenSecurityValidation();
    }
    
    ///Set the stun server
    void Account::setSipStunServer(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::STUN::SERVER, detail);
    }
    
    ///Set the published address
    void Account::setPublishedAddress(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::PUBLISHED_ADDRESS, detail);
    }
    
    ///Set the ringtone path, it have to be a valid absolute path
    void Account::setRingtonePath(const QString& detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Ringtone::PATH, detail);
    }
    
    ///Set the number of voice mails
    void Account::setVoiceMailCount(int count)
    {
       d_ptr->m_VoiceMailCount = count;
    }
    
    ///Set the account timeout, it will be renegotiated when that timeout occur
    void Account::setRegistrationExpire(int detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Registration::EXPIRE, QString::number(detail));
    }
    
    ///Set TLS negotiation timeout in second
    void Account::setTlsNegotiationTimeoutSec(int detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::NEGOTIATION_TIMEOUT_SEC, QString::number(detail));
       d_ptr->regenSecurityValidation();
    }
    
    ///Set the local port for SIP/RING communications
    void Account::setLocalPort(unsigned short detail)
    {
       switch (protocol()) {
          case Account::Protocol::SIP:
             if (isTlsEnabled())
                d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::LISTENER_PORT, QString::number(detail));
             else
                d_ptr->setAccountProperty(DRing::Account::ConfProperties::LOCAL_PORT, QString::number(detail));
          case Account::Protocol::RING:
             d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::LISTENER_PORT, QString::number(detail));
             break;
          case Account::Protocol::COUNT__:
             break;
       };
    }
    
    ///Set the TLS listener port (0-2^16)
    void Account::setBootstrapPort(unsigned short detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::DHT::PORT, QString::number(detail));
    }
    
    ///Set the published port (0-2^16)
    void Account::setPublishedPort(unsigned short detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::PUBLISHED_PORT, QString::number(detail));
    }
    
    ///Set if the account is enabled or not
    void Account::setEnabled(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ENABLED, (detail)TO_BOOL);
    }
    
    ///Set if the account should auto answer
    void Account::setAutoAnswer(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::AUTOANSWER, (detail)TO_BOOL);
    }
    
    ///Set the TLS verification server
    void Account::setTlsVerifyServer(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::VERIFY_SERVER, (detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    ///Set the TLS verification client
    void Account::setTlsVerifyClient(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::VERIFY_CLIENT, (detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    ///Set if the peer need to be providing a certificate
    void Account::setTlsRequireClientCertificate(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::REQUIRE_CLIENT_CERTIFICATE ,(detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    ///Set if the security settings are enabled
    void Account::setTlsEnabled(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TLS::ENABLED ,(detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    void Account::setSrtpRtpFallback(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::SRTP::RTP_FALLBACK, (detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    void Account::setSrtpEnabled(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::SRTP::ENABLED, (detail)TO_BOOL);
       d_ptr->regenSecurityValidation();
    }
    
    void Account::setSipStunEnabled(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::STUN::ENABLED, (detail)TO_BOOL);
    }
    
    /**
     * Set if the published address is the same as the local IP address
     * @see Account::setPublishedAddress
     * @see Account::publishedAddress
     */
    void Account::setPublishedSameAsLocal(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::PUBLISHED_SAMEAS_LOCAL, (detail)TO_BOOL);
    }
    
    ///Set if custom ringtone are enabled
    void Account::setRingtoneEnabled(bool detail)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Ringtone::ENABLED, (detail)TO_BOOL);
    }
    
    /**
     * Set if the account broadcast its presence data
     *
     * @note This only works when the account support presence
     * @see Account::supportPresencePublish()
     */
    void Account::setPresenceEnabled(bool enable)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Presence::ENABLED, (enable)TO_BOOL);
       emit presenceEnabledChanged(enable);
    }
    
    ///Use video by default when available
    void Account::setVideoEnabled(bool enable)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Video::ENABLED, (enable)TO_BOOL);
    }
    
    /**Set the maximum audio port
     * This can be used when some routers without UPnP support open a narrow range
     * of ports to allow the stream to go through.
     */
    void Account::setAudioPortMax(int port )
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Audio::PORT_MAX, QString::number(port));
    }
    
    /**Set the minimum audio port
     * This can be used when some routers without UPnP support open a narrow range
     * of ports to allow the stream to go through.
     */
    void Account::setAudioPortMin(int port )
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Audio::PORT_MIN, QString::number(port));
    }
    
    /**Set the maximum video port
     * This can be used when some routers without UPnP support open a narrow range
     * of ports to allow the stream to go through.
     */
    void Account::setVideoPortMax(int port )
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Video::PORT_MAX, QString::number(port));
    }
    
    /**Set the minimum video port
     * This can be used when some routers without UPnP support open a narrow range
     * of ports to allow the stream to go through.
     */
    void Account::setVideoPortMin(int port )
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::Video::PORT_MIN, QString::number(port));
    }
    
    void Account::setUpnpEnabled(bool enable)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::UPNP_ENABLED, (enable)TO_BOOL);
    }
    
    void Account::setHasCustomUserAgent(bool enable)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::HAS_CUSTOM_USER_AGENT, (enable)TO_BOOL);
    }
    
    ///TODO implement the "use default" logic correctly
    /**Set the user agent
     * If the string is unchanged, the daemon should upgrade it automatically
     */
    void Account::setUserAgent(const QString& agent)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::USER_AGENT, agent);
    }
    
    void Account::setUseDefaultPort(bool value)
    {
       if (value) {
          switch (protocol()) {
             case Account::Protocol::SIP:
                setLocalPort(5060); //FIXME check is TLS is used
                break;
             case Account::Protocol::RING:
                setLocalPort(5061);
                break;
             case Account::Protocol::COUNT__:
                break;
          };
       }
       d_ptr->m_UseDefaultPort = value;
    }
    
    void Account::setTurnEnabled(bool value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::ENABLED, (value)TO_BOOL);
    }
    
    void Account::setTurnServer(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER, value);
    }
    
    void Account::setTurnServerUsername(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_UNAME, value);
    }
    
    void Account::setTurnServerPassword(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_PWD, value);
    }
    
    void Account::setTurnServerRealm(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::TURN::SERVER_REALM, value);
    }
    
    void Account::setDisplayName(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::DISPLAYNAME, value);
    }
    
    void Account::setArchivePassword(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ARCHIVE_PASSWORD, value);
    }
    
    void Account::setArchivePin(const QString& value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ARCHIVE_PIN, value);
    }
    
    void Account::setAllowIncomingFromUnknown(bool value)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::DHT::PUBLIC_IN_CALLS, (value)TO_BOOL);
    }
    
    void Account::setAllowIncomingFromHistory(bool value)
    {
       if (protocol() != Account::Protocol::RING)
          return;
    
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY, value TO_BOOL);
       performAction(Account::EditAction::MODIFY);
    }
    
    void Account::setAllowIncomingFromContact(bool value)
    {
       if (protocol() != Account::Protocol::RING)
          return;
    
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT, value TO_BOOL);
       performAction(Account::EditAction::MODIFY);
    }
    
    void Account::setActiveCallLimit(int value )
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, QString::number(value));
    }
    
    void Account::setHasActiveCallLimit(bool value )
    {
       if ((!value) && activeCallLimit() != -1)
          return;
    
       setActiveCallLimit(value?1:-1);
    }
    
    ///Set the DTMF type
    void Account::setDTMFType(DtmfType type)
    {
       d_ptr->setAccountProperty(DRing::Account::ConfProperties::DTMF_TYPE,(type==OverRtp)?"overrtp":"oversip");
    }
    
    void Account::setProfile(Profile* p)
    {
        if (!p) {
            qWarning() << "Cannot set profile to null as all accounts must belong to a profile";
            return;
        }
    
        if (p == d_ptr->m_pProfile)
            return; // nothing to do
    
        if (d_ptr->m_pProfile)
            d_ptr->m_pProfile->removeAccount(this);
    
        if (p->addAccount(this))
            p->save();
    
        d_ptr->m_pProfile = p;
    
        emit changed(this);
    }
    
    Profile* Account::profile() const
    {
       // Make sure all accounts belong to a profile
       if (!d_ptr->m_pProfile) {
          Profile* p = ProfileModel::instance().selectedProfile();
    
          if (!p) // for now default to the first profile
             p = ProfileModel::instance().getProfile(ProfileModel::instance().index(0,0));
    
          if (!p)
             return nullptr;
    
          // Use a const cast rather than a mutable to make sure the logic is the
          // same between "automatic" default profile" and the setProfile
          // implementation.
          const_cast<Account*>(this)->setProfile(p);
       }
    
       return d_ptr->m_pProfile;
    }
    
    void Account::setLastSipRegistrationStatus(const QString& value )
    {
        d_ptr->m_LastSipRegistrationStatus = value;
    }
    
    void Account::setLastTransportCode(int value)
    {
        d_ptr->m_LastTransportCode = value;
    }
    
    void Account::setLastTransportMessage(const QString& value)
    {
        d_ptr->m_LastTransportMessage = value;
    }
    
    void Account::setRegistrationState(const RegistrationState& value)
    {
        d_ptr->m_RegistrationState = value;
    }
    
    #define CAST(item) static_cast<int>(item)
    ///Proxy for AccountModel::setData
    void Account::setRoleData(int role, const QVariant& value)
    {
       switch(role) {
          case CAST(Account::Role::Alias):
             setAlias(value.toString());
             break;
          case CAST(Account::Role::Proto): {
             const int proto = value.toInt();
             setProtocol((proto>=0&&proto<=1)?static_cast<Account::Protocol>(proto):Account::Protocol::SIP);
             break;
          }
          case CAST(Account::Role::Hostname):
             setHostname(value.toString());
             break;
          case CAST(Account::Role::Username):
             setUsername(value.toString());
             break;
          case CAST(Account::Role::Mailbox):
             setMailbox(value.toString());
             break;
          case CAST(Account::Role::Proxy):
             setProxy(value.toString());
             break;
    //       case Password:
    //          accountPassword();
          case CAST(Account::Role::TlsPassword):
             setTlsPassword(value.toString());
             break;
          case CAST(Account::Role::TlsCaListCertificate): {
             setTlsCaListCertificate(value.toString());
             break;
          }
          case CAST(Account::Role::TlsCertificate): {
             setTlsCertificate(value.toString());
          }
             break;
          case CAST(Account::Role::TlsServerName):
             setTlsServerName(value.toString());
             break;
          case CAST(Account::Role::SipStunServer):
             setSipStunServer(value.toString());
             break;
          case CAST(Account::Role::PublishedAddress):
             setPublishedAddress(value.toString());
             break;
          case CAST(Account::Role::RingtonePath):
             setRingtonePath(value.toString());
             break;
          case CAST(Account::Role::RegistrationExpire):
             setRegistrationExpire(value.toInt());
             break;
          case CAST(Account::Role::TlsNegotiationTimeoutSec):
             setTlsNegotiationTimeoutSec(value.toInt());
             break;
          case CAST(Account::Role::LocalPort):
             setLocalPort(value.toInt());
             break;
          case CAST(Account::Role::BootstrapPort):
             setBootstrapPort(value.toInt());
             break;
          case CAST(Account::Role::PublishedPort):
             setPublishedPort(value.toInt());
             break;
          case CAST(Account::Role::Enabled):
             setEnabled(value.toBool());
             break;
          case CAST(Account::Role::AutoAnswer):
             setAutoAnswer(value.toBool());
             break;
          case CAST(Account::Role::TlsVerifyServer):
             setTlsVerifyServer(value.toBool());
             break;
          case CAST(Account::Role::TlsVerifyClient):
             setTlsVerifyClient(value.toBool());
             break;
          case CAST(Account::Role::TlsRequireClientCertificate):
             setTlsRequireClientCertificate(value.toBool());
             break;
          case CAST(Account::Role::TlsEnabled):
             setTlsEnabled(value.toBool());
             break;
          case CAST(Account::Role::SrtpRtpFallback):
             setSrtpRtpFallback(value.toBool());
             break;
          case CAST(Account::Role::SipStunEnabled):
             setSipStunEnabled(value.toBool());
             break;
          case CAST(Account::Role::PublishedSameAsLocal):
             setPublishedSameAsLocal(value.toBool());
             break;
          case CAST(Account::Role::RingtoneEnabled):
             setRingtoneEnabled(value.toBool());
             break;
          case CAST(Account::Role::dTMFType):
             setDTMFType((DtmfType)value.toInt());
             break;
          case CAST(Account::Role::Id):
             setId(value.toByteArray());
             break;
          case CAST(Account::Role::UserAgent):
             setUserAgent(value.toString());
             break;
          case CAST(Account::Role::Password):
             setPassword(value.toString());
             break;
          case CAST(Account::Role::SupportPresencePublish   ):
             break;
          case CAST(Account::Role::SupportPresenceSubscribe ):
             break;
          case CAST(Account::Role::PresenceEnabled          ):
             setPresenceEnabled(value.toBool());
             break;
          case CAST(Account::Role::IsVideoEnabled           ):
             setVideoEnabled(value.toBool());
             break;
          case CAST(Account::Role::VideoPortMax             ):
             setVideoPortMax(value.toInt());
             break;
          case CAST(Account::Role::VideoPortMin             ):
             setVideoPortMin(value.toInt());
             break;
          case CAST(Account::Role::AudioPortMin             ):
             setAudioPortMin(value.toInt());
             break;
          case CAST(Account::Role::AudioPortMax             ):
             setAudioPortMax(value.toInt());
             break;
          case CAST(Account::Role::IsUpnpEnabled            ):
             setUpnpEnabled(value.toBool());
             break;
          case CAST(Account::Role::HasCustomUserAgent       ):
             setHasCustomUserAgent(value.toBool());
             break;
          case CAST(Account::Role::LastTransportErrorCode   ):
             break;
          case CAST(Account::Role::LastTransportErrorMessage):
             break;
          case CAST(Account::Role::TurnServer               ):
             setTurnServer(value.toString());
             break;
          case CAST(Account::Role::DisplayName              ):
             setDisplayName(value.toString());
             break;
          case CAST(Account::Role::SrtpEnabled              ):
             setSrtpEnabled(value.toBool());
             break;
          case CAST(Account::Role::HasCustomBootstrap       ):
             //Do not create the model for nothing
             if (protocol() == Account::Protocol::RING && value.toBool())
                bootstrapModel()->reset();
             break;
          //Read-only
          case CAST(Account::Role::CredentialModel          ):
          case CAST(Account::Role::CodecModel               ):
          case CAST(Account::Role::KeyExchangeModel         ):
          case CAST(Account::Role::CipherModel              ):
          case CAST(Account::Role::StatusModel              ):
          case CAST(Account::Role::SecurityEvaluationModel  ):
          case CAST(Account::Role::TlsMethodModel           ):
          case CAST(Account::Role::ProtocolModel            ):
          case CAST(Account::Role::BootstrapModel           ):
          case CAST(Account::Role::NetworkInterfaceModel    ):
          case CAST(Account::Role::KnownCertificateModel    ):
          case CAST(Account::Role::BannedCertificatesModel  ):
          case CAST(Account::Role::AllowedCertificatesModel ):
             break;
          case CAST(Account::Role::AllowIncomingFromHistory ):
             setAllowIncomingFromHistory(value.toBool());
             break;
          case CAST(Account::Role::AllowIncomingFromContact ):
             setAllowIncomingFromContact(value.toBool());
             break;
          case CAST(Account::Role::AllowIncomingFromUnknown ):
             setAllowIncomingFromUnknown(value.toBool());
             break;
          case CAST(Account::Role::ActiveCallLimit):
             return setActiveCallLimit(value.toInt());
             break;
          case CAST(Account::Role::HasActiveCallLimit):
             return setHasActiveCallLimit(value.toBool());
             break;
          //Read-only
          case CAST(Account::Role::SecurityLevel):
          case CAST(Account::Role::SecurityLevelIcon):
             break;
          case CAST(Account::Role::TurnServerPassword):
           setTurnServerPassword(value.toString());
           break;
          case CAST(Account::Role::TurnServerRealm):
           setTurnServerRealm(value.toString());
           break;
          case CAST(Account::Role::TurnServerUsername):
           setTurnServerUsername(value.toString());
           break;
          case CAST(Account::Role::TurnServerEnabled):
           setTurnEnabled(value.toBool());
           break;
       }
    }
    #undef CAST
    
    
    /*****************************************************************************
     *                                                                           *
     *                                  Mutator                                  *
     *                                                                           *
     ****************************************************************************/
    
    void AccountPrivate::performAction(const Account::EditAction action)
    {
       (this->*(stateMachineActionsOnState[m_CurrentState][action]))();
    }
    
    /// anAccount << Call::EditAction::SAVE
    Account* Account::operator<<(Account::EditAction& action)
    {
       performAction(action);
       return this;
    }
    
    Account* operator<<(Account* a, Account::EditAction action)
    {
       return (!a)?nullptr : (*a) << action;
    }
    
    ///Change the current edition state
    bool Account::performAction(const Account::EditAction action)
    {
       Account::EditState curState = d_ptr->m_CurrentState;
       d_ptr->performAction(action);
       return curState != d_ptr->m_CurrentState;
    }
    
    ///Get the current account edition state
    Account::EditState Account::editState() const
    {
       return d_ptr->m_CurrentState;
    }
    
    /**
     * This method can be used to query if a field is available or not in current
     * context. This could be extended over time. For now, it only handle the fields
     * related to SDES.
     *
     * @todo Support of the private key password is necessary
     *
     * @param role An SDES related Account::Role
     * @return if the field is available in the current context
     */
    Account::RoleState Account::roleState(Account::Role role) const
    {
       #pragma GCC diagnostic push
       #pragma GCC diagnostic ignored "-Wswitch-enum"
       #pragma GCC diagnostic push
       #pragma GCC diagnostic ignored "-Wswitch"
    
       //Hide unsupported fields by protocol
       switch(protocol()) {
          case Account::Protocol::RING:
             switch(role) {
                case Account::Role::Password          :
                case Account::Role::RegistrationExpire:
                case Account::Role::Mailbox           :
                case Account::Role::UserAgent         :
                case Account::Role::HasCustomUserAgent:
                case Account::Role::HasProxy          :
                case Account::Role::Proxy             :
                case Account::Role::CipherModel       :
                case Account::Role::DisplayName       :
                   return Account::RoleState::UNAVAILABLE;
                case Account::Role::Username                :
                case Account::Role::TlsCaListCertificate    :
                case Account::Role::TlsCertificate          :
                case Account::Role::SrtpEnabled             :
                case Account::Role::TlsEnabled              :
                   return Account::RoleState::READ_ONLY;
                default:
                   break;
             }
    
             // The registered name cannot be changed once the account is created
             if (!isNew() && role == Account::Role::RegisteredName)
                 return Account::RoleState::READ_ONLY;
    
             break;
          case Account::Protocol::SIP     :
             switch(role) {
                case Account::Role::PresenceEnabled:
                   return supportPresenceSubscribe() || supportPresencePublish() ?
                      Account::RoleState::READ_WRITE : Account::RoleState::UNAVAILABLE;
             }
             [[clang::fallthrough]];
          case Account::Protocol::COUNT__ :
             switch(role) {
                case Account::Role::BannedCertificatesModel :
                case Account::Role::AllowedCertificatesModel:
                case Account::Role::AllowIncomingFromHistory:
                case Account::Role::AllowIncomingFromContact:
                case Account::Role::AllowIncomingFromUnknown:
                case Account::Role::RegisteredName          :
                case Account::Role::HasCustomBootstrap      :
                   return Account::RoleState::UNAVAILABLE;
             }
             break;
       }
    
       //Supported security fields
       enum class Fields {
          SDES_FALLBACK_RTP  ,
          COUNT__
       };
    
       //Mapping between the roles and the fields
       Fields f = Fields::COUNT__;
       switch(role) {
          case Account::Role::SrtpRtpFallback   :
             f = Fields::SDES_FALLBACK_RTP  ;
             break;
       }
       #pragma GCC diagnostic pop
       #pragma GCC diagnostic pop
    
       //The field is not possible to disable
       if (f == Fields::COUNT__)
          return Account::RoleState::READ_WRITE;
    
       constexpr static const Account::RoleState rw = Account::RoleState::READ_WRITE ;
       constexpr static const Account::RoleState un = Account::RoleState::UNAVAILABLE;
    
       //Matrix used to define if a field is enabled
       static const Matrix2D<KeyExchangeModel::Type, Fields, Account::RoleState>
       enabledFields={{
          /*                 ______________________> SDES_FALLBACK_RTP  */
          /*                /                                           */
          /*               \/                                           */
          /*Type::SDES*/ {{rw}},
          /*Type::NONE*/ {{un}},
       }};
    
       const QModelIndex idx = keyExchangeModel()->selectionModel()->currentIndex();
    
       if (!idx.isValid())
          return Account::RoleState::UNAVAILABLE;
    
       const KeyExchangeModel::Type type = qvariant_cast<KeyExchangeModel::Type>(
          idx.data(static_cast<int>(KeyExchangeModel::Role::TYPE))
       );
    
       return enabledFields[type][f];
    }
    
    /**
     * Get the **current** status of the role based on its content. ::roleState
     * check the field state based on protocol and context with ::roleStatus check
     * the content.
     */
    Account::RoleStatus Account::roleStatus(Account::Role role) const
    {
       //The validations are currently performed by the state machine
       return d_ptr->m_hRoleStatus[(int)role];
    }
    
    /**Update the account
     * @return if the state changed
     */
    bool AccountPrivate::updateState()
    {
       if(! q_ptr->isNew()) {
          ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
          const MapStringString details        = configurationManager.getVolatileAccountDetails(q_ptr->id());
          const QString         status         = details[DRing::Account::VolatileProperties::Registration::STATUS];
          const Account::RegistrationState cst = q_ptr->registrationState();
          const Account::RegistrationState st  = Account::fromDaemonName(status);
    
          setAccountProperty(DRing::Account::ConfProperties::Registration::STATUS, status); //Update -internal- object state
          m_RegistrationState = st;
    
          if (st != cst)
             emit q_ptr->stateChanged(q_ptr->registrationState());
    
          return st == cst;
       }
       return true;
    }
    
    bool Account::updateState()
    {
        return d_ptr->updateState();
    }
    
    ///Save the current account to the daemon
    void AccountPrivate::save()
    {
       ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
       if (q_ptr->isNew()) {
          MapStringString details;
          QMutableHashIterator<QString,QString> iter(m_hAccountDetails);
    
          while (iter.hasNext()) {
             iter.next();
             details[iter.key()] = iter.value();
          }
    
          //Clear the password
          q_ptr->setArchivePassword("");
    
          const QString currentId = configurationManager.addAccount(details);
    
          q_ptr->codecModel() << CodecModel::EditAction::RELOAD;
    
          q_ptr->setId(currentId.toLatin1());
       } //New account
       else { //Existing account
          MapStringString tmp;
          QMutableHashIterator<QString,QString> iter(m_hAccountDetails);
    
          while (iter.hasNext()) {
             iter.next();
             tmp[iter.key()] = iter.value();
          }
          configurationManager.setAccountDetails(q_ptr->id(), tmp);
          if (m_RemoteEnabledState != q_ptr->isEnabled()) {
             m_RemoteEnabledState = q_ptr->isEnabled();
             emit q_ptr->enabled(m_RemoteEnabledState);
          }
       }
    
       //Save the credentials if they changed
       q_ptr->credentialModel() << CredentialModel::EditAction::SAVE;
    
       if (!q_ptr->id().isEmpty()) {
          Account* acc =  AccountModel::instance().getById(q_ptr->id());
          if (acc != q_ptr) {
             qDebug() << "Adding the new account to the account list (" << q_ptr->id() << ")";
             AccountModel::instance().add(q_ptr);
          }
    
          q_ptr->performAction(Account::EditAction::RELOAD);
          updateState();
    
          changeState(Account::EditState::READY);
       }
    
       q_ptr->codecModel() << CodecModel::EditAction::SAVE;
    
       emit q_ptr->changed(q_ptr);
    }
    
    ///sync with the daemon, this need to be done manually to prevent reloading the account while it is being edited
    void AccountPrivate::reload()
    {
       if (!q_ptr->isNew()) {
          if (m_hAccountDetails.size())
             qDebug() << "Reloading" << q_ptr->id() << q_ptr->alias();
          else
             qDebug() << "Loading" << q_ptr->id();
          ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
          QMap<QString,QString> aDetails = configurationManager.getAccountDetails(q_ptr->id());
    
          if (!aDetails.count()) {
             qDebug() << "Account not found";
          }
          else {
             m_hAccountDetails.clear();
             //In case not all elements match, it is better to do a "merge"
             QMutableMapIterator<QString, QString> iter(aDetails);
             while (iter.hasNext()) {
                iter.next();
                m_hAccountDetails[iter.key()] = iter.value();
             }
    
             //Manually re-set elements that need extra business logic or caching
             q_ptr->setHostname(m_hAccountDetails[DRing::Account::ConfProperties::HOSTNAME]);
    
             const QString ca  (m_hAccountDetails[DRing::Account::ConfProperties::TLS::CA_LIST_FILE    ]);
             const QString cert(m_hAccountDetails[DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE]);
             const QString key (m_hAccountDetails[DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE]);
             const QString pass(m_hAccountDetails[DRing::Account::ConfProperties::TLS::PASSWORD]);
    
             if (!ca.isEmpty())
                q_ptr->setTlsCaListCertificate(ca);
    
             // Set the pvk file and password only if there is a certificate
             if (!cert.isEmpty()) {
                q_ptr->setTlsCertificate(cert);
                if (!key.isEmpty()) {
                   q_ptr->setTlsPrivateKey(key);
                      if (!pass.isEmpty())
                         q_ptr->setTlsPassword(pass);
                }
             }
    
             m_RemoteEnabledState = q_ptr->isEnabled();
          }
    
          changeState(Account::EditState::READY);
    
          //TODO port this to the URI class helpers, this doesn't cover all corner cases
          const QString currentUri = QString("%1@%2").arg(q_ptr->username()).arg(m_HostName);
    
          if (!m_pAccountNumber || (m_pAccountNumber && m_pAccountNumber->uri() != currentUri)) {
             if (m_pAccountNumber) {
                disconnect(m_pAccountNumber,SIGNAL(presenceMessageChanged(QString)),this,SLOT(slotPresenceMessageChanged(QString)));
                disconnect(m_pAccountNumber,SIGNAL(presentChanged(bool)),this,SLOT(slotPresentChanged(bool)));
             }
             m_pAccountNumber = PhoneDirectoryModel::instance().getNumber(currentUri,q_ptr);
             m_pAccountNumber->setType(ContactMethod::Type::ACCOUNT);
             connect(m_pAccountNumber,SIGNAL(presenceMessageChanged(QString)),this,SLOT(slotPresenceMessageChanged(QString)));
             connect(m_pAccountNumber,SIGNAL(presentChanged(bool)),this,SLOT(slotPresentChanged(bool)));
          }
    
          //If the credential model is loaded, then update it
          if (m_pCredentials)
             m_pCredentials << CredentialModel::EditAction::RELOAD;
    
          //If the codec model is loaded, then update it
          if (m_pCodecModel)
             m_pCodecModel << CodecModel::EditAction::RELOAD;
    
          emit q_ptr->changed(q_ptr);
    
          //The registration state is cached, update that cache
          updateState();
    
          AccountModel::instance().d_ptr->slotVolatileAccountDetailsChange(q_ptr->id(),configurationManager.getVolatileAccountDetails(q_ptr->id()));
       }
    }
    
    void AccountPrivate::nothing()
    {
    
    }
    
    void AccountPrivate::edit()    {
       changeState(Account::EditState::EDITING );
    }
    
    void AccountPrivate::modify()  {
       typedef Account::RoleStatus ST;
       typedef Account::Role       R ;
       //This check if the account can be saved or it would produce an invalid result
    
       //Future checks that could be implemented:
       // * 2 accounts with the same alias
       // * Weak password (will require a new COMPLETE_WARNING edit state)
       // * Invalid hostname
       // * Unreachable hostname (will require a new COMPLETE_WARNING edit state)
       // * Invalid username
       // * Non hash username for  Ring accounts
    
    
       /* ALIAS   : While valid, an account without one cause usability issues */
       /* HOSTNAME: Without hostname, an account cannot be "READY"             */
       /* USERNAME: All protocols but IP2IP require an username                */
       /* PASSWORD: SIP accounts require a password (unless used as IP2IP)     */
    
       m_hRoleStatus[(int)R::Alias   ] = q_ptr->alias   ().isEmpty() ? ST::REQUIRED_EMPTY : ST::OK;
       m_hRoleStatus[(int)R::Hostname] = q_ptr->hostname().isEmpty() ? ST::REQUIRED_EMPTY : ST::OK;
       m_hRoleStatus[(int)R::Username] = q_ptr->username().isEmpty() ? ST::REQUIRED_EMPTY : ST::OK;
       m_hRoleStatus[(int)R::Password] = q_ptr->password().isEmpty() ? ST::REQUIRED_EMPTY : ST::OK;
    
       //Apply some filters per protocol
       switch (q_ptr->protocol()) {
          case Account::Protocol::SIP:
             //IP2IP is very permissive about missing fields
             if (q_ptr->isIp2ip()) {
                m_hRoleStatus[(int)R::Alias   ] = ST::OK;
                m_hRoleStatus[(int)R::Username] = ST::OK;
                m_hRoleStatus[(int)R::Hostname] = ST::OK;
                m_hRoleStatus[(int)R::Password] = ST::OK;
             }
             break;
          case Account::Protocol::RING:
             m_hRoleStatus[(int)R::Hostname] = ST::OK;
             m_hRoleStatus[(int)R::Password] = ST::OK;
    
             //New accounts will get the hash later
             if (q_ptr->isNew() && q_ptr->username().isEmpty())
                m_hRoleStatus[(int)R::Username] = ST::OK     ;
             else if (q_ptr->isNew() && !q_ptr->username().isEmpty())
                m_hRoleStatus[(int)R::Username] = ST::INVALID;
    
             break;
          case Account::Protocol::COUNT__:
             //No changes needed
             break;
       }
    
       const bool isIncomplete = (
            (m_hRoleStatus[(int) R::Alias    ] != ST::OK)
          | (m_hRoleStatus[(int) R::Hostname ] != ST::OK)
          | (m_hRoleStatus[(int) R::Username ] != ST::OK)
          | (m_hRoleStatus[(int) R::Password ] != ST::OK)
       );
    
       const Account::EditState newState = isIncomplete ?
          Account::EditState::MODIFIED_INCOMPLETE :
          Account::EditState::MODIFIED_COMPLETE   ;
    
       if (newState != q_ptr->editState())
          changeState(newState);
    }
    
    void AccountPrivate::remove()  {
       changeState(Account::EditState::REMOVED );
    }
    
    void AccountPrivate::cancel()  {
       changeState(Account::EditState::READY   );
    }
    
    void AccountPrivate::outdate() {
       changeState(Account::EditState::OUTDATED);
    }
    
    void AccountPrivate::regenSecurityValidation()
    {
       if (m_pSecurityEvaluationModel) {
          m_pSecurityEvaluationModel->d_ptr->update();
       }
    }
    
    /*****************************************************************************
     *                                                                           *
     *                                 Operator                                  *
     *                                                                           *
     ****************************************************************************/
    
    ///Are both account the same
    bool Account::operator==(const Account& a)const
    {
       return d_ptr->m_AccountId == a.d_ptr->m_AccountId;
    }
    
    /*****************************************************************************
     *                                                                           *
     *                               Placeholder                                 *
     *                                                                           *
     ****************************************************************************/
    
    ///Constructor
    AccountPlaceHolder::AccountPlaceHolder(const QByteArray& uid) : Account(),
    d_ptr(nullptr)
    {
       Account::d_ptr->m_AccountId = uid  ;
       Account::d_ptr->m_isLoaded  = false;
    }
    
    /*****************************************************************************
     *                                                                           *
     *                                 Mutator                                   *
     *                                                                           *
     ****************************************************************************/
    
    ///Merge an existing account into this temporary one
    bool AccountPrivate::merge(Account* account)
    {
       if ((!account) || this == account->Account::d_ptr.data())
          return false;
    
       /*AccountPrivate* p = (AccountPrivate*) (q_ptr->Account::d_ptr.take());
       delete p;*/ //FIXME memory leak
    
       q_ptr->Account::d_ptr = account->Account::d_ptr;
       emit q_ptr->changed(q_ptr);
    
       return true;
    }
    
    /*****************************************************************************
     *                                                                           *
     *                                 Helper                                    *
     *                                                                           *
     ****************************************************************************/
    
    void Account::regenSecurityValidation()
    {
        d_ptr->regenSecurityValidation();
    }
    
    /**
     * The client have a different point of view when it come to the account
     * state. All the different errors are also handled elsewhere
     */
    Account::RegistrationState Account::fromDaemonName(const QString& st)
    {
       if     ( st == DRing::Account::States::REGISTERED
            ||  st == DRing::Account::States::READY                    )
          return Account::RegistrationState::READY;
    
       else if( st == DRing::Account::States::UNREGISTERED             )
          return Account::RegistrationState::UNREGISTERED;
    
       else if( st == DRing::Account::States::TRYING                   )
          return Account::RegistrationState::TRYING;
    
       else if (st == DRing::Account::States::INITIALIZING             )
          return Account::RegistrationState::INITIALIZING;
    
       else if( st == DRing::Account::States::ERROR
            ||  st == DRing::Account::States::ERROR_GENERIC
            ||  st == DRing::Account::States::ERROR_AUTH
            ||  st == DRing::Account::States::ERROR_NETWORK
            ||  st == DRing::Account::States::ERROR_HOST
            ||  st == DRing::Account::States::ERROR_CONF_STUN
            ||  st == DRing::Account::States::ERROR_EXIST_STUN
            ||  st == DRing::Account::States::ERROR_SERVICE_UNAVAILABLE
            ||  st == DRing::Account::States::ERROR_NOT_ACCEPTABLE
            ||  st == DRing::Account::States::ERROR_NEED_MIGRATION
            ||  st == DRing::Account::States::REQUEST_TIMEOUT          )
          return Account::RegistrationState::ERROR;
    
       else {
          qWarning() << "Unknown registration state" << st;
          return Account::RegistrationState::ERROR;
       }
    
    }
    
    #undef TO_BOOL
    #undef IS_TRUE
    #include <account.moc>