IMAP: downloading mails is now working.

* Changed the way the attributes are written to make sure that everything
  that can be written once is in fact written just once.
* The rename code in BMailProtocol::_ProcessFetchedHeader() was broken,
  and caused the hang of the last commit.
This commit is contained in:
Axel Dörfler 2013-07-03 22:52:22 +02:00
parent cf14e7f21f
commit d33e4744df
16 changed files with 178 additions and 116 deletions

View File

@ -21,8 +21,10 @@ public:
virtual ~BMailFilter();
// Message hooks if filter is installed to an inbound protocol
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file);
virtual void BodyFetched(const entry_ref& ref, BFile& file);
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes);
virtual void BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes);
virtual void MailboxSynchronized(status_t status);
// Message hooks if filter is installed to an outbound protocol

View File

@ -95,9 +95,11 @@ protected:
// Filter notifications
BMailFilterAction ProcessHeaderFetched(entry_ref& ref,
BFile& mail);
BFile& mail, BMessage& attributes);
void NotifyBodyFetched(const entry_ref& ref,
BFile& mail);
BFile& mail, BMessage& attributes);
BMailFilterAction ProcessMessageFetched(entry_ref& ref,
BFile& mail, BMessage& attributes);
void NotifyMessageReadyToSend(const entry_ref& ref,
BFile& mail);
void NotifyMessageSent(const entry_ref& ref,
@ -108,6 +110,10 @@ protected:
private:
BMailFilter* _LoadFilter(const BMailAddOnSettings& settings);
BMailFilterAction _ProcessHeaderFetched(entry_ref& ref,
BFile& mail, BMessage& attributes);
void _NotifyBodyFetched(const entry_ref& ref,
BFile& mail, BMessage& attributes);
protected:
const BMailAccountSettings fAccountSettings;

View File

@ -489,7 +489,8 @@ public:
status_t SetRect(const char* name, const BRect& value);
status_t SetSize(const char* name, const BSize& value);
status_t SetData(const char* name, type_code type,
const void* data, ssize_t numBytes);
const void* data, ssize_t numBytes,
bool fixedSize = true, int count = 1);
class Private;
struct message_header;

View File

@ -57,7 +57,7 @@ RuleFilter::RuleFilter(BMailProtocol& protocol,
BMailFilterAction
RuleFilter::HeaderFetched(entry_ref& ref, BFile& file)
RuleFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{
// That field doesn't exist? NO match
if (fAttribute == "")
@ -68,16 +68,8 @@ RuleFilter::HeaderFetched(entry_ref& ref, BFile& file)
|| info.type != B_STRING_TYPE)
return B_NO_MAIL_ACTION;
char* buffer = new char[info.size];
if (file.ReadAttr(fAttribute, B_STRING_TYPE, 0, buffer, info.size) < 0) {
delete[] buffer;
return B_NO_MAIL_ACTION;
}
BString data = buffer;
delete[] buffer;
if (!fMatcher.Match(data)) {
BString data = attributes.GetString(fAttribute.String(), NULL);
if (data.IsEmpty() || !fMatcher.Match(data)) {
// We're not supposed to do anything
return B_NO_MAIL_ACTION;
}

View File

@ -21,7 +21,8 @@ public:
RuleFilter(BMailProtocol& protocol,
const BMailAddOnSettings& settings);
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file);
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes);
private:
BString fAttribute;

View File

@ -67,7 +67,7 @@ SpamFilter::~SpamFilter()
BMailFilterAction
SpamFilter::HeaderFetched(entry_ref& ref, BFile& file)
SpamFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{
_CheckForSpam(file);
return B_NO_MAIL_ACTION;
@ -75,7 +75,7 @@ SpamFilter::HeaderFetched(entry_ref& ref, BFile& file)
void
SpamFilter::BodyFetched(const entry_ref& ref, BFile& file)
SpamFilter::BodyFetched(const entry_ref& ref, BFile& file, BMessage& attributes)
{
if (fHeaderOnly)
return;
@ -85,7 +85,7 @@ SpamFilter::BodyFetched(const entry_ref& ref, BFile& file)
// untrain the partial part before training on the complete message, but we
// don't know how big it was, so instead just ignore the message.
attr_info attributeInfo;
if (file.GetAttrInfo ("MAIL:classification", &attributeInfo) == B_OK)
if (file.GetAttrInfo("MAIL:classification", &attributeInfo) == B_OK)
return;
_CheckForSpam(file);

View File

@ -18,8 +18,10 @@ public:
const BMailAddOnSettings& settings);
virtual ~SpamFilter();
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file);
virtual void BodyFetched(const entry_ref& ref, BFile& file);
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes);
virtual void BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes);
private:
status_t _CheckForSpam(BFile& file);

