[haiku-commits] r40399 - in haiku/trunk/src/add-ons/mail_daemon/inbound_protocols/imap: . imap_lib

  • From: clemens.zeidler@xxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 9 Feb 2011 02:54:18 +0100 (CET)

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 ...]

Other related posts: