imap: Fleshed out command processing.
* Still doesn't do anything useful, though.
This commit is contained in:
@@ -13,20 +13,103 @@
|
|||||||
#include "IMAPProtocol.h"
|
#include "IMAPProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerPrivate {
|
||||||
|
public:
|
||||||
|
WorkerPrivate(IMAPConnectionWorker& worker)
|
||||||
|
:
|
||||||
|
fWorker(worker)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAP::Protocol& Protocol()
|
||||||
|
{
|
||||||
|
return fWorker.fProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quit()
|
||||||
|
{
|
||||||
|
fWorker.fStopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMAPConnectionWorker& fWorker;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerCommand {
|
||||||
|
public:
|
||||||
|
WorkerCommand() {}
|
||||||
|
virtual ~WorkerCommand() {}
|
||||||
|
|
||||||
|
virtual status_t Process(IMAPConnectionWorker& worker) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QuitCommand : public WorkerCommand {
|
||||||
|
public:
|
||||||
|
QuitCommand()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual status_t Process(IMAPConnectionWorker& worker)
|
||||||
|
{
|
||||||
|
WorkerPrivate(worker).Quit();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CheckMailboxCommand : public WorkerCommand {
|
||||||
|
public:
|
||||||
|
CheckMailboxCommand(IMAPFolder& folder, IMAPMailbox& mailbox)
|
||||||
|
:
|
||||||
|
fFolder(folder),
|
||||||
|
fMailbox(mailbox)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual status_t Process(IMAPConnectionWorker& worker)
|
||||||
|
{
|
||||||
|
IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol();
|
||||||
|
|
||||||
|
IMAP::SelectCommand select(fFolder.MailboxName().String());
|
||||||
|
status_t status = protocol.ProcessCommand(select);
|
||||||
|
if (status == B_OK) {
|
||||||
|
fFolder.SetUIDValidity(select.UIDValidity());
|
||||||
|
// TODO: trigger download of mails until UIDNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMAPFolder& fFolder;
|
||||||
|
IMAPMailbox& fMailbox;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner,
|
IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner,
|
||||||
const Settings& settings, bool main)
|
const Settings& settings, bool main)
|
||||||
:
|
:
|
||||||
fOwner(owner),
|
fOwner(owner),
|
||||||
fSettings(settings),
|
fSettings(settings),
|
||||||
|
fPendingCommandsSemaphore(-1),
|
||||||
fIdleBox(NULL),
|
fIdleBox(NULL),
|
||||||
fMain(main),
|
fMain(main),
|
||||||
fStopped(false)
|
fStopped(false)
|
||||||
{
|
{
|
||||||
|
fExistsHandler.SetListener(this);
|
||||||
|
fProtocol.AddHandler(fExistsHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
IMAPConnectionWorker::~IMAPConnectionWorker()
|
IMAPConnectionWorker::~IMAPConnectionWorker()
|
||||||
{
|
{
|
||||||
|
delete_sem(fPendingCommandsSemaphore);
|
||||||
|
_Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -79,6 +162,10 @@ IMAPConnectionWorker::RemoveAllMailboxes()
|
|||||||
status_t
|
status_t
|
||||||
IMAPConnectionWorker::Run()
|
IMAPConnectionWorker::Run()
|
||||||
{
|
{
|
||||||
|
fPendingCommandsSemaphore = create_sem(0, "imap pending commands");
|
||||||
|
if (fPendingCommandsSemaphore < 0)
|
||||||
|
return fPendingCommandsSemaphore;
|
||||||
|
|
||||||
fThread = spawn_thread(&_Worker, "imap connection worker",
|
fThread = spawn_thread(&_Worker, "imap connection worker",
|
||||||
B_NORMAL_PRIORITY, this);
|
B_NORMAL_PRIORITY, this);
|
||||||
if (fThread < 0)
|
if (fThread < 0)
|
||||||
@@ -92,77 +179,136 @@ IMAPConnectionWorker::Run()
|
|||||||
void
|
void
|
||||||
IMAPConnectionWorker::Quit()
|
IMAPConnectionWorker::Quit()
|
||||||
{
|
{
|
||||||
// TODO: we'll also need to interrupt listening to the socket
|
_EnqueueCommand(new QuitCommand());
|
||||||
fStopped = true;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPConnectionWorker::EnqueueCheckMailboxes()
|
||||||
|
{
|
||||||
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
|
MailboxMap::iterator iterator = fMailboxes.begin();
|
||||||
|
for (; iterator != fMailboxes.end(); iterator++) {
|
||||||
|
IMAPFolder* folder = iterator->first;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = _EnqueueCommand(
|
||||||
|
new CheckMailboxCommand(*folder, *mailbox));
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPConnectionWorker::EnqueueRetrieveMail(entry_ref& ref)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPConnectionWorker::MessageExistsReceived(uint32 index)
|
||||||
|
{
|
||||||
|
printf("Message exists: %ld\n", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IMAPConnectionWorker::_Worker()
|
IMAPConnectionWorker::_Worker()
|
||||||
{
|
{
|
||||||
status_t status = fProtocol.Connect(fSettings.ServerAddress(),
|
|
||||||
fSettings.Username(), fSettings.Password(), fSettings.UseSSL());
|
|
||||||
if (status != B_OK)
|
|
||||||
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
|
||||||
// other workers as needed
|
// other workers as needed
|
||||||
fOwner.CheckSubscribedFolders(fProtocol);
|
status_t status = _Connect();
|
||||||
|
if (status == B_OK)
|
||||||
|
status = fOwner.CheckSubscribedFolders(fProtocol, fIdle);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAutolock locker(fLocker);
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
if (!HasMailboxes()) {
|
if (fPendingCommands.IsEmpty()) {
|
||||||
|
_Disconnect();
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
_Wait();
|
|
||||||
|
_WaitForCommands();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initial && idle && fIdleBox != NULL) {
|
WorkerCommand* command = fPendingCommands.RemoveItemAt(0);
|
||||||
printf("%p: IDLE: %s\n", this, fIdleBox->MailboxName().String());
|
if (command == NULL)
|
||||||
// TODO: enter IDLE mode
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
MailboxMap::iterator iterator = fMailboxes.begin();
|
status_t status = _Connect();
|
||||||
for (; iterator != fMailboxes.end(); iterator++) {
|
if (status != B_OK)
|
||||||
IMAPFolder* folder = iterator->first;
|
return status;
|
||||||
if (!initial && idle && folder == fIdleBox)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
printf("%p: check: %s\n", this, folder->MailboxName().String());
|
status = command->Process(*this);
|
||||||
IMAPMailbox* mailbox = iterator->second;
|
if (status != B_OK)
|
||||||
if (mailbox == NULL) {
|
return status;
|
||||||
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
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
/*! Enqueues the given command to the worker queue. This method will take
|
||||||
IMAPConnectionWorker::_Wait()
|
over ownership of the given command even in the error case.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
IMAPConnectionWorker::_EnqueueCommand(WorkerCommand* command)
|
||||||
{
|
{
|
||||||
while (acquire_sem(fOwner.FolderChangeSemaphore()) == B_INTERRUPTED);
|
BAutolock locker(fLocker);
|
||||||
|
|
||||||
|
if (!fPendingCommands.AddItem(command)) {
|
||||||
|
delete command;
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
locker.Unlock();
|
||||||
|
release_sem(fPendingCommandsSemaphore);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPConnectionWorker::_WaitForCommands()
|
||||||
|
{
|
||||||
|
while (acquire_sem(fPendingCommandsSemaphore) == B_INTERRUPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPConnectionWorker::_Connect()
|
||||||
|
{
|
||||||
|
if (fProtocol.IsConnected())
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
status_t status = fProtocol.Connect(fSettings.ServerAddress(),
|
||||||
|
fSettings.Username(), fSettings.Password(), fSettings.UseSSL());
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
fIdle = fSettings.IdleMode() && fProtocol.Capabilities().Contains("IDLE");
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPConnectionWorker::_Disconnect()
|
||||||
|
{
|
||||||
|
fProtocol.Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <Locker.h>
|
#include <Locker.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
//#include "Commands.h"
|
||||||
#include "Protocol.h"
|
#include "Protocol.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,9 +17,14 @@ class IMAPFolder;
|
|||||||
class IMAPMailbox;
|
class IMAPMailbox;
|
||||||
class IMAPProtocol;
|
class IMAPProtocol;
|
||||||
class Settings;
|
class Settings;
|
||||||
|
class WorkerCommand;
|
||||||
|
class WorkerPrivate;
|
||||||
|
|
||||||
|
|
||||||
class IMAPConnectionWorker {
|
typedef BObjectList<WorkerCommand> WorkerCommandList;
|
||||||
|
|
||||||
|
|
||||||
|
class IMAPConnectionWorker : public IMAP::ExistsListener {
|
||||||
public:
|
public:
|
||||||
IMAPConnectionWorker(IMAPProtocol& owner,
|
IMAPConnectionWorker(IMAPProtocol& owner,
|
||||||
const Settings& settings,
|
const Settings& settings,
|
||||||
@@ -35,17 +41,32 @@ public:
|
|||||||
status_t Run();
|
status_t Run();
|
||||||
void Quit();
|
void Quit();
|
||||||
|
|
||||||
|
status_t EnqueueCheckMailboxes();
|
||||||
|
status_t EnqueueRetrieveMail(entry_ref& ref);
|
||||||
|
|
||||||
|
// Handler listener
|
||||||
|
virtual void MessageExistsReceived(uint32 index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _Worker();
|
status_t _Worker();
|
||||||
void _Wait();
|
status_t _EnqueueCommand(WorkerCommand* command);
|
||||||
|
void _WaitForCommands();
|
||||||
|
status_t _Connect();
|
||||||
|
void _Disconnect();
|
||||||
static status_t _Worker(void* self);
|
static status_t _Worker(void* self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<IMAPFolder*, IMAPMailbox*> MailboxMap;
|
typedef std::map<IMAPFolder*, IMAPMailbox*> MailboxMap;
|
||||||
|
friend class WorkerPrivate;
|
||||||
|
|
||||||
IMAPProtocol& fOwner;
|
IMAPProtocol& fOwner;
|
||||||
const Settings& fSettings;
|
const Settings& fSettings;
|
||||||
IMAP::Protocol fProtocol;
|
IMAP::Protocol fProtocol;
|
||||||
|
sem_id fPendingCommandsSemaphore;
|
||||||
|
WorkerCommandList fPendingCommands;
|
||||||
|
|
||||||
|
IMAP::ExistsHandler fExistsHandler;
|
||||||
|
|
||||||
IMAPFolder* fIdleBox;
|
IMAPFolder* fIdleBox;
|
||||||
MailboxMap fMailboxes;
|
MailboxMap fMailboxes;
|
||||||
|
|
||||||
@@ -53,6 +74,7 @@ private:
|
|||||||
thread_id fThread;
|
thread_id fThread;
|
||||||
bool fMain;
|
bool fMain;
|
||||||
bool fStopped;
|
bool fStopped;
|
||||||
|
bool fIdle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
|
IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
|
||||||
:
|
:
|
||||||
BInboundMailProtocol(settings),
|
BInboundMailProtocol(settings),
|
||||||
fSettings(settings.InboundSettings())
|
fSettings(settings.InboundSettings()),
|
||||||
|
fWorkers(5, false)
|
||||||
{
|
{
|
||||||
BPath destination = fSettings.Destination();
|
BPath destination = fSettings.Destination();
|
||||||
|
|
||||||
@@ -26,10 +27,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +37,7 @@ IMAPProtocol::~IMAPProtocol()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
|
IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
|
||||||
{
|
{
|
||||||
// Get list of subscribed folders
|
// Get list of subscribed folders
|
||||||
|
|
||||||
@@ -62,7 +59,7 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
|
|||||||
|
|
||||||
int32 totalMailboxes = fFolders.size() + newFolders.size();
|
int32 totalMailboxes = fFolders.size() + newFolders.size();
|
||||||
int32 workersWanted = 1;
|
int32 workersWanted = 1;
|
||||||
if (fSettings.IdleMode())
|
if (idle)
|
||||||
workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
|
workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
|
||||||
|
|
||||||
if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
|
if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
|
||||||
@@ -84,8 +81,13 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
worker->Run();
|
status = worker->Run();
|
||||||
|
if (status != B_OK) {
|
||||||
|
fWorkers.RemoveItem(worker);
|
||||||
|
delete worker;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fWorkers.CountItems() > workersWanted) {
|
while (fWorkers.CountItems() > workersWanted) {
|
||||||
IMAPConnectionWorker* worker
|
IMAPConnectionWorker* worker
|
||||||
= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
|
= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
|
||||||
@@ -108,9 +110,12 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
|
|||||||
index = (index + 1) % fWorkers.CountItems();
|
index = (index + 1) % fWorkers.CountItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart waiting workers
|
// Start waiting workers
|
||||||
delete_sem(fFolderChangeSemaphore);
|
for (int32 i = 0; i < fWorkers.CountItems(); i++) {
|
||||||
return _CreateFolderChangeSemaphore();
|
fWorkers.ItemAt(i)->EnqueueCheckMailboxes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -221,14 +226,6 @@ IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPProtocol::_CreateFolderChangeSemaphore()
|
|
||||||
{
|
|
||||||
fFolderChangeSemaphore = create_sem(0, "imap folder change");
|
|
||||||
return fFolderChangeSemaphore < 0 ? fFolderChangeSemaphore : B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ public:
|
|||||||
virtual ~IMAPProtocol();
|
virtual ~IMAPProtocol();
|
||||||
|
|
||||||
status_t CheckSubscribedFolders(
|
status_t CheckSubscribedFolders(
|
||||||
IMAP::Protocol& protocol);
|
IMAP::Protocol& protocol, bool idle);
|
||||||
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);
|
||||||
@@ -50,13 +48,11 @@ protected:
|
|||||||
private:
|
private:
|
||||||
IMAPFolder* _CreateFolder(const BString& mailbox,
|
IMAPFolder* _CreateFolder(const BString& mailbox,
|
||||||
const BString& separator);
|
const BString& separator);
|
||||||
status_t _CreateFolderChangeSemaphore();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Settings fSettings;
|
Settings fSettings;
|
||||||
BObjectList<IMAPConnectionWorker> fWorkers;
|
BObjectList<IMAPConnectionWorker> fWorkers;
|
||||||
FolderMap fFolders;
|
FolderMap fFolders;
|
||||||
sem_id fFolderChangeSemaphore;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -507,42 +507,23 @@ ExistsHandler::ExistsHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ExistsHandler::SetListener(ExistsListener* listener)
|
||||||
|
{
|
||||||
|
fListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ExistsHandler::HandleUntagged(Response& response)
|
ExistsHandler::HandleUntagged(Response& response)
|
||||||
{
|
{
|
||||||
if (!response.EqualsAt(1, "EXISTS") || response.IsNumberAt(0))
|
if (!response.EqualsAt(1, "EXISTS") || response.IsNumberAt(0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// int32 expunge = response.NumberAt(0);
|
int32 index = response.NumberAt(0);
|
||||||
#if 0
|
|
||||||
Listener().ExistsReceived(expunge);
|
|
||||||
|
|
||||||
if (response.FindFirst("EXISTS") < 0)
|
if (fListener != NULL)
|
||||||
return false;
|
fListener->MessageExistsReceived(index);
|
||||||
|
|
||||||
int32 exists = 0;
|
|
||||||
if (!IMAPParser::ExtractUntagedFromLeft(response, "EXISTS", exists))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int32 nMessages = fIMAPMailbox.GetCurrentMessageCount();
|
|
||||||
if (exists <= nMessages)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
MinMessageList& list = const_cast<MinMessageList&>(
|
|
||||||
fIMAPMailbox.GetMessageList());
|
|
||||||
IMAPCommand* command = new FetchMinMessageCommand(fIMAPMailbox,
|
|
||||||
nMessages + 1, exists, &list, NULL);
|
|
||||||
fIMAPMailbox.AddAfterQuakeCommand(command);
|
|
||||||
|
|
||||||
fIMAPMailbox.Listener().NewMessagesToFetch(exists - nMessages);
|
|
||||||
|
|
||||||
command = new FetchMessageCommand(fIMAPMailbox, nMessages + 1, exists,
|
|
||||||
fIMAPMailbox.FetchBodyLimit());
|
|
||||||
fIMAPMailbox.AddAfterQuakeCommand(command);
|
|
||||||
|
|
||||||
TRACE("EXISTS %i\n", (int)exists);
|
|
||||||
fIMAPMailbox.SendRawCommand("DONE");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,11 +205,23 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class ExistsListener {
|
||||||
|
public:
|
||||||
|
virtual void MessageExistsReceived(uint32 index) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class ExistsHandler : public Handler {
|
class ExistsHandler : public Handler {
|
||||||
public:
|
public:
|
||||||
ExistsHandler();
|
ExistsHandler();
|
||||||
|
|
||||||
bool HandleUntagged(Response& response);
|
void SetListener(ExistsListener* listener);
|
||||||
|
ExistsListener* Listener() const { return fListener; }
|
||||||
|
|
||||||
|
virtual bool HandleUntagged(Response& response);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExistsListener* fListener;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Protocol::Protocol()
|
|||||||
:
|
:
|
||||||
fSocket(NULL),
|
fSocket(NULL),
|
||||||
fBufferedSocket(NULL),
|
fBufferedSocket(NULL),
|
||||||
|
fHandlerList(5, false),
|
||||||
fCommandID(0),
|
fCommandID(0),
|
||||||
fIsConnected(false)
|
fIsConnected(false)
|
||||||
{
|
{
|
||||||
@@ -35,9 +36,6 @@ Protocol::Protocol()
|
|||||||
|
|
||||||
Protocol::~Protocol()
|
Protocol::~Protocol()
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < fAfterQuackCommands.CountItems(); i++)
|
|
||||||
delete fAfterQuackCommands.ItemAt(i);
|
|
||||||
|
|
||||||
delete fSocket;
|
delete fSocket;
|
||||||
delete fBufferedSocket;
|
delete fBufferedSocket;
|
||||||
}
|
}
|
||||||
@@ -109,6 +107,20 @@ Protocol::IsConnected()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Protocol::AddHandler(Handler& handler)
|
||||||
|
{
|
||||||
|
return fHandlerList.AddItem(&handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Protocol::RemoveHandler(Handler& handler)
|
||||||
|
{
|
||||||
|
fHandlerList.RemoveItem(&handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Protocol::GetFolders(FolderList& folders, BString& separator)
|
Protocol::GetFolders(FolderList& folders, BString& separator)
|
||||||
{
|
{
|
||||||
@@ -238,24 +250,34 @@ Protocol::SendData(const char* buffer, uint32 length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
Protocol::AddAfterQuakeCommand(Command* command)
|
|
||||||
{
|
|
||||||
return fAfterQuackCommands.AddItem(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Protocol::ProcessCommand(Command& command, bigtime_t timeout)
|
Protocol::ProcessCommand(Command& command, bigtime_t timeout)
|
||||||
{
|
{
|
||||||
status_t status = _ProcessCommandWithoutAfterQuake(command, timeout);
|
BString commandString = command.CommandString();
|
||||||
|
if (commandString.IsEmpty())
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
ProcessAfterQuacks(timeout);
|
Handler* handler = dynamic_cast<Handler*>(&command);
|
||||||
|
if (handler != NULL && !AddHandler(*handler))
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
int32 commandID = NextCommandID();
|
||||||
|
status_t status = SendCommand(commandID, commandString.String());
|
||||||
|
if (status == B_OK) {
|
||||||
|
fOngoingCommands[commandID] = &command;
|
||||||
|
status = HandleResponse(&command, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler != NULL)
|
||||||
|
RemoveHandler(*handler);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - protected
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Protocol::HandleResponse(Command* command, bigtime_t timeout,
|
Protocol::HandleResponse(Command* command, bigtime_t timeout,
|
||||||
bool disconnectOnTimeout)
|
bool disconnectOnTimeout)
|
||||||
@@ -282,7 +304,7 @@ Protocol::HandleResponse(Command* command, bigtime_t timeout,
|
|||||||
|
|
||||||
if (response.IsUntagged() || response.IsContinuation()) {
|
if (response.IsUntagged() || response.IsContinuation()) {
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
for (int i = 0; i < fHandlerList.CountItems(); i++) {
|
for (int32 i = fHandlerList.CountItems(); i-- > 0;) {
|
||||||
if (fHandlerList.ItemAt(i)->HandleUntagged(response)) {
|
if (fHandlerList.ItemAt(i)->HandleUntagged(response)) {
|
||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
@@ -314,17 +336,6 @@ Protocol::HandleResponse(Command* command, bigtime_t timeout,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Protocol::ProcessAfterQuacks(bigtime_t timeout)
|
|
||||||
{
|
|
||||||
while (fAfterQuackCommands.CountItems() != 0) {
|
|
||||||
Command* currentCommand = fAfterQuackCommands.RemoveItemAt(0);
|
|
||||||
_ProcessCommandWithoutAfterQuake(*currentCommand, timeout);
|
|
||||||
delete currentCommand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Protocol::NextCommandID()
|
Protocol::NextCommandID()
|
||||||
{
|
{
|
||||||
@@ -333,29 +344,7 @@ Protocol::NextCommandID()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
// #pragma mark - private
|
||||||
Protocol::_ProcessCommandWithoutAfterQuake(Command& command, bigtime_t timeout)
|
|
||||||
{
|
|
||||||
BString commandString = command.CommandString();
|
|
||||||
if (commandString.IsEmpty())
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
Handler* handler = dynamic_cast<Handler*>(&command);
|
|
||||||
if (handler != NULL && !fHandlerList.AddItem(handler, 0))
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
int32 commandID = NextCommandID();
|
|
||||||
status_t status = SendCommand(commandID, commandString.String());
|
|
||||||
if (status == B_OK) {
|
|
||||||
fOngoingCommands[commandID] = &command;
|
|
||||||
status = HandleResponse(&command, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler != NULL)
|
|
||||||
fHandlerList.RemoveItem(handler);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ class Command;
|
|||||||
class Handler;
|
class Handler;
|
||||||
|
|
||||||
|
|
||||||
typedef BObjectList<Command> CommandList;
|
|
||||||
typedef BObjectList<Handler> HandlerList;
|
typedef BObjectList<Handler> HandlerList;
|
||||||
typedef std::map<int32, Command*> CommandIDMap;
|
typedef std::map<int32, Command*> CommandIDMap;
|
||||||
|
|
||||||
@@ -61,9 +60,8 @@ public:
|
|||||||
status_t Disconnect();
|
status_t Disconnect();
|
||||||
bool IsConnected();
|
bool IsConnected();
|
||||||
|
|
||||||
status_t SendCommand(const char* command);
|
bool AddHandler(Handler& handler);
|
||||||
status_t SendCommand(int32 id, const char* command);
|
void RemoveHandler(Handler& handler);
|
||||||
ssize_t SendData(const char* buffer, uint32 length);
|
|
||||||
|
|
||||||
// Some convenience methods
|
// Some convenience methods
|
||||||
status_t GetFolders(FolderList& folders,
|
status_t GetFolders(FolderList& folders,
|
||||||
@@ -74,27 +72,25 @@ public:
|
|||||||
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);
|
||||||
|
|
||||||
|
status_t SendCommand(const char* command);
|
||||||
|
status_t SendCommand(int32 id, const char* command);
|
||||||
|
ssize_t SendData(const char* buffer, uint32 length);
|
||||||
|
|
||||||
status_t ProcessCommand(Command& command,
|
status_t ProcessCommand(Command& command,
|
||||||
bigtime_t timeout = kIMAP4ClientTimeout);
|
bigtime_t timeout = kIMAP4ClientTimeout);
|
||||||
|
|
||||||
ArgumentList& Capabilities() { return fCapabilities; }
|
ArgumentList& Capabilities() { return fCapabilities; }
|
||||||
const ArgumentList& Capabilities() const { return fCapabilities; }
|
const ArgumentList& Capabilities() const { return fCapabilities; }
|
||||||
|
|
||||||
status_t AddAfterQuakeCommand(Command* command);
|
|
||||||
|
|
||||||
const BString& CommandError() { return fCommandError; }
|
const BString& CommandError() { return fCommandError; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
status_t HandleResponse(Command* command,
|
status_t HandleResponse(Command* command,
|
||||||
bigtime_t timeout = kIMAP4ClientTimeout,
|
bigtime_t timeout = kIMAP4ClientTimeout,
|
||||||
bool disconnectOnTimeout = true);
|
bool disconnectOnTimeout = true);
|
||||||
void ProcessAfterQuacks(bigtime_t timeout);
|
|
||||||
int32 NextCommandID();
|
int32 NextCommandID();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*! Same as ProccessCommand but AfterShockCommands are not send. */
|
|
||||||
status_t _ProcessCommandWithoutAfterQuake(
|
|
||||||
Command& command, bigtime_t timeout);
|
|
||||||
status_t _Disconnect();
|
status_t _Disconnect();
|
||||||
status_t _GetAllFolders(StringList& folders);
|
status_t _GetAllFolders(StringList& folders);
|
||||||
void _ParseCapabilities(
|
void _ParseCapabilities(
|
||||||
@@ -105,7 +101,6 @@ protected:
|
|||||||
BBufferedDataIO* fBufferedSocket;
|
BBufferedDataIO* fBufferedSocket;
|
||||||
|
|
||||||
HandlerList fHandlerList;
|
HandlerList fHandlerList;
|
||||||
CommandList fAfterQuackCommands;
|
|
||||||
ArgumentList fCapabilities;
|
ArgumentList fCapabilities;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user