xref: /haiku/src/kits/mail/MailProtocol.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2011, Haiku, Inc. All rights reserved.
3  * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
4  */
5 
6 
7 #include <stdio.h>
8 #include <fs_attr.h>
9 #include <stdlib.h>
10 #include <assert.h>
11 
12 #include <Alert.h>
13 #include <Directory.h>
14 #include <FindDirectory.h>
15 #include <Query.h>
16 #include <E-mail.h>
17 #include <Node.h>
18 #include <NodeInfo.h>
19 #include <NodeMonitor.h>
20 #include <Path.h>
21 #include <Roster.h>
22 #include <String.h>
23 #include <StringList.h>
24 #include <VolumeRoster.h>
25 
26 #include <mail_util.h>
27 #include <MailAddon.h>
28 #include <MailDaemon.h>
29 #include <MailProtocol.h>
30 #include <MailSettings.h>
31 
32 #include "HaikuMailFormatFilter.h"
33 
34 
35 using std::map;
36 
37 
38 const uint32 kMsgSyncMessages = '&SyM';
39 const uint32 kMsgDeleteMessage = '&DeM';
40 const uint32 kMsgAppendMessage = '&ApM';
41 
42 const uint32 kMsgMoveFile = '&MoF';
43 const uint32 kMsgDeleteFile = '&DeF';
44 const uint32 kMsgFileRenamed = '&FiR';
45 const uint32 kMsgFileDeleted = '&FDe';
46 const uint32 kMsgInit = '&Ini';
47 
48 const uint32 kMsgSendMessage = '&SeM';
49 
50 
51 MailFilter::MailFilter(MailProtocol& protocol, AddonSettings* settings)
52 	:
53 	fMailProtocol(protocol),
54 	fAddonSettings(settings)
55 {
56 }
57 
58 
59 MailFilter::~MailFilter()
60 {
61 }
62 
63 
64 void
65 MailFilter::HeaderFetched(const entry_ref& ref, BFile* file)
66 {
67 }
68 
69 
70 void
71 MailFilter::BodyFetched(const entry_ref& ref, BFile* file)
72 {
73 }
74 
75 
76 void
77 MailFilter::MailboxSynced(status_t status)
78 {
79 }
80 
81 
82 void
83 MailFilter::MessageReadyToSend(const entry_ref& ref, BFile* file)
84 {
85 }
86 
87 
88 void
89 MailFilter::MessageSent(const entry_ref& ref, BFile* file)
90 {
91 }
92 
93 
94 // #pragma mark -
95 
96 
97 MailProtocol::MailProtocol(BMailAccountSettings* settings)
98 	:
99 	fMailNotifier(NULL),
100 	fProtocolThread(NULL)
101 {
102 	fAccountSettings = *settings;
103 
104 	AddFilter(new HaikuMailFormatFilter(*this, settings));
105 }
106 
107 
108 MailProtocol::~MailProtocol()
109 {
110 	delete fMailNotifier;
111 
112 	for (int i = 0; i < fFilterList.CountItems(); i++)
113 		delete fFilterList.ItemAt(i);
114 
115 	map<entry_ref, image_id>::iterator it = fFilterImages.begin();
116 	for (; it != fFilterImages.end(); it++)
117 		unload_add_on(it->second);
118 }
119 
120 
121 BMailAccountSettings&
122 MailProtocol::AccountSettings()
123 {
124 	return fAccountSettings;
125 }
126 
127 
128 void
129 MailProtocol::SetProtocolThread(MailProtocolThread* protocolThread)
130 {
131 	if (fProtocolThread) {
132 		fProtocolThread->Lock();
133 		for (int i = 0; i < fHandlerList.CountItems(); i++)
134 			fProtocolThread->RemoveHandler(fHandlerList.ItemAt(i));
135 		fProtocolThread->Unlock();
136 	}
137 
138 	fProtocolThread = protocolThread;
139 
140 	if (!fProtocolThread)
141 		return;
142 
143 	fProtocolThread->Lock();
144 	for (int i = 0; i < fHandlerList.CountItems(); i++)
145 		fProtocolThread->AddHandler(fHandlerList.ItemAt(i));
146 	fProtocolThread->Unlock();
147 
148 	AddedToLooper();
149 }
150 
151 
152 MailProtocolThread*
153 MailProtocol::Looper()
154 {
155 	return fProtocolThread;
156 }
157 
158 
159 bool
160 MailProtocol::AddHandler(BHandler* handler)
161 {
162 	if (!fHandlerList.AddItem(handler))
163 		return false;
164 	if (fProtocolThread) {
165 		fProtocolThread->Lock();
166 		fProtocolThread->AddHandler(handler);
167 		fProtocolThread->Unlock();
168 	}
169 	return true;
170 }
171 
172 
173 bool
174 MailProtocol::RemoveHandler(BHandler* handler)
175 {
176 	if (!fHandlerList.RemoveItem(handler))
177 		return false;
178 	if (fProtocolThread) {
179 		fProtocolThread->Lock();
180 		fProtocolThread->RemoveHandler(handler);
181 		fProtocolThread->Unlock();
182 	}
183 	return true;
184 }
185 
186 
187 void
188 MailProtocol::SetMailNotifier(MailNotifier* mailNotifier)
189 {
190 	delete fMailNotifier;
191 	fMailNotifier = mailNotifier;
192 }
193 
194 
195 void
196 MailProtocol::ShowError(const char* error)
197 {
198 	if (fMailNotifier)
199 		fMailNotifier->ShowError(error);
200 }
201 
202 
203 void
204 MailProtocol::ShowMessage(const char* message)
205 {
206 	if (fMailNotifier)
207 		fMailNotifier->ShowMessage(message);
208 }
209 
210 
211 void
212 MailProtocol::SetTotalItems(int32 items)
213 {
214 	if (fMailNotifier)
215 		fMailNotifier->SetTotalItems(items);
216 }
217 
218 
219 void
220 MailProtocol::SetTotalItemsSize(int32 size)
221 {
222 	if (fMailNotifier)
223 		fMailNotifier->SetTotalItemsSize(size);
224 }
225 
226 
227 void
228 MailProtocol::ReportProgress(int bytes, int messages, const char* message)
229 {
230 	if (fMailNotifier)
231 		fMailNotifier->ReportProgress(bytes, messages, message);
232 }
233 
234 
235 void
236 MailProtocol::ResetProgress(const char* message)
237 {
238 	if (fMailNotifier)
239 		fMailNotifier->ResetProgress(message);
240 }
241 
242 
243 bool
244 MailProtocol::AddFilter(MailFilter* filter)
245 {
246 	return fFilterList.AddItem(filter);
247 }
248 
249 
250 int32
251 MailProtocol::CountFilter()
252 {
253 	return fFilterList.CountItems();
254 }
255 
256 
257 MailFilter*
258 MailProtocol::FilterAt(int32 index)
259 {
260 	return fFilterList.ItemAt(index);
261 }
262 
263 
264 MailFilter*
265 MailProtocol::RemoveFilter(int32 index)
266 {
267 	return fFilterList.RemoveItemAt(index);
268 }
269 
270 
271 bool
272 MailProtocol::RemoveFilter(MailFilter* filter)
273 {
274 	return fFilterList.RemoveItem(filter);
275 }
276 
277 
278 void
279 MailProtocol::NotifyNewMessagesToFetch(int32 nMessages)
280 {
281 	ResetProgress();
282 	SetTotalItems(nMessages);
283 }
284 
285 
286 void
287 MailProtocol::NotifyHeaderFetched(const entry_ref& ref, BFile* data)
288 {
289 	for (int i = 0; i < fFilterList.CountItems(); i++)
290 		fFilterList.ItemAt(i)->HeaderFetched(ref, data);
291 }
292 
293 
294 void
295 MailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile* data)
296 {
297 	for (int i = 0; i < fFilterList.CountItems(); i++)
298 		fFilterList.ItemAt(i)->BodyFetched(ref, data);
299 }
300 
301 
302 void
303 MailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile* data)
304 {
305 	for (int i = 0; i < fFilterList.CountItems(); i++)
306 		fFilterList.ItemAt(i)->MessageReadyToSend(ref, data);
307 }
308 
309 
310 void
311 MailProtocol::NotifyMessageSent(const entry_ref& ref, BFile* data)
312 {
313 	for (int i = 0; i < fFilterList.CountItems(); i++)
314 		fFilterList.ItemAt(i)->MessageSent(ref, data);
315 }
316 
317 
318 status_t
319 MailProtocol::MoveMessage(const entry_ref& ref, BDirectory& dir)
320 {
321 	BEntry entry(&ref);
322 	return entry.MoveTo(&dir);
323 }
324 
325 
326 status_t
327 MailProtocol::DeleteMessage(const entry_ref& ref)
328 {
329 	BEntry entry(&ref);
330 	return entry.Remove();
331 }
332 
333 
334 void
335 MailProtocol::FileRenamed(const entry_ref& from, const entry_ref& to)
336 {
337 
338 }
339 
340 
341 void
342 MailProtocol::FileDeleted(const node_ref& node)
343 {
344 
345 }
346 
347 
348 void
349 MailProtocol::LoadFilters(MailAddonSettings& settings)
350 {
351 	for (int i = 0; i < settings.CountFilterSettings(); i++) {
352 		AddonSettings* filterSettings = settings.FilterSettingsAt(i);
353 		MailFilter* filter = _LoadFilter(filterSettings);
354 		if (!filter)
355 			continue;
356 		AddFilter(filter);
357 	}
358 }
359 
360 
361 MailFilter*
362 MailProtocol::_LoadFilter(AddonSettings* filterSettings)
363 {
364 	const entry_ref& ref = filterSettings->AddonRef();
365 	map<entry_ref, image_id>::iterator it = fFilterImages.find(ref);
366 	image_id image;
367 	if (it != fFilterImages.end())
368 		image = it->second;
369 	else {
370 		BEntry entry(&ref);
371 		BPath path(&entry);
372 		image = load_add_on(path.Path());
373 	}
374 	if (image < 0)
375 		return NULL;
376 
377 	MailFilter* (*instantiate_mailfilter)(MailProtocol& protocol,
378 		AddonSettings* settings);
379 	if (get_image_symbol(image, "instantiate_mailfilter",
380 		B_SYMBOL_TYPE_TEXT, (void **)&instantiate_mailfilter)
381 			!= B_OK) {
382 		unload_add_on(image);
383 		return NULL;
384 	}
385 
386 	fFilterImages[ref] = image;
387 	return (*instantiate_mailfilter)(*this, filterSettings);
388 }
389 
390 
391 // #pragma mark -
392 
393 
394 InboundProtocol::InboundProtocol(BMailAccountSettings* settings)
395 	:
396 	MailProtocol(settings)
397 {
398 	LoadFilters(fAccountSettings.InboundSettings());
399 }
400 
401 
402 InboundProtocol::~InboundProtocol()
403 {
404 
405 }
406 
407 
408 status_t
409 InboundProtocol::AppendMessage(const entry_ref& ref)
410 {
411 	return false;
412 }
413 
414 
415 status_t
416 InboundProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
417 {
418 	BNode node(&ref);
419 	return write_read_attr(node, flag);
420 }
421 
422 
423 // #pragma mark -
424 
425 
426 OutboundProtocol::OutboundProtocol(BMailAccountSettings* settings)
427 	:
428 	MailProtocol(settings)
429 {
430 	LoadFilters(fAccountSettings.OutboundSettings());
431 }
432 
433 
434 OutboundProtocol::~OutboundProtocol()
435 {
436 
437 }
438 
439 
440 // #pragma mark -
441 
442 
443 MailProtocolThread::MailProtocolThread(MailProtocol* protocol)
444 	:
445 	fMailProtocol(protocol)
446 {
447 	PostMessage(kMsgInit);
448 }
449 
450 
451 void
452 MailProtocolThread::SetStopNow()
453 {
454 	fMailProtocol->SetStopNow();
455 }
456 
457 
458 void
459 MailProtocolThread::MessageReceived(BMessage* message)
460 {
461 	switch (message->what) {
462 		case kMsgInit:
463 			fMailProtocol->SetProtocolThread(this);
464 			break;
465 
466 		case kMsgMoveFile:
467 		{
468 			entry_ref file;
469 			message->FindRef("file", &file);
470 			entry_ref dir;
471 			message->FindRef("directory", &dir);
472 			BDirectory directory(&dir);
473 			fMailProtocol->MoveMessage(file, directory);
474 			break;
475 		}
476 
477 		case kMsgDeleteFile:
478 		{
479 			entry_ref file;
480 			message->FindRef("file", &file);
481 			fMailProtocol->DeleteMessage(file);
482 			break;
483 		}
484 
485 		case kMsgFileRenamed:
486 		{
487 			entry_ref from;
488 			message->FindRef("from", &from);
489 			entry_ref to;
490 			message->FindRef("to", &to);
491 			fMailProtocol->FileRenamed(from, to);
492 			break;
493 		}
494 
495 		case kMsgFileDeleted:
496 		{
497 			node_ref node;
498 			message->FindInt32("device",&node.device);
499 			message->FindInt64("node", &node.node);
500 			fMailProtocol->FileDeleted(node);
501 			break;
502 		}
503 
504 		default:
505 			BLooper::MessageReceived(message);
506 	}
507 }
508 
509 
510 void
511 MailProtocolThread::TriggerFileMove(const entry_ref& ref, BDirectory& dir)
512 {
513 	BMessage message(kMsgMoveFile);
514 	message.AddRef("file", &ref);
515 	BEntry entry;
516 	dir.GetEntry(&entry);
517 	entry_ref dirRef;
518 	entry.GetRef(&dirRef);
519 	message.AddRef("directory", &dirRef);
520 	PostMessage(&message);
521 }
522 
523 
524 void
525 MailProtocolThread::TriggerFileDeletion(const entry_ref& ref)
526 {
527 	BMessage message(kMsgDeleteFile);
528 	message.AddRef("file", &ref);
529 	PostMessage(&message);
530 }
531 
532 
533 void
534 MailProtocolThread::TriggerFileRenamed(const entry_ref& from,
535 	const entry_ref& to)
536 {
537 	BMessage message(kMsgFileRenamed);
538 	message.AddRef("from", &from);
539 	message.AddRef("to", &to);
540 	PostMessage(&message);
541 }
542 
543 
544 void
545 MailProtocolThread::TriggerFileDeleted(const node_ref& node)
546 {
547 	BMessage message(kMsgFileDeleted);
548 	message.AddInt32("device", node.device);
549 	message.AddInt64("node", node.node);
550 	PostMessage(&message);
551 }
552 
553 
554 // #pragma mark -
555 
556 
557 InboundProtocolThread::InboundProtocolThread(InboundProtocol* protocol)
558 	:
559 	MailProtocolThread(protocol),
560 	fProtocol(protocol)
561 {
562 
563 }
564 
565 
566 InboundProtocolThread::~InboundProtocolThread()
567 {
568 	fProtocol->SetProtocolThread(NULL);
569 }
570 
571 
572 void
573 InboundProtocolThread::MessageReceived(BMessage* message)
574 {
575 	switch (message->what) {
576 		case kMsgSyncMessages:
577 		{
578 			status_t status = fProtocol->SyncMessages();
579 			_NotiyMailboxSynced(status);
580 			break;
581 		}
582 
583 		case kMsgFetchBody:
584 		{
585 			entry_ref ref;
586 			message->FindRef("ref", &ref);
587 			status_t status = fProtocol->FetchBody(ref);
588 
589 			BMessenger target;
590 			if (message->FindMessenger("target", &target) != B_OK)
591 				break;
592 
593 			BMessage message(kMsgBodyFetched);
594 			message.AddInt32("status", status);
595 			message.AddRef("ref", &ref);
596 			target.SendMessage(&message);
597 			break;
598 		}
599 
600 		case kMsgMarkMessageAsRead:
601 		{
602 			entry_ref ref;
603 			message->FindRef("ref", &ref);
604 			read_flags read = (read_flags)message->FindInt32("read");
605 			fProtocol->MarkMessageAsRead(ref, read);
606 			break;
607 		}
608 
609 		case kMsgDeleteMessage:
610 		{
611 			entry_ref ref;
612 			message->FindRef("ref", &ref);
613 			fProtocol->DeleteMessage(ref);
614 			break;
615 		}
616 
617 		case kMsgAppendMessage:
618 		{
619 			entry_ref ref;
620 			message->FindRef("ref", &ref);
621 			fProtocol->AppendMessage(ref);
622 			break;
623 		}
624 
625 		default:
626 			MailProtocolThread::MessageReceived(message);
627 			break;
628 	}
629 }
630 
631 
632 void
633 InboundProtocolThread::SyncMessages()
634 {
635 	PostMessage(kMsgSyncMessages);
636 }
637 
638 
639 void
640 InboundProtocolThread::FetchBody(const entry_ref& ref, BMessenger* listener)
641 {
642 	BMessage message(kMsgFetchBody);
643 	message.AddRef("ref", &ref);
644 	if (listener)
645 		message.AddMessenger("target", *listener);
646 	PostMessage(&message);
647 }
648 
649 
650 void
651 InboundProtocolThread::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
652 {
653 	BMessage message(kMsgMarkMessageAsRead);
654 	message.AddRef("ref", &ref);
655 	message.AddInt32("read", flag);
656 	PostMessage(&message);
657 }
658 
659 
660 void
661 InboundProtocolThread::DeleteMessage(const entry_ref& ref)
662 {
663 	BMessage message(kMsgDeleteMessage);
664 	message.AddRef("ref", &ref);
665 	PostMessage(&message);
666 }
667 
668 
669 void
670 InboundProtocolThread::AppendMessage(const entry_ref& ref)
671 {
672 	BMessage message(kMsgAppendMessage);
673 	message.AddRef("ref", &ref);
674 	PostMessage(&message);
675 }
676 
677 
678 void
679 InboundProtocolThread::_NotiyMailboxSynced(status_t status)
680 {
681 	for (int i = 0; i < fProtocol->CountFilter(); i++)
682 		fProtocol->FilterAt(i)->MailboxSynced(status);
683 }
684 
685 
686 // #pragma mark -
687 
688 
689 OutboundProtocolThread::OutboundProtocolThread(OutboundProtocol* protocol)
690 	:
691 	MailProtocolThread(protocol),
692 	fProtocol(protocol)
693 {
694 
695 }
696 
697 
698 OutboundProtocolThread::~OutboundProtocolThread()
699 {
700 	fProtocol->SetProtocolThread(NULL);
701 }
702 
703 
704 void
705 OutboundProtocolThread::MessageReceived(BMessage* message)
706 {
707 	switch (message->what) {
708 		case kMsgSendMessage:
709 		{
710 			std::vector<entry_ref> mails;
711 			for (int32 i = 0; ;i++) {
712 				entry_ref ref;
713 				if (message->FindRef("ref", i, &ref) != B_OK)
714 					break;
715 				mails.push_back(ref);
716 			}
717 			size_t size = message->FindInt32("size");
718 			fProtocol->SendMessages(mails, size);
719 			break;
720 		}
721 
722 		default:
723 			MailProtocolThread::MessageReceived(message);
724 	}
725 }
726 
727 
728 void
729 OutboundProtocolThread::SendMessages(const std::vector<entry_ref>& mails,
730 	size_t totalBytes)
731 {
732 	BMessage message(kMsgSendMessage);
733 	for (unsigned int i = 0; i < mails.size(); i++)
734 		message.AddRef("ref", &mails[i]);
735 	message.AddInt32("size", totalBytes);
736 	PostMessage(&message);
737 }
738