xref: /haiku/src/servers/mail/MailDaemonApplication.cpp (revision fbc2a57b094f83ea3aeda0a9c0f7372861265828)
1 /*
2  * Copyright 2007-2016, Haiku, Inc. All rights reserved.
3  * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
4  * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "MailDaemonApplication.h"
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <vector>
14 
15 #include <Beep.h>
16 #include <Catalog.h>
17 #include <Deskbar.h>
18 #include <Directory.h>
19 #include <Entry.h>
20 #include <FindDirectory.h>
21 #include <fs_index.h>
22 #include <IconUtils.h>
23 #include <NodeInfo.h>
24 #include <NodeMonitor.h>
25 #include <Notification.h>
26 #include <Path.h>
27 #include <Roster.h>
28 #include <StringList.h>
29 #include <StringFormat.h>
30 #include <VolumeRoster.h>
31 
32 #include <E-mail.h>
33 #include <MailDaemon.h>
34 #include <MailMessage.h>
35 #include <MailSettings.h>
36 
37 #include <MailPrivate.h>
38 
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "MailDaemon"
42 
43 
44 static const uint32 kMsgStartAutoCheck = 'stAC';
45 static const uint32 kMsgAutoCheck = 'moto';
46 
47 static const bigtime_t kStartAutoCheckDelay = 30000000;
48 	// Wait 30 seconds before the first auto check - this usually lets the
49 	// boot process settle down, and give the network a chance to come up.
50 
51 
52 struct send_mails_info {
send_mails_infosend_mails_info53 	send_mails_info()
54 	{
55 		bytes = 0;
56 	}
57 
58 	BMessage	files;
59 	off_t		bytes;
60 };
61 
62 
63 class InboundMessenger : public BMessenger {
64 public:
InboundMessenger(BInboundMailProtocol * protocol)65 	InboundMessenger(BInboundMailProtocol* protocol)
66 		:
67 		BMessenger(protocol)
68 	{
69 	}
70 
MarkAsRead(const entry_ref & ref,read_flags flag)71 	status_t MarkAsRead(const entry_ref& ref, read_flags flag)
72 	{
73 		BMessage message(kMsgMarkMessageAsRead);
74 		message.AddRef("ref", &ref);
75 		message.AddInt32("read", flag);
76 
77 		return SendMessage(&message);
78 	}
79 
SynchronizeMessages()80 	status_t SynchronizeMessages()
81 	{
82 		BMessage message(kMsgSyncMessages);
83 		return SendMessage(&message);
84 	}
85 };
86 
87 
88 // #pragma mark -
89 
90 
91 static void
makeIndices()92 makeIndices()
93 {
94 	const char* stringIndices[] = {
95 		B_MAIL_ATTR_CC, B_MAIL_ATTR_FROM, B_MAIL_ATTR_NAME,
96 		B_MAIL_ATTR_PRIORITY, B_MAIL_ATTR_REPLY, B_MAIL_ATTR_STATUS,
97 		B_MAIL_ATTR_SUBJECT, B_MAIL_ATTR_TO, B_MAIL_ATTR_THREAD,
98 		B_MAIL_ATTR_ACCOUNT, NULL
99 	};
100 
101 	// add mail indices for all devices capable of querying
102 
103 	int32 cookie = 0;
104 	dev_t device;
105 	while ((device = next_dev(&cookie)) >= B_OK) {
106 		fs_info info;
107 		if (fs_stat_dev(device, &info) < 0
108 			|| (info.flags & B_FS_HAS_QUERY) == 0)
109 			continue;
110 
111 		for (int32 i = 0; stringIndices[i]; i++)
112 			fs_create_index(device, stringIndices[i], B_STRING_TYPE, 0);
113 
114 		fs_create_index(device, "MAIL:draft", B_INT32_TYPE, 0);
115 		fs_create_index(device, B_MAIL_ATTR_WHEN, B_INT32_TYPE, 0);
116 		fs_create_index(device, B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0);
117 		fs_create_index(device, B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0);
118 		fs_create_index(device, B_MAIL_ATTR_READ, B_INT32_TYPE, 0);
119 	}
120 }
121 
122 
123 static void
addAttribute(BMessage & msg,const char * name,const char * publicName,int32 type=B_STRING_TYPE,bool viewable=true,bool editable=false,int32 width=200)124 addAttribute(BMessage& msg, const char* name, const char* publicName,
125 	int32 type = B_STRING_TYPE, bool viewable = true, bool editable = false,
126 	int32 width = 200)
127 {
128 	msg.AddString("attr:name", name);
129 	msg.AddString("attr:public_name", publicName);
130 	msg.AddInt32("attr:type", type);
131 	msg.AddBool("attr:viewable", viewable);
132 	msg.AddBool("attr:editable", editable);
133 	msg.AddInt32("attr:width", width);
134 	msg.AddInt32("attr:alignment", B_ALIGN_LEFT);
135 }
136 
137 
138 // #pragma mark -
139 
140 
account_protocols()141 account_protocols::account_protocols()
142 	:
143 	inboundImage(-1),
144 	inboundProtocol(NULL),
145 	outboundImage(-1),
146 	outboundProtocol(NULL)
147 {
148 }
149 
150 
151 // #pragma mark -
152 
153 
MailDaemonApplication()154 MailDaemonApplication::MailDaemonApplication()
155 	:
156 	BServer(B_MAIL_DAEMON_SIGNATURE, true, NULL),
157 	fAutoCheckRunner(NULL)
158 {
159 	fErrorLogWindow = new ErrorLogWindow(BRect(200, 200, 500, 250),
160 		B_TRANSLATE("Mail daemon status log"), B_TITLED_WINDOW);
161 	// install MimeTypes, attributes, indices, and the
162 	// system beep add startup
163 	MakeMimeTypes();
164 	makeIndices();
165 	add_system_beep_event("New E-mail");
166 }
167 
168 
~MailDaemonApplication()169 MailDaemonApplication::~MailDaemonApplication()
170 {
171 	delete fAutoCheckRunner;
172 
173 	for (int32 i = 0; i < fQueries.CountItems(); i++)
174 		delete fQueries.ItemAt(i);
175 
176 	while (!fAccounts.empty()) {
177 		_RemoveAccount(fAccounts.begin()->second);
178 		fAccounts.erase(fAccounts.begin());
179 	}
180 
181 	delete fLEDAnimation;
182 	delete fNotification;
183 }
184 
185 
186 void
ReadyToRun()187 MailDaemonApplication::ReadyToRun()
188 {
189 	InstallDeskbarIcon();
190 
191 	_InitAccounts();
192 
193 	// Start auto mail check with a delay
194 	BMessage startAutoCheck(kMsgStartAutoCheck);
195 	BMessageRunner::StartSending(this, &startAutoCheck,
196 		kStartAutoCheckDelay, 1);
197 
198 	_InitNewMessagesCount();
199 
200 	fCentralBeep = false;
201 
202 	fNotification = new BNotification(B_INFORMATION_NOTIFICATION);
203 	fNotification->SetGroup(B_TRANSLATE("Mail status"));
204 	fNotification->SetMessageID("daemon_status");
205 
206 	BPath path;
207 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
208 		path.Append("Mail/New E-mail");
209 
210 		entry_ref ref;
211 		if (get_ref_for_path(path.Path(), &ref) == B_OK) {
212 			fNotification->SetOnClickApp("application/x-vnd.Be-TRAK");
213 			fNotification->AddOnClickRef(&ref);
214 		}
215 	}
216 
217 	_UpdateNewMessagesNotification();
218 
219 	app_info info;
220 	be_roster->GetAppInfo(B_MAIL_DAEMON_SIGNATURE, &info);
221 	BBitmap icon(BRect(0, 0, 32, 32), B_RGBA32);
222 	BNode node(&info.ref);
223 	BIconUtils::GetVectorIcon(&node, "BEOS:ICON", &icon);
224 	fNotification->SetIcon(&icon);
225 
226 	fLEDAnimation = new LEDAnimation();
227 	SetPulseRate(1000000);
228 }
229 
230 
231 void
RefsReceived(BMessage * message)232 MailDaemonApplication::RefsReceived(BMessage* message)
233 {
234 	entry_ref ref;
235 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
236 		BNode node(&ref);
237 		if (node.InitCheck() != B_OK)
238 			continue;
239 
240 		int32 account;
241 		if (node.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &account,
242 				sizeof(account)) < 0)
243 			continue;
244 
245 		BInboundMailProtocol* protocol = _InboundProtocol(account);
246 		if (protocol == NULL)
247 			continue;
248 
249 		BMessenger target;
250 		BMessenger* replyTo = &target;
251 		if (message->FindMessenger("target", &target) != B_OK)
252 			replyTo = NULL;
253 
254 		protocol->FetchBody(ref, replyTo);
255 	}
256 }
257 
258 
259 void
MessageReceived(BMessage * msg)260 MailDaemonApplication::MessageReceived(BMessage* msg)
261 {
262 	switch (msg->what) {
263 		case kMsgStartAutoCheck:
264 			_UpdateAutoCheckRunner();
265 			break;
266 
267 		case kMsgAutoCheck:
268 			// TODO: check whether internet is up and running!
269 			// supposed to fall through
270 		case kMsgCheckAndSend:	// check & send messages
271 			msg->what = kMsgSendMessages;
272 			PostMessage(msg);
273 			// supposed to fall trough
274 		case kMsgCheckMessage:	// check messages
275 			GetNewMessages(msg);
276 			break;
277 
278 		case kMsgSendMessages:	// send messages
279 			SendPendingMessages(msg);
280 			break;
281 
282 		case kMsgSettingsUpdated:
283 			fSettingsFile.Reload();
284 			_UpdateAutoCheckRunner();
285 			break;
286 
287 		case kMsgAccountsChanged:
288 			_ReloadAccounts(msg);
289 			break;
290 
291 		case kMsgMarkMessageAsRead:
292 		{
293 			int32 account = msg->FindInt32("account");
294 			entry_ref ref;
295 			if (msg->FindRef("ref", &ref) != B_OK)
296 				break;
297 			read_flags read = (read_flags)msg->FindInt32("read");
298 
299 			BInboundMailProtocol* protocol = _InboundProtocol(account);
300 			if (protocol != NULL)
301 				InboundMessenger(protocol).MarkAsRead(ref, read);
302 			break;
303 		}
304 
305 		case kMsgFetchBody:
306 			RefsReceived(msg);
307 			break;
308 
309 		case 'stwg':	// Status window gone
310 		{
311 			BMessage reply('mnuc');
312 			reply.AddInt32("num_new_messages", fNewMessages);
313 
314 			while ((msg = fFetchDoneRespondents.RemoveItemAt(0))) {
315 				msg->SendReply(&reply);
316 				delete msg;
317 			}
318 
319 			if (fAlertString != B_EMPTY_STRING) {
320 				fAlertString.Truncate(fAlertString.Length() - 1);
321 				BAlert* alert = new BAlert(B_TRANSLATE("New Messages"),
322 					fAlertString.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL);
323 				alert->SetFeel(B_NORMAL_WINDOW_FEEL);
324 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
325 				alert->Go(NULL);
326 				fAlertString = B_EMPTY_STRING;
327 			}
328 
329 			if (fCentralBeep) {
330 				system_beep("New E-mail");
331 				fCentralBeep = false;
332 			}
333 			break;
334 		}
335 
336 		case 'mcbp':
337 			if (fNewMessages > 0)
338 				fCentralBeep = true;
339 			break;
340 
341 		case kMsgCountNewMessages:	// Number of new messages
342 		{
343 			BMessage reply('mnuc');	// Mail New message Count
344 			if (msg->FindBool("wait_for_fetch_done")) {
345 				fFetchDoneRespondents.AddItem(DetachCurrentMessage());
346 				break;
347 			}
348 
349 			reply.AddInt32("num_new_messages", fNewMessages);
350 			msg->SendReply(&reply);
351 			break;
352 		}
353 
354 		case 'mblk':	// Mail Blink
355 			if (fNewMessages > 0)
356 				fLEDAnimation->Start();
357 			break;
358 
359 		case 'enda':	// End Auto Check
360 			delete fAutoCheckRunner;
361 			fAutoCheckRunner = NULL;
362 			break;
363 
364 		case 'numg':
365 		{
366 			static BStringFormat format(B_TRANSLATE("{0, plural, "
367 				"one{# new message} other{# new messages}} for %name\n"));
368 
369 			int32 numMessages = msg->FindInt32("num_messages");
370 			fAlertString.Truncate(0);
371 			format.Format(fAlertString, numMessages);
372 			fAlertString.ReplaceFirst("%name", msg->FindString("name"));
373 			break;
374 		}
375 
376 		case B_QUERY_UPDATE:
377 		{
378 			int32 previousCount = fNewMessages;
379 
380 			int32 opcode;
381 			msg->FindInt32("opcode", &opcode);
382 
383 			switch (opcode) {
384 				case B_ENTRY_CREATED:
385 				case B_ENTRY_REMOVED:
386 				{
387 					entry_ref ref;
388 					msg->FindInt32("device", &ref.device);
389 					msg->FindInt64("directory", &ref.directory);
390 					BEntry entry(&ref);
391 
392 					if (!_IsEntryInTrash(entry)) {
393 						if (opcode == B_ENTRY_CREATED)
394 							fNewMessages++;
395 						else
396 							fNewMessages--;
397 					}
398 					break;
399 				}
400 				default:
401 					return;
402 			}
403 
404 			_UpdateNewMessagesNotification();
405 
406 			if (fSettingsFile.ShowStatusWindow()
407 					!= B_MAIL_SHOW_STATUS_WINDOW_NEVER
408 				&& previousCount < fNewMessages) {
409 				fNotification->Send();
410 			}
411 			break;
412 		}
413 
414 		default:
415 			BApplication::MessageReceived(msg);
416 			break;
417 	}
418 }
419 
420 
421 void
Pulse()422 MailDaemonApplication::Pulse()
423 {
424 	bigtime_t idle = idle_time();
425 	if (fLEDAnimation->IsRunning() && idle < 100000)
426 		fLEDAnimation->Stop();
427 }
428 
429 
430 bool
QuitRequested()431 MailDaemonApplication::QuitRequested()
432 {
433 	RemoveDeskbarIcon();
434 	return true;
435 }
436 
437 
438 void
InstallDeskbarIcon()439 MailDaemonApplication::InstallDeskbarIcon()
440 {
441 	BDeskbar deskbar;
442 
443 	if (!deskbar.HasItem("mail_daemon")) {
444 		BRoster roster;
445 		entry_ref ref;
446 
447 		status_t status = roster.FindApp(B_MAIL_DAEMON_SIGNATURE, &ref);
448 		if (status < B_OK) {
449 			fprintf(stderr, "Can't find application to tell deskbar: %s\n",
450 				strerror(status));
451 			return;
452 		}
453 
454 		status = deskbar.AddItem(&ref);
455 		if (status < B_OK) {
456 			fprintf(stderr, "Can't add deskbar replicant: %s\n",
457 				strerror(status));
458 			return;
459 		}
460 	}
461 }
462 
463 
464 void
RemoveDeskbarIcon()465 MailDaemonApplication::RemoveDeskbarIcon()
466 {
467 	BDeskbar deskbar;
468 	if (deskbar.HasItem("mail_daemon"))
469 		deskbar.RemoveItem("mail_daemon");
470 }
471 
472 
473 void
GetNewMessages(BMessage * msg)474 MailDaemonApplication::GetNewMessages(BMessage* msg)
475 {
476 	int32 account = -1;
477 	if (msg->FindInt32("account", &account) == B_OK && account >= 0) {
478 		// Check the single requested account
479 		BInboundMailProtocol* protocol = _InboundProtocol(account);
480 		if (protocol != NULL)
481 			InboundMessenger(protocol).SynchronizeMessages();
482 		return;
483 	}
484 
485 	// Check all accounts
486 
487 	AccountMap::const_iterator iterator = fAccounts.begin();
488 	for (; iterator != fAccounts.end(); iterator++) {
489 		BInboundMailProtocol* protocol = iterator->second.inboundProtocol;
490 		if (protocol != NULL)
491 			InboundMessenger(protocol).SynchronizeMessages();
492 	}
493 }
494 
495 
496 void
SendPendingMessages(BMessage * msg)497 MailDaemonApplication::SendPendingMessages(BMessage* msg)
498 {
499 	BVolumeRoster roster;
500 	BVolume volume;
501 	std::map<int32, send_mails_info> messages;
502 	int32 account = msg->GetInt32("account", -1);
503 
504 	if (!msg->HasString("message_path")) {
505 		while (roster.GetNextVolume(&volume) == B_OK) {
506 			BQuery query;
507 			query.SetVolume(&volume);
508 			query.PushAttr(B_MAIL_ATTR_FLAGS);
509 			query.PushInt32(B_MAIL_PENDING);
510 			query.PushOp(B_EQ);
511 
512 			query.PushAttr(B_MAIL_ATTR_FLAGS);
513 			query.PushInt32(B_MAIL_PENDING | B_MAIL_SAVE);
514 			query.PushOp(B_EQ);
515 
516 			if (account >= 0) {
517 				query.PushAttr(B_MAIL_ATTR_ACCOUNT_ID);
518 				query.PushInt32(account);
519 				query.PushOp(B_EQ);
520 				query.PushOp(B_AND);
521 			}
522 
523 			query.PushOp(B_OR);
524 			query.Fetch();
525 			BEntry entry;
526 			while (query.GetNextEntry(&entry) == B_OK) {
527 				if (_IsEntryInTrash(entry))
528 					continue;
529 
530 				BNode node;
531 				while (node.SetTo(&entry) == B_BUSY)
532 					snooze(1000);
533 				if (!_IsPending(node))
534 					continue;
535 
536 				if (node.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0,
537 						&account, sizeof(int32)) < 0)
538 					account = -1;
539 
540 				_AddMessage(messages[account], entry, node);
541 			}
542 		}
543 	} else {
544 		// Send the requested message only
545 		const char* path;
546 		if (msg->FindString("message_path", &path) != B_OK)
547 			return;
548 
549 		BEntry entry(path);
550 		_AddMessage(messages[account], entry, BNode(&entry));
551 	}
552 
553 	std::map<int32, send_mails_info>::iterator iterator = messages.begin();
554 	for (; iterator != messages.end(); iterator++) {
555 		BOutboundMailProtocol* protocol = _OutboundProtocol(iterator->first);
556 		if (protocol == NULL)
557 			continue;
558 
559 		send_mails_info& info = iterator->second;
560 		if (info.bytes == 0)
561 			continue;
562 
563 		protocol->SendMessages(info.files, info.bytes);
564 	}
565 }
566 
567 
568 void
MakeMimeTypes(bool remakeMIMETypes)569 MailDaemonApplication::MakeMimeTypes(bool remakeMIMETypes)
570 {
571 	// Add MIME database entries for the e-mail file types we handle.  Either
572 	// do a full rebuild from nothing, or just add on the new attributes that
573 	// we support which the regular BeOS mail daemon didn't have.
574 
575 	const uint8 kNTypes = 2;
576 	const char* types[kNTypes] = {"text/x-email", "text/x-partial-email"};
577 
578 	for (size_t i = 0; i < kNTypes; i++) {
579 		BMessage info;
580 		BMimeType mime(types[i]);
581 		if (mime.InitCheck() != B_OK) {
582 			fputs("could not init mime type.\n", stderr);
583 			return;
584 		}
585 
586 		if (!mime.IsInstalled() || remakeMIMETypes) {
587 			// install the full mime type
588 			mime.Delete();
589 			mime.Install();
590 
591 			// Set up the list of e-mail related attributes that Tracker will
592 			// let you display in columns for e-mail messages.
593 			addAttribute(info, B_MAIL_ATTR_NAME, "Name");
594 			addAttribute(info, B_MAIL_ATTR_SUBJECT, "Subject");
595 			addAttribute(info, B_MAIL_ATTR_TO, "To");
596 			addAttribute(info, B_MAIL_ATTR_CC, "Cc");
597 			addAttribute(info, B_MAIL_ATTR_FROM, "From");
598 			addAttribute(info, B_MAIL_ATTR_REPLY, "Reply To");
599 			addAttribute(info, B_MAIL_ATTR_STATUS, "Status");
600 			addAttribute(info, B_MAIL_ATTR_PRIORITY, "Priority", B_STRING_TYPE,
601 				true, true, 40);
602 			addAttribute(info, B_MAIL_ATTR_WHEN, "When", B_TIME_TYPE, true,
603 				false, 150);
604 			addAttribute(info, B_MAIL_ATTR_THREAD, "Thread");
605 			addAttribute(info, B_MAIL_ATTR_ACCOUNT, "Account", B_STRING_TYPE,
606 				true, false, 100);
607 			addAttribute(info, B_MAIL_ATTR_READ, "Read", B_INT32_TYPE,
608 				true, false, 70);
609 			mime.SetAttrInfo(&info);
610 
611 			if (i == 0) {
612 				mime.SetShortDescription("E-mail");
613 				mime.SetLongDescription("Electronic Mail Message");
614 				mime.SetPreferredApp("application/x-vnd.Be-MAIL");
615 			} else {
616 				mime.SetShortDescription("Partial E-mail");
617 				mime.SetLongDescription("A Partially Downloaded E-mail");
618 				mime.SetPreferredApp("application/x-vnd.Be-MAIL");
619 			}
620 		}
621 	}
622 }
623 
624 
625 void
_InitAccounts()626 MailDaemonApplication::_InitAccounts()
627 {
628 	BMailAccounts accounts;
629 	for (int i = 0; i < accounts.CountAccounts(); i++)
630 		_InitAccount(*accounts.AccountAt(i));
631 }
632 
633 
634 void
_InitAccount(BMailAccountSettings & settings)635 MailDaemonApplication::_InitAccount(BMailAccountSettings& settings)
636 {
637 	account_protocols account;
638 
639 	// inbound
640 	if (settings.IsInboundEnabled()) {
641 		account.inboundProtocol = _CreateInboundProtocol(settings,
642 			account.inboundImage);
643 	}
644 	if (account.inboundProtocol != NULL) {
645 		DefaultNotifier* notifier = new DefaultNotifier(settings.Name(), true,
646 			fErrorLogWindow, fSettingsFile.ShowStatusWindow());
647 		account.inboundProtocol->SetMailNotifier(notifier);
648 		account.inboundProtocol->Run();
649 	}
650 
651 	// outbound
652 	if (settings.IsOutboundEnabled()) {
653 		account.outboundProtocol = _CreateOutboundProtocol(settings,
654 			account.outboundImage);
655 	}
656 	if (account.outboundProtocol != NULL) {
657 		DefaultNotifier* notifier = new DefaultNotifier(settings.Name(), false,
658 			fErrorLogWindow, fSettingsFile.ShowStatusWindow());
659 		account.outboundProtocol->SetMailNotifier(notifier);
660 		account.outboundProtocol->Run();
661 	}
662 
663 	printf("account name %s, id %i, in %p, out %p\n", settings.Name(),
664 		(int)settings.AccountID(), account.inboundProtocol,
665 		account.outboundProtocol);
666 
667 	if (account.inboundProtocol != NULL || account.outboundProtocol != NULL)
668 		fAccounts[settings.AccountID()] = account;
669 }
670 
671 
672 void
_ReloadAccounts(BMessage * message)673 MailDaemonApplication::_ReloadAccounts(BMessage* message)
674 {
675 	type_code typeFound;
676 	int32 countFound;
677 	message->GetInfo("account", &typeFound, &countFound);
678 	if (typeFound != B_INT32_TYPE)
679 		return;
680 
681 	// reload accounts
682 	BMailAccounts accounts;
683 
684 	for (int i = 0; i < countFound; i++) {
685 		int32 account = message->FindInt32("account", i);
686 		AccountMap::iterator found = fAccounts.find(account);
687 		if (found != fAccounts.end()) {
688 			_RemoveAccount(found->second);
689 			fAccounts.erase(found);
690 		}
691 
692 		BMailAccountSettings* settings = accounts.AccountByID(account);
693 		if (settings != NULL)
694 			_InitAccount(*settings);
695 	}
696 }
697 
698 
699 void
_RemoveAccount(const account_protocols & account)700 MailDaemonApplication::_RemoveAccount(const account_protocols& account)
701 {
702 	if (account.inboundProtocol != NULL) {
703 		account.inboundProtocol->Lock();
704 		account.inboundProtocol->Quit();
705 
706 		unload_add_on(account.inboundImage);
707 	}
708 
709 	if (account.outboundProtocol != NULL) {
710 		account.outboundProtocol->Lock();
711 		account.outboundProtocol->Quit();
712 
713 		unload_add_on(account.outboundImage);
714 	}
715 }
716 
717 
718 BInboundMailProtocol*
_CreateInboundProtocol(BMailAccountSettings & settings,image_id & image)719 MailDaemonApplication::_CreateInboundProtocol(BMailAccountSettings& settings,
720 	image_id& image)
721 {
722 	const entry_ref& entry = settings.InboundAddOnRef();
723 	BInboundMailProtocol* (*instantiateProtocol)(BMailAccountSettings*);
724 
725 	BPath path(&entry);
726 	image = load_add_on(path.Path());
727 	if (image < 0)
728 		return NULL;
729 
730 	if (get_image_symbol(image, "instantiate_inbound_protocol",
731 			B_SYMBOL_TYPE_TEXT, (void**)&instantiateProtocol) != B_OK) {
732 		unload_add_on(image);
733 		image = -1;
734 		return NULL;
735 	}
736 	return instantiateProtocol(&settings);
737 }
738 
739 
740 BOutboundMailProtocol*
_CreateOutboundProtocol(BMailAccountSettings & settings,image_id & image)741 MailDaemonApplication::_CreateOutboundProtocol(BMailAccountSettings& settings,
742 	image_id& image)
743 {
744 	const entry_ref& entry = settings.OutboundAddOnRef();
745 	BOutboundMailProtocol* (*instantiateProtocol)(BMailAccountSettings*);
746 
747 	BPath path(&entry);
748 	image = load_add_on(path.Path());
749 	if (image < 0)
750 		return NULL;
751 
752 	if (get_image_symbol(image, "instantiate_outbound_protocol",
753 			B_SYMBOL_TYPE_TEXT, (void**)&instantiateProtocol) != B_OK) {
754 		unload_add_on(image);
755 		image = -1;
756 		return NULL;
757 	}
758 	return instantiateProtocol(&settings);
759 }
760 
761 
762 BInboundMailProtocol*
_InboundProtocol(int32 account)763 MailDaemonApplication::_InboundProtocol(int32 account)
764 {
765 	AccountMap::iterator found = fAccounts.find(account);
766 	if (found == fAccounts.end())
767 		return NULL;
768 	return found->second.inboundProtocol;
769 }
770 
771 
772 BOutboundMailProtocol*
_OutboundProtocol(int32 account)773 MailDaemonApplication::_OutboundProtocol(int32 account)
774 {
775 	if (account < 0)
776 		account = BMailSettings().DefaultOutboundAccount();
777 
778 	AccountMap::iterator found = fAccounts.find(account);
779 	if (found == fAccounts.end())
780 		return NULL;
781 	return found->second.outboundProtocol;
782 }
783 
784 
785 void
_InitNewMessagesCount()786 MailDaemonApplication::_InitNewMessagesCount()
787 {
788 	BVolume volume;
789 	BVolumeRoster roster;
790 
791 	fNewMessages = 0;
792 
793 	while (roster.GetNextVolume(&volume) == B_OK) {
794 		BQuery* query = new BQuery;
795 
796 		query->SetTarget(this);
797 		query->SetVolume(&volume);
798 		query->PushAttr(B_MAIL_ATTR_STATUS);
799 		query->PushString("New");
800 		query->PushOp(B_EQ);
801 		query->PushAttr("BEOS:TYPE");
802 		query->PushString("text/x-email");
803 		query->PushOp(B_EQ);
804 		query->PushAttr("BEOS:TYPE");
805 		query->PushString("text/x-partial-email");
806 		query->PushOp(B_EQ);
807 		query->PushOp(B_OR);
808 		query->PushOp(B_AND);
809 		query->Fetch();
810 
811 		BEntry entry;
812 		while (query->GetNextEntry(&entry) == B_OK) {
813 			if (!_IsEntryInTrash(entry))
814 				fNewMessages++;
815 		}
816 
817 		fQueries.AddItem(query);
818 	}
819 }
820 
821 
822 void
_UpdateNewMessagesNotification()823 MailDaemonApplication::_UpdateNewMessagesNotification()
824 {
825 	BString title;
826 	if (fNewMessages > 0) {
827 		BStringFormat format(B_TRANSLATE(
828 			"{0, plural, one{One new message} other{# new messages}}"));
829 
830 		format.Format(title, fNewMessages);
831 	} else
832 		title = B_TRANSLATE("No new messages");
833 
834 	fNotification->SetTitle(title.String());
835 }
836 
837 
838 void
_UpdateAutoCheckRunner()839 MailDaemonApplication::_UpdateAutoCheckRunner()
840 {
841 	bigtime_t interval = fSettingsFile.AutoCheckInterval();
842 	if (interval > 0) {
843 		if (fAutoCheckRunner != NULL) {
844 			fAutoCheckRunner->SetInterval(interval);
845 			fAutoCheckRunner->SetCount(-1);
846 		} else {
847 			BMessage update(kMsgAutoCheck);
848 			fAutoCheckRunner = new BMessageRunner(be_app_messenger, &update,
849 				interval);
850 
851 			// Send one right away -- the message runner will wait until the
852 			// first interval has passed before sending a message
853 			PostMessage(&update);
854 		}
855 	} else {
856 		delete fAutoCheckRunner;
857 		fAutoCheckRunner = NULL;
858 	}
859 }
860 
861 
862 void
_AddMessage(send_mails_info & info,const BEntry & entry,const BNode & node)863 MailDaemonApplication::_AddMessage(send_mails_info& info, const BEntry& entry,
864 	const BNode& node)
865 {
866 	entry_ref ref;
867 	off_t size;
868 	if (node.GetSize(&size) == B_OK && entry.GetRef(&ref) == B_OK) {
869 		info.files.AddRef("ref", &ref);
870 		info.bytes += size;
871 	}
872 }
873 
874 
875 /*!	Work-around for a broken index that contains out-of-date information.
876 */
877 /*static*/ bool
_IsPending(BNode & node)878 MailDaemonApplication::_IsPending(BNode& node)
879 {
880 	int32 flags;
881 	if (node.ReadAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, sizeof(int32))
882 			!= (ssize_t)sizeof(int32))
883 		return false;
884 
885 	return (flags & B_MAIL_PENDING) != 0;
886 }
887 
888 
889 /*static*/ bool
_IsEntryInTrash(BEntry & entry)890 MailDaemonApplication::_IsEntryInTrash(BEntry& entry)
891 {
892 	entry_ref ref;
893 	entry.GetRef(&ref);
894 
895 	BVolume volume(ref.device);
896 	BPath path;
897 	if (volume.InitCheck() != B_OK
898 		|| find_directory(B_TRASH_DIRECTORY, &path, false, &volume) != B_OK)
899 		return false;
900 
901 	BDirectory trash(path.Path());
902 	return trash.Contains(&entry);
903 }
904