xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp (revision 1052525dc5cfda3563a36ad82b4a900d7beb5221)
1186c96d5SAxel Dörfler /*
2186c96d5SAxel Dörfler  * Copyright 2013, 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
126*1052525dSAxel Dörfler IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
127*1052525dSAxel Dörfler 	uint32 fetchFlags)
128eba458b9SAxel Dörfler {
129eba458b9SAxel Dörfler 	if ((fetchFlags & IMAP::kFetchHeader) != 0)
130eba458b9SAxel Dörfler 		NotifyHeaderFetched(ref, &stream);
131eba458b9SAxel Dörfler 	if ((fetchFlags & IMAP::kFetchBody) != 0)
132eba458b9SAxel Dörfler 		NotifyBodyFetched(ref, &stream);
133eba458b9SAxel Dörfler }
134eba458b9SAxel Dörfler 
135eba458b9SAxel Dörfler 
136adbe8fc9SAxel Dörfler status_t
137186c96d5SAxel Dörfler IMAPProtocol::SyncMessages()
138186c96d5SAxel Dörfler {
139186c96d5SAxel Dörfler 	puts("IMAP: sync");
140adbe8fc9SAxel Dörfler 
141adbe8fc9SAxel Dörfler 	if (fWorkers.IsEmpty()) {
142adbe8fc9SAxel Dörfler 		// Create main (and possibly initial) connection worker
143adbe8fc9SAxel Dörfler 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
144adbe8fc9SAxel Dörfler 			fSettings, true);
145adbe8fc9SAxel Dörfler 		if (!fWorkers.AddItem(worker)) {
146adbe8fc9SAxel Dörfler 			delete worker;
147adbe8fc9SAxel Dörfler 			return B_NO_MEMORY;
148adbe8fc9SAxel Dörfler 		}
149adbe8fc9SAxel Dörfler 
150229c7773SAxel Dörfler 		worker->EnqueueCheckSubscribedFolders();
151a4bdd26dSAxel Dörfler 		return worker->Run();
152adbe8fc9SAxel Dörfler 	}
153adbe8fc9SAxel Dörfler 
1544b2c5571SAxel Dörfler 	return _EnqueueCheckMailboxes();
155186c96d5SAxel Dörfler }
156186c96d5SAxel Dörfler 
157186c96d5SAxel Dörfler 
158186c96d5SAxel Dörfler status_t
159186c96d5SAxel Dörfler IMAPProtocol::FetchBody(const entry_ref& ref)
160186c96d5SAxel Dörfler {
161186c96d5SAxel Dörfler 	printf("IMAP: fetch body %s\n", ref.name);
162186c96d5SAxel Dörfler 	return B_ERROR;
163186c96d5SAxel Dörfler }
164186c96d5SAxel Dörfler 
165186c96d5SAxel Dörfler 
166186c96d5SAxel Dörfler status_t
167186c96d5SAxel Dörfler IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
168186c96d5SAxel Dörfler {
169186c96d5SAxel Dörfler 	printf("IMAP: mark as read %s: %d\n", ref.name, flags);
170186c96d5SAxel Dörfler 	return B_ERROR;
171186c96d5SAxel Dörfler }
172186c96d5SAxel Dörfler 
173186c96d5SAxel Dörfler 
174186c96d5SAxel Dörfler status_t
175186c96d5SAxel Dörfler IMAPProtocol::DeleteMessage(const entry_ref& ref)
176186c96d5SAxel Dörfler {
177186c96d5SAxel Dörfler 	printf("IMAP: delete message %s\n", ref.name);
178186c96d5SAxel Dörfler 	return B_ERROR;
179186c96d5SAxel Dörfler }
180186c96d5SAxel Dörfler 
181186c96d5SAxel Dörfler 
182186c96d5SAxel Dörfler status_t
183186c96d5SAxel Dörfler IMAPProtocol::AppendMessage(const entry_ref& ref)
184186c96d5SAxel Dörfler {
185186c96d5SAxel Dörfler 	printf("IMAP: append message %s\n", ref.name);
186186c96d5SAxel Dörfler 	return B_ERROR;
187186c96d5SAxel Dörfler }
188186c96d5SAxel Dörfler 
189186c96d5SAxel Dörfler 
190186c96d5SAxel Dörfler void
191186c96d5SAxel Dörfler IMAPProtocol::MessageReceived(BMessage* message)
192186c96d5SAxel Dörfler {
193186c96d5SAxel Dörfler 	switch (message->what) {
194186c96d5SAxel Dörfler 		case B_READY_TO_RUN:
195186c96d5SAxel Dörfler 			ReadyToRun();
196186c96d5SAxel Dörfler 			break;
197adbe8fc9SAxel Dörfler 
198adbe8fc9SAxel Dörfler 		default:
199adbe8fc9SAxel Dörfler 			BInboundMailProtocol::MessageReceived(message);
200adbe8fc9SAxel Dörfler 			break;
201186c96d5SAxel Dörfler 	}
202186c96d5SAxel Dörfler }
203186c96d5SAxel Dörfler 
204186c96d5SAxel Dörfler 
205186c96d5SAxel Dörfler void
206186c96d5SAxel Dörfler IMAPProtocol::ReadyToRun()
207186c96d5SAxel Dörfler {
208adbe8fc9SAxel Dörfler 	puts("IMAP: ready to run!");
209adbe8fc9SAxel Dörfler 	if (fSettings.IdleMode())
210adbe8fc9SAxel Dörfler 		SyncMessages();
211186c96d5SAxel Dörfler }
212186c96d5SAxel Dörfler 
213186c96d5SAxel Dörfler 
214a4bdd26dSAxel Dörfler IMAPFolder*
215a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
216a4bdd26dSAxel Dörfler {
217a4bdd26dSAxel Dörfler 	BString name = MailboxToFolderName(mailbox, separator);
218a4bdd26dSAxel Dörfler 
219a4bdd26dSAxel Dörfler 	BPath path(fSettings.Destination());
220a4bdd26dSAxel Dörfler 	if (path.Append(name.String()) != B_OK) {
221a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not append path: %s\n", name.String());
222a4bdd26dSAxel Dörfler 		return NULL;
223a4bdd26dSAxel Dörfler 	}
224a4bdd26dSAxel Dörfler 
225a4bdd26dSAxel Dörfler 	status_t status = create_directory(path.Path(), 0755);
226a4bdd26dSAxel Dörfler 	if (status != B_OK) {
227a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
228a4bdd26dSAxel Dörfler 			strerror(status));
229a4bdd26dSAxel Dörfler 		return NULL;
230a4bdd26dSAxel Dörfler 	}
231a4bdd26dSAxel Dörfler 
232a4bdd26dSAxel Dörfler 	entry_ref ref;
233a4bdd26dSAxel Dörfler 	status = get_ref_for_path(path.Path(), &ref);
234a4bdd26dSAxel Dörfler 	if (status != B_OK) {
235a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
236a4bdd26dSAxel Dörfler 			strerror(status));
237a4bdd26dSAxel Dörfler 		return NULL;
238a4bdd26dSAxel Dörfler 	}
239a4bdd26dSAxel Dörfler 
240eba458b9SAxel Dörfler 	return new IMAPFolder(*this, mailbox, ref);
241a4bdd26dSAxel Dörfler }
242a4bdd26dSAxel Dörfler 
243a4bdd26dSAxel Dörfler 
2444b2c5571SAxel Dörfler status_t
2454b2c5571SAxel Dörfler IMAPProtocol::_EnqueueCheckMailboxes()
2464b2c5571SAxel Dörfler {
2474b2c5571SAxel Dörfler 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
2484b2c5571SAxel Dörfler 		fWorkers.ItemAt(i)->EnqueueCheckMailboxes();
2494b2c5571SAxel Dörfler 	}
2504b2c5571SAxel Dörfler 
2514b2c5571SAxel Dörfler 	return B_OK;
2524b2c5571SAxel Dörfler }
2534b2c5571SAxel Dörfler 
2544b2c5571SAxel Dörfler 
255186c96d5SAxel Dörfler // #pragma mark -
256186c96d5SAxel Dörfler 
257186c96d5SAxel Dörfler 
258186c96d5SAxel Dörfler extern "C" BInboundMailProtocol*
259186c96d5SAxel Dörfler instantiate_inbound_protocol(const BMailAccountSettings& settings)
260186c96d5SAxel Dörfler {
261186c96d5SAxel Dörfler 	return new IMAPProtocol(settings);
262186c96d5SAxel Dörfler }
263