/*
 * Copyright 2007-2016, Haiku, Inc. All rights reserved.
 * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
 * Distributed under the terms of the MIT License.
 */
 
 
#include "MailDaemonApplication.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <vector>
 
#include <Beep.h>
#include <Catalog.h>
#include <Deskbar.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <fs_index.h>
#include <IconUtils.h>
#include <NodeMonitor.h>
#include <Notification.h>
#include <Path.h>
#include <Roster.h>
#include <StringList.h>
#include <StringFormat.h>
#include <VolumeRoster.h>
 
#include <E-mail.h>
#include <MailDaemon.h>
#include <MailMessage.h>
#include <MailSettings.h>
 
#include <MailPrivate.h>
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MailDaemon"
 
 
static const uint32 kMsgStartAutoCheck = 'stAC';
static const uint32 kMsgAutoCheck = 'moto';
 
static const bigtime_t kStartAutoCheckDelay = 30000000;
	// Wait 30 seconds before the first auto check - this usually lets the
	// boot process settle down, and give the network a chance to come up.
 
 
struct send_mails_info {
	send_mails_info()
	{
		bytes = 0;
	}
 
	BMessage	files;
	off_t		bytes;
};
 
 
class InboundMessenger : public BMessenger {
public:
	InboundMessenger(BInboundMailProtocol* protocol)
		:
		BMessenger(protocol)
	{
	}
 
	status_t MarkAsRead(const entry_ref& ref, read_flags flag)
	{
		BMessage message(kMsgMarkMessageAsRead);
		message.AddRef("ref", &ref);
		message.AddInt32("read", flag);
 
		return SendMessage(&message);
	}
 
