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