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
2015-01-06 15:26:47 +01:00
parent cf14e7f21f
commit d33e4744df
16 changed files with 178 additions and 116 deletions
+4 -2
View File
@@ -21,8 +21,10 @@ public:
virtual ~BMailFilter(); virtual ~BMailFilter();
// Message hooks if filter is installed to an inbound protocol // Message hooks if filter is installed to an inbound protocol
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file); virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
virtual void BodyFetched(const entry_ref& ref, BFile& file); BMessage& attributes);
virtual void BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes);
virtual void MailboxSynchronized(status_t status); virtual void MailboxSynchronized(status_t status);
// Message hooks if filter is installed to an outbound protocol // Message hooks if filter is installed to an outbound protocol
@@ -95,9 +95,11 @@ protected:
// Filter notifications // Filter notifications
BMailFilterAction ProcessHeaderFetched(entry_ref& ref, BMailFilterAction ProcessHeaderFetched(entry_ref& ref,
BFile& mail); BFile& mail, BMessage& attributes);
void NotifyBodyFetched(const entry_ref& ref, 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, void NotifyMessageReadyToSend(const entry_ref& ref,
BFile& mail); BFile& mail);
void NotifyMessageSent(const entry_ref& ref, void NotifyMessageSent(const entry_ref& ref,
@@ -108,6 +110,10 @@ protected:
private: private:
BMailFilter* _LoadFilter(const BMailAddOnSettings& settings); 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: protected:
const BMailAccountSettings fAccountSettings; const BMailAccountSettings fAccountSettings;
+2 -1
View File
@@ -489,7 +489,8 @@ public:
status_t SetRect(const char* name, const BRect& value); status_t SetRect(const char* name, const BRect& value);
status_t SetSize(const char* name, const BSize& value); status_t SetSize(const char* name, const BSize& value);
status_t SetData(const char* name, type_code type, 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; class Private;
struct message_header; struct message_header;
@@ -57,7 +57,7 @@ RuleFilter::RuleFilter(BMailProtocol& protocol,
BMailFilterAction BMailFilterAction
RuleFilter::HeaderFetched(entry_ref& ref, BFile& file) RuleFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{ {
// That field doesn't exist? NO match // That field doesn't exist? NO match
if (fAttribute == "") if (fAttribute == "")
@@ -68,16 +68,8 @@ RuleFilter::HeaderFetched(entry_ref& ref, BFile& file)
|| info.type != B_STRING_TYPE) || info.type != B_STRING_TYPE)
return B_NO_MAIL_ACTION; return B_NO_MAIL_ACTION;
char* buffer = new char[info.size]; BString data = attributes.GetString(fAttribute.String(), NULL);
if (file.ReadAttr(fAttribute, B_STRING_TYPE, 0, buffer, info.size) < 0) { if (data.IsEmpty() || !fMatcher.Match(data)) {
delete[] buffer;
return B_NO_MAIL_ACTION;
}
BString data = buffer;
delete[] buffer;
if (!fMatcher.Match(data)) {
// We're not supposed to do anything // We're not supposed to do anything
return B_NO_MAIL_ACTION; return B_NO_MAIL_ACTION;
} }
@@ -21,7 +21,8 @@ public:
RuleFilter(BMailProtocol& protocol, RuleFilter(BMailProtocol& protocol,
const BMailAddOnSettings& settings); const BMailAddOnSettings& settings);
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file); virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes);
private: private:
BString fAttribute; BString fAttribute;
@@ -67,7 +67,7 @@ SpamFilter::~SpamFilter()
BMailFilterAction BMailFilterAction
SpamFilter::HeaderFetched(entry_ref& ref, BFile& file) SpamFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{ {
_CheckForSpam(file); _CheckForSpam(file);
return B_NO_MAIL_ACTION; return B_NO_MAIL_ACTION;
@@ -75,7 +75,7 @@ SpamFilter::HeaderFetched(entry_ref& ref, BFile& file)
void void
SpamFilter::BodyFetched(const entry_ref& ref, BFile& file) SpamFilter::BodyFetched(const entry_ref& ref, BFile& file, BMessage& attributes)
{ {
if (fHeaderOnly) if (fHeaderOnly)
return; 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 // 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. // don't know how big it was, so instead just ignore the message.
attr_info attributeInfo; attr_info attributeInfo;
if (file.GetAttrInfo ("MAIL:classification", &attributeInfo) == B_OK) if (file.GetAttrInfo("MAIL:classification", &attributeInfo) == B_OK)
return; return;
_CheckForSpam(file); _CheckForSpam(file);
@@ -18,8 +18,10 @@ public:
const BMailAddOnSettings& settings); const BMailAddOnSettings& settings);
virtual ~SpamFilter(); virtual ~SpamFilter();
virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file); virtual BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
virtual void BodyFetched(const entry_ref& ref, BFile& file); BMessage& attributes);
virtual void BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes);
private: private:
status_t _CheckForSpam(BFile& file); status_t _CheckForSpam(BFile& file);
@@ -231,7 +231,14 @@ IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
if ((fetchFlags & IMAP::kFetchFlags) != 0) if ((fetchFlags & IMAP::kFetchFlags) != 0)
_WriteFlags(file, flags); _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(); file.Unset();
fRefMap.insert(std::make_pair(uid, ref)); fRefMap.insert(std::make_pair(uid, ref));
@@ -282,7 +289,8 @@ IMAPFolder::StoreBody(uint32 uid, BDataIO& stream, size_t& length,
void void
IMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid) 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(); file.Unset();
} }
@@ -124,15 +124,16 @@ IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
void void
IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream, IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
uint32 fetchFlags) uint32 fetchFlags, BMessage& attributes)
{ {
if ((fetchFlags & IMAP::kFetchHeader) != 0) { if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
BMailFilterAction action = ProcessHeaderFetched(ref, stream); == IMAP::kFetchHeader | IMAP::kFetchBody) {
if (action < B_OK || action == B_DELETE_MAIL_ACTION) ProcessMessageFetched(ref, stream, attributes);
return; } 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);
} }
@@ -36,7 +36,7 @@ public:
void MessageStored(IMAPFolder& folder, void MessageStored(IMAPFolder& folder,
entry_ref& ref, BFile& stream, entry_ref& ref, BFile& stream,
uint32 fetchFlags); uint32 fetchFlags, BMessage& attributes);
virtual status_t SyncMessages(); virtual status_t SyncMessages();
virtual status_t FetchBody(const entry_ref& ref); virtual status_t FetchBody(const entry_ref& ref);
@@ -203,14 +203,11 @@ POP3Protocol::SyncMessages()
break; break;
} }
BMailMessageIO mailIO(this, &file, toRetrieve); BMailMessageIO mailIO(this, &file, toRetrieve);
BMessage attributes;
entry_ref ref; entry_ref ref;
entry.GetRef(&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); int32 size = MessageSize(toRetrieve);
if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) { if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) {
error = mailIO.Seek(0, SEEK_END); error = mailIO.Seek(0, SEEK_END);
@@ -218,8 +215,7 @@ POP3Protocol::SyncMessages()
printf("POP3: Failed to download body %s\n ", uid); printf("POP3: Failed to download body %s\n ", uid);
break; break;
} }
ProcessHeaderFetched(ref, file); ProcessMessageFetched(ref, file, attributes);
NotifyBodyFetched(ref, file);
if (!leaveOnServer) if (!leaveOnServer)
Delete(toRetrieve); Delete(toRetrieve);
@@ -230,7 +226,7 @@ POP3Protocol::SyncMessages()
printf("POP3: Failed to download header %s\n ", uid); printf("POP3: Failed to download header %s\n ", uid);
break; break;
} }
ProcessHeaderFetched(ref, file); ProcessHeaderFetched(ref, file, attributes);
} }
ReportProgress(1, 0); ReportProgress(1, 0);
@@ -302,7 +298,8 @@ POP3Protocol::FetchBody(const entry_ref& ref)
return status; return status;
} }
NotifyBodyFetched(ref, file); BMessage attributes;
NotifyBodyFetched(ref, file, attributes);
if (!leaveOnServer) if (!leaveOnServer)
Delete(toRetrieve); Delete(toRetrieve);
+5 -4
View File
@@ -3194,20 +3194,21 @@ BMessage::GetString(const char* name, int32 index,
status_t status_t
BMessage::SetString(const char* name, const BString& value) 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 status_t
BMessage::SetString(const char* name, const char* value) 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 status_t
BMessage::SetData(const char* name, type_code type, const void* data, 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) if (numBytes <= 0 || data == NULL)
return B_BAD_VALUE; 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) if (ReplaceData(name, type, data, numBytes) == B_OK)
return B_OK; return B_OK;
return AddData(name, type, data, numBytes); return AddData(name, type, data, numBytes, fixedSize, count);
} }
+16 -10
View File
@@ -16,7 +16,6 @@
#include <NodeInfo.h> #include <NodeInfo.h>
#include <mail_util.h> #include <mail_util.h>
#include <NodeMessage.h>
struct mail_header_field { struct mail_header_field {
@@ -98,11 +97,11 @@ HaikuMailFormatFilter::DescriptiveName() const
BMailFilterAction BMailFilterAction
HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file) HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file,
BMessage& attributes)
{ {
file.Seek(0, SEEK_SET); file.Seek(0, SEEK_SET);
BMessage attributes;
// TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length); // TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length);
attributes.AddInt32(B_MAIL_ATTR_ACCOUNT_ID, fAccountID); attributes.AddInt32(B_MAIL_ATTR_ACCOUNT_ID, fAccountID);
attributes.AddString(B_MAIL_ATTR_ACCOUNT, fAccountName); attributes.AddString(B_MAIL_ATTR_ACCOUNT, fAccountName);
@@ -196,11 +195,10 @@ HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file)
_RemoveLeadingDots(name); _RemoveLeadingDots(name);
// Avoid files starting with a dot. // 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 _SetType(attributes, B_PARTIAL_MAIL_TYPE);
BNodeInfo info(&file);
info.SetType(B_PARTIAL_MAIL_TYPE);
ref.set_name(name.String()); ref.set_name(name.String());
@@ -209,10 +207,10 @@ HaikuMailFormatFilter::HeaderFetched(entry_ref& ref, BFile& file)
void void
HaikuMailFormatFilter::BodyFetched(const entry_ref& ref, BFile& file) HaikuMailFormatFilter::BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{ {
BNodeInfo info(&file); _SetType(attributes, B_MAIL_TYPE);
info.SetType(B_MAIL_TYPE);
} }
@@ -307,3 +305,11 @@ HaikuMailFormatFilter::_ExtractName(const BString& from)
name.Trim(); name.Trim();
return name; 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);
}
+6 -2
View File
@@ -18,8 +18,10 @@ public:
virtual BString DescriptiveName() const; virtual BString DescriptiveName() const;
BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file); BMailFilterAction HeaderFetched(entry_ref& ref, BFile& file,
void BodyFetched(const 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); void MessageSent(const entry_ref& ref, BFile& file);
@@ -27,6 +29,8 @@ private:
void _RemoveExtraWhitespace(BString& name); void _RemoveExtraWhitespace(BString& name);
void _RemoveLeadingDots(BString& name); void _RemoveLeadingDots(BString& name);
BString _ExtractName(const BString& from); BString _ExtractName(const BString& from);
status_t _SetType(BMessage& attributes,
const char* mimeType);
private: private:
int32 fAccountID; int32 fAccountID;
+3 -2
View File
@@ -22,14 +22,15 @@ BMailFilter::~BMailFilter()
BMailFilterAction BMailFilterAction
BMailFilter::HeaderFetched(entry_ref& ref, BFile& file) BMailFilter::HeaderFetched(entry_ref& ref, BFile& file, BMessage& attributes)
{ {
return B_NO_MAIL_ACTION; return B_NO_MAIL_ACTION;
} }
void void
BMailFilter::BodyFetched(const entry_ref& ref, BFile& file) BMailFilter::BodyFetched(const entry_ref& ref, BFile& file,
BMessage& attributes)
{ {
} }
+98 -58
View File
@@ -32,6 +32,7 @@
#include <mail_util.h> #include <mail_util.h>
#include <MailPrivate.h> #include <MailPrivate.h>
#include <NodeMessage.h>
#include "HaikuMailFormatFilter.h" #include "HaikuMailFormatFilter.h"
@@ -210,83 +211,53 @@ BMailProtocol::NotifyNewMessagesToFetch(int32 count)
BMailFilterAction 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++) { return action;
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;
} }
void 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++) _NotifyBodyFetched(ref, file, attributes);
fFilterList.ItemAt(i)->BodyFetched(ref, data); 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 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++) for (int i = 0; i < fFilterList.CountItems(); i++)
fFilterList.ItemAt(i)->MessageReadyToSend(ref, data); fFilterList.ItemAt(i)->MessageReadyToSend(ref, file);
} }
void 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++) 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 - // #pragma mark -