imap: Fetching headers is now working.

* No state is stored yet, though, so that all messages will be downloaded
  over and over again.
This commit is contained in:
Axel Dörfler
2015-01-06 15:25:47 +01:00
parent c500331f66
commit eba458b940
9 changed files with 183 additions and 44 deletions
@@ -135,7 +135,8 @@ public:
// TODO: trigger download of mails for all messages below the // TODO: trigger download of mails for all messages below the
// body fetch limit // body fetch limit
IMAP::FetchCommand fetch(fFrom, fTo, IMAP::kFetchHeader); IMAP::FetchCommand fetch(fFrom, fTo,
IMAP::kFetchHeader | IMAP::kFetchFlags);
fetch.SetListener(this); fetch.SetListener(this);
status = protocol.ProcessCommand(fetch); status = protocol.ProcessCommand(fetch);
@@ -145,9 +146,9 @@ public:
return B_OK; 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); length, fRef);
return true; return true;
} }
@@ -155,7 +156,7 @@ public:
virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags)
{ {
if (fFetchStatus == B_OK) if (fFetchStatus == B_OK)
fFolder.StoreMessage(fetchFlags, uid, flags, fRef); fFolder.MessageStored(fRef, fFile, fetchFlags, uid, flags);
} }
private: private:
@@ -164,6 +165,7 @@ private:
uint32 fFrom; uint32 fFrom;
uint32 fTo; uint32 fTo;
entry_ref fRef; entry_ref fRef;
BFile fFile;
status_t fFetchStatus; status_t fFetchStatus;
}; };
@@ -16,15 +16,76 @@
#include <Node.h> #include <Node.h>
#include <Path.h> #include <Path.h>
#include "Commands.h"
#include "IMAPProtocol.h"
static const char* kMailboxNameAttribute = "IMAP:mailbox"; static const char* kMailboxNameAttribute = "IMAP:mailbox";
static const char* kUIDValidityAttribute = "IMAP:uidvalidity"; static const char* kUIDValidityAttribute = "IMAP:uidvalidity";
static const char* kStateAttribute = "IMAP:state"; static const char* kStateAttribute = "IMAP:state";
static const char* kFlagsAttribute = "IMAP:flags";
static const char* kUIDAttribute = "MAIL:unique_id"; 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), fRef(ref),
fMailboxName(mailboxName), fMailboxName(mailboxName),
fUIDValidity(UINT32_MAX) 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 status_t
IMAPFolder::StoreTemporaryMessage(uint32 fetchFlags, BDataIO& stream, IMAPFolder::StoreMessage(BFile& file, uint32 fetchFlags, BDataIO& stream,
size_t length, entry_ref& ref) size_t& length, entry_ref& ref)
{ {
BPath path; BPath path;
status_t status = path.SetTo(&fRef); status_t status = path.SetTo(&fRef);
if (status != B_OK) if (status != B_OK)
return status; return status;
path.Append("tmp1234"); TemporaryFile temporaryFile(file);
status = temporaryFile.Init(path, ref);
BFile file;
status = (path.Path(), B_CREATE_FILE);
if (status != B_OK) if (status != B_OK)
return status; return status;
get_ref_for_path(path.Path(), &ref);
char buffer[65535]; char buffer[65535];
while (length > 0) { while (length > 0) {
ssize_t bytesRead = stream.Read(buffer, ssize_t bytesRead = stream.Read(buffer,
min_c(sizeof(buffer), length)); std::min(sizeof(buffer), length));
if (bytesRead < 0)
return bytesRead;
if (bytesRead <= 0) if (bytesRead <= 0)
break; break;
file.Write(buffer, bytesRead);
length -= 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; 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 void
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, uint32 flags, IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
entry_ref& ref) uint32 uid, uint32 flags)
{ {
BString name = "mail-"; _WriteUniqueID(file, uid);
name << uid; if ((fetchFlags & IMAP::kFetchFlags) != 0)
_WriteFlags(file, flags);
// TODO: remove renaming as its superfluous here // TODO: the call below may move/rename the file
BEntry entry(&ref); fProtocol.MessageStored(ref, file, fetchFlags);
entry.Rename(name.String());
}
file.Unset();
status_t
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, BDataIO& stream,
size_t length)
{
return B_ERROR;
} }
@@ -207,8 +277,8 @@ IMAPFolder::_InitializeFolderState()
} else { } else {
// This is a new message // This is a new message
// TODO: the token must be the originating token! // TODO: the token must be the originating token!
// TODO: uid might be udpated from the call
uid = fListener->MessageAdded(_Token(uid), ref); uid = fListener->MessageAdded(_Token(uid), ref);
_WriteUniqueID(node, uid);
} }
fRefMap.insert(std::make_pair(uid, ref)); 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 uint32
IMAPFolder::_ReadFlags(BNode& node) IMAPFolder::_ReadFlags(BNode& node)
{ {
// TODO! // TODO!
return 0; 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;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012, Axel Dörfler, [email protected]. * Copyright 2012-2013, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef IMAP_FOLDER_H #ifndef IMAP_FOLDER_H
@@ -15,6 +15,9 @@
#include <String.h> #include <String.h>
class IMAPProtocol;
struct MessageToken { struct MessageToken {
BString mailboxName; BString mailboxName;
uint32 uidValidity; uint32 uidValidity;
@@ -36,7 +39,8 @@ public:
class IMAPFolder : public BHandler { class IMAPFolder : public BHandler {
public: public:
IMAPFolder(const BString& mailboxName, IMAPFolder(IMAPProtocol& protocol,
const BString& mailboxName,
const entry_ref& ref); const entry_ref& ref);
virtual ~IMAPFolder(); virtual ~IMAPFolder();
@@ -45,13 +49,13 @@ public:
void SetListener(FolderListener* listener); void SetListener(FolderListener* listener);
void SetUIDValidity(uint32 uidValidity); void SetUIDValidity(uint32 uidValidity);
status_t StoreTemporaryMessage(uint32 fetchFlags, status_t StoreMessage(BFile& file, uint32 fetchFlags,
BDataIO& stream, size_t length, BDataIO& stream, size_t& length,
entry_ref& ref); entry_ref& ref);
void StoreMessage(uint32 fetchFlags, uint32 uid, void MessageStored(entry_ref& ref, BFile& file,
uint32 flags, entry_ref& ref); uint32 fetchFlags, uint32 uid,
status_t StoreMessage(uint32 fetchFlags, uint32 uid, uint32 flags);
BDataIO& stream, size_t length);
void DeleteMessage(uint32 uid); void DeleteMessage(uint32 uid);
void SetMessageFlags(uint32 uid, uint32 flags); void SetMessageFlags(uint32 uid, uint32 flags);
@@ -61,12 +65,15 @@ private:
void _InitializeFolderState(); void _InitializeFolderState();
const MessageToken _Token(uint32 uid) const; const MessageToken _Token(uint32 uid) const;
uint32 _ReadUniqueID(BNode& node); uint32 _ReadUniqueID(BNode& node);
status_t _WriteUniqueID(BNode& node, uint32 uid);
uint32 _ReadFlags(BNode& node); uint32 _ReadFlags(BNode& node);
status_t _WriteFlags(BNode& node, uint32 flags);
private: private:
typedef std::hash_map<uint32, uint32> UIDToFlagsMap; typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
typedef std::hash_map<uint32, entry_ref> UIDToRefMap; typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
IMAPProtocol& fProtocol;
const entry_ref fRef; const entry_ref fRef;
BString fMailboxName; BString fMailboxName;
uint32 fUIDValidity; uint32 fUIDValidity;
@@ -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 status_t
IMAPProtocol::SyncMessages() IMAPProtocol::SyncMessages()
{ {
@@ -230,7 +240,7 @@ IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
return NULL; return NULL;
} }
return new IMAPFolder(mailbox, ref); return new IMAPFolder(*this, mailbox, ref);
} }
@@ -34,6 +34,9 @@ public:
IMAP::Protocol& protocol, bool idle); IMAP::Protocol& protocol, bool idle);
void WorkerQuit(IMAPConnectionWorker* worker); void WorkerQuit(IMAPConnectionWorker* worker);
void MessageStored(entry_ref& ref, BFile& stream,
uint32 fetchFlags);
virtual status_t SyncMessages(); virtual status_t SyncMessages();
virtual status_t FetchBody(const entry_ref& ref); virtual status_t FetchBody(const entry_ref& ref);
virtual status_t MarkMessageAsRead(const entry_ref& ref, virtual status_t MarkMessageAsRead(const entry_ref& ref,
@@ -402,7 +402,7 @@ FetchCommand::HandleUntagged(Response& response)
bool bool
FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments, FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments,
BDataIO& stream, size_t length) BDataIO& stream, size_t& length)
{ {
if (fListener == NULL || !response.EqualsAt(1, "FETCH") if (fListener == NULL || !response.EqualsAt(1, "FETCH")
|| !response.IsListAt(2)) || !response.IsListAt(2))
@@ -147,7 +147,7 @@ enum FetchFlags {
class FetchListener { class FetchListener {
public: public:
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, virtual bool FetchData(uint32 fetchFlags, BDataIO& stream,
size_t length) = 0; size_t& length) = 0;
virtual void FetchedData(uint32 fetchFlags, uint32 uid, virtual void FetchedData(uint32 fetchFlags, uint32 uid,
uint32 flags) = 0; uint32 flags) = 0;
}; };
@@ -166,7 +166,7 @@ public:
virtual bool HandleUntagged(Response& response); virtual bool HandleUntagged(Response& response);
virtual bool HandleLiteral(Response& response, virtual bool HandleLiteral(Response& response,
ArgumentList& arguments, BDataIO& stream, ArgumentList& arguments, BDataIO& stream,
size_t length); size_t& length);
private: private:
uint32 fFrom; uint32 fFrom;
@@ -6,6 +6,7 @@
#include "Response.h" #include "Response.h"
#include <algorithm>
#include <stdlib.h> #include <stdlib.h>
#include <UnicodeChar.h> #include <UnicodeChar.h>
@@ -588,7 +589,7 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
size); size);
} }
if (!handled) { if (!handled && size <= 65536) {
// The default implementation just adds the data as a string // The default implementation just adds the data as a string
TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size); TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size);
BString string; BString string;
@@ -612,6 +613,9 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
string.UnlockBuffer(size); string.UnlockBuffer(size);
arguments.AddItem(new StringArgument(string)); 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 - // #pragma mark -
@@ -130,7 +130,7 @@ public:
virtual bool HandleLiteral(Response& response, virtual bool HandleLiteral(Response& response,
ArgumentList& arguments, BDataIO& stream, ArgumentList& arguments, BDataIO& stream,
size_t length) = 0; size_t& length) = 0;
}; };
@@ -168,6 +168,9 @@ protected:
char Peek(BDataIO& stream); char Peek(BDataIO& stream);
char Read(BDataIO& stream); char Read(BDataIO& stream);
private:
void _SkipLiteral(BDataIO& stream, size_t size);
protected: protected:
LiteralHandler* fLiteralHandler; LiteralHandler* fLiteralHandler;
uint32 fTag; uint32 fTag;