File indexing completed on 2025-02-16 04:59:06
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include <QTimer> 0024 #include "NetworkWatcher.h" 0025 #include "ImapAccess.h" 0026 #include "Model.h" 0027 0028 namespace Imap { 0029 namespace Mailbox { 0030 0031 NetworkWatcher::NetworkWatcher(ImapAccess *parent, Model *model): 0032 QObject(parent), m_imapAccess(parent), m_model(model), m_desiredPolicy(NETWORK_OFFLINE) 0033 { 0034 // No assert for m_imapAccess. The test suite doesn't create one. 0035 Q_ASSERT(m_model); 0036 connect(m_model, &Model::networkPolicyChanged, this, &NetworkWatcher::effectiveNetworkPolicyChanged); 0037 connect(m_model, &Model::networkError, this, &NetworkWatcher::attemptReconnect); 0038 connect(m_model, &Model::connectionStateChanged, this, &NetworkWatcher::handleConnectionStateChanged); 0039 0040 m_reconnectTimer = new QTimer(this); 0041 m_reconnectTimer->setSingleShot(true); 0042 m_reconnectTimer->setInterval(MIN_RECONNECT_TIMEOUT/2); 0043 connect(m_reconnectTimer, &QTimer::timeout, this, &NetworkWatcher::tryReconnect); 0044 } 0045 0046 NetworkPolicy NetworkWatcher::effectiveNetworkPolicy() const 0047 { 0048 // Do we have any connection which is really alive? 0049 auto parser = std::find_if(m_model->m_parsers.cbegin(), m_model->m_parsers.cend(), 0050 [](const Imap::Mailbox::ParserState & state) { 0051 return state.connState != CONN_STATE_LOGOUT && state.connState >= CONN_STATE_AUTHENTICATED; 0052 }); 0053 return parser == m_model->m_parsers.cend() ? NETWORK_OFFLINE : m_model->networkPolicy(); 0054 } 0055 0056 /** @short Start the reconnect attempt cycle */ 0057 void NetworkWatcher::attemptReconnect() 0058 { 0059 // Update the reconnect time-out value. Double it up to a max value of MAX_RECONNECT_TIMEOUT secs. 0060 m_reconnectTimer->setInterval(qMin(MAX_RECONNECT_TIMEOUT, m_reconnectTimer->interval()*2)); 0061 m_reconnectTimer->start(); 0062 m_model->logTrace(0, Common::LogKind::LOG_OTHER, QStringLiteral("Network"), 0063 tr("Attempting to reconnect in %n secs", 0, m_reconnectTimer->interval()/1000)); 0064 emit reconnectAttemptScheduled(m_reconnectTimer->interval()); 0065 } 0066 0067 void NetworkWatcher::resetReconnectTimer() 0068 { 0069 m_reconnectTimer->stop(); 0070 m_reconnectTimer->setInterval(MIN_RECONNECT_TIMEOUT/2); 0071 emit resetReconnectState(); 0072 } 0073 0074 void NetworkWatcher::handleConnectionStateChanged(uint parserId, Imap::ConnectionState state) 0075 { 0076 Q_UNUSED(parserId); 0077 // Only treat a real, 100%-successful connection as a succeeding one. This is because 0078 // some of our socket backends (QProcess, for example) do not really have any possibility of signaling 0079 // "hey, network is available" to the other side. 0080 // 0081 // How come? Because "a process has started" could very well mean "yeha, SSH has launched, but we 0082 // don't know anything about the net yet". We *could* hack the QProcess backend to wait for a first byte 0083 // and signal "ok, connected" only afterwards, but that sounds a bit ugly because it uses "magic" knowledge 0084 // that in case IMAP, a server always greets us with something, which would be a bit layer-violating. 0085 // Not that the current enum values of the ConnectionState are free of IMAP idioms, but... 0086 if (state >= CONN_STATE_AUTHENTICATED && state < CONN_STATE_LOGOUT) { 0087 resetReconnectTimer(); 0088 } 0089 } 0090 0091 /** @short Set model's network policy to the desiredPolicy */ 0092 void NetworkWatcher::tryReconnect() 0093 { 0094 m_model->setNetworkPolicy(m_desiredPolicy); 0095 } 0096 0097 /** @short Set the network access policy to "no access allowed" */ 0098 void NetworkWatcher::setNetworkOffline() 0099 { 0100 resetReconnectTimer(); 0101 setDesiredNetworkPolicy(NETWORK_OFFLINE); 0102 emit desiredNetworkPolicyChanged(NETWORK_OFFLINE); 0103 } 0104 0105 /** @short Set the network access policy to "possible, but expensive" */ 0106 void NetworkWatcher::setNetworkExpensive() 0107 { 0108 if (!m_imapAccess || !m_imapAccess->isConfigured()) 0109 return; 0110 0111 resetReconnectTimer(); 0112 setDesiredNetworkPolicy(NETWORK_EXPENSIVE); 0113 emit desiredNetworkPolicyChanged(NETWORK_EXPENSIVE); 0114 } 0115 0116 /** @short Set the network access policy to "it's cheap to use it" */ 0117 void NetworkWatcher::setNetworkOnline() 0118 { 0119 if (!m_imapAccess || !m_imapAccess->isConfigured()) 0120 return; 0121 0122 resetReconnectTimer(); 0123 setDesiredNetworkPolicy(NETWORK_ONLINE); 0124 emit desiredNetworkPolicyChanged(NETWORK_ONLINE); 0125 } 0126 0127 NetworkPolicy NetworkWatcher::desiredNetworkPolicy() const 0128 { 0129 return m_desiredPolicy; 0130 } 0131 0132 } 0133 }