diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp index 011d4c0e2a..7cdf655025 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp @@ -64,6 +64,11 @@ public: return fWorker._EnqueueCommand(command); } + void SyncCommandDone() + { + fWorker._SyncCommandDone(); + } + void Quit() { fWorker.fStopped = true; @@ -84,6 +89,10 @@ public: }; +class SyncCommand : public WorkerCommand { +}; + + class QuitCommand : public WorkerCommand { public: QuitCommand() @@ -112,7 +121,7 @@ public: }; -class FetchHeadersCommand : public WorkerCommand, public IMAP::FetchListener { +class FetchHeadersCommand : public SyncCommand, public IMAP::FetchListener { public: FetchHeadersCommand(IMAPFolder& folder, IMAPMailbox& mailbox, uint32 from, uint32 to) @@ -133,9 +142,16 @@ public: if (status != B_OK) return status; + // TODO: this does not scale that well. Over time, the holes in the + // UIDs might become really large + uint32 to = fTo; + if (to - fFrom >= kMaxFetchEntries) + to = fFrom + kMaxFetchEntries - 1; + // TODO: trigger download of mails for all messages below the // body fetch limit - IMAP::FetchCommand fetch(fFrom, fTo, + printf("IMAP: fetch headers from %lu to %lu\n", fFrom, to); + IMAP::FetchCommand fetch(fFrom, to, IMAP::kFetchHeader | IMAP::kFetchFlags); fetch.SetListener(this); @@ -143,9 +159,15 @@ public: if (status != B_OK) return status; + fFrom = to + 1; return B_OK; } + virtual bool IsDone() const + { + return fFrom >= fTo; + } + virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) { fFetchStatus = fFolder.StoreMessage(fFile, fetchFlags, stream, @@ -170,10 +192,11 @@ private: }; -class CheckMailboxesCommand : public WorkerCommand { +class CheckMailboxesCommand : public SyncCommand { public: - CheckMailboxesCommand() + CheckMailboxesCommand(IMAPConnectionWorker& worker) : + fWorker(worker), fFolders(5, false), fState(INIT), fFolder(NULL), @@ -188,8 +211,10 @@ public: if (fState == INIT) { // Collect folders status_t status = WorkerPrivate(worker).AddFolders(fFolders); - if (status != B_OK || fFolders.IsEmpty()) + if (status != B_OK || fFolders.IsEmpty()) { + fState = DONE; return status; + } fState = SELECT; } @@ -228,8 +253,8 @@ public: // UIDs might become really large uint32 from = fLastUID; uint32 to = fNextUID; - if (to - from > kMaxFetchEntries) - to = from + kMaxFetchEntries; + if (to - from >= kMaxFetchEntries) + to = from + kMaxFetchEntries - 1; printf("IMAP: get entries from %lu to %lu\n", from, to); // TODO: we don't really need the flags at this point at all @@ -248,13 +273,13 @@ public: fTotalEntries += entries.size(); fMailboxEntries += entries.size(); - fLastUID = to; + fLastUID = to + 1; if (to == fNextUID) { if (fMailboxEntries > 0) { // Add pending command to fetch the message headers WorkerCommand* command = new FetchHeadersCommand(*fFolder, - *fMailbox, fFirstUID, fLastUID); + *fMailbox, fFirstUID, fNextUID); if (!fFetchCommands.AddItem(command)) delete command; } @@ -278,6 +303,7 @@ private: DONE }; + IMAPConnectionWorker& fWorker; BObjectList fFolders; State fState; IMAPFolder* fFolder; @@ -292,6 +318,38 @@ private: }; +struct CommandDelete +{ + inline void operator()(WorkerCommand* command) + { + delete command; + } +}; + + +/*! An auto deleter similar to ObjectDeleter that called SyncCommandDone() + for all SyncCommands. +*/ +struct CommandDeleter : BPrivate::AutoDeleter +{ + CommandDeleter(IMAPConnectionWorker& worker, WorkerCommand* command) + : + BPrivate::AutoDeleter(command), + fWorker(worker) + { + } + + ~CommandDeleter() + { + if (dynamic_cast(fObject) != NULL) + WorkerPrivate(fWorker).SyncCommandDone(); + } + +private: + IMAPConnectionWorker& fWorker; +}; + + // #pragma mark - @@ -400,8 +458,13 @@ IMAPConnectionWorker::EnqueueCheckSubscribedFolders() status_t IMAPConnectionWorker::EnqueueCheckMailboxes() { + // Do not schedule checking mailboxes again if we're still working on + // those. + if (fSyncPending > 0) + return B_OK; + printf("IMAP: worker %p: enqueue check mailboxes\n", this); - return _EnqueueCommand(new CheckMailboxesCommand()); + return _EnqueueCommand(new CheckMailboxesCommand(*this)); } @@ -450,7 +513,7 @@ IMAPConnectionWorker::_Worker() if (command == NULL) continue; - ObjectDeleter deleter(command); + CommandDeleter deleter(*this, command); status_t status = _Connect(); if (status != B_OK) @@ -484,6 +547,9 @@ IMAPConnectionWorker::_EnqueueCommand(WorkerCommand* command) return B_NO_MEMORY; } + if (dynamic_cast(command) != NULL) + fSyncPending++; + locker.Unlock(); release_sem(fPendingCommandsSemaphore); return B_OK; @@ -534,6 +600,13 @@ IMAPConnectionWorker::_MailboxFor(IMAPFolder& folder) } +void +IMAPConnectionWorker::_SyncCommandDone() +{ + fSyncPending--; +} + + status_t IMAPConnectionWorker::_Connect() { diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.h b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.h index f85536ec57..53a5f0ffda 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.h @@ -61,6 +61,7 @@ private: uint32* _nextUID); IMAPMailbox* _MailboxFor(IMAPFolder& folder); IMAPFolder* _Selected() const { return fSelectedBox; } + void _SyncCommandDone(); status_t _Connect(); void _Disconnect(); @@ -76,6 +77,7 @@ private: IMAP::Protocol fProtocol; sem_id fPendingCommandsSemaphore; WorkerCommandList fPendingCommands; + uint32 fSyncPending; IMAP::ExistsHandler fExistsHandler; IMAP::ExpungeHandler fExpungeHandler;