View File

@ -231,7 +231,14 @@ IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
if ((fetchFlags & IMAP::kFetchFlags) != 0)
_WriteFlags(file, flags);
fProtocol.MessageStored(*this, ref, file, fetchFlags);
// TODO: add some utility function for this in libmail.so
BMessage attributes;
if ((flags & IMAP::kAnswered) != 0)
attributes.AddString(B_MAIL_ATTR_STATUS, "Answered");
else if ((flags & IMAP::kSeen) != 0)
attributes.AddString(B_MAIL_ATTR_STATUS, "Read");
fProtocol.MessageStored(*this, ref, file, fetchFlags, attributes);
file.Unset();
fRefMap.insert(std::make_pair(uid, ref));
@ -282,7 +289,8 @@ IMAPFolder::StoreBody(uint32 uid, BDataIO& stream, size_t& length,
void
IMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid)
{
fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody);
BMessage attributes;
fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody, attributes);
file.Unset();
}

View File

@ -124,15 +124,16 @@ IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
void
IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
uint32 fetchFlags)
uint32 fetchFlags, BMessage& attributes)
{
if ((fetchFlags & IMAP::kFetchHeader) != 0) {
BMailFilterAction action = ProcessHeaderFetched(ref, stream);
if (action < B_OK || action == B_DELETE_MAIL_ACTION)
return;
if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
== IMAP::kFetchHeader | IMAP::kFetchBody) {
ProcessMessageFetched(ref, stream, attributes);
} else if ((fetchFlags & IMAP::kFetchHeader) != 0) {
ProcessHeaderFetched(ref, stream, attributes);
} else if ((fetchFlags & IMAP::kFetchBody) != 0) {
NotifyBodyFetched(ref, stream, attributes);
}
if ((fetchFlags & IMAP::kFetchBody) != 0)
NotifyBodyFetched(ref, stream);
}

View File

@ -36,7 +36,7 @@ public:
void MessageStored(IMAPFolder& folder,
entry_ref& ref, BFile& stream,
uint32 fetchFlags);
uint32 fetchFlags, BMessage& attributes);
virtual status_t SyncMessages();
virtual status_t FetchBody(const entry_ref& ref);

View File

@ -203,14 +203,11 @@ POP3Protocol::SyncMessages()
break;
}
BMailMessageIO mailIO(this, &file, toRetrieve);
BMessage attributes;
entry_ref ref;
entry.GetRef(&ref);
// the ref becomes invalid after renaming the file thus we already
// write the status here
MarkMessageAsRead(ref, B_UNREAD);
int32 size = MessageSize(toRetrieve);
if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) {
error = mailIO.Seek(0, SEEK_END);
@ -218,8 +215,7 @@ POP3Protocol::SyncMessages()
printf("POP3: Failed to download body %s\n ", uid);
break;
}
ProcessHeaderFetched(ref, file);
NotifyBodyFetched(ref, file);
ProcessMessageFetched(ref, file, attributes);
if (!leaveOnServer)
Delete(toRetrieve);
@ -230,7 +226,7 @@ POP3Protocol::SyncMessages()
printf("POP3: Failed to download header %s\n ", uid);
break;
}
ProcessHeaderFetched(ref, file);
ProcessHeaderFetched(ref, file, attributes);
}
ReportProgress(1, 0);
@ -302,7 +298,8 @@ POP3Protocol::FetchBody(const entry_ref& ref)
return status;
}
NotifyBodyFetched(ref, file);
BMessage attributes;
NotifyBodyFetched(ref, file, attributes);
if (!leaveOnServer)
Delete(toRetrieve);

View File

