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 }