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