File indexing completed on 2023-05-30 11:40:28

0001 /*
0002     Class for displaying connection errors
0003     Copyright (C) 2011  Martin Klapetek <martin.klapetek@gmail.com>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Lesser General Public
0007     License as published by the Free Software Foundation; either
0008     version 2.1 of the License, or (at your option) any later version.
0009 
0010     This library is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013     Lesser General Public License for more details.
0014 
0015     You should have received a copy of the GNU Lesser General Public
0016     License along with this library; if not, write to the Free Software
0017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 */
0019 
0020 
0021 #include "error-handler.h"
0022 
0023 #include <KTp/core.h>
0024 #include <KTp/error-dictionary.h>
0025 
0026 #include <TelepathyQt/Account>
0027 #include <TelepathyQt/AccountManager>
0028 #include <TelepathyQt/Connection>
0029 
0030 #include <KLocalizedString>
0031 #include <KNotification>
0032 
0033 #include <QScopedPointer>
0034 #include <QTimer>
0035 #include <QNetworkConfigurationManager>
0036 
0037 /** Stores the last error message for an account
0038     For every new error if we're online we wait 30 seconds and show 1 notification for all errors. This will be the only error we show for that account until the user reconnects.
0039 */
0040 
0041 class ConnectionError
0042 {
0043 public:
0044     ConnectionError(Tp::ConnectionStatusReason connectionStatusReason,
0045                     const QString &connectionError,
0046                     const Tp::Connection::ErrorDetails &connectionErrorDetails);
0047 
0048     Tp::ConnectionStatusReason connectionStatusReason() const;
0049     QString connectionError() const;
0050     Tp::Connection::ErrorDetails connectionErrorDetails() const;
0051 
0052     bool shown() const;
0053     void setShown(bool);
0054 
0055     QDateTime errorTime() const;
0056 
0057 private:
0058     bool m_shown;
0059     Tp::ConnectionStatusReason m_connectionStatusReason;
0060     Tp::Connection::ErrorDetails m_connectionErrorDetails;
0061     QString m_connectionError;
0062     QDateTime m_errorTime;
0063 };
0064 
0065 ConnectionError::ConnectionError(Tp::ConnectionStatusReason connectionStatusReason, const QString &connectionError, const Tp::Connection::ErrorDetails &connectionErrorDetails):
0066     m_connectionStatusReason(connectionStatusReason),
0067     m_connectionErrorDetails(connectionErrorDetails),
0068     m_connectionError(connectionError)
0069 {
0070     m_shown = false;
0071     m_errorTime = QDateTime::currentDateTime();
0072 }
0073 
0074 Tp::ConnectionStatusReason ConnectionError::connectionStatusReason() const
0075 {
0076     return m_connectionStatusReason;
0077 }
0078 
0079 QString ConnectionError::connectionError() const
0080 {
0081     return m_connectionError;
0082 }
0083 
0084 Tp::Connection::ErrorDetails ConnectionError::connectionErrorDetails() const
0085 {
0086     return m_connectionErrorDetails;
0087 }
0088 
0089 QDateTime ConnectionError::errorTime() const
0090 {
0091     return m_errorTime;
0092 }
0093 
0094 bool ConnectionError::shown() const
0095 {
0096     return m_shown;
0097 }
0098 
0099 void ConnectionError::setShown(bool)
0100 {
0101     m_shown = true;
0102 }
0103 
0104 
0105 ErrorHandler::ErrorHandler(QObject *parent)
0106     : QObject(parent)
0107 {
0108     Q_FOREACH(const Tp::AccountPtr &account, KTp::accountManager()->allAccounts()) {
0109         onNewAccount(account);
0110     }
0111 
0112     connect(KTp::accountManager().data(), SIGNAL(newAccount(Tp::AccountPtr)),
0113             this, SLOT(onNewAccount(Tp::AccountPtr)));
0114 }
0115 
0116 ErrorHandler::~ErrorHandler()
0117 {
0118 
0119 }
0120 
0121 void ErrorHandler::showErrorNotification()
0122 {
0123     //if we're not currently connected to the network, any older errors were probably related to this, ignore them.
0124     QNetworkConfigurationManager network;
0125     if (!network.isOnline()) {
0126         return;
0127     }
0128 
0129     QString errorMessage;
0130 
0131     QHash<Tp::AccountPtr, ConnectionError>::iterator i = m_errorMap.begin();
0132     while (i != m_errorMap.end()) {
0133         const Tp::AccountPtr account = i.key();
0134         ConnectionError &error = i.value();
0135 
0136         //try to group as many error messages as we can, but we still want to give accounts a chance to reconnect
0137         //only want to show messages that are at least 20 seconds old to give the account a chance to connect.
0138         if (!error.shown() && error.errorTime().secsTo(QDateTime::currentDateTime()) > 20) {
0139             switch (error.connectionStatusReason()) {
0140             case Tp::ConnectionStatusReasonNetworkError:
0141                 errorMessage += i18nc("%1 is the account name", "Could not connect %1. There was a network error, check your connection", account->displayName()) + QLatin1String("<br>");
0142                 break;
0143             default:
0144                 if (error.connectionError() == QLatin1String(TP_QT_ERROR_CANCELLED)) {
0145                     break;
0146                 }
0147                 if (error.connectionErrorDetails().hasServerMessage()) {
0148                     errorMessage += i18nc("%1 is the account name, %2 the error message", "There was a problem while trying to connect %1 - %2", account->displayName(), error.connectionErrorDetails().serverMessage()) + QLatin1String("<br>");
0149                 } else {
0150                     errorMessage += i18nc("%1 is the account name, %2 the error message", "There was a problem while trying to connect %1 - %2", account->displayName(), KTp::ErrorDictionary::displayVerboseErrorMessage(error.connectionError())) + QLatin1String("<br>");
0151                 }
0152                 break;
0153             }
0154             error.setShown(true);
0155         }
0156         ++i;
0157     }
0158 
0159     if (!errorMessage.isEmpty()) {
0160         if (errorMessage.endsWith(QLatin1String("<br>"))) {
0161             errorMessage.chop(4);
0162         }
0163 
0164         showMessageToUser(errorMessage, ErrorHandler::SystemMessageError);
0165     }
0166 }
0167 
0168 void ErrorHandler::onConnectionStatusChanged(const Tp::ConnectionStatus status)
0169 {
0170     Tp::AccountPtr account(qobject_cast< Tp::Account* >(sender()));
0171 
0172     //if we're not connected to the network, errors are pointless
0173     QNetworkConfigurationManager network;
0174     if (!network.isOnline()) {
0175         return;
0176     }
0177 
0178     if (status == Tp::ConnectionStatusDisconnected) {
0179         //if we're deliberately disconnected do nothing
0180         //else if this is the first error for this account, store the details of the error to show
0181         if (account->connectionStatusReason() == Tp::ConnectionStatusReasonRequested) {
0182             m_errorMap.remove(account);
0183         } else if (!m_errorMap.contains(account)) {
0184             m_errorMap.insert(account, ConnectionError(account->connectionStatusReason(), account->connectionError(), account->connectionErrorDetails()));
0185             QTimer::singleShot(30 * 1000, this, SLOT(showErrorNotification())); //a timer is kept per account because we want to show 30 seconds after the first still valid error.
0186             account->reconnect();
0187         }
0188 
0189     } else  if (status == Tp::ConnectionStatusConnected) {
0190         //we are now connected, removed pending error messages
0191         m_errorMap.remove(account);
0192     }
0193 }
0194 
0195 void ErrorHandler::onRequestedPresenceChanged()
0196 {
0197     Tp::AccountPtr account(qobject_cast< Tp::Account* >(sender()));
0198     m_errorMap.remove(account);
0199 }
0200 
0201 void ErrorHandler::showMessageToUser(const QString &text, const ErrorHandler::SystemMessageType type)
0202 {
0203     //The pointer is automatically deleted when the event is closed
0204     KNotification *notification;
0205     if (type == ErrorHandler::SystemMessageError) {
0206         notification = new KNotification(QLatin1String("telepathyError"), KNotification::Persistent);
0207     } else {
0208         notification = new KNotification(QLatin1String("telepathyInfo"), KNotification::CloseOnTimeout);
0209     }
0210 
0211     notification->setComponentName(QStringLiteral("ktelepathy"));
0212 
0213     notification->setText(text);
0214     notification->sendEvent();
0215 }
0216 
0217 void ErrorHandler::onNewAccount(const Tp::AccountPtr &account)
0218 {
0219     connect(account.data(), SIGNAL(connectionStatusChanged(Tp::ConnectionStatus)),
0220             this, SLOT(onConnectionStatusChanged(Tp::ConnectionStatus)));
0221 
0222     connect(account.data(), SIGNAL(requestedPresenceChanged(Tp::Presence)), SLOT(onRequestedPresenceChanged()));
0223     connect(account.data(), SIGNAL(removed()), SLOT(onAccountRemoved()));
0224 }
0225 
0226 void ErrorHandler::onAccountRemoved()
0227 {
0228     Tp::AccountPtr account(qobject_cast<Tp::Account*>(sender()));
0229     Q_ASSERT(account);
0230     m_errorMap.remove(account);
0231 }