@ -3194,20 +3194,21 @@ BMessage::GetString(const char* name, int32 index,
status_t
BMessage::SetString(const char* name, const BString& value)
{
return SetData(name, B_STRING_TYPE, value.String(), value.Length() + 1);
return SetData(name, B_STRING_TYPE, value.String(), value.Length() + 1,
false);
}
status_t
BMessage::SetString(const char* name, const char* value)
{
return SetData(name, B_STRING_TYPE, value, strlen(value) + 1);
return SetData(name, B_STRING_TYPE, value, strlen(value) + 1, false);
}
status_t
BMessage::SetData(const char* name, type_code type, const void* data,
ssize_t numBytes)
ssize_t numBytes, bool fixedSize, int count)
{
if (numBytes <= 0 || data == NULL)
return B_BAD_VALUE;
@ -3215,5 +3216,5 @@ BMessage::SetData(const char* name, type_code type, const void* data,
if (ReplaceData(name, type, data, numBytes) == B_OK)
return B_OK;
return AddData(name, type, data, numBytes);
return AddData(name, type, data, numBytes, fixedSize, count);
}

View File

@ -16,7 +16,6 @@
#include <NodeInfo.h>
#include <mail_util.h>
#include <NodeMessage.h>
struct mail_header_field {
@ -98,11 +97,11 @@ HaikuMailFormatFilter::DescriptiveName() const
BMailFilterAction
HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file)
HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes)
{
file.Seek(0, SEEK_SET);
BMessage attributes;
// TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length);
attributes.AddInt32(B_MAIL_ATTR_ACCOUNT_ID, fAccountID);
attributes.AddString(B_MAIL_ATTR_ACCOUNT, fAccountName);
@ -196,11 +195,10 @@ HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file)
_RemoveLeadingDots(name);
// Avoid files starting with a dot.
file << attributes;
if (!attributes.HasString(B_MAIL_ATTR_STATUS))
attributes.AddString(B_MAIL_ATTR_STATUS, "New");
// TODO: find a way to not set that twice for each complete mail
BNodeInfo info(&file);
info.SetType(B_PARTIAL_MAIL_TYPE);
_SetType(attributes, B_PARTIAL_MAIL_TYPE);
ref.set_name(name.String());
@ -209,10 +207,10 @@ HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file)
void
HaikuMailFormatFilter::BodyFetched(const entry_ref& ref, BFile& file)
HaikuMailFormatFilter::BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{
BNodeInfo info(&file);
info.SetType(B_MAIL_TYPE);
_SetType(attributes, B_MAIL_TYPE);
}
@ -307,3 +305,11 @@ HaikuMailFormatFilter::_ExtractName(const BString& from)
name.Trim();
return name;
}
status_t
HaikuMailFormatFilter::_SetType(BMessage& attributes, const char* mimeType)
{
return attributes.SetData("BEOS:TYPE", B_MIME_STRING_TYPE, mimeType,
strlen(mimeType) + 1, false);
}

View File

@ -18,8 +18,10 @@ public:
virtual BString DescriptiveName() const;
BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file);
void BodyFetched(const entry_ref& ref, BFile& file);
BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes);
void BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes);
void MessageSent(const entry_ref& ref, BFile& file);
@ -27,6 +29,8 @@ private:
void _RemoveExtraWhitespace(BString& name);
void _RemoveLeadingDots(BString& name);
BString _ExtractName(const BString& from);
status_t _SetType(BMessage& attributes,
const char* mimeType);
private:
int32 fAccountID;

View File

@ -22,14 +22,15 @@ BMailFilter::~BMailFilter()
BMailFilterAction
BMailFilter::HeaderFetched(entry_ref& ref, BFile& file)
BMailFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{
return B_NO_MAIL_ACTION;
}
void
BMailFilter::BodyFetched(const entry_ref& ref, BFile& file)
BMailFilter::BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{
}

View File

