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