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:
Axel Dörfler
2015-01-06 15:25:10 +01:00
parent adbe8fc944
commit a4bdd26d61
16 changed files with 382 additions and 102 deletions
@@ -20,6 +20,7 @@
#include <StringForSize.h>
#include "Settings.h"
#include "Utilities.h"
#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
fFolderList.clear();
fProtocol.GetFolders(fFolderList);
BString separator;
fProtocol.GetFolders(fFolderList, separator);
for (size_t i = 0; i < fFolderList.size(); 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);
item->SetListView(fFolderListView);
}
@@ -8,6 +8,8 @@
#include <Autolock.h>
#include "IMAPFolder.h"
#include "IMAPMailbox.h"
#include "IMAPProtocol.h"
@@ -16,6 +18,7 @@ IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner,
:
fOwner(owner),
fSettings(settings),
fIdleBox(NULL),
fMain(main),
fStopped(false)
{
@@ -27,55 +30,54 @@ IMAPConnectionWorker::~IMAPConnectionWorker()
}
bool
IMAPConnectionWorker::HasMailboxes() const
{
BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker);
return !fMailboxes.empty();
}
uint32
IMAPConnectionWorker::CountMailboxes() const
{
BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker);
return (fIdleBox.IsEmpty() ? 0 : 1) + fOtherBoxes.size();
return fMailboxes.size();
}
void
IMAPConnectionWorker::AddMailbox(const BString& name)
IMAPConnectionWorker::AddMailbox(IMAPFolder* folder)
{
BAutolock locker(fLocker);
if (fSettings.IdleMode() && fIdleBox.IsEmpty()) {
fIdleBox = name;
} else if (fSettings.IdleMode() && name == "INBOX") {
// Prefer to have the INBOX in idle mode over other mail boxes
fOtherBoxes.push_back(fIdleBox);
fIdleBox = name;
} else
fOtherBoxes.push_back(name);
fMailboxes.insert(std::make_pair(folder, (IMAPMailbox*)NULL));
// Prefer to have the INBOX in idle mode over other mail boxes
if (fIdleBox == NULL || folder->MailboxName().ICompare("INBOX") == 0)
fIdleBox = folder;
}
void
IMAPConnectionWorker::RemoveMailbox(const BString& name)
IMAPConnectionWorker::RemoveAllMailboxes()
{
BAutolock locker(fLocker);
if (fSettings.IdleMode() && fIdleBox == name) {
if (!fOtherBoxes.empty()) {
fIdleBox = fOtherBoxes[0];
fOtherBoxes.erase(fOtherBoxes.begin());
} else
fIdleBox.SetTo(NULL);
} else {
StringList::iterator iterator = fOtherBoxes.begin();
for (; iterator != fOtherBoxes.end(); iterator++) {
if (*iterator == name) {
fOtherBoxes.erase(iterator);
break;
}
}
// Reset listeners, and delete the mailboxes
MailboxMap::iterator iterator = fMailboxes.begin();
for (; iterator != fMailboxes.end(); iterator++) {
iterator->first->SetListener(NULL);
delete iterator->second;
}
fIdleBox = NULL;
fMailboxes.clear();
}
status_t
IMAPConnectionWorker::Start()
IMAPConnectionWorker::Run()
{
fThread = spawn_thread(&_Worker, "imap connection worker",
B_NORMAL_PRIORITY, this);
@@ -88,7 +90,7 @@ IMAPConnectionWorker::Start()
void
IMAPConnectionWorker::Stop()
IMAPConnectionWorker::Quit()
{
// TODO: we'll also need to interrupt listening to the socket
fStopped = true;
@@ -103,6 +105,10 @@ IMAPConnectionWorker::_Worker()
if (status != B_OK)
return status;
bool idle = fSettings.IdleMode()
&& fProtocol.Capabilities().Contains("IDLE");
bool initial = true;
while (!fStopped) {
if (fMain) {
// The main worker checks the subscribed folders, and creates
@@ -112,14 +118,39 @@ IMAPConnectionWorker::_Worker()
BAutolock locker(fLocker);
if (!fIdleBox.IsEmpty())
printf("%p: IDLE: %s\n", this, fIdleBox.String());
StringList::iterator iterator = fOtherBoxes.begin();
for (; iterator != fOtherBoxes.end(); iterator++) {
printf("%p: check: %s\n", this, iterator->String());
if (!HasMailboxes()) {
locker.Unlock();
_Wait();
continue;
}
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
break;
}
@@ -128,6 +159,13 @@ IMAPConnectionWorker::_Worker()
}
void
IMAPConnectionWorker::_Wait()
{
while (acquire_sem(fOwner.FolderChangeSemaphore()) == B_INTERRUPTED);
}
/*static*/ status_t
IMAPConnectionWorker::_Worker(void* _self)
{
@@ -12,6 +12,8 @@
#include "Protocol.h"
class IMAPFolder;
class IMAPMailbox;
class IMAPProtocol;
class Settings;
@@ -23,25 +25,29 @@ public:
bool main = false);
virtual ~IMAPConnectionWorker();
bool HasMailboxes() const;
uint32 CountMailboxes() const;
void AddMailbox(const BString& name);
void RemoveMailbox(const BString& name);
void AddMailbox(IMAPFolder* folder);
void RemoveAllMailboxes();
bool IsMain() const { return fMain; }
status_t Start();
void Stop();
status_t Run();
void Quit();
private:
status_t _Worker();
void _Wait();
static status_t _Worker(void* self);
private:
typedef std::map<IMAPFolder*, IMAPMailbox*> MailboxMap;
IMAPProtocol& fOwner;
const Settings& fSettings;
IMAP::Protocol fProtocol;
BString fIdleBox;
StringList fOtherBoxes;
IMAPFolder* fIdleBox;
MailboxMap fMailboxes;
BLocker fLocker;
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.
*/
@@ -21,10 +21,11 @@ static const char* kStateAttribute = "IMAP:state";
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),
fListener(listener)
fMailboxName(mailboxName),
fUIDValidity(UINT32_MAX)
{
// Initialize from folder attributes
BNode node(&ref);
@@ -81,8 +82,16 @@ IMAPFolder::~IMAPFolder()
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 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
@@ -141,14 +151,14 @@ IMAPFolder::_InitializeFolderState()
ASSERT(flagsFound != fUIDMap.end());
if (flagsFound->second != flags) {
// Its flags have changed locally, and need to be updated
fListener.MessageFlagsChanged(_Token(uid), ref,
fListener->MessageFlagsChanged(_Token(uid), ref,
flagsFound->second, flags);
}
} else {
// This is a new message
// TODO: the token must be the originating token!
// 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));
@@ -6,7 +6,7 @@
#define IMAP_FOLDER_H
#include <map>
#include <hash_map>
#include <sys/stat.h>
#include <Entry.h>
@@ -36,11 +36,14 @@ public:
class IMAPFolder : public BHandler {
public:
IMAPFolder(const entry_ref& ref,
FolderListener& listener);
IMAPFolder(const BString& mailboxName,
const entry_ref& ref);
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 DeleteMessage(uint32 uid);
@@ -55,13 +58,13 @@ private:
uint32 _ReadFlags(BNode& node);
private:
typedef std::map<uint32, uint32> UIDToFlagsMap;
typedef std::map<uint32, entry_ref> UIDToRefMap;
typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
const entry_ref fRef;
BString fMailboxName;
uint32 fUIDValidity;
FolderListener& fListener;
FolderListener* fListener;
UIDToRefMap fRefMap;
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 "IMAPConnectionWorker.h"
#include "IMAPFolder.h"
#include "Utilities.h"
IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
@@ -24,6 +26,10 @@ IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
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);
}
@@ -38,51 +44,73 @@ IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
{
// Get list of subscribed folders
StringList folders;
status_t status = protocol.GetSubscribedFolders(folders);
StringList newFolders;
BString separator;
status_t status = protocol.GetSubscribedFolders(newFolders, separator);
if (status != B_OK)
return status;
// Determine how many new mailboxes we have
StringList::iterator iterator = folders.begin();
for (; iterator != folders.end(); iterator++) {
if (fKnownMailboxes.find(*iterator) != fKnownMailboxes.end())
iterator = folders.erase(iterator);
StringList::iterator folderIterator = newFolders.begin();
while (folderIterator != newFolders.end()) {
if (fFolders.find(*folderIterator) != fFolders.end())
folderIterator = newFolders.erase(folderIterator);
else
folderIterator++;
}
if (fSettings.IdleMode()) {
// Create connection workers as allowed
int32 totalMailboxes = fFolders.size() + newFolders.size();
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()
&& fWorkers.CountItems() < totalMailboxes) {
IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
fSettings);
if (!fWorkers.AddItem(worker)) {
delete worker;
break;
}
// Remove mailboxes from workers
for (int32 i = 0; i < fWorkers.CountItems(); i++) {
fWorkers.ItemAt(i)->RemoveAllMailboxes();
}
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;
while (!folders.empty()) {
BString folder = folders[0];
folders.erase(folders.begin());
fWorkers.ItemAt(index)->AddMailbox(folder);
fKnownMailboxes.insert(folder);
for (; iterator != fFolders.end(); iterator++) {
fWorkers.ItemAt(index)->AddMailbox(iterator->second);
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 worker->Start();
return worker->Run();
}
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 -
@@ -6,7 +6,7 @@
#define IMAP_PROTOCOL_H
#include <set>
#include <map>
#include <MailProtocol.h>
#include <ObjectList.h>
@@ -15,12 +15,13 @@
class IMAPConnectionWorker;
class IMAPFolder;
namespace IMAP {
class Protocol;
}
typedef std::set<BString> StringSet;
typedef std::map<BString, IMAPFolder*> FolderMap;
class IMAPProtocol : public BInboundMailProtocol {
@@ -31,6 +32,8 @@ public:
status_t CheckSubscribedFolders(
IMAP::Protocol& protocol);
sem_id FolderChangeSemaphore() const
{ return fFolderChangeSemaphore; }
virtual status_t SyncMessages();
virtual status_t FetchBody(const entry_ref& ref);
@@ -44,10 +47,16 @@ public:
protected:
void ReadyToRun();
private:
IMAPFolder* _CreateFolder(const BString& mailbox,
const BString& separator);
status_t _CreateFolderChangeSemaphore();
protected:
Settings fSettings;
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 ] ;
local sources =
# IMAPInboundProtocol.cpp
# IMAPRootInboundProtocol.cpp
IMAPProtocol.cpp
ConfigView.cpp
FolderConfigWindow.cpp
IMAPFolder.cpp
IMAPConnectionWorker.cpp
IMAPMailbox.cpp
Settings.cpp
Utilities.cpp
# imap_lib
Commands.cpp
@@ -0,0 +1,21 @@
/*
* Copyright 2013, Axel Dörfler, [email protected].
* 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, [email protected].
* 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 <[email protected]>
* Distributed under the terms of the MIT License.
*/
@@ -202,10 +202,10 @@ SelectCommand::SelectCommand()
SelectCommand::SelectCommand(const char* name)
:
fMailboxName(name),
fNextUID(0),
fUIDValidity(0)
{
SetTo(name);
}
@@ -216,7 +216,6 @@ SelectCommand::CommandString()
return "";
BString command = "SELECT \"";
// TODO: properly quote the string!
command += fMailboxName;
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 -
@@ -654,10 +661,20 @@ ListCommand::CommandString()
bool
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);
if (folder == "")
return true;
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) {
// Decoding failed, just add the plain text
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.
*
* Distributed under the terms of the MIT License.
@@ -97,8 +97,7 @@ public:
virtual BString CommandString();
virtual bool HandleUntagged(Response& response);
void SetTo(const char* mailboxName)
{ fMailboxName = mailboxName; }
void SetTo(const char* mailboxName);
uint32 NextUID() { return fNextUID; }
uint32 UIDValidity() { return fUIDValidity; }
@@ -249,10 +248,11 @@ public:
ListCommand(const char* prefix,
bool subscribedOnly);
BString CommandString();
bool HandleUntagged(Response& response);
virtual BString CommandString();
virtual bool HandleUntagged(Response& response);
const StringList& FolderList();
const BString& Separator() { return fSeparator; }
private:
const char* _Command() const;
@@ -261,6 +261,7 @@ private:
RFC3501Encoding fEncoding;
const char* fPrefix;
StringList fFolders;
BString fSeparator;
bool fSubscribedOnly;
};
@@ -110,7 +110,7 @@ Protocol::IsConnected()
status_t
Protocol::GetFolders(FolderList& folders)
Protocol::GetFolders(FolderList& folders, BString& separator)
{
StringList allFolders;
status_t status = _GetAllFolders(allFolders);
@@ -118,7 +118,7 @@ Protocol::GetFolders(FolderList& folders)
return status;
StringList subscribedFolders;
status = GetSubscribedFolders(subscribedFolders);
status = GetSubscribedFolders(subscribedFolders, separator);
if (status != B_OK)
return status;
@@ -158,7 +158,7 @@ Protocol::GetFolders(FolderList& folders)
status_t
Protocol::GetSubscribedFolders(StringList& folders)
Protocol::GetSubscribedFolders(StringList& folders, BString& separator)
{
ListCommand command(NULL, true);
status_t status = ProcessCommand(command);
@@ -166,6 +166,7 @@ Protocol::GetSubscribedFolders(StringList& folders)
return status;
folders = command.FolderList();
separator = command.Separator();
return status;
}
@@ -66,8 +66,10 @@ public:
ssize_t SendData(const char* buffer, uint32 length);
// Some convenience methods
status_t GetFolders(FolderList& folders);
status_t GetSubscribedFolders(StringList& folders);
status_t GetFolders(FolderList& folders,
BString& separator);
status_t GetSubscribedFolders(StringList& folders,
BString& separator);
status_t SubscribeFolder(const char* folder);
status_t UnsubscribeFolder(const char* folder);
status_t GetQuota(uint64& used, uint64& total);