summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPekka Vuorela <pekka.vuorela@jolla.com>2018-08-13 17:24:36 +0300
committerDamien Caliste <dcaliste@free.fr>2019-03-21 10:12:04 +0000
commit2e146e56182c1de46bb20a61272296443c9eb0ee (patch)
treece1af696e2cb2ff9e69e5b1b818c28b5bea6dead
parentfd5ef18c4d7e8576070c1035d2585fad4f5d66b2 (diff)
Add network listeners to IDLE connections
Use QMailAccount::HasPersistentConnection status flag for IMAP idle instead of updating last sync time every minute, this reduces accounts db writes (they trigger notifications to other processes). Trigger IDLE connection error on session error. When a network session error occurs (e.g. the Wifi connection changes), the connection can sometimes become unusable, but without generating a transport error. IdleProtocol only listens for transport errors, so this can leave the IDLE connection broken until the 28 minute failsafe timer triggers. This patch pushes network session errors from ImapService through to the IdleProtocol objects, so they can act in the same way as if a transport error had occurred, causing them to restart after a short delay. Change-Id: I8034df3d40fcc8e100ae204beee9251629a95704 Reviewed-by: Rolf Eike Beer <eb@emlix.com> Reviewed-by: Pekka Vuorela <pvuorela@iki.fi>
-rw-r--r--src/libraries/qmfclient/qmailaccount.cpp11
-rw-r--r--src/libraries/qmfclient/qmailaccount.h1
-rw-r--r--src/libraries/qmfclient/qmailstore_p.cpp2
-rw-r--r--src/plugins/messageservices/imap/imapclient.cpp13
-rw-r--r--src/plugins/messageservices/imap/imapclient.h1
-rw-r--r--src/plugins/messageservices/imap/imapservice.cpp237
-rw-r--r--src/plugins/messageservices/imap/imapservice.h26
7 files changed, 286 insertions, 5 deletions
diff --git a/src/libraries/qmfclient/qmailaccount.cpp b/src/libraries/qmfclient/qmailaccount.cpp
index 3211155d..85796363 100644
--- a/src/libraries/qmfclient/qmailaccount.cpp
+++ b/src/libraries/qmfclient/qmailaccount.cpp
@@ -60,6 +60,7 @@ static quint64 canTransmitViaReferenceFlag = 0;
static quint64 canCreateFoldersFlag = 0;
static quint64 useSmartReplyFlag = 0;
static quint64 canSearchOnServerFlag = 0;
+static quint64 hasPersistentConnectionFlag = 0;
class QMailAccountPrivate : public QSharedData
{
@@ -344,6 +345,15 @@ public:
\sa QMailSearchAction::searchMessages()
*/
+/*!
+ \variable QMailAccount::HasPersistentConnection
+
+ The status mask needed for testing the value of the registered status flag named
+ \c "HasPersistentConnection" against the result of QMailAccount::status().
+
+ This flag indicates that an account has a persistent connection to the server(always online).
+*/
+
const quint64 &QMailAccount::SynchronizationEnabled = synchronizationEnabledFlag;
const quint64 &QMailAccount::Synchronized = synchronizedFlag;
const quint64 &QMailAccount::AppendSignature = appendSignatureFlag;
@@ -360,6 +370,7 @@ const quint64 &QMailAccount::CanTransmitViaReference = canTransmitViaReferenceFl
const quint64 &QMailAccount::CanCreateFolders = canCreateFoldersFlag;
const quint64 &QMailAccount::UseSmartReply = useSmartReplyFlag;
const quint64 &QMailAccount::CanSearchOnServer = canSearchOnServerFlag;
+const quint64 &QMailAccount::HasPersistentConnection = hasPersistentConnectionFlag;
/*!
Creates an uninitialised account object.
diff --git a/src/libraries/qmfclient/qmailaccount.h b/src/libraries/qmfclient/qmailaccount.h
index 4d5ab660..8ba3efa9 100644
--- a/src/libraries/qmfclient/qmailaccount.h
+++ b/src/libraries/qmfclient/qmailaccount.h
@@ -76,6 +76,7 @@ public:
static const quint64 &CanCreateFolders;
static const quint64 &UseSmartReply;
static const quint64 &CanSearchOnServer;
+ static const quint64 &HasPersistentConnection;
QMailAccount();
explicit QMailAccount(const QMailAccountId& id);
diff --git a/src/libraries/qmfclient/qmailstore_p.cpp b/src/libraries/qmfclient/qmailstore_p.cpp
index 112e3e5a..80c7b955 100644
--- a/src/libraries/qmfclient/qmailstore_p.cpp
+++ b/src/libraries/qmfclient/qmailstore_p.cpp
@@ -2619,6 +2619,8 @@ bool QMailStorePrivate::initStore()
63, true, const_cast<quint64 *>(&QMailAccount::UseSmartReply), t, false)
|| attemptRegisterStatusBit(QLatin1String("CanSearchOnServer"), QLatin1String("accountstatus"),
63, true, const_cast<quint64 *>(&QMailAccount::CanSearchOnServer), t, false)
+ || attemptRegisterStatusBit(QLatin1String("HasPersistentConnection"), QLatin1String("accountstatus"),
+ 63, true, const_cast<quint64 *>(&QMailAccount::HasPersistentConnection), t, false)
|| attemptRegisterStatusBit(QLatin1String("SynchronizationEnabled"), QLatin1String("folderstatus"),
63, true, const_cast<quint64 *>(&QMailFolder::SynchronizationEnabled), t, false)
|| attemptRegisterStatusBit(QLatin1String("Synchronized"), QLatin1String("folderstatus"),
diff --git a/src/plugins/messageservices/imap/imapclient.cpp b/src/plugins/messageservices/imap/imapclient.cpp
index 679abeef..a710d484 100644
--- a/src/plugins/messageservices/imap/imapclient.cpp
+++ b/src/plugins/messageservices/imap/imapclient.cpp
@@ -225,6 +225,8 @@ IdleProtocol::IdleProtocol(ImapClient *client, const QMailFolder &folder)
this, SLOT(idleTransportError()) );
connect(this, SIGNAL(connectionError(QMailServiceAction::Status::ErrorCode,QString)),
this, SLOT(idleTransportError()) );
+ connect(_client, SIGNAL(sessionError()),
+ this, SLOT(idleTransportError()) );
_idleTimer.setSingleShot(true);
connect(&_idleTimer, SIGNAL(timeout()),
@@ -1462,6 +1464,17 @@ void ImapClient::setAccount(const QMailAccountId &id)
qMailLog(Messaging) << "CanCreateFolders for " << account.id() << "changed to" << true;
}
}
+
+ // At this point account can't have a persistent connection to the server, if for some reason the status is wrong(crash/abort) we will
+ // reset correct status here.
+ if (account.status() & QMailAccount::HasPersistentConnection) {
+ account.setStatus(QMailAccount::HasPersistentConnection, false);
+ if (!QMailStore::instance()->updateAccount(&account)) {
+ qWarning() << "Unable to disable HasPersistentConnection for account" << account.id();
+ } else {
+ qMailLog(Messaging) << "Disable HasPersistentConnection for account" << account.id();
+ }
+ }
}
QMailAccountId ImapClient::account() const
diff --git a/src/plugins/messageservices/imap/imapclient.h b/src/plugins/messageservices/imap/imapclient.h
index 621cbf72..771a7a45 100644
--- a/src/plugins/messageservices/imap/imapclient.h
+++ b/src/plugins/messageservices/imap/imapclient.h
@@ -114,6 +114,7 @@ signals:
void allMessagesReceived();
void idleNewMailNotification(QMailFolderId);
void idleFlagsChangedNotification(QMailFolderId);
+ void sessionError();
public slots:
void transportError(int, const QString &msg);
diff --git a/src/plugins/messageservices/imap/imapservice.cpp b/src/plugins/messageservices/imap/imapservice.cpp
index bf4a24ec..eca22f75 100644
--- a/src/plugins/messageservices/imap/imapservice.cpp
+++ b/src/plugins/messageservices/imap/imapservice.cpp
@@ -45,6 +45,7 @@
#include <qmaildisconnected.h>
#include <QCoreApplication>
#include <typeinfo>
+#include <QNetworkConfigurationManager>
namespace {
@@ -1447,9 +1448,14 @@ ImapService::ImapService(const QMailAccountId &accountId)
_client(0),
_source(new Source(this)),
_restartPushEmailTimer(new QTimer(this)),
+ _establishingPushEmail(false),
+ _idling(false),
_accountWasEnabled(false),
_accountWasPushEnabled(false),
- _initiatePushEmailTimer(new QTimer(this))
+ _initiatePushEmailTimer(new QTimer(this)),
+ _networkConfigManager(0),
+ _networkSession(0),
+ _networkSessionTimer(new QTimer(this))
{
QMailAccount account(accountId);
if (!(account.status() & QMailAccount::CanSearchOnServer)) {
@@ -1514,12 +1520,13 @@ void ImapService::disable()
{
QMailAccountConfiguration accountCfg(_accountId);
ImapConfiguration imapCfg(accountCfg);
+ _restartPushEmailTimer->stop();
+ _initiatePushEmailTimer->stop();
+ setPersistentConnectionStatus(false);
_accountWasEnabled = false;
_accountWasPushEnabled = imapCfg.pushEnabled();
_previousPushFolders = imapCfg.pushFolders();
_previousConnectionSettings = connectionSettings(imapCfg);
- _restartPushEmailTimer->stop();
- _initiatePushEmailTimer->stop();
_source->setIntervalTimer(0);
_source->setPushIntervalTimer(0);
_source->retrievalTerminated();
@@ -1574,6 +1581,7 @@ void ImapService::accountsUpdated(const QMailAccountIdList &ids)
ImapService::~ImapService()
{
disable();
+ destroyIdleSession();
delete _source;
}
@@ -1617,19 +1625,30 @@ bool ImapService::cancelOperation(QMailServiceAction::Status::ErrorCode code, co
void ImapService::restartPushEmail()
{
+ qMailLog(Messaging) << "Attempting to restart push email for account" << _accountId
+ << QMailAccount(_accountId).name();
cancelOperation(QMailServiceAction::Status::ErrInternalStateReset, tr("Initiating push email"));
initiatePushEmail();
}
void ImapService::initiatePushEmail()
{
- qMailLog(Messaging) << "Attempting to establish push email for account" << _accountId
- << QMailAccount(_accountId).name();
_restartPushEmailTimer->stop();
_initiatePushEmailTimer->stop();
+ setPersistentConnectionStatus(false);
+
+ if (!_networkSession || _networkSession->state() != QNetworkSession::Connected) {
+ createIdleSession();
+ return;
+ }
+
+ qMailLog(Messaging) << "Attempting to establish push email for account" << _accountId
+ << QMailAccount(_accountId).name();
QMailFolderIdList ids(_client->configurationIdleFolderIds());
if (ids.count()) {
_establishingPushEmail = true;
+ setPersistentConnectionStatus(true);
+
foreach(QMailFolderId id, ids) {
// Check for flag changes and new mail
_source->queueFlagsChangedCheck(id);
@@ -1677,6 +1696,214 @@ void ImapService::updateStatus(const QString &text)
updateStatus(QMailServiceAction::Status::ErrNoError, text, _accountId);
}
+void ImapService::createIdleSession()
+{
+ if (!_networkConfigManager) {
+ _networkConfigManager = new QNetworkConfigurationManager(this);
+ connect(_networkConfigManager, SIGNAL(onlineStateChanged(bool)),
+ SLOT(onOnlineStateChanged(bool)));
+ // Fail after 10 sec if no network reply is received
+ _networkSessionTimer->setSingleShot(true);
+ _networkSessionTimer->setInterval(10000);
+ connect(_networkSessionTimer,SIGNAL(timeout()),
+ this,SLOT(onSessionConnectionTimeout()));
+ }
+ openIdleSession();
+}
+
+void ImapService::destroyIdleSession()
+{
+ qMailLog(Messaging) << "IDLE Session: Destroying IDLE network session";
+
+ if (_networkSession) {
+ closeIdleSession();
+ }
+
+ delete _networkConfigManager;
+ _networkConfigManager = 0;
+}
+
+void ImapService::openIdleSession()
+{
+ closeIdleSession();
+ if (_networkConfigManager) {
+ qMailLog(Messaging) << "IDLE Session: Opening...";
+ QNetworkConfiguration netConfig = _networkConfigManager->defaultConfiguration();
+
+ if (!netConfig.isValid()) {
+ qMailLog(Messaging) << "IDLE Session: default configuration is not valid, looking for another...";
+ foreach (const QNetworkConfiguration & cfg, _networkConfigManager->allConfigurations()) {
+ if (cfg.isValid()) {
+ netConfig = cfg;
+ break;
+ }
+ }
+ if (!netConfig.isValid()) {
+ qWarning() << "IDLE Session:: no valid configuration found";
+ return;
+ }
+ }
+
+ _networkSession = new QNetworkSession(netConfig);
+
+ connect(_networkSession, SIGNAL(error(QNetworkSession::SessionError)),
+ SLOT(onSessionError(QNetworkSession::SessionError)));
+ connect(_networkSession, SIGNAL(opened()), this, SLOT(onSessionOpened()));
+
+ _networkSession->open();
+ // This timer will cancel the IDLE session if no network
+ // connection can be established in a given amount of time.
+ _networkSessionTimer->start();
+ } else {
+ qMailLog(Messaging) << "IDLE session error: Invalid network configuration manager";
+ createIdleSession();
+ }
+}
+
+void ImapService::closeIdleSession()
+{
+ if (_networkSession) {
+ qMailLog(Messaging) << "IDLE Session: Closing...";
+ _networkSession->disconnect();
+ _networkSession->close();
+ delete _networkSession;
+ _networkSession = 0;
+ }
+ _networkSessionTimer->stop();
+ _networkSessionTimer->disconnect();
+}
+
+void ImapService::onOnlineStateChanged(bool isOnline)
+{
+ qMailLog(Messaging) << "IDLE Session: Network state changed: " << isOnline;
+ if (accountPushEnabled() && isOnline
+ && (!_networkSession
+ || _networkSession->state() != QNetworkSession::Connected)) {
+ openIdleSession();
+ } else if (!isOnline) {
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ closeIdleSession();
+ }
+}
+
+void ImapService::onSessionOpened()
+{
+ if (!_networkSession || sender() != _networkSession) return;
+
+ // stop timer
+ _networkSessionTimer->stop();
+ _networkSessionTimer->disconnect();
+
+ qMailLog(Messaging) << "IDLE session opened, state" << _networkSession->state();
+ connect(_networkSession, SIGNAL(stateChanged(QNetworkSession::State)), this,
+ SLOT(onSessionStateChanged(QNetworkSession::State)));
+
+ if (accountPushEnabled() && !_idling) {
+ restartPushEmail();
+ }
+}
+
+void ImapService::onSessionStateChanged(QNetworkSession::State status)
+{
+ switch (status) {
+ case QNetworkSession::Invalid:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::Invalid";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ case QNetworkSession::NotAvailable:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::NotAvailable";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ case QNetworkSession::Connecting:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::Connecting";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ case QNetworkSession::Connected:
+ qMailLog(Messaging) << "IDLE session connected: QNetworkSession::Connected";
+ break;
+ case QNetworkSession::Closing:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::Closing";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ case QNetworkSession::Disconnected:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::Disconnected";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ case QNetworkSession::Roaming:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::Roaming";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ default:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession:: Unknown status change";
+ onSessionError(QNetworkSession::InvalidConfigurationError);
+ break;
+ }
+}
+
+void ImapService::onSessionError(QNetworkSession::SessionError error)
+{
+ switch (error) {
+ case QNetworkSession::UnknownSessionError:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::UnknownSessionError";
+ break;
+ case QNetworkSession::SessionAbortedError:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::SessionAbortedError";
+ break;
+ case QNetworkSession::RoamingError:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::RoamingError";
+ break;
+ case QNetworkSession::OperationNotSupportedError:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::OperationNotSupportedError";
+ break;
+ case QNetworkSession::InvalidConfigurationError:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession::InvalidConfigurationError";
+ break;
+ default:
+ qMailLog(Messaging) << "IDLE session error: QNetworkSession:: Invalid error code";
+ break;
+ }
+
+ setPersistentConnectionStatus(false);
+ if (_client) {
+ emit _client->sessionError();
+ }
+ closeIdleSession();
+}
+
+void ImapService::onSessionConnectionTimeout()
+{
+ if (_networkSession) {
+ if (!_networkSession->isOpen()) {
+ qWarning() << "IDLE session error: No network reply received after 10 seconds";
+ onSessionError(_networkSession->error());
+ }
+ }
+}
+
+bool ImapService::accountPushEnabled()
+{
+ QMailAccountConfiguration accountCfg(_accountId);
+ ImapConfiguration imapCfg(accountCfg);
+ return imapCfg.pushEnabled();
+}
+
+void ImapService::setPersistentConnectionStatus(bool status)
+{
+ QMailAccount account(_accountId);
+ if (static_cast<bool>(account.status() & QMailAccount::HasPersistentConnection) != status) {
+ account.setStatus(QMailAccount::HasPersistentConnection, status);
+ if (!status) {
+ account.setLastSynchronized(QMailTimeStamp::currentDateTime());
+ }
+ if (!QMailStore::instance()->updateAccount(&account)) {
+ qWarning() << "Unable to update account" << account.id() << "to HasPersistentConnection" << status;
+ } else {
+ qMailLog(Messaging) << "HasPersistentConnection for" << account.id() << "changed to" << status;
+ }
+ }
+ _idling = status;
+}
+
class ImapConfigurator : public QMailMessageServiceConfigurator
{
public:
diff --git a/src/plugins/messageservices/imap/imapservice.h b/src/plugins/messageservices/imap/imapservice.h
index 7b688c52..00682a87 100644
--- a/src/plugins/messageservices/imap/imapservice.h
+++ b/src/plugins/messageservices/imap/imapservice.h
@@ -36,6 +36,13 @@
#include "imapclient.h"
#include <qmailmessageservice.h>
+#include <QNetworkSession>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkConfigurationManager;
+
+QT_END_NAMESPACE
class ImapService : public QMailMessageService
{
@@ -70,16 +77,32 @@ protected slots:
void errorOccurred(QMailServiceAction::Status::ErrorCode code, const QString &text);
void updateStatus(const QString& text);
+ // Only used for IMAP IDLE, network session for other request types are managed by the caller.
+ void createIdleSession();
+ void destroyIdleSession();
+ void openIdleSession();
+ void closeIdleSession();
+
+private slots:
+ void onOnlineStateChanged(bool isOnline);
+ void onSessionOpened();
+ void onSessionStateChanged(QNetworkSession::State status);
+ void onSessionError(QNetworkSession::SessionError error);
+ void onSessionConnectionTimeout();
private:
class Source;
friend class Source;
+ bool accountPushEnabled();
+ void setPersistentConnectionStatus(bool status);
+
QMailAccountId _accountId;
ImapClient *_client;
Source *_source;
QTimer *_restartPushEmailTimer;
bool _establishingPushEmail;
+ bool _idling;
int _pushRetry;
bool _accountWasEnabled;
bool _accountWasPushEnabled;
@@ -88,6 +111,9 @@ private:
enum { ThirtySeconds = 30 };
static QMap<QMailAccountId, int> _initiatePushDelay; // Limit battery consumption
QTimer *_initiatePushEmailTimer;
+ QNetworkConfigurationManager *_networkConfigManager; // Qt network configuration manager
+ QNetworkSession *_networkSession; // Qt network session
+ QTimer *_networkSessionTimer;
};
class ImapServicePlugin : public QMailMessageServicePlugin