IMAP: setting flags, and body fetching works now.

* The direct methods in BMailProtocol now forward the request to the
  looper; it's no longer the mail_daemon's responsibility to know
  anything about that protocol.
* It's in desperate need of refactoring, but it doesn't hurt to add
  it to the repository as is.
This commit is contained in:
Axel Dörfler
2016-01-05 20:12:26 +01:00
parent 3592932936
commit 8180539313
15 changed files with 631 additions and 174 deletions
+12 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2004-2015, Haiku, Inc. All Rights Reserved.
* Copyright 2004-2016, Haiku, Inc. All Rights Reserved.
* Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2011 Clemens Zeidler. All rights reserved.
*
@@ -136,13 +136,22 @@ public:
virtual void MessageReceived(BMessage* message);
virtual status_t SyncMessages() = 0;
virtual status_t FetchBody(const entry_ref& ref) = 0;
virtual status_t FetchBody(const entry_ref& ref,
BMessenger* replyTo);
virtual status_t MarkMessageAsRead(const entry_ref& ref,
read_flags flags = B_READ);
virtual status_t DeleteMessage(const entry_ref& ref) = 0;
virtual status_t DeleteMessage(const entry_ref& ref);
virtual status_t AppendMessage(const entry_ref& ref);
static void ReplyBodyFetched(const BMessenger& replyTo,
const entry_ref& ref, status_t status);
protected:
virtual status_t HandleFetchBody(const entry_ref& ref,
const BMessenger& replyTo) = 0;
virtual status_t HandleDeleteMessage(const entry_ref& ref) = 0;
void NotiyMailboxSynchronized(status_t status);
};
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013, Axel Dörfler, [email protected].
* Copyright 2011-2016, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
@@ -7,6 +7,7 @@
#include "IMAPConnectionWorker.h"
#include <Autolock.h>
#include <Messenger.h>
#include <AutoDeleter.h>
@@ -141,12 +142,13 @@ public:
class FetchBodiesCommand : public SyncCommand, public IMAP::FetchListener {
public:
FetchBodiesCommand(IMAPFolder& folder, IMAPMailbox& mailbox,
std::vector<uint32>& entries)
MessageUIDList& entries, const BMessenger* replyTo = NULL)
:
fFolder(folder),
fMailbox(mailbox),
fEntries(entries)
{
folder.RegisterPendingBodies(entries, replyTo);
}
virtual status_t Process(IMAPConnectionWorker& worker)
@@ -160,18 +162,23 @@ public:
fEntries.erase(fEntries.begin());
status_t status = WorkerPrivate(worker).SelectMailbox(fFolder);
if (status == B_OK) {
printf("IMAP: fetch body for %" B_PRIu32 "\n", fUID);
// Since RFC3501 does not specify whether the FETCH response may
// alter the order of the message data items we request, we cannot
// request more than a single UID at a time, or else we may not be
// able to assign the data to the correct message beforehand.
IMAP::FetchCommand fetch(fUID, fUID, IMAP::kFetchBody);
fetch.SetListener(this);
status = protocol.ProcessCommand(fetch);
}
if (status == B_OK)
status = fFetchStatus;
if (status != B_OK)
return status;
fFolder.StoringBodyFailed(fRef, fUID, status);
printf("IMAP: fetch body for %" B_PRIu32 "\n", fUID);
// Since RFC3501 does not specify whether the FETCH response may
// alter the order of the message data items we request, we cannot
// request more than a single UID at a time, or else we may not be
// able to assign the data to the correct message beforehand.
IMAP::FetchCommand fetch(fUID, fUID, IMAP::kFetchBody);
fetch.SetListener(this);
return protocol.ProcessCommand(fetch);
return status;
}
virtual bool IsDone() const
@@ -194,7 +201,7 @@ public:
private:
IMAPFolder& fFolder;
IMAPMailbox& fMailbox;
std::vector<uint32> fEntries;
MessageUIDList fEntries;
uint32 fUID;
entry_ref fRef;
BFile fFile;
@@ -362,6 +369,9 @@ public:
if (entries[i].uid > fFolder->LastUID()) {
fTotalBytes += entries[i].size;
fUIDsToFetch.push_back(entries[i].uid);
} else {
fFolder->UpdateMessageFlags(entries[i].uid,
entries[i].flags);
}
}
@@ -413,6 +423,50 @@ private:
};
class UpdateFlagsCommand : public WorkerCommand {
public:
UpdateFlagsCommand(IMAPFolder& folder, IMAPMailbox& mailbox,
MessageUIDList& entries, uint32 flags)
:
fFolder(folder),
fMailbox(mailbox),
fEntries(entries),
fFlags(flags)
{
}
virtual status_t Process(IMAPConnectionWorker& worker)
{
if (fEntries.empty())
return B_OK;
fUID = *fEntries.begin();
fEntries.erase(fEntries.begin());
status_t status = WorkerPrivate(worker).SelectMailbox(fFolder);
if (status == B_OK) {
IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol();
IMAP::SetFlagsCommand set(fUID, fFlags);
status = protocol.ProcessCommand(set);
}
return status;
}
virtual bool IsDone() const
{
return fEntries.empty();
}
private:
IMAPFolder& fFolder;
IMAPMailbox& fMailbox;
MessageUIDList fEntries;
uint32 fUID;
uint32 fFlags;
};
struct CommandDelete
{
inline void operator()(WorkerCommand* command)
@@ -567,9 +621,34 @@ IMAPConnectionWorker::EnqueueCheckMailboxes()
status_t
IMAPConnectionWorker::EnqueueRetrieveMail(entry_ref& ref)
IMAPConnectionWorker::EnqueueFetchBody(IMAPFolder& folder, uint32 uid,
const BMessenger& replyTo)
{
return B_OK;
IMAPMailbox* mailbox = _MailboxFor(folder);
if (mailbox == NULL)
return B_ENTRY_NOT_FOUND;
std::vector<uint32> uids;
uids.push_back(uid);
return _EnqueueCommand(new FetchBodiesCommand(folder, *mailbox, uids,
&replyTo));
}
status_t
IMAPConnectionWorker::EnqueueUpdateFlags(IMAPFolder& folder, uint32 uid,
uint32 flags)
{
IMAPMailbox* mailbox = _MailboxFor(folder);
if (mailbox == NULL)
return B_ENTRY_NOT_FOUND;
std::vector<uint32> uids;
uids.push_back(uid);
return _EnqueueCommand(new UpdateFlagsCommand(folder, *mailbox, uids,
flags));
}
@@ -595,6 +674,8 @@ IMAPConnectionWorker::MessageExpungeReceived(uint32 index)
if (fSelectedBox == NULL)
return;
BLocker locker(this);
IMAPMailbox* mailbox = _MailboxFor(*fSelectedBox);
if (mailbox != NULL) {
mailbox->RemoveMessageEntry(index);
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2011-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_CONNECTION_WORKER_H
@@ -9,7 +9,6 @@
#include <Locker.h>
#include <String.h>
//#include "Commands.h"
#include "Protocol.h"
@@ -20,7 +19,6 @@ class Settings;
class WorkerCommand;
class WorkerPrivate;
typedef BObjectList<WorkerCommand> WorkerCommandList;
@@ -46,7 +44,10 @@ public:
status_t EnqueueCheckSubscribedFolders();
status_t EnqueueCheckMailboxes();
status_t EnqueueRetrieveMail(entry_ref& ref);
status_t EnqueueFetchBody(IMAPFolder& folder,
uint32 uid, const BMessenger& replyTo);
status_t EnqueueUpdateFlags(IMAPFolder& folder,
uint32 uid, uint32 flags);
// Handler listener
virtual void MessageExistsReceived(uint32 index);
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
@@ -13,10 +13,11 @@
#include <Directory.h>
#include <File.h>
#include <fs_attr.h>
#include <Messenger.h>
#include <Node.h>
#include <Path.h>
#include "Commands.h"
#include <NodeMessage.h>
#include "IMAPProtocol.h"
@@ -91,16 +92,37 @@ IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
fRef(ref),
fMailboxName(mailboxName),
fUIDValidity(UINT32_MAX),
fLastUID(0)
fLastUID(0),
fListener(NULL)
{
mutex_init(&fLock, "imap folder lock");
}
IMAPFolder::~IMAPFolder()
{
}
status_t
IMAPFolder::Init()
{
// Initialize from folder attributes
BNode node(&ref);
if (node.InitCheck() != B_OK)
return;
BNode node(&fRef);
status_t status = node.InitCheck();
if (status != B_OK)
return status;
node_ref nodeRef;
status = node.GetNodeRef(&nodeRef);
if (status != B_OK)
return status;
fNodeID = nodeRef.node;
BString originalMailboxName;
if (node.ReadAttrString(kMailboxNameAttribute, &originalMailboxName) == B_OK
&& originalMailboxName != mailboxName) {
&& originalMailboxName != fMailboxName) {
// TODO: mailbox name has changed
}
@@ -110,24 +132,20 @@ IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
fLastUID);
attr_info info;
status_t status = node.GetAttrInfo(kStateAttribute, &info);
status = node.GetAttrInfo(kStateAttribute, &info);
if (status == B_OK) {
struct entry {
uint32 uid;
uint32 flags;
} _PACKED;
struct entry* entries = (struct entry*)malloc(info.size);
if (entries == NULL) {
// TODO: indicate B_NO_MEMORY
return;
}
if (entries == NULL)
return B_NO_MEMORY;
ssize_t bytesRead = node.ReadAttr(kStateAttribute, B_RAW_TYPE, 0,
entries, info.size);
if (bytesRead != info.size) {
// TODO: indicate read error resp. corrupted data
return;
}
if (bytesRead != info.size)
return B_BAD_DATA;
for (size_t i = 0; i < info.size / sizeof(entry); i++) {
uint32 uid = B_BENDIAN_TO_HOST_INT32(entries[i].uid);
@@ -137,21 +155,20 @@ IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
}
}
// Initialize current state from actual folder
// TODO: this should be done in another thread
//_InitializeFolderState();
}
IMAPFolder::~IMAPFolder()
{
return B_OK;
}
void
IMAPFolder::SetListener(FolderListener* listener)
{
ASSERT(fListener == NULL);
fListener = listener;
// Initialize current state from actual folder
// TODO: this should be done in another thread
_InitializeFolderState();
}
@@ -169,20 +186,33 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity)
status_t
IMAPFolder::GetMessageEntryRef(uint32 uid, entry_ref& ref) const
IMAPFolder::GetMessageEntryRef(uint32 uid, entry_ref& ref)
{
UIDToRefMap::const_iterator found = fRefMap.find(uid);
if (found == fRefMap.end())
MutexLocker locker(fLock);
return _GetMessageEntryRef(uid, ref);
}
status_t
IMAPFolder::GetMessageUID(const entry_ref& ref, uint32& uid) const
{
BNode node(&ref);
status_t status = node.InitCheck();
if (status != B_OK)
return status;
uid = _ReadUniqueID(node);
if (uid == 0)
return B_ENTRY_NOT_FOUND;
ref = found->second;
return B_OK;
}
uint32
IMAPFolder::MessageFlags(uint32 uid) const
IMAPFolder::MessageFlags(uint32 uid)
{
MutexLocker locker(fLock);
UIDToFlagsMap::const_iterator found = fFlagsMap.find(uid);
if (found == fFlagsMap.end())
return 0;
@@ -191,13 +221,71 @@ IMAPFolder::MessageFlags(uint32 uid) const
}
void
IMAPFolder::UpdateMessageFlags(uint32 uid, uint32 mailboxFlags)
{
if (uid > LastUID())
return;
entry_ref ref;
BNode node;
while (true) {
status_t status = GetMessageEntryRef(uid, ref);
if (status == B_ENTRY_NOT_FOUND) {
// The message does not exist anymore locally, delete it on the server
// TODO: copy it to the trash directory first!
fProtocol.UpdateMessageFlags(*this, uid, IMAP::kDeleted);
return;
}
if (status == B_OK)
status = node.SetTo(&ref);
if (status == B_TIMED_OUT)
continue;
if (status != B_OK)
return;
break;
}
uint32 previousFlags = MessageFlags(uid);
uint32 currentFlags = previousFlags;
if (_MailToIMAPFlags(node, currentFlags) != B_OK)
return;
// Compare flags to previous/current flags, and update either the
// message on the server, or the message locally (or even both)
uint32 nextFlags = mailboxFlags;
_TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
IMAP::kSeen, nextFlags);
_TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
IMAP::kAnswered, nextFlags);
if (nextFlags != previousFlags)
_WriteFlags(node, nextFlags);
if (currentFlags != nextFlags) {
// Update mail message attributes
BMessage attributes;
_IMAPToMailFlags(nextFlags, attributes);
node << attributes;
fFlagsMap[uid] = nextFlags;
}
if (mailboxFlags != nextFlags) {
// Update server flags
fProtocol.UpdateMessageFlags(*this, uid, nextFlags);
}
}
/*! 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.
was an error.
*/
status_t
IMAPFolder::StoreMessage(uint32 fetchFlags, BDataIO& stream,
@@ -228,16 +316,13 @@ void
IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
uint32 uid, uint32 flags)
{
_WriteUniqueIDValidity(file);
_WriteUniqueID(file, uid);
if ((fetchFlags & IMAP::kFetchFlags) != 0)
_WriteFlags(file, flags);
// 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");
_IMAPToMailFlags(flags, attributes);
fProtocol.MessageStored(*this, ref, file, fetchFlags, attributes);
file.Unset();
@@ -258,6 +343,29 @@ IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
}
/*! Pushes the refs for the pending bodies to the pending bodies list.
This allows to prevent retrieving bodies more than once.
*/
void
IMAPFolder::RegisterPendingBodies(IMAP::MessageUIDList& uids,
const BMessenger* replyTo)
{
MutexLocker locker(fLock);
MessengerList messengers;
if (replyTo != NULL)
messengers.push_back(*replyTo);
IMAP::MessageUIDList::const_iterator iterator = uids.begin();
for (; iterator != uids.end(); iterator++) {
if (replyTo != NULL)
fPendingBodies[*iterator].push_back(*replyTo);
else
fPendingBodies[*iterator].begin();
}
}
/*! Appends the given \a stream as body to the message file for the
specified unique ID. The file will remain open when this method exits
without an error.
@@ -293,6 +401,15 @@ IMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid)
BMessage attributes;
fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody, attributes);
file.Unset();
_NotifyStoredBody(ref, uid, B_OK);
}
void
IMAPFolder::StoringBodyFailed(const entry_ref& ref, uint32 uid, status_t error)
{
_NotifyStoredBody(ref, uid, error);
}
@@ -302,15 +419,6 @@ IMAPFolder::DeleteMessage(uint32 uid)
}
/*! Called when the flags of a message changed on the server. This will update
the flags for the local file.
*/
void
IMAPFolder::SetMessageFlags(uint32 uid, uint32 flags)
{
}
void
IMAPFolder::MessageReceived(BMessage* message)
{
@@ -320,12 +428,7 @@ IMAPFolder::MessageReceived(BMessage* message)
void
IMAPFolder::_InitializeFolderState()
{
// Create set of the last known UID state - if an entry is found, it
// is being removed from the list. The remaining entries were deleted.
std::set<uint32> lastUIDs;
UIDToFlagsMap::iterator iterator = fFlagsMap.begin();
for (; iterator != fFlagsMap.end(); iterator++)
lastUIDs.insert(iterator->first);
fInitializing = true;
BDirectory directory(&fRef);
BEntry entry;
@@ -336,30 +439,42 @@ IMAPFolder::_InitializeFolderState()
|| node.SetTo(&entry) != B_OK)
continue;
uint32 uidValidity = _ReadUniqueIDValidity(node);
if (uidValidity != fUIDValidity) {
// TODO: add file to mailbox
continue;
}
uint32 uid = _ReadUniqueID(node);
uint32 flags = _ReadFlags(node);
// TODO: make sure a listener exists at this point!
std::set<uint32>::iterator found = lastUIDs.find(uid);
if (found != lastUIDs.end()) {
// The message is still around
lastUIDs.erase(found);
uint32 flagsFound = MessageFlags(uid);
if (flagsFound != flags) {
// Its flags have changed locally, and need to be updated
fListener->MessageFlagsChanged(_Token(uid), ref,
flagsFound, flags);
}
} else {
// This is a new message
// TODO: the token must be the originating token!
uid = fListener->MessageAdded(_Token(uid), ref);
_WriteUniqueID(node, uid);
}
MutexLocker locker(fLock);
fRefMap.insert(std::make_pair(uid, ref));
fFlagsMap.insert(std::make_pair(uid, flags));
// // TODO: make sure a listener exists at this point!
// std::set<uint32>::iterator found = lastUIDs.find(uid);
// if (found != lastUIDs.end()) {
// // The message is still around
// lastUIDs.erase(found);
//
// uint32 flagsFound = MessageFlags(uid);
// if (flagsFound != flags) {
// // Its flags have changed locally, and need to be updated
// fListener->MessageFlagsChanged(_Token(uid), ref,
// flagsFound, flags);
// }
// } else {
// // This is a new message
// // TODO: the token must be the originating token!
// uid = fListener->MessageAdded(_Token(uid), ref);
// _WriteUniqueID(node, uid);
// }
//
fRefMap.insert(std::make_pair(uid, ref));
}
fInitializing = false;
}
@@ -375,8 +490,99 @@ IMAPFolder::_Token(uint32 uid) const
}
void
IMAPFolder::_NotifyStoredBody(const entry_ref& ref, uint32 uid, status_t status)
{
MutexLocker locker(fLock);
MessengerMap::iterator found = fPendingBodies.find(uid);
if (found != fPendingBodies.end()) {
MessengerList messengers = found->second;
fPendingBodies.erase(found);
locker.Unlock();
MessengerList::iterator iterator = messengers.begin();
for (; iterator != messengers.end(); iterator++)
BInboundMailProtocol::ReplyBodyFetched(*iterator, ref, status);
}
}
status_t
IMAPFolder::_GetMessageEntryRef(uint32 uid, entry_ref& ref) const
{
UIDToRefMap::const_iterator found = fRefMap.find(uid);
if (found == fRefMap.end())
return fInitializing ? B_TIMED_OUT : B_ENTRY_NOT_FOUND;
ref = found->second;
return B_OK;
}
void
IMAPFolder::_IMAPToMailFlags(uint32 flags, BMessage& attributes)
{
// TODO: add some utility function for this in libmail.so
if ((flags & IMAP::kAnswered) != 0)
attributes.AddString(B_MAIL_ATTR_STATUS, "Answered");
else if ((flags & IMAP::kFlagged) != 0)
attributes.AddString(B_MAIL_ATTR_STATUS, "Starred");
else if ((flags & IMAP::kSeen) != 0)
attributes.AddString(B_MAIL_ATTR_STATUS, "Read");
}
status_t
IMAPFolder::_MailToIMAPFlags(BNode& node, uint32& flags)
{
BString mailStatus;
status_t status = node.ReadAttrString(B_MAIL_ATTR_STATUS, &mailStatus);
if (status != B_OK)
return status;
flags &= ~(IMAP::kAnswered | IMAP::kSeen);
// TODO: add some utility function for this in libmail.so
if (mailStatus == "Answered")
flags |= IMAP::kAnswered | IMAP::kSeen;
else if (mailStatus == "Read")
flags |= IMAP::kSeen;
else if (mailStatus == "Starred")
flags |= IMAP::kFlagged | IMAP::kSeen;
return B_OK;
}
void
IMAPFolder::_TestMessageFlags(uint32 previousFlags, uint32 mailboxFlags,
uint32 currentFlags, uint32 testFlag, uint32& nextFlags)
{
if ((previousFlags & testFlag) != (mailboxFlags & testFlag)) {
if ((previousFlags & testFlag) == (currentFlags & testFlag)) {
// The flags on the mailbox changed, update local flags
nextFlags &= ~testFlag;
nextFlags |= mailboxFlags & testFlag;
} else {
// Both flags changed. Since we don't have the means to do
// conflict resolution, we use a best effort mechanism
nextFlags |= testFlag;
}
return;
}
// Previous message flags, and mailbox flags are identical, see
// if the user changed the flag locally
if ((currentFlags & testFlag) != (previousFlags & testFlag)) {
// Flag changed, update mailbox
nextFlags &= ~testFlag;
nextFlags |= currentFlags & testFlag;
}
}
uint32
IMAPFolder::_ReadUniqueID(BNode& node)
IMAPFolder::_ReadUniqueID(BNode& node) const
{
// For compatibility we must assume that the UID is stored as a string
BString string;
@@ -388,8 +594,9 @@ IMAPFolder::_ReadUniqueID(BNode& node)
status_t
IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid)
IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid) const
{
// For compatibility we must assume that the UID is stored as a string
BString string;
string << uid;
@@ -398,21 +605,36 @@ IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid)
uint32
IMAPFolder::_ReadFlags(BNode& node)
IMAPFolder::_ReadUniqueIDValidity(BNode& node) const
{
return _ReadUInt32(node, kUIDValidityAttribute);
}
status_t
IMAPFolder::_WriteUniqueIDValidity(BNode& node) const
{
return _WriteUInt32(node, kUIDValidityAttribute, fUIDValidity);
}
uint32
IMAPFolder::_ReadFlags(BNode& node) const
{
return _ReadUInt32(node, kFlagsAttribute);
}
status_t
IMAPFolder::_WriteFlags(BNode& node, uint32 flags)
IMAPFolder::_WriteFlags(BNode& node, uint32 flags) const
{
return _WriteUInt32(node, kFlagsAttribute, flags);
}
uint32
IMAPFolder::_ReadUInt32(BNode& node, const char* attribute)
IMAPFolder::_ReadUInt32(BNode& node, const char* attribute) const
{
uint32 value;
ssize_t bytesRead = node.ReadAttr(attribute, B_UINT32_TYPE, 0,
@@ -425,7 +647,7 @@ IMAPFolder::_ReadUInt32(BNode& node, const char* attribute)
status_t
IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value)
IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value) const
{
ssize_t bytesWritten = node.WriteAttr(attribute, B_UINT32_TYPE, 0,
&value, sizeof(uint32));
@@ -437,7 +659,7 @@ IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value)
status_t
IMAPFolder::_WriteStream(BFile& file, BDataIO& stream, size_t& length)
IMAPFolder::_WriteStream(BFile& file, BDataIO& stream, size_t& length) const
{
char buffer[65535];
while (length > 0) {
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_FOLDER_H
@@ -14,6 +14,10 @@
#include <Handler.h>
#include <String.h>
#include <locks.h>
#include "Commands.h"
class BFile;
class IMAPProtocol;
@@ -50,16 +54,23 @@ public:
const entry_ref& ref);
virtual ~IMAPFolder();
status_t Init();
const BString& MailboxName() const { return fMailboxName; }
ino_t NodeID() const { return fNodeID; }
void SetListener(FolderListener* listener);
void SetUIDValidity(uint32 uidValidity);
uint32 LastUID() const { return fLastUID; }
status_t GetMessageEntryRef(uint32 uid,
entry_ref& ref) const;
uint32 MessageFlags(uint32 uid) const;
status_t GetMessageEntryRef(uint32 uid, entry_ref& ref);
status_t GetMessageUID(const entry_ref& ref,
uint32& uid) const;
uint32 MessageFlags(uint32 uid);
void UpdateMessageFlags(uint32 uid,
uint32 mailboxFlags);
status_t StoreMessage(uint32 fetchFlags, BDataIO& stream,
size_t& length, entry_ref& ref,
@@ -68,49 +79,75 @@ public:
uint32 fetchFlags, uint32 uid,
uint32 flags);
void RegisterPendingBodies(
IMAP::MessageUIDList& uids,
const BMessenger* replyTo);
status_t StoreBody(uint32 uid, BDataIO& stream,
size_t& length, entry_ref& ref,
BFile& file);
void BodyStored(entry_ref& ref, BFile& file,
uint32 uid);
void StoringBodyFailed(const entry_ref& ref,
uint32 uid, status_t error);
void DeleteMessage(uint32 uid);
void SetMessageFlags(uint32 uid, uint32 flags);
virtual void MessageReceived(BMessage* message);
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);
void _NotifyStoredBody(const entry_ref& ref,
uint32 uid, status_t status);
status_t _GetMessageEntryRef(uint32 uid,
entry_ref& ref) const;
uint32 _ReadUInt32(BNode& node, const char* attribute);
void _IMAPToMailFlags(uint32 flags,
BMessage& attributes);
status_t _MailToIMAPFlags(BNode& node, uint32& flags);
void _TestMessageFlags(uint32 previousFlags,
uint32 mailboxFlags, uint32 currentFlags,
uint32 testFlag, uint32& nextFlags);
uint32 _ReadUniqueID(BNode& node) const;
status_t _WriteUniqueID(BNode& node, uint32 uid) const;
uint32 _ReadUniqueIDValidity(BNode& node) const;
status_t _WriteUniqueIDValidity(BNode& node) const;
uint32 _ReadFlags(BNode& node) const;
status_t _WriteFlags(BNode& node, uint32 flags) const;
uint32 _ReadUInt32(BNode& node,
const char* attribute) const;
status_t _WriteUInt32(BNode& node,
const char* attribute, uint32 value);
const char* attribute, uint32 value) const;
status_t _WriteStream(BFile& file, BDataIO& stream,
size_t& length);
size_t& length) const;
private:
typedef std::vector<BMessenger> MessengerList;
#if __GNUC__ >= 4
typedef __gnu_cxx::hash_map<uint32, uint32> UIDToFlagsMap;
typedef __gnu_cxx::hash_map<uint32, entry_ref> UIDToRefMap;
typedef __gnu_cxx::hash_map<uint32, MessengerList> MessengerMap;
#else
typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
typedef std::hash_map<uint32, MessengerList> MessengerMap;
#endif
IMAPProtocol& fProtocol;
const entry_ref fRef;
BString fMailboxName;
ino_t fNodeID;
uint32 fUIDValidity;
uint32 fLastUID;
FolderListener* fListener;
mutex fLock;
bool fInitializing;
UIDToRefMap fRefMap;
UIDToFlagsMap fFlagsMap;
MessengerMap fPendingBodies;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2013-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
@@ -86,7 +86,7 @@ IMAPMailbox::MessageAdded(const MessageToken& fromToken, const entry_ref& ref)
{
printf("IMAP: message added %s, uid %" B_PRIu32 "\n", ref.name,
fromToken.uid);
return 0;
return fromToken.uid;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2013-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
@@ -7,6 +7,7 @@
#include "IMAPProtocol.h"
#include <Directory.h>
#include <Messenger.h>
#include "IMAPConnectionWorker.h"
#include "IMAPFolder.h"
@@ -27,6 +28,8 @@ IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
destination.Path(), strerror(status));
}
mutex_init(&fWorkerLock, "imap worker lock");
PostMessage(B_READY_TO_RUN);
}
@@ -61,6 +64,8 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
if (idle)
workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
MutexLocker locker(fWorkerLock);
if (newFolders.IsEmpty() && fWorkers.CountItems() == workersWanted) {
// Nothing to do - we've already distributed everything
return B_OK;
@@ -70,6 +75,7 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
for (int32 i = 0; i < fWorkers.CountItems(); i++) {
fWorkers.ItemAt(i)->RemoveAllMailboxes();
}
fWorkerMap.clear();
// Create/remove connection workers as allowed and needed
while (fWorkers.CountItems() < workersWanted) {
@@ -104,7 +110,11 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
FolderMap::iterator iterator = fFolders.begin();
int32 index = 0;
for (; iterator != fFolders.end(); iterator++) {
fWorkers.ItemAt(index)->AddMailbox(iterator->second);
IMAPConnectionWorker* worker = fWorkers.ItemAt(index);
IMAPFolder* folder = iterator->second;
worker->AddMailbox(folder);
fWorkerMap.insert(std::make_pair(folder, worker));
index = (index + 1) % fWorkers.CountItems();
}
@@ -116,13 +126,21 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
void
IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
{
MutexLocker locker(fWorkerLock);
fWorkers.RemoveItem(worker);
WorkerMap::iterator iterator = fWorkerMap.begin();
while (iterator != fWorkerMap.end()) {
WorkerMap::iterator removed = iterator++;
if (removed->second == worker)
fWorkerMap.erase(removed);
}
}
void
IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
uint32 fetchFlags, BMessage& attributes)
IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref,
BFile& stream, uint32 fetchFlags, BMessage& attributes)
{
if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
== (IMAP::kFetchHeader | IMAP::kFetchBody)) {
@@ -135,11 +153,26 @@ IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
}
status_t
IMAPProtocol::UpdateMessageFlags(IMAPFolder& folder, uint32 uid, uint32 flags)
{
MutexLocker locker(fWorkerLock);
WorkerMap::const_iterator found = fWorkerMap.find(&folder);
if (found == fWorkerMap.end())
return B_ERROR;
IMAPConnectionWorker* worker = found->second;
return worker->EnqueueUpdateFlags(folder, uid, flags);
}
status_t
IMAPProtocol::SyncMessages()
{
puts("IMAP: sync");
MutexLocker locker(fWorkerLock);
if (fWorkers.IsEmpty()) {
// Create main (and possibly initial) connection worker
IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
@@ -157,14 +190,6 @@ IMAPProtocol::SyncMessages()
}
status_t
IMAPProtocol::FetchBody(const entry_ref& ref)
{
printf("IMAP: fetch body %s\n", ref.name);
return B_ERROR;
}
status_t
IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
{
@@ -173,14 +198,6 @@ IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
}
status_t
IMAPProtocol::DeleteMessage(const entry_ref& ref)
{
printf("IMAP: delete message %s\n", ref.name);
return B_ERROR;
}
status_t
IMAPProtocol::AppendMessage(const entry_ref& ref)
{
@@ -204,6 +221,38 @@ IMAPProtocol::MessageReceived(BMessage* message)
}
status_t
IMAPProtocol::HandleFetchBody(const entry_ref& ref, const BMessenger& replyTo)
{
printf("IMAP: fetch body %s\n", ref.name);
MutexLocker locker(fWorkerLock);
IMAPFolder* folder = _FolderFor(ref.directory);
if (folder == NULL)
return B_ENTRY_NOT_FOUND;
uint32 uid;
status_t status = folder->GetMessageUID(ref, uid);
if (status != B_OK)
return status;
WorkerMap::const_iterator found = fWorkerMap.find(folder);
if (found == fWorkerMap.end())
return B_ERROR;
IMAPConnectionWorker* worker = found->second;
return worker->EnqueueFetchBody(*folder, uid, replyTo);
}
status_t
IMAPProtocol::HandleDeleteMessage(const entry_ref& ref)
{
printf("IMAP: delete message %s\n", ref.name);
return B_ERROR;
}
void
IMAPProtocol::ReadyToRun()
{
@@ -239,7 +288,27 @@ IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
return NULL;
}
return new IMAPFolder(*this, mailbox, ref);
IMAPFolder* folder = new IMAPFolder(*this, mailbox, ref);
status = folder->Init();
if (status != B_OK) {
fprintf(stderr, "Initializing folder %s failed: %s\n", path.Path(),
strerror(status));
return NULL;
}
fFolderNodeMap.insert(std::make_pair(folder->NodeID(), folder));
return folder;
}
IMAPFolder*
IMAPProtocol::_FolderFor(ino_t directory)
{
FolderNodeMap::const_iterator found = fFolderNodeMap.find(directory);
if (found != fFolderNodeMap.end())
return found->second;
return NULL;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2013-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_PROTOCOL_H
@@ -11,6 +11,9 @@
#include <MailProtocol.h>
#include <ObjectList.h>
#include <locks.h>
#include "Commands.h"
#include "Settings.h"
@@ -38,27 +41,39 @@ public:
entry_ref& ref, BFile& stream,
uint32 fetchFlags, BMessage& attributes);
status_t UpdateMessageFlags(IMAPFolder& folder,
uint32 uid, uint32 flags);
virtual status_t SyncMessages();
virtual status_t FetchBody(const entry_ref& ref);
virtual status_t MarkMessageAsRead(const entry_ref& ref,
read_flags flags = B_READ);
virtual status_t DeleteMessage(const entry_ref& ref);
virtual status_t AppendMessage(const entry_ref& ref);
virtual void MessageReceived(BMessage* message);
protected:
virtual status_t HandleFetchBody(const entry_ref& ref,
const BMessenger& replyTo);
virtual status_t HandleDeleteMessage(const entry_ref& ref);
void ReadyToRun();
private:
IMAPFolder* _CreateFolder(const BString& mailbox,
const BString& separator);
IMAPFolder* _FolderFor(ino_t directory);
status_t _EnqueueCheckMailboxes();
protected:
typedef std::map<IMAPFolder*, IMAPConnectionWorker*> WorkerMap;
typedef std::map<ino_t, IMAPFolder*> FolderNodeMap;
Settings fSettings;
mutex fWorkerLock;
BObjectList<IMAPConnectionWorker> fWorkers;
WorkerMap fWorkerMap;
FolderMap fFolders;
FolderNodeMap fFolderNodeMap;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015, Haiku, Inc. All rights reserved.
* Copyright 2011-2016, Haiku, Inc. All rights reserved.
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
* Distributed under the terms of the MIT License.
*/
@@ -473,7 +473,7 @@ SetFlagsCommand::SetFlagsCommand(uint32 uid, uint32 flags)
BString
SetFlagsCommand::CommandString()
{
BString command = "STORE ";
BString command = "UID STORE ";
command << fUID << " FLAGS (" << GenerateFlagString(fFlags) << ")";
return command;
@@ -1,5 +1,5 @@
/*
* Copyright 2007-2015, Haiku, Inc. All rights reserved.
* Copyright 2007-2016, Haiku, Inc. All rights reserved.
* Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
*
@@ -253,7 +253,7 @@ POP3Protocol::SyncMessages()
status_t
POP3Protocol::FetchBody(const entry_ref& ref)
POP3Protocol::HandleFetchBody(const entry_ref& ref, const BMessenger& replyTo)
{
ResetProgress("Fetch body");
SetTotalItems(1);
@@ -303,6 +303,7 @@ POP3Protocol::FetchBody(const entry_ref& ref)
BMessage attributes;
NotifyBodyFetched(ref, file, attributes);
ReplyBodyFetched(replyTo, ref, B_OK);
if (!leaveOnServer)
Delete(toRetrieve);
@@ -316,7 +317,7 @@ POP3Protocol::FetchBody(const entry_ref& ref)
status_t
POP3Protocol::DeleteMessage(const entry_ref& ref)
POP3Protocol::HandleDeleteMessage(const entry_ref& ref)
{
status_t error = Connect();
if (error < B_OK)
@@ -1,5 +1,5 @@
/*
* Copyright 2007-2013, Haiku Inc. All Rights Reserved.
* Copyright 2007-2016, Haiku Inc. All Rights Reserved.
* Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
*
@@ -35,14 +35,16 @@ public:
status_t Disconnect();
status_t SyncMessages();
status_t FetchBody(const entry_ref& ref);
status_t DeleteMessage(const entry_ref& ref);
status_t Retrieve(int32 message, BPositionIO* to);
status_t GetHeader(int32 message, BPositionIO* to);
void Delete(int32 index);
protected:
virtual status_t HandleFetchBody(const entry_ref& ref,
const BMessenger& replyTo);
virtual status_t HandleDeleteMessage(const entry_ref& ref);
// pop3 methods
status_t Open(const char* server, int port,
int protocol);
+4 -1
View File
@@ -1036,6 +1036,7 @@ TMailWindow::MessageReceived(BMessage* msg)
{
status_t status = msg->FindInt32("status");
if (status != B_OK) {
fprintf(stderr, "Body could not be fetched: %s\n", strerror(status));
PostMessage(B_QUIT_REQUESTED);
break;
}
@@ -2862,7 +2863,9 @@ TMailWindow::OpenMessage(const entry_ref* ref, uint32 characterSetForDecoding)
if (strcmp(mimeType, B_PARTIAL_MAIL_TYPE) == 0) {
BMessenger listener(this);
BMailDaemon().FetchBody(*ref, &listener);
status_t status = BMailDaemon().FetchBody(*ref, &listener);
if (status != B_OK)
fprintf(stderr, "Could not fetch body: %s\n", strerror(status));
fileInfo.GetType(mimeType);
_SetDownloading(true);
} else
+38 -11
View File
@@ -1,10 +1,9 @@
/*
* Copyright 2011-2015, Haiku, Inc. All rights reserved.
* Copyright 2011-2016, Haiku, Inc. All rights reserved.
* Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
*/
//#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
@@ -415,17 +414,15 @@ BInboundMailProtocol::MessageReceived(BMessage* message)
case kMsgFetchBody:
{
entry_ref ref;
message->FindRef("ref", &ref);
status_t status = FetchBody(ref);
BMessenger target;
if (message->FindMessenger("target", &target) != B_OK)
if (message->FindRef("ref", &ref) != B_OK)
break;
BMessage message(B_MAIL_BODY_FETCHED);
message.AddInt32("status", status);
message.AddRef("ref", &ref);
target.SendMessage(&message);
BMessenger target;
message->FindMessenger("target", &target);
status_t status = HandleFetchBody(ref, target);
if (status != B_OK)
ReplyBodyFetched(target, ref, status);
break;
}
@@ -461,6 +458,18 @@ BInboundMailProtocol::MessageReceived(BMessage* message)
}
status_t
BInboundMailProtocol::FetchBody(const entry_ref& ref, BMessenger* replyTo)
{
BMessage message(kMsgFetchBody);
message.AddRef("ref", &ref);
if (replyTo != NULL)
message.AddMessenger("target", *replyTo);
return BMessenger(this).SendMessage(&message);
}
status_t
BInboundMailProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
{
@@ -469,6 +478,13 @@ BInboundMailProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
}
status_t
BInboundMailProtocol::DeleteMessage(const entry_ref& ref)
{
return B_ERROR;
}
status_t
BInboundMailProtocol::AppendMessage(const entry_ref& ref)
{
@@ -476,6 +492,17 @@ BInboundMailProtocol::AppendMessage(const entry_ref& ref)
}
/*static*/ void
BInboundMailProtocol::ReplyBodyFetched(const BMessenger& replyTo,
const entry_ref& ref, status_t status)
{
BMessage message(B_MAIL_BODY_FETCHED);
message.AddInt32("status", status);
message.AddRef("ref", &ref);
replyTo.SendMessage(&message);
}
void
BInboundMailProtocol::NotiyMailboxSynchronized(status_t status)
{
+7 -7
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2011, Haiku, Inc. All rights reserved.
* Copyright 2011-2016, Haiku, Inc. All rights reserved.
* Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
*/
@@ -159,13 +159,13 @@ write_read_attr(BNode& node, read_flags flag)
< 0)
return B_ERROR;
// manage the status string only if it currently has a "read" status
// Manage the status string only if it currently has a known state
BString currentStatus;
if (node.ReadAttrString(B_MAIL_ATTR_STATUS, &currentStatus) == B_OK) {
if (currentStatus.ICompare("New") != 0
&& currentStatus.ICompare("Read") != 0
&& currentStatus.ICompare("Seen") != 0)
return B_OK;
if (node.ReadAttrString(B_MAIL_ATTR_STATUS, &currentStatus) == B_OK
&& currentStatus.ICompare("New") != 0
&& currentStatus.ICompare("Read") != 0
&& currentStatus.ICompare("Seen") != 0) {
return B_OK;
}
const char* statusString = flag == B_READ ? "Read"
+2 -12
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2007-2015, Haiku, Inc. All rights reserved.
* Copyright 2007-2016, Haiku, Inc. All rights reserved.
* Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
* Distributed under the terms of the MIT License.
@@ -67,16 +67,6 @@ public:
{
}
status_t FetchBody(const entry_ref& ref, BMessenger* replyTo)
{
BMessage message(kMsgFetchBody);
message.AddRef("ref", &ref);
if (replyTo != NULL)
message.AddMessenger("target", *replyTo);
return SendMessage(&message);
}
status_t MarkAsRead(const entry_ref& ref, read_flags flag)
{
BMessage message(kMsgMarkMessageAsRead);
@@ -248,7 +238,7 @@ MailDaemonApplication::RefsReceived(BMessage* message)
if (message->FindMessenger("target", &target) != B_OK)
replyTo = NULL;
InboundMessenger(protocol).FetchBody(ref, replyTo);
protocol->FetchBody(ref, replyTo);
}
}