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