File indexing completed on 2024-11-17 04:45:06
0001 /* 0002 SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org> 0003 SPDX-FileCopyrightText: 2008 Omat Holding B.V. <info@omat.nl> 0004 SPDX-FileCopyrightText: 2009 Kevin Ottens <ervin@kde.org> 0005 0006 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0007 SPDX-FileContributor: Kevin Ottens <kevin@kdab.com> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include "imapidlemanager.h" 0013 0014 #include "imapresource_debug.h" 0015 0016 #include <KIMAP/IdleJob> 0017 #include <KIMAP/SelectJob> 0018 #include <KIMAP/Session> 0019 0020 #include <QTimer> 0021 0022 #include "imapresource.h" 0023 #include "sessionpool.h" 0024 0025 #include <chrono> 0026 0027 namespace 0028 { 0029 // RFC2177 says clients should restart IDLE every 29 minutes, as 0030 // servers MAY consider clients inactive after 30 minutes. 0031 // TODO: Make configurable to support less RF-conformant servers 0032 static const auto IdleTimeout = std::chrono::minutes(29); 0033 } 0034 0035 ImapIdleManager::ImapIdleManager(ResourceStateInterface::Ptr state, SessionPool *pool, ImapResourceBase *parent) 0036 : QObject(parent) 0037 , m_sessionRequestId(0) 0038 , m_pool(pool) 0039 , m_resource(parent) 0040 , m_state(state) 0041 , m_lastMessageCount(-1) 0042 , m_lastRecentCount(-1) 0043 { 0044 connect(pool, &SessionPool::sessionRequestDone, this, &ImapIdleManager::onSessionRequestDone); 0045 m_sessionRequestId = m_pool->requestSession(); 0046 0047 m_idleTimeout = new QTimer(this); 0048 m_idleTimeout->setSingleShot(true); 0049 connect(m_idleTimeout, &QTimer::timeout, this, &ImapIdleManager::restartIdle); 0050 } 0051 0052 ImapIdleManager::~ImapIdleManager() 0053 { 0054 stop(); 0055 if (m_pool) { 0056 if (m_sessionRequestId) { 0057 m_pool->cancelSessionRequest(m_sessionRequestId); 0058 } 0059 if (m_session) { 0060 m_pool->releaseSession(m_session); 0061 } 0062 } 0063 } 0064 0065 void ImapIdleManager::stop() 0066 { 0067 m_idleTimeout->stop(); 0068 if (m_idle) { 0069 m_idle->stop(); 0070 disconnect(m_idle, nullptr, this, nullptr); 0071 m_idle = nullptr; 0072 } 0073 if (m_pool) { 0074 disconnect(m_pool, nullptr, this, nullptr); 0075 } 0076 } 0077 0078 KIMAP::Session *ImapIdleManager::session() const 0079 { 0080 return m_session; 0081 } 0082 0083 void ImapIdleManager::reconnect() 0084 { 0085 qCDebug(IMAPRESOURCE_LOG) << "attempting to reconnect IDLE session"; 0086 if (m_session == nullptr && m_pool->isConnected() && m_sessionRequestId == 0) { 0087 m_sessionRequestId = m_pool->requestSession(); 0088 } 0089 } 0090 0091 void ImapIdleManager::onSessionRequestDone(qint64 requestId, KIMAP::Session *session, int errorCode, const QString & /*errorString*/) 0092 { 0093 if (requestId != m_sessionRequestId || session == nullptr || errorCode != SessionPool::NoError) { 0094 return; 0095 } 0096 0097 m_session = session; 0098 m_sessionRequestId = 0; 0099 0100 connect(m_pool, &SessionPool::connectionLost, this, &ImapIdleManager::onConnectionLost); 0101 connect(m_pool, &SessionPool::disconnectDone, this, &ImapIdleManager::onPoolDisconnect); 0102 0103 startIdle(); 0104 } 0105 0106 void ImapIdleManager::startIdle() 0107 { 0108 const auto idleMailBox = m_state->mailBoxForCollection(m_state->collection()); 0109 if (m_session->selectedMailBox() != idleMailBox) { 0110 auto select = new KIMAP::SelectJob(m_session); 0111 select->setMailBox(idleMailBox); 0112 connect(select, &KIMAP::SelectJob::result, this, &ImapIdleManager::onSelectDone); 0113 select->start(); 0114 } 0115 0116 m_idle = new KIMAP::IdleJob(m_session); 0117 connect(m_idle.data(), &KIMAP::IdleJob::mailBoxStats, this, &ImapIdleManager::onStatsReceived); 0118 connect(m_idle.data(), &KIMAP::IdleJob::mailBoxMessageFlagsChanged, this, &ImapIdleManager::onFlagsChanged); 0119 connect(m_idle.data(), &KIMAP::IdleJob::result, this, &ImapIdleManager::onIdleStopped); 0120 m_idle->start(); 0121 m_idleTimeout->start(std::chrono::milliseconds(IdleTimeout).count()); 0122 } 0123 0124 void ImapIdleManager::restartIdle() 0125 { 0126 qCDebug(IMAPRESOURCE_LOG) << "Restarting IDLE to prevent server from disconnecting me!"; 0127 if (m_idle) { 0128 m_idle->stop(); // this will invoke onIdleStopped(), which will automatically reconnect 0129 } 0130 } 0131 0132 void ImapIdleManager::onConnectionLost(KIMAP::Session *session) 0133 { 0134 if (session == m_session) { 0135 // Our session becomes invalid, so get ride of 0136 // the pointer, we don't need to release it once the 0137 // task is done 0138 m_session = nullptr; 0139 QMetaObject::invokeMethod(this, &ImapIdleManager::reconnect, Qt::QueuedConnection); 0140 } 0141 } 0142 0143 void ImapIdleManager::onPoolDisconnect() 0144 { 0145 // All the sessions in the pool we used changed, 0146 // so get ride of the pointer, we don't need to 0147 // release our session anymore 0148 m_pool = nullptr; 0149 } 0150 0151 void ImapIdleManager::onSelectDone(KJob *job) 0152 { 0153 auto select = static_cast<KIMAP::SelectJob *>(job); 0154 0155 m_lastMessageCount = select->messageCount(); 0156 m_lastRecentCount = select->recentCount(); 0157 } 0158 0159 void ImapIdleManager::onIdleStopped() 0160 { 0161 qCDebug(IMAPRESOURCE_LOG) << "IDLE dropped maybe we should reconnect?"; 0162 m_idle = nullptr; 0163 if (m_session) { 0164 qCDebug(IMAPRESOURCE_LOG) << "Restarting the IDLE session!"; 0165 startIdle(); 0166 } 0167 } 0168 0169 void ImapIdleManager::onStatsReceived(KIMAP::IdleJob *job, const QString &mailBox, int messageCount, int recentCount) 0170 { 0171 qCDebug(IMAPRESOURCE_LOG) << "IDLE stats received:" << job << mailBox << messageCount << recentCount; 0172 qCDebug(IMAPRESOURCE_LOG) << "Cached information:" << m_state->collection().remoteId() << m_state->collection().id() << m_lastMessageCount 0173 << m_lastRecentCount; 0174 0175 // It seems we're not in sync with the cache, resync is needed 0176 if (messageCount != m_lastMessageCount || recentCount != m_lastRecentCount) { 0177 m_lastMessageCount = messageCount; 0178 m_lastRecentCount = recentCount; 0179 0180 qCDebug(IMAPRESOURCE_LOG) << "Resync needed for" << mailBox << m_state->collection().id(); 0181 m_resource->synchronizeCollection(m_state->collection().id()); 0182 } 0183 } 0184 0185 void ImapIdleManager::onFlagsChanged(KIMAP::IdleJob *job) 0186 { 0187 Q_UNUSED(job) 0188 qCDebug(IMAPRESOURCE_LOG) << "IDLE flags changed in" << m_session->selectedMailBox(); 0189 m_resource->synchronizeCollection(m_state->collection().id()); 0190 } 0191 0192 #include "moc_imapidlemanager.cpp"