xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp (revision 4fe2002b472457c74c6ba59f4ceec5606df6a690)
1186c96d5SAxel Dörfler /*
2*4fe2002bSAxel Dörfler  * Copyright 2013-2015, 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>
10186c96d5SAxel Dörfler 
11186c96d5SAxel Dörfler #include "IMAPConnectionWorker.h"
12a4bdd26dSAxel Dörfler #include "IMAPFolder.h"
13a4bdd26dSAxel Dörfler #include "Utilities.h"
14186c96d5SAxel Dörfler 
15186c96d5SAxel Dörfler 
16186c96d5SAxel Dörfler IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
17186c96d5SAxel Dörfler 	:
18186c96d5SAxel Dörfler 	BInboundMailProtocol(settings),
1928ee6c28SAxel Dörfler 	fSettings(settings.Name(), settings.InboundSettings()),
207993ddfaSAxel Dörfler 	fWorkers(5, false)
21186c96d5SAxel Dörfler {
22186c96d5SAxel Dörfler 	BPath destination = fSettings.Destination();
23186c96d5SAxel Dörfler 
24186c96d5SAxel Dörfler 	status_t status = create_directory(destination.Path(), 0755);
25186c96d5SAxel Dörfler 	if (status != B_OK) {
2628ee6c28SAxel Dörfler 		fprintf(stderr, "IMAP: Could not create destination directory %s: %s\n",
27186c96d5SAxel Dörfler 			destination.Path(), strerror(status));
28186c96d5SAxel Dörfler 	}
29186c96d5SAxel Dörfler 
30186c96d5SAxel Dörfler 	PostMessage(B_READY_TO_RUN);
31186c96d5SAxel Dörfler }
32186c96d5SAxel Dörfler 
33186c96d5SAxel Dörfler 
34186c96d5SAxel Dörfler IMAPProtocol::~IMAPProtocol()
35186c96d5SAxel Dörfler {
36186c96d5SAxel Dörfler }
37186c96d5SAxel Dörfler 
38186c96d5SAxel Dörfler 
39186c96d5SAxel Dörfler status_t
407993ddfaSAxel Dörfler IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
41adbe8fc9SAxel Dörfler {
42adbe8fc9SAxel Dörfler 	// Get list of subscribed folders
43adbe8fc9SAxel Dörfler 
44a4bdd26dSAxel Dörfler 	StringList newFolders;
45a4bdd26dSAxel Dörfler 	BString separator;
46a4bdd26dSAxel Dörfler 	status_t status = protocol.GetSubscribedFolders(newFolders, separator);
47adbe8fc9SAxel Dörfler 	if (status != B_OK)
48adbe8fc9SAxel Dörfler 		return status;
49adbe8fc9SAxel Dörfler 
50adbe8fc9SAxel Dörfler 	// Determine how many new mailboxes we have
51adbe8fc9SAxel Dörfler 
52a4bdd26dSAxel Dörfler 	StringList::iterator folderIterator = newFolders.begin();
53a4bdd26dSAxel Dörfler 	while (folderIterator != newFolders.end()) {
54a4bdd26dSAxel Dörfler 		if (fFolders.find(*folderIterator) != fFolders.end())
55a4bdd26dSAxel Dörfler 			folderIterator = newFolders.erase(folderIterator);
56a4bdd26dSAxel Dörfler 		else
57a4bdd26dSAxel Dörfler 			folderIterator++;
58adbe8fc9SAxel Dörfler 	}
59adbe8fc9SAxel Dörfler 
60a4bdd26dSAxel Dörfler 	int32 totalMailboxes = fFolders.size() + newFolders.size();
61a4bdd26dSAxel Dörfler 	int32 workersWanted = 1;
627993ddfaSAxel Dörfler 	if (idle)
63a4bdd26dSAxel Dörfler 		workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
64adbe8fc9SAxel Dörfler 
65a4bdd26dSAxel Dörfler 	if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
66a4bdd26dSAxel Dörfler 		// Nothing to do - we've already distributed everything
67a4bdd26dSAxel Dörfler 		return B_OK;
68a4bdd26dSAxel Dörfler 	}
69adbe8fc9SAxel Dörfler 
70a4bdd26dSAxel Dörfler 	// Remove mailboxes from workers
71a4bdd26dSAxel Dörfler 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
72a4bdd26dSAxel Dörfler 		fWorkers.ItemAt(i)->RemoveAllMailboxes();
73a4bdd26dSAxel Dörfler 	}
74a4bdd26dSAxel Dörfler 
75a4bdd26dSAxel Dörfler 	// Create/remove connection workers as allowed and needed
76a4bdd26dSAxel Dörfler 	while (fWorkers.CountItems() < workersWanted) {
77adbe8fc9SAxel Dörfler 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
78adbe8fc9SAxel Dörfler 			fSettings);
79adbe8fc9SAxel Dörfler 		if (!fWorkers.AddItem(worker)) {
80adbe8fc9SAxel Dörfler 			delete worker;
81adbe8fc9SAxel Dörfler 			break;
82adbe8fc9SAxel Dörfler 		}
83adbe8fc9SAxel Dörfler 
847993ddfaSAxel Dörfler 		status = worker->Run();
857993ddfaSAxel Dörfler 		if (status != B_OK) {
867993ddfaSAxel Dörfler 			fWorkers.RemoveItem(worker);
877993ddfaSAxel Dörfler 			delete worker;
88adbe8fc9SAxel Dörfler 		}
897993ddfaSAxel Dörfler 	}
907993ddfaSAxel Dörfler 
91a4bdd26dSAxel Dörfler 	while (fWorkers.CountItems() > workersWanted) {
92a4bdd26dSAxel Dörfler 		IMAPConnectionWorker* worker
93a4bdd26dSAxel Dörfler 			= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
94a4bdd26dSAxel Dörfler 		worker->Quit();
95adbe8fc9SAxel Dörfler 	}
96adbe8fc9SAxel Dörfler 
97a4bdd26dSAxel Dörfler 	// Update known mailboxes
98a4bdd26dSAxel Dörfler 	folderIterator = newFolders.begin();
99a4bdd26dSAxel Dörfler 	for (; folderIterator != newFolders.end(); folderIterator++) {
100a4bdd26dSAxel Dörfler 		const BString& mailbox = *folderIterator;
101a4bdd26dSAxel Dörfler 		fFolders.insert(std::make_pair(mailbox,
102a4bdd26dSAxel Dörfler 			_CreateFolder(mailbox, separator)));
103a4bdd26dSAxel Dörfler 	}
104adbe8fc9SAxel Dörfler 
105a4bdd26dSAxel Dörfler 	// Distribute the mailboxes evenly to the workers
106a4bdd26dSAxel Dörfler 	FolderMap::iterator iterator = fFolders.begin();
107adbe8fc9SAxel Dörfler 	int32 index = 0;
108a4bdd26dSAxel Dörfler 	for (; iterator != fFolders.end(); iterator++) {
109a4bdd26dSAxel Dörfler 		fWorkers.ItemAt(index)->AddMailbox(iterator->second);
110adbe8fc9SAxel Dörfler 		index = (index + 1) % fWorkers.CountItems();
111adbe8fc9SAxel Dörfler 	}
112adbe8fc9SAxel Dörfler 
1137993ddfaSAxel Dörfler 	// Start waiting workers
1144b2c5571SAxel Dörfler 	return _EnqueueCheckMailboxes();
115adbe8fc9SAxel Dörfler }
116adbe8fc9SAxel Dörfler 
117adbe8fc9SAxel Dörfler 
118229c7773SAxel Dörfler void
119229c7773SAxel Dörfler IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
120229c7773SAxel Dörfler {
121229c7773SAxel Dörfler 	fWorkers.RemoveItem(worker);
122229c7773SAxel Dörfler }
123229c7773SAxel Dörfler 
124229c7773SAxel Dörfler 
125eba458b9SAxel Dörfler void
1261052525dSAxel Dörfler IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
127d33e4744SAxel Dörfler 	uint32 fetchFlags, BMessage& attributes)
128eba458b9SAxel Dörfler {
129d33e4744SAxel Dörfler 	if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
130*4fe2002bSAxel Dörfler 			== (IMAP::kFetchHeader | IMAP::kFetchBody)) {
131d33e4744SAxel Dörfler 		ProcessMessageFetched(ref, stream, attributes);
132d33e4744SAxel Dörfler 	} else if ((fetchFlags & IMAP::kFetchHeader) != 0) {
133d33e4744SAxel Dörfler 		ProcessHeaderFetched(ref, stream, attributes);
134d33e4744SAxel Dörfler 	} else if ((fetchFlags & IMAP::kFetchBody) != 0) {
135d33e4744SAxel Dörfler 		NotifyBodyFetched(ref, stream, attributes);
136549949b2SAxel Dörfler 	}
137eba458b9SAxel Dörfler }
138eba458b9SAxel Dörfler 
139eba458b9SAxel Dörfler 
140adbe8fc9SAxel Dörfler status_t
141186c96d5SAxel Dörfler IMAPProtocol::SyncMessages()
142186c96d5SAxel Dörfler {
143186c96d5SAxel Dörfler 	puts("IMAP: sync");
144adbe8fc9SAxel Dörfler 
145adbe8fc9SAxel Dörfler 	if (fWorkers.IsEmpty()) {
146adbe8fc9SAxel Dörfler 		// Create main (and possibly initial) connection worker
147adbe8fc9SAxel Dörfler 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
148adbe8fc9SAxel Dörfler 			fSettings, true);
149adbe8fc9SAxel Dörfler 		if (!fWorkers.AddItem(worker)) {
150adbe8fc9SAxel Dörfler 			delete worker;
151adbe8fc9SAxel Dörfler 			return B_NO_MEMORY;
152adbe8fc9SAxel Dörfler 		}
153adbe8fc9SAxel Dörfler 
154229c7773SAxel Dörfler 		worker->EnqueueCheckSubscribedFolders();
155a4bdd26dSAxel Dörfler 		return worker->Run();
156adbe8fc9SAxel Dörfler 	}
157adbe8fc9SAxel Dörfler 
1584b2c5571SAxel Dörfler 	return _EnqueueCheckMailboxes();
159186c96d5SAxel Dörfler }
160186c96d5SAxel Dörfler 
161186c96d5SAxel Dörfler 
162186c96d5SAxel Dörfler status_t
163186c96d5SAxel Dörfler IMAPProtocol::FetchBody(const entry_ref& ref)
164186c96d5SAxel Dörfler {
165186c96d5SAxel Dörfler 	printf("IMAP: fetch body %s\n", ref.name);
166186c96d5SAxel Dörfler 	return B_ERROR;
167186c96d5SAxel Dörfler }
168186c96d5SAxel Dörfler 
169186c96d5SAxel Dörfler 
170186c96d5SAxel Dörfler status_t
171186c96d5SAxel Dörfler IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
172186c96d5SAxel Dörfler {
173186c96d5SAxel Dörfler 	printf("IMAP: mark as read %s: %d\n", ref.name, flags);
174186c96d5SAxel Dörfler 	return B_ERROR;
175186c96d5SAxel Dörfler }
176186c96d5SAxel Dörfler 
177186c96d5SAxel Dörfler 
178186c96d5SAxel Dörfler status_t
179186c96d5SAxel Dörfler IMAPProtocol::DeleteMessage(const entry_ref& ref)
180186c96d5SAxel Dörfler {
181186c96d5SAxel Dörfler 	printf("IMAP: delete message %s\n", ref.name);
182186c96d5SAxel Dörfler 	return B_ERROR;
183186c96d5SAxel Dörfler }
184186c96d5SAxel Dörfler 
185186c96d5SAxel Dörfler 
186186c96d5SAxel Dörfler status_t
187186c96d5SAxel Dörfler IMAPProtocol::AppendMessage(const entry_ref& ref)
188186c96d5SAxel Dörfler {
189186c96d5SAxel Dörfler 	printf("IMAP: append message %s\n", ref.name);
190186c96d5SAxel Dörfler 	return B_ERROR;
191186c96d5SAxel Dörfler }
192186c96d5SAxel Dörfler 
193186c96d5SAxel Dörfler 
194186c96d5SAxel Dörfler void
195186c96d5SAxel Dörfler IMAPProtocol::MessageReceived(BMessage* message)
196186c96d5SAxel Dörfler {
197186c96d5SAxel Dörfler 	switch (message->what) {
198186c96d5SAxel Dörfler 		case B_READY_TO_RUN:
199186c96d5SAxel Dörfler 			ReadyToRun();
200186c96d5SAxel Dörfler 			break;
201adbe8fc9SAxel Dörfler 
202adbe8fc9SAxel Dörfler 		default:
203adbe8fc9SAxel Dörfler 			BInboundMailProtocol::MessageReceived(message);
204adbe8fc9SAxel Dörfler 			break;
205186c96d5SAxel Dörfler 	}
206186c96d5SAxel Dörfler }
207186c96d5SAxel Dörfler 
208186c96d5SAxel Dörfler 
209186c96d5SAxel Dörfler void
210186c96d5SAxel Dörfler IMAPProtocol::ReadyToRun()
211186c96d5SAxel Dörfler {
212adbe8fc9SAxel Dörfler 	puts("IMAP: ready to run!");
213adbe8fc9SAxel Dörfler 	if (fSettings.IdleMode())
214adbe8fc9SAxel Dörfler 		SyncMessages();
215186c96d5SAxel Dörfler }
216186c96d5SAxel Dörfler 
217186c96d5SAxel Dörfler 
218a4bdd26dSAxel Dörfler IMAPFolder*
219a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
220a4bdd26dSAxel Dörfler {
221a4bdd26dSAxel Dörfler 	BString name = MailboxToFolderName(mailbox, separator);
222a4bdd26dSAxel Dörfler 
223a4bdd26dSAxel Dörfler 	BPath path(fSettings.Destination());
224a4bdd26dSAxel Dörfler 	if (path.Append(name.String()) != B_OK) {
225a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not append path: %s\n", name.String());
226a4bdd26dSAxel Dörfler 		return NULL;
227a4bdd26dSAxel Dörfler 	}
228a4bdd26dSAxel Dörfler 
229a4bdd26dSAxel Dörfler 	status_t status = create_directory(path.Path(), 0755);
230a4bdd26dSAxel Dörfler 	if (status != B_OK) {
231a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
232a4bdd26dSAxel Dörfler 			strerror(status));
233a4bdd26dSAxel Dörfler 		return NULL;
234a4bdd26dSAxel Dörfler 	}
235a4bdd26dSAxel Dörfler 
236a4bdd26dSAxel Dörfler 	entry_ref ref;
237a4bdd26dSAxel Dörfler 	status = get_ref_for_path(path.Path(), &ref);
238a4bdd26dSAxel Dörfler 	if (status != B_OK) {
239a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
240a4bdd26dSAxel Dörfler 			strerror(status));
241a4bdd26dSAxel Dörfler 		return NULL;
242a4bdd26dSAxel Dörfler 	}
243a4bdd26dSAxel Dörfler 
244eba458b9SAxel Dörfler 	return new IMAPFolder(*this, mailbox, ref);
245a4bdd26dSAxel Dörfler }
246a4bdd26dSAxel Dörfler 
247a4bdd26dSAxel Dörfler 
2484b2c5571SAxel Dörfler status_t
2494b2c5571SAxel Dörfler IMAPProtocol::_EnqueueCheckMailboxes()
2504b2c5571SAxel Dörfler {
2514b2c5571SAxel Dörfler 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
2524b2c5571SAxel Dörfler 		fWorkers.ItemAt(i)->EnqueueCheckMailboxes();
2534b2c5571SAxel Dörfler 	}
2544b2c5571SAxel Dörfler 
2554b2c5571SAxel Dörfler 	return B_OK;
2564b2c5571SAxel Dörfler }
2574b2c5571SAxel Dörfler 
2584b2c5571SAxel Dörfler 
259186c96d5SAxel Dörfler // #pragma mark -
260186c96d5SAxel Dörfler 
261186c96d5SAxel Dörfler 
262186c96d5SAxel Dörfler extern "C" BInboundMailProtocol*
263186c96d5SAxel Dörfler instantiate_inbound_protocol(const BMailAccountSettings& settings)
264186c96d5SAxel Dörfler {
265186c96d5SAxel Dörfler 	return new IMAPProtocol(settings);
266186c96d5SAxel Dörfler }
267