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