imap: WIP of getting fetching headers to work.
* Changed the FetchListener mechanism quite a bit. * Doesn't seem to work correctly yet, although most pieces are in place now.
This commit is contained in:
@@ -112,7 +112,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class FetchHeadersCommand : public WorkerCommand {
|
||||
class FetchHeadersCommand : public WorkerCommand, public IMAP::FetchListener {
|
||||
public:
|
||||
FetchHeadersCommand(IMAPFolder& folder, IMAPMailbox& mailbox,
|
||||
uint32 from, uint32 to)
|
||||
@@ -133,11 +133,11 @@ public:
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// TODO: create messages!
|
||||
// TODO: trigger download of mails for all messages below the
|
||||
// body fetch limit
|
||||
// TODO: we would really like to have the message flags at this point
|
||||
IMAP::FetchCommand fetch(fFrom, fTo, IMAP::kFetchHeader);
|
||||
fetch.SetListener(this);
|
||||
|
||||
status = protocol.ProcessCommand(fetch);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -145,11 +145,26 @@ public:
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t length)
|
||||
{
|
||||
fFetchStatus = fFolder.StoreTemporaryMessage(fetchFlags, stream,
|
||||
length, fRef);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags)
|
||||
{
|
||||
if (fFetchStatus == B_OK)
|
||||
fFolder.StoreMessage(fetchFlags, uid, flags, fRef);
|
||||
}
|
||||
|
||||
private:
|
||||
IMAPFolder& fFolder;
|
||||
IMAPMailbox& fMailbox;
|
||||
uint32 fFrom;
|
||||
uint32 fTo;
|
||||
entry_ref fRef;
|
||||
status_t fFetchStatus;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <ByteOrder.h>
|
||||
#include <Debug.h>
|
||||
#include <Directory.h>
|
||||
#include <File.h>
|
||||
#include <fs_attr.h>
|
||||
#include <Node.h>
|
||||
#include <Path.h>
|
||||
|
||||
|
||||
static const char* kMailboxNameAttribute = "IMAP:mailbox";
|
||||
@@ -95,9 +97,57 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IMAPFolder::StoreMessage(uint32 uid, ...)
|
||||
status_t
|
||||
IMAPFolder::StoreTemporaryMessage(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);
|
||||
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));
|
||||
if (bytesRead <= 0)
|
||||
break;
|
||||
|
||||
file.Write(buffer, bytesRead);
|
||||
length -= bytesRead;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, uint32 flags,
|
||||
entry_ref& ref)
|
||||
{
|
||||
BString name = "mail-";
|
||||
name << uid;
|
||||
|
||||
// TODO: remove renaming as its superfluous here
|
||||
BEntry entry(&ref);
|
||||
entry.Rename(name.String());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, BDataIO& stream,
|
||||
size_t length)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,13 @@ public:
|
||||
void SetListener(FolderListener* listener);
|
||||
void SetUIDValidity(uint32 uidValidity);
|
||||
|
||||
void StoreMessage(uint32 uid, ...);
|
||||
status_t StoreTemporaryMessage(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 DeleteMessage(uint32 uid);
|
||||
void SetMessageFlags(uint32 uid, uint32 flags);
|
||||
|
||||
|
||||
@@ -330,11 +330,11 @@ FetchMessageEntriesCommand::HandleUntagged(Response& response)
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
FetchCommand::FetchCommand(uint32 from, uint32 to, FetchMode mode)
|
||||
FetchCommand::FetchCommand(uint32 from, uint32 to, uint32 flags)
|
||||
:
|
||||
fFrom(from),
|
||||
fTo(to),
|
||||
fMode(mode)
|
||||
fFlags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -355,7 +355,9 @@ FetchCommand::CommandString()
|
||||
command << ":" << fTo;
|
||||
|
||||
command += " (UID ";
|
||||
switch (fMode) {
|
||||
if ((fFlags & kFetchFlags) != 0)
|
||||
command += "FLAGS ";
|
||||
switch (fFlags & kFetchAll) {
|
||||
case kFetchHeader:
|
||||
command += "RFC822.HEADER";
|
||||
break;
|
||||
@@ -378,35 +380,35 @@ FetchCommand::HandleUntagged(Response& response)
|
||||
if (!response.EqualsAt(1, "FETCH") || !response.IsListAt(2))
|
||||
return false;
|
||||
|
||||
// We don't need to parse anything here - all the data is processed via
|
||||
// HandleLiteral().
|
||||
ArgumentList& list = response.ListAt(2);
|
||||
uint32 uid = 0;
|
||||
uint32 flags = 0;
|
||||
|
||||
for (int32 i = 0; i < list.CountItems(); i += 2) {
|
||||
if (list.EqualsAt(i, "UID") && list.IsNumberAt(i + 1))
|
||||
uid = list.NumberAt(i + 1);
|
||||
else if (list.EqualsAt(i, "FLAGS") && list.IsListAt(i + 1)) {
|
||||
// Parse flags
|
||||
ArgumentList& flagList = list.ListAt(i + 1);
|
||||
flags = ParseFlags(flagList);
|
||||
}
|
||||
}
|
||||
|
||||
if (fListener != NULL)
|
||||
fListener->FetchedData(fFlags, uid, flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
FetchCommand::HandleLiteral(Response& response, BDataIO& stream,
|
||||
size_t length)
|
||||
FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments,
|
||||
BDataIO& stream, size_t length)
|
||||
{
|
||||
if (fListener == NULL || !response.EqualsAt(1, "FETCH")
|
||||
|| !response.IsListAt(2))
|
||||
return false;
|
||||
|
||||
uint32 uid = 0;
|
||||
|
||||
// Get current UID
|
||||
ArgumentList& list = response.ListAt(2);
|
||||
for (int32 i = 0; i < list.CountItems(); i += 2) {
|
||||
if (list.EqualsAt(i, "UID") && list.IsNumberAt(i + 1)) {
|
||||
uid = list.NumberAt(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid == 0)
|
||||
return false;
|
||||
|
||||
return fListener->FetchData(response, uid, fMode, stream, length);
|
||||
return fListener->FetchData(fFlags, stream, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -136,18 +136,20 @@ private:
|
||||
};
|
||||
|
||||
|
||||
enum FetchMode {
|
||||
kFetchHeader,
|
||||
kFetchBody,
|
||||
kFetchAll
|
||||
enum FetchFlags {
|
||||
kFetchHeader = 0x01,
|
||||
kFetchBody = 0x02,
|
||||
kFetchAll = kFetchHeader | kFetchBody,
|
||||
kFetchFlags = 0x04,
|
||||
};
|
||||
|
||||
|
||||
class FetchListener {
|
||||
public:
|
||||
virtual bool FetchData(Response& reponse, uint32 uid,
|
||||
FetchMode mode, BDataIO& stream,
|
||||
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream,
|
||||
size_t length) = 0;
|
||||
virtual void FetchedData(uint32 fetchFlags, uint32 uid,
|
||||
uint32 flags) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,7 +157,7 @@ class FetchCommand : public Command, public Handler,
|
||||
public LiteralHandler {
|
||||
public:
|
||||
FetchCommand(uint32 from, uint32 to,
|
||||
FetchMode mode);
|
||||
uint32 fetchFlags);
|
||||
|
||||
void SetListener(FetchListener* listener);
|
||||
FetchListener* Listener() const { return fListener; }
|
||||
@@ -163,12 +165,13 @@ public:
|
||||
virtual BString CommandString();
|
||||
virtual bool HandleUntagged(Response& response);
|
||||
virtual bool HandleLiteral(Response& response,
|
||||
BDataIO& stream, size_t length);
|
||||
ArgumentList& arguments, BDataIO& stream,
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
uint32 fFrom;
|
||||
uint32 fTo;
|
||||
FetchMode fMode;
|
||||
uint32 fFlags;
|
||||
FetchListener* fListener;
|
||||
};
|
||||
|
||||
|
||||
@@ -85,6 +85,34 @@ Protocol::Connect(const BNetworkAddress& address, const char* username,
|
||||
_ParseCapabilities(capabilityHandler.Capabilities());
|
||||
}
|
||||
|
||||
if (Capabilities().Contains("ID")) {
|
||||
// Get the server's ID into our log
|
||||
class IDCommand : public IMAP::Command, public IMAP::Handler {
|
||||
public:
|
||||
BString CommandString()
|
||||
{
|
||||
return "ID NIL";
|
||||
}
|
||||
|
||||
bool HandleUntagged(IMAP::Response& response)
|
||||
{
|
||||
if (response.IsCommand("ID") && response.IsListAt(1)) {
|
||||
puts("Server:");
|
||||
ArgumentList& list = response.ListAt(1);
|
||||
for (int32 i = 0; i < list.CountItems(); i += 2) {
|
||||
printf(" %s: %s\n",
|
||||
list.ItemAt(i)->ToString().String(),
|
||||
list.ItemAt(i + 1)->ToString().String());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
IDCommand idCommand;
|
||||
ProcessCommand(idCommand);
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -583,8 +583,10 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
|
||||
Consume(stream, '\n');
|
||||
|
||||
bool handled = false;
|
||||
if (fLiteralHandler != NULL)
|
||||
handled = fLiteralHandler->HandleLiteral(*this, stream, size);
|
||||
if (fLiteralHandler != NULL) {
|
||||
handled = fLiteralHandler->HandleLiteral(*this, arguments, stream,
|
||||
size);
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
// The default implementation just adds the data as a string
|
||||
|
||||
@@ -129,7 +129,8 @@ public:
|
||||
virtual ~LiteralHandler();
|
||||
|
||||
virtual bool HandleLiteral(Response& response,
|
||||
BDataIO& stream, size_t length) = 0;
|
||||
ArgumentList& arguments, BDataIO& stream,
|
||||
size_t length) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -87,17 +87,17 @@ do_fetch(int argc, char** argv)
|
||||
{
|
||||
uint32 from = 1;
|
||||
uint32 to;
|
||||
IMAP::FetchMode mode = IMAP::kFetchAll;
|
||||
uint32 flags = IMAP::kFetchAll;
|
||||
if (argc < 2) {
|
||||
printf("usage: %s [<from>] [<to>] [header|body]\n", argv[0]);
|
||||
return;
|
||||
}
|
||||
if (argc > 2) {
|
||||
if (!strcasecmp(argv[argc - 1], "header")) {
|
||||
mode = IMAP::kFetchHeader;
|
||||
flags = IMAP::kFetchHeader;
|
||||
argc--;
|
||||
} else if (!strcasecmp(argv[argc - 1], "body")) {
|
||||
mode = IMAP::kFetchBody;
|
||||
flags = IMAP::kFetchBody;
|
||||
argc--;
|
||||
}
|
||||
}
|
||||
@@ -107,15 +107,20 @@ do_fetch(int argc, char** argv)
|
||||
} else
|
||||
from = to = atoul(argv[1]);
|
||||
|
||||
IMAP::FetchCommand command(from, to, mode);
|
||||
IMAP::FetchCommand command(from, to, flags | IMAP::kFetchFlags);
|
||||
|
||||
// A fetch listener that dumps everything to stdout
|
||||
class Listener : public IMAP::FetchListener {
|
||||
public:
|
||||
virtual bool FetchData(IMAP::Response& reponse, uint32 uid,
|
||||
IMAP::FetchMode mode, BDataIO& stream, size_t length)
|
||||
virtual ~Listener()
|
||||
{
|
||||
BMallocIO copy;
|
||||
}
|
||||
|
||||
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream,
|
||||
size_t length)
|
||||
{
|
||||
fBuffer.SetSize(0);
|
||||
|
||||
char buffer[65535];
|
||||
while (length > 0) {
|
||||
ssize_t bytesRead = stream.Read(buffer,
|
||||
@@ -123,18 +128,26 @@ do_fetch(int argc, char** argv)
|
||||
if (bytesRead <= 0)
|
||||
break;
|
||||
|
||||
copy.Write(buffer, bytesRead);
|
||||
fBuffer.Write(buffer, bytesRead);
|
||||
length -= bytesRead;
|
||||
}
|
||||
|
||||
// Null terminate the buffer
|
||||
char null = '\0';
|
||||
copy.Write(&null, 1);
|
||||
fBuffer.Write(&null, 1);
|
||||
|
||||
printf("================= UID %ld =================\n", uid);
|
||||
puts((const char*)copy.Buffer());
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags)
|
||||
{
|
||||
printf("================= UID %ld, flags %lx =================\n",
|
||||
uid, flags);
|
||||
puts((const char*)fBuffer.Buffer());
|
||||
}
|
||||
|
||||
private:
|
||||
BMallocIO fBuffer;
|
||||
} listener;
|
||||
|
||||
command.SetListener(&listener);
|
||||
|
||||
Reference in New Issue
Block a user