IMAP: removed superfluous files from the old implementation.
This commit is contained in:
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
#ifndef IMAP_H
|
|
||||||
#define IMAP_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "MailProtocol.h"
|
|
||||||
|
|
||||||
#include <Handler.h>
|
|
||||||
#include <Locker.h>
|
|
||||||
#include <Message.h>
|
|
||||||
|
|
||||||
#include "MailSettings.h"
|
|
||||||
|
|
||||||
#include "Protocol.h"
|
|
||||||
#include "IMAPStorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
class DispatcherIMAPListener : public IMAPMailboxListener {
|
|
||||||
public:
|
|
||||||
DispatcherIMAPListener(MailProtocol& protocol,
|
|
||||||
IMAPStorage& storage);
|
|
||||||
|
|
||||||
bool Lock();
|
|
||||||
void Unlock();
|
|
||||||
|
|
||||||
void HeaderFetched(int32 uid, BPositionIO* data,
|
|
||||||
bool bodyIsComming);
|
|
||||||
void BodyFetched(int32 uid, BPositionIO* data);
|
|
||||||
|
|
||||||
void NewMessagesToFetch(int32 nMessages);
|
|
||||||
void FetchEnd();
|
|
||||||
private:
|
|
||||||
MailProtocol& fProtocol;
|
|
||||||
IMAPStorage& fStorage;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol;
|
|
||||||
|
|
||||||
|
|
||||||
/*! Just wait for a IDLE (watching) IMAP response in this thread. */
|
|
||||||
class IMAPMailboxThread {
|
|
||||||
public:
|
|
||||||
IMAPMailboxThread(IMAPInboundProtocol& protocol,
|
|
||||||
IMAPMailbox& mailbox);
|
|
||||||
~IMAPMailboxThread();
|
|
||||||
|
|
||||||
bool IsWatching();
|
|
||||||
status_t SyncAndStartWatchingMailbox();
|
|
||||||
status_t StopWatchingMailbox();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static status_t _WatchThreadFunction(void* data);
|
|
||||||
void _Watch();
|
|
||||||
|
|
||||||
private:
|
|
||||||
IMAPInboundProtocol& fProtocol;
|
|
||||||
IMAPMailbox& fIMAPMailbox;
|
|
||||||
|
|
||||||
BLocker fLock;
|
|
||||||
bool fIsWatching;
|
|
||||||
|
|
||||||
thread_id fThread;
|
|
||||||
sem_id fWatchSyncSem;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol;
|
|
||||||
|
|
||||||
|
|
||||||
class MailboxWatcher : public BHandler {
|
|
||||||
public:
|
|
||||||
MailboxWatcher(IMAPInboundProtocol* protocol);
|
|
||||||
~MailboxWatcher();
|
|
||||||
|
|
||||||
void StartWatching(const char* mailboxDir);
|
|
||||||
void MessageReceived(BMessage* message);
|
|
||||||
private:
|
|
||||||
node_ref fWatchDir;
|
|
||||||
IMAPInboundProtocol* fProtocol;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol : public InboundProtocol {
|
|
||||||
public:
|
|
||||||
IMAPInboundProtocol(
|
|
||||||
BMailAccountSettings* settings,
|
|
||||||
const char* mailbox);
|
|
||||||
virtual ~IMAPInboundProtocol();
|
|
||||||
|
|
||||||
//! thread safe interface
|
|
||||||
virtual status_t Connect(const char* server,
|
|
||||||
const char* username, const char* password,
|
|
||||||
bool useSSL = true, int32 port = -1);
|
|
||||||
virtual status_t Disconnect();
|
|
||||||
status_t Reconnect();
|
|
||||||
bool IsConnected();
|
|
||||||
|
|
||||||
virtual void SetStopNow();
|
|
||||||
|
|
||||||
void AddedToLooper();
|
|
||||||
void UpdateSettings(const BMessage& settings);
|
|
||||||
|
|
||||||
bool InterestingEntry(const entry_ref& ref);
|
|
||||||
|
|
||||||
virtual status_t SyncMessages();
|
|
||||||
virtual status_t FetchBody(const entry_ref& ref);
|
|
||||||
virtual status_t MarkMessageAsRead(const entry_ref& ref,
|
|
||||||
read_flags flag = B_READ);
|
|
||||||
|
|
||||||
virtual status_t DeleteMessage(const entry_ref& ref);
|
|
||||||
virtual status_t AppendMessage(const entry_ref& ref);
|
|
||||||
|
|
||||||
virtual status_t DeleteMessage(node_ref& node);
|
|
||||||
//! these should be thread save
|
|
||||||
virtual void FileRenamed(const entry_ref& from,
|
|
||||||
const entry_ref& to);
|
|
||||||
virtual void FileDeleted(const node_ref& node);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BString fServer;
|
|
||||||
BString fUsername;
|
|
||||||
BString fPassword;
|
|
||||||
bool fUseSSL;
|
|
||||||
bool fIsConnected;
|
|
||||||
|
|
||||||
BString fMailboxName;
|
|
||||||
BPath fMailboxPath;
|
|
||||||
|
|
||||||
IMAPStorage fStorage;
|
|
||||||
IMAPMailbox fIMAPMailbox;
|
|
||||||
DispatcherIMAPListener fDispatcherIMAPListener;
|
|
||||||
|
|
||||||
MailboxWatcher* fINBOXWatcher;
|
|
||||||
IMAPMailboxThread* fIMAPMailboxThread;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // IMAP_H
|
|
||||||
@@ -1,615 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "IMAPInboundProtocol.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <Autolock.h>
|
|
||||||
#include <Directory.h>
|
|
||||||
#include <Messenger.h>
|
|
||||||
#include <NodeMonitor.h>
|
|
||||||
|
|
||||||
#include <crypt.h>
|
|
||||||
|
|
||||||
|
|
||||||
const uint32 kMsgStartWatching = '&StW';
|
|
||||||
|
|
||||||
|
|
||||||
DispatcherIMAPListener::DispatcherIMAPListener(MailProtocol& protocol,
|
|
||||||
IMAPStorage& storage)
|
|
||||||
:
|
|
||||||
fProtocol(protocol),
|
|
||||||
fStorage(storage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
DispatcherIMAPListener::HeaderFetched(int32 uid, BPositionIO* data,
|
|
||||||
bool bodyIsComing)
|
|
||||||
{
|
|
||||||
BFile* file = dynamic_cast<BFile*>(data);
|
|
||||||
if (file == NULL)
|
|
||||||
return;
|
|
||||||
entry_ref ref;
|
|
||||||
if (!fStorage.UIDToRef(uid, ref))
|
|
||||||
return;
|
|
||||||
|
|
||||||
fProtocol.NotifyHeaderFetched(ref, file);
|
|
||||||
|
|
||||||
if (!bodyIsComing)
|
|
||||||
fProtocol.ReportProgress(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
DispatcherIMAPListener::BodyFetched(int32 uid, BPositionIO* data)
|
|
||||||
{
|
|
||||||
BFile* file = dynamic_cast<BFile*>(data);
|
|
||||||
if (file == NULL)
|
|
||||||
return;
|
|
||||||
entry_ref ref;
|
|
||||||
if (!fStorage.UIDToRef(uid, ref))
|
|
||||||
return;
|
|
||||||
fProtocol.NotifyBodyFetched(ref, file);
|
|
||||||
|
|
||||||
fProtocol.ReportProgress(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
DispatcherIMAPListener::NewMessagesToFetch(int32 nMessages)
|
|
||||||
{
|
|
||||||
fProtocol.NotifyNewMessagesToFetch(nMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
DispatcherIMAPListener::FetchEnd()
|
|
||||||
{
|
|
||||||
fProtocol.ReportProgress(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
IMAPMailboxThread::IMAPMailboxThread(IMAPInboundProtocol& protocol,
|
|
||||||
IMAPMailbox& mailbox)
|
|
||||||
:
|
|
||||||
fProtocol(protocol),
|
|
||||||
fIMAPMailbox(mailbox),
|
|
||||||
|
|
||||||
fIsWatching(false),
|
|
||||||
fThread(-1)
|
|
||||||
{
|
|
||||||
fWatchSyncSem = create_sem(0, "watch sync sem");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IMAPMailboxThread::~IMAPMailboxThread()
|
|
||||||
{
|
|
||||||
delete_sem(fWatchSyncSem);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPMailboxThread::IsWatching()
|
|
||||||
{
|
|
||||||
BAutolock _(fLock);
|
|
||||||
return fIsWatching;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailboxThread::SyncAndStartWatchingMailbox()
|
|
||||||
{
|
|
||||||
if (!fProtocol.IsConnected())
|
|
||||||
return B_ERROR;
|
|
||||||
|
|
||||||
if (fIMAPMailbox.SupportWatching()) {
|
|
||||||
BAutolock autolock(fLock);
|
|
||||||
if (fIsWatching)
|
|
||||||
return B_OK;
|
|
||||||
fThread = spawn_thread(_WatchThreadFunction, "IMAPMailboxThread",
|
|
||||||
B_LOW_PRIORITY, this);
|
|
||||||
if (resume_thread(fThread) != B_OK) {
|
|
||||||
fThread = -1;
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
acquire_sem(fWatchSyncSem);
|
|
||||||
fIsWatching = true;
|
|
||||||
} else {
|
|
||||||
status_t status = fIMAPMailbox.CheckMailbox();
|
|
||||||
// if we lost connection reconnect and try again
|
|
||||||
if (status != B_OK)
|
|
||||||
status = fProtocol.Reconnect();
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
status = fIMAPMailbox.CheckMailbox();
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailboxThread::StopWatchingMailbox()
|
|
||||||
{
|
|
||||||
status_t status = fIMAPMailbox.StopWatchingMailbox();
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// wait till watching stopped
|
|
||||||
status_t exitCode;
|
|
||||||
return wait_for_thread(fThread, &exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
|
||||||
IMAPMailboxThread::_WatchThreadFunction(void* data)
|
|
||||||
{
|
|
||||||
((IMAPMailboxThread*)data)->_Watch();
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPMailboxThread::_Watch()
|
|
||||||
{
|
|
||||||
status_t status = fIMAPMailbox.StartWatchingMailbox(fWatchSyncSem);
|
|
||||||
if (status != B_OK)
|
|
||||||
fProtocol.Disconnect();
|
|
||||||
|
|
||||||
fLock.Lock();
|
|
||||||
fIsWatching = false;
|
|
||||||
fLock.Unlock();
|
|
||||||
|
|
||||||
fThread = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
MailboxWatcher::MailboxWatcher(IMAPInboundProtocol* protocol)
|
|
||||||
:
|
|
||||||
fProtocol(protocol)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MailboxWatcher::~MailboxWatcher()
|
|
||||||
{
|
|
||||||
stop_watching(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MailboxWatcher::StartWatching(const char* mailboxDir)
|
|
||||||
{
|
|
||||||
create_directory(mailboxDir, 0755);
|
|
||||||
|
|
||||||
BDirectory dir(mailboxDir);
|
|
||||||
dir.GetNodeRef(&fWatchDir);
|
|
||||||
watch_node(&fWatchDir, B_WATCH_DIRECTORY, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MailboxWatcher::MessageReceived(BMessage* message)
|
|
||||||
{
|
|
||||||
int32 opcode;
|
|
||||||
entry_ref ref;
|
|
||||||
node_ref nref;
|
|
||||||
const char* name;
|
|
||||||
switch (message->what) {
|
|
||||||
case B_NODE_MONITOR:
|
|
||||||
if (message->FindInt32("opcode", &opcode) != B_OK)
|
|
||||||
break;
|
|
||||||
switch (opcode) {
|
|
||||||
case B_ENTRY_CREATED:
|
|
||||||
break;
|
|
||||||
message->FindInt32("device", &ref.device);
|
|
||||||
message->FindInt64("directory", &ref.directory);
|
|
||||||
message->FindString("name", &name);
|
|
||||||
ref.set_name(name);
|
|
||||||
|
|
||||||
fProtocol->AppendMessage(ref);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case B_ENTRY_REMOVED:
|
|
||||||
message->FindInt32("device", &nref.device);
|
|
||||||
message->FindInt64("node", &nref.node);
|
|
||||||
|
|
||||||
fProtocol->DeleteMessage(nref);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case B_ENTRY_MOVED:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
entry_ref from;
|
|
||||||
entry_ref to;
|
|
||||||
ino_t toDirectory = -1;
|
|
||||||
ino_t fromDirectory = -2;
|
|
||||||
message->FindInt64("to directory", &toDirectory);
|
|
||||||
message->FindInt64("from directory", &fromDirectory);
|
|
||||||
if (toDirectory != fromDirectory)
|
|
||||||
break;
|
|
||||||
message->FindInt32("device", &to.device);
|
|
||||||
message->FindInt64("directory", &to.directory);
|
|
||||||
message->FindString("name", &name);
|
|
||||||
to.set_name(name);
|
|
||||||
|
|
||||||
from.device = to.device;
|
|
||||||
from.directory = to.directory;
|
|
||||||
from.set_name(message->FindString("from name"));
|
|
||||||
fProtocol->Looper()->TriggerFileRenamed(from, to);
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (fWatchDir.node == toDirectory) {
|
|
||||||
// append
|
|
||||||
message->FindInt32("device", &ref.device);
|
|
||||||
ref.directory = toDirectory;
|
|
||||||
message->FindString("name", &name);
|
|
||||||
ref.set_name(name);
|
|
||||||
fProtocol->AppendMessage(ref);
|
|
||||||
} else {
|
|
||||||
// delete
|
|
||||||
message->FindInt32("device", &nref.device);
|
|
||||||
message->FindInt64("node", &nref.node);
|
|
||||||
fProtocol->DeleteMessage(nref);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
BHandler::MessageReceived(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
IMAPInboundProtocol::IMAPInboundProtocol(BMailAccountSettings* settings,
|
|
||||||
const char* mailbox)
|
|
||||||
:
|
|
||||||
InboundProtocol(settings),
|
|
||||||
|
|
||||||
fIsConnected(false),
|
|
||||||
fMailboxName(mailbox),
|
|
||||||
fIMAPMailbox(fStorage),
|
|
||||||
fDispatcherIMAPListener(*this, fStorage),
|
|
||||||
fINBOXWatcher(NULL)
|
|
||||||
{
|
|
||||||
const BMessage& settingsMsg = fAccountSettings.InboundSettings().Settings();
|
|
||||||
int32 bodyLimit = 0;
|
|
||||||
if (settingsMsg.HasInt32("partial_download_limit"))
|
|
||||||
bodyLimit = settingsMsg.FindInt32("partial_download_limit");
|
|
||||||
|
|
||||||
BString tempString;
|
|
||||||
if (settingsMsg.FindString("destination", &tempString) != B_OK) {
|
|
||||||
tempString = "/boot/home/mail/";
|
|
||||||
tempString += settings->Name();
|
|
||||||
}
|
|
||||||
fMailboxPath.SetTo(tempString);
|
|
||||||
fMailboxPath.Append(fMailboxName);
|
|
||||||
fStorage.SetTo(fMailboxPath.Path());
|
|
||||||
|
|
||||||
fIMAPMailbox.SetListener(&fDispatcherIMAPListener);
|
|
||||||
fIMAPMailbox.SetFetchBodyLimit(bodyLimit);
|
|
||||||
|
|
||||||
fIMAPMailboxThread = new IMAPMailboxThread(*this, fIMAPMailbox);
|
|
||||||
|
|
||||||
// set watch directory
|
|
||||||
fINBOXWatcher = new MailboxWatcher(this);
|
|
||||||
AddHandler(fINBOXWatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IMAPInboundProtocol::~IMAPInboundProtocol()
|
|
||||||
{
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
|
|
||||||
RemoveHandler(fINBOXWatcher);
|
|
||||||
delete fINBOXWatcher;
|
|
||||||
|
|
||||||
delete fIMAPMailboxThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::Connect(const char* server, const char* username,
|
|
||||||
const char* password, bool useSSL, int32 port)
|
|
||||||
{
|
|
||||||
if (fIsConnected)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
BString statusMessage;
|
|
||||||
|
|
||||||
statusMessage = "Connect to: ";
|
|
||||||
statusMessage += username;
|
|
||||||
SetTotalItems(5);
|
|
||||||
ReportProgress(0, 1, statusMessage);
|
|
||||||
|
|
||||||
status_t status = fIMAPMailbox.Connect(server, username, password, useSSL,
|
|
||||||
port);
|
|
||||||
if (status != B_OK) {
|
|
||||||
if (fIMAPMailbox.CommandError().CountChars() > 0) {
|
|
||||||
// This was a IMAP error
|
|
||||||
statusMessage = "Failed to login: ";
|
|
||||||
statusMessage += fIMAPMailbox.CommandError();
|
|
||||||
} else {
|
|
||||||
// Probably a connection error
|
|
||||||
statusMessage = "Connection error: ";
|
|
||||||
statusMessage += strerror(status);
|
|
||||||
}
|
|
||||||
ShowError(statusMessage);
|
|
||||||
ResetProgress();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportProgress(0, 1, "Load database");
|
|
||||||
|
|
||||||
fStorage.StartReadDatabase();
|
|
||||||
status = fIMAPMailbox.SelectMailbox(fMailboxName);
|
|
||||||
if (status != B_OK) {
|
|
||||||
fStorage.WaitForDatabaseRead();
|
|
||||||
statusMessage = "Failed to select mailbox (";
|
|
||||||
statusMessage += fMailboxName;
|
|
||||||
statusMessage += "): ";
|
|
||||||
statusMessage += fIMAPMailbox.CommandError();
|
|
||||||
ShowError(statusMessage);
|
|
||||||
ResetProgress();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportProgress(0, 1, "Fetch message list");
|
|
||||||
status = fIMAPMailbox.Sync();
|
|
||||||
if (status != B_OK) {
|
|
||||||
fStorage.WaitForDatabaseRead();
|
|
||||||
ShowError("Failed to sync mailbox");
|
|
||||||
ResetProgress();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportProgress(0, 1, "Read local message list");
|
|
||||||
status = fStorage.WaitForDatabaseRead();
|
|
||||||
if (status != B_OK) {
|
|
||||||
ShowError("Can't read database");
|
|
||||||
ResetProgress();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReportProgress(0, 1, "Sync mailbox");
|
|
||||||
|
|
||||||
IMAPMailboxSync mailboxSync;
|
|
||||||
status = mailboxSync.Sync(fStorage, fIMAPMailbox);
|
|
||||||
|
|
||||||
ResetProgress();
|
|
||||||
|
|
||||||
if (status == B_OK)
|
|
||||||
fIsConnected = true;
|
|
||||||
else
|
|
||||||
Disconnect();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::Disconnect()
|
|
||||||
{
|
|
||||||
fIsConnected = false;
|
|
||||||
return fIMAPMailbox.Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::Reconnect()
|
|
||||||
{
|
|
||||||
Disconnect();
|
|
||||||
return Connect(fServer, fUsername, fPassword, fUseSSL, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPInboundProtocol::IsConnected()
|
|
||||||
{
|
|
||||||
if (!fIsConnected)
|
|
||||||
return false;
|
|
||||||
if (fIMAPMailbox.IsConnected())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPInboundProtocol::SetStopNow()
|
|
||||||
{
|
|
||||||
fIMAPMailbox.SetStopNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPInboundProtocol::AddedToLooper()
|
|
||||||
{
|
|
||||||
fINBOXWatcher->StartWatching(fMailboxPath.Path());
|
|
||||||
|
|
||||||
const BMessage& settingsMsg = fAccountSettings.InboundSettings().Settings();
|
|
||||||
UpdateSettings(settingsMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPInboundProtocol::UpdateSettings(const BMessage& settings)
|
|
||||||
{
|
|
||||||
settings.FindString("server", &fServer);
|
|
||||||
int32 ssl;
|
|
||||||
settings.FindInt32("flavor", &ssl);
|
|
||||||
if (ssl == 1)
|
|
||||||
fUseSSL = true;
|
|
||||||
else
|
|
||||||
fUseSSL = false;
|
|
||||||
settings.FindString("username", &fUsername);
|
|
||||||
|
|
||||||
char* passwd = get_passwd(&settings, "cpasswd");
|
|
||||||
if (passwd) {
|
|
||||||
fPassword = passwd;
|
|
||||||
delete[] passwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPInboundProtocol::InterestingEntry(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
BEntry entry(&ref);
|
|
||||||
return BDirectory(fMailboxPath.Path()).Contains(&entry, B_FILE_NODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::SyncMessages()
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
return fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::FetchBody(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
int32 uid = fStorage.RefToUID(ref);
|
|
||||||
|
|
||||||
ResetProgress("Fetch body");
|
|
||||||
SetTotalItems(1);
|
|
||||||
|
|
||||||
status_t status = fIMAPMailbox.FetchBody(fIMAPMailbox.UIDToMessageNumber(
|
|
||||||
uid));
|
|
||||||
|
|
||||||
ResetProgress();
|
|
||||||
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
int32 uid = fStorage.RefToUID(ref);
|
|
||||||
int32 flags = fStorage.GetFlags(uid);
|
|
||||||
if (flag == B_READ)
|
|
||||||
flags |= kSeen;
|
|
||||||
else
|
|
||||||
flags &= ~kSeen;
|
|
||||||
status_t status = fIMAPMailbox.SetFlags(
|
|
||||||
fIMAPMailbox.UIDToMessageNumber(uid), flags);
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
return InboundProtocol::MarkMessageAsRead(ref, flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::DeleteMessage(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
|
|
||||||
int32 uid = fStorage.RefToUID(ref);
|
|
||||||
status_t status = fIMAPMailbox.DeleteMessage(uid, false);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPInboundProtocol::FileRenamed(const entry_ref& from, const entry_ref& to)
|
|
||||||
{
|
|
||||||
fStorage.FileRenamed(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPInboundProtocol::FileDeleted(const node_ref& node)
|
|
||||||
{
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::AppendMessage(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
BFile file(&ref, B_READ_ONLY);
|
|
||||||
off_t size;
|
|
||||||
file.GetSize(&size);
|
|
||||||
|
|
||||||
// check if file is already known
|
|
||||||
int32 uid = -1;
|
|
||||||
file.ReadAttr("MAIL:unique_id", B_INT32_TYPE, 0, &uid, sizeof(int32));
|
|
||||||
if (fStorage.HasFile(uid)) {
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 flags = 0;
|
|
||||||
file.ReadAttr("MAIL:server_flags", B_INT32_TYPE, 0, &flags, sizeof(int32));
|
|
||||||
|
|
||||||
status_t status = fIMAPMailbox.AppendMessage(file, size, flags);
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPInboundProtocol::DeleteMessage(node_ref& node)
|
|
||||||
{
|
|
||||||
if (!IsConnected())
|
|
||||||
Connect(fServer, fUsername, fPassword, fUseSSL);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->StopWatchingMailbox();
|
|
||||||
StorageMailEntry* entry = fStorage.GetEntryForRef(node);
|
|
||||||
if (entry == NULL) {
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 uid = entry->uid;
|
|
||||||
status_t status = fIMAPMailbox.DeleteMessage(uid, false);
|
|
||||||
|
|
||||||
fIMAPMailboxThread->SyncAndStartWatchingMailbox();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
#ifndef IMAP_INBOUND_PROTOCOL_H
|
|
||||||
#define IMAP_INBOUND_PROTOCOL_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "MailProtocol.h"
|
|
||||||
|
|
||||||
#include <Handler.h>
|
|
||||||
#include <Locker.h>
|
|
||||||
#include <Message.h>
|
|
||||||
|
|
||||||
#include "MailSettings.h"
|
|
||||||
|
|
||||||
#include "Protocol.h"
|
|
||||||
#include "IMAPStorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
class DispatcherIMAPListener : public IMAPMailboxListener {
|
|
||||||
public:
|
|
||||||
DispatcherIMAPListener(MailProtocol& protocol,
|
|
||||||
IMAPStorage& storage);
|
|
||||||
|
|
||||||
bool Lock();
|
|
||||||
void Unlock();
|
|
||||||
|
|
||||||
void HeaderFetched(int32 uid, BPositionIO* data,
|
|
||||||
bool bodyIsComming);
|
|
||||||
void BodyFetched(int32 uid, BPositionIO* data);
|
|
||||||
|
|
||||||
void NewMessagesToFetch(int32 nMessages);
|
|
||||||
void FetchEnd();
|
|
||||||
private:
|
|
||||||
MailProtocol& fProtocol;
|
|
||||||
IMAPStorage& fStorage;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol;
|
|
||||||
|
|
||||||
|
|
||||||
/*! Just wait for a IDLE (watching) IMAP response in this thread. */
|
|
||||||
class IMAPMailboxThread {
|
|
||||||
public:
|
|
||||||
IMAPMailboxThread(IMAPInboundProtocol& protocol,
|
|
||||||
IMAPMailbox& mailbox);
|
|
||||||
~IMAPMailboxThread();
|
|
||||||
|
|
||||||
bool IsWatching();
|
|
||||||
status_t SyncAndStartWatchingMailbox();
|
|
||||||
status_t StopWatchingMailbox();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static status_t _WatchThreadFunction(void* data);
|
|
||||||
void _Watch();
|
|
||||||
|
|
||||||
private:
|
|
||||||
IMAPInboundProtocol& fProtocol;
|
|
||||||
IMAPMailbox& fIMAPMailbox;
|
|
||||||
|
|
||||||
BLocker fLock;
|
|
||||||
bool fIsWatching;
|
|
||||||
|
|
||||||
thread_id fThread;
|
|
||||||
sem_id fWatchSyncSem;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol;
|
|
||||||
|
|
||||||
|
|
||||||
class MailboxWatcher : public BHandler {
|
|
||||||
public:
|
|
||||||
MailboxWatcher(IMAPInboundProtocol* protocol);
|
|
||||||
~MailboxWatcher();
|
|
||||||
|
|
||||||
void StartWatching(const char* mailboxDir);
|
|
||||||
void MessageReceived(BMessage* message);
|
|
||||||
private:
|
|
||||||
node_ref fWatchDir;
|
|
||||||
IMAPInboundProtocol* fProtocol;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPInboundProtocol : public InboundProtocol {
|
|
||||||
public:
|
|
||||||
IMAPInboundProtocol(
|
|
||||||
BMailAccountSettings* settings,
|
|
||||||
const char* mailbox);
|
|
||||||
virtual ~IMAPInboundProtocol();
|
|
||||||
|
|
||||||
//! thread safe interface
|
|
||||||
virtual status_t Connect(const char* server,
|
|
||||||
const char* username, const char* password,
|
|
||||||
bool useSSL = true, int32 port = -1);
|
|
||||||
virtual status_t Disconnect();
|
|
||||||
status_t Reconnect();
|
|
||||||
bool IsConnected();
|
|
||||||
|
|
||||||
virtual void SetStopNow();
|
|
||||||
|
|
||||||
void AddedToLooper();
|
|
||||||
void UpdateSettings(const BMessage& settings);
|
|
||||||
|
|
||||||
bool InterestingEntry(const entry_ref& ref);
|
|
||||||
|
|
||||||
virtual status_t SyncMessages();
|
|
||||||
virtual status_t FetchBody(const entry_ref& ref);
|
|
||||||
virtual status_t MarkMessageAsRead(const entry_ref& ref,
|
|
||||||
read_flags flag = B_READ);
|
|
||||||
|
|
||||||
virtual status_t DeleteMessage(const entry_ref& ref);
|
|
||||||
virtual status_t AppendMessage(const entry_ref& ref);
|
|
||||||
|
|
||||||
virtual status_t DeleteMessage(node_ref& node);
|
|
||||||
//! these should be thread save
|
|
||||||
virtual void FileRenamed(const entry_ref& from,
|
|
||||||
const entry_ref& to);
|
|
||||||
virtual void FileDeleted(const node_ref& node);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BString fServer;
|
|
||||||
BString fUsername;
|
|
||||||
BString fPassword;
|
|
||||||
bool fUseSSL;
|
|
||||||
bool fIsConnected;
|
|
||||||
|
|
||||||
BString fMailboxName;
|
|
||||||
BPath fMailboxPath;
|
|
||||||
|
|
||||||
IMAPStorage fStorage;
|
|
||||||
IMAPMailbox fIMAPMailbox;
|
|
||||||
DispatcherIMAPListener fDispatcherIMAPListener;
|
|
||||||
|
|
||||||
MailboxWatcher* fINBOXWatcher;
|
|
||||||
IMAPMailboxThread* fIMAPMailboxThread;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // IMAP_INBOUND_PROTOCOL_H
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "IMAPRootInboundProtocol.h"
|
|
||||||
|
|
||||||
#include <Messenger.h>
|
|
||||||
|
|
||||||
|
|
||||||
IMAPRootInboundProtocol::IMAPRootInboundProtocol(BMailAccountSettings* settings)
|
|
||||||
:
|
|
||||||
IMAPInboundProtocol(settings, "INBOX")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IMAPRootInboundProtocol::~IMAPRootInboundProtocol()
|
|
||||||
{
|
|
||||||
_ShutdownChilds();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::Connect(const char* server, const char* username,
|
|
||||||
const char* password, bool useSSL, int32 port)
|
|
||||||
{
|
|
||||||
status_t status = IMAPInboundProtocol::Connect(server, username, password,
|
|
||||||
useSSL, port);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
FolderList folders;
|
|
||||||
fIMAPMailbox.GetFolders(folders);
|
|
||||||
for (unsigned int i = 0; i < folders.size(); i++) {
|
|
||||||
if (!folders[i].subscribed || folders[i].folder == "INBOX")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
IMAPInboundProtocol* inboundProtocol = new IMAPInboundProtocol(
|
|
||||||
&fAccountSettings, folders[i].folder);
|
|
||||||
inboundProtocol->SetMailNotifier(fMailNotifier->Clone());
|
|
||||||
|
|
||||||
InboundProtocolThread* inboundThread = new InboundProtocolThread(
|
|
||||||
inboundProtocol);
|
|
||||||
inboundThread->Run();
|
|
||||||
|
|
||||||
fProtocolThreadList.AddItem(inboundThread);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::Disconnect()
|
|
||||||
{
|
|
||||||
status_t status = IMAPInboundProtocol::Disconnect();
|
|
||||||
_ShutdownChilds();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPRootInboundProtocol::SetStopNow()
|
|
||||||
{
|
|
||||||
for (int32 i = 0; i < fProtocolThreadList.CountItems(); i++)
|
|
||||||
fProtocolThreadList.ItemAt(i)->SetStopNow();
|
|
||||||
IMAPInboundProtocol::SetStopNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::SyncMessages()
|
|
||||||
{
|
|
||||||
for (int32 i = 0; i < fProtocolThreadList.CountItems(); i++)
|
|
||||||
fProtocolThreadList.ItemAt(i)->SyncMessages();
|
|
||||||
return IMAPInboundProtocol::SyncMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::FetchBody(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (InterestingEntry(ref))
|
|
||||||
return IMAPInboundProtocol::FetchBody(ref);
|
|
||||||
InboundProtocolThread* thread = _FindThreadFor(ref);
|
|
||||||
if (!thread)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
thread->FetchBody(ref);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::MarkMessageAsRead(const entry_ref& ref,
|
|
||||||
read_flags flag)
|
|
||||||
{
|
|
||||||
if (InterestingEntry(ref))
|
|
||||||
return IMAPInboundProtocol::MarkMessageAsRead(ref, flag);
|
|
||||||
InboundProtocolThread* thread = _FindThreadFor(ref);
|
|
||||||
if (!thread)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
thread->MarkMessageAsRead(ref, flag);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::DeleteMessage(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (InterestingEntry(ref))
|
|
||||||
return IMAPInboundProtocol::DeleteMessage(ref);
|
|
||||||
InboundProtocolThread* thread = _FindThreadFor(ref);
|
|
||||||
if (!thread)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
thread->DeleteMessage(ref);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::AppendMessage(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
if (InterestingEntry(ref))
|
|
||||||
return IMAPInboundProtocol::AppendMessage(ref);
|
|
||||||
InboundProtocolThread* thread = _FindThreadFor(ref);
|
|
||||||
if (!thread)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
thread->AppendMessage(ref);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPRootInboundProtocol::DeleteMessage(node_ref& node)
|
|
||||||
{
|
|
||||||
status_t status = IMAPInboundProtocol::DeleteMessage(node);
|
|
||||||
if (status == B_OK)
|
|
||||||
return status;
|
|
||||||
for (int32 i = 0; i < fProtocolThreadList.CountItems(); i++)
|
|
||||||
fProtocolThreadList.ItemAt(i)->TriggerFileDeleted(node);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPRootInboundProtocol::_ShutdownChilds()
|
|
||||||
{
|
|
||||||
BMessage reply;
|
|
||||||
for (int32 i = 0; i < fProtocolThreadList.CountItems(); i++) {
|
|
||||||
InboundProtocolThread* thread = fProtocolThreadList.ItemAt(i);
|
|
||||||
MailProtocol* protocol = thread->Protocol();
|
|
||||||
|
|
||||||
thread->SetStopNow();
|
|
||||||
BMessenger(thread).SendMessage(B_QUIT_REQUESTED, &reply);
|
|
||||||
|
|
||||||
delete protocol;
|
|
||||||
}
|
|
||||||
fProtocolThreadList.MakeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
InboundProtocolThread*
|
|
||||||
IMAPRootInboundProtocol::_FindThreadFor(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
for (int32 i = 0; i < fProtocolThreadList.CountItems(); i++) {
|
|
||||||
InboundProtocolThread* thread = fProtocolThreadList.ItemAt(i);
|
|
||||||
IMAPInboundProtocol* protocol
|
|
||||||
= (IMAPInboundProtocol*)thread->Protocol();
|
|
||||||
if (protocol->InterestingEntry(ref))
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
InboundProtocol*
|
|
||||||
instantiate_inbound_protocol(BMailAccountSettings* settings)
|
|
||||||
{
|
|
||||||
return new IMAPRootInboundProtocol(settings);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
#ifndef IMAP_ROOT_INBOUND_PROTOCOL_H
|
|
||||||
#define IMAP_ROOT_INBOUND_PROTOCOL_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "IMAPInboundProtocol.h"
|
|
||||||
|
|
||||||
#include "MailAddon.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef BObjectList<InboundProtocolThread> ProtocolThreadList;
|
|
||||||
|
|
||||||
|
|
||||||
/*! Hold the main INBOX mailbox and manage all the other other mailboxes. */
|
|
||||||
class IMAPRootInboundProtocol : public IMAPInboundProtocol {
|
|
||||||
public:
|
|
||||||
IMAPRootInboundProtocol(
|
|
||||||
BMailAccountSettings* settings);
|
|
||||||
~IMAPRootInboundProtocol();
|
|
||||||
|
|
||||||
//! thread safe interface
|
|
||||||
status_t Connect(const char* server,
|
|
||||||
const char* username, const char* password,
|
|
||||||
bool useSSL = true, int32 port = -1);
|
|
||||||
status_t Disconnect();
|
|
||||||
|
|
||||||
void SetStopNow();
|
|
||||||
|
|
||||||
status_t SyncMessages();
|
|
||||||
status_t FetchBody(const entry_ref& ref);
|
|
||||||
status_t MarkMessageAsRead(const entry_ref& ref,
|
|
||||||
read_flags flag = B_READ);
|
|
||||||
|
|
||||||
status_t DeleteMessage(const entry_ref& ref);
|
|
||||||
status_t AppendMessage(const entry_ref& ref);
|
|
||||||
|
|
||||||
status_t DeleteMessage(node_ref& node);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void _ShutdownChilds();
|
|
||||||
InboundProtocolThread* _FindThreadFor(const entry_ref& ref);
|
|
||||||
|
|
||||||
ProtocolThreadList fProtocolThreadList;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // IMAP_ROOT_INBOUND_PROTOCOL_H
|
|
||||||
@@ -1,304 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
||||||
* Copyright 2011, Clemens Zeidler <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "IMAPMailbox.h"
|
|
||||||
|
|
||||||
#include "IMAPHandler.h"
|
|
||||||
#include "IMAPStorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define DEBUG_IMAP_MAILBOX
|
|
||||||
#ifdef DEBUG_IMAP_MAILBOX
|
|
||||||
# include <stdio.h>
|
|
||||||
# define TRACE(x...) printf(x)
|
|
||||||
#else
|
|
||||||
# define TRACE(x...) ;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
MinMessage::MinMessage()
|
|
||||||
{
|
|
||||||
uid = 0;
|
|
||||||
flags = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
IMAPMailbox::IMAPMailbox(IMAPStorage& storage)
|
|
||||||
:
|
|
||||||
fStorage(storage),
|
|
||||||
|
|
||||||
fMailboxSelectHandler(*this),
|
|
||||||
fExistsHandler(*this),
|
|
||||||
fExpungeHandler(*this),
|
|
||||||
fFlagsHandler(*this),
|
|
||||||
|
|
||||||
fWatching(0),
|
|
||||||
|
|
||||||
fFetchBodyLimit(0)
|
|
||||||
{
|
|
||||||
fIMAPMailboxListener = &fNULLListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IMAPMailbox::~IMAPMailbox()
|
|
||||||
{
|
|
||||||
Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPMailbox::SetListener(IMAPMailboxListener* listener)
|
|
||||||
{
|
|
||||||
if (listener == NULL)
|
|
||||||
fIMAPMailboxListener = &fNULLListener;
|
|
||||||
else
|
|
||||||
fIMAPMailboxListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BString
|
|
||||||
IMAPMailbox::Mailbox()
|
|
||||||
{
|
|
||||||
return fSelectedMailbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::Sync()
|
|
||||||
{
|
|
||||||
TRACE("Sync\n");
|
|
||||||
if (fMailboxSelectHandler.NextUID() <= 1)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
_InstallUnsolicitedHandler(false);
|
|
||||||
|
|
||||||
fMessageList.clear();
|
|
||||||
FetchMessageListCommand fetchMessageListCommand(*this, &fMessageList,
|
|
||||||
fMailboxSelectHandler.NextUID());
|
|
||||||
status_t status = ProcessCommand(&fetchMessageListCommand);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
_InstallUnsolicitedHandler(true);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPMailbox::SupportWatching()
|
|
||||||
{
|
|
||||||
if (fCapabilityHandler.Capabilities().FindFirst("IDLE") < 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::StartWatchingMailbox(sem_id startedSem)
|
|
||||||
{
|
|
||||||
atomic_set(&fWatching, 1);
|
|
||||||
|
|
||||||
bool firstIDLE = true;
|
|
||||||
// refresh every 29 min
|
|
||||||
bigtime_t timeout = 1000 * 1000 * 60 * 29; // 29 min
|
|
||||||
status_t status;
|
|
||||||
while (true) {
|
|
||||||
int32 commandID = NextCommandID();
|
|
||||||
TRACE("IDLE ...\n");
|
|
||||||
status = SendCommand("IDLE", commandID);
|
|
||||||
if (firstIDLE) {
|
|
||||||
release_sem(startedSem);
|
|
||||||
firstIDLE = false;
|
|
||||||
}
|
|
||||||
if (status != B_OK)
|
|
||||||
break;
|
|
||||||
|
|
||||||
status = HandleResponse(commandID, timeout, false);
|
|
||||||
ProcessAfterQuacks(kIMAP4ClientTimeout);
|
|
||||||
|
|
||||||
if (atomic_get(&fWatching) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (status == B_TIMED_OUT) {
|
|
||||||
TRACE("Renew IDLE connection.\n");
|
|
||||||
status = SendRawCommand("DONE");
|
|
||||||
if (status != B_OK)
|
|
||||||
break;
|
|
||||||
// handle IDLE response and more
|
|
||||||
status = ProcessCommand("NOOP");
|
|
||||||
if (status != B_OK)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (status != B_OK)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic_set(&fWatching, 0);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::StopWatchingMailbox()
|
|
||||||
{
|
|
||||||
if (atomic_get(&fWatching) == 0)
|
|
||||||
return B_OK;
|
|
||||||
atomic_set(&fWatching, 0);
|
|
||||||
return SendRawCommand("DONE");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::CheckMailbox()
|
|
||||||
{
|
|
||||||
return ProcessCommand("NOOP");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::FetchMinMessage(int32 messageNumber, BPositionIO** data)
|
|
||||||
{
|
|
||||||
if (messageNumber <= 0)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
FetchMinMessageCommand fetchMinMessageCommand(*this, messageNumber,
|
|
||||||
&fMessageList, data);
|
|
||||||
return ProcessCommand(&fetchMinMessageCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::FetchBody(int32 messageNumber, BPositionIO* data)
|
|
||||||
{
|
|
||||||
if (data == NULL || messageNumber <= 0) {
|
|
||||||
delete data;
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
FetchBodyCommand fetchBodyCommand(*this, messageNumber, data);
|
|
||||||
status_t status = ProcessCommand(&fetchBodyCommand);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPMailbox::SetFetchBodyLimit(int32 limit)
|
|
||||||
{
|
|
||||||
fFetchBodyLimit = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::FetchMessage(int32 messageNumber)
|
|
||||||
{
|
|
||||||
return FetchMessages(messageNumber, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::FetchMessages(int32 firstMessage, int32 lastMessage)
|
|
||||||
{
|
|
||||||
FetchMessageCommand fetchCommand(*this, firstMessage, lastMessage,
|
|
||||||
fFetchBodyLimit);
|
|
||||||
return ProcessCommand(&fetchCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::FetchBody(int32 messageNumber)
|
|
||||||
{
|
|
||||||
if (fStorage.IsBodyFetched(MessageNumberToUID(messageNumber)))
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
BPositionIO* file;
|
|
||||||
status_t status = fStorage.OpenMessage(MessageNumberToUID(messageNumber),
|
|
||||||
&file);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// fetch command deletes the file
|
|
||||||
status = FetchBody(messageNumber, file);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::SetFlags(int32 messageNumber, int32 flags)
|
|
||||||
{
|
|
||||||
if (messageNumber <= 0)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
SetFlagsCommand setFlagsCommand(*this, messageNumber, flags);
|
|
||||||
return ProcessCommand(&setFlagsCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::AppendMessage(BPositionIO& message, off_t size, int32 flags,
|
|
||||||
time_t time)
|
|
||||||
{
|
|
||||||
AppendCommand appendCommand(*this, message, size, flags, time);
|
|
||||||
return ProcessCommand(&appendCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
IMAPMailbox::UIDToMessageNumber(int32 uid)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < fMessageList.size(); i++) {
|
|
||||||
if (fMessageList[i].uid == uid)
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
IMAPMailbox::MessageNumberToUID(int32 messageNumber)
|
|
||||||
{
|
|
||||||
int32 index = messageNumber - 1;
|
|
||||||
if (index < 0 || index >= (int32)fMessageList.size())
|
|
||||||
return -1;
|
|
||||||
return fMessageList[index].uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailbox::DeleteMessage(int32 uid, bool permanently)
|
|
||||||
{
|
|
||||||
int32 flags = fStorage.GetFlags(uid);
|
|
||||||
flags |= kDeleted;
|
|
||||||
status_t status = SetFlags(UIDToMessageNumber(uid), flags);
|
|
||||||
|
|
||||||
if (!permanently || status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// delete permanently by invoking expunge
|
|
||||||
ExpungeCommmand expungeCommand(*this);
|
|
||||||
return ProcessCommand(&expungeCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPMailbox::_InstallUnsolicitedHandler(bool install)
|
|
||||||
{
|
|
||||||
if (install) {
|
|
||||||
fHandlerList.AddItem(&fFlagsHandler, 0);
|
|
||||||
fHandlerList.AddItem(&fExpungeHandler, 0);
|
|
||||||
fHandlerList.AddItem(&fExistsHandler, 0);
|
|
||||||
} else {
|
|
||||||
fHandlerList.RemoveItem(&fFlagsHandler);
|
|
||||||
fHandlerList.RemoveItem(&fExpungeHandler);
|
|
||||||
fHandlerList.RemoveItem(&fExistsHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +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_MAILBOX_H
|
|
||||||
#define IMAP_MAILBOX_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <DataIO.h>
|
|
||||||
|
|
||||||
#include "IMAPHandler.h"
|
|
||||||
#include "IMAPProtocol.h"
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPMailboxListener {
|
|
||||||
public:
|
|
||||||
virtual ~IMAPMailboxListener() {}
|
|
||||||
|
|
||||||
virtual void HeaderFetched(int32 uid, BPositionIO* data,
|
|
||||||
bool bodyIsComing) {}
|
|
||||||
virtual void BodyFetched(int32 uid, BPositionIO* data) {}
|
|
||||||
|
|
||||||
virtual void NewMessagesToFetch(int32 nMessages) {}
|
|
||||||
virtual void FetchEnd() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPStorage;
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPMailbox : public IMAP::Protocol {
|
|
||||||
public:
|
|
||||||
IMAPMailbox(IMAPStorage& storage);
|
|
||||||
~IMAPMailbox();
|
|
||||||
|
|
||||||
void SetListener(IMAPMailboxListener* listener);
|
|
||||||
IMAPMailboxListener& Listener() { return *fIMAPMailboxListener; }
|
|
||||||
|
|
||||||
/*! Select mailbox and sync with the storage. */
|
|
||||||
BString Mailbox();
|
|
||||||
status_t Sync();
|
|
||||||
|
|
||||||
bool SupportWatching();
|
|
||||||
status_t StartWatchingMailbox(sem_id startedSem = -1);
|
|
||||||
status_t StopWatchingMailbox();
|
|
||||||
|
|
||||||
status_t CheckMailbox();
|
|
||||||
|
|
||||||
//! Low level fetch functions.
|
|
||||||
status_t FetchMinMessage(int32 messageNumber,
|
|
||||||
BPositionIO** data = NULL);
|
|
||||||
status_t FetchBody(int32 messageNumber,
|
|
||||||
BPositionIO* data);
|
|
||||||
|
|
||||||
void SetFetchBodyLimit(int32 limit);
|
|
||||||
int32 FetchBodyLimit() { return fFetchBodyLimit; }
|
|
||||||
status_t FetchMessage(int32 messageNumber);
|
|
||||||
status_t FetchMessages(int32 firstMessage,
|
|
||||||
int32 lastMessage);
|
|
||||||
status_t FetchBody(int32 messageNumber);
|
|
||||||
|
|
||||||
status_t SetFlags(int32 messageNumber, int32 flags);
|
|
||||||
status_t AppendMessage(BPositionIO& message, off_t size,
|
|
||||||
int32 flags = 0, time_t time = -1);
|
|
||||||
|
|
||||||
int32 GetCurrentMessageCount()
|
|
||||||
{ return fMessageList.size(); }
|
|
||||||
const IMAP::MinMessageList& GetMessageList() { return fMessageList; }
|
|
||||||
IMAPStorage& GetStorage() { return fStorage; }
|
|
||||||
|
|
||||||
int32 UIDToMessageNumber(int32 uid);
|
|
||||||
int32 MessageNumberToUID(int32 messageNumber);
|
|
||||||
|
|
||||||
status_t DeleteMessage(int32 uid, bool permanently);
|
|
||||||
private:
|
|
||||||
void _InstallUnsolicitedHandler(bool install);
|
|
||||||
|
|
||||||
IMAP::MinMessageList fMessageList;
|
|
||||||
|
|
||||||
IMAPStorage& fStorage;
|
|
||||||
IMAPMailboxListener* fIMAPMailboxListener;
|
|
||||||
IMAPMailboxListener fNULLListener;
|
|
||||||
|
|
||||||
IMAP::ExistsHandler fExistsHandler;
|
|
||||||
IMAP::ExpungeHandler fExpungeHandler;
|
|
||||||
// FlagsHandler fFlagsHandler;
|
|
||||||
|
|
||||||
int32 fWatching;
|
|
||||||
|
|
||||||
BString fSelectedMailbox;
|
|
||||||
|
|
||||||
int32 fFetchBodyLimit;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // IMAP_MAILBOX_H
|
|
||||||
@@ -1,513 +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 "IMAPStorage.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <Directory.h>
|
|
||||||
#include <Entry.h>
|
|
||||||
#include <NodeInfo.h>
|
|
||||||
#include <OS.h>
|
|
||||||
|
|
||||||
#include <mail_util.h>
|
|
||||||
|
|
||||||
#include "IMAPMailbox.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define DEBUG_IMAP_STORAGE
|
|
||||||
#ifdef DEBUG_IMAP_STORAGE
|
|
||||||
# include <stdio.h>
|
|
||||||
# define TRACE(x...) printf(x)
|
|
||||||
#else
|
|
||||||
# define TRACE(x...) /* nothing */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPMailboxSync::Sync(IMAPStorage& storage, IMAPMailbox& mailbox)
|
|
||||||
{
|
|
||||||
const MailEntryMap& files = storage.GetFiles();
|
|
||||||
const MinMessageList& messages = mailbox.GetMessageList();
|
|
||||||
|
|
||||||
for (MailEntryMap::const_iterator it = files.begin(); it != files.end();
|
|
||||||
it++) {
|
|
||||||
const StorageMailEntry& mailEntry = (*it).second;
|
|
||||||
bool found = false;
|
|
||||||
for (unsigned int a = 0; a < messages.size(); a++) {
|
|
||||||
const MinMessage& minMessage = messages[a];
|
|
||||||
if (mailEntry.uid == minMessage.uid) {
|
|
||||||
found = true;
|
|
||||||
if (mailEntry.flags != minMessage.flags)
|
|
||||||
storage.SetFlags(mailEntry.uid, minMessage.flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
storage.DeleteMessage(mailEntry.uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < messages.size(); i++) {
|
|
||||||
const MinMessage& minMessage = messages[i];
|
|
||||||
bool found = false;
|
|
||||||
for (MailEntryMap::const_iterator it = files.begin(); it != files.end();
|
|
||||||
it++) {
|
|
||||||
const StorageMailEntry& mailEntry = (*it).second;
|
|
||||||
|
|
||||||
if (mailEntry.uid == minMessage.uid) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
fToFetchList.push_back(i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mailbox.Listener().NewMessagesToFetch(fToFetchList.size());
|
|
||||||
|
|
||||||
// fetch headers in big bunches if possible
|
|
||||||
int32 fetchCount = 0;
|
|
||||||
int32 start = -1;
|
|
||||||
int32 end = -1;
|
|
||||||
int32 lastId = -1;
|
|
||||||
for (unsigned int i = 0; i < fToFetchList.size(); i++) {
|
|
||||||
if (mailbox.StopNow())
|
|
||||||
return B_ERROR;
|
|
||||||
|
|
||||||
int32 current = fToFetchList[i];
|
|
||||||
fetchCount++;
|
|
||||||
|
|
||||||
if (start < 0) {
|
|
||||||
start = current;
|
|
||||||
lastId = current;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (current - 1 == lastId) {
|
|
||||||
end = current;
|
|
||||||
lastId = current;
|
|
||||||
// limit to a fix number to make it interruptable see StopNow()
|
|
||||||
if (fetchCount < 250)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchCount = 0;
|
|
||||||
mailbox.FetchMessages(start, end);
|
|
||||||
start = -1;
|
|
||||||
end = -1;
|
|
||||||
}
|
|
||||||
if (start > 0)
|
|
||||||
mailbox.FetchMessages(start, end);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
IMAPStorage::IMAPStorage()
|
|
||||||
{
|
|
||||||
fLoadDatabaseLock = create_sem(1, "sync lock");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IMAPStorage::~IMAPStorage()
|
|
||||||
{
|
|
||||||
delete_sem(fLoadDatabaseLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IMAPStorage::SetTo(const char* dir)
|
|
||||||
{
|
|
||||||
fMailboxPath.SetTo(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::StartReadDatabase()
|
|
||||||
{
|
|
||||||
status_t status = create_directory(fMailboxPath.Path(), 0755);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
thread_id id = spawn_thread(_ReadFilesThreadFunction, "read mailbox",
|
|
||||||
B_LOW_PRIORITY, this);
|
|
||||||
if (id < 0)
|
|
||||||
return id;
|
|
||||||
|
|
||||||
// will be unlocked from thread
|
|
||||||
acquire_sem(fLoadDatabaseLock);
|
|
||||||
status = resume_thread(id);
|
|
||||||
if (status != B_OK)
|
|
||||||
release_sem(fLoadDatabaseLock);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::WaitForDatabaseRead()
|
|
||||||
{
|
|
||||||
// just wait for thread
|
|
||||||
if (acquire_sem(fLoadDatabaseLock) != B_OK)
|
|
||||||
return B_ERROR;
|
|
||||||
release_sem(fLoadDatabaseLock);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::AddNewMessage(int32 uid, int32 flags, BPositionIO** file)
|
|
||||||
{
|
|
||||||
if (file != NULL)
|
|
||||||
*file = NULL;
|
|
||||||
// TODO: make sure there is not a valid mail with this name
|
|
||||||
BString fileName = "Downloading file... uid: ";
|
|
||||||
fileName << uid;
|
|
||||||
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(fileName);
|
|
||||||
TRACE("AddNewMessage %s\n", filePath.Path());
|
|
||||||
BFile* newFile = new BFile(filePath.Path(), B_READ_WRITE | B_CREATE_FILE
|
|
||||||
| B_ERASE_FILE);
|
|
||||||
if (newFile == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
StorageMailEntry storageEntry;
|
|
||||||
storageEntry.uid = uid;
|
|
||||||
storageEntry.flags = flags;
|
|
||||||
storageEntry.fileName = fileName;
|
|
||||||
newFile->GetNodeRef(&storageEntry.nodeRef);
|
|
||||||
|
|
||||||
if (_WriteUniqueID(*newFile, uid) != B_OK) {
|
|
||||||
delete newFile;
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t status = _WriteFlags(flags, *newFile);
|
|
||||||
if (status != B_OK) {
|
|
||||||
delete newFile;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file)
|
|
||||||
*file = newFile;
|
|
||||||
else
|
|
||||||
delete newFile;
|
|
||||||
fMailEntryMap[uid] = storageEntry;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::OpenMessage(int32 uid, BPositionIO** file)
|
|
||||||
{
|
|
||||||
*file = NULL;
|
|
||||||
|
|
||||||
MailEntryMap::const_iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
const StorageMailEntry& storageEntry = (*it).second;
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(storageEntry.fileName);
|
|
||||||
BFile* ourFile = new BFile(filePath.Path(), B_READ_WRITE);
|
|
||||||
if (!ourFile)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
status_t status = ourFile->InitCheck();
|
|
||||||
if (status != B_OK) {
|
|
||||||
delete *file;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
*file = ourFile;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::DeleteMessage(int32 uid)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
const StorageMailEntry& storageEntry = (*it).second;
|
|
||||||
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(storageEntry.fileName);
|
|
||||||
BEntry entry(filePath.Path());
|
|
||||||
TRACE("IMAPStorage::DeleteMessage %s, %" B_PRId32 "\n", filePath.Path(), uid);
|
|
||||||
|
|
||||||
status_t status = entry.Remove();
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
fMailEntryMap.erase(it);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::SetFlags(int32 uid, int32 flags)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
StorageMailEntry& entry = (*it).second;
|
|
||||||
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(entry.fileName);
|
|
||||||
BNode node(filePath.Path());
|
|
||||||
|
|
||||||
status_t status = _WriteFlags(flags, node);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
entry.flags = flags;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
IMAPStorage::GetFlags(int32 uid)
|
|
||||||
{
|
|
||||||
MailEntryMap::const_iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return -1;
|
|
||||||
const StorageMailEntry& entry = (*it).second;
|
|
||||||
return entry.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::SetFileName(int32 uid, const BString& name)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return -1;
|
|
||||||
StorageMailEntry& storageEntry = (*it).second;
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(storageEntry.fileName);
|
|
||||||
BEntry entry(filePath.Path());
|
|
||||||
status_t status = entry.Rename(name);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
storageEntry.fileName = name;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::FileRenamed(const entry_ref& from, const entry_ref& to)
|
|
||||||
{
|
|
||||||
int32 uid = RefToUID(from);
|
|
||||||
if (uid < 0)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
fMailEntryMap[uid].fileName = to.name;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPStorage::HasFile(int32 uid)
|
|
||||||
{
|
|
||||||
MailEntryMap::const_iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::SetCompleteMessageSize(int32 uid, int32 size)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return false;
|
|
||||||
StorageMailEntry& storageEntry = (*it).second;
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(storageEntry.fileName);
|
|
||||||
BNode node(filePath.Path());
|
|
||||||
|
|
||||||
if (node.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32)) < 0)
|
|
||||||
return B_ERROR;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StorageMailEntry*
|
|
||||||
IMAPStorage::GetEntryForRef(const node_ref& ref)
|
|
||||||
{
|
|
||||||
for (MailEntryMap::iterator it = fMailEntryMap.begin();
|
|
||||||
it != fMailEntryMap.end(); it++) {
|
|
||||||
StorageMailEntry& mailEntry = (*it).second;
|
|
||||||
if (mailEntry.nodeRef == ref)
|
|
||||||
return &mailEntry;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPStorage::IsBodyFetched(int32 uid)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return false;
|
|
||||||
StorageMailEntry& storageEntry = (*it).second;
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(storageEntry.fileName);
|
|
||||||
BNode node(filePath.Path());
|
|
||||||
|
|
||||||
char buffer[B_MIME_TYPE_LENGTH];
|
|
||||||
BNodeInfo info(&node);
|
|
||||||
info.GetType(buffer);
|
|
||||||
|
|
||||||
return strcmp(buffer, B_MAIL_TYPE) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
IMAPStorage::RefToUID(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
for (MailEntryMap::iterator it = fMailEntryMap.begin();
|
|
||||||
it != fMailEntryMap.end(); it++) {
|
|
||||||
StorageMailEntry& mailEntry = (*it).second;
|
|
||||||
if (mailEntry.fileName == ref.name)
|
|
||||||
return mailEntry.uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not found try to fix internal name
|
|
||||||
BDirectory dir(fMailboxPath.Path());
|
|
||||||
if (!dir.Contains(ref.name))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
BNode node(&ref);
|
|
||||||
int32 uid;
|
|
||||||
if (ReadUniqueID(node, uid) != B_OK)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return -1;
|
|
||||||
it->second.fileName = ref.name;
|
|
||||||
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
IMAPStorage::UIDToRef(int32 uid, entry_ref& ref)
|
|
||||||
{
|
|
||||||
MailEntryMap::iterator it = fMailEntryMap.find(uid);
|
|
||||||
if (it == fMailEntryMap.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
StorageMailEntry& mailEntry = (*it).second;
|
|
||||||
BPath filePath = fMailboxPath;
|
|
||||||
filePath.Append(mailEntry.fileName);
|
|
||||||
|
|
||||||
BEntry entry(filePath.Path());
|
|
||||||
if (entry.GetRef(&ref) == B_OK)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::_ReadFilesThreadFunction(void* data)
|
|
||||||
{
|
|
||||||
IMAPStorage* storage = (IMAPStorage*)data;
|
|
||||||
return storage->_ReadFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::_ReadFiles()
|
|
||||||
{
|
|
||||||
fMailEntryMap.clear();
|
|
||||||
|
|
||||||
BDirectory directory(fMailboxPath.Path());
|
|
||||||
|
|
||||||
entry_ref ref;
|
|
||||||
while (directory.GetNextRef(&ref) != B_ENTRY_NOT_FOUND) {
|
|
||||||
BNode node(&ref);
|
|
||||||
if (node.InitCheck() != B_OK || !node.IsFile())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char buffer[B_MIME_TYPE_LENGTH];
|
|
||||||
BNodeInfo info(&node);
|
|
||||||
info.GetType(buffer);
|
|
||||||
|
|
||||||
// maybe interrupted downloads ignore them
|
|
||||||
if (strcmp(buffer, "text/x-partial-email") != 0
|
|
||||||
&& strcmp(buffer, B_MAIL_TYPE) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
StorageMailEntry entry;
|
|
||||||
entry.fileName = ref.name;
|
|
||||||
if (ReadUniqueID(node, entry.uid) != B_OK) {
|
|
||||||
TRACE("IMAPStorage::_ReadFilesThread() failed to read uid %s\n",
|
|
||||||
ref.name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.ReadAttr("MAIL:server_flags", B_INT32_TYPE, 0, &entry.flags,
|
|
||||||
sizeof(int32)) != sizeof(int32))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
node.GetNodeRef(&entry.nodeRef);
|
|
||||||
|
|
||||||
fMailEntryMap[entry.uid] = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
release_sem(fLoadDatabaseLock);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::_WriteFlags(int32 flags, BNode& node)
|
|
||||||
{
|
|
||||||
if ((flags & kSeen) != 0)
|
|
||||||
write_read_attr(node, B_READ);
|
|
||||||
else
|
|
||||||
write_read_attr(node, B_UNREAD);
|
|
||||||
|
|
||||||
ssize_t writen = node.WriteAttr("MAIL:server_flags", B_INT32_TYPE, 0,
|
|
||||||
&flags, sizeof(int32));
|
|
||||||
if (writen != sizeof(int32))
|
|
||||||
return writen;
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::ReadUniqueID(BNode& node, int32& uid)
|
|
||||||
{
|
|
||||||
const uint32 kMaxUniqueLength = 32;
|
|
||||||
char uidString[kMaxUniqueLength];
|
|
||||||
memset(uidString, 0, kMaxUniqueLength);
|
|
||||||
if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString,
|
|
||||||
kMaxUniqueLength) < 0)
|
|
||||||
return B_ERROR;
|
|
||||||
uid = atoi(uidString);
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
IMAPStorage::_WriteUniqueID(BNode& node, int32 uid)
|
|
||||||
{
|
|
||||||
BString uidString;
|
|
||||||
uidString << uid;
|
|
||||||
ssize_t written = node.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0,
|
|
||||||
uidString.String(), uidString.Length());
|
|
||||||
if (written < 0)
|
|
||||||
return written;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
@@ -1,102 +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_STORAGE_H
|
|
||||||
#define IMAP_STORAGE_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <File.h>
|
|
||||||
#include <kernel/OS.h>
|
|
||||||
#include <Path.h>
|
|
||||||
#include <String.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct StorageMailEntry {
|
|
||||||
int32 uid;
|
|
||||||
int32 flags;
|
|
||||||
node_ref nodeRef;
|
|
||||||
BString fileName;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
typedef std::map<int32, StorageMailEntry> MailEntryMap;
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPStorage {
|
|
||||||
public:
|
|
||||||
enum MessageState {
|
|
||||||
kHeaderDownloaded = 0x01,
|
|
||||||
kBodyDownloaded = 0x02
|
|
||||||
};
|
|
||||||
|
|
||||||
IMAPStorage();
|
|
||||||
~IMAPStorage();
|
|
||||||
|
|
||||||
void SetTo(const char* dir);
|
|
||||||
|
|
||||||
status_t StartReadDatabase();
|
|
||||||
status_t WaitForDatabaseRead();
|
|
||||||
|
|
||||||
status_t AddNewMessage(int32 uid, int32 flags,
|
|
||||||
BPositionIO** file = NULL);
|
|
||||||
status_t OpenMessage(int32 uid, BPositionIO** file);
|
|
||||||
|
|
||||||
/*! Remove the message from the storage. */
|
|
||||||
status_t DeleteMessage(int32 uid);
|
|
||||||
|
|
||||||
status_t SetFlags(int32 uid, int32 flags);
|
|
||||||
int32 GetFlags(int32 uid);
|
|
||||||
|
|
||||||
status_t SetFileName(int32 uid, const BString& name);
|
|
||||||
status_t FileRenamed(const entry_ref& from,
|
|
||||||
const entry_ref& to);
|
|
||||||
|
|
||||||
const MailEntryMap& GetFiles() { return fMailEntryMap; }
|
|
||||||
bool HasFile(int32 uid);
|
|
||||||
|
|
||||||
status_t SetCompleteMessageSize(int32 uid, int32 size);
|
|
||||||
|
|
||||||
bool IsBodyFetched(int32 uid);
|
|
||||||
|
|
||||||
StorageMailEntry* GetEntryForRef(const node_ref& ref);
|
|
||||||
int32 RefToUID(const entry_ref& ref);
|
|
||||||
bool UIDToRef(int32 uid, entry_ref& ref);
|
|
||||||
|
|
||||||
status_t ReadUniqueID(BNode& node, int32& uid);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static status_t _ReadFilesThreadFunction(void* data);
|
|
||||||
status_t _ReadFiles();
|
|
||||||
|
|
||||||
status_t _WriteFlags(int32 flags, BNode& node);
|
|
||||||
status_t _WriteUniqueID(BNode& node, int32 uid);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BPath fMailboxPath;
|
|
||||||
|
|
||||||
sem_id fLoadDatabaseLock;
|
|
||||||
MailEntryMap fMailEntryMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IMAPMailbox;
|
|
||||||
typedef std::vector<int32> MessageNumberList;
|
|
||||||
|
|
||||||
class IMAPMailboxSync {
|
|
||||||
public:
|
|
||||||
status_t Sync(IMAPStorage& storage,
|
|
||||||
IMAPMailbox& mailbox);
|
|
||||||
const MessageNumberList& ToFetchList() { return fToFetchList; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
MessageNumberList fToFetchList;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // IMAP_STORAGE_H
|
|
||||||
Reference in New Issue
Block a user