imap: Work in progress of connection setup.
* SelectCommand now correctly encodes the mailbox name again. * Hierarchy separators from the LIST/LSUB commands are now properly parsed, and taken into account. * Folders in the file system are now created for mailboxes on the server, supporting hierarchical mailbox names. * A connection worker now has an IMAPFolder that handles the local side of the mailbox, and an IMAPMailbox that handles the server side. * Connection workers are now created/deleted, and setup correctly. They will now also wait in case they don't have a mailbox until they get some.
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
#include <StringForSize.h>
|
#include <StringForSize.h>
|
||||||
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "Utilities.h"
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
@@ -332,10 +333,12 @@ FolderConfigWindow::_LoadFolders()
|
|||||||
|
|
||||||
// TODO: don't get all of them at once, but retrieve them level by level
|
// TODO: don't get all of them at once, but retrieve them level by level
|
||||||
fFolderList.clear();
|
fFolderList.clear();
|
||||||
fProtocol.GetFolders(fFolderList);
|
BString separator;
|
||||||
|
fProtocol.GetFolders(fFolderList, separator);
|
||||||
for (size_t i = 0; i < fFolderList.size(); i++) {
|
for (size_t i = 0; i < fFolderList.size(); i++) {
|
||||||
IMAP::FolderEntry& entry = fFolderList[i];
|
IMAP::FolderEntry& entry = fFolderList[i];
|
||||||
CheckBoxItem* item = new CheckBoxItem(entry.folder, entry.subscribed);
|
CheckBoxItem* item = new CheckBoxItem(
|
||||||
|
MailboxToFolderName(entry.folder, separator), entry.subscribed);
|
||||||
fFolderListView->AddItem(item);
|
fFolderListView->AddItem(item);
|
||||||
item->SetListView(fFolderListView);
|
item->SetListView(fFolderListView);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <Autolock.h>
|
#include <Autolock.h>
|
||||||
|
|
||||||
|
#include "IMAPFolder.h"
|
||||||
|
#include "IMAPMailbox.h"
|
||||||
#include "IMAPProtocol.h"
|
#include "IMAPProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner,
|
|||||||
:
|
:
|
||||||
fOwner(owner),
|
fOwner(owner),
|
||||||
fSettings(settings),
|
fSettings(settings),
|
||||||
|
fIdleBox(NULL),
|
||||||
fMain(main),
|
fMain(main),
|
||||||
fStopped(false)
|
fStopped(false)
|
||||||
{
|
{
|
||||||
@@ -27,55 +30,54 @@ IMAPConnectionWorker::~IMAPConnectionWorker()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
IMAPConnectionWorker::HasMailboxes() const
|
||||||
|
{
|
||||||
|
BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker);
|
||||||
|
return !fMailboxes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
IMAPConnectionWorker::CountMailboxes() const
|
IMAPConnectionWorker::CountMailboxes() const
|
||||||
{
|
{
|
||||||
BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker);
|
BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker);
|
||||||
return (fIdleBox.IsEmpty() ? 0 : 1) + fOtherBoxes.size();
|
return fMailboxes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IMAPConnectionWorker::AddMailbox(const BString& name)
|
IMAPConnectionWorker::AddMailbox(IMAPFolder* folder)
|
||||||
{
|
{
|
||||||
BAutolock locker(fLocker);
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
if (fSettings.IdleMode() && fIdleBox.IsEmpty()) {
|
fMailboxes.insert(std::make_pair(folder, (IMAPMailbox*)NULL));
|
||||||
fIdleBox = name;
|
|
||||||
} else if (fSettings.IdleMode() && name == "INBOX") {
|
// Prefer to have the INBOX in idle mode over other mail boxes
|
||||||
// Prefer to have the INBOX in idle mode over other mail boxes
|
if (fIdleBox == NULL || folder->MailboxName().ICompare("INBOX") == 0)
|
||||||
fOtherBoxes.push_back(fIdleBox);
|
fIdleBox = folder;
|
||||||
fIdleBox = name;
|
|
||||||
} else
|
|
||||||
fOtherBoxes.push_back(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IMAPConnectionWorker::RemoveMailbox(const BString& name)
|
IMAPConnectionWorker::RemoveAllMailboxes()
|
||||||
{
|
{
|
||||||
BAutolock locker(fLocker);
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
if (fSettings.IdleMode() && fIdleBox == name) {
|
// Reset listeners, and delete the mailboxes
|
||||||
if (!fOtherBoxes.empty()) {
|
MailboxMap::iterator iterator = fMailboxes.begin();
|
||||||
fIdleBox = fOtherBoxes[0];
|
for (; iterator != fMailboxes.end(); iterator++) {
|
||||||
fOtherBoxes.erase(fOtherBoxes.begin());
|
iterator->first->SetListener(NULL);
|
||||||
} else
|
delete iterator->second;
|
||||||
fIdleBox.SetTo(NULL);
|
|
||||||
} else {
|
|
||||||
StringList::iterator iterator = fOtherBoxes.begin();
|
|
||||||
for (; iterator != fOtherBoxes.end(); iterator++) {
|
|
||||||
if (*iterator == name) {
|
|
||||||
fOtherBoxes.erase(iterator);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fIdleBox = NULL;
|
||||||
|
fMailboxes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IMAPConnectionWorker::Start()
|
IMAPConnectionWorker::Run()
|
||||||
{
|
{
|
||||||
fThread = spawn_thread(&_Worker, "imap connection worker",
|
fThread = spawn_thread(&_Worker, "imap connection worker",
|
||||||
B_NORMAL_PRIORITY, this);
|
B_NORMAL_PRIORITY, this);
|
||||||
@@ -88,7 +90,7 @@ IMAPConnectionWorker::Start()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IMAPConnectionWorker::Stop()
|
IMAPConnectionWorker::Quit()
|
||||||
{
|
{
|
||||||
// TODO: we'll also need to interrupt listening to the socket
|
// TODO: we'll also need to interrupt listening to the socket
|
||||||
fStopped = true;
|
fStopped = true;
|
||||||
@@ -103,6 +105,10 @@ IMAPConnectionWorker::_Worker()
|
|||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
bool idle = fSettings.IdleMode()
|
||||||
|
&& fProtocol.Capabilities().Contains("IDLE");
|
||||||
|
bool initial = true;
|
||||||
|
|
||||||
while (!fStopped) {
|
while (!fStopped) {
|
||||||
if (fMain) {
|
if (fMain) {
|
||||||
// The main worker checks the subscribed folders, and creates
|
// The main worker checks the subscribed folders, and creates
|
||||||
@@ -112,14 +118,39 @@ IMAPConnectionWorker::_Worker()
|
|||||||
|
|
||||||
BAutolock locker(fLocker);
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
if (!fIdleBox.IsEmpty())
|
if (!HasMailboxes()) {
|
||||||
printf("%p: IDLE: %s\n", this, fIdleBox.String());
|
locker.Unlock();
|
||||||
|
_Wait();
|
||||||
StringList::iterator iterator = fOtherBoxes.begin();
|
continue;
|
||||||
for (; iterator != fOtherBoxes.end(); iterator++) {
|
|
||||||
printf("%p: check: %s\n", this, iterator->String());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!initial && idle && fIdleBox != NULL) {
|
||||||
|
printf("%p: IDLE: %s\n", this, fIdleBox->MailboxName().String());
|
||||||
|
// TODO: enter IDLE mode
|
||||||
|
}
|
||||||
|
|
||||||
|
MailboxMap::iterator iterator = fMailboxes.begin();
|
||||||
|
for (; iterator != fMailboxes.end(); iterator++) {
|
||||||
|
IMAPFolder* folder = iterator->first;
|
||||||
|
if (!initial && idle && folder == fIdleBox)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
printf("%p: check: %s\n", this, folder->MailboxName().String());
|
||||||
|
IMAPMailbox* mailbox = iterator->second;
|
||||||
|
if (mailbox == NULL) {
|
||||||
|
mailbox = new IMAPMailbox(fProtocol, folder->MailboxName());
|
||||||
|
folder->SetListener(mailbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAP::SelectCommand select(folder->MailboxName().String());
|
||||||
|
status_t status = fProtocol.ProcessCommand(select);
|
||||||
|
if (status == B_OK) {
|
||||||
|
folder->SetUIDValidity(select.UIDValidity());
|
||||||
|
// TODO: trigger download of mails until UIDNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initial = false;
|
||||||
// TODO: for now
|
// TODO: for now
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -128,6 +159,13 @@ IMAPConnectionWorker::_Worker()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPConnectionWorker::_Wait()
|
||||||
|
{
|
||||||
|
while (acquire_sem(fOwner.FolderChangeSemaphore()) == B_INTERRUPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
/*static*/ status_t
|
||||||
IMAPConnectionWorker::_Worker(void* _self)
|
IMAPConnectionWorker::_Worker(void* _self)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "Protocol.h"
|
#include "Protocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
class IMAPFolder;
|
||||||
|
class IMAPMailbox;
|
||||||
class IMAPProtocol;
|
class IMAPProtocol;
|
||||||
class Settings;
|
class Settings;
|
||||||
|
|
||||||
@@ -23,25 +25,29 @@ public:
|
|||||||
bool main = false);
|
bool main = false);
|
||||||
virtual ~IMAPConnectionWorker();
|
virtual ~IMAPConnectionWorker();
|
||||||
|
|
||||||
|
bool HasMailboxes() const;
|
||||||
uint32 CountMailboxes() const;
|
uint32 CountMailboxes() const;
|
||||||
void AddMailbox(const BString& name);
|
void AddMailbox(IMAPFolder* folder);
|
||||||
void RemoveMailbox(const BString& name);
|
void RemoveAllMailboxes();
|
||||||
|
|
||||||
bool IsMain() const { return fMain; }
|
bool IsMain() const { return fMain; }
|
||||||
|
|
||||||
status_t Start();
|
status_t Run();
|
||||||
void Stop();
|
void Quit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _Worker();
|
status_t _Worker();
|
||||||
|
void _Wait();
|
||||||
static status_t _Worker(void* self);
|
static status_t _Worker(void* self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
typedef std::map<IMAPFolder*, IMAPMailbox*> MailboxMap;
|
||||||
|
|
||||||
IMAPProtocol& fOwner;
|
IMAPProtocol& fOwner;
|
||||||
const Settings& fSettings;
|
const Settings& fSettings;
|
||||||
IMAP::Protocol fProtocol;
|
IMAP::Protocol fProtocol;
|
||||||
BString fIdleBox;
|
IMAPFolder* fIdleBox;
|
||||||
StringList fOtherBoxes;
|
MailboxMap fMailboxes;
|
||||||
|
|
||||||
BLocker fLocker;
|
BLocker fLocker;
|
||||||
thread_id fThread;
|
thread_id fThread;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012, Axel Dörfler, [email protected].
|
* Copyright 2012-2013, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -21,10 +21,11 @@ static const char* kStateAttribute = "IMAP:state";
|
|||||||
static const char* kUIDAttribute = "MAIL:unique_id";
|
static const char* kUIDAttribute = "MAIL:unique_id";
|
||||||
|
|
||||||
|
|
||||||
IMAPFolder::IMAPFolder(const entry_ref& ref, FolderListener& listener)
|
IMAPFolder::IMAPFolder(const BString& mailboxName, const entry_ref& ref)
|
||||||
:
|
:
|
||||||
fRef(ref),
|
fRef(ref),
|
||||||
fListener(listener)
|
fMailboxName(mailboxName),
|
||||||
|
fUIDValidity(UINT32_MAX)
|
||||||
{
|
{
|
||||||
// Initialize from folder attributes
|
// Initialize from folder attributes
|
||||||
BNode node(&ref);
|
BNode node(&ref);
|
||||||
@@ -81,8 +82,16 @@ IMAPFolder::~IMAPFolder()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IMAPFolder::SetFolderID(const char* mailboxName, uint32 id)
|
IMAPFolder::SetListener(FolderListener* listener)
|
||||||
{
|
{
|
||||||
|
fListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPFolder::SetUIDValidity(uint32 uidValidity)
|
||||||
|
{
|
||||||
|
fUIDValidity = uidValidity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -132,6 +141,7 @@ IMAPFolder::_InitializeFolderState()
|
|||||||
uint32 uid = _ReadUniqueID(node);
|
uint32 uid = _ReadUniqueID(node);
|
||||||
uint32 flags = _ReadFlags(node);
|
uint32 flags = _ReadFlags(node);
|
||||||
|
|
||||||
|
// TODO: make sure a listener exists at this point!
|
||||||
std::set<uint32>::iterator found = lastUIDs.find(uid);
|
std::set<uint32>::iterator found = lastUIDs.find(uid);
|
||||||
if (found != lastUIDs.end()) {
|
if (found != lastUIDs.end()) {
|
||||||
// The message is still around
|
// The message is still around
|
||||||
@@ -141,14 +151,14 @@ IMAPFolder::_InitializeFolderState()
|
|||||||
ASSERT(flagsFound != fUIDMap.end());
|
ASSERT(flagsFound != fUIDMap.end());
|
||||||
if (flagsFound->second != flags) {
|
if (flagsFound->second != flags) {
|
||||||
// Its flags have changed locally, and need to be updated
|
// Its flags have changed locally, and need to be updated
|
||||||
fListener.MessageFlagsChanged(_Token(uid), ref,
|
fListener->MessageFlagsChanged(_Token(uid), ref,
|
||||||
flagsFound->second, flags);
|
flagsFound->second, flags);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a new message
|
// This is a new message
|
||||||
// TODO: the token must be the originating token!
|
// TODO: the token must be the originating token!
|
||||||
// TODO: uid might be udpated from the call
|
// TODO: uid might be udpated from the call
|
||||||
uid = fListener.MessageAdded(_Token(uid), ref);
|
uid = fListener->MessageAdded(_Token(uid), ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
fRefMap.insert(std::make_pair(uid, ref));
|
fRefMap.insert(std::make_pair(uid, ref));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#define IMAP_FOLDER_H
|
#define IMAP_FOLDER_H
|
||||||
|
|
||||||
|
|
||||||
#include <map>
|
#include <hash_map>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <Entry.h>
|
#include <Entry.h>
|
||||||
@@ -36,11 +36,14 @@ public:
|
|||||||
|
|
||||||
class IMAPFolder : public BHandler {
|
class IMAPFolder : public BHandler {
|
||||||
public:
|
public:
|
||||||
IMAPFolder(const entry_ref& ref,
|
IMAPFolder(const BString& mailboxName,
|
||||||
FolderListener& listener);
|
const entry_ref& ref);
|
||||||
virtual ~IMAPFolder();
|
virtual ~IMAPFolder();
|
||||||
|
|
||||||
void SetFolderID(const char* mailboxName, uint32 id);
|
const BString& MailboxName() const { return fMailboxName; }
|
||||||
|
|
||||||
|
void SetListener(FolderListener* listener);
|
||||||
|
void SetUIDValidity(uint32 uidValidity);
|
||||||
|
|
||||||
void StoreMessage(uint32 uid, ...);
|
void StoreMessage(uint32 uid, ...);
|
||||||
void DeleteMessage(uint32 uid);
|
void DeleteMessage(uint32 uid);
|
||||||
@@ -55,13 +58,13 @@ private:
|
|||||||
uint32 _ReadFlags(BNode& node);
|
uint32 _ReadFlags(BNode& node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<uint32, uint32> UIDToFlagsMap;
|
typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
|
||||||
typedef std::map<uint32, entry_ref> UIDToRefMap;
|
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
|
||||||
|
|
||||||
const entry_ref fRef;
|
const entry_ref fRef;
|
||||||
BString fMailboxName;
|
BString fMailboxName;
|
||||||
uint32 fUIDValidity;
|
uint32 fUIDValidity;
|
||||||
FolderListener& fListener;
|
FolderListener* fListener;
|
||||||
UIDToRefMap fRefMap;
|
UIDToRefMap fRefMap;
|
||||||
UIDToFlagsMap fUIDMap;
|
UIDToFlagsMap fUIDMap;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*! This class offers a high level look at an IMAP mailbox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "IMAPMailbox.h"
|
||||||
|
|
||||||
|
|
||||||
|
IMAPMailbox::IMAPMailbox(IMAP::Protocol& protocol, const BString& mailboxName)
|
||||||
|
:
|
||||||
|
fProtocol(protocol),
|
||||||
|
fMailboxName(mailboxName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMAPMailbox::~IMAPMailbox()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32
|
||||||
|
IMAPMailbox::MessageAdded(const MessageToken& fromToken, const entry_ref& ref)
|
||||||
|
{
|
||||||
|
printf("IMAP: message added %s, uid %lu\n", ref.name, fromToken.uid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPMailbox::MessageDeleted(const MessageToken& token)
|
||||||
|
{
|
||||||
|
printf("IMAP: message deleted, uid %lu\n", token.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPMailbox::MessageFlagsChanged(const MessageToken& token,
|
||||||
|
const entry_ref& ref, uint32 oldFlags, uint32 newFlags)
|
||||||
|
{
|
||||||
|
printf("IMAP: flags changed %s, uid %lu, from %lx to %lx\n", ref.name,
|
||||||
|
token.uid, oldFlags, newFlags);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef IMAP_MAILBOX_H
|
||||||
|
#define IMAP_MAILBOX_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "IMAPFolder.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace IMAP {
|
||||||
|
class Protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class IMAPMailbox : public FolderListener {
|
||||||
|
public:
|
||||||
|
IMAPMailbox(IMAP::Protocol& protocol,
|
||||||
|
const BString& mailboxName);
|
||||||
|
virtual ~IMAPMailbox();
|
||||||
|
|
||||||
|
const BString& MailboxName() const { return fMailboxName; }
|
||||||
|
|
||||||
|
// FolderListener interface
|
||||||
|
virtual uint32 MessageAdded(const MessageToken& fromToken,
|
||||||
|
const entry_ref& ref);
|
||||||
|
virtual void MessageDeleted(const MessageToken& token);
|
||||||
|
|
||||||
|
virtual void MessageFlagsChanged(const MessageToken& token,
|
||||||
|
const entry_ref& ref, uint32 oldFlags,
|
||||||
|
uint32 newFlags);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IMAP::Protocol& fProtocol;
|
||||||
|
BString fMailboxName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // IMAP_MAILBOX_H
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
#include <Directory.h>
|
#include <Directory.h>
|
||||||
|
|
||||||
#include "IMAPConnectionWorker.h"
|
#include "IMAPConnectionWorker.h"
|
||||||
|
#include "IMAPFolder.h"
|
||||||
|
#include "Utilities.h"
|
||||||
|
|
||||||
|
|
||||||
IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
|
IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
|
||||||
@@ -24,6 +26,10 @@ IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
|
|||||||
destination.Path(), strerror(status));
|
destination.Path(), strerror(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status = _CreateFolderChangeSemaphore();
|
||||||
|
if (status != B_OK)
|
||||||
|
fprintf(stderr, "imap: Failed to create sem: %s\n", strerror(status));
|
||||||
|
|
||||||
PostMessage(B_READY_TO_RUN);
|
PostMessage(B_READY_TO_RUN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,51 +44,73 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
|
|||||||
{
|
{
|
||||||
// Get list of subscribed folders
|
// Get list of subscribed folders
|
||||||
|
|
||||||
StringList folders;
|
StringList newFolders;
|
||||||
status_t status = protocol.GetSubscribedFolders(folders);
|
BString separator;
|
||||||
|
status_t status = protocol.GetSubscribedFolders(newFolders, separator);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// Determine how many new mailboxes we have
|
// Determine how many new mailboxes we have
|
||||||
|
|
||||||
StringList::iterator iterator = folders.begin();
|
StringList::iterator folderIterator = newFolders.begin();
|
||||||
for (; iterator != folders.end(); iterator++) {
|
while (folderIterator != newFolders.end()) {
|
||||||
if (fKnownMailboxes.find(*iterator) != fKnownMailboxes.end())
|
if (fFolders.find(*folderIterator) != fFolders.end())
|
||||||
iterator = folders.erase(iterator);
|
folderIterator = newFolders.erase(folderIterator);
|
||||||
|
else
|
||||||
|
folderIterator++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fSettings.IdleMode()) {
|
int32 totalMailboxes = fFolders.size() + newFolders.size();
|
||||||
// Create connection workers as allowed
|
int32 workersWanted = 1;
|
||||||
|
if (fSettings.IdleMode())
|
||||||
|
workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
|
||||||
|
|
||||||
int32 totalMailboxes = fKnownMailboxes.size() + folders.size();
|
if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
|
||||||
|
// Nothing to do - we've already distributed everything
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
while (fWorkers.CountItems() < fSettings.MaxConnections()
|
// Remove mailboxes from workers
|
||||||
&& fWorkers.CountItems() < totalMailboxes) {
|
for (int32 i = 0; i < fWorkers.CountItems(); i++) {
|
||||||
IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
|
fWorkers.ItemAt(i)->RemoveAllMailboxes();
|
||||||
fSettings);
|
}
|
||||||
if (!fWorkers.AddItem(worker)) {
|
|
||||||
delete worker;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
worker->Start();
|
// Create/remove connection workers as allowed and needed
|
||||||
|
while (fWorkers.CountItems() < workersWanted) {
|
||||||
|
IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
|
||||||
|
fSettings);
|
||||||
|
if (!fWorkers.AddItem(worker)) {
|
||||||
|
delete worker;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
worker->Run();
|
||||||
|
}
|
||||||
|
while (fWorkers.CountItems() > workersWanted) {
|
||||||
|
IMAPConnectionWorker* worker
|
||||||
|
= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
|
||||||
|
worker->Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distribute the new mailboxes to the existing workers
|
// Update known mailboxes
|
||||||
|
folderIterator = newFolders.begin();
|
||||||
|
for (; folderIterator != newFolders.end(); folderIterator++) {
|
||||||
|
const BString& mailbox = *folderIterator;
|
||||||
|
fFolders.insert(std::make_pair(mailbox,
|
||||||
|
_CreateFolder(mailbox, separator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distribute the mailboxes evenly to the workers
|
||||||
|
FolderMap::iterator iterator = fFolders.begin();
|
||||||
int32 index = 0;
|
int32 index = 0;
|
||||||
while (!folders.empty()) {
|
for (; iterator != fFolders.end(); iterator++) {
|
||||||
BString folder = folders[0];
|
fWorkers.ItemAt(index)->AddMailbox(iterator->second);
|
||||||
folders.erase(folders.begin());
|
|
||||||
|
|
||||||
fWorkers.ItemAt(index)->AddMailbox(folder);
|
|
||||||
fKnownMailboxes.insert(folder);
|
|
||||||
|
|
||||||
index = (index + 1) % fWorkers.CountItems();
|
index = (index + 1) % fWorkers.CountItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
// Restart waiting workers
|
||||||
|
delete_sem(fFolderChangeSemaphore);
|
||||||
|
return _CreateFolderChangeSemaphore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -100,7 +128,7 @@ IMAPProtocol::SyncMessages()
|
|||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return worker->Start();
|
return worker->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -163,6 +191,44 @@ IMAPProtocol::ReadyToRun()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMAPFolder*
|
||||||
|
IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
|
||||||
|
{
|
||||||
|
BString name = MailboxToFolderName(mailbox, separator);
|
||||||
|
|
||||||
|
BPath path(fSettings.Destination());
|
||||||
|
if (path.Append(name.String()) != B_OK) {
|
||||||
|
fprintf(stderr, "Could not append path: %s\n", name.String());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = create_directory(path.Path(), 0755);
|
||||||
|
if (status != B_OK) {
|
||||||
|
fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
|
||||||
|
strerror(status));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_ref ref;
|
||||||
|
status = get_ref_for_path(path.Path(), &ref);
|
||||||
|
if (status != B_OK) {
|
||||||
|
fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
|
||||||
|
strerror(status));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IMAPFolder(mailbox, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPProtocol::_CreateFolderChangeSemaphore()
|
||||||
|
{
|
||||||
|
fFolderChangeSemaphore = create_sem(0, "imap folder change");
|
||||||
|
return fFolderChangeSemaphore < 0 ? fFolderChangeSemaphore : B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#define IMAP_PROTOCOL_H
|
#define IMAP_PROTOCOL_H
|
||||||
|
|
||||||
|
|
||||||
#include <set>
|
#include <map>
|
||||||
|
|
||||||
#include <MailProtocol.h>
|
#include <MailProtocol.h>
|
||||||
#include <ObjectList.h>
|
#include <ObjectList.h>
|
||||||
@@ -15,12 +15,13 @@
|
|||||||
|
|
||||||
|
|
||||||
class IMAPConnectionWorker;
|
class IMAPConnectionWorker;
|
||||||
|
class IMAPFolder;
|
||||||
namespace IMAP {
|
namespace IMAP {
|
||||||
class Protocol;
|
class Protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef std::set<BString> StringSet;
|
typedef std::map<BString, IMAPFolder*> FolderMap;
|
||||||
|
|
||||||
|
|
||||||
class IMAPProtocol : public BInboundMailProtocol {
|
class IMAPProtocol : public BInboundMailProtocol {
|
||||||
@@ -31,6 +32,8 @@ public:
|
|||||||
|
|
||||||
status_t CheckSubscribedFolders(
|
status_t CheckSubscribedFolders(
|
||||||
IMAP::Protocol& protocol);
|
IMAP::Protocol& protocol);
|
||||||
|
sem_id FolderChangeSemaphore() const
|
||||||
|
{ return fFolderChangeSemaphore; }
|
||||||
|
|
||||||
virtual status_t SyncMessages();
|
virtual status_t SyncMessages();
|
||||||
virtual status_t FetchBody(const entry_ref& ref);
|
virtual status_t FetchBody(const entry_ref& ref);
|
||||||
@@ -44,10 +47,16 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void ReadyToRun();
|
void ReadyToRun();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMAPFolder* _CreateFolder(const BString& mailbox,
|
||||||
|
const BString& separator);
|
||||||
|
status_t _CreateFolderChangeSemaphore();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Settings fSettings;
|
Settings fSettings;
|
||||||
BObjectList<IMAPConnectionWorker> fWorkers;
|
BObjectList<IMAPConnectionWorker> fWorkers;
|
||||||
StringSet fKnownMailboxes;
|
FolderMap fFolders;
|
||||||
|
sem_id fFolderChangeSemaphore;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ UseLibraryHeaders linprog alm ;
|
|||||||
SubDirHdrs [ FDirName $(HAIKU_TOP) headers os add-ons mail_daemon ] ;
|
SubDirHdrs [ FDirName $(HAIKU_TOP) headers os add-ons mail_daemon ] ;
|
||||||
|
|
||||||
local sources =
|
local sources =
|
||||||
# IMAPInboundProtocol.cpp
|
|
||||||
# IMAPRootInboundProtocol.cpp
|
|
||||||
IMAPProtocol.cpp
|
IMAPProtocol.cpp
|
||||||
ConfigView.cpp
|
ConfigView.cpp
|
||||||
FolderConfigWindow.cpp
|
FolderConfigWindow.cpp
|
||||||
IMAPFolder.cpp
|
IMAPFolder.cpp
|
||||||
IMAPConnectionWorker.cpp
|
IMAPConnectionWorker.cpp
|
||||||
|
IMAPMailbox.cpp
|
||||||
Settings.cpp
|
Settings.cpp
|
||||||
|
Utilities.cpp
|
||||||
|
|
||||||
# imap_lib
|
# imap_lib
|
||||||
Commands.cpp
|
Commands.cpp
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Utilities.h"
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
MailboxToFolderName(const BString& mailbox, const BString& separator)
|
||||||
|
{
|
||||||
|
if (separator == "/")
|
||||||
|
return mailbox;
|
||||||
|
|
||||||
|
BString name = mailbox;
|
||||||
|
name.ReplaceAll('/', '_');
|
||||||
|
name.ReplaceAll(separator.String(), "/");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef UTILITIES_H
|
||||||
|
#define UTILITIES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
|
BString MailboxToFolderName(const BString& mailbox, const BString& separator);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // UTILITIES_H
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
* Copyright 2011-2013, Haiku, Inc. All rights reserved.
|
||||||
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
|
* Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -202,10 +202,10 @@ SelectCommand::SelectCommand()
|
|||||||
|
|
||||||
SelectCommand::SelectCommand(const char* name)
|
SelectCommand::SelectCommand(const char* name)
|
||||||
:
|
:
|
||||||
fMailboxName(name),
|
|
||||||
fNextUID(0),
|
fNextUID(0),
|
||||||
fUIDValidity(0)
|
fUIDValidity(0)
|
||||||
{
|
{
|
||||||
|
SetTo(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +216,6 @@ SelectCommand::CommandString()
|
|||||||
return "";
|
return "";
|
||||||
|
|
||||||
BString command = "SELECT \"";
|
BString command = "SELECT \"";
|
||||||
// TODO: properly quote the string!
|
|
||||||
command += fMailboxName;
|
command += fMailboxName;
|
||||||
command += "\"";
|
command += "\"";
|
||||||
return command;
|
return command;
|
||||||
@@ -243,6 +242,14 @@ SelectCommand::HandleUntagged(Response& response)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SelectCommand::SetTo(const char* mailboxName)
|
||||||
|
{
|
||||||
|
RFC3501Encoding encoding;
|
||||||
|
fMailboxName = encoding.Encode(mailboxName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -654,10 +661,20 @@ ListCommand::CommandString()
|
|||||||
bool
|
bool
|
||||||
ListCommand::HandleUntagged(Response& response)
|
ListCommand::HandleUntagged(Response& response)
|
||||||
{
|
{
|
||||||
if (response.IsCommand(_Command()) && response.IsStringAt(3)) {
|
if (response.IsCommand(_Command()) && response.IsStringAt(2)
|
||||||
|
&& response.IsStringAt(3)) {
|
||||||
|
fSeparator = response.StringAt(2);
|
||||||
|
|
||||||
BString folder = response.StringAt(3);
|
BString folder = response.StringAt(3);
|
||||||
|
if (folder == "")
|
||||||
|
return true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fFolders.push_back(fEncoding.Decode(folder));
|
folder = fEncoding.Decode(folder);
|
||||||
|
// The folder INBOX is always case insensitive
|
||||||
|
if (folder.ICompare("INBOX") == 0)
|
||||||
|
folder = "Inbox";
|
||||||
|
fFolders.push_back(folder);
|
||||||
} catch (ParseException& exception) {
|
} catch (ParseException& exception) {
|
||||||
// Decoding failed, just add the plain text
|
// Decoding failed, just add the plain text
|
||||||
fprintf(stderr, "Decoding \"%s\" failed: %s\n", folder.String(),
|
fprintf(stderr, "Decoding \"%s\" failed: %s\n", folder.String(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2011, Haiku Inc. All Rights Reserved.
|
* Copyright 2010-2013, Haiku Inc. All Rights Reserved.
|
||||||
* Copyright 2010 Clemens Zeidler. All rights reserved.
|
* Copyright 2010 Clemens Zeidler. All rights reserved.
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
@@ -97,8 +97,7 @@ public:
|
|||||||
virtual BString CommandString();
|
virtual BString CommandString();
|
||||||
virtual bool HandleUntagged(Response& response);
|
virtual bool HandleUntagged(Response& response);
|
||||||
|
|
||||||
void SetTo(const char* mailboxName)
|
void SetTo(const char* mailboxName);
|
||||||
{ fMailboxName = mailboxName; }
|
|
||||||
uint32 NextUID() { return fNextUID; }
|
uint32 NextUID() { return fNextUID; }
|
||||||
uint32 UIDValidity() { return fUIDValidity; }
|
uint32 UIDValidity() { return fUIDValidity; }
|
||||||
|
|
||||||
@@ -249,10 +248,11 @@ public:
|
|||||||
ListCommand(const char* prefix,
|
ListCommand(const char* prefix,
|
||||||
bool subscribedOnly);
|
bool subscribedOnly);
|
||||||
|
|
||||||
BString CommandString();
|
virtual BString CommandString();
|
||||||
bool HandleUntagged(Response& response);
|
virtual bool HandleUntagged(Response& response);
|
||||||
|
|
||||||
const StringList& FolderList();
|
const StringList& FolderList();
|
||||||
|
const BString& Separator() { return fSeparator; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* _Command() const;
|
const char* _Command() const;
|
||||||
@@ -261,6 +261,7 @@ private:
|
|||||||
RFC3501Encoding fEncoding;
|
RFC3501Encoding fEncoding;
|
||||||
const char* fPrefix;
|
const char* fPrefix;
|
||||||
StringList fFolders;
|
StringList fFolders;
|
||||||
|
BString fSeparator;
|
||||||
bool fSubscribedOnly;
|
bool fSubscribedOnly;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ Protocol::IsConnected()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Protocol::GetFolders(FolderList& folders)
|
Protocol::GetFolders(FolderList& folders, BString& separator)
|
||||||
{
|
{
|
||||||
StringList allFolders;
|
StringList allFolders;
|
||||||
status_t status = _GetAllFolders(allFolders);
|
status_t status = _GetAllFolders(allFolders);
|
||||||
@@ -118,7 +118,7 @@ Protocol::GetFolders(FolderList& folders)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
StringList subscribedFolders;
|
StringList subscribedFolders;
|
||||||
status = GetSubscribedFolders(subscribedFolders);
|
status = GetSubscribedFolders(subscribedFolders, separator);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ Protocol::GetFolders(FolderList& folders)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Protocol::GetSubscribedFolders(StringList& folders)
|
Protocol::GetSubscribedFolders(StringList& folders, BString& separator)
|
||||||
{
|
{
|
||||||
ListCommand command(NULL, true);
|
ListCommand command(NULL, true);
|
||||||
status_t status = ProcessCommand(command);
|
status_t status = ProcessCommand(command);
|
||||||
@@ -166,6 +166,7 @@ Protocol::GetSubscribedFolders(StringList& folders)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
folders = command.FolderList();
|
folders = command.FolderList();
|
||||||
|
separator = command.Separator();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,10 @@ public:
|
|||||||
ssize_t SendData(const char* buffer, uint32 length);
|
ssize_t SendData(const char* buffer, uint32 length);
|
||||||
|
|
||||||
// Some convenience methods
|
// Some convenience methods
|
||||||
status_t GetFolders(FolderList& folders);
|
status_t GetFolders(FolderList& folders,
|
||||||
status_t GetSubscribedFolders(StringList& folders);
|
BString& separator);
|
||||||
|
status_t GetSubscribedFolders(StringList& folders,
|
||||||
|
BString& separator);
|
||||||
status_t SubscribeFolder(const char* folder);
|
status_t SubscribeFolder(const char* folder);
|
||||||
status_t UnsubscribeFolder(const char* folder);
|
status_t UnsubscribeFolder(const char* folder);
|
||||||
status_t GetQuota(uint64& used, uint64& total);
|
status_t GetQuota(uint64& used, uint64& total);
|
||||||
|
|||||||
Reference in New Issue
Block a user