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:
parent
c500331f66
commit
eba458b940
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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 -
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user