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 2013-04-25 01:35:38 +02:00
parent c500331f66
commit eba458b940
9 changed files with 183 additions and 44 deletions

View File

@ -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;
};

View File

@ -16,15 +16,76 @@
#include <Node.h>
#include <Path.h>
#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;
}

View File

@ -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 <String.h>
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<uint32, uint32> UIDToFlagsMap;
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
IMAPProtocol& fProtocol;
const entry_ref fRef;
BString fMailboxName;
uint32 fUIDValidity;

View File

@ -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);
}

View File

@ -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,

View File

@ -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))

View File

@ -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;

View File

@ -6,6 +6,7 @@
#include "Response.h"
#include <algorithm>
#include <stdlib.h>
#include <UnicodeChar.h>
@ -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 -

View File

@ -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;