Work in progress of rewrite; IMAP doesn't even compile at this point.

This commit is contained in:
Axel Dörfler
2015-01-06 15:20:11 +01:00
parent 2ba93e7d81
commit 37d26ae5e2
24 changed files with 1607 additions and 1526 deletions
@@ -329,9 +329,9 @@ FolderConfigWindow::_LoadFolders()
delete[] passwd;
}
fIMAPFolders.Connect(server, username, password, useSSL);
fProtocol.Connect(server, username, password, useSSL);
fFolderList.clear();
fIMAPFolders.GetFolders(fFolderList);
fProtocol.GetFolders(fFolderList);
for (unsigned int i = 0; i < fFolderList.size(); i++) {
FolderInfo& info = fFolderList[i];
CheckBoxItem* item = new CheckBoxItem(info.folder, info.subscribed);
@@ -340,7 +340,7 @@ FolderConfigWindow::_LoadFolders()
}
uint64 used, total;
if (fIMAPFolders.GetQuota(used, total) == B_OK) {
if (fProtocol.GetQuota(used, total) == B_OK) {
char buffer[256];
BString quotaString = "Server storage: ";
quotaString += string_for_size(used, buffer, 256);
@@ -377,9 +377,9 @@ FolderConfigWindow::_ApplyChanges()
FolderInfo& info = fFolderList[i];
CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i);
if (info.subscribed && !item->Checked())
fIMAPFolders.UnsubscribeFolder(info.folder);
fProtocol.UnsubscribeFolder(info.folder);
else if (!info.subscribed && item->Checked())
fIMAPFolders.SubscribeFolder(info.folder);
fProtocol.SubscribeFolder(info.folder);
}
status->PostMessage(B_QUIT_REQUESTED);
@@ -14,7 +14,7 @@
#include <MailSettings.h>
#include "IMAPFolders.h"
#include "Protocol.h"
class FolderConfigWindow : public BWindow {
@@ -28,11 +28,11 @@ private:
void _LoadFolders();
void _ApplyChanges();
IMAPFolders fIMAPFolders;
IMAP::Protocol fProtocol;
BListView* fFolderListView;
BButton* fApplyButton;
const BMessage fSettings;
FolderList fFolderList;
IMAP::FolderList fFolderList;
BStringView* fQuotaView;
};
@@ -15,7 +15,7 @@
#include "MailSettings.h"
#include "IMAPMailbox.h"
#include "Protocol.h"
#include "IMAPStorage.h"
@@ -9,8 +9,6 @@
#include <Messenger.h>
#include "IMAPFolders.h"
IMAPRootInboundProtocol::IMAPRootInboundProtocol(BMailAccountSettings* settings)
:
@@ -34,9 +32,8 @@ IMAPRootInboundProtocol::Connect(const char* server, const char* username,
if (status != B_OK)
return status;
IMAPFolders folder(fIMAPMailbox);
FolderList folders;
folder.GetFolders(folders);
fIMAPMailbox.GetFolders(folders);
for (unsigned int i = 0; i < folders.size(); i++) {
if (!folders[i].subscribed || folders[i].folder == "INBOX")
continue;
@@ -21,12 +21,10 @@ local sources =
IMAPRootInboundProtocol.cpp
IMAPFolderConfig.cpp
# imap_lib
IMAPFolders.cpp
IMAPHandler.cpp
IMAPMailbox.cpp
IMAPParser.cpp
IMAPProtocol.cpp
Commands.cpp
Protocol.cpp
IMAPStorage.cpp
Response.cpp
;
AddResources IMAP : IMAP.rdef ;
@@ -46,8 +44,8 @@ Addon IMAP
libshared.a [ TargetLibsupc++ ] [ TargetLibstdc++ ]
;
SEARCH on [ FGristFiles IMAPFolders.cpp IMAPHandler.cpp IMAPMailbox.cpp
IMAPParser.cpp IMAPProtocol.cpp IMAPStorage.cpp ServerConnection.cpp ]
SEARCH on [ FGristFiles Commands.cpp IMAPMailbox.cpp
IMAPProtocol.cpp IMAPStorage.cpp Response.cpp ]
= [ FDirName $(HAIKU_TOP) src add-ons mail_daemon inbound_protocols imap
imap_lib ] ;
@@ -5,16 +5,12 @@
*/
#include "IMAPHandler.h"
#include "Commands.h"
#include <stdlib.h>
#include <AutoDeleter.h>
#include "IMAPMailbox.h"
#include "IMAPParser.h"
#include "IMAPStorage.h"
#define DEBUG_IMAP_HANDLER
#ifdef DEBUG_IMAP_HANDLER
@@ -28,28 +24,82 @@
using namespace BPrivate;
IMAPCommand::~IMAPCommand()
{
}
namespace IMAP {
BString
IMAPCommand::Command()
{
return "";
}
static HandlerListener sEmptyHandler;
IMAPMailboxCommand::IMAPMailboxCommand(IMAPMailbox& mailbox)
Handler::Handler()
:
fIMAPMailbox(mailbox),
fStorage(mailbox.GetStorage()),
fConnectionReader(mailbox.GetConnectionReader())
fListener(&sEmptyHandler)
{
}
IMAPMailboxCommand::~IMAPMailboxCommand()
Handler::~Handler()
{
}
void
Handler::SetListener(HandlerListener& listener)
{
fListener = &listener;
}
IMAP::LiteralHandler*
Handler::LiteralHandler()
{
return NULL;
}
// #pragma mark -
Command::~Command()
{
}
status_t
Command::HandleTagged(Response& response)
{
if (response.StringAt(0) == "OK")
return B_OK;
if (response.StringAt(0) == "BAD")
return B_BAD_VALUE;
if (response.StringAt(0) == "NO")
return B_NOT_ALLOWED;
return B_ERROR;
}
// #pragma mark -
HandlerListener::~HandlerListener()
{
}
void
HandlerListener::ExpungeReceived(int32 number)
{
}
void
HandlerListener::ExistsReceived(int32 number)
{
}
void
HandlerListener::FetchBody(Command& command, int32 size)
{
}
@@ -57,19 +107,83 @@ IMAPMailboxCommand::~IMAPMailboxCommand()
// #pragma mark -
MailboxSelectHandler::MailboxSelectHandler(IMAPMailbox& mailbox)
RawCommand::RawCommand(const BString& command)
:
IMAPMailboxCommand(mailbox),
fMailboxName(""),
fNextUID(-1),
fUIDValidity(-1)
fCommand(command)
{
}
BString
MailboxSelectHandler::Command()
RawCommand::CommandString()
{
return fCommand;
}
// #pragma mark -
LoginCommand::LoginCommand(const char* user, const char* password)
:
fUser(user),
fPassword(password)
{
}
BString
LoginCommand::CommandString()
{
BString command = "LOGIN ";
command << "\"" << fUser << "\" " << "\"" << fPassword << "\"";
return command;
}
bool
LoginCommand::HandleUntagged(Response& response)
{
if (!response.EqualsAt(0, "OK") || !response.IsListAt(1, '['))
return false;
// TODO: we only support capabilities at the moment
ArgumentList& list = response.ListAt(1);
if (!list.EqualsAt(0, "CAPABILITY"))
return false;
fCapabilities.MakeEmpty();
while (list.CountItems() > 1)
fCapabilities.AddItem(list.RemoveItemAt(1));
TRACE("CAPABILITY: %s\n", fCapabilities.ToString().String());
return true;
}
// #pragma mark -
SelectCommand::SelectCommand()
:
fNextUID(0),
fUIDValidity(0)
{
}
SelectCommand::SelectCommand(const char* name)
:
fMailboxName(name),
fNextUID(0),
fUIDValidity(0)
{
}
BString
SelectCommand::CommandString()
{
if (fMailboxName == "")
return "";
@@ -82,21 +196,19 @@ MailboxSelectHandler::Command()
bool
MailboxSelectHandler::Handle(const BString& response)
SelectCommand::HandleUntagged(Response& response)
{
BString extracted = IMAPParser::ExtractStringAfter(response,
"* OK [UIDVALIDITY");
if (extracted != "") {
fUIDValidity = IMAPParser::RemoveIntegerFromLeft(extracted);
TRACE("UIDValidity %i\n", (int)fUIDValidity);
return true;
}
extracted = IMAPParser::ExtractStringAfter(response, "* OK [UIDNEXT");
if (extracted != "") {
fNextUID = IMAPParser::RemoveIntegerFromLeft(extracted);
TRACE("NextUID %i\n", (int)fNextUID);
return true;
if (response.EqualsAt(0, "OK") && response.IsListAt(1, '[')) {
const ArgumentList& arguments = response.ListAt(1);
if (arguments.EqualsAt(0, "UIDVALIDITY")
&& arguments.IsNumberAt(1)) {
fUIDValidity = arguments.NumberAt(1);
return true;
} else if (arguments.EqualsAt(0, "UIDNEXT")
&& arguments.IsNumberAt(1)) {
fNextUID = arguments.NumberAt(1);
return true;
}
}
return false;
@@ -106,42 +218,94 @@ MailboxSelectHandler::Handle(const BString& response)
// #pragma mark -
CapabilityHandler::CapabilityHandler()
:
fCapabilities("")
{
}
BString
CapabilityHandler::Command()
CapabilityHandler::CommandString()
{
return "CAPABILITY";
}
bool
CapabilityHandler::Handle(const BString& response)
CapabilityHandler::HandleUntagged(Response& response)
{
BString cap = IMAPParser::ExtractStringAfter(response, "* CAPABILITY");
if (cap == "")
if (!response.IsCommand("CAPABILITY"))
return false;
fCapabilities = cap;
TRACE("CAPABILITY: %s\n", fCapabilities.String());
fCapabilities.MakeEmpty();
while (response.CountItems() > 1)
fCapabilities.AddItem(response.RemoveItemAt(1));
TRACE("CAPABILITY: %s\n", fCapabilities.ToString().String());
return true;
}
BString&
CapabilityHandler::Capabilities()
{
return fCapabilities;
}
// #pragma mark -
FetchMessageEntriesCommand::FetchMessageEntriesCommand(
MessageEntryList& entries, uint32 from, uint32 to)
:
fEntries(entries),
fFrom(from),
fTo(to)
{
}
BString
FetchMessageEntriesCommand::CommandString()
{
BString command = "UID FETCH ";
command << fFrom << ":" << fTo << " FLAGS";
return command;
}
bool
FetchMessageEntriesCommand::HandleUntagged(Response& response)
{
if (!response.EqualsAt(1, "FETCH") || !response.IsListAt(2))
return false;
MessageEntry entry;
ArgumentList& list = response.ListAt(2);
for (int32 i = 0; i < list.CountItems(); i += 2) {
if (list.EqualsAt(i, "UID") && list.IsNumberAt(i + 1))
entry.uid = list.NumberAt(i + 1);
else if (list.EqualsAt(i, "FLAGS") && list.IsListAt(i + 1)) {
// Parse flags
ArgumentList& flags = list.ListAt(i + 1);
printf("flags: %s\n", flags.ToString().String());
for (int32 j = 0; j < flags.CountItems(); j++) {
if (flags.EqualsAt(j, "\\Seen"))
entry.flags |= kSeen;
else if (flags.EqualsAt(j, "\\Answered"))
entry.flags |= kAnswered;
else if (flags.EqualsAt(j, "\\Flagged"))
entry.flags |= kFlagged;
else if (flags.EqualsAt(j, "\\Deleted"))
entry.flags |= kDeleted;
else if (flags.EqualsAt(j, "\\Draft"))
entry.flags |= kDraft;
}
}
}
if (entry.uid == 0)
return false;
fEntries.push_back(entry);
return true;
}
// #pragma mark -
#if 0
FetchMinMessageCommand::FetchMinMessageCommand(IMAPMailbox& mailbox,
int32 message, MinMessageList* list, BPositionIO** data)
:
@@ -170,7 +334,7 @@ FetchMinMessageCommand::FetchMinMessageCommand(IMAPMailbox& mailbox,
BString
FetchMinMessageCommand::Command()
FetchMinMessageCommand::CommandString()
{
if (fMessage <= 0)
return "";
@@ -186,7 +350,7 @@ FetchMinMessageCommand::Command()
bool
FetchMinMessageCommand::Handle(const BString& response)
FetchMinMessageCommand::HandleUntagged(const BString& response)
{
BString extracted = response;
int32 message;
@@ -256,47 +420,6 @@ FetchMinMessageCommand::ExtractFlags(const BString& response)
// #pragma mark -
FetchMessageListCommand::FetchMessageListCommand(IMAPMailbox& mailbox,
MinMessageList* list, int32 nextId)
:
IMAPMailboxCommand(mailbox),
fMinMessageList(list),
fNextId(nextId)
{
}
BString
FetchMessageListCommand::Command()
{
BString command = "UID FETCH 1:";
command << fNextId - 1;
command << " FLAGS";
return command;
}
bool
FetchMessageListCommand::Handle(const BString& response)
{
BString extracted = response;
int32 message;
if (!IMAPParser::RemoveUntagedFromLeft(extracted, "FETCH", message))
return false;
MinMessage minMessage;
if (!FetchMinMessageCommand::ParseMinMessage(extracted, minMessage))
return false;
fMinMessageList->push_back(minMessage);
return true;
}
// #pragma mark -
FetchMessageCommand::FetchMessageCommand(IMAPMailbox& mailbox, int32 message,
BPositionIO* data, int32 fetchBodyLimit)
:
@@ -335,7 +458,7 @@ FetchMessageCommand::~FetchMessageCommand()
BString
FetchMessageCommand::Command()
FetchMessageCommand::CommandString()
{
BString command = "FETCH ";
command << fMessage;
@@ -349,7 +472,7 @@ FetchMessageCommand::Command()
bool
FetchMessageCommand::Handle(const BString& response)
FetchMessageCommand::HandleUntagged(const BString& response)
{
BString extracted = response;
int32 message;
@@ -454,7 +577,7 @@ FetchBodyCommand::~FetchBodyCommand()
BString
FetchBodyCommand::Command()
FetchBodyCommand::CommandString()
{
BString command = "FETCH ";
command << fMessage;
@@ -464,7 +587,7 @@ FetchBodyCommand::Command()
bool
FetchBodyCommand::Handle(const BString& response)
FetchBodyCommand::HandleUntagged(const BString& response)
{
if (response.FindFirst("FETCH") < 0)
return false;
@@ -521,7 +644,7 @@ SetFlagsCommand::SetFlagsCommand(IMAPMailbox& mailbox, int32 message,
BString
SetFlagsCommand::Command()
SetFlagsCommand::CommandString()
{
BString command = "STORE ";
command << fMessage;
@@ -533,7 +656,7 @@ SetFlagsCommand::Command()
bool
SetFlagsCommand::Handle(const BString& response)
SetFlagsCommand::HandleUntagged(const BString& response)
{
return false;
}
@@ -576,7 +699,7 @@ AppendCommand::AppendCommand(IMAPMailbox& mailbox, BPositionIO& message,
BString
AppendCommand::Command()
AppendCommand::CommandString()
{
BString command = "APPEND ";
command << fIMAPMailbox.Mailbox();
@@ -591,7 +714,7 @@ AppendCommand::Command()
bool
AppendCommand::Handle(const BString& response)
AppendCommand::HandleUntagged(const BString& response)
{
if (response.FindFirst("+") != 0)
return false;
@@ -614,21 +737,27 @@ AppendCommand::Handle(const BString& response)
fIMAPMailbox.SendRawData(CRLF, strlen(CRLF));
return true;
}
#endif
// #pragma mark -
ExistsHandler::ExistsHandler(IMAPMailbox& mailbox)
:
IMAPMailboxCommand(mailbox)
ExistsHandler::ExistsHandler()
{
}
bool
ExistsHandler::Handle(const BString& response)
ExistsHandler::HandleUntagged(Response& response)
{
if (!response.EqualsAt(1, "EXISTS") || response.IsNumberAt(0))
return false;
int32 expunge = response.NumberAt(0);
Listener().ExistsReceived(expunge);
#if 0
if (response.FindFirst("EXISTS") < 0)
return false;
@@ -654,6 +783,7 @@ ExistsHandler::Handle(const BString& response)
TRACE("EXISTS %i\n", (int)exists);
fIMAPMailbox.SendRawCommand("DONE");
#endif
return true;
}
@@ -662,47 +792,36 @@ ExistsHandler::Handle(const BString& response)
// #pragma mark -
ExpungeCommmand::ExpungeCommmand(IMAPMailbox& mailbox)
:
IMAPMailboxCommand(mailbox)
ExpungeCommand::ExpungeCommand()
{
}
BString
ExpungeCommmand::Command()
ExpungeCommand::CommandString()
{
return "EXPUNGE";
}
bool
ExpungeCommmand::Handle(const BString& response)
{
return false;
}
// #pragma mark -
ExpungeHandler::ExpungeHandler(IMAPMailbox& mailbox)
:
IMAPMailboxCommand(mailbox)
ExpungeHandler::ExpungeHandler()
{
}
bool
ExpungeHandler::Handle(const BString& response)
ExpungeHandler::HandleUntagged(Response& response)
{
if (response.FindFirst("EXPUNGE") < 0)
if (!response.EqualsAt(1, "EXPUNGE") || response.IsNumberAt(0))
return false;
int32 expunge = 0;
if (!IMAPParser::ExtractUntagedFromLeft(response, "EXPUNGE", expunge))
return false;
int32 expunge = response.NumberAt(0);
Listener().ExpungeReceived(expunge);
#if 0
// remove from storage
IMAPStorage& storage = fIMAPMailbox.GetStorage();
storage.DeleteMessage(fIMAPMailbox.MessageNumberToUID(expunge));
@@ -717,6 +836,7 @@ ExpungeHandler::Handle(const BString& response)
// the watching loop restarts again, we need to watch again to because
// some IDLE implementation stop sending notifications
fIMAPMailbox.SendRawCommand("DONE");
#endif
return true;
}
@@ -724,6 +844,7 @@ ExpungeHandler::Handle(const BString& response)
// #pragma mark -
#if 0
FlagsHandler::FlagsHandler(IMAPMailbox& mailbox)
:
IMAPMailboxCommand(mailbox)
@@ -732,7 +853,7 @@ FlagsHandler::FlagsHandler(IMAPMailbox& mailbox)
bool
FlagsHandler::Handle(const BString& response)
FlagsHandler::HandleUntagged(const BString& response)
{
if (response.FindFirst("FETCH") < 0)
return false;
@@ -749,13 +870,14 @@ FlagsHandler::Handle(const BString& response)
return true;
}
#endif
// #pragma mark -
BString
ListCommand::Command()
ListCommand::CommandString()
{
fFolders.clear();
return "LIST \"\" \"*\"";
@@ -763,9 +885,14 @@ ListCommand::Command()
bool
ListCommand::Handle(const BString& response)
ListCommand::HandleUntagged(Response& response)
{
return ParseList("LIST", response, fFolders);
if (response.IsCommand("LIST") && response.IsStringAt(3)) {
fFolders.push_back(response.StringAt(3));
return true;
}
return false;
}
@@ -776,41 +903,11 @@ ListCommand::FolderList()
}
bool
ListCommand::ParseList(const char* command, const BString& response,
StringList& list)
{
int32 textPos = response.FindFirst(command);
if (textPos < 0)
return false;
BString extracted = response;
extracted.Remove(0, textPos + strlen(command) + 1);
extracted.Trim();
if (extracted[0] == '(') {
BString flags = IMAPParser::ExtractBetweenBrackets(extracted, "(", ")");
if (flags.IFindFirst("\\Noselect") >= 0)
return true;
textPos = extracted.FindFirst(")");
extracted.Remove(0, textPos + 1);
}
IMAPParser::RemovePrimitiveFromLeft(extracted);
extracted.Trim();
// remove quotation marks
extracted.Remove(0, 1);
extracted.Truncate(extracted.Length() - 1);
list.push_back(extracted);
return true;
}
// #pragma mark -
BString
ListSubscribedCommand::Command()
ListSubscribedCommand::CommandString()
{
fFolders.clear();
return "LSUB \"\" \"*\"";
@@ -818,9 +915,14 @@ ListSubscribedCommand::Command()
bool
ListSubscribedCommand::Handle(const BString& response)
ListSubscribedCommand::HandleUntagged(Response& response)
{
return ListCommand::ParseList("LSUB", response, fFolders);
if (response.IsCommand("LSUB") && response.IsStringAt(3)) {
fFolders.push_back(response.StringAt(3));
return true;
}
return false;
}
@@ -842,7 +944,7 @@ SubscribeCommand::SubscribeCommand(const char* mailboxName)
BString
SubscribeCommand::Command()
SubscribeCommand::CommandString()
{
BString command = "SUBSCRIBE \"";
command += fMailboxName;
@@ -851,13 +953,6 @@ SubscribeCommand::Command()
}
bool
SubscribeCommand::Handle(const BString& response)
{
return false;
}
// #pragma mark -
@@ -869,7 +964,7 @@ UnsubscribeCommand::UnsubscribeCommand(const char* mailboxName)
BString
UnsubscribeCommand::Command()
UnsubscribeCommand::CommandString()
{
BString command = "UNSUBSCRIBE \"";
command += fMailboxName;
@@ -878,13 +973,6 @@ UnsubscribeCommand::Command()
}
bool
UnsubscribeCommand::Handle(const BString& response)
{
return false;
}
// #pragma mark -
@@ -898,7 +986,7 @@ GetQuotaCommand::GetQuotaCommand(const char* mailboxName)
BString
GetQuotaCommand::Command()
GetQuotaCommand::CommandString()
{
BString command = "GETQUOTA \"";
command += fMailboxName;
@@ -908,15 +996,14 @@ GetQuotaCommand::Command()
bool
GetQuotaCommand::Handle(const BString& response)
GetQuotaCommand::HandleUntagged(Response& response)
{
if (response.FindFirst("QUOTA") < 0)
if (!response.IsCommand("QUOTA") || response.IsListAt(1))
return false;
BString data = IMAPParser::ExtractBetweenBrackets(response, "(", ")");
IMAPParser::RemovePrimitiveFromLeft(data);
fUsedStorage = (uint64)IMAPParser::RemoveIntegerFromLeft(data) * 1024;
fTotalStorage = (uint64)IMAPParser::RemoveIntegerFromLeft(data) * 1024;
const ArgumentList& arguments = response.ListAt(1);
fUsedStorage = (uint64)arguments.NumberAt(0) * 1024;
fTotalStorage = (uint64)arguments.NumberAt(1) * 1024;
return true;
}
@@ -934,3 +1021,6 @@ GetQuotaCommand::TotalStorage()
{
return fTotalStorage;
}
} // namespace
@@ -0,0 +1,362 @@
/*
* Copyright 2010-2011, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef COMMANDS_H
#define COMMANDS_H
#include <vector>
#include "Response.h"
typedef std::vector<BString> StringList;
namespace IMAP {
class ConnectionReader;
class HandlerListener;
struct MessageEntry {
MessageEntry()
:
uid(0),
flags(0)
{
}
uint32 uid;
uint32 flags;
};
typedef std::vector<MessageEntry> MessageEntryList;
enum MessageFlags {
kSeen = 0x01,
kAnswered = 0x02,
kFlagged = 0x04,
kDeleted = 0x08,
kDraft = 0x10
};
class Handler {
public:
Handler();
virtual ~Handler();
void SetListener(HandlerListener& listener);
HandlerListener& Listener() { return *fListener; }
virtual bool HandleUntagged(Response& response) = 0;
virtual IMAP::LiteralHandler* LiteralHandler();
protected:
HandlerListener* fListener;
};
class Command {
public:
virtual ~Command();
virtual BString CommandString() = 0;
virtual status_t HandleTagged(Response& response);
};
class HandlerListener {
public:
virtual ~HandlerListener();
virtual void ExpungeReceived(int32 number);
virtual void ExistsReceived(int32 number);
virtual void FetchBody(Command& command, int32 size);
};
class RawCommand : public Command {
public:
RawCommand(const BString& command);
virtual BString CommandString();
private:
BString fCommand;
};
class LoginCommand : public Command, public Handler {
public:
LoginCommand(const char* user,
const char* password);
virtual BString CommandString();
virtual bool HandleUntagged(Response& response);
const ArgumentList& Capabilities() const { return fCapabilities; }
private:
const char* fUser;
const char* fPassword;
ArgumentList fCapabilities;
};
class SelectCommand : public Command, public Handler {
public:
SelectCommand();
SelectCommand(const char* mailboxName);
BString CommandString();
bool HandleUntagged(Response& response);
void SetTo(const char* mailboxName)
{ fMailboxName = mailboxName; }
uint32 NextUID() { return fNextUID; }
uint32 UIDValidity() { return fUIDValidity; }
private:
BString fMailboxName;
uint32 fNextUID;
uint32 fUIDValidity;
};
class CapabilityHandler : public Command, public Handler {
public:
virtual BString CommandString();
virtual bool HandleUntagged(Response& response);
const ArgumentList& Capabilities() const { return fCapabilities; }
private:
ArgumentList fCapabilities;
};
class FetchMessageEntriesCommand : public Command, public Handler {
public:
FetchMessageEntriesCommand(
MessageEntryList& entries, uint32 from,
uint32 to);
BString CommandString();
virtual bool HandleUntagged(Response& response);
private:
MessageEntryList& fEntries;
uint32 fFrom;
uint32 fTo;
};
#if 0
class FetchMinMessageCommand : public IMAPMailboxCommand {
public:
FetchMinMessageCommand(IMAPMailbox& mailbox,
int32 message, MinMessageList* list,
BPositionIO** data);
FetchMinMessageCommand(IMAPMailbox& mailbox,
int32 firstMessage, int32 lastMessage,
MinMessageList* list, BPositionIO** data);
BString CommandString();
bool HandleUntagged(const BString& response);
static bool ParseMinMessage(const BString& response,
MinMessage& minMessage);
static int32 ExtractFlags(const BString& response);
private:
int32 fMessage;
int32 fEndMessage;
MinMessageList* fMinMessageList;
BPositionIO** fData;
};
class FetchMessageCommand : public IMAPMailboxCommand {
public:
FetchMessageCommand(IMAPMailbox& mailbox,
int32 message, BPositionIO* data,
int32 fetchBodyLimit = -1);
/*! Fetch multiple message within a range. */
FetchMessageCommand(IMAPMailbox& mailbox,
int32 firstMessage, int32 lastMessage,
int32 fetchBodyLimit = -1);
~FetchMessageCommand();
BString CommandString();
bool HandleUntagged(const BString& response);
private:
int32 fMessage;
int32 fEndMessage;
BPositionIO* fOutData;
int32 fFetchBodyLimit;
int32 fUnhandled;
};
class FetchBodyCommand : public IMAPMailboxCommand {
public:
/*! takes ownership of the data */
FetchBodyCommand(IMAPMailbox& mailbox,
int32 message, BPositionIO* data);
~FetchBodyCommand();
BString CommandString();
bool HandleUntagged(const BString& response);
private:
int32 fMessage;
BPositionIO* fOutData;
};
class SetFlagsCommand : public IMAPMailboxCommand {
public:
SetFlagsCommand(IMAPMailbox& mailbox,
int32 message, int32 flags);
BString CommandString();
bool HandleUntagged(const BString& response);
static BString GenerateFlagList(int32 flags);
private:
int32 fMessage;
int32 fFlags;
};
class AppendCommand : public IMAPMailboxCommand {
public:
AppendCommand(IMAPMailbox& mailbox,
BPositionIO& message, off_t size,
int32 flags, time_t time);
BString CommandString();
bool HandleUntagged(const BString& response);
private:
BPositionIO& fMessageData;
off_t fDataSize;
int32 fFlags;
time_t fTime;
};
#endif
class ExistsHandler : public Handler {
public:
ExistsHandler();
bool HandleUntagged(Response& response);
};
/*! Just send a expunge command to delete kDeleted flagged messages. The
response is handled by the unsolicited ExpungeHandler which is installed
all the time.
*/
class ExpungeCommand : public Command {
public:
ExpungeCommand();
BString CommandString();
};
class ExpungeHandler : public Handler {
public:
ExpungeHandler();
bool HandleUntagged(Response& response);
};
#if 0
class FlagsHandler : public Handler {
public:
FlagsHandler(IMAPMailbox& mailbox);
bool HandleUntagged(const BString& response);
};
#endif
class ListCommand : public Command, public Handler {
public:
BString CommandString();
bool HandleUntagged(Response& response);
const StringList& FolderList();
private:
StringList fFolders;
};
class ListSubscribedCommand : public Command, public Handler {
public:
BString CommandString();
bool HandleUntagged(Response& response);
const StringList& FolderList();
private:
StringList fFolders;
};
class SubscribeCommand : public Command {
public:
SubscribeCommand(const char* mailboxName);
BString CommandString();
private:
BString fMailboxName;
};
class UnsubscribeCommand : public Command {
public:
UnsubscribeCommand(const char* mailboxName);
BString CommandString();
private:
BString fMailboxName;
};
class GetQuotaCommand : public Command, public Handler {
public:
GetQuotaCommand(const char* mailboxName = "");
BString CommandString();
bool HandleUntagged(Response& response);
uint64 UsedStorage();
uint64 TotalStorage();
private:
BString fMailboxName;
uint64 fUsedStorage;
uint64 fTotalStorage;
};
} // namespace IMAP
#endif // COMMANDS_H
@@ -1,130 +0,0 @@
/*
* Copyright 2011, Haiku, Inc. All rights reserved.
* Copyright 2011, Clemens Zeidler <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "IMAPFolders.h"
IMAPFolders::IMAPFolders()
{
fHandlerList.AddItem(&fCapabilityHandler);
}
IMAPFolders::IMAPFolders(IMAPProtocol& connection)
:
IMAPProtocol(connection)
{
fHandlerList.AddItem(&fCapabilityHandler);
}
status_t
IMAPFolders::GetFolders(FolderList& folders)
{
StringList allFolders;
status_t status = _GetAllFolders(allFolders);
if (status != B_OK)
return status;
StringList subscribedFolders;
status = _GetSubscribedFolders(subscribedFolders);
if (status != B_OK)
return status;
for (unsigned int i = 0; i < allFolders.size(); i++) {
FolderInfo info;
info.folder = allFolders[i];
for (unsigned int a = 0; a < subscribedFolders.size(); a++) {
if (allFolders[i] == subscribedFolders[a]
|| allFolders[i].ICompare("INBOX") == 0) {
info.subscribed = true;
break;
}
}
folders.push_back(info);
}
// you could be subscribed to a folder which not exist currently, add them:
for (unsigned int a = 0; a < subscribedFolders.size(); a++) {
bool isInlist = false;
for (unsigned int i = 0; i < allFolders.size(); i++) {
if (subscribedFolders[a] == allFolders[i]) {
isInlist = true;
break;
}
}
if (isInlist)
continue;
FolderInfo info;
info.folder = subscribedFolders[a];
info.subscribed = true;
folders.push_back(info);
}
return B_OK;
}
status_t
IMAPFolders::SubscribeFolder(const char* folder)
{
SubscribeCommand command(folder);
return ProcessCommand(&command);
}
status_t
IMAPFolders::UnsubscribeFolder(const char* folder)
{
UnsubscribeCommand command(folder);
return ProcessCommand(&command);
}
status_t
IMAPFolders::GetQuota(uint64& used, uint64& total)
{
if (fCapabilityHandler.Capabilities() == "")
ProcessCommand(fCapabilityHandler.Command());
if (fCapabilityHandler.Capabilities().FindFirst("QUOTA") < 0)
return B_ERROR;
GetQuotaCommand quotaCommand;
status_t status = ProcessCommand(&quotaCommand);
if (status != B_OK)
return status;
used = quotaCommand.UsedStorage();
total = quotaCommand.TotalStorage();
return B_OK;
}
status_t
IMAPFolders::_GetAllFolders(StringList& folders)
{
ListCommand listCommand;
status_t status = ProcessCommand(&listCommand);
if (status != B_OK)
return status;
folders = listCommand.FolderList();
return status;
}
status_t
IMAPFolders::_GetSubscribedFolders(StringList& folders)
{
ListSubscribedCommand listSubscribedCommand;
status_t status = ProcessCommand(&listSubscribedCommand);
if (status != B_OK)
return status;
folders = listSubscribedCommand.FolderList();
return status;
}
@@ -1,51 +0,0 @@
/*
* Copyright 2011, Haiku Inc. All Rights Reserved.
* Copyright 2011 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_FOLDERS_H
#define IMAP_FOLDERS_H
#include <vector>
#include "IMAPHandler.h"
#include "IMAPProtocol.h"
class FolderInfo {
public:
FolderInfo()
:
subscribed(false)
{
}
BString folder;
bool subscribed;
};
typedef std::vector<FolderInfo> FolderList;
class IMAPFolders : public IMAPProtocol {
public:
IMAPFolders();
IMAPFolders(IMAPProtocol& connection);
status_t GetFolders(FolderList& folders);
status_t SubscribeFolder(const char* folder);
status_t UnsubscribeFolder(const char* folder);
status_t GetQuota(uint64& used, uint64& total);
private:
status_t _GetAllFolders(StringList& folders);
status_t _GetSubscribedFolders(StringList& folders);
CapabilityHandler fCapabilityHandler;
};
#endif // IMAP_FOLDERS_H
@@ -1,303 +0,0 @@
/*
* Copyright 2010-2011, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_HANDLER_H
#define IMAP_HANDLER_H
#include <vector>
#include <DataIO.h>
#include <String.h>
class ConnectionReader;
class IMAPMailbox;
class IMAPStorage;
class IMAPCommand {
public:
virtual ~IMAPCommand();
virtual BString Command();
virtual bool Handle(const BString& response) = 0;
};
class IMAPMailboxCommand : public IMAPCommand{
public:
IMAPMailboxCommand(IMAPMailbox& mailbox);
virtual ~IMAPMailboxCommand();
protected:
IMAPMailbox& fIMAPMailbox;
IMAPStorage& fStorage;
ConnectionReader& fConnectionReader;
};
class MailboxSelectHandler : public IMAPMailboxCommand {
public:
MailboxSelectHandler(IMAPMailbox& mailbox);
BString Command();
bool Handle(const BString& response);
void SetTo(const char* mailboxName)
{ fMailboxName = mailboxName; }
int32 NextUID() { return fNextUID; }
int32 UIDValidity() { return fUIDValidity; }
private:
BString fMailboxName;
int32 fNextUID;
int32 fUIDValidity;
};
class CapabilityHandler : public IMAPCommand {
public:
CapabilityHandler();
BString Command();
bool Handle(const BString& response);
BString& Capabilities();
private:
BString fCapabilities;
};
struct MinMessage {
MinMessage();
int32 uid;
int32 flags;
};
typedef std::vector<MinMessage> MinMessageList;
class FetchMinMessageCommand : public IMAPMailboxCommand {
public:
FetchMinMessageCommand(IMAPMailbox& mailbox,
int32 message, MinMessageList* list,
BPositionIO** data);
FetchMinMessageCommand(IMAPMailbox& mailbox,
int32 firstMessage, int32 lastMessage,
MinMessageList* list, BPositionIO** data);
BString Command();
bool Handle(const BString& response);
static bool ParseMinMessage(const BString& response,
MinMessage& minMessage);
static int32 ExtractFlags(const BString& response);
private:
int32 fMessage;
int32 fEndMessage;
MinMessageList* fMinMessageList;
BPositionIO** fData;
};
class FetchMessageListCommand : public IMAPMailboxCommand {
public:
FetchMessageListCommand(IMAPMailbox& mailbox,
MinMessageList* list, int32 nextId);
BString Command();
bool Handle(const BString& response);
private:
MinMessageList* fMinMessageList;
int32 fNextId;
};
class FetchMessageCommand : public IMAPMailboxCommand {
public:
FetchMessageCommand(IMAPMailbox& mailbox,
int32 message, BPositionIO* data,
int32 fetchBodyLimit = -1);
/*! Fetch multiple message within a range. */
FetchMessageCommand(IMAPMailbox& mailbox,
int32 firstMessage, int32 lastMessage,
int32 fetchBodyLimit = -1);
~FetchMessageCommand();
BString Command();
bool Handle(const BString& response);
private:
int32 fMessage;
int32 fEndMessage;
BPositionIO* fOutData;
int32 fFetchBodyLimit;
int32 fUnhandled;
};
class FetchBodyCommand : public IMAPMailboxCommand {
public:
/*! takes ownership of the data */
FetchBodyCommand(IMAPMailbox& mailbox,
int32 message, BPositionIO* data);
~FetchBodyCommand();
BString Command();
bool Handle(const BString& response);
private:
int32 fMessage;
BPositionIO* fOutData;
};
class SetFlagsCommand : public IMAPMailboxCommand {
public:
SetFlagsCommand(IMAPMailbox& mailbox,
int32 message, int32 flags);
BString Command();
bool Handle(const BString& response);
static BString GenerateFlagList(int32 flags);
private:
int32 fMessage;
int32 fFlags;
};
class AppendCommand : public IMAPMailboxCommand {
public:
AppendCommand(IMAPMailbox& mailbox,
BPositionIO& message, off_t size,
int32 flags, time_t time);
BString Command();
bool Handle(const BString& response);
private:
BPositionIO& fMessageData;
off_t fDataSize;
int32 fFlags;
time_t fTime;
};
class ExistsHandler : public IMAPMailboxCommand {
public:
ExistsHandler(IMAPMailbox& mailbox);
bool Handle(const BString& response);
};
/*! Just send a expunge command to delete kDeleted flagged messages. The
response is handled by the unsolicited ExpungeHandler which is installed all
the time. */
class ExpungeCommmand : public IMAPMailboxCommand {
public:
ExpungeCommmand(IMAPMailbox& mailbox);
BString Command();
bool Handle(const BString& response);
};
class ExpungeHandler : public IMAPMailboxCommand {
public:
ExpungeHandler(IMAPMailbox& mailbox);
bool Handle(const BString& response);
};
class FlagsHandler : public IMAPMailboxCommand {
public:
FlagsHandler(IMAPMailbox& mailbox);
bool Handle(const BString& response);
};
typedef std::vector<BString> StringList;
class ListCommand : public IMAPCommand {
public:
BString Command();
bool Handle(const BString& response);
const StringList& FolderList();
static bool ParseList(const char* command,
const BString& response, StringList& list);
private:
StringList fFolders;
};
class ListSubscribedCommand : public IMAPCommand {
public:
BString Command();
bool Handle(const BString& response);
const StringList& FolderList();
private:
StringList fFolders;
};
class SubscribeCommand : public IMAPCommand {
public:
SubscribeCommand(const char* mailboxName);
BString Command();
bool Handle(const BString& response);
private:
BString fMailboxName;
};
class UnsubscribeCommand : public IMAPCommand {
public:
UnsubscribeCommand(const char* mailboxName);
BString Command();
bool Handle(const BString& response);
private:
BString fMailboxName;
};
class GetQuotaCommand : public IMAPCommand {
public:
GetQuotaCommand(const char* mailboxName = "");
BString Command();
bool Handle(const BString& response);
uint64 UsedStorage();
uint64 TotalStorage();
private:
BString fMailboxName;
uint64 fUsedStorage;
uint64 fTotalStorage;
};
#endif // IMAP_HANDLER_H
@@ -43,7 +43,6 @@ IMAPMailbox::IMAPMailbox(IMAPStorage& storage)
fFetchBodyLimit(0)
{
fHandlerList.AddItem(&fCapabilityHandler);
fIMAPMailboxListener = &fNULLListener;
}
@@ -64,27 +63,6 @@ IMAPMailbox::SetListener(IMAPMailboxListener* listener)
}
status_t
IMAPMailbox::SelectMailbox(const char* mailbox)
{
TRACE("SELECT %s\n", mailbox);
fMailboxSelectHandler.SetTo(mailbox);
status_t status = ProcessCommand(&fMailboxSelectHandler);
if (status != B_OK)
return status;
fSelectedMailbox = mailbox;
if (fCapabilityHandler.Capabilities() != "")
return status;
//else get them
status = ProcessCommand(fCapabilityHandler.Command());
if (status != B_OK)
return status;
return B_OK;
}
BString
IMAPMailbox::Mailbox()
{
@@ -239,7 +217,7 @@ IMAPMailbox::FetchMessages(int32 firstMessage, int32 lastMessage)
status_t
IMAPMailbox::FetchBody(int32 messageNumber)
{
if (fStorage.BodyFetched(MessageNumberToUID(messageNumber)))
if (fStorage.IsBodyFetched(MessageNumberToUID(messageNumber)))
return B_BAD_VALUE;
BPositionIO* file;
@@ -30,7 +30,7 @@ public:
class IMAPStorage;
class IMAPMailbox : public IMAPProtocol {
class IMAPMailbox : public IMAP::Protocol {
public:
IMAPMailbox(IMAPStorage& storage);
~IMAPMailbox();
@@ -39,7 +39,6 @@ public:
IMAPMailboxListener& Listener() { return *fIMAPMailboxListener; }
/*! Select mailbox and sync with the storage. */
status_t SelectMailbox(const char* mailbox);
BString Mailbox();
status_t Sync();
@@ -68,7 +67,7 @@ public:
int32 GetCurrentMessageCount()
{ return fMessageList.size(); }
const MinMessageList& GetMessageList() { return fMessageList; }
const IMAP::MinMessageList& GetMessageList() { return fMessageList; }
IMAPStorage& GetStorage() { return fStorage; }
int32 UIDToMessageNumber(int32 uid);
@@ -78,17 +77,15 @@ public:
private:
void _InstallUnsolicitedHandler(bool install);
MinMessageList fMessageList;
IMAP::MinMessageList fMessageList;
IMAPStorage& fStorage;
IMAPMailboxListener* fIMAPMailboxListener;
IMAPMailboxListener fNULLListener;
MailboxSelectHandler fMailboxSelectHandler;
CapabilityHandler fCapabilityHandler;
ExistsHandler fExistsHandler;
ExpungeHandler fExpungeHandler;
FlagsHandler fFlagsHandler;
IMAP::ExistsHandler fExistsHandler;
IMAP::ExpungeHandler fExpungeHandler;
// FlagsHandler fFlagsHandler;
int32 fWatching;
@@ -1,139 +0,0 @@
/*
* Copyright 2010, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "IMAPParser.h"
#include <stdlib.h>
BString
IMAPParser::ExtractStringAfter(const BString& string, const char* after)
{
int32 pos = string.FindFirst(after);
if (pos < 0)
return "";
BString extractedString = string;
extractedString.Remove(0, pos + strlen(after));
return extractedString.Trim();
}
BString
IMAPParser::ExtractFirstPrimitive(const BString& string)
{
BString extractedString = string;
extractedString.Trim();
int32 end = extractedString.FindFirst(" ");
if (end < 0)
return extractedString;
return extractedString.Truncate(end);
}
BString
IMAPParser::ExtractBetweenBrackets(const BString& string, const char* start,
const char* end)
{
BString outString = string;
int32 startPos = outString.FindFirst(start);
if (startPos < 0)
return "";
outString.Remove(0, startPos + 1);
int32 searchPos = 0;
while (true) {
int32 endPos = outString.FindFirst(end, searchPos);
if (endPos < 0)
return "";
int32 nextStartPos = outString.FindFirst(start, searchPos);
if (nextStartPos < 0 || nextStartPos > endPos)
return outString.Truncate(endPos);
searchPos = endPos + 1;
}
return "";
};
BString
IMAPParser::ExtractNextElement(const BString& string)
{
if (string[0] == '(')
return ExtractBetweenBrackets(string, "(", ")");
else if (string[0] == '[')
return ExtractBetweenBrackets(string, "[", "]");
else if (string[0] == '{')
return ExtractBetweenBrackets(string, "{", "}");
else
return ExtractFirstPrimitive(string);
return "";
}
BString
IMAPParser::ExtractElementAfter(const BString& string, const char* after)
{
BString rest = ExtractStringAfter(string, after);
if (rest == "")
return rest;
return ExtractNextElement(rest);
}
BString
IMAPParser::RemovePrimitiveFromLeft(BString& string)
{
BString extracted;
string.Trim();
int32 end = string.FindFirst(" ");
if (end < 0) {
extracted = string;
string = "";
} else {
string.MoveInto(extracted, 0, end);
string.Trim();
}
return extracted;
}
int
IMAPParser::RemoveIntegerFromLeft(BString& string)
{
BString extracted = RemovePrimitiveFromLeft(string);
return atoi(extracted);
}
bool
IMAPParser::RemoveUntagedFromLeft(BString& string, const char* keyword,
int32& number)
{
BString result = RemovePrimitiveFromLeft(string);
if (result != "*")
return false;
number = RemoveIntegerFromLeft(string);
if (number == 0)
return false;
result = RemovePrimitiveFromLeft(string);
if (result != keyword)
return false;
return true;
}
bool
IMAPParser::ExtractUntagedFromLeft(const BString& string, const char* keyword,
int32& number)
{
//TODO: could be more efficient without copy the string
BString copy = string;
return RemoveUntagedFromLeft(copy, keyword, number);
}
@@ -1,35 +0,0 @@
/*
* Copyright 2010, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_PARSER_H
#define IMAP_PARSER_H
#include <String.h>
namespace IMAPParser {
BString ExtractStringAfter(const BString& string, const char* after);
BString ExtractFirstPrimitive(const BString& string);
BString ExtractBetweenBrackets(const BString& string, const char* start,
const char* end);
BString ExtractNextElement(const BString& string);
BString ExtractElementAfter(const BString& string, const char* after);
BString RemovePrimitiveFromLeft(BString& string);
int RemoveIntegerFromLeft(BString& string);
// remove the part like "* 234 FETCH " where FETCH is the keyword
bool RemoveUntagedFromLeft(BString& string, const char* keyword,
int32& number);
bool ExtractUntagedFromLeft(const BString& string, const char* keyword,
int32& number);
}
#endif // IMAP_PARSER_H
@@ -1,433 +0,0 @@
/*
* Copyright 2010-2011, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "IMAPProtocol.h"
#include "IMAPHandler.h"
#include "IMAPParser.h"
#define DEBUG_IMAP_PROTOCOL
#ifdef DEBUG_IMAP_PROTOCOL
# include <stdio.h>
# define TRACE(x...) printf(x)
#else
# define TRACE(x...) ;
#endif
ConnectionReader::ConnectionReader(ServerConnection* connection)
:
fServerConnection(connection)
{
}
status_t
ConnectionReader::GetNextLine(BString& line, bigtime_t timeout,
int32 maxUnfinishedLine)
{
line.SetTo((const char*)NULL, 0);
while (true) {
status_t status = _GetNextDataBunch(line, timeout);
if (status == B_OK)
return status;
if (status == B_NAME_NOT_FOUND) {
if (maxUnfinishedLine < 0 || line.Length() < maxUnfinishedLine)
continue;
else
return status;
}
return status;
}
return B_ERROR;
}
status_t
ConnectionReader::FinishLine(BString& line)
{
while (true) {
status_t status = _GetNextDataBunch(line, B_INFINITE_TIMEOUT);
if (status == B_OK)
return status;
if (status == B_NAME_NOT_FOUND)
continue;
return status;
}
return B_ERROR;
}
status_t
ConnectionReader::ReadToFile(int32 size, BPositionIO* out)
{
const int32 kBunchSize = 1024; // 1Kb
char buffer[kBunchSize];
int32 readSize = size - fStringBuffer.Length();
int32 readed = fStringBuffer.Length();
if (readSize < 0) {
readed = size;
}
out->Write(fStringBuffer.String(), readed);
fStringBuffer.Remove(0, readed);
while (readSize > 0) {
int32 bunchSize = readSize < kBunchSize ? readSize : kBunchSize;
int nReaded = fServerConnection->Read(buffer, bunchSize);
if (nReaded < 0)
return B_ERROR;
readSize -= nReaded;
out->Write(buffer, nReaded);
}
return B_OK;
}
status_t
ConnectionReader::_GetNextDataBunch(BString& line, bigtime_t timeout,
uint32 maxNewLength)
{
if (_ExtractTillEndOfLine(line))
return B_OK;
char buffer[maxNewLength];
if (timeout != B_INFINITE_TIMEOUT) {
status_t status = fServerConnection->WaitForData(timeout);
if (status != B_OK)
return status;
}
int nReaded = fServerConnection->Read(buffer, maxNewLength);
if (nReaded <= 0)
return B_ERROR;
fStringBuffer.SetTo(buffer, nReaded);
if (_ExtractTillEndOfLine(line))
return B_OK;
return B_NAME_NOT_FOUND;
}
bool
ConnectionReader::_ExtractTillEndOfLine(BString& out)
{
int32 endPos = fStringBuffer.FindFirst('\n');
if (endPos == B_ERROR) {
endPos = fStringBuffer.FindFirst(xEOF);
if (endPos == B_ERROR) {
out += fStringBuffer;
fStringBuffer.SetTo((const char*)NULL, 0);
return false;
}
}
out.Append(fStringBuffer, endPos + 1);
fStringBuffer.Remove(0, endPos + 1);
return true;
}
// #pragma mark -
IMAPProtocol::IMAPProtocol()
:
fServerConnection(&fOwnServerConnection),
fConnectionReader(fServerConnection),
fCommandID(0),
fStopNow(0),
fIsConnected(false)
{
}
IMAPProtocol::IMAPProtocol(IMAPProtocol& connection)
:
fServerConnection(connection.fServerConnection),
fConnectionReader(fServerConnection),
fCommandID(0),
fStopNow(0),
fIsConnected(false)
{
}
IMAPProtocol::~IMAPProtocol()
{
for (int32 i = 0; i < fAfterQuackCommands.CountItems(); i++)
delete fAfterQuackCommands.ItemAt(i);
}
void
IMAPProtocol::SetStopNow()
{
atomic_set(&fStopNow, 1);
}
bool
IMAPProtocol::StopNow()
{
return (atomic_get(&fStopNow) != 0);
}
status_t
IMAPProtocol::Connect(const char* server, const char* username,
const char* password, bool useSSL, int32 port)
{
TRACE("Connect\n");
status_t status = B_ERROR;
if (useSSL) {
if (port >= 0)
status = fServerConnection->ConnectSSL(server, port);
else
status = fServerConnection->ConnectSSL(server);
} else {
if (port >= 0)
status = fServerConnection->ConnectSocket(server, port);
else
status = fServerConnection->ConnectSocket(server);
}
if (status != B_OK)
return status;
TRACE("Login\n");
fIsConnected = true;
BString command = "LOGIN ";
command << "\"" << username << "\" ";
command << "\"" << password << "\"";
status = ProcessCommand(command);
if (status != B_OK) {
_Disconnect();
return status;
}
return B_OK;
}
status_t
IMAPProtocol::Disconnect()
{
ProcessCommand("LOGOUT");
return _Disconnect();
}
bool
IMAPProtocol::IsConnected()
{
return fIsConnected;
}
ConnectionReader&
IMAPProtocol::GetConnectionReader()
{
return fConnectionReader;
}
status_t
IMAPProtocol::SendRawCommand(const char* command)
{
static char cmd[256];
::sprintf(cmd, "%s"CRLF, command);
int32 commandLength = strlen(cmd);
if (fServerConnection->Write(cmd, commandLength) != commandLength)
return B_ERROR;
return B_OK;
}
int32
IMAPProtocol::SendRawData(const char* buffer, uint32 nBytes)
{
return fServerConnection->Write(buffer, nBytes);
}
status_t
IMAPProtocol::AddAfterQuakeCommand(IMAPCommand* command)
{
return fAfterQuackCommands.AddItem(command);
}
status_t
IMAPProtocol::ProcessCommand(IMAPCommand* command, bigtime_t timeout)
{
status_t status = _ProcessCommandWithoutAfterQuake(command, timeout);
ProcessAfterQuacks(timeout);
return status;
}
status_t
IMAPProtocol::ProcessCommand(const char* command, bigtime_t timeout)
{
status_t status = _ProcessCommandWithoutAfterQuake(command, timeout);
ProcessAfterQuacks(timeout);
return status;
}
status_t
IMAPProtocol::SendCommand(const char* command, int32 commandID)
{
if (strlen(command) + 10 > 256)
return B_NO_MEMORY;
static char cmd[256];
::sprintf(cmd, "A%.7" B_PRId32 " %s"CRLF, commandID, command);
TRACE("_SendCommand: %s\n", cmd);
int commandLength = strlen(cmd);
if (fServerConnection->Write(cmd, commandLength) != commandLength) {
// we might lost the connection, clear the connection state
_Disconnect();
return B_ERROR;
}
fOngoingCommands.push_back(commandID);
return B_OK;
}
status_t
IMAPProtocol::HandleResponse(int32 commandID, bigtime_t timeout,
bool disconnectOnTimeout)
{
status_t commandStatus = B_ERROR;
bool done = false;
while (done != true) {
BString line;
status_t status = fConnectionReader.GetNextLine(line, timeout);
if (status != B_OK) {
// we might lost the connection, clear the connection state
if (status != B_TIMED_OUT) {
TRACE("S:read error %s", line.String());
_Disconnect();
} else if (disconnectOnTimeout) {
_Disconnect();
}
return status;
}
//TRACE("S: %s", line.String());
bool handled = false;
for (int i = 0; i < fHandlerList.CountItems(); i++) {
if (fHandlerList.ItemAt(i)->Handle(line) == true) {
handled = true;
break;
}
}
if (handled == true)
continue;
for (std::vector<int32>::iterator it = fOngoingCommands.begin();
it != fOngoingCommands.end(); it++) {
static char idString[8];
::sprintf(idString, "A%.7" B_PRId32, *it);
if (line.FindFirst(idString) >= 0) {
if (*it == commandID) {
BString result = IMAPParser::ExtractElementAfter(line,
idString);
if (result == "OK")
commandStatus = B_OK;
else {
fCommandError = IMAPParser::ExtractStringAfter(line,
idString);
TRACE("Command Error %s\n", fCommandError.String());
}
}
fOngoingCommands.erase(it);
break;
}
}
if (fOngoingCommands.size() == 0)
done = true;
TRACE("Unhandled S: %s", line.String());
}
return commandStatus;
}
void
IMAPProtocol::ProcessAfterQuacks(bigtime_t timeout)
{
while (fAfterQuackCommands.CountItems() != 0) {
IMAPCommand* currentCommand = fAfterQuackCommands.RemoveItemAt(0);
_ProcessCommandWithoutAfterQuake(currentCommand, timeout);
delete currentCommand;
}
}
int32
IMAPProtocol::NextCommandID()
{
fCommandID++;
return fCommandID;
}
status_t
IMAPProtocol::_ProcessCommandWithoutAfterQuake(IMAPCommand* command,
bigtime_t timeout)
{
BString cmd = command->Command();
if (cmd == "")
return B_BAD_VALUE;
if (!fHandlerList.AddItem(command, 0))
return B_NO_MEMORY;
status_t status = _ProcessCommandWithoutAfterQuake(cmd, timeout);
fHandlerList.RemoveItem(command);
return status;
}
status_t
IMAPProtocol::_ProcessCommandWithoutAfterQuake(const char* command,
bigtime_t timeout)
{
int32 commandID = NextCommandID();
status_t status = SendCommand(command, commandID);
if (status != B_OK)
return status;
return HandleResponse(commandID, timeout);
}
status_t
IMAPProtocol::_Disconnect()
{
fOngoingCommands.clear();
fIsConnected = false;
return fOwnServerConnection.Disconnect();
}
@@ -1,129 +0,0 @@
/*
* Copyright 2001-2002, Haiku Inc. All Rights Reserved.
* Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef IMAP_PROTOCOL_H
#define IMAP_PROTOCOL_H
#include "ServerConnection.h"
#include <vector>
#include <DataIO.h>
#include <ObjectList.h>
#include <OS.h>
#include <String.h>
#define CRLF "\r\n"
#define xEOF 236
const bigtime_t kIMAP4ClientTimeout = 1000000 * 60; // 60 sec
class ConnectionReader {
public:
ConnectionReader(ServerConnection* connection);
/*! Try to read line. If no end of line is found at least
minUnfinishedLine characters are returned. */
status_t GetNextLine(BString& line,
bigtime_t timeout = kIMAP4ClientTimeout,
int32 maxUnfinishedLine = -1);
/*! Read data and append it to line till the end of file is
reached. */
status_t FinishLine(BString& line);
status_t ReadToFile(int32 size, BPositionIO* out);
private:
/*! Try to read till the end of line is reached. To do so maximal
maxNewLength bytes are read from the server if needed. */
status_t _GetNextDataBunch(BString& line,
bigtime_t timeout,
uint32 maxNewLength = 256);
bool _ExtractTillEndOfLine(BString& out);
ServerConnection* fServerConnection;
BString fStringBuffer;
};
class IMAPCommand;
typedef BObjectList<IMAPCommand> IMAPCommandList;
class IMAPProtocol {
public:
IMAPProtocol();
/*! Use the server connection from another protocol. */
IMAPProtocol(IMAPProtocol& connection);
~IMAPProtocol();
/*! Indicates that the current action should be interrupted because
we are going to be deleted. */
void SetStopNow();
bool StopNow();
status_t Connect(const char* server,
const char* username, const char* password,
bool useSSL = true, int32 port = -1);
status_t Disconnect();
bool IsConnected();
ConnectionReader& GetConnectionReader();
status_t SendRawCommand(const char* command);
int32 SendRawData(const char* buffer, uint32 nBytes);
status_t AddAfterQuakeCommand(IMAPCommand* command);
const BString& CommandError() { return fCommandError; }
protected:
/*! Install a temporary handler at first position in the handler
list. */
status_t ProcessCommand(IMAPCommand* command,
bigtime_t timeout = kIMAP4ClientTimeout);
status_t ProcessCommand(const char* command,
bigtime_t timeout = kIMAP4ClientTimeout);
status_t SendCommand(const char* command,
int32 commandId);
status_t HandleResponse(int32 commandId,
bigtime_t timeout = kIMAP4ClientTimeout,
bool disconnectOnTimeout = true);
void ProcessAfterQuacks(bigtime_t timeout);
int32 NextCommandID();
ServerConnection* fServerConnection;
ServerConnection fOwnServerConnection;
ConnectionReader fConnectionReader;
IMAPCommandList fHandlerList;
IMAPCommandList fAfterQuackCommands;
private:
/*! Same as ProccessCommand but AfterShockCommands are not send. */
status_t _ProcessCommandWithoutAfterQuake(
IMAPCommand* command, bigtime_t timeout);
status_t _ProcessCommandWithoutAfterQuake(
const char* command,
bigtime_t timeout = kIMAP4ClientTimeout);
status_t _Disconnect();
int32 fCommandID;
std::vector<int32> fOngoingCommands;
BString fCommandError;
int32 fStopNow;
bool fIsConnected;
};
#endif // IMAP_PROTOCOL_H
@@ -36,7 +36,7 @@ IMAPMailboxSync::Sync(IMAPStorage& storage, IMAPMailbox& mailbox)
const MinMessageList& messages = mailbox.GetMessageList();
for (MailEntryMap::const_iterator it = files.begin(); it != files.end();
it++) {
it++) {
const StorageMailEntry& mailEntry = (*it).second;
bool found = false;
for (unsigned int a = 0; a < messages.size(); a++) {
@@ -352,7 +352,7 @@ IMAPStorage::GetEntryForRef(const node_ref& ref)
bool
IMAPStorage::BodyFetched(int32 uid)
IMAPStorage::IsBodyFetched(int32 uid)
{
MailEntryMap::iterator it = fMailEntryMap.find(uid);
if (it == fMailEntryMap.end())
@@ -365,9 +365,8 @@ IMAPStorage::BodyFetched(int32 uid)
char buffer[B_MIME_TYPE_LENGTH];
BNodeInfo info(&node);
info.GetType(buffer);
if (strcmp(buffer, B_MAIL_TYPE) == 0)
return true;
return false;
return strcmp(buffer, B_MAIL_TYPE) == 0;
}
@@ -16,17 +16,6 @@
#include <Path.h>
#include <String.h>
#include "IMAPHandler.h"
enum FileFlags {
kSeen = 0x01,
kAnswered = 0x02,
kFlagged = 0x04,
kDeleted = 0x08,
kDraft = 0x10
};
struct StorageMailEntry {
int32 uid;
@@ -73,7 +62,7 @@ public:
status_t SetCompleteMessageSize(int32 uid, int32 size);
bool BodyFetched(int32 uid);
bool IsBodyFetched(int32 uid);
StorageMailEntry* GetEntryForRef(const node_ref& ref);
int32 RefToUID(const entry_ref& ref);
@@ -0,0 +1,548 @@
/*
* Copyright 2010-2011, Haiku Inc. All Rights Reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "Protocol.h"
#include "Commands.h"
#define DEBUG_IMAP_PROTOCOL
#ifdef DEBUG_IMAP_PROTOCOL
# include <stdio.h>
# define TRACE(...) printf(__VA_ARGS__)
#else
# define TRACE(...) ;
#endif
namespace IMAP {
ConnectionReader::ConnectionReader()
:
fSocket(NULL)
{
}
void
ConnectionReader::SetTo(BSocket& socket)
{
fSocket = &socket;
fBufferedSocket = new BBufferedDataIO(socket, 32768, false, true);
}
status_t
ConnectionReader::GetNextLine(BString& line, bigtime_t timeout,
int32 maxUnfinishedLine)
{
line.SetTo((const char*)NULL, 0);
while (true) {
status_t status = _GetNextDataBunch(line, timeout);
if (status == B_OK)
return status;
if (status == B_NAME_NOT_FOUND) {
if (maxUnfinishedLine < 0 || line.Length() < maxUnfinishedLine)
continue;
else
return status;
}
return status;
}
return B_ERROR;
}
status_t
ConnectionReader::FinishLine(BString& line)
{
while (true) {
status_t status = _GetNextDataBunch(line, B_INFINITE_TIMEOUT);
if (status == B_OK)
return status;
if (status == B_NAME_NOT_FOUND)
continue;
return status;
}
return B_ERROR;
}
status_t
ConnectionReader::ReadToStream(int32 size, BDataIO& out)
{
const int32 kBunchSize = 1024; // 1Kb
char buffer[kBunchSize];
int32 readSize = size - fStringBuffer.Length();
int32 readed = fStringBuffer.Length();
if (readSize < 0) {
readed = size;
}
out.Write(fStringBuffer.String(), readed);
fStringBuffer.Remove(0, readed);
while (readSize > 0) {
int32 bunchSize = readSize < kBunchSize ? readSize : kBunchSize;
int nReaded = fBufferedSocket->Read(buffer, bunchSize);
if (nReaded < 0)
return B_ERROR;
readSize -= nReaded;
out.Write(buffer, nReaded);
}
return B_OK;
}
status_t
ConnectionReader::_GetNextDataBunch(BString& line, bigtime_t timeout,
uint32 maxNewLength)
{
if (_ExtractTillEndOfLine(line))
return B_OK;
char buffer[maxNewLength];
//
// if (timeout != B_INFINITE_TIMEOUT) {
// status_t status = fSocket->WaitForReadable(timeout);
// if (status != B_OK)
// return status;
// }
int nReaded = fBufferedSocket->Read(buffer, maxNewLength);
if (nReaded <= 0)
return B_ERROR;
fStringBuffer.SetTo(buffer, nReaded);
if (_ExtractTillEndOfLine(line))
return B_OK;
return B_NAME_NOT_FOUND;
}
bool
ConnectionReader::_ExtractTillEndOfLine(BString& out)
{
int32 endPos = fStringBuffer.FindFirst('\n');
if (endPos == B_ERROR) {
endPos = fStringBuffer.FindFirst(xEOF);
if (endPos == B_ERROR) {
out += fStringBuffer;
fStringBuffer.SetTo((const char*)NULL, 0);
return false;
}
}
out.Append(fStringBuffer, endPos + 1);
fStringBuffer.Remove(0, endPos + 1);
return true;
}
// #pragma mark -
Protocol::Protocol()
:
fSocket(NULL),
fBufferedSocket(NULL),
fCommandID(0),
fStopNow(0),
fIsConnected(false)
{
}
Protocol::~Protocol()
{
for (int32 i = 0; i < fAfterQuackCommands.CountItems(); i++)
delete fAfterQuackCommands.ItemAt(i);
delete fSocket;
delete fBufferedSocket;
}
void
Protocol::SetStopNow()
{
atomic_set(&fStopNow, 1);
}
bool
Protocol::StopNow()
{
return (atomic_get(&fStopNow) != 0);
}
status_t
Protocol::Connect(const BNetworkAddress& address, const char* username,
const char* password, bool useSSL)
{
TRACE("Connect\n");
if (useSSL)
fSocket = new(std::nothrow) BSecureSocket(address);
else
fSocket = new(std::nothrow) BSocket(address);
if (fSocket == NULL)
return B_NO_MEMORY;
status_t status = fSocket->InitCheck();
if (status != B_OK)
return status;
fBufferedSocket = new(std::nothrow) BBufferedDataIO(*fSocket, 32768, false,
true);
if (fBufferedSocket == NULL)
return B_NO_MEMORY;
TRACE("Login\n");
fConnectionReader.SetTo(*fSocket);
fIsConnected = true;
LoginCommand login(username, password);
status = ProcessCommand(login);
if (status != B_OK) {
_Disconnect();
return status;
}
_ParseCapabilities(login.Capabilities());
return B_OK;
}
status_t
Protocol::Disconnect()
{
if (IsConnected()) {
RawCommand command("LOGOUT");
ProcessCommand(command);
}
return _Disconnect();
}
bool
Protocol::IsConnected()
{
return fIsConnected;
}
status_t
Protocol::GetFolders(FolderList& folders)
{
StringList allFolders;
status_t status = _GetAllFolders(allFolders);
if (status != B_OK)
return status;
StringList subscribedFolders;
status = _GetSubscribedFolders(subscribedFolders);
if (status != B_OK)
return status;
for (unsigned int i = 0; i < allFolders.size(); i++) {
FolderInfo info;
info.folder = allFolders[i];
for (unsigned int a = 0; a < subscribedFolders.size(); a++) {
if (allFolders[i] == subscribedFolders[a]
|| allFolders[i].ICompare("INBOX") == 0) {
info.subscribed = true;
break;
}
}
folders.push_back(info);
}
// you could be subscribed to a folder which not exist currently, add them:
for (unsigned int a = 0; a < subscribedFolders.size(); a++) {
bool isInlist = false;
for (unsigned int i = 0; i < allFolders.size(); i++) {
if (subscribedFolders[a] == allFolders[i]) {
isInlist = true;
break;
}
}
if (isInlist)
continue;
FolderInfo info;
info.folder = subscribedFolders[a];
info.subscribed = true;
folders.push_back(info);
}
return B_OK;
}
status_t
Protocol::SubscribeFolder(const char* folder)
{
SubscribeCommand command(folder);
return ProcessCommand(command);
}
status_t
Protocol::UnsubscribeFolder(const char* folder)
{
UnsubscribeCommand command(folder);
return ProcessCommand(command);
}
status_t
Protocol::SelectMailbox(const char* mailbox)
{
TRACE("SELECT %s\n", mailbox);
SelectCommand command(mailbox);
status_t status = ProcessCommand(command);
if (status != B_OK)
return status;
if (fCapabilities.IsEmpty()) {
CapabilityHandler capabilityHandler;
status = ProcessCommand(capabilityHandler);
if (status != B_OK)
return status;
_ParseCapabilities(capabilityHandler.Capabilities());
}
fMailbox = mailbox;
return B_OK;
}
status_t
Protocol::GetQuota(uint64& used, uint64& total)
{
if (!Capabilities().Contains("QUOTA"))
return B_ERROR;
GetQuotaCommand quotaCommand;
status_t status = ProcessCommand(quotaCommand);
if (status != B_OK)
return status;
used = quotaCommand.UsedStorage();
total = quotaCommand.TotalStorage();
return B_OK;
}
status_t
Protocol::SendCommand(const char* command)
{
return SendCommand(0, command);
}
status_t
Protocol::SendCommand(int32 id, const char* command)
{
char buffer[2048];
int32 length;
if (id > 0)
length = snprintf(buffer, sizeof(buffer), "A%.7ld %s\r\n", id, command);
else
length = snprintf(buffer, sizeof(buffer), "%s\r\n", command);
ssize_t bytesWritten = fSocket->Write(buffer, length);
if (bytesWritten < 0)
return bytesWritten;
return bytesWritten == length ? B_OK : B_ERROR;
}
int32
Protocol::SendData(const char* buffer, uint32 length)
{
return fSocket->Write(buffer, length);
}
status_t
Protocol::AddAfterQuakeCommand(Command* command)
{
return fAfterQuackCommands.AddItem(command);
}
status_t
Protocol::ProcessCommand(Command& command, bigtime_t timeout)
{
status_t status = _ProcessCommandWithoutAfterQuake(command, timeout);
ProcessAfterQuacks(timeout);
return status;
}
status_t
Protocol::HandleResponse(bigtime_t timeout, bool disconnectOnTimeout)
{
status_t commandStatus = B_OK;
IMAP::ResponseParser parser(fConnectionReader);
IMAP::Response response;
bool done = false;
while (!done) {
try {
status_t status = parser.NextResponse(response, timeout);
if (status != B_OK) {
// we might have lost the connection, clear the connection state
if (status != B_TIMED_OUT || disconnectOnTimeout)
_Disconnect();
return status;
}
if (response.IsUntagged() || response.IsContinuation()) {
bool handled = false;
for (int i = 0; i < fHandlerList.CountItems(); i++) {
if (fHandlerList.ItemAt(i)->HandleUntagged(response)) {
handled = true;
break;
}
}
if (!handled)
printf("Unhandled S: %s\n", response.ToString().String());
} else {
CommandIDMap::iterator found
= fOngoingCommands.find(response.Tag());
if (found != fOngoingCommands.end()) {
status_t status = found->second->HandleTagged(response);
if (status != B_OK)
commandStatus = status;
fOngoingCommands.erase(found);
} else
printf("Unknown tag S: %s\n", response.ToString().String());
}
} catch (IMAP::ParseException& exception) {
printf("Error during parsing: %s\n", exception.Message());
}
if (fOngoingCommands.size() == 0)
done = true;
}
return commandStatus;
}
void
Protocol::ProcessAfterQuacks(bigtime_t timeout)
{
while (fAfterQuackCommands.CountItems() != 0) {
Command* currentCommand = fAfterQuackCommands.RemoveItemAt(0);
_ProcessCommandWithoutAfterQuake(*currentCommand, timeout);
delete currentCommand;
}
}
int32
Protocol::NextCommandID()
{
fCommandID++;
return fCommandID;
}
status_t
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(timeout);
}
if (handler != NULL)
fHandlerList.RemoveItem(handler);
return status;
}
status_t
Protocol::_Disconnect()
{
fOngoingCommands.clear();
fIsConnected = false;
delete fSocket;
fSocket = NULL;
return B_OK;
}
status_t
Protocol::_GetAllFolders(StringList& folders)
{
ListCommand listCommand;
status_t status = ProcessCommand(listCommand);
if (status != B_OK)
return status;
folders = listCommand.FolderList();
return status;
}
status_t
Protocol::_GetSubscribedFolders(StringList& folders)
{
ListSubscribedCommand listSubscribedCommand;
status_t status = ProcessCommand(listSubscribedCommand);
if (status != B_OK)
return status;
folders = listSubscribedCommand.FolderList();
return status;
}
void
Protocol::_ParseCapabilities(const ArgumentList& arguments)
{
fCapabilities.MakeEmpty();
for (int32 i = 0; i < arguments.CountItems(); i++) {
if (StringArgument* argument
= dynamic_cast<StringArgument*>(arguments.ItemAt(i)))
fCapabilities.AddItem(new StringArgument(*argument));
}
TRACE("capabilities: %s\n", fCapabilities.ToString().String());
}
} // namespace IMAP
@@ -0,0 +1,167 @@
/*
* Copyright 2001-2011, Haiku Inc. All Rights Reserved.
* Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
* Copyright 2010 Clemens Zeidler. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <map>
#include <BufferedDataIO.h>
#include <ObjectList.h>
#include <OS.h>
#include <SecureSocket.h>
#include <String.h>
#include "Commands.h"
#define xEOF 236
const bigtime_t kIMAP4ClientTimeout = 1000000 * 60; // 60 sec
namespace IMAP {
class Command;
class Handler;
typedef BObjectList<Command> CommandList;
typedef BObjectList<Handler> HandlerList;
typedef std::map<int32, Command*> CommandIDMap;
// TODO: throw this class away, and just use a BBufferedDataIO instead.
class ConnectionReader {
public:
ConnectionReader();
void SetTo(BSocket& socket);
/*! Try to read line. If no end of line is found at least
minUnfinishedLine characters are returned. */
status_t GetNextLine(BString& line,
bigtime_t timeout = kIMAP4ClientTimeout,
int32 maxUnfinishedLine = -1);
/*! Read data and append it to line till the end of file is
reached. */
status_t FinishLine(BString& line);
status_t ReadToStream(int32 size, BDataIO& out);
private:
/*! Try to read till the end of line is reached. To do so maximal
maxNewLength bytes are read from the server if needed. */
status_t _GetNextDataBunch(BString& line,
bigtime_t timeout,
uint32 maxNewLength = 256);
bool _ExtractTillEndOfLine(BString& out);
private:
BSocket* fSocket;
BBufferedDataIO* fBufferedSocket;
BString fStringBuffer;
};
class FolderInfo {
public:
FolderInfo()
:
subscribed(false)
{
}
BString folder;
bool subscribed;
};
typedef std::vector<FolderInfo> FolderList;
class Protocol {
public:
Protocol();
~Protocol();
/*! Indicates that the current action should be interrupted because
we are going to be deleted. */
void SetStopNow();
bool StopNow();
status_t Connect(const BNetworkAddress& address,
const char* username, const char* password,
bool useSSL = true);
status_t Disconnect();
bool IsConnected();
status_t SendCommand(const char* command);
status_t SendCommand(int32 id, const char* command);
ssize_t SendData(const char* buffer, uint32 length);
status_t GetFolders(FolderList& folders);
status_t SubscribeFolder(const char* folder);
status_t UnsubscribeFolder(const char* folder);
status_t SelectMailbox(const char* mailbox);
const BString& Mailbox() const { return fMailbox; }
status_t GetQuota(uint64& used, uint64& total);
/*! Install a temporary handler at first position in the handler
list. */
status_t ProcessCommand(Command& command,
bigtime_t timeout = kIMAP4ClientTimeout);
ArgumentList& Capabilities() { return fCapabilities; }
const ArgumentList& Capabilities() const { return fCapabilities; }
status_t AddAfterQuakeCommand(Command* command);
const BString& CommandError() { return fCommandError; }
protected:
status_t HandleResponse(
bigtime_t timeout = kIMAP4ClientTimeout,
bool disconnectOnTimeout = true);
void ProcessAfterQuacks(bigtime_t timeout);
int32 NextCommandID();
private:
/*! Same as ProccessCommand but AfterShockCommands are not send. */
status_t _ProcessCommandWithoutAfterQuake(
Command& command, bigtime_t timeout);
status_t _Disconnect();
status_t _GetAllFolders(StringList& folders);
status_t _GetSubscribedFolders(StringList& folders);
void _ParseCapabilities(
const ArgumentList& arguments);
protected:
BSocket* fSocket;
BBufferedDataIO* fBufferedSocket;
ConnectionReader fConnectionReader;
HandlerList fHandlerList;
CommandList fAfterQuackCommands;
ArgumentList fCapabilities;
private:
BString fMailbox;
int32 fCommandID;
CommandIDMap fOngoingCommands;
BString fCommandError;
vint32 fStopNow;
bool fIsConnected;
};
} // namespace IMAP
#endif // PROTOCOL_H
@@ -6,6 +6,19 @@
#include "Response.h"
#include <stdlib.h>
#include "Protocol.h"
// TODO: remove again once the ConnectionReader is out
#define TRACE_IMAP
#ifdef TRACE_IMAP
# define TRACE(...) printf(__VA_ARGS__)
#else
# define TRACE(...) ;
#endif
namespace IMAP {
@@ -82,11 +95,8 @@ ArgumentList::ListAt(int32 index) const
bool
ArgumentList::IsListAt(int32 index) const
{
if (index >= 0 && index < CountItems()) {
if (ListArgument* argument = dynamic_cast<ListArgument*>(ItemAt(index)))
return true;
}
return false;
return index >= 0 && index < CountItems()
&& dynamic_cast<ListArgument*>(ItemAt(index)) != NULL;
}
@@ -101,15 +111,15 @@ ArgumentList::IsListAt(int32 index, char kind) const
}
int32
ArgumentList::IntegerAt(int32 index) const
uint32
ArgumentList::NumberAt(int32 index) const
{
return atoi(StringAt(index).String());
return atoul(StringAt(index).String());
}
bool
ArgumentList::IsIntegerAt(int32 index) const
ArgumentList::IsNumberAt(int32 index) const
{
BString string = StringAt(index);
for (int32 i = 0; i < string.Length(); i++) {
@@ -209,11 +219,6 @@ ParseException::ParseException(const char* message)
}
ParseException::~ParseException()
{
}
// #pragma mark -
@@ -228,6 +233,19 @@ ExpectedParseException::ExpectedParseException(char expected, char instead)
// #pragma mark -
LiteralHandler::LiteralHandler()
{
}
LiteralHandler::~LiteralHandler()
{
}
// #pragma mark -
Response::Response()
:
fTag(0),
@@ -242,9 +260,12 @@ Response::~Response()
void
Response::SetTo(const char* line) throw(ParseException)
Response::Parse(ConnectionReader& reader, const char* line,
LiteralHandler* handler) throw(ParseException)
{
MakeEmpty();
fReader = &reader;
fLiteralHandler = handler;
fTag = 0;
fContinuation = false;
@@ -384,8 +405,24 @@ Response::ParseQuoted(ArgumentList& arguments, const char*& line)
void
Response::ParseLiteral(ArgumentList& arguments, const char*& line)
{
// TODO!
throw ParseException("Literals are not yet supported!");
Consume(line, '{');
off_t size = atoll(ExtractString(line));
Consume(line, '}');
Consume(line, '\r');
Consume(line, '\n');
if (fLiteralHandler != NULL)
fLiteralHandler->HandleLiteral(*fReader, size);
else {
// The default implementation just throws the data away
BMallocIO stream;
TRACE("Trying to read literal with %llu bytes.\n", size);
status_t status = fReader->ReadToStream(size, stream);
if (status == B_OK) {
TRACE("LITERAL: %-*s\n", (int)size, (char*)stream.Buffer());
} else
TRACE("Reading literal failed: %s\n", strerror(status));
}
}
@@ -401,6 +438,7 @@ Response::ExtractString(const char*& line)
{
const char* start = line;
// TODO: parse modified UTF-7 as described in RFC 3501, 5.1.3
while (line[0] != '\0') {
char c = line[0];
if (c <= ' ' || strchr("()[]{}\"", c) != NULL)
@@ -413,4 +451,52 @@ Response::ExtractString(const char*& line)
}
// #pragma mark -
ResponseParser::ResponseParser(ConnectionReader& reader)
:
fLiteralHandler(NULL)
{
SetTo(reader);
}
ResponseParser::~ResponseParser()
{
}
void
ResponseParser::SetTo(ConnectionReader& reader)
{
fReader = &reader;
}
void
ResponseParser::SetLiteralHandler(LiteralHandler* handler)
{
fLiteralHandler = handler;
}
status_t
ResponseParser::NextResponse(Response& response, bigtime_t timeout)
throw(ParseException)
{
BString line;
status_t status = fReader->GetNextLine(line, timeout);
if (status != B_OK) {
TRACE("S: read error %s", line.String());
return status;
}
TRACE("S: %s", line.String());
response.Parse(*fReader, line, fLiteralHandler);
return B_OK;
}
} // namespace IMAP
@@ -16,6 +16,7 @@ namespace IMAP {
class Argument;
class ConnectionReader;
class ArgumentList : public BObjectList<Argument> {
@@ -33,8 +34,8 @@ public:
bool IsListAt(int32 index) const;
bool IsListAt(int32 index, char kind) const;
int32 IntegerAt(int32 index) const;
bool IsIntegerAt(int32 index) const;
uint32 NumberAt(int32 index) const;
bool IsNumberAt(int32 index) const;
BString ToString() const;
};
@@ -82,7 +83,6 @@ class ParseException : public std::exception {
public:
ParseException();
ParseException(const char* message);
virtual ~ParseException();
const char* Message() const { return fMessage; }
@@ -91,7 +91,7 @@ protected:
};
class ExpectedParseException : ParseException {
class ExpectedParseException : public ParseException {
public:
ExpectedParseException(char expected,
char instead);
@@ -101,12 +101,24 @@ protected:
};
class LiteralHandler {
public:
LiteralHandler();
virtual ~LiteralHandler();
virtual void HandleLiteral(ConnectionReader& reader,
off_t length) = 0;
};
class Response : public ArgumentList {
public:
Response();
~Response();
void SetTo(const char* line) throw(ParseException);
void Parse(ConnectionReader& reader,
const char* line, LiteralHandler* handler)
throw(ParseException);
bool IsUntagged() const { return fTag == 0; }
int32 Tag() const { return fTag; }
@@ -128,11 +140,30 @@ protected:
BString ExtractString(const char*& line);
protected:
ConnectionReader* fReader;
LiteralHandler* fLiteralHandler;
int32 fTag;
bool fContinuation;
};
class ResponseParser {
public:
ResponseParser(ConnectionReader& reader);
~ResponseParser();
void SetTo(ConnectionReader& reader);
void SetLiteralHandler(LiteralHandler* handler);
status_t NextResponse(Response& response,
bigtime_t timeout) throw(ParseException);
protected:
ConnectionReader* fReader;
LiteralHandler* fLiteralHandler;
};
} // namespace IMAP
+15 -3
View File
@@ -8,8 +8,20 @@ SubDirHdrs [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems
SubDirHdrs [ FDirName $(HAIKU_TOP) src add-ons mail_daemon inbound_protocols
imap imap_lib ] ;
local libSources = IMAPFolders.cpp IMAPHandler.cpp IMAPMailbox.cpp
IMAPParser.cpp IMAPProtocol.cpp IMAPStorage.cpp ;
local libSources = Commands.cpp Protocol.cpp Response.cpp ;
#IMAPStorage.cpp
# use OpenSSL, if enabled
if $(HAIKU_OPENSSL_ENABLED) {
SubDirC++Flags -DUSE_SSL ;
SubDirSysHdrs $(HAIKU_OPENSSL_HEADERS) ;
Includes [ FGristFiles $(sources) ] : $(HAIKU_OPENSSL_HEADERS_DEPENDENCY) ;
# Dependency needed to trigger downloading/unzipping the package before
# compiling the files.
SetupFeatureObjectsDir ssl ;
} else {
SetupFeatureObjectsDir no-ssl ;
}
SimpleTest imap_tester :
imap_tester.cpp
@@ -18,7 +30,7 @@ SimpleTest imap_tester :
# from fs_shell
argv.c
: be [ TargetLibsupc++ ] mail
: be [ TargetLibstdc++ ] [ TargetLibsupc++ ] bnetapi mail
;
SEARCH on [ FGristFiles $(libSources) ]
+66 -19
View File
@@ -1,14 +1,22 @@
#include "IMAPFolders.h"
#include "IMAPMailbox.h"
#include "IMAPStorage.h"
/*
* Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <stdlib.h>
//#include "IMAPStorage.h"
#include "Protocol.h"
#include "Response.h"
#include "argv.h"
struct cmd_entry {
char* name;
void (*func)(int argc, char **argv);
char* help;
const char* name;
void (*func)(int argc, char **argv);
const char* help;
};
@@ -18,8 +26,8 @@ static void do_help(int argc, char** argv);
extern const char* __progname;
static const char* kProgramName = __progname;
static IMAPStorage sStorage;
static IMAPMailbox sMailbox(sStorage);
//static IMAPStorage sStorage;
static IMAP::Protocol sMailbox;//(/*sStorage*/);
static void
@@ -56,11 +64,13 @@ do_select(int argc, char** argv)
static void
do_folders(int argc, char** argv)
{
IMAPFolders folder(sMailbox);
FolderList folders;
status_t status = folder.GetFolders(folders);
if (status != B_OK)
IMAP::FolderList folders;
status_t status = sMailbox.GetFolders(folders);
if (status != B_OK) {
error("folders", status);
return;
}
for (size_t i = 0; i < folders.size(); i++) {
printf(" %s %s\n", folders[i].subscribed ? "*" : " ",
@@ -69,6 +79,34 @@ do_folders(int argc, char** argv)
}
static void
do_flags(int argc, char** argv)
{
uint32 from = 1;
uint32 to;
if (argc < 2) {
printf("usage: %s [<from>] [<to>]\n", argv[0]);
return;
}
if (argc > 2) {
from = atoul(argv[1]);
to = atoul(argv[2]);
} else
to = atoul(argv[1]);
IMAP::MessageEntryList entries;
IMAP::FetchMessageEntriesCommand command(entries, from, to);
status_t status = sMailbox.ProcessCommand(command);
if (status != B_OK) {
error("flags", status);
return;
}
for (size_t i = 0; i < entries.size(); i++)
printf(" %lu %lx\n", entries[i].uid, entries[i].flags);
}
static void
do_raw(int argc, char** argv)
{
@@ -82,7 +120,7 @@ do_raw(int argc, char** argv)
strlcat(command, argv[i], sizeof(command));
}
class RawCommand : public IMAPCommand {
class RawCommand : public IMAP::Command, public IMAP::Handler {
public:
RawCommand(const char* command)
:
@@ -90,13 +128,18 @@ do_raw(int argc, char** argv)
{
}
BString Command()
BString CommandString()
{
return fCommand;
}
bool Handle(const BString& response)
bool HandleUntagged(IMAP::Response& response)
{
if (response.IsCommand(fCommand)) {
printf("-> %s\n", response.ToString().String());
return true;
}
return false;
}
@@ -104,7 +147,7 @@ do_raw(int argc, char** argv)
const char* fCommand;
};
RawCommand rawCommand(command);
status_t status = sMailbox.ProcessCommand(&rawCommand, 60 * 1000);
status_t status = sMailbox.ProcessCommand(rawCommand);
if (status != B_OK)
error("raw", status);
}
@@ -113,6 +156,8 @@ do_raw(int argc, char** argv)
static cmd_entry sBuiltinCommands[] = {
{"select", do_select, "Selects a mailbox, defaults to INBOX"},
{"folders", do_folders, "List of existing folders"},
{"flags", do_flags,
"List of all mail UIDs in the mailbox with their flags"},
{"raw", do_raw, "Issue a raw command to the server"},
{"help", do_help, "prints this help text"},
{"quit", NULL, "exits the application"},
@@ -145,11 +190,13 @@ main(int argc, char** argv)
const char* user = argv[2];
const char* password = argv[3];
bool useSSL = argc > 4;
uint16 port = useSSL ? 995 : 143;
uint16 port = useSSL ? 993 : 143;
printf("Connecting to \"%s\" as %s\n", server, user);
BNetworkAddress address(AF_INET, server, port);
printf("Connecting to \"%s\" as %s%s, port %u\n", server, user,
useSSL ? " with SSL" : "", address.Port());
status_t status = sMailbox.Connect(server, user, password, useSSL, port);
status_t status = sMailbox.Connect(address, user, password, useSSL);
if (status != B_OK) {
error("connect", status);
return 1;