1/* 2 * Copyright 2004-2015, Haiku Inc. All rights reserved. 3 * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9//! The mail daemon's settings 10 11 12#include <MailSettings.h> 13 14#include <stdio.h> 15#include <string.h> 16#include <stdlib.h> 17 18#include <Directory.h> 19#include <Entry.h> 20#include <File.h> 21#include <FindDirectory.h> 22#include <MailDaemon.h> 23#include <Message.h> 24#include <Messenger.h> 25#include <Path.h> 26#include <PathFinder.h> 27#include <String.h> 28#include <Window.h> 29 30#include <MailPrivate.h> 31 32 33// #pragma mark - BMailSettings 34 35 36BMailSettings::BMailSettings() 37{ 38 Reload(); 39} 40 41 42BMailSettings::~BMailSettings() 43{ 44} 45 46 47status_t 48BMailSettings::InitCheck() const 49{ 50 return B_OK; 51} 52 53 54status_t 55BMailSettings::Save() 56{ 57 BPath path; 58 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 59 if (status != B_OK) { 60 fprintf(stderr, "Couldn't find user settings directory: %s\n", 61 strerror(status)); 62 return status; 63 } 64 65 path.Append("Mail"); 66 67 status = BPrivate::WriteMessageFile(fData, path, "new_mail_daemon"); 68 if (status != B_OK) 69 return status; 70 71 BMessenger(B_MAIL_DAEMON_SIGNATURE).SendMessage( 72 BPrivate::kMsgSettingsUpdated); 73 return B_OK; 74} 75 76 77status_t 78BMailSettings::Reload() 79{ 80 // Try directories from most specific to least 81 directory_which which[] = { 82 B_USER_SETTINGS_DIRECTORY, 83 B_SYSTEM_SETTINGS_DIRECTORY}; 84 status_t status = B_ENTRY_NOT_FOUND; 85 86 for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) { 87 BPath path; 88 status = find_directory(which[i], &path); 89 if (status != B_OK) 90 continue; 91 92 path.Append("Mail/new_mail_daemon"); 93 BFile file; 94 status = file.SetTo(path.Path(), B_READ_ONLY); 95 if (status != B_OK) 96 continue; 97 98 // read settings 99 BMessage settings; 100 status = settings.Unflatten(&file); 101 if (status != B_OK) { 102 fprintf(stderr, "Couldn't read settings from '%s': %s\n", 103 path.Path(), strerror(status)); 104 continue; 105 } 106 107 // clobber old settings 108 fData = settings; 109 return B_OK; 110 } 111 112 return status; 113} 114 115 116// # pragma mark - Global settings 117 118 119int32 120BMailSettings::WindowFollowsCorner() 121{ 122 return fData.FindInt32("WindowFollowsCorner"); 123} 124 125 126void 127BMailSettings::SetWindowFollowsCorner(int32 whichCorner) 128{ 129 if (fData.ReplaceInt32("WindowFollowsCorner", whichCorner) != B_OK) 130 fData.AddInt32("WindowFollowsCorner", whichCorner); 131} 132 133 134uint32 135BMailSettings::ShowStatusWindow() 136{ 137 int32 showStatusWindow; 138 if (fData.FindInt32("ShowStatusWindow", &showStatusWindow) != B_OK) { 139 // show during send and receive 140 return 2; 141 } 142 143 return showStatusWindow; 144} 145 146 147void 148BMailSettings::SetShowStatusWindow(uint32 mode) 149{ 150 if (fData.ReplaceInt32("ShowStatusWindow", mode) != B_OK) 151 fData.AddInt32("ShowStatusWindow", mode); 152} 153 154 155bool 156BMailSettings::DaemonAutoStarts() 157{ 158 return fData.FindBool("DaemonAutoStarts"); 159} 160 161 162void 163BMailSettings::SetDaemonAutoStarts(bool startIt) 164{ 165 if (fData.ReplaceBool("DaemonAutoStarts", startIt) != B_OK) 166 fData.AddBool("DaemonAutoStarts", startIt); 167} 168 169 170BRect 171BMailSettings::ConfigWindowFrame() 172{ 173 return fData.FindRect("ConfigWindowFrame"); 174} 175 176 177void 178BMailSettings::SetConfigWindowFrame(BRect frame) 179{ 180 if (fData.ReplaceRect("ConfigWindowFrame", frame) != B_OK) 181 fData.AddRect("ConfigWindowFrame", frame); 182} 183 184 185BRect 186BMailSettings::StatusWindowFrame() 187{ 188 BRect frame; 189 if (fData.FindRect("StatusWindowFrame", &frame) != B_OK) 190 return BRect(100, 100, 200, 120); 191 192 return frame; 193} 194 195 196void 197BMailSettings::SetStatusWindowFrame(BRect frame) 198{ 199 if (fData.ReplaceRect("StatusWindowFrame", frame) != B_OK) 200 fData.AddRect("StatusWindowFrame", frame); 201} 202 203 204int32 205BMailSettings::StatusWindowWorkspaces() 206{ 207 uint32 workspaces; 208 if (fData.FindInt32("StatusWindowWorkSpace", (int32*)&workspaces) != B_OK) 209 return B_ALL_WORKSPACES; 210 211 return workspaces; 212} 213 214 215void 216BMailSettings::SetStatusWindowWorkspaces(int32 workspace) 217{ 218 if (fData.ReplaceInt32("StatusWindowWorkSpace", workspace) != B_OK) 219 fData.AddInt32("StatusWindowWorkSpace", workspace); 220 221 BMessage msg('wsch'); 222 msg.AddInt32("StatusWindowWorkSpace",workspace); 223 BMessenger(B_MAIL_DAEMON_SIGNATURE).SendMessage(&msg); 224} 225 226 227int32 228BMailSettings::StatusWindowLook() 229{ 230 return fData.FindInt32("StatusWindowLook"); 231} 232 233 234void 235BMailSettings::SetStatusWindowLook(int32 look) 236{ 237 if (fData.ReplaceInt32("StatusWindowLook", look) != B_OK) 238 fData.AddInt32("StatusWindowLook", look); 239 240 BMessage msg('lkch'); 241 msg.AddInt32("StatusWindowLook", look); 242 BMessenger(B_MAIL_DAEMON_SIGNATURE).SendMessage(&msg); 243} 244 245 246bigtime_t 247BMailSettings::AutoCheckInterval() 248{ 249 bigtime_t value; 250 if (fData.FindInt64("AutoCheckInterval", &value) != B_OK) { 251 // every 5 min 252 return 5 * 60 * 1000 * 1000; 253 } 254 return value; 255} 256 257 258void 259BMailSettings::SetAutoCheckInterval(bigtime_t interval) 260{ 261 if (fData.ReplaceInt64("AutoCheckInterval", interval) != B_OK) 262 fData.AddInt64("AutoCheckInterval", interval); 263} 264 265 266bool 267BMailSettings::CheckOnlyIfPPPUp() 268{ 269 return fData.FindBool("CheckOnlyIfPPPUp"); 270} 271 272 273void 274BMailSettings::SetCheckOnlyIfPPPUp(bool yes) 275{ 276 if (fData.ReplaceBool("CheckOnlyIfPPPUp", yes)) 277 fData.AddBool("CheckOnlyIfPPPUp", yes); 278} 279 280 281bool 282BMailSettings::SendOnlyIfPPPUp() 283{ 284 return fData.FindBool("SendOnlyIfPPPUp"); 285} 286 287 288void 289BMailSettings::SetSendOnlyIfPPPUp(bool yes) 290{ 291 if (fData.ReplaceBool("SendOnlyIfPPPUp", yes)) 292 fData.AddBool("SendOnlyIfPPPUp", yes); 293} 294 295 296int32 297BMailSettings::DefaultOutboundAccount() 298{ 299 return fData.FindInt32("DefaultOutboundAccount"); 300} 301 302 303void 304BMailSettings::SetDefaultOutboundAccount(int32 to) 305{ 306 if (fData.ReplaceInt32("DefaultOutboundAccount", to) != B_OK) 307 fData.AddInt32("DefaultOutboundAccount", to); 308} 309 310 311// #pragma mark - 312 313 314BMailAccounts::BMailAccounts() 315{ 316 BPath path; 317 status_t status = AccountsPath(path); 318 if (status != B_OK) 319 return; 320 321 BDirectory dir(path.Path()); 322 if (dir.InitCheck() != B_OK) 323 return; 324 325 std::vector<time_t> creationTimeList; 326 BEntry entry; 327 while (dir.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 328 BNode node(&entry); 329 time_t creationTime; 330 if (node.GetCreationTime(&creationTime) != B_OK) 331 continue; 332 333 BMailAccountSettings* account = new BMailAccountSettings(entry); 334 if (account->InitCheck() != B_OK) { 335 delete account; 336 continue; 337 } 338 339 // sort by creation time 340 int insertIndex = -1; 341 for (unsigned int i = 0; i < creationTimeList.size(); i++) { 342 if (creationTimeList[i] > creationTime) { 343 insertIndex = i; 344 break; 345 } 346 } 347 if (insertIndex < 0) { 348 fAccounts.AddItem(account); 349 creationTimeList.push_back(creationTime); 350 } else { 351 fAccounts.AddItem(account, insertIndex); 352 creationTimeList.insert(creationTimeList.begin() + insertIndex, 353 creationTime); 354 } 355 } 356} 357 358 359status_t 360BMailAccounts::AccountsPath(BPath& path) 361{ 362 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 363 if (status != B_OK) 364 return status; 365 return path.Append("Mail/accounts"); 366} 367 368 369BMailAccounts::~BMailAccounts() 370{ 371 for (int i = 0; i < fAccounts.CountItems(); i++) 372 delete fAccounts.ItemAt(i); 373} 374 375 376int32 377BMailAccounts::CountAccounts() 378{ 379 return fAccounts.CountItems(); 380} 381 382 383BMailAccountSettings* 384BMailAccounts::AccountAt(int32 index) 385{ 386 return fAccounts.ItemAt(index); 387} 388 389 390BMailAccountSettings* 391BMailAccounts::AccountByID(int32 id) 392{ 393 for (int i = 0; i < fAccounts.CountItems(); i++) { 394 BMailAccountSettings* account = fAccounts.ItemAt(i); 395 if (account->AccountID() == id) 396 return account; 397 } 398 return NULL; 399} 400 401 402BMailAccountSettings* 403BMailAccounts::AccountByName(const char* name) 404{ 405 for (int i = 0; i < fAccounts.CountItems(); i++) { 406 BMailAccountSettings* account = fAccounts.ItemAt(i); 407 if (strcmp(account->Name(), name) == 0) 408 return account; 409 } 410 return NULL; 411} 412 413 414// #pragma mark - 415 416 417BMailAddOnSettings::BMailAddOnSettings() 418{ 419} 420 421 422BMailAddOnSettings::~BMailAddOnSettings() 423{ 424} 425 426 427status_t 428BMailAddOnSettings::Load(const BMessage& message) 429{ 430 const char* pathString = NULL; 431 if (message.FindString("add-on path", &pathString) != B_OK) 432 return B_BAD_VALUE; 433 434 BPath path(pathString); 435 436 if (!path.IsAbsolute()) { 437 BStringList paths; 438 BPathFinder().FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "mail_daemon", 439 paths); 440 441 status_t status = B_ENTRY_NOT_FOUND; 442 443 for (int32 i = 0; i < paths.CountStrings(); i++) { 444 path.SetTo(paths.StringAt(i), pathString); 445 BEntry entry(path.Path()); 446 if (entry.Exists()) { 447 status = B_OK; 448 break; 449 } 450 } 451 if (status != B_OK) 452 return status; 453 } 454 455 status_t status = get_ref_for_path(path.Path(), &fRef); 456 if (status != B_OK) 457 return status; 458 459 BMessage settings; 460 message.FindMessage("settings", &settings); 461 462 MakeEmpty(); 463 Append(settings); 464 465 fOriginalSettings = *this; 466 fOriginalRef = fRef; 467 return B_OK; 468} 469 470 471status_t 472BMailAddOnSettings::Save(BMessage& message) 473{ 474 BPath path(&fRef); 475 status_t status = message.AddString("add-on path", _RelativizePath(path)); 476 if (status == B_OK) 477 status = message.AddMessage("settings", this); 478 if (status != B_OK) 479 return status; 480 481 fOriginalSettings = *this; 482 fOriginalRef = fRef; 483 return B_OK; 484} 485 486 487void 488BMailAddOnSettings::SetAddOnRef(const entry_ref& ref) 489{ 490 fRef = ref; 491} 492 493 494const entry_ref& 495BMailAddOnSettings::AddOnRef() const 496{ 497 return fRef; 498} 499 500 501bool 502BMailAddOnSettings::HasBeenModified() const 503{ 504 return fRef != fOriginalRef 505 || !fOriginalSettings.HasSameData(*this, true, true); 506} 507 508 509/*! Cuts off the ".../add-ons/mail_daemon" part of the provided \a path 510 in case it exists. Otherwise, the complete path will be returned. 511*/ 512const char* 513BMailAddOnSettings::_RelativizePath(const BPath& path) const 514{ 515 const char* string = path.Path(); 516 const char* parentDirectory = "/mail_daemon/"; 517 const char* at = strstr(string, parentDirectory); 518 if (at == NULL) 519 return string; 520 521 return at + strlen(parentDirectory); 522} 523 524 525// #pragma mark - 526 527 528BMailProtocolSettings::BMailProtocolSettings() 529 : 530 fFiltersSettings(5, true) 531{ 532} 533 534 535BMailProtocolSettings::~BMailProtocolSettings() 536{ 537} 538 539 540status_t 541BMailProtocolSettings::Load(const BMessage& message) 542{ 543 status_t status = BMailAddOnSettings::Load(message); 544 if (status != B_OK) 545 return status; 546 547 type_code typeFound; 548 int32 countFound; 549 message.GetInfo("filters", &typeFound, &countFound); 550 if (typeFound != B_MESSAGE_TYPE) 551 return B_BAD_VALUE; 552 553 for (int i = 0; i < countFound; i++) { 554 int32 index = AddFilterSettings(); 555 if (index < 0) 556 return B_NO_MEMORY; 557 558 BMailAddOnSettings* filterSettings = fFiltersSettings.ItemAt(index); 559 560 BMessage filterMessage; 561 message.FindMessage("filters", i, &filterMessage); 562 if (filterSettings->Load(filterMessage) != B_OK) 563 RemoveFilterSettings(index); 564 } 565 return B_OK; 566} 567 568 569status_t 570BMailProtocolSettings::Save(BMessage& message) 571{ 572 status_t status = BMailAddOnSettings::Save(message); 573 if (status != B_OK) 574 return status; 575 576 for (int i = 0; i < CountFilterSettings(); i++) { 577 BMessage filter; 578 BMailAddOnSettings* filterSettings = fFiltersSettings.ItemAt(i); 579 filterSettings->Save(filter); 580 message.AddMessage("filters", &filter); 581 } 582 return B_OK; 583} 584 585 586int32 587BMailProtocolSettings::CountFilterSettings() const 588{ 589 return fFiltersSettings.CountItems(); 590} 591 592 593int32 594BMailProtocolSettings::AddFilterSettings(const entry_ref* ref) 595{ 596 BMailAddOnSettings* filterSettings = new BMailAddOnSettings(); 597 if (ref != NULL) 598 filterSettings->SetAddOnRef(*ref); 599 600 if (fFiltersSettings.AddItem(filterSettings)) 601 return fFiltersSettings.CountItems() - 1; 602 603 delete filterSettings; 604 return -1; 605} 606 607 608void 609BMailProtocolSettings::RemoveFilterSettings(int32 index) 610{ 611 fFiltersSettings.RemoveItemAt(index); 612} 613 614 615bool 616BMailProtocolSettings::MoveFilterSettings(int32 from, int32 to) 617{ 618 if (from < 0 || from >= (int32)CountFilterSettings() || to < 0 619 || to >= (int32)CountFilterSettings()) 620 return false; 621 if (from == to) 622 return true; 623 624 BMailAddOnSettings* settings = fFiltersSettings.RemoveItemAt(from); 625 fFiltersSettings.AddItem(settings, to); 626 return true; 627} 628 629 630BMailAddOnSettings* 631BMailProtocolSettings::FilterSettingsAt(int32 index) const 632{ 633 return fFiltersSettings.ItemAt(index); 634} 635 636 637bool 638BMailProtocolSettings::HasBeenModified() const 639{ 640 if (BMailAddOnSettings::HasBeenModified()) 641 return true; 642 for (int32 i = 0; i < CountFilterSettings(); i++) { 643 if (FilterSettingsAt(i)->HasBeenModified()) 644 return true; 645 } 646 return false; 647} 648 649 650// #pragma mark - 651 652 653BMailAccountSettings::BMailAccountSettings() 654 : 655 fStatus(B_OK), 656 fInboundEnabled(true), 657 fOutboundEnabled(true), 658 fModified(true) 659{ 660 fAccountID = real_time_clock(); 661} 662 663 664BMailAccountSettings::BMailAccountSettings(BEntry account) 665 : 666 fAccountFile(account), 667 fModified(false) 668{ 669 fStatus = Reload(); 670} 671 672 673BMailAccountSettings::~BMailAccountSettings() 674{ 675 676} 677 678 679void 680BMailAccountSettings::SetAccountID(int32 id) 681{ 682 fModified = true; 683 fAccountID = id; 684} 685 686 687int32 688BMailAccountSettings::AccountID() const 689{ 690 return fAccountID; 691} 692 693 694void 695BMailAccountSettings::SetName(const char* name) 696{ 697 fModified = true; 698 fAccountName = name; 699} 700 701 702const char* 703BMailAccountSettings::Name() const 704{ 705 return fAccountName; 706} 707 708 709void 710BMailAccountSettings::SetRealName(const char* realName) 711{ 712 fModified = true; 713 fRealName = realName; 714} 715 716 717const char* 718BMailAccountSettings::RealName() const 719{ 720 return fRealName; 721} 722 723 724void 725BMailAccountSettings::SetReturnAddress(const char* returnAddress) 726{ 727 fModified = true; 728 fReturnAdress = returnAddress; 729} 730 731 732const char* 733BMailAccountSettings::ReturnAddress() const 734{ 735 return fReturnAdress; 736} 737 738 739bool 740BMailAccountSettings::SetInboundAddOn(const char* name) 741{ 742 entry_ref ref; 743 if (_GetAddOnRef("mail_daemon/inbound_protocols", name, ref) != B_OK) 744 return false; 745 746 fInboundSettings.SetAddOnRef(ref); 747 return true; 748} 749 750 751bool 752BMailAccountSettings::SetOutboundAddOn(const char* name) 753{ 754 entry_ref ref; 755 if (_GetAddOnRef("mail_daemon/outbound_protocols", name, ref) != B_OK) 756 return false; 757 758 fOutboundSettings.SetAddOnRef(ref); 759 return true; 760} 761 762 763const entry_ref& 764BMailAccountSettings::InboundAddOnRef() const 765{ 766 return fInboundSettings.AddOnRef(); 767} 768 769 770const entry_ref& 771BMailAccountSettings::OutboundAddOnRef() const 772{ 773 return fOutboundSettings.AddOnRef(); 774} 775 776 777BMailProtocolSettings& 778BMailAccountSettings::InboundSettings() 779{ 780 return fInboundSettings; 781} 782 783 784const BMailProtocolSettings& 785BMailAccountSettings::InboundSettings() const 786{ 787 return fInboundSettings; 788} 789 790 791BMailProtocolSettings& 792BMailAccountSettings::OutboundSettings() 793{ 794 return fOutboundSettings; 795} 796 797 798const BMailProtocolSettings& 799BMailAccountSettings::OutboundSettings() const 800{ 801 return fOutboundSettings; 802} 803 804 805bool 806BMailAccountSettings::HasInbound() 807{ 808 return BEntry(&fInboundSettings.AddOnRef()).Exists(); 809} 810 811 812bool 813BMailAccountSettings::HasOutbound() 814{ 815 return BEntry(&fOutboundSettings.AddOnRef()).Exists(); 816} 817 818 819void 820BMailAccountSettings::SetInboundEnabled(bool enabled) 821{ 822 fInboundEnabled = enabled; 823 fModified = true; 824} 825 826 827bool 828BMailAccountSettings::IsInboundEnabled() const 829{ 830 return fInboundEnabled; 831} 832 833 834void 835BMailAccountSettings::SetOutboundEnabled(bool enabled) 836{ 837 fOutboundEnabled = enabled; 838 fModified = true; 839} 840 841 842bool 843BMailAccountSettings::IsOutboundEnabled() const 844{ 845 return fOutboundEnabled; 846} 847 848 849status_t 850BMailAccountSettings::Reload() 851{ 852 BFile file(&fAccountFile, B_READ_ONLY); 853 status_t status = file.InitCheck(); 854 if (status != B_OK) 855 return status; 856 BMessage settings; 857 settings.Unflatten(&file); 858 859 int32 id; 860 if (settings.FindInt32("id", &id) == B_OK) 861 fAccountID = id; 862 settings.FindString("name", &fAccountName); 863 settings.FindString("real_name", &fRealName); 864 settings.FindString("return_address", &fReturnAdress); 865 866 BMessage inboundSettings; 867 settings.FindMessage("inbound", &inboundSettings); 868 fInboundSettings.Load(inboundSettings); 869 BMessage outboundSettings; 870 settings.FindMessage("outbound", &outboundSettings); 871 fOutboundSettings.Load(outboundSettings); 872 873 if (settings.FindBool("inbound_enabled", &fInboundEnabled) != B_OK) 874 fInboundEnabled = true; 875 if (settings.FindBool("outbound_enabled", &fOutboundEnabled) != B_OK) 876 fOutboundEnabled = true; 877 878 fModified = false; 879 return B_OK; 880} 881 882 883status_t 884BMailAccountSettings::Save() 885{ 886 fModified = false; 887 888 BMessage settings; 889 settings.AddInt32("id", fAccountID); 890 settings.AddString("name", fAccountName); 891 settings.AddString("real_name", fRealName); 892 settings.AddString("return_address", fReturnAdress); 893 894 BMessage inboundSettings; 895 fInboundSettings.Save(inboundSettings); 896 settings.AddMessage("inbound", &inboundSettings); 897 BMessage outboundSettings; 898 fOutboundSettings.Save(outboundSettings); 899 settings.AddMessage("outbound", &outboundSettings); 900 901 settings.AddBool("inbound_enabled", fInboundEnabled); 902 settings.AddBool("outbound_enabled", fOutboundEnabled); 903 904 status_t status = _CreateAccountFilePath(); 905 if (status != B_OK) 906 return status; 907 908 BFile file(&fAccountFile, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 909 status = file.InitCheck(); 910 if (status != B_OK) 911 return status; 912 return settings.Flatten(&file); 913} 914 915 916status_t 917BMailAccountSettings::Delete() 918{ 919 return fAccountFile.Remove(); 920} 921 922 923bool 924BMailAccountSettings::HasBeenModified() const 925{ 926 return fModified 927 || fInboundSettings.HasBeenModified() 928 || fOutboundSettings.HasBeenModified(); 929} 930 931 932const BEntry& 933BMailAccountSettings::AccountFile() const 934{ 935 return fAccountFile; 936} 937 938 939status_t 940BMailAccountSettings::_CreateAccountFilePath() 941{ 942 BPath path; 943 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 944 if (status != B_OK) 945 return status; 946 path.Append("Mail/accounts"); 947 create_directory(path.Path(), 777); 948 949 if (fAccountFile.InitCheck() == B_OK) 950 return B_OK; 951 952 BString fileName = fAccountName; 953 if (fileName == "") 954 fileName << fAccountID; 955 for (int i = 0; ; i++) { 956 BString testFileName = fileName; 957 if (i != 0) { 958 testFileName += "_"; 959 testFileName << i; 960 } 961 BPath testPath(path); 962 testPath.Append(testFileName); 963 BEntry testEntry(testPath.Path()); 964 if (!testEntry.Exists()) { 965 fileName = testFileName; 966 break; 967 } 968 } 969 970 path.Append(fileName); 971 return fAccountFile.SetTo(path.Path()); 972} 973 974 975status_t 976BMailAccountSettings::_GetAddOnRef(const char* subPath, const char* name, 977 entry_ref& ref) 978{ 979 BStringList paths; 980 BPathFinder().FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, subPath, paths); 981 982 for (int32 i = 0; i < paths.CountStrings(); i++) { 983 BPath path(paths.StringAt(i), name); 984 BEntry entry(path.Path()); 985 if (entry.Exists()) { 986 if (entry.GetRef(&ref) == B_OK) 987 return B_OK; 988 } 989 } 990 return B_ENTRY_NOT_FOUND; 991} 992