1186c96d5SAxel Dörfler /*
281805393SAxel Dörfler * Copyright 2013-2016, Axel Dörfler, axeld@pinc-software.de.
3186c96d5SAxel Dörfler * Distributed under the terms of the MIT License.
4186c96d5SAxel Dörfler */
5186c96d5SAxel Dörfler
6186c96d5SAxel Dörfler
7186c96d5SAxel Dörfler #include "IMAPProtocol.h"
8186c96d5SAxel Dörfler
9186c96d5SAxel Dörfler #include <Directory.h>
1081805393SAxel Dörfler #include <Messenger.h>
11186c96d5SAxel Dörfler
12186c96d5SAxel Dörfler #include "IMAPConnectionWorker.h"
13a4bdd26dSAxel Dörfler #include "IMAPFolder.h"
14a4bdd26dSAxel Dörfler #include "Utilities.h"
15186c96d5SAxel Dörfler
16618cc43bSAxel Dörfler #include <mail_util.h>
17618cc43bSAxel Dörfler
18186c96d5SAxel Dörfler
IMAPProtocol(const BMailAccountSettings & settings)19186c96d5SAxel Dörfler IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
20186c96d5SAxel Dörfler :
2162eec600SAxel Dörfler BInboundMailProtocol("IMAP", settings),
2228ee6c28SAxel Dörfler fSettings(settings.Name(), settings.InboundSettings()),
237993ddfaSAxel Dörfler fWorkers(5, false)
24186c96d5SAxel Dörfler {
25186c96d5SAxel Dörfler BPath destination = fSettings.Destination();
26186c96d5SAxel Dörfler
27186c96d5SAxel Dörfler status_t status = create_directory(destination.Path(), 0755);
28186c96d5SAxel Dörfler if (status != B_OK) {
2928ee6c28SAxel Dörfler fprintf(stderr, "IMAP: Could not create destination directory %s: %s\n",
30186c96d5SAxel Dörfler destination.Path(), strerror(status));
31186c96d5SAxel Dörfler }
32186c96d5SAxel Dörfler
3381805393SAxel Dörfler mutex_init(&fWorkerLock, "imap worker lock");
3481805393SAxel Dörfler
35186c96d5SAxel Dörfler PostMessage(B_READY_TO_RUN);
36186c96d5SAxel Dörfler }
37186c96d5SAxel Dörfler
38186c96d5SAxel Dörfler
~IMAPProtocol()39186c96d5SAxel Dörfler IMAPProtocol::~IMAPProtocol()
40186c96d5SAxel Dörfler {
416fa27973SPeter Kosyh MutexLocker locker(fWorkerLock);
426fa27973SPeter Kosyh std::vector<thread_id> threads;
436fa27973SPeter Kosyh for (int32 i = 0; i < fWorkers.CountItems(); i++) {
446fa27973SPeter Kosyh threads.push_back(fWorkers.ItemAt(i)->Thread());
456fa27973SPeter Kosyh fWorkers.ItemAt(i)->Quit();
46186c96d5SAxel Dörfler }
476fa27973SPeter Kosyh locker.Unlock();
48186c96d5SAxel Dörfler
496fa27973SPeter Kosyh for (uint32 i = 0; i < threads.size(); i++)
506fa27973SPeter Kosyh wait_for_thread(threads[i], NULL);
516fa27973SPeter Kosyh
526fa27973SPeter Kosyh FolderMap::iterator iterator = fFolders.begin();
536fa27973SPeter Kosyh for (; iterator != fFolders.end(); iterator++) {
546fa27973SPeter Kosyh IMAPFolder* folder = iterator->second;
556fa27973SPeter Kosyh delete folder; // to stop thread
566fa27973SPeter Kosyh }
576fa27973SPeter Kosyh }
58186c96d5SAxel Dörfler
59186c96d5SAxel Dörfler status_t
CheckSubscribedFolders(IMAP::Protocol & protocol,bool idle)607993ddfaSAxel Dörfler IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
61adbe8fc9SAxel Dörfler {
62adbe8fc9SAxel Dörfler // Get list of subscribed folders
63adbe8fc9SAxel Dörfler
6429871039SAxel Dörfler BStringList newFolders;
65a4bdd26dSAxel Dörfler BString separator;
66a4bdd26dSAxel Dörfler status_t status = protocol.GetSubscribedFolders(newFolders, separator);
67adbe8fc9SAxel Dörfler if (status != B_OK)
68adbe8fc9SAxel Dörfler return status;
69adbe8fc9SAxel Dörfler
70adbe8fc9SAxel Dörfler // Determine how many new mailboxes we have
71adbe8fc9SAxel Dörfler
7229871039SAxel Dörfler for (int32 i = 0; i < newFolders.CountStrings();) {
7329871039SAxel Dörfler if (fFolders.find(newFolders.StringAt(i)) != fFolders.end())
7429871039SAxel Dörfler newFolders.Remove(i);
75a4bdd26dSAxel Dörfler else
7629871039SAxel Dörfler i++;
77adbe8fc9SAxel Dörfler }
78adbe8fc9SAxel Dörfler
7929871039SAxel Dörfler int32 totalMailboxes = fFolders.size() + newFolders.CountStrings();
80a4bdd26dSAxel Dörfler int32 workersWanted = 1;
817993ddfaSAxel Dörfler if (idle)
82a4bdd26dSAxel Dörfler workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
83adbe8fc9SAxel Dörfler
8481805393SAxel Dörfler MutexLocker locker(fWorkerLock);
8581805393SAxel Dörfler
8629871039SAxel Dörfler if (newFolders.IsEmpty() && fWorkers.CountItems() == workersWanted) {
87a4bdd26dSAxel Dörfler // Nothing to do - we've already distributed everything
886fa27973SPeter Kosyh return _EnqueueCheckMailboxes();
89a4bdd26dSAxel Dörfler }
90adbe8fc9SAxel Dörfler
91a4bdd26dSAxel Dörfler // Remove mailboxes from workers
92a4bdd26dSAxel Dörfler for (int32 i = 0; i < fWorkers.CountItems(); i++) {
93a4bdd26dSAxel Dörfler fWorkers.ItemAt(i)->RemoveAllMailboxes();
94a4bdd26dSAxel Dörfler }
9581805393SAxel Dörfler fWorkerMap.clear();
96a4bdd26dSAxel Dörfler
97a4bdd26dSAxel Dörfler // Create/remove connection workers as allowed and needed
98a4bdd26dSAxel Dörfler while (fWorkers.CountItems() < workersWanted) {
99adbe8fc9SAxel Dörfler IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
100adbe8fc9SAxel Dörfler fSettings);
101adbe8fc9SAxel Dörfler if (!fWorkers.AddItem(worker)) {
102adbe8fc9SAxel Dörfler delete worker;
103adbe8fc9SAxel Dörfler break;
104adbe8fc9SAxel Dörfler }
105adbe8fc9SAxel Dörfler
1067993ddfaSAxel Dörfler status = worker->Run();
1077993ddfaSAxel Dörfler if (status != B_OK) {
1087993ddfaSAxel Dörfler fWorkers.RemoveItem(worker);
1097993ddfaSAxel Dörfler delete worker;
110adbe8fc9SAxel Dörfler }
1117993ddfaSAxel Dörfler }
1127993ddfaSAxel Dörfler
113a4bdd26dSAxel Dörfler while (fWorkers.CountItems() > workersWanted) {
114a4bdd26dSAxel Dörfler IMAPConnectionWorker* worker
115a4bdd26dSAxel Dörfler = fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
116a4bdd26dSAxel Dörfler worker->Quit();
117adbe8fc9SAxel Dörfler }
118adbe8fc9SAxel Dörfler
119a4bdd26dSAxel Dörfler // Update known mailboxes
12029871039SAxel Dörfler for (int32 i = 0; i < newFolders.CountStrings(); i++) {
12129871039SAxel Dörfler const BString& mailbox = newFolders.StringAt(i);
1224f1e67e1SZach Dykstra IMAPFolder* folder = _CreateFolder(mailbox, separator);
1234f1e67e1SZach Dykstra if (folder != NULL)
1244f1e67e1SZach Dykstra fFolders.insert(std::make_pair(mailbox, folder));
125a4bdd26dSAxel Dörfler }
126adbe8fc9SAxel Dörfler
127a4bdd26dSAxel Dörfler // Distribute the mailboxes evenly to the workers
128a4bdd26dSAxel Dörfler FolderMap::iterator iterator = fFolders.begin();
129adbe8fc9SAxel Dörfler int32 index = 0;
130a4bdd26dSAxel Dörfler for (; iterator != fFolders.end(); iterator++) {
13181805393SAxel Dörfler IMAPConnectionWorker* worker = fWorkers.ItemAt(index);
13281805393SAxel Dörfler IMAPFolder* folder = iterator->second;
13381805393SAxel Dörfler worker->AddMailbox(folder);
13481805393SAxel Dörfler fWorkerMap.insert(std::make_pair(folder, worker));
13581805393SAxel Dörfler
136adbe8fc9SAxel Dörfler index = (index + 1) % fWorkers.CountItems();
137adbe8fc9SAxel Dörfler }
138adbe8fc9SAxel Dörfler
1397993ddfaSAxel Dörfler // Start waiting workers
1404b2c5571SAxel Dörfler return _EnqueueCheckMailboxes();
141adbe8fc9SAxel Dörfler }
142adbe8fc9SAxel Dörfler
143adbe8fc9SAxel Dörfler
144229c7773SAxel Dörfler void
WorkerQuit(IMAPConnectionWorker * worker)145229c7773SAxel Dörfler IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
146229c7773SAxel Dörfler {
14781805393SAxel Dörfler MutexLocker locker(fWorkerLock);
148229c7773SAxel Dörfler fWorkers.RemoveItem(worker);
14981805393SAxel Dörfler
15081805393SAxel Dörfler WorkerMap::iterator iterator = fWorkerMap.begin();
15181805393SAxel Dörfler while (iterator != fWorkerMap.end()) {
15281805393SAxel Dörfler WorkerMap::iterator removed = iterator++;
15381805393SAxel Dörfler if (removed->second == worker)
15481805393SAxel Dörfler fWorkerMap.erase(removed);
15581805393SAxel Dörfler }
156229c7773SAxel Dörfler }
157229c7773SAxel Dörfler
158229c7773SAxel Dörfler
159eba458b9SAxel Dörfler void
MessageStored(IMAPFolder & folder,entry_ref & ref,BFile & stream,uint32 fetchFlags,BMessage & attributes)16081805393SAxel Dörfler IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref,
16181805393SAxel Dörfler BFile& stream, uint32 fetchFlags, BMessage& attributes)
162eba458b9SAxel Dörfler {
163d33e4744SAxel Dörfler if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
1644fe2002bSAxel Dörfler == (IMAP::kFetchHeader | IMAP::kFetchBody)) {
165d33e4744SAxel Dörfler ProcessMessageFetched(ref, stream, attributes);
166d33e4744SAxel Dörfler } else if ((fetchFlags & IMAP::kFetchHeader) != 0) {
167d33e4744SAxel Dörfler ProcessHeaderFetched(ref, stream, attributes);
168d33e4744SAxel Dörfler } else if ((fetchFlags & IMAP::kFetchBody) != 0) {
169d33e4744SAxel Dörfler NotifyBodyFetched(ref, stream, attributes);
170549949b2SAxel Dörfler }
171eba458b9SAxel Dörfler }
172eba458b9SAxel Dörfler
173eba458b9SAxel Dörfler
174adbe8fc9SAxel Dörfler status_t
UpdateMessageFlags(IMAPFolder & folder,uint32 uid,uint32 flags)17581805393SAxel Dörfler IMAPProtocol::UpdateMessageFlags(IMAPFolder& folder, uint32 uid, uint32 flags)
17681805393SAxel Dörfler {
17781805393SAxel Dörfler MutexLocker locker(fWorkerLock);
17881805393SAxel Dörfler
17981805393SAxel Dörfler WorkerMap::const_iterator found = fWorkerMap.find(&folder);
18081805393SAxel Dörfler if (found == fWorkerMap.end())
18181805393SAxel Dörfler return B_ERROR;
18281805393SAxel Dörfler
18381805393SAxel Dörfler IMAPConnectionWorker* worker = found->second;
18481805393SAxel Dörfler return worker->EnqueueUpdateFlags(folder, uid, flags);
18581805393SAxel Dörfler }
18681805393SAxel Dörfler
18781805393SAxel Dörfler
18881805393SAxel Dörfler status_t
SyncMessages()189186c96d5SAxel Dörfler IMAPProtocol::SyncMessages()
190186c96d5SAxel Dörfler {
191186c96d5SAxel Dörfler puts("IMAP: sync");
192adbe8fc9SAxel Dörfler
19381805393SAxel Dörfler MutexLocker locker(fWorkerLock);
194adbe8fc9SAxel Dörfler if (fWorkers.IsEmpty()) {
195adbe8fc9SAxel Dörfler // Create main (and possibly initial) connection worker
196adbe8fc9SAxel Dörfler IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
197adbe8fc9SAxel Dörfler fSettings, true);
198adbe8fc9SAxel Dörfler if (!fWorkers.AddItem(worker)) {
199adbe8fc9SAxel Dörfler delete worker;
200adbe8fc9SAxel Dörfler return B_NO_MEMORY;
201adbe8fc9SAxel Dörfler }
202adbe8fc9SAxel Dörfler
203229c7773SAxel Dörfler worker->EnqueueCheckSubscribedFolders();
204a4bdd26dSAxel Dörfler return worker->Run();
205adbe8fc9SAxel Dörfler }
2066fa27973SPeter Kosyh fWorkers.ItemAt(0)->EnqueueCheckSubscribedFolders();
2076fa27973SPeter Kosyh return B_OK;
208186c96d5SAxel Dörfler }
209186c96d5SAxel Dörfler
210186c96d5SAxel Dörfler
211186c96d5SAxel Dörfler status_t
MarkMessageAsRead(const entry_ref & ref,read_flags flags)212186c96d5SAxel Dörfler IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
213186c96d5SAxel Dörfler {
214186c96d5SAxel Dörfler printf("IMAP: mark as read %s: %d\n", ref.name, flags);
215186c96d5SAxel Dörfler return B_ERROR;
216186c96d5SAxel Dörfler }
217186c96d5SAxel Dörfler
218186c96d5SAxel Dörfler
219186c96d5SAxel Dörfler void
MessageReceived(BMessage * message)220186c96d5SAxel Dörfler IMAPProtocol::MessageReceived(BMessage* message)
221186c96d5SAxel Dörfler {
222186c96d5SAxel Dörfler switch (message->what) {
223186c96d5SAxel Dörfler case B_READY_TO_RUN:
224186c96d5SAxel Dörfler ReadyToRun();
225186c96d5SAxel Dörfler break;
226adbe8fc9SAxel Dörfler
227adbe8fc9SAxel Dörfler default:
228adbe8fc9SAxel Dörfler BInboundMailProtocol::MessageReceived(message);
229adbe8fc9SAxel Dörfler break;
230186c96d5SAxel Dörfler }
231186c96d5SAxel Dörfler }
232186c96d5SAxel Dörfler
233186c96d5SAxel Dörfler
23481805393SAxel Dörfler status_t
HandleFetchBody(const entry_ref & ref,const BMessenger & replyTo)23581805393SAxel Dörfler IMAPProtocol::HandleFetchBody(const entry_ref& ref, const BMessenger& replyTo)
23681805393SAxel Dörfler {
23781805393SAxel Dörfler printf("IMAP: fetch body %s\n", ref.name);
23881805393SAxel Dörfler MutexLocker locker(fWorkerLock);
23981805393SAxel Dörfler
24081805393SAxel Dörfler IMAPFolder* folder = _FolderFor(ref.directory);
24181805393SAxel Dörfler if (folder == NULL)
24281805393SAxel Dörfler return B_ENTRY_NOT_FOUND;
24381805393SAxel Dörfler
24481805393SAxel Dörfler uint32 uid;
24581805393SAxel Dörfler status_t status = folder->GetMessageUID(ref, uid);
24681805393SAxel Dörfler if (status != B_OK)
24781805393SAxel Dörfler return status;
24881805393SAxel Dörfler
24981805393SAxel Dörfler WorkerMap::const_iterator found = fWorkerMap.find(folder);
25081805393SAxel Dörfler if (found == fWorkerMap.end())
25181805393SAxel Dörfler return B_ERROR;
25281805393SAxel Dörfler
25381805393SAxel Dörfler IMAPConnectionWorker* worker = found->second;
25481805393SAxel Dörfler return worker->EnqueueFetchBody(*folder, uid, replyTo);
25581805393SAxel Dörfler }
25681805393SAxel Dörfler
25781805393SAxel Dörfler
258186c96d5SAxel Dörfler void
ReadyToRun()259186c96d5SAxel Dörfler IMAPProtocol::ReadyToRun()
260186c96d5SAxel Dörfler {
261adbe8fc9SAxel Dörfler puts("IMAP: ready to run!");
262adbe8fc9SAxel Dörfler if (fSettings.IdleMode())
263adbe8fc9SAxel Dörfler SyncMessages();
264186c96d5SAxel Dörfler }
265186c96d5SAxel Dörfler
266186c96d5SAxel Dörfler
267a4bdd26dSAxel Dörfler IMAPFolder*
_CreateFolder(const BString & mailbox,const BString & separator)268a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
269a4bdd26dSAxel Dörfler {
270a4bdd26dSAxel Dörfler BString name = MailboxToFolderName(mailbox, separator);
271a4bdd26dSAxel Dörfler
272a4bdd26dSAxel Dörfler BPath path(fSettings.Destination());
273a4bdd26dSAxel Dörfler if (path.Append(name.String()) != B_OK) {
274a4bdd26dSAxel Dörfler fprintf(stderr, "Could not append path: %s\n", name.String());
275a4bdd26dSAxel Dörfler return NULL;
276a4bdd26dSAxel Dörfler }
277a4bdd26dSAxel Dörfler
278*dd3f595bSZach Dykstra status_t status;
279*dd3f595bSZach Dykstra BNode node(path.Path());
280*dd3f595bSZach Dykstra
281*dd3f595bSZach Dykstra if (node.InitCheck() == B_OK) {
282*dd3f595bSZach Dykstra if (!node.IsDirectory()) {
283*dd3f595bSZach Dykstra fprintf(stderr, "%s already exists and is not a directory\n",
284*dd3f595bSZach Dykstra path.Path());
285*dd3f595bSZach Dykstra return NULL;
286*dd3f595bSZach Dykstra }
287*dd3f595bSZach Dykstra } else {
288*dd3f595bSZach Dykstra status = create_directory(path.Path(), 0755);
289a4bdd26dSAxel Dörfler if (status != B_OK) {
290a4bdd26dSAxel Dörfler fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
291a4bdd26dSAxel Dörfler strerror(status));
292a4bdd26dSAxel Dörfler return NULL;
293a4bdd26dSAxel Dörfler }
294618cc43bSAxel Dörfler CopyMailFolderAttributes(path.Path());
295*dd3f595bSZach Dykstra }
296618cc43bSAxel Dörfler
297a4bdd26dSAxel Dörfler entry_ref ref;
298a4bdd26dSAxel Dörfler status = get_ref_for_path(path.Path(), &ref);
299a4bdd26dSAxel Dörfler if (status != B_OK) {
300a4bdd26dSAxel Dörfler fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
301a4bdd26dSAxel Dörfler strerror(status));
302a4bdd26dSAxel Dörfler return NULL;
303a4bdd26dSAxel Dörfler }
304a4bdd26dSAxel Dörfler
30581805393SAxel Dörfler IMAPFolder* folder = new IMAPFolder(*this, mailbox, ref);
30681805393SAxel Dörfler status = folder->Init();
30781805393SAxel Dörfler if (status != B_OK) {
30881805393SAxel Dörfler fprintf(stderr, "Initializing folder %s failed: %s\n", path.Path(),
30981805393SAxel Dörfler strerror(status));
310ed78fbfcSMurai Takashi delete folder;
31181805393SAxel Dörfler return NULL;
31281805393SAxel Dörfler }
31381805393SAxel Dörfler
31481805393SAxel Dörfler fFolderNodeMap.insert(std::make_pair(folder->NodeID(), folder));
31581805393SAxel Dörfler return folder;
31681805393SAxel Dörfler }
31781805393SAxel Dörfler
31881805393SAxel Dörfler
31981805393SAxel Dörfler IMAPFolder*
_FolderFor(ino_t directory)32081805393SAxel Dörfler IMAPProtocol::_FolderFor(ino_t directory)
32181805393SAxel Dörfler {
32281805393SAxel Dörfler FolderNodeMap::const_iterator found = fFolderNodeMap.find(directory);
32381805393SAxel Dörfler if (found != fFolderNodeMap.end())
32481805393SAxel Dörfler return found->second;
32581805393SAxel Dörfler
32681805393SAxel Dörfler return NULL;
327a4bdd26dSAxel Dörfler }
328a4bdd26dSAxel Dörfler
329a4bdd26dSAxel Dörfler
3304b2c5571SAxel Dörfler status_t
_EnqueueCheckMailboxes()3314b2c5571SAxel Dörfler IMAPProtocol::_EnqueueCheckMailboxes()
3324b2c5571SAxel Dörfler {
3334b2c5571SAxel Dörfler for (int32 i = 0; i < fWorkers.CountItems(); i++) {
3344b2c5571SAxel Dörfler fWorkers.ItemAt(i)->EnqueueCheckMailboxes();
3354b2c5571SAxel Dörfler }
3364b2c5571SAxel Dörfler
3374b2c5571SAxel Dörfler return B_OK;
3384b2c5571SAxel Dörfler }
3394b2c5571SAxel Dörfler
3404b2c5571SAxel Dörfler
341186c96d5SAxel Dörfler // #pragma mark -
342186c96d5SAxel Dörfler
343186c96d5SAxel Dörfler
344186c96d5SAxel Dörfler extern "C" BInboundMailProtocol*
instantiate_inbound_protocol(const BMailAccountSettings & settings)345186c96d5SAxel Dörfler instantiate_inbound_protocol(const BMailAccountSettings& settings)
346186c96d5SAxel Dörfler {
347186c96d5SAxel Dörfler return new IMAPProtocol(settings);
348186c96d5SAxel Dörfler }
349