1 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
2 
3    This file is part of the Trojita Qt IMAP e-mail client,
4    http://trojita.flaska.net/
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License as
8    published by the Free Software Foundation; either version 2 of
9    the License or (at your option) version 3 or any later version
10    accepted by the membership of KDE e.V. (or its successor approved
11    by the membership of KDE e.V.), which shall act as a proxy
12    defined in Section 14 of version 3 of the license.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <QTimer>
24 #include "IdleLauncher.h"
25 #include "Imap/Model/Model.h"
26 #include "KeepMailboxOpenTask.h"
27 
28 namespace Imap
29 {
30 namespace Mailbox
31 {
32 
IdleLauncher(KeepMailboxOpenTask * parent)33 IdleLauncher::IdleLauncher(KeepMailboxOpenTask *parent):
34     QObject(parent), task(parent), m_idling(false), m_idleCommandRunning(false)
35 {
36     delayedEnter = new QTimer(this);
37     delayedEnter->setObjectName(QStringLiteral("%1-IdleLauncher-delayedEnter").arg(task->objectName()));
38     delayedEnter->setSingleShot(true);
39     // It's a question about what timeout to set here -- if it's too long, we enter IDLE too soon, before the
40     // user has a chance to click on a message, but if we set it too long, we needlessly wait too long between
41     // we receive updates, and also between terminating one IDLE and starting another.
42     // 6 seconds is a compromise here.
43     bool ok;
44     int timeout = parent->model->property("trojita-imap-idle-delayedEnter").toUInt(&ok);
45     if (! ok)
46         timeout = 6 * 1000;
47     delayedEnter->setInterval(timeout);
48     connect(delayedEnter, &QTimer::timeout, this, &IdleLauncher::slotEnterIdleNow);
49     renewal = new QTimer(this);
50     renewal->setObjectName(QStringLiteral("%1-IdleLauncher-renewal").arg(task->objectName()));
51     renewal->setSingleShot(true);
52     timeout = parent->model->property("trojita-imap-idle-renewal").toUInt(&ok);
53     if (! ok)
54         timeout = 1000 * 29 * 60; // 29 minutes -- that's the longest allowed time to IDLE
55     renewal->setInterval(timeout);
56     connect(renewal, &QTimer::timeout, this, &IdleLauncher::slotTerminateLongIdle);
57 }
58 
slotEnterIdleNow()59 void IdleLauncher::slotEnterIdleNow()
60 {
61     delayedEnter->stop();
62     renewal->stop();
63 
64     if (m_idleCommandRunning) {
65         enterIdleLater();
66         return;
67     }
68 
69     Q_ASSERT(task->parser);
70     Q_ASSERT(! m_idling);
71     Q_ASSERT(! m_idleCommandRunning);
72     Q_ASSERT(task->tagIdle.isEmpty());
73     task->tagIdle = task->parser->idle();
74     renewal->start();
75     m_idling = true;
76     m_idleCommandRunning = true;
77 }
78 
finishIdle()79 void IdleLauncher::finishIdle()
80 {
81     Q_ASSERT(task->parser);
82     if (m_idling) {
83         renewal->stop();
84         task->parser->idleDone();
85         m_idling = false;
86     } else if (delayedEnter->isActive()) {
87         delayedEnter->stop();
88     }
89 }
90 
slotTerminateLongIdle()91 void IdleLauncher::slotTerminateLongIdle()
92 {
93     if (m_idling)
94         finishIdle();
95 }
96 
enterIdleLater()97 void IdleLauncher::enterIdleLater()
98 {
99     if (m_idling)
100         return;
101 
102     delayedEnter->start();
103 }
104 
die()105 void IdleLauncher::die()
106 {
107     delayedEnter->stop();
108     delayedEnter->disconnect();
109     renewal->stop();
110     renewal->disconnect();
111 }
112 
idling() const113 bool IdleLauncher::idling() const
114 {
115     return m_idling;
116 }
117 
waitingForIdleTaggedTermination() const118 bool IdleLauncher::waitingForIdleTaggedTermination() const
119 {
120     return m_idleCommandRunning;
121 }
122 
idleCommandCompleted()123 void IdleLauncher::idleCommandCompleted()
124 {
125     // FIXME: these asseerts could be triggered by a rogue server...
126     if (m_idling) {
127         task->log(QStringLiteral("Warning: IDLE completed before we could ask for its termination..."), Common::LOG_MAILBOX_SYNC);
128         m_idling = false;
129         renewal->stop();
130         task->parser->idleMagicallyTerminatedByServer();
131     }
132     Q_ASSERT(m_idleCommandRunning);
133     m_idleCommandRunning = false;
134 }
135 
idleCommandFailed()136 void IdleLauncher::idleCommandFailed()
137 {
138     // FIXME: these asseerts could be triggered by a rogue server...
139     Q_ASSERT(m_idling);
140     Q_ASSERT(m_idleCommandRunning);
141     renewal->stop();
142     m_idleCommandRunning = false;
143     task->parser->idleContinuationWontCome();
144     die();
145 }
146 
147 }
148 }
149