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