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