@ -32,6 +32,7 @@
#include <mail_util.h>
#include <MailPrivate.h>
#include <NodeMessage.h>
#include "HaikuMailFormatFilter.h"
@ -210,83 +211,53 @@ BMailProtocol::NotifyNewMessagesToFetch(int32 count)
BMailFilterAction
BMailProtocol::ProcessHeaderFetched(entry_ref& ref, BFile& data)
BMailProtocol::ProcessHeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes)
{
entry_ref outRef = ref;
BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
if (action >= B_OK && action != B_DELETE_MAIL_ACTION)
file << attributes;
for (int i = 0; i < fFilterList.CountItems(); i++) {
BMailFilterAction action = fFilterList.ItemAt(i)->HeaderFetched(outRef,
data);
if (action == B_DELETE_MAIL_ACTION) {
// We have to delete the message
BEntry entry(&ref);
status_t status = entry.Remove();
if (status != B_OK) {
fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could "
"not delete mail: %s\n", strerror(status));
}
return B_DELETE_MAIL_ACTION;
}
}
if (ref == outRef)
return B_NO_MAIL_ACTION;
// We have to rename the file
node_ref newParentRef;
newParentRef.device = outRef.device;
newParentRef.node = outRef.directory;
BDirectory newParent(&newParentRef);
status_t status = newParent.InitCheck();
BString workerName;
if (status == B_OK) {
int32 uniqueNumber = 1;
do {
workerName = outRef.name;
if (uniqueNumber > 1)
workerName << "_" << ++uniqueNumber;
// TODO: support copying to another device!
BEntry entry(&ref);
status = entry.Rename(workerName);
} while (status == B_FILE_EXISTS);
}
if (status != B_OK) {
fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could not "
"rename mail (%s)! (should be: %s)\n", strerror(status),
workerName.String());
}
ref = outRef;
ref.set_name(workerName.String());
return B_MOVE_MAIL_ACTION;
return action;
}
void
BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& data)
BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{
for (int i = 0; i < fFilterList.CountItems(); i++)
fFilterList.ItemAt(i)->BodyFetched(ref, data);
_NotifyBodyFetched(ref, file, attributes);
file << attributes;
}
BMailFilterAction
BMailProtocol::ProcessMessageFetched(entry_ref& ref, BFile& file,
BMessage& attributes)
{
BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
if (action >= B_OK && action != B_DELETE_MAIL_ACTION) {
_NotifyBodyFetched(ref, file, attributes);
file << attributes;
}
return action;
}
void
BMailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile& data)
BMailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile& file)
{
for (int i = 0; i < fFilterList.CountItems(); i++)
fFilterList.ItemAt(i)->MessageReadyToSend(ref, data);
fFilterList.ItemAt(i)->MessageReadyToSend(ref, file);
}
void
BMailProtocol::NotifyMessageSent(const entry_ref& ref, BFile& data)
BMailProtocol::NotifyMessageSent(const entry_ref& ref, BFile& file)
{
for (int i = 0; i < fFilterList.CountItems(); i++)
fFilterList.ItemAt(i)->MessageSent(ref, data);
fFilterList.ItemAt(i)->MessageSent(ref, file);
}
@ -331,6 +302,75 @@ BMailProtocol::_LoadFilter(const BMailAddOnSettings& settings)
}
BMailFilterAction
BMailProtocol::_ProcessHeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes)
{
entry_ref outRef = ref;
for (int i = 0; i < fFilterList.CountItems(); i++) {
BMailFilterAction action = fFilterList.ItemAt(i)->HeaderFetched(outRef,
file, attributes);
if (action == B_DELETE_MAIL_ACTION) {
// We have to delete the message
BEntry entry(&ref);
status_t status = entry.Remove();
if (status != B_OK) {
fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could "
"not delete mail: %s\n", strerror(status));
}
return B_DELETE_MAIL_ACTION;
}
}
if (ref == outRef)
return B_NO_MAIL_ACTION;
// We have to rename the file
node_ref newParentRef;
newParentRef.device = outRef.device;
newParentRef.node = outRef.directory;
BDirectory newParent(&newParentRef);
status_t status = newParent.InitCheck();
BString workerName;
if (status == B_OK) {
int32 uniqueNumber = 1;
do {
workerName = outRef.name;
if (uniqueNumber > 1)
workerName << "_" << uniqueNumber;
// TODO: support copying to another device!
BEntry entry(&ref);
status = entry.Rename(workerName);
uniqueNumber++;
} while (status == B_FILE_EXISTS);
}
if (status != B_OK) {
fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could not "
"rename mail (%s)! (should be: %s)\n", strerror(status),
workerName.String());
}
ref = outRef;
ref.set_name(workerName.String());
return B_MOVE_MAIL_ACTION;
}
void
BMailProtocol::_NotifyBodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{
for (int i = 0; i < fFilterList.CountItems(); i++)
fFilterList.ItemAt(i)->BodyFetched(ref, file, attributes);
}
// #pragma mark -