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 { 53 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: 65 InboundMessenger(BInboundMailProtocol* protocol) 66 : 67 BMessenger(protocol) 68 { 69 } 70 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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* 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* 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* 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* 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 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 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 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 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 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 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