/* * 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 #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 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& PsiContactList::accounts() const { return accounts_; } /** * Returns list with all enabled accounts. */ const QList& 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 PsiContactList::sortedEnabledAccounts() const { QList 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& 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_; }