xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/IMAPProtocol.cpp (revision 77320941256637f4b494c57104ff5a45f86a89ba)
1 /*
2  * Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "IMAPProtocol.h"
8 
9 #include <Directory.h>
10 
11 #include "IMAPConnectionWorker.h"
12 #include "IMAPFolder.h"
13 #include "Utilities.h"
14 
15 
16 IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings)
17 	:
18 	BInboundMailProtocol(settings),
19 	fSettings(settings.Name(), settings.InboundSettings()),
20 	fWorkers(5, false)
21 {
22 	BPath destination = fSettings.Destination();
23 
24 	status_t status = create_directory(destination.Path(), 0755);
25 	if (status != B_OK) {
26 		fprintf(stderr, "IMAP: Could not create destination directory %s: %s\n",
27 			destination.Path(), strerror(status));
28 	}
29 
30 	PostMessage(B_READY_TO_RUN);
31 }
32 
33 
34 IMAPProtocol::~IMAPProtocol()
35 {
36 }
37 
38 
39 status_t
40 IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle)
41 {
42 	// Get list of subscribed folders
43 
44 	StringList newFolders;
45 	BString separator;
46 	status_t status = protocol.GetSubscribedFolders(newFolders, separator);
47 	if (status != B_OK)
48 		return status;
49 
50 	// Determine how many new mailboxes we have
51 
52 	StringList::iterator folderIterator = newFolders.begin();
53 	while (folderIterator != newFolders.end()) {
54 		if (fFolders.find(*folderIterator) != fFolders.end())
55 			folderIterator = newFolders.erase(folderIterator);
56 		else
57 			folderIterator++;
58 	}
59 
60 	int32 totalMailboxes = fFolders.size() + newFolders.size();
61 	int32 workersWanted = 1;
62 	if (idle)
63 		workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes);
64 
65 	if (newFolders.empty() && fWorkers.CountItems() == workersWanted) {
66 		// Nothing to do - we've already distributed everything
67 		return B_OK;
68 	}
69 
70 	// Remove mailboxes from workers
71 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
72 		fWorkers.ItemAt(i)->RemoveAllMailboxes();
73 	}
74 
75 	// Create/remove connection workers as allowed and needed
76 	while (fWorkers.CountItems() < workersWanted) {
77 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
78 			fSettings);
79 		if (!fWorkers.AddItem(worker)) {
80 			delete worker;
81 			break;
82 		}
83 
84 		status = worker->Run();
85 		if (status != B_OK) {
86 			fWorkers.RemoveItem(worker);
87 			delete worker;
88 		}
89 	}
90 
91 	while (fWorkers.CountItems() > workersWanted) {
92 		IMAPConnectionWorker* worker
93 			= fWorkers.RemoveItemAt(fWorkers.CountItems() - 1);
94 		worker->Quit();
95 	}
96 
97 	// Update known mailboxes
98 	folderIterator = newFolders.begin();
99 	for (; folderIterator != newFolders.end(); folderIterator++) {
100 		const BString& mailbox = *folderIterator;
101 		fFolders.insert(std::make_pair(mailbox,
102 			_CreateFolder(mailbox, separator)));
103 	}
104 
105 	// Distribute the mailboxes evenly to the workers
106 	FolderMap::iterator iterator = fFolders.begin();
107 	int32 index = 0;
108 	for (; iterator != fFolders.end(); iterator++) {
109 		fWorkers.ItemAt(index)->AddMailbox(iterator->second);
110 		index = (index + 1) % fWorkers.CountItems();
111 	}
112 
113 	// Start waiting workers
114 	return _EnqueueCheckMailboxes();
115 }
116 
117 
118 void
119 IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
120 {
121 	fWorkers.RemoveItem(worker);
122 }
123 
124 
125 void
126 IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream,
127 	uint32 fetchFlags, BMessage& attributes)
128 {
129 	if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody))
130 			== IMAP::kFetchHeader | IMAP::kFetchBody) {
131 		ProcessMessageFetched(ref, stream, attributes);
132 	} else if ((fetchFlags & IMAP::kFetchHeader) != 0) {
133 		ProcessHeaderFetched(ref, stream, attributes);
134 	} else if ((fetchFlags & IMAP::kFetchBody) != 0) {
135 		NotifyBodyFetched(ref, stream, attributes);
136 	}
137 }
138 
139 
140 status_t
141 IMAPProtocol::SyncMessages()
142 {
143 	puts("IMAP: sync");
144 
145 	if (fWorkers.IsEmpty()) {
146 		// Create main (and possibly initial) connection worker
147 		IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this,
148 			fSettings, true);
149 		if (!fWorkers.AddItem(worker)) {
150 			delete worker;
151 			return B_NO_MEMORY;
152 		}
153 
154 		worker->EnqueueCheckSubscribedFolders();
155 		return worker->Run();
156 	}
157 
158 	return _EnqueueCheckMailboxes();
159 }
160 
161 
162 status_t
163 IMAPProtocol::FetchBody(const entry_ref& ref)
164 {
165 	printf("IMAP: fetch body %s\n", ref.name);
166 	return B_ERROR;
167 }
168 
169 
170 status_t
171 IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags)
172 {
173 	printf("IMAP: mark as read %s: %d\n", ref.name, flags);
174 	return B_ERROR;
175 }
176 
177 
178 status_t
179 IMAPProtocol::DeleteMessage(const entry_ref& ref)
180 {
181 	printf("IMAP: delete message %s\n", ref.name);
182 	return B_ERROR;
183 }
184 
185 
186 status_t
187 IMAPProtocol::AppendMessage(const entry_ref& ref)
188 {
189 	printf("IMAP: append message %s\n", ref.name);
190 	return B_ERROR;
191 }
192 
193 
194 void
195 IMAPProtocol::MessageReceived(BMessage* message)
196 {
197 	switch (message->what) {
198 		case B_READY_TO_RUN:
199 			ReadyToRun();
200 			break;
201 
202 		default:
203 			BInboundMailProtocol::MessageReceived(message);
204 			break;
205 	}
206 }
207 
208 
209 void
210 IMAPProtocol::ReadyToRun()
211 {
212 	puts("IMAP: ready to run!");
213 	if (fSettings.IdleMode())
214 		SyncMessages();
215 }
216 
217 
218 IMAPFolder*
219 IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
220 {
221 	BString name = MailboxToFolderName(mailbox, separator);
222 
223 	BPath path(fSettings.Destination());
224 	if (path.Append(name.String()) != B_OK) {
225 		fprintf(stderr, "Could not append path: %s\n", name.String());
226 		return NULL;
227 	}
228 
229 	status_t status = create_directory(path.Path(), 0755);
230 	if (status != B_OK) {
231 		fprintf(stderr, "Could not create path %s: %s\n", path.Path(),
232 			strerror(status));
233 		return NULL;
234 	}
235 
236 	entry_ref ref;
237 	status = get_ref_for_path(path.Path(), &ref);
238 	if (status != B_OK) {
239 		fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(),
240 			strerror(status));
241 		return NULL;
242 	}
243 
244 	return new IMAPFolder(*this, mailbox, ref);
245 }
246 
247 
248 status_t
249 IMAPProtocol::_EnqueueCheckMailboxes()
250 {
251 	for (int32 i = 0; i < fWorkers.CountItems(); i++) {
252 		fWorkers.ItemAt(i)->EnqueueCheckMailboxes();
253 	}
254 
255 	return B_OK;
256 }
257 
258 
259 // #pragma mark -
260 
261 
262 extern "C" BInboundMailProtocol*
263 instantiate_inbound_protocol(const BMailAccountSettings& settings)
264 {
265 	return new IMAPProtocol(settings);
266 }
267