	status_t SynchronizeMessages()
	{
		BMessage message(kMsgSyncMessages);
		return SendMessage(&message);
	}
};
 
 
// #pragma mark -
 
 
static void
makeIndices()
{
	const char* stringIndices[] = {
		B_MAIL_ATTR_CC, B_MAIL_ATTR_FROM, B_MAIL_ATTR_NAME,
		B_MAIL_ATTR_PRIORITY, B_MAIL_ATTR_REPLY, B_MAIL_ATTR_STATUS,
		B_MAIL_ATTR_SUBJECT, B_MAIL_ATTR_TO, B_MAIL_ATTR_THREAD,
		B_MAIL_ATTR_ACCOUNT, NULL
	};
 
	// add mail indices for all devices capable of querying
 
	int32 cookie = 0;
	dev_t device;
	while ((device = next_dev(&cookie)) >= B_OK) {
		fs_info info;
		if (fs_stat_dev(device, &info) < 0
			|| (info.flags & B_FS_HAS_QUERY) == 0)
			continue;
 
		for (int32 i = 0; stringIndices[i]; i++)
			fs_create_index(device, stringIndices[i], B_STRING_TYPE, 0);
 
		fs_create_index(device, "MAIL:draft", B_INT32_TYPE, 0);
		fs_create_index(device, B_MAIL_ATTR_WHEN, B_INT32_TYPE, 0);
		fs_create_index(device, B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0);
		fs_create_index(device, B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0);
		fs_create_index(device, B_MAIL_ATTR_READ, B_INT32_TYPE, 0);
	}
}
 
 
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)
{
	msg.AddString("attr:name", name);
	msg.AddString("attr:public_name", publicName);
	msg.AddInt32("attr:type", type);
	msg.AddBool("attr:viewable", viewable);
	msg.AddBool("attr:editable", editable);
	msg.AddInt32("attr:width", width);
	msg.AddInt32("attr:alignment", B_ALIGN_LEFT);
}
 
 
// #pragma mark -
 
 
account_protocols::account_protocols()
	:
	inboundImage(-1),
	inboundProtocol(NULL),
	outboundImage(-1),
	outboundProtocol(NULL)
{
}
 
 
// #pragma mark -
 
 
MailDaemonApplication::MailDaemonApplication()
	:
	BServer(B_MAIL_DAEMON_SIGNATURE, true, NULL),
	fAutoCheckRunner(NULL)
{
	fErrorLogWindow = new ErrorLogWindow(BRect(200, 200, 500, 250),
		B_TRANSLATE("Mail daemon status log"), B_TITLED_WINDOW);
	// install MimeTypes, attributes, indices, and the
	// system beep add startup
	MakeMimeTypes();
	makeIndices();
	add_system_beep_event("New E-mail");
}
 
 
MailDaemonApplication::~MailDaemonApplication()
{
	delete fAutoCheckRunner;
 
	for (int32 i = 0; i < fQueries.CountItems(); i++)
		delete fQueries.ItemAt(i);
 
	while (!fAccounts.empty()) {
		_RemoveAccount(fAccounts.begin()->second);
		fAccounts.erase(fAccounts.begin());
	}
 
	delete fLEDAnimation;
	delete fNotification;
}
 
 
void
MailDaemonApplication::ReadyToRun()
{
	InstallDeskbarIcon();
 
	_InitAccounts();
 
	// Start auto mail check with a delay
	BMessage startAutoCheck(kMsgStartAutoCheck);
	BMessageRunner::StartSending(this, &startAutoCheck,
		kStartAutoCheckDelay, 1);
 
	_InitNewMessagesCount();
 
	fCentralBeep = false;
 
	fNotification = new BNotification(B_INFORMATION_NOTIFICATION);
	fNotification->SetGroup(B_TRANSLATE("Mail status"));
	fNotification->SetMessageID("daemon_status");
	_UpdateNewMessagesNotification();
 
	app_info info;
	be_roster->GetAppInfo(B_MAIL_DAEMON_SIGNATURE, &info);
	BBitmap icon(BRect(0, 0, 32, 32), B_RGBA32);
	BNode node(&info.ref);
	BIconUtils::GetVectorIcon(&node, "BEOS:ICON", &icon);
	fNotification->SetIcon(&icon);
 
	fLEDAnimation = new LEDAnimation();
	SetPulseRate(1000000);
}
 
 
void
MailDaemonApplication::RefsReceived(BMessage* message)
{
	entry_ref ref;
	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
		BNode node(&ref);
		if (node.InitCheck() != B_OK)
			continue;
 
		int32 account;
		if (node.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &account,
				sizeof(account)) < 0)
			continue;
 
		BInboundMailProtocol* protocol = _InboundProtocol(account);
		if (protocol == NULL)
			continue;
 
		BMessenger target;
		BMessenger* replyTo = &target;
		if (message->FindMessenger("target", &target) != B_OK)
			replyTo = NULL;
 
		protocol->FetchBody(ref, replyTo);
	}
}
 
 
void
MailDaemonApplication::MessageReceived(BMessage* msg)
{
	switch (msg->what) {
		case kMsgStartAutoCheck:
			_UpdateAutoCheckRunner();
			break;
 
		case kMsgAutoCheck:
			// TODO: check whether internet is up and running!
			// supposed to fall through
		case kMsgCheckAndSend:	// check & send messages
			msg->what = kMsgSendMessages;
			PostMessage(msg);
			// supposed to fall trough
		case kMsgCheckMessage:	// check messages
			GetNewMessages(msg);
			break;
 
		case kMsgSendMessages:	// send messages
			SendPendingMessages(msg);
			break;
 
		case kMsgSettingsUpdated:
			fSettingsFile.Reload();
			_UpdateAutoCheckRunner();
			break;
 
		case kMsgAccountsChanged:
			_ReloadAccounts(msg);
			break;
 
		case kMsgMarkMessageAsRead:
		{
			int32 account = msg->FindInt32("account");
			entry_ref ref;
			if (msg->FindRef("ref", &ref) != B_OK)
				break;
			read_flags read = (read_flags)msg->FindInt32("read");
 
			BInboundMailProtocol* protocol = _InboundProtocol(account);
			if (protocol != NULL)
				InboundMessenger(protocol).MarkAsRead(ref, read);
			break;
		}
 
		case kMsgFetchBody:
			RefsReceived(msg);
			break;
 
		case 'stwg':	// Status window gone
		{
			BMessage reply('mnuc');
			reply.AddInt32("num_new_messages", fNewMessages);
 
			while ((msg = fFetchDoneRespondents.RemoveItemAt(0))) {
				msg->SendReply(&reply);
				delete msg;
			}
 
			if (fAlertString != B_EMPTY_STRING) {
				fAlertString.Truncate(fAlertString.Length() - 1);
				BAlert* alert = new BAlert(B_TRANSLATE("New Messages"),
					fAlertString.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL);
				alert->SetFeel(B_NORMAL_WINDOW_FEEL);
				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
				alert->Go(NULL);
				fAlertString = B_EMPTY_STRING;
			}
 
			if (fCentralBeep) {
				system_beep("New E-mail");
				fCentralBeep = false;
			}
			break;
		}
 
		case 'mcbp':
			if (fNewMessages > 0)
				fCentralBeep = true;
			break;
 
		case kMsgCountNewMessages:	// Number of new messages
		{
			BMessage reply('mnuc');	// Mail New message Count
			if (msg->FindBool("wait_for_fetch_done")) {
				fFetchDoneRespondents.AddItem(DetachCurrentMessage());
				break;
			}
 
			reply.AddInt32("num_new_messages", fNewMessages);
			msg->SendReply(&reply);
			break;
		}
 
		case 'mblk':	// Mail Blink
			if (fNewMessages > 0)
				fLEDAnimation->Start();
			break;
 
		case 'enda':	// End Auto Check
			delete fAutoCheckRunner;
			fAutoCheckRunner = NULL;
			break;
 
		case 'numg':
		{
			static BStringFormat format(B_TRANSLATE("{0, plural, "
				"one{# new message} other{# new messages}} for %name\n"));
 
			int32 numMessages = msg->FindInt32("num_messages");
			fAlertString.Truncate(0);
			format.Format(fAlertString, numMessages);
			fAlertString.ReplaceFirst("%name", msg->FindString("name"));
			break;
		}
 
		case B_QUERY_UPDATE:
		{
			int32 previousCount = fNewMessages;
 
			int32 opcode = msg->GetInt32("opcode", -1);
			switch (opcode) {
				case B_ENTRY_CREATED:
					fNewMessages++;
					break;
				case B_ENTRY_REMOVED:
					fNewMessages--;
					break;
				default:
					return;
			}
 
			_UpdateNewMessagesNotification();
 
			if (fSettingsFile.ShowStatusWindow()
					!= B_MAIL_SHOW_STATUS_WINDOW_NEVER
				&& previousCount < fNewMessages) {
				fNotification->Send();
			}
			break;
		}
 
		default:
			BApplication::MessageReceived(msg);
			break;
	}
}
 
 
void
MailDaemonApplication::Pulse()
{
	bigtime_t idle = idle_time();
	if (fLEDAnimation->IsRunning() && idle < 100000)
		fLEDAnimation->Stop();
}
 
 
bool
MailDaemonApplication::QuitRequested()
{
	RemoveDeskbarIcon();
	return true;
}
 
 
void
MailDaemonApplication::InstallDeskbarIcon()
{
	BDeskbar deskbar;
 
	if (!deskbar.HasItem("mail_daemon")) {
		BRoster roster;
		entry_ref ref;
 
		status_t status = roster.FindApp(B_MAIL_DAEMON_SIGNATURE, &ref);
		if (status < B_OK) {
			fprintf(stderr, "Can't find application to tell deskbar: %s\n",
				strerror(status));
			return;
		}
 
		status = deskbar.AddItem(&ref);
		if (status < B_OK) {
			fprintf(stderr, "Can't add deskbar replicant: %s\n",
				strerror(status));
			return;
		}
	}
}
 
 
void
MailDaemonApplication::RemoveDeskbarIcon()
{
	BDeskbar deskbar;
	if (deskbar.HasItem("mail_daemon"))
		deskbar.RemoveItem("mail_daemon");
}
 
 
void
MailDaemonApplication::GetNewMessages(BMessage* msg)
{
	int32 account = -1;
	if (msg->FindInt32("account", &account) == B_OK && account >= 0) {
		// Check the single requested account
		BInboundMailProtocol* protocol = _InboundProtocol(account);
		if (protocol != NULL)
			InboundMessenger(protocol).SynchronizeMessages();
		return;
	}
 
	// Check all accounts
 
	AccountMap::const_iterator iterator = fAccounts.begin();
	for (; iterator != fAccounts.end(); iterator++) {
		BInboundMailProtocol* protocol = iterator->second.inboundProtocol;
		if (protocol != NULL)
			InboundMessenger(protocol).SynchronizeMessages();
	}
}
 
 
void
MailDaemonApplication::SendPendingMessages(BMessage* msg)
{
	BVolumeRoster roster;
	BVolume volume;
	std::map<int32, send_mails_info> messages;
	int32 account = msg->GetInt32("account", -1);
 
	if (!msg->HasString("message_path")) {
		while (roster.GetNextVolume(&volume) == B_OK) {
			BQuery query;
			query.SetVolume(&volume);
			query.PushAttr(B_MAIL_ATTR_FLAGS);
			query.PushInt32(B_MAIL_PENDING);
			query.PushOp(B_EQ);
 
			query.PushAttr(B_MAIL_ATTR_FLAGS);
			query.PushInt32(B_MAIL_PENDING | B_MAIL_SAVE);
			query.PushOp(B_EQ);
 
			if (account >= 0) {
				query.PushAttr(B_MAIL_ATTR_ACCOUNT_ID);
				query.PushInt32(account);
				query.PushOp(B_EQ);
				query.PushOp(B_AND);
			}
 
			query.PushOp(B_OR);
			query.Fetch();
			BEntry entry;
			while (query.GetNextEntry(&entry) == B_OK) {
				if (_IsEntryInTrash(entry))
					continue;
 
				BNode node;
				while (node.SetTo(&entry) == B_BUSY)
					snooze(1000);
				if (!_IsPending(node))
					continue;
 
				if (node.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0,
						&account, sizeof(int32)) < 0)
					account = -1;
 
				_AddMessage(messages[account], entry, node);
			}
		}
	} else {
		// Send the requested message only
		const char* path;
		if (msg->FindString("message_path", &path) != B_OK)
			return;
 
		BEntry entry(path);
		_AddMessage(messages[account], entry, BNode(&entry));
	}
 
	std::map<int32, send_mails_info>::iterator iterator = messages.begin();
	for (; iterator != messages.end(); iterator++) {
		BOutboundMailProtocol* protocol = _OutboundProtocol(iterator->first);
		if (protocol == NULL)
			continue;
 
		send_mails_info& info = iterator->second;
		if (info.bytes == 0)
			continue;
 
		protocol->SendMessages(info.files, info.bytes);
	}
}
 
 
void
MailDaemonApplication::MakeMimeTypes(bool remakeMIMETypes)
{
	// Add MIME database entries for the e-mail file types we handle.  Either
	// do a full rebuild from nothing, or just add on the new attributes that
	// we support which the regular BeOS mail daemon didn't have.
 
	const uint8 kNTypes = 2;
	const char* types[kNTypes] = {"text/x-email", "text/x-partial-email"};
 
	for (size_t i = 0; i < kNTypes; i++) {
		BMessage info;
		BMimeType mime(types[i]);
		if (mime.InitCheck() != B_OK) {
			fputs("could not init mime type.\n", stderr);
			return;
		}
 
		if (!mime.IsInstalled() || remakeMIMETypes) {
			// install the full mime type
			mime.Delete();
			mime.Install();
 
			// Set up the list of e-mail related attributes that Tracker will
			// let you display in columns for e-mail messages.
			addAttribute(info, B_MAIL_ATTR_NAME, "Name");
			addAttribute(info, B_MAIL_ATTR_SUBJECT, "Subject");
			addAttribute(info, B_MAIL_ATTR_TO, "To");
			addAttribute(info, B_MAIL_ATTR_CC, "Cc");
			addAttribute(info, B_MAIL_ATTR_FROM, "From");
			addAttribute(info, B_MAIL_ATTR_REPLY, "Reply To");
			addAttribute(info, B_MAIL_ATTR_STATUS, "Status");
			addAttribute(info, B_MAIL_ATTR_PRIORITY, "Priority", B_STRING_TYPE,
				true, true, 40);
			addAttribute(info, B_MAIL_ATTR_WHEN, "When", B_TIME_TYPE, true,
				false, 150);
			addAttribute(info, B_MAIL_ATTR_THREAD, "Thread");
			addAttribute(info, B_MAIL_ATTR_ACCOUNT, "Account", B_STRING_TYPE,
				true, false, 100);
			addAttribute(info, B_MAIL_ATTR_READ, "Read", B_INT32_TYPE,
				true, false, 70);
			mime.SetAttrInfo(&info);
 
			if (i == 0) {
				mime.SetShortDescription("E-mail");
				mime.SetLongDescription("Electronic Mail Message");
				mime.SetPreferredApp("application/x-vnd.Be-MAIL");
			} else {
				mime.SetShortDescription("Partial E-mail");
				mime.SetLongDescription("A Partially Downloaded E-mail");
				mime.SetPreferredApp("application/x-vnd.Be-MAIL");
			}
		}
	}
}
 
 
void
MailDaemonApplication::_InitAccounts()
{
	BMailAccounts accounts;
	for (int i = 0; i < accounts.CountAccounts(); i++)
		_InitAccount(*accounts.AccountAt(i));
}
 
 
void
MailDaemonApplication::_InitAccount(BMailAccountSettings& settings)
{
	account_protocols account;
 
	// inbound
	if (settings.IsInboundEnabled()) {
		account.inboundProtocol = _CreateInboundProtocol(settings,
			account.inboundImage);
	}
	if (account.inboundProtocol != NULL) {
		DefaultNotifier* notifier = new DefaultNotifier(settings.Name(), true,
			fErrorLogWindow, fSettingsFile.ShowStatusWindow());
		account.inboundProtocol->SetMailNotifier(notifier);
		account.inboundProtocol->Run();
	}
 
	// outbound
	if (settings.IsOutboundEnabled()) {
		account.outboundProtocol = _CreateOutboundProtocol(settings,
			account.outboundImage);
	}
	if (account.outboundProtocol != NULL) {
		DefaultNotifier* notifier = new DefaultNotifier(settings.Name(), false,
			fErrorLogWindow, fSettingsFile.ShowStatusWindow());
		account.outboundProtocol->SetMailNotifier(notifier);
		account.outboundProtocol->Run();
	}
 
	printf("account name %s, id %i, in %p, out %p\n", settings.Name(),
		(int)settings.AccountID(), account.inboundProtocol,
		account.outboundProtocol);
 
	if (account.inboundProtocol != NULL || account.outboundProtocol != NULL)
		fAccounts[settings.AccountID()] = account;
}
 
 
void
MailDaemonApplication::_ReloadAccounts(BMessage* message)
{
	type_code typeFound;
	int32 countFound;
	message->GetInfo("account", &typeFound, &countFound);
	if (typeFound != B_INT32_TYPE)
		return;
 
	// reload accounts
	BMailAccounts accounts;
 
	for (int i = 0; i < countFound; i++) {
		int32 account = message->FindInt32("account", i);
		AccountMap::iterator found = fAccounts.find(account);
		if (found != fAccounts.end()) {
			_RemoveAccount(found->second);
			fAccounts.erase(found);
		}
 
		BMailAccountSettings* settings = accounts.AccountByID(account);
		if (settings != NULL)
			_InitAccount(*settings);
	}
}
 
 
void
MailDaemonApplication::_RemoveAccount(const account_protocols& account)
{
	if (account.inboundProtocol != NULL) {
		account.inboundProtocol->Lock();
		account.inboundProtocol->Quit();
 
		unload_add_on(account.inboundImage);
	}
 
	if (account.outboundProtocol != NULL) {
		account.outboundProtocol->Lock();
		account.outboundProtocol->Quit();
 
		unload_add_on(account.outboundImage);
	}
}
 
 
BInboundMailProtocol*
MailDaemonApplication::_CreateInboundProtocol(BMailAccountSettings& settings,
	image_id& image)
{
	const entry_ref& entry = settings.InboundAddOnRef();
	BInboundMailProtocol* (*instantiateProtocol)(BMailAccountSettings*);
 
	BPath path(&entry);
	image = load_add_on(path.Path());
	if (image < 0)
		return NULL;
 
	if (get_image_symbol(image, "instantiate_inbound_protocol",
			B_SYMBOL_TYPE_TEXT, (void**)&instantiateProtocol) != B_OK) {
		unload_add_on(image);
		image = -1;
		return NULL;
	}
	return instantiateProtocol(&settings);
}
 
 
BOutboundMailProtocol*
MailDaemonApplication::_CreateOutboundProtocol(BMailAccountSettings& settings,
	image_id& image)
{
	const entry_ref& entry = settings.OutboundAddOnRef();
	BOutboundMailProtocol* (*instantiateProtocol)(BMailAccountSettings*);
 
	BPath path(&entry);
	image = load_add_on(path.Path());
	if (image < 0)
		return NULL;
 
	if (get_image_symbol(image, "instantiate_outbound_protocol",
			B_SYMBOL_TYPE_TEXT, (void**)&instantiateProtocol) != B_OK) {
		unload_add_on(image);
		image = -1;
		return NULL;
	}
	return instantiateProtocol(&settings);
}
 
 
BInboundMailProtocol*
MailDaemonApplication::_InboundProtocol(int32 account)
{
	AccountMap::iterator found = fAccounts.find(account);
	if (found == fAccounts.end())
		return NULL;
	return found->second.inboundProtocol;
}
 
 
BOutboundMailProtocol*
MailDaemonApplication::_OutboundProtocol(int32 account)
{
	if (account < 0)
		account = BMailSettings().DefaultOutboundAccount();
 
	AccountMap::iterator found = fAccounts.find(account);
	if (found == fAccounts.end())
		return NULL;
	return found->second.outboundProtocol;
}
 
 
void
MailDaemonApplication::_InitNewMessagesCount()
{
	BVolume volume;
	BVolumeRoster roster;
 
	fNewMessages = 0;
 
	while (roster.GetNextVolume(&volume) == B_OK) {
		BQuery* query = new BQuery;
 
		query->SetTarget(this);
		query->SetVolume(&volume);
		query->PushAttr(B_MAIL_ATTR_STATUS);
		query->PushString("New");
		query->PushOp(B_EQ);
		query->PushAttr("BEOS:TYPE");
		query->PushString("text/x-email");
		query->PushOp(B_EQ);
		query->PushAttr("BEOS:TYPE");
		query->PushString("text/x-partial-email");
		query->PushOp(B_EQ);
		query->PushOp(B_OR);
		query->PushOp(B_AND);
		query->Fetch();
 
		BEntry entry;
		while (query->GetNextEntry(&entry) == B_OK)
			fNewMessages++;
 
		fQueries.AddItem(query);
	}
}
 
 
void
MailDaemonApplication::_UpdateNewMessagesNotification()
{
	BString title;
	if (fNewMessages > 0) {
		BStringFormat format(B_TRANSLATE(
			"{0, plural, one{One new message} other{# new messages}}"));
 
		format.Format(title, fNewMessages);
	} else
		title = B_TRANSLATE("No new messages");
 
	fNotification->SetTitle(title.String());
}
 
 
void
MailDaemonApplication::_UpdateAutoCheckRunner()
{
	bigtime_t interval = fSettingsFile.AutoCheckInterval();
	if (interval > 0) {
		if (fAutoCheckRunner != NULL) {
			fAutoCheckRunner->SetInterval(interval);
			fAutoCheckRunner->SetCount(-1);
		} else {
			BMessage update(kMsgAutoCheck);
			fAutoCheckRunner = new BMessageRunner(be_app_messenger, &update,
				interval);
 
			// Send one right away -- the message runner will wait until the
			// first interval has passed before sending a message
			PostMessage(&update);
		}
	} else {
		delete fAutoCheckRunner;
		fAutoCheckRunner = NULL;
	}
}
 
 
void
MailDaemonApplication::_AddMessage(send_mails_info& info, const BEntry& entry,
	const BNode& node)
{
	entry_ref ref;
	off_t size;
	if (node.GetSize(&size) == B_OK && entry.GetRef(&ref) == B_OK) {
		info.files.AddRef("ref", &ref);
		info.bytes += size;
	}
}
 
 
/*!	Work-around for a broken index that contains out-of-date information.
*/
/*static*/ bool
MailDaemonApplication::_IsPending(BNode& node)
{
	int32 flags;
	if (node.ReadAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, sizeof(int32))
			!= (ssize_t)sizeof(int32))
		return false;
 
	return (flags & B_MAIL_PENDING) != 0;
}
 
 
/*static*/ bool
MailDaemonApplication::_IsEntryInTrash(BEntry& entry)
{
	entry_ref ref;
	entry.GetRef(&ref);
 
	BVolume volume(ref.device);
	BPath path;
	if (volume.InitCheck() != B_OK
		|| find_directory(B_TRASH_DIRECTORY, &path, false, &volume) != B_OK)
		return false;
 
	BDirectory trash(path.Path());
	return trash.Contains(&entry);
}

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.