xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp (revision a4bdd26d61967231469220573c4b5ee09e29dd49)
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"
12*a4bdd26dSAxel Dörfler #include "IMAPFolder.h"
13*a4bdd26dSAxel 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),
19186c96d5SAxel Dörfler 	fSettings(settings.InboundSettings())
20186c96d5SAxel Dörfler {
21186c96d5SAxel Dörfler 	BPath destination = fSettings.Destination();
22186c96d5SAxel Dörfler 
23186c96d5SAxel Dörfler 	status_t status = create_directory(destination.Path(), 0755);
24186c96d5SAxel Dörfler 	if (status != B_OK) {
25186c96d5SAxel Dörfler 		fprintf(stderr, "imap: Could not create destination directory %s: %s\n",
26186c96d5SAxel Dörfler 			destination.Path(), strerror(status));
27186c96d5SAxel Dörfler 	}
28186c96d5SAxel Dörfler 
29*a4bdd26dSAxel Dörfler 	status = _CreateFolderChangeSemaphore();
30*a4bdd26dSAxel Dörfler 	if (status != B_OK)
31*a4bdd26dSAxel Dörfler 		fprintf(stderr, "imap: Failed to create sem: %s\n", strerror(status));
32*a4bdd26dSAxel Dörfler 
33186c96d5SAxel Dörfler 	PostMessage(B_READY_TO_RUN);
34186c96d5SAxel Dörfler }
35186c96d5SAxel Dörfler 
36186c96d5SAxel Dörfler 
37186c96d5SAxel Dörfler IMAPProtocol::~IMAPProtocol()
38186c96d5SAxel Dörfler {
39186c96d5SAxel Dörfler }
40186c96d5SAxel Dörfler 
41186c96d5SAxel Dörfler 
42186c96d5SAxel Dörfler status_t
43adbe8fc9SAxel Dörfler IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol)
44adbe8fc9SAxel Dörfler {
45adbe8fc9SAxel Dörfler 	// Get list of subscribed folders
46adbe8fc9SAxel Dörfler 
47*a4bdd26dSAxel Dörfler 	StringList newFolders;
48*a4bdd26dSAxel Dörfler 	BString separator;
49*a4bdd26dSAxel Dörfler 	status_t status = protocol.GetSubscribedFolders(newFolders, separator);
50adbe8fc9SAxel Dörfler 	if (status != B_OK)
51adbe8fc9SAxel Dörfler 		return status;
52adbe8fc9SAxel Dörfler 
53adbe8fc9SAxel Dörfler 	// Determine how many new mailboxes we have
54adbe8fc9SAxel Dörfler 
55*a4bdd26dSAxel Dörfler 	StringList::iterator folderIterator = newFolders.begin();
56*a4bdd26dSAxel Dörfler 	while (folderIterator != newFolders.end()) {
57*a4bdd26dSAxel Dörfler 		if (fFolders.find(*folderIterator) != fFolders.end())
58*a4bdd26dSAxel Dörfler 			folderIterator = newFolders.erase(folderIterator);
59*a4bdd26dSAxel Dörfler 		else
60*a4bdd26dSAxel Dörfler 			folderIterator++;
61adbe8fc9SAxel Dörfler 	}
62adbe8fc9SAxel Dörfler 
63*a4bdd26dSAxel Dörfler 	int32 totalMailboxes = fFolders.size() + newFolders.size();
64*a4bdd26dSAxel Dörfler 	int32 workersWanted = 1;
65*a4bdd26dSAxel Dörfler 	if (fSettings.IdleMode())
66*a4bdd26dSAxel Dörfler 		workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
67adbe8fc9SAxel Dörfler 
68*a4bdd26dSAxel Dörfler 	if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
69*a4bdd26dSAxel Dörfler 		// Nothing to do - we've already distributed everything
70*a4bdd26dSAxel Dörfler 		return B_OK;
71*a4bdd26dSAxel Dörfler 	}
72adbe8fc9SAxel Dörfler 
73*a4bdd26dSAxel Dörfler 	// Remove mailboxes from workers
74*a4bdd26dSAxel Dörfler 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
75*a4bdd26dSAxel Dörfler 		fWorkers.ItemAt(i)->RemoveAllMailboxes();
76*a4bdd26dSAxel Dörfler 	}
77*a4bdd26dSAxel Dörfler 
78*a4bdd26dSAxel Dörfler 	// Create/remove connection workers as allowed and needed
79*a4bdd26dSAxel Dörfler 	while (fWorkers.CountItems() < workersWanted) {
80adbe8fc9SAxel Dörfler 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
81adbe8fc9SAxel Dörfler 			fSettings);
82adbe8fc9SAxel Dörfler 		if (!fWorkers.AddItem(worker)) {
83adbe8fc9SAxel Dörfler 			delete worker;
84adbe8fc9SAxel Dörfler 			break;
85adbe8fc9SAxel Dörfler 		}
86adbe8fc9SAxel Dörfler 
87*a4bdd26dSAxel Dörfler 		worker->Run();
88adbe8fc9SAxel Dörfler 	}
89*a4bdd26dSAxel Dörfler 	while (fWorkers.CountItems() > workersWanted) {
90*a4bdd26dSAxel Dörfler 		IMAPConnectionWorker* worker
91*a4bdd26dSAxel Dörfler 			= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
92*a4bdd26dSAxel Dörfler 		worker->Quit();
93adbe8fc9SAxel Dörfler 	}
94adbe8fc9SAxel Dörfler 
95*a4bdd26dSAxel Dörfler 	// Update known mailboxes
96*a4bdd26dSAxel Dörfler 	folderIterator = newFolders.begin();
97*a4bdd26dSAxel Dörfler 	for (; folderIterator != newFolders.end(); folderIterator++) {
98*a4bdd26dSAxel Dörfler 		const BString& mailbox = *folderIterator;
99*a4bdd26dSAxel Dörfler 		fFolders.insert(std::make_pair(mailbox,
100*a4bdd26dSAxel Dörfler 			_CreateFolder(mailbox, separator)));
101*a4bdd26dSAxel Dörfler 	}
102adbe8fc9SAxel Dörfler 
103*a4bdd26dSAxel Dörfler 	// Distribute the mailboxes evenly to the workers
104*a4bdd26dSAxel Dörfler 	FolderMap::iterator iterator = fFolders.begin();
105adbe8fc9SAxel Dörfler 	int32 index = 0;
106*a4bdd26dSAxel Dörfler 	for (; iterator != fFolders.end(); iterator++) {
107*a4bdd26dSAxel Dörfler 		fWorkers.ItemAt(index)->AddMailbox(iterator->second);
108adbe8fc9SAxel Dörfler 		index = (index + 1) % fWorkers.CountItems();
109adbe8fc9SAxel Dörfler 	}
110adbe8fc9SAxel Dörfler 
111*a4bdd26dSAxel Dörfler 	// Restart waiting workers
112*a4bdd26dSAxel Dörfler 	delete_sem(fFolderChangeSemaphore);
113*a4bdd26dSAxel Dörfler 	return _CreateFolderChangeSemaphore();
114adbe8fc9SAxel Dörfler }
115adbe8fc9SAxel Dörfler 
116adbe8fc9SAxel Dörfler 
117adbe8fc9SAxel Dörfler status_t
118186c96d5SAxel Dörfler IMAPProtocol::SyncMessages()
119186c96d5SAxel Dörfler {
120186c96d5SAxel Dörfler 	puts("IMAP: sync");
121adbe8fc9SAxel Dörfler 
122adbe8fc9SAxel Dörfler 	if (fWorkers.IsEmpty()) {
123adbe8fc9SAxel Dörfler 		// Create main (and possibly initial) connection worker
124adbe8fc9SAxel Dörfler 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
125adbe8fc9SAxel Dörfler 			fSettings, true);
126adbe8fc9SAxel Dörfler 		if (!fWorkers.AddItem(worker)) {
127adbe8fc9SAxel Dörfler 			delete worker;
128adbe8fc9SAxel Dörfler 			return B_NO_MEMORY;
129adbe8fc9SAxel Dörfler 		}
130adbe8fc9SAxel Dörfler 
131*a4bdd26dSAxel Dörfler 		return worker->Run();
132adbe8fc9SAxel Dörfler 	}
133adbe8fc9SAxel Dörfler 
134adbe8fc9SAxel Dörfler 	return B_OK;
135186c96d5SAxel Dörfler }
136186c96d5SAxel Dörfler 
137186c96d5SAxel Dörfler 
138186c96d5SAxel Dörfler status_t
139186c96d5SAxel Dörfler IMAPProtocol::FetchBody(const entry_ref& ref)
140186c96d5SAxel Dörfler {
141186c96d5SAxel Dörfler 	printf("IMAP: fetch body %s\n", ref.name);
142186c96d5SAxel Dörfler 	return B_ERROR;
143186c96d5SAxel Dörfler }
144186c96d5SAxel Dörfler 
145186c96d5SAxel Dörfler 
146186c96d5SAxel Dörfler status_t
147186c96d5SAxel Dörfler IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
148186c96d5SAxel Dörfler {
149186c96d5SAxel Dörfler 	printf("IMAP: mark as read %s: %d\n", ref.name, flags);
150186c96d5SAxel Dörfler 	return B_ERROR;
151186c96d5SAxel Dörfler }
152186c96d5SAxel Dörfler 
153186c96d5SAxel Dörfler 
154186c96d5SAxel Dörfler status_t
155186c96d5SAxel Dörfler IMAPProtocol::DeleteMessage(const entry_ref& ref)
156186c96d5SAxel Dörfler {
157186c96d5SAxel Dörfler 	printf("IMAP: delete message %s\n", ref.name);
158186c96d5SAxel Dörfler 	return B_ERROR;
159186c96d5SAxel Dörfler }
160186c96d5SAxel Dörfler 
161186c96d5SAxel Dörfler 
162186c96d5SAxel Dörfler status_t
163186c96d5SAxel Dörfler IMAPProtocol::AppendMessage(const entry_ref& ref)
164186c96d5SAxel Dörfler {
165186c96d5SAxel Dörfler 	printf("IMAP: append message %s\n", ref.name);
166186c96d5SAxel Dörfler 	return B_ERROR;
167186c96d5SAxel Dörfler }
168186c96d5SAxel Dörfler 
169186c96d5SAxel Dörfler 
170186c96d5SAxel Dörfler void
171186c96d5SAxel Dörfler IMAPProtocol::MessageReceived(BMessage* message)
172186c96d5SAxel Dörfler {
173186c96d5SAxel Dörfler 	switch (message->what) {
174186c96d5SAxel Dörfler 		case B_READY_TO_RUN:
175186c96d5SAxel Dörfler 			ReadyToRun();
176186c96d5SAxel Dörfler 			break;
177adbe8fc9SAxel Dörfler 
178adbe8fc9SAxel Dörfler 		default:
179adbe8fc9SAxel Dörfler 			BInboundMailProtocol::MessageReceived(message);
180adbe8fc9SAxel Dörfler 			break;
181186c96d5SAxel Dörfler 	}
182186c96d5SAxel Dörfler }
183186c96d5SAxel Dörfler 
184186c96d5SAxel Dörfler 
185186c96d5SAxel Dörfler void
186186c96d5SAxel Dörfler IMAPProtocol::ReadyToRun()
187186c96d5SAxel Dörfler {
188adbe8fc9SAxel Dörfler 	puts("IMAP: ready to run!");
189adbe8fc9SAxel Dörfler 	if (fSettings.IdleMode())
190adbe8fc9SAxel Dörfler 		SyncMessages();
191186c96d5SAxel Dörfler }
192186c96d5SAxel Dörfler 
193186c96d5SAxel Dörfler 
194*a4bdd26dSAxel Dörfler IMAPFolder*
195*a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
196*a4bdd26dSAxel Dörfler {
197*a4bdd26dSAxel Dörfler 	BString name = MailboxToFolderName(mailbox, separator);
198*a4bdd26dSAxel Dörfler 
199*a4bdd26dSAxel Dörfler 	BPath path(fSettings.Destination());
200*a4bdd26dSAxel Dörfler 	if (path.Append(name.String()) != B_OK) {
201*a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not append path: %s\n", name.String());
202*a4bdd26dSAxel Dörfler 		return NULL;
203*a4bdd26dSAxel Dörfler 	}
204*a4bdd26dSAxel Dörfler 
205*a4bdd26dSAxel Dörfler 	status_t status = create_directory(path.Path(), 0755);
206*a4bdd26dSAxel Dörfler 	if (status != B_OK) {
207*a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
208*a4bdd26dSAxel Dörfler 			strerror(status));
209*a4bdd26dSAxel Dörfler 		return NULL;
210*a4bdd26dSAxel Dörfler 	}
211*a4bdd26dSAxel Dörfler 
212*a4bdd26dSAxel Dörfler 	entry_ref ref;
213*a4bdd26dSAxel Dörfler 	status = get_ref_for_path(path.Path(), &ref);
214*a4bdd26dSAxel Dörfler 	if (status != B_OK) {
215*a4bdd26dSAxel Dörfler 		fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
216*a4bdd26dSAxel Dörfler 			strerror(status));
217*a4bdd26dSAxel Dörfler 		return NULL;
218*a4bdd26dSAxel Dörfler 	}
219*a4bdd26dSAxel Dörfler 
220*a4bdd26dSAxel Dörfler 	return new IMAPFolder(mailbox, ref);
221*a4bdd26dSAxel Dörfler }
222*a4bdd26dSAxel Dörfler 
223*a4bdd26dSAxel Dörfler 
224*a4bdd26dSAxel Dörfler status_t
225*a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolderChangeSemaphore()
226*a4bdd26dSAxel Dörfler {
227*a4bdd26dSAxel Dörfler 	fFolderChangeSemaphore = create_sem(0, "imap folder change");
228*a4bdd26dSAxel Dörfler 	return fFolderChangeSemaphore < 0 ? fFolderChangeSemaphore : B_OK;
229*a4bdd26dSAxel Dörfler }
230*a4bdd26dSAxel Dörfler 
231*a4bdd26dSAxel Dörfler 
232186c96d5SAxel Dörfler // #pragma mark -
233186c96d5SAxel Dörfler 
234186c96d5SAxel Dörfler 
235186c96d5SAxel Dörfler extern "C" BInboundMailProtocol*
236186c96d5SAxel Dörfler instantiate_inbound_protocol(const BMailAccountSettings& settings)
237186c96d5SAxel Dörfler {
238186c96d5SAxel Dörfler 	return new IMAPProtocol(settings);
239186c96d5SAxel Dörfler }
240