xref: /haiku/src/kits/mail/MailProtocol.cpp (revision 3c08adef21129761f27ae654a1c5d1705786691a)
1 /*
2  * Copyright 2011-2016, Haiku, Inc. All rights reserved.
3  * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
4  */
5 
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 
10 #include <fs_attr.h>
11 
12 #include <Alert.h>
13 #include <Autolock.h>
14 #include <Directory.h>
15 #include <E-mail.h>
16 #include <FindDirectory.h>
17 #include <Node.h>
18 #include <NodeInfo.h>
19 #include <NodeMonitor.h>
20 #include <Path.h>
21 #include <Query.h>
22 #include <Roster.h>
23 #include <String.h>
24 #include <StringList.h>
25 #include <VolumeRoster.h>
26 
27 #include <MailFilter.h>
28 #include <MailDaemon.h>
29 #include <MailProtocol.h>
30 #include <MailSettings.h>
31 
32 #include <mail_util.h>
33 #include <MailPrivate.h>
34 #include <NodeMessage.h>
35 
36 #include "HaikuMailFormatFilter.h"
37 
38 
39 using namespace BPrivate;
40 
41 
42 BMailProtocol::BMailProtocol(const char* name,
43 	const BMailAccountSettings& settings)
44 	:
45 	BLooper(_LooperName(name, settings)),
46 	fAccountSettings(settings),
47 	fMailNotifier(NULL)
48 {
49 	AddFilter(new HaikuMailFormatFilter(*this, settings));
50 }
51 
52 
53 BMailProtocol::~BMailProtocol()
54 {
55 	delete fMailNotifier;
56 
57 	for (int i = 0; i < fFilterList.CountItems(); i++)
58 		delete fFilterList.ItemAt(i);
59 
60 	std::map<entry_ref, image_id>::iterator it = fFilterImages.begin();
61 	for (; it != fFilterImages.end(); it++)
62 		unload_add_on(it->second);
63 }
64 
65 
66 const BMailAccountSettings&
67 BMailProtocol::AccountSettings() const
68 {
69 	return fAccountSettings;
70 }
71 
72 
73 void
74 BMailProtocol::SetMailNotifier(BMailNotifier* mailNotifier)
75 {
76 	delete fMailNotifier;
77 	fMailNotifier = mailNotifier;
78 }
79 
80 
81 BMailNotifier*
82 BMailProtocol::MailNotifier() const
83 {
84 	return fMailNotifier;
85 }
86 
87 
88 bool
89 BMailProtocol::AddFilter(BMailFilter* filter)
90 {
91 	BAutolock locker(const_cast< BMailProtocol * >(this));
92 	return fFilterList.AddItem(filter);
93 }
94 
95 
96 int32
97 BMailProtocol::CountFilter() const
98 {
99 	BAutolock locker(const_cast< BMailProtocol * >(this));
100 	return fFilterList.CountItems();
101 }
102 
103 
104 BMailFilter*
105 BMailProtocol::FilterAt(int32 index) const
106 {
107 	BAutolock locker(const_cast< BMailProtocol * >(this));
108 	return fFilterList.ItemAt(index);
109 }
110 
111 
112 BMailFilter*
113 BMailProtocol::RemoveFilter(int32 index)
114 {
115 	BAutolock locker(const_cast< BMailProtocol * >(this));
116 	return fFilterList.RemoveItemAt(index);
117 }
118 
119 
120 bool
121 BMailProtocol::RemoveFilter(BMailFilter* filter)
122 {
123 	BAutolock locker(const_cast< BMailProtocol * >(this));
124 	return fFilterList.RemoveItem(filter);
125 }
126 
127 
128 void
129 BMailProtocol::MessageReceived(BMessage* message)
130 {
131 	BLooper::MessageReceived(message);
132 }
133 
134 
135 void
136 BMailProtocol::ShowError(const char* error)
137 {
138 	if (MailNotifier() != NULL)
139 		MailNotifier()->ShowError(error);
140 }
141 
142 
143 void
144 BMailProtocol::ShowMessage(const char* message)
145 {
146 	if (MailNotifier() != NULL)
147 		MailNotifier()->ShowMessage(message);
148 }
149 
150 
151 void
152 BMailProtocol::SetTotalItems(uint32 items)
153 {
154 	if (MailNotifier() != NULL)
155 		MailNotifier()->SetTotalItems(items);
156 }
157 
158 
159 void
160 BMailProtocol::SetTotalItemsSize(uint64 size)
161 {
162 	if (MailNotifier() != NULL)
163 		MailNotifier()->SetTotalItemsSize(size);
164 }
165 
166 
167 void
168 BMailProtocol::ReportProgress(uint32 messages, uint64 bytes,
169 	const char* message)
170 {
171 	if (MailNotifier() != NULL)
172 		MailNotifier()->ReportProgress(messages, bytes, message);
173 }
174 
175 
176 void
177 BMailProtocol::ResetProgress(const char* message)
178 {
179 	if (MailNotifier() != NULL)
180 		MailNotifier()->ResetProgress(message);
181 }
182 
183 
184 void
185 BMailProtocol::NotifyNewMessagesToFetch(int32 count)
186 {
187 	ResetProgress();
188 	SetTotalItems(count);
189 }
190 
191 
192 BMailFilterAction
193 BMailProtocol::ProcessHeaderFetched(entry_ref& ref, BFile& file,
194 	BMessage& attributes)
195 {
196 	BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
197 	if (action >= B_OK && action != B_DELETE_MAIL_ACTION)
198 		file << attributes;
199 
200 	return action;
201 }
202 
203 
204 void
205 BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file,
206 	BMessage& attributes)
207 {
208 	_NotifyBodyFetched(ref, file, attributes);
209 	file << attributes;
210 }
211 
212 
213 BMailFilterAction
214 BMailProtocol::ProcessMessageFetched(entry_ref& ref, BFile& file,
215 	BMessage& attributes)
216 {
217 	BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
218 	if (action >= B_OK && action != B_DELETE_MAIL_ACTION) {
219 		_NotifyBodyFetched(ref, file, attributes);
220 		file << attributes;
221 	}
222 
223 	return action;
224 }
225 
226 
227 void
228 BMailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile& file)
229 {
230 	for (int i = 0; i < fFilterList.CountItems(); i++)
231 		fFilterList.ItemAt(i)->MessageReadyToSend(ref, file);
232 }
233 
234 
235 void
236 BMailProtocol::NotifyMessageSent(const entry_ref& ref, BFile& file)
237 {
238 	for (int i = 0; i < fFilterList.CountItems(); i++)
239 		fFilterList.ItemAt(i)->MessageSent(ref, file);
240 }
241 
242 
243 void
244 BMailProtocol::LoadFilters(const BMailProtocolSettings& settings)
245 {
246 	for (int i = 0; i < settings.CountFilterSettings(); i++) {
247 		BMailAddOnSettings* filterSettings = settings.FilterSettingsAt(i);
248 		BMailFilter* filter = _LoadFilter(*filterSettings);
249 		if (filter != NULL)
250 			AddFilter(filter);
251 	}
252 }
253 
254 
255 /*static*/ BString
256 BMailProtocol::_LooperName(const char* addOnName,
257 	const BMailAccountSettings& settings)
258 {
259 	BString name = addOnName;
260 
261 	const char* accountName = settings.Name();
262 	if (accountName != NULL && accountName[0] != '\0')
263 		name << " " << accountName;
264 
265 	return name;
266 }
267 
268 
269 BMailFilter*
270 BMailProtocol::_LoadFilter(const BMailAddOnSettings& settings)
271 {
272 	const entry_ref& ref = settings.AddOnRef();
273 	std::map<entry_ref, image_id>::iterator it = fFilterImages.find(ref);
274 	image_id image;
275 	if (it != fFilterImages.end())
276 		image = it->second;
277 	else {
278 		BEntry entry(&ref);
279 		BPath path(&entry);
280 		image = load_add_on(path.Path());
281 	}
282 	if (image < 0)
283 		return NULL;
284 
285 	BMailFilter* (*instantiateFilter)(BMailProtocol& protocol,
286 		const BMailAddOnSettings& settings);
287 	if (get_image_symbol(image, "instantiate_filter", B_SYMBOL_TYPE_TEXT,
288 			(void**)&instantiateFilter) != B_OK) {
289 		unload_add_on(image);
290 		return NULL;
291 	}
292 
293 	fFilterImages[ref] = image;
294 	return instantiateFilter(*this, settings);
295 }
296 
297 
298 BMailFilterAction
299 BMailProtocol::_ProcessHeaderFetched(entry_ref& ref, BFile& file,
300 	BMessage& attributes)
301 {
302 	entry_ref outRef = ref;
303 
304 	for (int i = 0; i < fFilterList.CountItems(); i++) {
305 		BMailFilterAction action = fFilterList.ItemAt(i)->HeaderFetched(outRef,
306 			file, attributes);
307 		if (action == B_DELETE_MAIL_ACTION) {
308 			// We have to delete the message
309 			BEntry entry(&ref);
310 			status_t status = entry.Remove();
311 			if (status != B_OK) {
312 				fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could "
313 					"not delete mail: %s\n", strerror(status));
314 			}
315 			return B_DELETE_MAIL_ACTION;
316 		}
317 	}
318 
319 	if (ref == outRef)
320 		return B_NO_MAIL_ACTION;
321 
322 	// We have to rename the file
323 	node_ref newParentRef;
324 	newParentRef.device = outRef.device;
325 	newParentRef.node = outRef.directory;
326 
327 	BDirectory newParent(&newParentRef);
328 	status_t status = newParent.InitCheck();
329 	BString workerName;
330 	if (status == B_OK) {
331 		int32 uniqueNumber = 1;
332 		do {
333 			workerName = outRef.name;
334 			if (uniqueNumber > 1)
335 				workerName << "_" << uniqueNumber;
336 
337 			// TODO: support copying to another device!
338 			BEntry entry(&ref);
339 			status = entry.Rename(workerName);
340 
341 			uniqueNumber++;
342 		} while (status == B_FILE_EXISTS);
343 	}
344 
345 	if (status != B_OK) {
346 		fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could not "
347 			"rename mail (%s)! (should be: %s)\n", strerror(status),
348 			workerName.String());
349 	}
350 
351 	ref = outRef;
352 	ref.set_name(workerName.String());
353 
354 	return B_MOVE_MAIL_ACTION;
355 }
356 
357 
358 void
359 BMailProtocol::_NotifyBodyFetched(const entry_ref& ref, BFile& file,
360 	BMessage& attributes)
361 {
362 	for (int i = 0; i < fFilterList.CountItems(); i++)
363 		fFilterList.ItemAt(i)->BodyFetched(ref, file, attributes);
364 }
365 
366 
367 // #pragma mark -
368 
369 
370 BInboundMailProtocol::BInboundMailProtocol(const char* name,
371 	const BMailAccountSettings& settings)
372 	:
373 	BMailProtocol(name, settings)
374 {
375 	LoadFilters(fAccountSettings.InboundSettings());
376 }
377 
378 
379 BInboundMailProtocol::~BInboundMailProtocol()
380 {
381 }
382 
383 
384 void
385 BInboundMailProtocol::MessageReceived(BMessage* message)
386 {
387 	switch (message->what) {
388 		case kMsgSyncMessages:
389 		{
390 			NotiyMailboxSynchronized(SyncMessages());
391 			break;
392 		}
393 
394 		case kMsgFetchBody:
395 		{
396 			entry_ref ref;
397 			if (message->FindRef("ref", &ref) != B_OK)
398 				break;
399 
400 			BMessenger target;
401 			message->FindMessenger("target", &target);
402 
403 			status_t status = HandleFetchBody(ref, target);
404 			if (status != B_OK)
405 				ReplyBodyFetched(target, ref, status);
406 			break;
407 		}
408 
409 		case kMsgMarkMessageAsRead:
410 		{
411 			entry_ref ref;
412 			message->FindRef("ref", &ref);
413 			read_flags read = (read_flags)message->FindInt32("read");
414 			MarkMessageAsRead(ref, read);
415 			break;
416 		}
417 
418 		default:
419 			BMailProtocol::MessageReceived(message);
420 			break;
421 	}
422 }
423 
424 
425 status_t
426 BInboundMailProtocol::FetchBody(const entry_ref& ref, BMessenger* replyTo)
427 {
428 	BMessage message(kMsgFetchBody);
429 	message.AddRef("ref", &ref);
430 	if (replyTo != NULL)
431 		message.AddMessenger("target", *replyTo);
432 
433 	return BMessenger(this).SendMessage(&message);
434 }
435 
436 
437 status_t
438 BInboundMailProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
439 {
440 	BNode node(&ref);
441 	return write_read_attr(node, flag);
442 }
443 
444 
445 /*static*/ void
446 BInboundMailProtocol::ReplyBodyFetched(const BMessenger& replyTo,
447 	const entry_ref& ref, status_t status)
448 {
449 	BMessage message(B_MAIL_BODY_FETCHED);
450 	message.AddInt32("status", status);
451 	message.AddRef("ref", &ref);
452 	replyTo.SendMessage(&message);
453 }
454 
455 
456 void
457 BInboundMailProtocol::NotiyMailboxSynchronized(status_t status)
458 {
459 	for (int32 i = 0; i < CountFilter(); i++)
460 		FilterAt(i)->MailboxSynchronized(status);
461 }
462 
463 
464 // #pragma mark -
465 
466 
467 BOutboundMailProtocol::BOutboundMailProtocol(const char* name,
468 	const BMailAccountSettings& settings)
469 	:
470 	BMailProtocol(name, settings)
471 {
472 	LoadFilters(fAccountSettings.OutboundSettings());
473 }
474 
475 
476 BOutboundMailProtocol::~BOutboundMailProtocol()
477 {
478 }
479 
480 
481 status_t
482 BOutboundMailProtocol::SendMessages(const BMessage& files, off_t totalBytes)
483 {
484 	BMessage message(kMsgSendMessages);
485 	message.Append(files);
486 	message.AddInt64("bytes", totalBytes);
487 
488 	return BMessenger(this).SendMessage(&message);
489 }
490 
491 
492 void
493 BOutboundMailProtocol::MessageReceived(BMessage* message)
494 {
495 	switch (message->what) {
496 		case kMsgSendMessages:
497 			HandleSendMessages(*message, message->FindInt64("bytes"));
498 			break;
499 
500 		default:
501 			BMailProtocol::MessageReceived(message);
502 	}
503 }
504