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 <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);