This repository has been archived on 2025-12-24. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
yachat/src/psicontactlist.cpp

713 lines
16 KiB
C++
Raw Normal View History

2025-12-25 01:37:49 +05:00
/*
* psicontactlist.cpp - general abstraction of the psi-specific contact list
* Copyright (C) 2006 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "psicontactlist.h"
#include <QTimer>
#include "psiaccount.h"
#include "psievent.h"
#include "serverinfomanager.h"
#include "psicon.h"
#ifdef YAPSI
#include "yacontactlistmodel.h"
#include "yapddmanager.h"
#include "yacommon.h"
#endif
#ifdef YAPSI_ACTIVEX_SERVER
#include "yaonline.h"
#endif
/**
* Constructs new PsiContactList. \param psi will not be PsiContactList's parent though.
*/
PsiContactList::PsiContactList(PsiCon* psi)
: QObject()
, psi_(psi)
#ifdef YAPSI_ACTIVEX_SERVER
, onlineAccount_(0)
#endif
, showAgents_(false)
, showHidden_(false)
, showSelf_(false)
, showOffline_(false)
, accountsLoaded_(false)
{
#ifdef YAPSI
updateErrorMessageTimer_ = new QTimer(this);
updateErrorMessageTimer_->setInterval(100);
updateErrorMessageTimer_->setSingleShot(true);
connect(updateErrorMessageTimer_, SIGNAL(timeout()), SLOT(updateErrorMessage()));
yaPddManager_ = psi ? new YaPddManager(psi, this) : 0;
connect(this, SIGNAL(accountCountChanged()), updateErrorMessageTimer_, SLOT(start()));
#endif
connect(this, SIGNAL(accountCountChanged()), SIGNAL(accountStateChanged()));
connect(this, SIGNAL(accountStateChanged()), SLOT(private_accountStateChanged()));
}
/**
* Destroys the PsiContactList along with all PsiAccounts.
*/
PsiContactList::~PsiContactList()
{
emit destroying();
accountsLoaded_ = false;
// PsiAccount calls some signals while being deleted prior to being unlinked,
// which in result could cause calls to PsiContactList::accounts()
QList<PsiAccount*> toDelete(accounts_);
enabledAccounts_.clear();
foreach(PsiAccount* account, toDelete)
delete account;
}
/**
* Returns pointer to the global Psi Controller.
*/
PsiCon* PsiContactList::psi() const
{
return psi_;
}
/**
* Returns list of all accounts if \param enabledOnly is false,
* equivalent to enabledAccounts() otherwise.
*/
const QList<PsiAccount*>& PsiContactList::accounts() const
{
return accounts_;
}
/**
* Returns list with all enabled accounts.
*/
const QList<PsiAccount*>& PsiContactList::enabledAccounts() const
{
return enabledAccounts_;
}
#ifdef YAPSI
/**
* Returns a list of enabled accounts, where yandex accounts are placed in the beginning
* of the list.
*/
QList<PsiAccount*> PsiContactList::sortedEnabledAccounts() const
{
QList<PsiAccount*> accounts;
foreach(PsiAccount* account, enabledAccounts()) {
bool ya = account->isYaAccount() || account->isYaPddAccount();
if (ya)
accounts << account;
}
foreach(PsiAccount* account, enabledAccounts()) {
bool ya = account->isYaAccount() || account->isYaPddAccount();
if (!ya)
accounts << account;
}
return accounts;
}
#endif
/**
* Returns true, if there are some enabled accounts which are active.
*/
bool PsiContactList::haveActiveAccounts() const
{
foreach(PsiAccount* account, enabledAccounts_)
if (account->isActive())
return true;
return false;
}
bool PsiContactList::haveAvailableAccounts() const
{
foreach(PsiAccount* account, enabledAccounts_)
if (account->isAvailable())
return true;
return false;
}
/**
* Returns true if enabledAccounts() list is not empty.
*/
bool PsiContactList::haveEnabledAccounts() const
{
return !enabledAccounts_.isEmpty();
}
/**
* Returns true if there are some accounts that are trying to establish a connection.
*/
bool PsiContactList::haveConnectingAccounts() const
{
foreach(PsiAccount* account, enabledAccounts()) {
#ifdef YAPSI
if (account->enabled() && !account->isAvailable())
return true;
#else
if (account->isActive() && !account->isAvailable())
return true;
#endif
}
return false;
}
/**
* At the moment, it returns first enabled account.
* Note: In YaPsi it tries to return first enabled ya.ru account, then
* reverts to the usual behavior.
*/
PsiAccount *PsiContactList::defaultAccount() const
{
#ifdef YAPSI_ACTIVEX_SERVER
if (onlineAccount_) {
return onlineAccount();
}
#endif
#ifdef YAPSI
PsiAccount* account = !accounts_.isEmpty() ? accounts_.first() : 0;
return account;
#endif
if (!enabledAccounts_.isEmpty()) {
return enabledAccounts_.first();
}
return 0;
}
#ifdef YAPSI
PsiAccount* PsiContactList::yaServerHistoryAccount() const
{
#ifdef YAPSI_ACTIVEX_SERVER
return onlineAccount();
#else
PsiAccount* account = defaultAccount();
if (account && (account->isYaAccount() || account->isYaPddAccount())) {
return account;
}
return 0;
#endif
}
#endif
#ifdef YAPSI_ACTIVEX_SERVER
PsiAccount* PsiContactList::onlineAccount() const
{
Q_ASSERT(onlineAccount_);
return onlineAccount_;
}
#endif
#ifdef YAPSI
PsiAccount* PsiContactList::yandexTeamAccount() const
{
foreach(PsiAccount* account, enabledAccounts())
if (Ya::isYandexTeamJid(account->jid()))
return account;
return 0;
}
#endif
/**
* Creates new PsiAccount based on some initial settings. This is used by AccountAddDlg.
*/
PsiAccount* PsiContactList::createAccount(const QString& name, const Jid& j, const QString& pass, bool opt_host, const QString& host, int port, bool legacy_ssl_probe, UserAccount::SSLFlag ssl, int proxy, bool modify)
{
UserAccount acc;
acc.name = name;
acc.jid = j.full();
if(!pass.isEmpty()) {
acc.opt_pass = true;
acc.pass = pass;
}
acc.opt_host = opt_host;
acc.host = host;
acc.port = port;
acc.ssl = ssl;
acc.proxy_index = proxy;
acc.legacy_ssl_probe = legacy_ssl_probe;
acc.tog_offline = showOffline();
acc.tog_agents = showAgents();
acc.tog_hidden = showHidden();
acc.tog_self = showSelf();
#ifdef YAPSI
acc.opt_pass = true;
acc.opt_auto = true;
acc.opt_ignoreSSLWarnings = false;
acc.allow_plain = XMPP::ClientStream::AllowPlainOverTLS;
acc.opt_reconn = true;
acc.opt_log = true;
acc.opt_keepAlive = true;
acc.opt_compress = true;
#endif
PsiAccount *pa = loadAccount(acc);
emit saveAccounts();
// pop up the modify dialog so the user can customize the new account
if (modify)
pa->modify();
return pa;
}
void PsiContactList::createAccount(const UserAccount& acc)
{
loadAccount(acc);
emit saveAccounts();
}
/**
* Call this to remove account completely from system.
*/
void PsiContactList::removeAccount(PsiAccount* account)
{
emit accountRemoved(account);
account->deleteQueueFile();
delete account;
emit saveAccounts();
}
/**
* Obsolete. Call PsiAccount::setEnabled() directly.
*/
void PsiContactList::setAccountEnabled(PsiAccount* account, bool enabled)
{
account->setEnabled(enabled);
}
/**
* Counts total number of unread events for all accounts.
*/
int PsiContactList::queueCount() const
{
int total = 0;
foreach(PsiAccount* account, enabledAccounts_)
total += account->eventQueue()->count();
return total;
}
/**
* Finds account with unprocessed event of highest priority, starting with
* non-DND accounts.
*/
PsiAccount* PsiContactList::queueLowestEventId()
{
PsiAccount *low = 0;
// first try to get event from non-dnd account
low = tryQueueLowestEventId(false);
// if failed, then get the event from dnd account instead
if (!low)
low = tryQueueLowestEventId(true);
return low;
}
/**
* Creates new PsiAccount from \param acc.
*/
PsiAccount *PsiContactList::loadAccount(const UserAccount& acc)
{
emit beginBulkContactUpdate();
PsiAccount *pa = psi_->createAccount(acc);
connect(pa, SIGNAL(enabledChanged()), SIGNAL(accountCountChanged()));
emit accountAdded(pa);
emit endBulkContactUpdate();
return pa;
}
/**
* Loads accounts from \param list
*/
void PsiContactList::loadAccounts(const UserAccountList &_list)
{
UserAccountList list = _list;
emit beginBulkContactUpdate();
#ifdef YAPSI_ACTIVEX_SERVER
UserAccount acc;
acc.id = "{94011113-0d7c-41b6-8ff4-49febfa1e304}";
foreach(UserAccount account, _list) {
if (account.id == acc.id) {
acc = account;
break;
}
}
acc.saveable = false;
list.clear();
foreach(UserAccount account, _list) {
if (account.id != acc.id) {
Q_ASSERT(account.saveable);
list << account;
}
}
// Ya.Online account **must** be the first one
Q_ASSERT(accounts_.isEmpty());
loadAccount(acc);
Q_ASSERT(accounts_.count() == 1);
onlineAccount_ = accounts_.first();
Q_ASSERT(!onlineAccount_->userAccount().saveable);
#endif
foreach(UserAccount account, list)
loadAccount(account);
emit endBulkContactUpdate();
accountsLoaded_ = true;
emit loadedAccounts();
emit accountCountChanged();
}
/**
* Creates new UserAccountList from all the PsiAccounts ready for saving to disk.
*/
UserAccountList PsiContactList::getUserAccountList() const
{
UserAccountList acc;
foreach(PsiAccount* account, accounts_)
acc += account->userAccount();
return acc;
}
/**
* It's called by each and every PsiAccount on its creation.
*/
void PsiContactList::link(PsiAccount* account)
{
Q_ASSERT(!accounts_.contains(account));
if (accounts_.contains(account))
return;
connect(account, SIGNAL(updatedActivity()), this, SIGNAL(accountActivityChanged()));
connect(account->serverInfoManager(),SIGNAL(featuresChanged()), this, SIGNAL(accountFeaturesChanged()));
connect(account, SIGNAL(queueChanged()), this, SIGNAL(queueChanged()));
connect(account, SIGNAL(beginBulkContactUpdate()), this, SIGNAL(beginBulkContactUpdate()));
connect(account, SIGNAL(endBulkContactUpdate()), this, SIGNAL(endBulkContactUpdate()));
connect(account, SIGNAL(rosterRequestFinished()), this, SIGNAL(rosterRequestFinished()));
#ifdef YAPSI
connect(account, SIGNAL(connectionError(const QString&)), updateErrorMessageTimer_, SLOT(start()));
#endif
connect(account, SIGNAL(updatedActivity()), SIGNAL(accountStateChanged()));
connect(account, SIGNAL(updatedAccount()), SIGNAL(accountStateChanged()));
connect(account, SIGNAL(connectionError(const QString&)), SIGNAL(accountStateChanged()), Qt::QueuedConnection);
accounts_.append(account);
if (account->enabled())
addEnabledAccount(account);
connect(account, SIGNAL(enabledChanged()), SLOT(accountEnabledChanged()));
emit accountCountChanged();
}
/**
* It's called by each and every PsiAccount on its destruction.
*/
void PsiContactList::unlink(PsiAccount* account)
{
Q_ASSERT(accounts_.contains(account));
if (!accounts_.contains(account))
return;
disconnect(account, SIGNAL(updatedActivity()), this, SIGNAL(accountActivityChanged()));
accounts_.removeAll(account);
removeEnabledAccount(account);
emit accountCountChanged();
}
/**
* TODO
*/
bool PsiContactList::showAgents() const
{
return showAgents_;
}
/**
* TODO
*/
bool PsiContactList::showHidden() const
{
return showHidden_;
}
/**
* TODO
*/
bool PsiContactList::showSelf() const
{
return showSelf_;
}
bool PsiContactList::showOffline() const
{
return showOffline_;
}
bool PsiContactList::showGroups() const
{
return showGroups_;
}
/**
* TODO
*/
void PsiContactList::setShowAgents(bool showAgents)
{
if (showAgents_ != showAgents) {
showAgents_ = showAgents;
emit showAgentsChanged(showAgents_);
}
}
/**
* TODO
*/
void PsiContactList::setShowHidden(bool showHidden)
{
if (showHidden_ != showHidden) {
showHidden_ = showHidden;
emit showHiddenChanged(showHidden_);
}
}
/**
* TODO
*/
void PsiContactList::setShowSelf(bool showSelf)
{
if (showSelf_ != showSelf) {
showSelf_ = showSelf;
emit showSelfChanged(showSelf_);
}
}
void PsiContactList::setShowOffline(bool showOffline)
{
if (showOffline_ != showOffline) {
showOffline_ = showOffline;
emit showOfflineChanged(showOffline_);
}
}
void PsiContactList::setShowGroups(bool showGroups)
{
if (showGroups_ != showGroups) {
showGroups_ = showGroups;
emit showGroupsChanged(showGroups_);
}
}
PsiAccount *PsiContactList::tryQueueLowestEventId(bool includeDND)
{
PsiAccount *low = 0;
int low_id = 0;
int low_prior = option.EventPriorityDontCare;
foreach(PsiAccount *account, enabledAccounts_) {
int n = account->eventQueue()->nextId();
if ( n == -1 )
continue;
if (!includeDND && account->status().type() == XMPP::Status::DND)
continue;
int p = account->eventQueue()->peekNext()->priority();
if ( !low || (n < low_id && p == low_prior) || p > low_prior ) {
low = account;
low_id = n;
low_prior = p;
}
}
return low;
}
void PsiContactList::accountEnabledChanged()
{
PsiAccount* account = (PsiAccount*)sender();
if (account->enabled())
addEnabledAccount(account);
else
removeEnabledAccount(account);
}
PsiAccount* PsiContactList::getAccount(const QString& id) const
{
foreach(PsiAccount* account, accounts())
if (account->id() == id)
return account;
return 0;
}
PsiAccount* PsiContactList::getAccountByJid(const XMPP::Jid& jid) const
{
foreach(PsiAccount* account, accounts())
if (account->jid().compare(jid, false))
return account;
return 0;
}
const QList<PsiContact*>& PsiContactList::contacts() const
{
return contacts_;
}
void PsiContactList::addEnabledAccount(PsiAccount* account)
{
if (enabledAccounts_.contains(account))
return;
enabledAccounts_.append(account);
connect(account, SIGNAL(addedContact(PsiContact*)), SLOT(accountAddedContact(PsiContact*)));
connect(account, SIGNAL(removedContact(PsiContact*)), SLOT(accountRemovedContact(PsiContact*)));
emit beginBulkContactUpdate();
foreach(PsiContact* contact, account->contactList())
accountAddedContact(contact);
emit endBulkContactUpdate();
}
void PsiContactList::removeEnabledAccount(PsiAccount* account)
{
if (!enabledAccounts_.contains(account))
return;
emit beginBulkContactUpdate();
foreach(PsiContact* contact, account->contactList())
accountRemovedContact(contact);
emit endBulkContactUpdate();
disconnect(account, SIGNAL(addedContact(PsiContact*)), this, SLOT(accountAddedContact(PsiContact*)));
disconnect(account, SIGNAL(removedContact(PsiContact*)), this, SLOT(accountRemovedContact(PsiContact*)));
enabledAccounts_.removeAll(account);
}
void PsiContactList::accountAddedContact(PsiContact* contact)
{
Q_ASSERT(!contacts_.contains(contact));
contacts_.append(contact);
emit addedContact(contact);
}
void PsiContactList::accountRemovedContact(PsiContact* contact)
{
Q_ASSERT(contacts_.contains(contact));
contacts_.removeAll(contact);
emit removedContact(contact);
}
bool PsiContactList::accountsLoaded() const
{
return accountsLoaded_;
}
#ifdef YAPSI
void PsiContactList::updateErrorMessage()
{
if (!accountsLoaded())
return;
QString msg;
foreach(PsiAccount* account, accounts()) {
if (!account->currentConnectionError().isEmpty()) {
msg = tr("%1: %2", "account_name: account_error")
.arg(account->name())
.arg(account->currentConnectionError());
break;
}
}
if (msg != firstErrorMessage_) {
firstErrorMessage_ = msg;
emit firstErrorMessageChanged(firstErrorMessage_);
if (firstErrorMessage_.isEmpty()) {
// qWarning("PsiContactList::clearErrorMessage");
}
else {
// qWarning("PsiContactList::setErrorMessage: %s", qPrintable(firstErrorMessage_));
}
}
}
QString PsiContactList::firstErrorMessage() const
{
return firstErrorMessage_;
}
PsiContact* PsiContactList::addFakeTempContact(const XMPP::Jid& jid, PsiAccount* account)
{
if (!contactListModel_)
return 0;
return contactListModel_->addFakeTempContact(jid, account);
}
void PsiContactList::setContactListModel(YaContactListModel* model)
{
contactListModel_ = model;
}
YaPddManager* PsiContactList::yaPddManager() const
{
return yaPddManager_;
}
#endif
void PsiContactList::private_accountStateChanged()
{
if (accountsLoaded()) {
QStringList connectingAccounts;
foreach(PsiAccount* account, enabledAccounts()) {
QString name = account->jid().bare();
if (!name.isEmpty() && account->enabled() && !account->isAvailable()) {
connectingAccounts << name;
}
}
if (lastConnectingAccounts_ != connectingAccounts) {
lastConnectingAccounts_ = connectingAccounts;
emit connectingAccountsChanged();
}
}
}
QStringList PsiContactList::connectingAccounts() const
{
return lastConnectingAccounts_;
}