Author: czeidler Date: 2011-02-09 02:54:18 +0100 (Wed, 09 Feb 2011) New Revision: 40399 Changeset: http://dev.haiku-os.org/changeset/40399 Added: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPRootInboundProtocol.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPRootInboundProtocol.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/ haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPFolders.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPFolders.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPHandler.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPHandler.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPMailbox.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPMailbox.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPParser.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPParser.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPProtocol.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPProtocol.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPStorage.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/IMAPStorage.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/ServerConnection.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/ServerConnection.h Removed: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/NestedString.cpp haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/NestedString.h haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_client.cpp Modified: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/Jamfile haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/imap_config.cpp Log: Map one or more server mailboxes two a local directory. All subscribed mailboxes are fetched automatically and populated under the root account folder. On startup the local copy is synced with the server, means all local changes are rejected. (We should think about store offline changes and apply them when online) Also the read flags are synced with the server. This makes it easy to have the same mailbox state on different machines. Messages could be deleted by delete them form the folder (not when moving them to trash) This maybe needs some more thoughts but its a save solution. One problem with moving it to trash is that you also want to have the option to restore it again. If it is a header only messages and you delete it from the server you lose the body part. Complete messages could theoretically be append to the mailbox again when restoring the mail form trash. Append is commented out though... An solution for the delete problem would be to move the message to a trash folder on the server. Moving mails on the server is not implemented yet, though. You can subscribe or unsubscribe to a mailbox using the imap pref panel. The settings are written to the server and are not stored locally. Add some helper classes which could also be used for POP and SMTP: ServerConnection: abstract ssl or socket connection ConnectionReader (still a bit IMAP specific but could be easily separated IMHO): read complete lines or a bunch of data more efficient. Old implementation did it byte by byte, this class read data in bunches and buffer the left over for the next request... Added: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.cpp =================================================================== --- haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.cpp (rev 0) +++ haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.cpp 2011-02-09 01:54:18 UTC (rev 40399) @@ -0,0 +1,362 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2011, Clemens Zeidler <haiku@xxxxxxxxxxxxxxxxxx> + * Distributed under the terms of the MIT License. + */ + + +#include "IMAPFolderConfig.h" + +#include <ControlLook.h> +#include <ListItem.h> +#include <SpaceLayoutItem.h> +#include <StringView.h> + +#include "ALMLayout.h" + +#include <crypt.h> + + +class EditableListItem { +public: + EditableListItem(); + virtual ~EditableListItem() {} + + virtual void MouseDown(BPoint where) = 0; + virtual void MouseUp(BPoint where) = 0; + + void SetListView(BListView* list) + { fListView = list; } + +protected: + BListView* fListView; +}; + + +EditableListItem::EditableListItem() + : + fListView(NULL) +{ + +} + + +class CheckBoxItem : public BStringItem, public EditableListItem { +public: + CheckBoxItem(const char* text, bool checked); + + void DrawItem(BView* owner, BRect itemRect, + bool drawEverything = false); + + void MouseDown(BPoint where); + void MouseUp(BPoint where); + + bool Checked() { return fChecked; } +private: + BRect fBoxRect; + bool fChecked; + bool fMouseDown; +}; + + + +CheckBoxItem::CheckBoxItem(const char* text, bool checked) + : + BStringItem(text), + fChecked(checked), + fMouseDown(false) +{ + +} + + +void +CheckBoxItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything) +{ + BStringItem::DrawItem(owner, itemRect, drawEverything); + + if (!be_control_look) + return; + + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + uint32 flags = 0; + if (fMouseDown) + flags |= BControlLook::B_CLICKED; + if (fChecked) + flags |= BControlLook::B_ACTIVATED; + font_height fontHeight; + owner->GetFontHeight(&fontHeight); + BRect boxRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent), + ceilf(5.0f + fontHeight.ascent)); + + + fBoxRect.left = itemRect.right - boxRect.Width(); + fBoxRect.top = itemRect.top + (itemRect.Height() - boxRect.Height()) / 2; + fBoxRect.right = itemRect.right; + fBoxRect.bottom = itemRect.top + boxRect.Height(); + + be_control_look->DrawCheckBox(owner, fBoxRect, fBoxRect, base, flags); +} + + +void +CheckBoxItem::MouseDown(BPoint where) +{ + if (!fBoxRect.Contains(where)) + return; + + fMouseDown = true; + + fListView->InvalidateItem(fListView->IndexOf(this)); +} + + +void +CheckBoxItem::MouseUp(BPoint where) +{ + if (!fMouseDown) + return; + fMouseDown = false; + + if (fBoxRect.Contains(where)) { + if (fChecked) + fChecked = false; + else + fChecked = true; + } + + fListView->InvalidateItem(fListView->IndexOf(this)); +} + + +class EditListView : public BListView { +public: + EditListView(const char* name, + list_view_type type + = B_SINGLE_SELECTION_LIST, + uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS + | B_NAVIGABLE); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void FrameResized(float newWidth, float newHeight); + +private: + EditableListItem* fLastMouseDown; +}; + + +EditListView::EditListView(const char* name, list_view_type type, uint32 flags) + : + BListView(name, type, flags), + fLastMouseDown(NULL) +{ + +} + + +void +EditListView::MouseDown(BPoint where) +{ + BListView::MouseDown(where); + + int32 index = IndexOf(where); + BListItem* item = ItemAt(index); + if (!item) + return; + EditableListItem* handler = dynamic_cast<EditableListItem*>(item); + if (!handler) + return; + + fLastMouseDown = handler; + handler->MouseDown(where); + SetMouseEventMask(B_POINTER_EVENTS); +} + + +void +EditListView::MouseUp(BPoint where) +{ + BListView::MouseUp(where); + if (fLastMouseDown) { + fLastMouseDown->MouseUp(where); + fLastMouseDown = NULL; + } + + int32 index = IndexOf(where); + BListItem* item = ItemAt(index); + if (!item) + return; + EditableListItem* handler = dynamic_cast<EditableListItem*>(item); + if (!handler) + return; + + handler->MouseUp(where); +} + + +void +EditListView::FrameResized(float newWidth, float newHeight) +{ + Invalidate(); +} + + +class StatusWindow : public BWindow { +public: + StatusWindow(const char* text) + : + BWindow(BRect(0, 0, 10, 10), "status", B_MODAL_WINDOW_LOOK, + B_MODAL_APP_WINDOW_FEEL, B_NO_WORKSPACE_ACTIVATION | B_NOT_ZOOMABLE + | B_AVOID_FRONT | B_NOT_RESIZABLE) + { + BView* rootView = new BView(Bounds(), "root", B_FOLLOW_ALL, + B_WILL_DRAW); + AddChild(rootView); + rootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + float spacing = be_control_look->DefaultItemSpacing(); + BALMLayout* layout = new BALMLayout(spacing); + rootView->SetLayout(layout); + layout->SetInset(spacing); + + BStringView* string = new BStringView("text", text); + layout->AddView(string, layout->Left(), layout->Top(), layout->Right(), + layout->Bottom()); + BSize min = layout->MinSize(); + ResizeTo(min.Width(), min.Height()); + CenterOnScreen(); + } +}; + + +const uint32 kMsgApplyButton = '&Abu'; +const uint32 kMsgInit = '&Ini'; + + +FolderConfigWindow::FolderConfigWindow(BRect parent, const BMessage& settings) + : + BWindow(BRect(0, 0, 250, 300), "IMAP Folders", B_TITLED_WINDOW_LOOK, + B_MODAL_APP_WINDOW_FEEL, B_NO_WORKSPACE_ACTIVATION | B_NOT_ZOOMABLE + | B_AVOID_FRONT), + fSettings(settings) +{ + BView* rootView = new BView(Bounds(), "root", B_FOLLOW_ALL, B_WILL_DRAW); + AddChild(rootView); + rootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + float spacing = be_control_look->DefaultItemSpacing(); + BALMLayout* layout = new BALMLayout(spacing); + rootView->SetLayout(layout); + layout->SetInset(spacing); + + fFolderListView = new EditListView("IMAP Folders"); + fFolderListView->SetExplicitPreferredSize(BSize(B_SIZE_UNLIMITED, + B_SIZE_UNLIMITED)); + fApplyButton = new BButton("Apply", "Apply", new BMessage(kMsgApplyButton)); + + layout->AddView(fFolderListView, layout->Left(), layout->Top(), + layout->Right(), layout->Bottom()); + + GroupItem item = GroupItem(fFolderListView) + / (GroupItem(BSpaceLayoutItem::CreateGlue()) + | GroupItem(fApplyButton)); + layout->BuildLayout(item); + + PostMessage(kMsgInit); + + BSize min = layout->MinSize(); + BSize max = layout->MaxSize(); + SetSizeLimits(min.Width(), max.Width(), min.Height(), max.Height()); + + CenterIn(parent); +} + + +void +FolderConfigWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgInit: + _LoadFolders(); + break; + + case kMsgApplyButton: + _ApplyChanges(); + PostMessage(B_QUIT_REQUESTED); + break; + + default: + BWindow::MessageReceived(message); + } +} + + +void +FolderConfigWindow::_LoadFolders() +{ + StatusWindow* status = new StatusWindow("Fetching IMAP folders, have " + "patience..."); + status->Show(); + + BString server; + fSettings.FindString("server", &server); + int32 ssl; + fSettings.FindInt32("flavor", &ssl); + bool useSSL = false; + if (ssl == 1) + useSSL = true; + + BString username; + fSettings.FindString("username", &username); + + BString password; + char* passwd = get_passwd(&fSettings, "cpasswd"); + if (passwd) { + password = passwd; + delete[] passwd; + } + + fIMAPFolders.Connect(server, username, password, useSSL); + fIMAPFolders.GetFolders(fFolderList); + for (unsigned int i = 0; i < fFolderList.size(); i++) { + FolderInfo& info = fFolderList[i]; + CheckBoxItem* item = new CheckBoxItem(info.folder, info.subscribed); + fFolderListView->AddItem(item); + item->SetListView(fFolderListView); + } + status->PostMessage(B_QUIT_REQUESTED); +} + + +void +FolderConfigWindow::_ApplyChanges() +{ + bool haveChanges = false; + for (unsigned int i = 0; i < fFolderList.size(); i++) { + FolderInfo& info = fFolderList[i]; + CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i); + if ((info.subscribed && !item->Checked()) + || (!info.subscribed && item->Checked())) { + haveChanges = true; + break; + } + } + if (!haveChanges) + return; + + StatusWindow* status = new StatusWindow("Subcribe / Unsuscribe IMAP " + "folders, have patience..."); + status->Show(); + + for (unsigned int i = 0; i < fFolderList.size(); i++) { + FolderInfo& info = fFolderList[i]; + CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i); + if (info.subscribed && !item->Checked()) + fIMAPFolders.UnsubscribeFolder(info.folder); + else if (!info.subscribed && item->Checked()) + fIMAPFolders.SubscribeFolder(info.folder); + } + + status->PostMessage(B_QUIT_REQUESTED); +} + Added: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.h =================================================================== --- haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.h (rev 0) +++ haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPFolderConfig.h 2011-02-09 01:54:18 UTC (rev 40399) @@ -0,0 +1,38 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2011, Clemens Zeidler <haiku@xxxxxxxxxxxxxxxxxx> + * Distributed under the terms of the MIT License. + */ +#ifndef IMAP_FOLDER_CONFIG_H +#define IMAP_FOLDER_CONFIG_H + + +#include <Button.h> +#include <ListView.h> +#include <Window.h> + +#include <MailSettings.h> + +#include "IMAPFolders.h" + + +class FolderConfigWindow : public BWindow { +public: + FolderConfigWindow(BRect rect, + const BMessage& settings); + + void MessageReceived(BMessage* message); + +private: + void _LoadFolders(); + void _ApplyChanges(); + + IMAPFolders fIMAPFolders; + BListView* fFolderListView; + BButton* fApplyButton; + const BMessage fSettings; + FolderList fFolderList; +}; + + +#endif //IMAP_FOLDER_CONFIG_H Added: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.cpp =================================================================== --- haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.cpp (rev 0) +++ haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.cpp 2011-02-09 01:54:18 UTC (rev 40399) @@ -0,0 +1,575 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2011, Clemens Zeidler <haiku@xxxxxxxxxxxxxxxxxx> + * 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> + + +DispatcherIMAPListener::DispatcherIMAPListener(MailProtocol& protocol, + IMAPStorage& storage) + : + fProtocol(protocol), + fStorage(storage) +{ + +} + + +void +DispatcherIMAPListener::HeaderFetched(int32 uid, BPositionIO* data) +{ + BFile* file = dynamic_cast<BFile*>(data); + if (file == NULL) + return; + entry_ref ref; + if (!fStorage.UIDToRef(uid, ref)) + return; + + fProtocol.NotifyHeaderFetched(ref, file); +} + + +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); +} + + +void +DispatcherIMAPListener::NewMessagesToFetch(int32 nMessages) +{ + fProtocol.NotifyNewMessagesToFetch(nMessages); +} + + +const uint32 kMsgStartWatching = '&StW'; + + +IMAPMailboxThread::IMAPMailboxThread(IMAPInboundProtocol& protocol, + IMAPMailbox& mailbox) + : + BLooper("IMAPMailboxThread"), + + fProtocol(protocol), + fIMAPMailbox(mailbox), + + fIsWatching(false) +{ + +} + + +void +IMAPMailboxThread::MessageReceived(BMessage* message) +{ + status_t status = B_ERROR; + + switch (message->what) { + case kMsgStartWatching: + status = fIMAPMailbox.StartWatchingMailbox(); + if (status != B_OK) + fProtocol.Disconnect(); + + fLock.Lock(); + fIsWatching = false; + fLock.Unlock(); + break; + + default: + BLooper::MessageReceived(message); + } +} + + +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; + fIsWatching = true; + autolock.Unlock(); + PostMessage(kMsgStartWatching); + } 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 + const uint32 kMsgNoMeaning = '&NME'; + BMessage reply; + return BMessenger(this).SendMessage(kMsgNoMeaning, &reply); +} + + +// #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); +} + + +#include <stdio.h> +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: + printf("entry created\n"); + break; + message->FindInt32("device", &ref.device); + message->FindInt64("directory", &ref.directory); + message->FindString("name", &name); + ref.set_name(name); + //TODO not thread safe + fProtocol->AppendMessage(ref); + break; + + case B_ENTRY_REMOVED: + message->FindInt32("device", &nref.device); + message->FindInt64("node", &nref.node); + //TODO not thread safe + fProtocol->DeleteMessage(nref); + break; + + case B_ENTRY_MOVED: + { + printf("entry moved\n"); + 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); + fIMAPMailboxThread->Run(); + + // set watch directory + fINBOXWatcher = new MailboxWatcher(this); + AddHandler(fINBOXWatcher); +} + + +IMAPInboundProtocol::~IMAPInboundProtocol() +{ + fIMAPMailboxThread->StopWatchingMailbox(); + + RemoveHandler(fINBOXWatcher); + delete fINBOXWatcher; + + fIMAPMailboxThread->Lock(); + fIMAPMailboxThread->Quit(); +} + + +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(4); + ReportProgress(0, 1, statusMessage); + + status_t status = fIMAPMailbox.Connect(server, username, password, useSSL, + port); + if (status != B_OK) { + statusMessage = "Failed to login: "; + statusMessage += fIMAPMailbox.CommandError(); + ShowError(statusMessage); + ResetProgress(); + return status; + } + + ReportProgress(0, 1, "Load database"); + + fStorage.StartReadDatabase(); + status = fIMAPMailbox.SelectMailbox(fMailboxName); + if (status != B_OK) { + fStorage.WaitForDatabaseReaded(); + 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.WaitForDatabaseReaded(); + ShowError("Failed to sync mailbox"); + ResetProgress(); + return status; + } + + status = fStorage.WaitForDatabaseReaded(); + 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() +{ + return fIsConnected; +} + + +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; + } + + // restart mailbox's + 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); + status_t status = fIMAPMailbox.FetchBody(fIMAPMailbox.UIDToMessageNumber( + uid)); + fIMAPMailboxThread->SyncAndStartWatchingMailbox(); + return status; +} + + +status_t +IMAPInboundProtocol::MarkMessageAsRead(const entry_ref& ref, bool read) +{ + if (!IsConnected()) + Connect(fServer, fUsername, fPassword, fUseSSL); + + fIMAPMailboxThread->StopWatchingMailbox(); + int32 uid = fStorage.RefToUID(ref); + int32 flags = fStorage.GetFlags(uid); + if (read) + flags |= kSeen; + else + flags &= ~kSeen; + status_t status = fIMAPMailbox.SetFlags( + fIMAPMailbox.UIDToMessageNumber(uid), flags); + fIMAPMailboxThread->SyncAndStartWatchingMailbox(); + return status; +} + + +status_t +IMAPInboundProtocol::DeleteMessage(const entry_ref& ref) +{ + if (!IsConnected()) + Connect(fServer, fUsername, fPassword, fUseSSL); + + fIMAPMailboxThread->StopWatchingMailbox(); + int32 uid = fStorage.RefToUID(ref); + int32 flags = fStorage.GetFlags(uid); + flags |= kDeleted; + status_t status = fIMAPMailbox.SetFlags( + fIMAPMailbox.UIDToMessageNumber(uid), flags); + 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; + int32 flags = fStorage.GetFlags(uid); + flags |= kDeleted; + status_t status = fIMAPMailbox.SetFlags( + fIMAPMailbox.UIDToMessageNumber(uid), flags); + fIMAPMailboxThread->SyncAndStartWatchingMailbox(); + return status; +} Added: haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.h =================================================================== --- haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.h (rev 0) +++ haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPInboundProtocol.h 2011-02-09 01:54:18 UTC (rev 40399) @@ -0,0 +1,133 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. [... truncated: 4018 lines follow ...]