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 746226fffa..fd61d58ab8 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPConnectionWorker.cpp @@ -135,7 +135,8 @@ public: // TODO: trigger download of mails for all messages below the // body fetch limit - IMAP::FetchCommand fetch(fFrom, fTo, IMAP::kFetchHeader); + IMAP::FetchCommand fetch(fFrom, fTo, + IMAP::kFetchHeader | IMAP::kFetchFlags); fetch.SetListener(this); status = protocol.ProcessCommand(fetch); @@ -145,9 +146,9 @@ public: return B_OK; } - virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t length) + virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) { - fFetchStatus = fFolder.StoreTemporaryMessage(fetchFlags, stream, + fFetchStatus = fFolder.StoreMessage(fFile, fetchFlags, stream, length, fRef); return true; } @@ -155,7 +156,7 @@ public: virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) { if (fFetchStatus == B_OK) - fFolder.StoreMessage(fetchFlags, uid, flags, fRef); + fFolder.MessageStored(fRef, fFile, fetchFlags, uid, flags); } private: @@ -164,6 +165,7 @@ private: uint32 fFrom; uint32 fTo; entry_ref fRef; + BFile fFile; status_t fFetchStatus; }; 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 3b7cda6501..0739f20ea6 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.cpp @@ -16,15 +16,76 @@ #include #include +#include "Commands.h" + +#include "IMAPProtocol.h" + static const char* kMailboxNameAttribute = "IMAP:mailbox"; static const char* kUIDValidityAttribute = "IMAP:uidvalidity"; static const char* kStateAttribute = "IMAP:state"; +static const char* kFlagsAttribute = "IMAP:flags"; static const char* kUIDAttribute = "MAIL:unique_id"; -IMAPFolder::IMAPFolder(const BString& mailboxName, const entry_ref& ref) +class TemporaryFile : public BFile { +public: + TemporaryFile(BFile& file) + : + fFile(file), + fDeleteFile(false) + { + } + + ~TemporaryFile() + { + if (fDeleteFile) { + fFile.Unset(); + BEntry(fPath.Path()).Remove(); + } + } + + status_t Init(const BPath& path, entry_ref& ref) + { + int32 tries = 53; + while (true) { + BString name("temp-mail-"); + name << system_time(); + + fPath = path; + fPath.Append(name.String()); + + status_t status = fFile.SetTo(fPath.Path(), + B_CREATE_FILE | B_FAIL_IF_EXISTS | B_READ_WRITE); + if (status == B_FILE_EXISTS && tries-- > 0) + continue; + if (status != B_OK) + return status; + + fDeleteFile = true; + return get_ref_for_path(fPath.Path(), &ref); + } + } + + void KeepFile() + { + fDeleteFile = false; + } + +private: + BFile& fFile; + BPath fPath; + bool fDeleteFile; +}; + + +// #pragma mark - + + +IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName, + const entry_ref& ref) : + fProtocol(protocol), fRef(ref), fMailboxName(mailboxName), fUIDValidity(UINT32_MAX) @@ -97,57 +158,66 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity) } +/*! 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 + an error. + + \a length will reflect how many bytes are left to read in case there + were an error. +*/ status_t -IMAPFolder::StoreTemporaryMessage(uint32 fetchFlags, BDataIO& stream, - size_t length, entry_ref& ref) +IMAPFolder::StoreMessage(BFile& file, uint32 fetchFlags, BDataIO& stream, + size_t& length, entry_ref& ref) { BPath path; status_t status = path.SetTo(&fRef); if (status != B_OK) return status; - path.Append("tmp1234"); - - BFile file; - status = (path.Path(), B_CREATE_FILE); + TemporaryFile temporaryFile(file); + status = temporaryFile.Init(path, ref); if (status != B_OK) return status; - get_ref_for_path(path.Path(), &ref); - char buffer[65535]; while (length > 0) { ssize_t bytesRead = stream.Read(buffer, - min_c(sizeof(buffer), length)); + std::min(sizeof(buffer), length)); + if (bytesRead < 0) + return bytesRead; if (bytesRead <= 0) break; - file.Write(buffer, bytesRead); length -= bytesRead; + + ssize_t bytesWritten = file.Write(buffer, bytesRead); + if (bytesWritten < 0) + return bytesWritten; + if (bytesWritten != bytesRead) + return B_IO_ERROR; } + temporaryFile.KeepFile(); return B_OK; } +/*! Writes UID, and flags to the message, and notifies the protocol that a + message has been fetched. This method also closes the \a file passed in. +*/ void -IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, uint32 flags, - entry_ref& ref) +IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags, + uint32 uid, uint32 flags) { - BString name = "mail-"; - name << uid; + _WriteUniqueID(file, uid); + if ((fetchFlags & IMAP::kFetchFlags) != 0) + _WriteFlags(file, flags); - // TODO: remove renaming as its superfluous here - BEntry entry(&ref); - entry.Rename(name.String()); -} + // TODO: the call below may move/rename the file + fProtocol.MessageStored(ref, file, fetchFlags); - -status_t -IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, BDataIO& stream, - size_t length) -{ - return B_ERROR; + file.Unset(); } @@ -207,8 +277,8 @@ IMAPFolder::_InitializeFolderState() } else { // This is a new message // TODO: the token must be the originating token! - // TODO: uid might be udpated from the call uid = fListener->MessageAdded(_Token(uid), ref); + _WriteUniqueID(node, uid); } fRefMap.insert(std::make_pair(uid, ref)); @@ -240,9 +310,31 @@ IMAPFolder::_ReadUniqueID(BNode& node) } +status_t +IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid) +{ + BString string; + string << uid; + + return node.WriteAttrString(kUIDAttribute, &string); +} + + uint32 IMAPFolder::_ReadFlags(BNode& node) { // TODO! return 0; } + + +status_t +IMAPFolder::_WriteFlags(BNode& node, uint32 flags) +{ + ssize_t bytesWritten = node.WriteAttr(kFlagsAttribute, B_UINT32_TYPE, 0, + &flags, sizeof(uint32)); + if (bytesWritten == (ssize_t)sizeof(uint32)) + return B_OK; + + return bytesWritten < 0 ? bytesWritten : B_IO_ERROR; +} 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 1b8b2e580e..1da9e82292 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolder.h @@ -1,5 +1,5 @@ /* - * Copyright 2012, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2012-2013, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #ifndef IMAP_FOLDER_H @@ -15,6 +15,9 @@ #include +class IMAPProtocol; + + struct MessageToken { BString mailboxName; uint32 uidValidity; @@ -36,7 +39,8 @@ public: class IMAPFolder : public BHandler { public: - IMAPFolder(const BString& mailboxName, + IMAPFolder(IMAPProtocol& protocol, + const BString& mailboxName, const entry_ref& ref); virtual ~IMAPFolder(); @@ -45,13 +49,13 @@ public: void SetListener(FolderListener* listener); void SetUIDValidity(uint32 uidValidity); - status_t StoreTemporaryMessage(uint32 fetchFlags, - BDataIO& stream, size_t length, + status_t StoreMessage(BFile& file, uint32 fetchFlags, + BDataIO& stream, size_t& length, entry_ref& ref); - void StoreMessage(uint32 fetchFlags, uint32 uid, - uint32 flags, entry_ref& ref); - status_t StoreMessage(uint32 fetchFlags, uint32 uid, - BDataIO& stream, size_t length); + void MessageStored(entry_ref& ref, BFile& file, + uint32 fetchFlags, uint32 uid, + uint32 flags); + void DeleteMessage(uint32 uid); void SetMessageFlags(uint32 uid, uint32 flags); @@ -61,12 +65,15 @@ private: void _InitializeFolderState(); const MessageToken _Token(uint32 uid) const; uint32 _ReadUniqueID(BNode& node); + status_t _WriteUniqueID(BNode& node, uint32 uid); uint32 _ReadFlags(BNode& node); + status_t _WriteFlags(BNode& node, uint32 flags); private: typedef std::hash_map UIDToFlagsMap; typedef std::hash_map UIDToRefMap; + IMAPProtocol& fProtocol; const entry_ref fRef; BString fMailboxName; uint32 fUIDValidity; 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 431d77d0de..e6da5f3415 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp @@ -126,6 +126,16 @@ IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker) } +void +IMAPProtocol::MessageStored(entry_ref& ref, BFile& stream, uint32 fetchFlags) +{ + if ((fetchFlags & IMAP::kFetchHeader) != 0) + NotifyHeaderFetched(ref, &stream); + if ((fetchFlags & IMAP::kFetchBody) != 0) + NotifyBodyFetched(ref, &stream); +} + + status_t IMAPProtocol::SyncMessages() { @@ -230,7 +240,7 @@ IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator) return NULL; } - return new IMAPFolder(mailbox, ref); + return new IMAPFolder(*this, mailbox, ref); } 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 77ebf0b8fa..71eed42fe7 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.h @@ -34,6 +34,9 @@ public: IMAP::Protocol& protocol, bool idle); void WorkerQuit(IMAPConnectionWorker* worker); + void MessageStored(entry_ref& ref, BFile& stream, + uint32 fetchFlags); + virtual status_t SyncMessages(); virtual status_t FetchBody(const entry_ref& ref); virtual status_t MarkMessageAsRead(const entry_ref& ref, diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp index 0a60206afc..df42f681b0 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp @@ -402,7 +402,7 @@ FetchCommand::HandleUntagged(Response& response) bool FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments, - BDataIO& stream, size_t length) + BDataIO& stream, size_t& length) { if (fListener == NULL || !response.EqualsAt(1, "FETCH") || !response.IsListAt(2)) 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 428440957b..b47d194680 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 @@ -147,7 +147,7 @@ enum FetchFlags { class FetchListener { public: virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, - size_t length) = 0; + size_t& length) = 0; virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) = 0; }; @@ -166,7 +166,7 @@ public: virtual bool HandleUntagged(Response& response); virtual bool HandleLiteral(Response& response, ArgumentList& arguments, BDataIO& stream, - size_t length); + size_t& length); private: uint32 fFrom; diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp index 446d2c1e6b..c8595846a8 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp @@ -6,6 +6,7 @@ #include "Response.h" +#include #include #include @@ -588,7 +589,7 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream) size); } - if (!handled) { + if (!handled && size <= 65536) { // The default implementation just adds the data as a string TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size); BString string; @@ -612,6 +613,9 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream) string.UnlockBuffer(size); arguments.AddItem(new StringArgument(string)); + } else { + // Skip any bytes left in the literal stream + _SkipLiteral(stream, size); } } @@ -708,6 +712,24 @@ Response::Read(BDataIO& stream) } +void +Response::_SkipLiteral(BDataIO& stream, size_t size) +{ + char buffer[4096]; + size_t totalRead = 0; + while (totalRead < size) { + size_t toRead = std::min(sizeof(buffer), size - totalRead); + ssize_t bytesRead = stream.Read(buffer, toRead); + if (bytesRead == 0) + throw ParseException("Unexpected end of literal"); + if (bytesRead < 0) + throw StreamException(bytesRead); + + totalRead += bytesRead; + } +} + + // #pragma mark - diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h index 584ce6c64d..b37bc9826b 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h @@ -130,7 +130,7 @@ public: virtual bool HandleLiteral(Response& response, ArgumentList& arguments, BDataIO& stream, - size_t length) = 0; + size_t& length) = 0; }; @@ -168,6 +168,9 @@ protected: char Peek(BDataIO& stream); char Read(BDataIO& stream); +private: + void _SkipLiteral(BDataIO& stream, size_t size); + protected: LiteralHandler* fLiteralHandler; uint32 fTag;