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 = ⌖
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