File indexing completed on 2024-06-23 05:21:14

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 #ifndef IMAP_KEEPMAILBOXOPENTASK_H
0024 #define IMAP_KEEPMAILBOXOPENTASK_H
0025 
0026 #include <QModelIndex>
0027 #include <QSet>
0028 #include "ImapTask.h"
0029 
0030 class QTimer;
0031 class ImapModelIdleTest;
0032 class LibMailboxSync;
0033 
0034 namespace Imap
0035 {
0036 
0037 class Parser;
0038 
0039 namespace Mailbox
0040 {
0041 
0042 class DeleteMailboxTask;
0043 class ObtainSynchronizedMailboxTask;
0044 class IdleLauncher;
0045 class FetchMsgMetadataTask;
0046 class FetchMsgPartTask;
0047 class TreeItemMailbox;
0048 class UnSelectTask;
0049 
0050 /** @short Maintain a connection to a mailbox
0051 
0052 This Task shall maintain a connection to a remote mailbox, updating the mailbox state while receiving various messages from the
0053 IMAP server.
0054 
0055 Essentially, this Task is responsible for processing stuff like EXPUNGE replies while the mailbox is selected. It's a bit special
0056 task because it will not emit completed() unless something else wants to re-use its Parser instance.
0057 
0058 There are four sorts of tasks:
0059 
0060 1) Those that require a synchronized mailbox connection and are already running
0061 2) Those that require such a connection but are not running yet
0062 3) Tasks which are going to replace this KeepMailboxOpenTask when it's done
0063 4) Tasks that do not need any particular mailbox; they can work no matter if there's any mailbox opened
0064 
0065 Of these sorts, 4) are relevant only when dispatching the IDLE command (as IDLE can only run when nothing else is using this
0066 connection because it is implemented as a "command in progress" thing) and also when deciding whether we can die already.
0067 
0068 Sorts 2) and 3) have a common feature -- they are somehow "waiting" for their turn, so that they could get their job done.
0069 They will start when this KeepMailboxOpenTask asks them to start.
0070 
0071 Finally, tasks of the first kind shall not be treated as "children of the KeepMailboxOpenTask", but shall be able to keep the
0072 KeepMailboxOpenTask from disappearing.
0073 
0074 An "active task" means that a task is receiving the server's responses.  An active task in this sense is always present in the
0075 corresponding ParserState's activeTasks list.  These tasks have their ImapTask::parentTask set to zero because they are no
0076 longer waiting for their chance to run, they are running already.  They are also *not* included in any other task's
0077 dependentTasks -- simply because the sanity rule says that:
0078 
0079     (ImapTask::parentTask is non-zero) <=> (the parentTask::dependingTasks contains that task)
0080 
0081 This approach is also used for visualization of the task tree.
0082 
0083 This is the mapping of the task sorts into the places which keep track of them:
0084 
0085 * If the KeepTask is already active, the 4) goes straight to the ParserState::activeTasks and remain there until they terminate.
0086 In case the task isn't active yet, the tasks remain waiting in dependingTasksNoMailbox until the KeepTask gets activated.
0087 * The 3) gets added to the waitingKeepTasks *and* to the dependentTasks. They remain there until the time this
0088 KeepMailboxOpenTask terminates. At that time, the first of them gets activated while the others get prepended to the first
0089 task's waitingKeepTasks & dependentTasks list.
0090 * The 2) are found in the dependentTasks. They used to be present in dependingTasksForThisMailbox as well, but got removed when they
0091 got started.
0092 * The 1) originally existed as 2), but got run at some time.  When they got run, they got also added to the
0093 ParserState::activeTasks, but vanished from this KeepMailboxOpenTask::dependentTasks.  However, to prevent this
0094 KeepMailboxOpenTask from disappearing, they are also kept in the runningTasksForThisMailbox list.
0095 
0096 
0097 The KeepMailboxOpenTask can be in one of the following four states:
0098 
0099 - Waiting for its synchronizeConn to finish. Nothing else can happen during this phase.
0100 - Resynchronizing changes which have happened to the mailbox while the KeepMailboxOpenTask was in charge. In this state, other
0101 commands can be scheduled and executing.
0102 - Not doing anything on its own. Various tasks can be scheduled from this state.
0103 - An ObtainSynchronizedMailboxTask is waiting to replace us. Existing tasks are allowed to finish, tasks for this mailbox are
0104 still accepted and will be executed and tasks which aren't dependent on this mailbox can also run.  When everything finishes, the
0105 waiting task will replace this one.
0106 - Finally, the KeepMailboxOpenTask can be executing the IDLE command. Nothing else can ever run from this context.
0107 
0108 */
0109 
0110 class KeepMailboxOpenTask : public ImapTask
0111 {
0112     Q_OBJECT
0113 public:
0114     /** @short Create new task for maintaining a mailbox
0115 
0116     @arg mailboxIndex the new mailbox to open and keep open
0117 
0118     @arg formerMailbox the mailbox which was kept open by the previous KeepMailboxOpenTask;
0119     that mailbox now loses its KeepMailboxOpenTask and the underlying parser is reused for this task
0120     */
0121     KeepMailboxOpenTask(Model *model, const QModelIndex &mailboxIndex, Parser *oldParser);
0122 
0123     void abort() override;
0124     void die(const QString &message) override;
0125 
0126     void stopForLogout();
0127 
0128     /** @short Start child processes
0129 
0130     This function is called when the synchronizing task finished successfully, that is, when we are ready
0131     to execute regular tasks which depend on us.
0132     */
0133     void perform() override;
0134 
0135     /** @short Add any other task which somehow needs our current mailbox
0136 
0137     This function also automatically registers the depending task in a special list which will make
0138     sure that we won't emit finished() until all the dependant tasks have finished. This essnetially
0139     prevents replacing an "alive" KeepMailboxOpenTask with a different one.
0140     */
0141     void addDependentTask(ImapTask *task) override;
0142 
0143     /** @short Make sure to re-open the mailbox, even if it is already open */
0144     void resynchronizeMailbox();
0145 
0146     QString debugIdentification() const override;
0147 
0148     void requestPartDownload(const uint uid, const QByteArray &partId, const uint estimatedSize);
0149     /** @short Request a delayed loading of a message envelope */
0150     void requestEnvelopeDownload(const uint uid);
0151 
0152     QVariant taskData(const int role) const override;
0153 
0154     bool needsMailbox() const override {return true;}
0155 
0156     bool isReadyToTerminate() const;
0157 
0158     void feelFreeToAbortCaller(ImapTask *task);
0159 
0160     bool hasItsOwnActivity() const;
0161 
0162 private slots:
0163     void slotTaskDeleted(QObject *object);
0164 
0165     /** @short Start mailbox synchronization process
0166 
0167     This function is called when we know that the underlying Parser is no longer in active use
0168     in any mailbox and that it is ready to be used for our purposes. It doesn't matter if that
0169     happened because the older KeepMailboxOpenTask got finished or because new connection got
0170     established and entered the authenticated state; the important part is that we should
0171     initialize synchronization now.
0172     */
0173     void slotPerformConnection();
0174 
0175     /** @short The synchronization is done, let's start working now */
0176     void slotSyncHasCompleted() { perform(); }
0177 
0178     bool handleNumberResponse(const Imap::Responses::NumberResponse *const resp) override;
0179     bool handleFetch(const Imap::Responses::Fetch *const resp) override;
0180     bool handleStateHelper(const Imap::Responses::State *const resp) override;
0181     bool handleFlags(const Imap::Responses::Flags *const resp) override;
0182     bool handleVanished(const Responses::Vanished *const resp) override;
0183     bool handleResponseCodeInsideState(const Imap::Responses::State *const resp);
0184 
0185     void slotPerformNoop();
0186     void slotActivateTasks() { activateTasks(); }
0187     void slotFetchRequestedParts();
0188     /** @short Fetch the ENVELOPEs which were queued for later retrieval */
0189     void slotFetchRequestedEnvelopes();
0190 
0191     /** @short Something bad has happened to the connection, and we're no longer in that mailbox */
0192     void slotUnselected();
0193 
0194     void terminate();
0195 
0196     void finalizeTermination();
0197 
0198     void syncingTimeout();
0199 
0200 private:
0201     /** @short Activate the dependent tasks while also limiting the rate */
0202     void activateTasks();
0203 
0204     /** @short If there's an IDLE running, be sure to stop it. If it's queued, delay it. */
0205     void breakOrCancelPossibleIdle();
0206 
0207     /** @short Check current mailbox for validity, and take an evasive action if it disappeared
0208 
0209     This is an equivalent of ObtainSynchronizedMailboxTask::dieIfInvalidMailbox. It will check whether
0210     the underlying index is still valid, and do best to detach from this mailbox if the index disappears.
0211     More details about why this is needed along the fix to ObtainSynchronizedMailboxTask can be found in
0212     issue #124.
0213 
0214     @see ObtainSynchronizedMailboxTask::dieIfInvalidMailbox
0215     */
0216     bool dieIfInvalidMailbox();
0217 
0218     /** @short Close mailbox forcifully, destroying its content in the process
0219 
0220     Closing is performed via the CLOSE command, which is mandatory in all IMAP implementations. It has the ugly
0221     side effect of removing any messages marked as \\Deleted. That's why this is a private method, only to be used
0222     by the DeleteMailboxTask.
0223     */
0224     void closeMailboxDestructively();
0225 
0226     /** @short Return true if this has a list of stuff to do */
0227     bool hasPendingInternalActions() const;
0228 
0229     void detachFromMailbox();
0230 
0231     bool canRunIdleRightNow() const;
0232 
0233     void saveSyncStateNowOrLater(Imap::Mailbox::TreeItemMailbox *mailbox);
0234     void saveSyncStateIfPossible(Imap::Mailbox::TreeItemMailbox *mailbox);
0235 
0236 protected:
0237     void killAllPendingTasks(const QString &message) override;
0238 
0239     QPersistentModelIndex mailboxIndex;
0240 
0241     /** @short Future maintaining tasks which are waiting for their opportunity to run
0242 
0243     A list of KeepMailboxOpenTask which would like to use this connection to the IMAP server for conducting their business.  They
0244     have to wait until this KeepMailboxOpenTask finished whatever it has to do.
0245     */
0246     QList<ObtainSynchronizedMailboxTask *> waitingObtainTasks;
0247 
0248     /** @short List of pending or running tasks which require this mailbox
0249 
0250     */
0251     QList<ImapTask *> runningTasksForThisMailbox;
0252     /** @short Contents of the dependentTasks without the waitingObtainTasks */
0253     QList<ImapTask *> dependingTasksForThisMailbox;
0254     /** @short Depending tasks which don't need this mailbox */
0255     QList<ImapTask *> dependingTasksNoMailbox;
0256     /** @short Tasks which shall be aborted when the mailbox is to be abandoned */
0257     QList<ImapTask *> abortableTasks;
0258     /** @short An ImapTask that will be started to actually sync to a mailbox once the connection is free */
0259     ObtainSynchronizedMailboxTask *synchronizeConn;
0260 
0261     bool shouldExit;
0262     enum class Running {
0263         NOT_YET,
0264         RUNNING,
0265         NOT_ANYMORE,
0266     };
0267     Running isRunning;
0268 
0269     QTimer *noopTimer;
0270     QTimer *fetchPartTimer;
0271     QTimer *fetchEnvelopeTimer;
0272     bool shouldRunNoop;
0273     bool shouldRunIdle;
0274     IdleLauncher *idleLauncher;
0275     QList<FetchMsgPartTask *> fetchPartTasks;
0276     QList<FetchMsgMetadataTask *> fetchMetadataTasks;
0277     QPointer<DeleteMailboxTask> m_deleteCurrentMailboxTask;
0278     CommandHandle tagIdle;
0279     QList<CommandHandle> newArrivalsFetch;
0280     CommandHandle tagClose;
0281     friend class IdleLauncher;
0282     friend class ImapTask; // needs access to slotTaskDeleted()
0283     friend class ObtainSynchronizedMailboxTask; // needs access to slotUnSelectCompleted()
0284     friend class SortTask; // needs access to breakOrCancelPossibleIdle()
0285     friend class UnSelectTask; // needs access to breakPossibleIdle()
0286     friend class DeleteMailboxTask; // needs access to the closeMailboxDestructively()
0287     friend class TreeItemMailbox; // wants to know if our index is OK
0288     friend class ::ImapModelIdleTest;
0289     friend class ::LibMailboxSync;
0290 
0291     QMap<uint, QSet<QByteArray> > requestedParts;
0292     QMap<uint, uint> requestedPartSizes;
0293     /** @short UIDs of messages with pending FetchMsgMetadataTask request
0294 
0295     QList is used in preference to the QSet in an attempt to maintain the order of requests. Simply ordering via UID is
0296     not enough because of output sorting, threads etc etc.
0297     */
0298     Imap::Uids requestedEnvelopes;
0299 
0300     uint limitBytesAtOnce;
0301     int limitMessagesAtOnce;
0302     int limitParallelFetchTasks;
0303     int limitActiveTasks;
0304 
0305     /** @short An UNSELECT task, if active */
0306     UnSelectTask *unSelectTask;
0307 
0308     /** @short Number of skipped syncing the mailbox state since the last performed sync */
0309     uint m_skippedStateSynces;
0310     /** @short Number of times the sync state got saved since the last timer reset */
0311     uint m_performedStateSynces;
0312     /** @short Tracking time since the last reset of our counters */
0313     QTimer *m_syncingTimer;
0314 };
0315 
0316 }
0317 }
0318 
0319 #endif // IMAP_KEEPMAILBOXOPENTASK_H