added 4 changesets to branch 'refs/remotes/axeld-github/imap' old head: 455e6baab2e44265a8c97b12ade9d5047ff7f9b3 new head: 49c03e1c524312652a0e400a7201a10b882efb64 overview: https://github.com/axeld/haiku/compare/455e6ba...49c03e1 ---------------------------------------------------------------------------- d107e0a: mail_daemon: cleanup, 30s auto check startup delay. * Refactored new message retrieval a bit, so that the notification strings aren't duplicated. * The daemon now waits 30 seconds before doing the first mail check. 8615ddf: IMAP: make the body fetch limit setting available. * This is set by the ProtocolConfigView -- there should really be some constant for this. 636ddb7: IMAP: if connecting fails, try again a few times. * Also documented SyncCommand. 49c03e1: IMAP: work in progress of downloading the mail body. * Most things are in place now, we just try to download the body to the wrong file, as the final location is currently unknown. * Added local only kPartialMessage flag for mails, but it's not being used yet. [ Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> ] ---------------------------------------------------------------------------- 12 files changed, 386 insertions(+), 114 deletions(-) .../imap/IMAPConnectionWorker.cpp | 120 ++++++++++++++-- .../inbound_protocols/imap/IMAPFolder.cpp | 138 ++++++++++++++----- .../inbound_protocols/imap/IMAPFolder.h | 26 +++- .../inbound_protocols/imap/IMAPMailbox.cpp | 30 ++++ .../inbound_protocols/imap/IMAPMailbox.h | 19 +++ .../inbound_protocols/imap/IMAPProtocol.cpp | 3 +- .../inbound_protocols/imap/IMAPProtocol.h | 3 +- .../inbound_protocols/imap/Settings.cpp | 7 + .../inbound_protocols/imap/Settings.h | 1 + .../inbound_protocols/imap/imap_lib/Commands.h | 12 +- src/servers/mail/MailDaemonApplication.cpp | 135 ++++++++++-------- src/servers/mail/MailDaemonApplication.h | 6 +- ############################################################################ Commit: d107e0acaee11ab692574592e34b8f87e78b365e Author: Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> Date: Wed May 22 21:15:29 2013 UTC mail_daemon: cleanup, 30s auto check startup delay. * Refactored new message retrieval a bit, so that the notification strings aren't duplicated. * The daemon now waits 30 seconds before doing the first mail check. ---------------------------------------------------------------------------- diff --git a/src/servers/mail/MailDaemonApplication.cpp b/src/servers/mail/MailDaemonApplication.cpp index 1ed531e..b34ba1f 100644 --- a/src/servers/mail/MailDaemonApplication.cpp +++ b/src/servers/mail/MailDaemonApplication.cpp @@ -39,8 +39,13 @@ #define B_TRANSLATION_CONTEXT "MailDaemon" +static const uint32 kMsgStartAutoCheck = 'stAC'; static const uint32 kMsgAutoCheck = 'moto'; +static const bigtime_t kStartAutoCheckDelay = 30000000; + // Wait 30 seconds before the first auto check - this usually lets the + // boot process settle down, and give the network a chance to come up. + struct send_mails_info { send_mails_info() @@ -212,56 +217,20 @@ MailDaemonApplication::ReadyToRun() InstallDeskbarIcon(); _InitAccounts(); - _UpdateAutoCheck(fSettingsFile.AutoCheckInterval()); - - BVolume volume; - BVolumeRoster roster; - fNewMessages = 0; + // Start auto mail check with a delay + BMessage startAutoCheck(kMsgStartAutoCheck); + BMessageRunner::StartSending(this, &startAutoCheck, + kStartAutoCheckDelay, 1); - while (roster.GetNextVolume(&volume) == B_OK) { - BQuery* query = new BQuery; - - query->SetTarget(this); - query->SetVolume(&volume); - query->PushAttr(B_MAIL_ATTR_STATUS); - query->PushString("New"); - query->PushOp(B_EQ); - query->PushAttr("BEOS:TYPE"); - query->PushString("text/x-email"); - query->PushOp(B_EQ); - query->PushAttr("BEOS:TYPE"); - query->PushString("text/x-partial-email"); - query->PushOp(B_EQ); - query->PushOp(B_OR); - query->PushOp(B_AND); - query->Fetch(); - - BEntry entry; - while (query->GetNextEntry(&entry) == B_OK) - fNewMessages++; - - fQueries.AddItem(query); - } - - BString string, numString; - if (fNewMessages > 0) { - if (fNewMessages != 1) - string << B_TRANSLATE("%num new messages."); - else - string << B_TRANSLATE("%num new message."); - - numString << fNewMessages; - string.ReplaceFirst("%num", numString); - } else - string = B_TRANSLATE("No new messages"); + _InitNewMessagesCount(); fCentralBeep = false; fNotification = new BNotification(B_INFORMATION_NOTIFICATION); fNotification->SetGroup(B_TRANSLATE("Mail status")); - fNotification->SetTitle(string); fNotification->SetMessageID("daemon_status"); + _UpdateNewMessagesNotification(); app_info info; be_roster->GetAppInfo(B_MAIL_DAEMON_SIGNATURE, &info); @@ -270,7 +239,7 @@ MailDaemonApplication::ReadyToRun() BIconUtils::GetVectorIcon(&node, "BEOS:ICON", &icon); fNotification->SetIcon(&icon); - fLEDAnimation = new LEDAnimation; + fLEDAnimation = new LEDAnimation(); SetPulseRate(1000000); } @@ -307,6 +276,10 @@ void MailDaemonApplication::MessageReceived(BMessage* msg) { switch (msg->what) { + case kMsgStartAutoCheck: + _UpdateAutoCheckRunner(); + break; + case kMsgAutoCheck: // TODO: check whether internet is up and running! @@ -325,7 +298,7 @@ MailDaemonApplication::MessageReceived(BMessage* msg) case kMsgSettingsUpdated: fSettingsFile.Reload(); - _UpdateAutoCheck(fSettingsFile.AutoCheckInterval()); + _UpdateAutoCheckRunner(); break; case kMsgAccountsChanged: @@ -442,21 +415,7 @@ MailDaemonApplication::MessageReceived(BMessage* msg) break; } - BString string, numString; - - if (fNewMessages > 0) { - if (fNewMessages != 1) - string << B_TRANSLATE("%num new messages."); - else - string << B_TRANSLATE("%num new message."); - - numString << fNewMessages; - string.ReplaceFirst("%num", numString); - } - else - string << B_TRANSLATE("No new messages."); - - fNotification->SetTitle(string.String()); + _UpdateNewMessagesNotification(); if (fNotifyMode != B_MAIL_SHOW_STATUS_WINDOW_NEVER) fNotification->Send(); break; @@ -838,8 +797,64 @@ MailDaemonApplication::_OutboundProtocol(int32 account) void -MailDaemonApplication::_UpdateAutoCheck(bigtime_t interval) +MailDaemonApplication::_InitNewMessagesCount() +{ + BVolume volume; + BVolumeRoster roster; + + fNewMessages = 0; + + while (roster.GetNextVolume(&volume) == B_OK) { + BQuery* query = new BQuery; + + query->SetTarget(this); + query->SetVolume(&volume); + query->PushAttr(B_MAIL_ATTR_STATUS); + query->PushString("New"); + query->PushOp(B_EQ); + query->PushAttr("BEOS:TYPE"); + query->PushString("text/x-email"); + query->PushOp(B_EQ); + query->PushAttr("BEOS:TYPE"); + query->PushString("text/x-partial-email"); + query->PushOp(B_EQ); + query->PushOp(B_OR); + query->PushOp(B_AND); + query->Fetch(); + + BEntry entry; + while (query->GetNextEntry(&entry) == B_OK) + fNewMessages++; + + fQueries.AddItem(query); + } +} + + +void +MailDaemonApplication::_UpdateNewMessagesNotification() +{ + BString title; + if (fNewMessages > 0) { + if (fNewMessages != 1) + title << B_TRANSLATE("%num new messages."); + else + title << B_TRANSLATE("%num new message."); + + BString numString; + numString << fNewMessages; + title.ReplaceFirst("%num", numString); + } else + title = B_TRANSLATE("No new messages"); + + fNotification->SetTitle(title.String()); +} + + +void +MailDaemonApplication::_UpdateAutoCheckRunner() { + bigtime_t interval = fSettingsFile.AutoCheckInterval(); if (interval > 0) { if (fAutoCheckRunner != NULL) { fAutoCheckRunner->SetInterval(interval); diff --git a/src/servers/mail/MailDaemonApplication.h b/src/servers/mail/MailDaemonApplication.h index 343c0c6..7960f15 100644 --- a/src/servers/mail/MailDaemonApplication.h +++ b/src/servers/mail/MailDaemonApplication.h @@ -1,5 +1,5 @@ /* - * Copyright 2007-2012, Haiku, Inc. All rights reserved. + * Copyright 2007-2013, Haiku, Inc. All rights reserved. * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved. * Copyright 2011, Clemens Zeidler <haiku@xxxxxxxxxxxxxxxxxx> * Distributed under the terms of the MIT License. @@ -78,7 +78,9 @@ private: BInboundMailProtocol* _InboundProtocol(int32 account); BOutboundMailProtocol* _OutboundProtocol(int32 account); - void _UpdateAutoCheck(bigtime_t interval); + void _InitNewMessagesCount(); + void _UpdateNewMessagesNotification(); + void _UpdateAutoCheckRunner(); void _AddMessage(send_mails_info& info, const BEntry& entry, const BNode& node); ############################################################################ Commit: 8615ddf6df78de41ff16bcb15b5dc4d516a77755 Author: Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> Date: Wed May 22 22:40:27 2013 UTC IMAP: make the body fetch limit setting available. * This is set by the ProtocolConfigView -- there should really be some constant for this. ---------------------------------------------------------------------------- diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.cpp index a5673ea..397d951 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.cpp @@ -101,3 +101,10 @@ Settings::IdleMode() const { return fMessage.GetBool("idle", true); } + + +int32 +Settings::BodyFetchLimit() const +{ + return fMessage.GetInt32("partial_download_limit", -1); +} diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.h b/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.h index 3555e00..40c268c 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/Settings.h @@ -30,6 +30,7 @@ public: int32 MaxConnections() const; bool IdleMode() const; + int32 BodyFetchLimit() const; private: const BMessage fMessage; ############################################################################ Commit: 636ddb712ba5c3713453e773b5e24ecfd393aa5e Author: Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> Date: Wed May 22 22:43:16 2013 UTC IMAP: if connecting fails, try again a few times. * Also documented SyncCommand. ---------------------------------------------------------------------------- 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 7cdf655..79ef13b 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp @@ -89,6 +89,10 @@ public: }; +/*! All commands that inherit from this class will automatically maintain the + worker's fSyncPending member, and will thus prevent syncing more than once + concurrently. +*/ class SyncCommand : public WorkerCommand { }; @@ -613,8 +617,19 @@ IMAPConnectionWorker::_Connect() if (fProtocol.IsConnected()) return B_OK; - status_t status = fProtocol.Connect(fSettings.ServerAddress(), - fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); + status_t status; + int tries = 6; + while (tries-- > 0) { + status = fProtocol.Connect(fSettings.ServerAddress(), + fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); + if (status == B_OK) + break; + + // Wait for 10 seconds, and try again + snooze(10000000); + } + // TODO: if other workers are connected, but it fails for us, we need to + // remove this worker, and reduce the number of concurrent connections if (status != B_OK) return status; ############################################################################ Commit: 49c03e1c524312652a0e400a7201a10b882efb64 Author: Axel Dörfler <axeld@xxxxxxxxxxxxxxxx> Date: Wed May 22 22:50:26 2013 UTC IMAP: work in progress of downloading the mail body. * Most things are in place now, we just try to download the body to the wrong file, as the final location is currently unknown. * Added local only kPartialMessage flag for mails, but it's not being used yet. ---------------------------------------------------------------------------- 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 79ef13b..97add7b 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp @@ -59,6 +59,11 @@ public: return fWorker._MailboxFor(folder); } + int32 BodyFetchLimit() + { + return fWorker.fSettings.BodyFetchLimit(); + } + status_t EnqueueCommand(WorkerCommand* command) { return fWorker._EnqueueCommand(command); @@ -125,15 +130,78 @@ public: }; +class FetchBodiesCommand : public SyncCommand, public IMAP::FetchListener { +public: + FetchBodiesCommand(IMAPFolder& folder, IMAPMailbox& mailbox, + std::vector<uint32>& entries) + : + fFolder(folder), + fMailbox(mailbox), + fEntries(entries) + { + } + + virtual status_t Process(IMAPConnectionWorker& worker) + { + IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); + + if (fEntries.empty()) + return B_OK; + + fUID = *fEntries.begin(); + fEntries.erase(fEntries.begin()); + + // TODO: check nextUID if we get one + status_t status = WorkerPrivate(worker).SelectMailbox(fFolder); + if (status != B_OK) + return status; + + printf("IMAP: fetch body for %lu\n", fUID); + // TODO: combine smaller messages together for faster retrieval + IMAP::FetchCommand fetch(fUID, fUID, IMAP::kFetchBody); + fetch.SetListener(this); + + return protocol.ProcessCommand(fetch); + } + + virtual bool IsDone() const + { + return fEntries.empty(); + } + + virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) + { + fFetchStatus = fFolder.StoreBody(fUID, stream, length, fRef, fFile); + return true; + } + + virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) + { + if (fFetchStatus == B_OK) + fFolder.BodyStored(fRef, fFile, uid); + } + +private: + IMAPFolder& fFolder; + IMAPMailbox& fMailbox; + std::vector<uint32> fEntries; + uint32 fUID; + entry_ref fRef; + BFile fFile; + status_t fFetchStatus; +}; + + class FetchHeadersCommand : public SyncCommand, public IMAP::FetchListener { public: FetchHeadersCommand(IMAPFolder& folder, IMAPMailbox& mailbox, - uint32 from, uint32 to) + uint32 from, uint32 to, int32 bodyFetchLimit) : fFolder(folder), fMailbox(mailbox), fFrom(from), - fTo(to) + fTo(to), + fBodyFetchLimit(bodyFetchLimit) { } @@ -152,8 +220,6 @@ public: if (to - fFrom >= kMaxFetchEntries) to = fFrom + kMaxFetchEntries - 1; - // TODO: trigger download of mails for all messages below the - // body fetch limit printf("IMAP: fetch headers from %lu to %lu\n", fFrom, to); IMAP::FetchCommand fetch(fFrom, to, IMAP::kFetchHeader | IMAP::kFetchFlags); @@ -164,6 +230,13 @@ public: return status; fFrom = to + 1; + + if (IsDone() && !fFetchBodies.empty()) { + // Enqueue command to fetch the message bodies + WorkerPrivate(worker).EnqueueCommand(new FetchBodiesCommand(fFolder, + fMailbox, fFetchBodies)); + } + return B_OK; } @@ -174,15 +247,20 @@ public: virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) { - fFetchStatus = fFolder.StoreMessage(fFile, fetchFlags, stream, - length, fRef); + fFetchStatus = fFolder.StoreMessage(fetchFlags, stream, length, + fRef, fFile); return true; } virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) { - if (fFetchStatus == B_OK) + if (fFetchStatus == B_OK) { fFolder.MessageStored(fRef, fFile, fetchFlags, uid, flags); + + uint32 size = fMailbox.MessageSize(uid); + if (fBodyFetchLimit < 0 || size < fBodyFetchLimit) + fFetchBodies.push_back(uid); + } } private: @@ -190,6 +268,8 @@ private: IMAPMailbox& fMailbox; uint32 fFrom; uint32 fTo; + uint32 fBodyFetchLimit; + std::vector<uint32> fFetchBodies; entry_ref fRef; BFile fFile; status_t fFetchStatus; @@ -269,9 +349,13 @@ public: return status; // Determine how much we need to download + // TODO: also retrieve the header size, and only take the body + // size into account if it's below the limit for (size_t i = 0; i < entries.size(); i++) { printf("%10lu %8lu bytes, flags: %#lx\n", entries[i].uid, entries[i].size, entries[i].flags); + fMailbox->AddMessageEntry(entries[i].uid, entries[i].flags, + entries[i].size); fTotalBytes += entries[i].size; } fTotalEntries += entries.size(); @@ -283,7 +367,8 @@ public: if (fMailboxEntries > 0) { // Add pending command to fetch the message headers WorkerCommand* command = new FetchHeadersCommand(*fFolder, - *fMailbox, fFirstUID, fNextUID); + *fMailbox, fFirstUID, fNextUID, + WorkerPrivate(worker).BodyFetchLimit()); if (!fFetchCommands.AddItem(command)) delete command; } diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp index 6cb060a..a7abf30 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp @@ -132,7 +132,7 @@ IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName, uint32 uid = B_BENDIAN_TO_HOST_INT32(entries[i].uid); uint32 flags = B_BENDIAN_TO_HOST_INT32(entries[i].flags); - fUIDMap.insert(std::make_pair(uid, flags)); + fFlagsMap.insert(std::make_pair(uid, flags)); } } @@ -167,6 +167,29 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity) } +status_t +IMAPFolder::GetMessageEntryRef(uint32 uid, entry_ref& ref) const +{ + UIDToRefMap::const_iterator found = fRefMap.find(uid); + if (found == fRefMap.end()) + return B_ENTRY_NOT_FOUND; + + ref = found->second; + return B_OK; +} + + +uint32 +IMAPFolder::MessageFlags(uint32 uid) const +{ + UIDToFlagsMap::const_iterator found = fFlagsMap.find(uid); + if (found == fFlagsMap.end()) + return 0; + + return found->second; +} + + /*! Stores the given \a stream into a temporary file using the provided BFile object. A new file will be created, and the \a ref object will point to it. The file will remain open when this method exits without @@ -176,8 +199,8 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity) were an error. */ status_t -IMAPFolder::StoreMessage(BFile& file, uint32 fetchFlags, BDataIO& stream, - size_t& length, entry_ref& ref) +IMAPFolder::StoreMessage(uint32 fetchFlags, BDataIO& stream, + size_t& length, entry_ref& ref, BFile& file) { BPath path; status_t status = path.SetTo(&fRef); @@ -189,26 +212,11 @@ IMAPFolder::StoreMessage(BFile& file, uint32 fetchFlags, BDataIO& stream, if (status != B_OK) return status; - char buffer[65535]; - while (length > 0) { - ssize_t bytesRead = stream.Read(buffer, - std::min(sizeof(buffer), length)); - if (bytesRead < 0) - return bytesRead; - if (bytesRead <= 0) - break; - - length -= bytesRead; - - ssize_t bytesWritten = file.Write(buffer, bytesRead); - if (bytesWritten < 0) - return bytesWritten; - if (bytesWritten != bytesRead) - return B_IO_ERROR; - } + status = _WriteStream(file, stream, length); + if (status == B_OK) + temporaryFile.KeepFile(); - temporaryFile.KeepFile(); - return B_OK; + return status; } @@ -223,29 +231,73 @@ IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags, if ((fetchFlags & IMAP::kFetchFlags) != 0) _WriteFlags(file, flags); - // TODO: the call below may move/rename the file - fProtocol.MessageStored(ref, file, fetchFlags); + // TODO: the call below may move/rename the file - this prevents downloading + // the body to the correct file! + fProtocol.MessageStored(*this, ref, file, fetchFlags); file.Unset(); + fRefMap.insert(std::make_pair(uid, ref)); + if (uid > fLastUID) { // Update last known UID fLastUID = uid; BNode directory(&fRef); status_t status = _WriteUInt32(directory, kLastUIDAttribute, uid); - if (status != B_OK) + if (status != B_OK) { fprintf(stderr, "IMAP: Could not write last UID for mailbox " "%s: %s\n", fMailboxName.String(), strerror(status)); + } } } +/*! Appends the given \a stream as body to the message file for the + specified unique ID. The file will remain open when this method exits + without an error. + + \a length will reflect how many bytes are left to read in case there + were an error. +*/ +status_t +IMAPFolder::StoreBody(uint32 uid, BDataIO& stream, size_t& length, + entry_ref& ref, BFile& file) +{ + status_t status = GetMessageEntryRef(uid, ref); + if (status != B_OK) + return status; + + status = file.SetTo(&ref, B_OPEN_AT_END | B_WRITE_ONLY); + if (status != B_OK) + return status; + + BPath path(&ref); + printf("IMAP: write body to %s\n", path.Path()); + + return _WriteStream(file, stream, length); +} + + +/*! Notifies the protocol that a body has been fetched. + This method also closes the \a file passed in. +*/ +void +IMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid) +{ + fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody); + file.Unset(); +} + + void IMAPFolder::DeleteMessage(uint32 uid) { } +/*! Called when the flags of a message changed on the server. This will update + the flags for the local file. +*/ void IMAPFolder::SetMessageFlags(uint32 uid, uint32 flags) { @@ -264,8 +316,8 @@ IMAPFolder::_InitializeFolderState() // Create set of the last known UID state - if an entry is found, it // is being removed from the list. The remaining entries were deleted. std::set<uint32> lastUIDs; - UIDToFlagsMap::iterator iterator = fUIDMap.begin(); - for (; iterator != fUIDMap.end(); iterator++) + UIDToFlagsMap::iterator iterator = fFlagsMap.begin(); + for (; iterator != fFlagsMap.end(); iterator++) lastUIDs.insert(iterator->first); BDirectory directory(&fRef); @@ -286,12 +338,11 @@ IMAPFolder::_InitializeFolderState() // The message is still around lastUIDs.erase(found); - UIDToFlagsMap::iterator flagsFound = fUIDMap.find(uid); - ASSERT(flagsFound != fUIDMap.end()); - if (flagsFound->second != flags) { + uint32 flagsFound = MessageFlags(uid); + if (flagsFound != flags) { // Its flags have changed locally, and need to be updated fListener->MessageFlagsChanged(_Token(uid), ref, - flagsFound->second, flags); + flagsFound, flags); } } else { // This is a new message @@ -376,3 +427,28 @@ IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value) return bytesWritten < 0 ? bytesWritten : B_IO_ERROR; } + + +status_t +IMAPFolder::_WriteStream(BFile& file, BDataIO& stream, size_t& length) +{ + char buffer[65535]; + while (length > 0) { + ssize_t bytesRead = stream.Read(buffer, + std::min(sizeof(buffer), length)); + if (bytesRead < 0) + return bytesRead; + if (bytesRead <= 0) + break; + + length -= bytesRead; + + ssize_t bytesWritten = file.Write(buffer, bytesRead); + if (bytesWritten < 0) + return bytesWritten; + if (bytesWritten != bytesRead) + return B_IO_ERROR; + } + + return B_OK; +} diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h index 6c9100e..997b65c 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h @@ -24,6 +24,11 @@ struct MessageToken { uint32 uid; }; +// Additional local only message flags +enum FolderMessageFlags { + kPartialMessage = 0x00010000, +}; + class FolderListener { public: @@ -51,13 +56,23 @@ public: uint32 LastUID() const { return fLastUID; } - status_t StoreMessage(BFile& file, uint32 fetchFlags, - BDataIO& stream, size_t& length, - entry_ref& ref); + status_t GetMessageEntryRef(uint32 uid, + entry_ref& ref) const; + uint32 MessageFlags(uint32 uid) const; + + status_t StoreMessage(uint32 fetchFlags, BDataIO& stream, + size_t& length, entry_ref& ref, + BFile& file); void MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags, uint32 uid, uint32 flags); + status_t StoreBody(uint32 uid, BDataIO& stream, + size_t& length, entry_ref& ref, + BFile& file); + void BodyStored(entry_ref& ref, BFile& file, + uint32 uid); + void DeleteMessage(uint32 uid); void SetMessageFlags(uint32 uid, uint32 flags); @@ -75,6 +90,9 @@ private: status_t _WriteUInt32(BNode& node, const char* attribute, uint32 value); + status_t _WriteStream(BFile& file, BDataIO& stream, + size_t& length); + private: typedef std::hash_map<uint32, uint32> UIDToFlagsMap; typedef std::hash_map<uint32, entry_ref> UIDToRefMap; @@ -86,7 +104,7 @@ private: uint32 fLastUID; FolderListener* fListener; UIDToRefMap fRefMap; - UIDToFlagsMap fUIDMap; + UIDToFlagsMap fFlagsMap; }; diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.cpp index 0703147..2c07be5 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.cpp @@ -24,6 +24,36 @@ IMAPMailbox::~IMAPMailbox() } +void +IMAPMailbox::AddMessageEntry(uint32 uid, uint32 flags, uint32 size) +{ + fMessageEntries.insert( + std::make_pair(uid, MessageFlagsAndSize(flags, size))); +} + + +uint32 +IMAPMailbox::MessageFlags(uint32 uid) const +{ + MessageEntryMap::const_iterator found = fMessageEntries.find(uid); + if (found == fMessageEntries.end()) + return 0; + + return found->second.flags; +} + + +uint32 +IMAPMailbox::MessageSize(uint32 uid) const +{ + MessageEntryMap::const_iterator found = fMessageEntries.find(uid); + if (found == fMessageEntries.end()) + return 0; + + return found->second.size; +} + + uint32 IMAPMailbox::MessageAdded(const MessageToken& fromToken, const entry_ref& ref) { diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.h b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.h index 46c1a04..352b2de 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPMailbox.h @@ -22,6 +22,11 @@ public: const BString& MailboxName() const { return fMailboxName; } + void AddMessageEntry(uint32 uid, uint32 flags, + uint32 size); + uint32 MessageFlags(uint32 uid) const; + uint32 MessageSize(uint32 uid) const; + // FolderListener interface virtual uint32 MessageAdded(const MessageToken& fromToken, const entry_ref& ref); @@ -32,8 +37,22 @@ public: uint32 newFlags); protected: + struct MessageFlagsAndSize { + MessageFlagsAndSize(uint32 _flags, uint32 _size) + : + flags(_flags), + size(_size) + { + } + + uint32 flags; + uint32 size; + }; + typedef std::hash_map<uint32, MessageFlagsAndSize> MessageEntryMap; + IMAP::Protocol& fProtocol; BString fMailboxName; + MessageEntryMap fMessageEntries; }; diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp index 6b1c5e5..5499c06 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp @@ -123,7 +123,8 @@ IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker) void -IMAPProtocol::MessageStored(entry_ref& ref, BFile& stream, uint32 fetchFlags) +IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream, + uint32 fetchFlags) { if ((fetchFlags & IMAP::kFetchHeader) != 0) NotifyHeaderFetched(ref, &stream); diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h index c3c43a3..531b5c9 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h @@ -34,7 +34,8 @@ public: IMAP::Protocol& protocol, bool idle); void WorkerQuit(IMAPConnectionWorker* worker); - void MessageStored(entry_ref& ref, BFile& stream, + void MessageStored(IMAPFolder& folder, + entry_ref& ref, BFile& stream, uint32 fetchFlags); virtual status_t SyncMessages(); diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h index b47d194..634d2ec 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h @@ -34,12 +34,14 @@ struct MessageEntry { typedef std::vector<MessageEntry> MessageEntryList; enum MessageFlags { - kSeen = 0x01, - kAnswered = 0x02, - kFlagged = 0x04, - kDeleted = 0x08, - kDraft = 0x10 + kSeen = 0x01, + kAnswered = 0x02, + kFlagged = 0x04, + kDeleted = 0x08, + kDraft = 0x10, // \Recent doesn't really have any useful meaning, so we just ignore it + + kServerFlagsMask = 0x0000ffff };