1 /* 2 * Copyright 2001-2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Ingo Weinhold, ingo_weinhold@gmx.de 8 */ 9 10 11 #include <Roster.h> 12 13 #include <ctype.h> 14 #include <new> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <strings.h> 18 #include <unistd.h> 19 20 #include <AppFileInfo.h> 21 #include <Application.h> 22 #include <Bitmap.h> 23 #include <Directory.h> 24 #include <File.h> 25 #include <FindDirectory.h> 26 #include <fs_index.h> 27 #include <fs_info.h> 28 #include <image.h> 29 #include <List.h> 30 #include <Mime.h> 31 #include <Node.h> 32 #include <NodeInfo.h> 33 #include <OS.h> 34 #include <Path.h> 35 #include <Query.h> 36 #include <RegistrarDefs.h> 37 #include <String.h> 38 #include <Volume.h> 39 #include <VolumeRoster.h> 40 41 #include <locks.h> 42 43 #include <AppMisc.h> 44 #include <DesktopLink.h> 45 #include <LaunchRoster.h> 46 #include <MessengerPrivate.h> 47 #include <PortLink.h> 48 #include <RosterPrivate.h> 49 #include <ServerProtocol.h> 50 51 52 using namespace std; 53 using namespace BPrivate; 54 55 56 // debugging 57 //#define DBG(x) x 58 #define DBG(x) 59 #ifdef DEBUG_PRINTF 60 # define OUT DEBUG_PRINTF 61 #else 62 # define OUT printf 63 #endif 64 65 66 enum { 67 NOT_IMPLEMENTED = B_ERROR, 68 }; 69 70 71 const BRoster* be_roster; 72 73 74 // #pragma mark - Helper functions 75 76 77 /*! Extracts an app_info from a BMessage. 78 79 The function searchs for a field "app_info" typed B_REG_APP_INFO_TYPE 80 and initializes \a info with the found data. 81 82 \param message The message 83 \param info A pointer to a pre-allocated app_info to be filled in with the 84 info found in the message. 85 86 \return A status code. 87 \retval B_OK Everything went fine. 88 \retval B_BAD_VALUE \c NULL \a message or \a info. 89 */ 90 static status_t 91 find_message_app_info(BMessage* message, app_info* info) 92 { 93 status_t error = (message && info ? B_OK : B_BAD_VALUE); 94 const flat_app_info* flatInfo = NULL; 95 ssize_t size = 0; 96 // find the flat app info in the message 97 if (error == B_OK) { 98 error = message->FindData("app_info", B_REG_APP_INFO_TYPE, 99 (const void**)&flatInfo, &size); 100 } 101 // unflatten the flat info 102 if (error == B_OK) { 103 if (size == sizeof(flat_app_info)) { 104 memcpy(info, &flatInfo->info, sizeof(app_info)); 105 info->ref.name = NULL; 106 if (strlen(flatInfo->ref_name) > 0) 107 info->ref.set_name(flatInfo->ref_name); 108 } else 109 error = B_ERROR; 110 } 111 112 return error; 113 } 114 115 116 /*! Checks whether or not an application can be used. 117 118 Currently it is only checked whether the application is in the trash. 119 120 \param ref An entry_ref referring to the application executable. 121 122 \return A status code, \c B_OK on success oir other error codes specifying 123 why the application cannot be used. 124 \retval B_OK The application can be used. 125 \retval B_ENTRY_NOT_FOUND \a ref doesn't refer to and existing entry. 126 \retval B_IS_A_DIRECTORY \a ref refers to a directory. 127 \retval B_LAUNCH_FAILED_APP_IN_TRASH The application executable is in the 128 trash. 129 */ 130 static status_t 131 can_app_be_used(const entry_ref* ref) 132 { 133 status_t error = (ref ? B_OK : B_BAD_VALUE); 134 // check whether the file exists and is a file. 135 BEntry entry; 136 if (error == B_OK) 137 error = entry.SetTo(ref, true); 138 139 if (error == B_OK && !entry.Exists()) 140 error = B_ENTRY_NOT_FOUND; 141 142 if (error == B_OK && !entry.IsFile()) 143 error = B_IS_A_DIRECTORY; 144 145 // check whether the file is in trash 146 BPath trashPath; 147 BDirectory directory; 148 BVolume volume; 149 if (error == B_OK 150 && volume.SetTo(ref->device) == B_OK 151 && find_directory(B_TRASH_DIRECTORY, &trashPath, false, &volume) 152 == B_OK 153 && directory.SetTo(trashPath.Path()) == B_OK 154 && directory.Contains(&entry)) { 155 error = B_LAUNCH_FAILED_APP_IN_TRASH; 156 } 157 158 return error; 159 } 160 161 162 /*! Compares the supplied version infos. 163 164 \param info1 The first info. 165 \param info2 The second info. 166 167 \return \c -1, if the first info is less than the second one, \c 1, if 168 the first one is greater than the second one, and \c 0, if both 169 are equal. 170 */ 171 static int32 172 compare_version_infos(const version_info& info1, const version_info& info2) 173 { 174 int32 result = 0; 175 if (info1.major < info2.major) 176 result = -1; 177 else if (info1.major > info2.major) 178 result = 1; 179 else if (info1.middle < info2.middle) 180 result = -1; 181 else if (info1.middle > info2.middle) 182 result = 1; 183 else if (info1.minor < info2.minor) 184 result = -1; 185 else if (info1.minor > info2.minor) 186 result = 1; 187 else if (info1.variety < info2.variety) 188 result = -1; 189 else if (info1.variety > info2.variety) 190 result = 1; 191 else if (info1.internal < info2.internal) 192 result = -1; 193 else if (info1.internal > info2.internal) 194 result = 1; 195 196 return result; 197 } 198 199 200 /*! Compares two applications to decide which one should be rather 201 returned as a query result. 202 203 First, it checks if both apps are in the path, and prefers the app that 204 appears earlier. 205 206 If both files have a version info, then those are compared. 207 If one file has a version info, it is said to be greater. If both 208 files have no version info, their modification times are compared. 209 210 \param app1 An entry_ref referring to the first application. 211 \param app2 An entry_ref referring to the second application. 212 \return \c -1, if the first application version is less than the second 213 one, \c 1, if the first one is greater than the second one, and 214 \c 0, if both are equal. 215 */ 216 static int32 217 compare_queried_apps(const entry_ref* app1, const entry_ref* app2) 218 { 219 BPath path1(app1); 220 BPath path2(app2); 221 222 // Check search path 223 224 const char* searchPathes = getenv("PATH"); 225 if (searchPathes != NULL) { 226 char* searchBuffer = strdup(searchPathes); 227 if (searchBuffer != NULL) { 228 char* last; 229 const char* path = strtok_r(searchBuffer, ":", &last); 230 while (path != NULL) { 231 // Check if any app path matches 232 size_t length = strlen(path); 233 bool found1 = !strncmp(path, path1.Path(), length) 234 && path1.Path()[length] == '/'; 235 bool found2 = !strncmp(path, path2.Path(), length) 236 && path2.Path()[length] == '/';; 237 238 if (found1 != found2) { 239 free(searchBuffer); 240 return found1 ? 1 : -1; 241 } 242 243 path = strtok_r(NULL, ":", &last); 244 } 245 246 free(searchBuffer); 247 } 248 } 249 250 // Check system servers folder 251 BPath path; 252 find_directory(B_SYSTEM_SERVERS_DIRECTORY, &path); 253 BString serverPath(path.Path()); 254 serverPath << '/'; 255 size_t length = serverPath.Length(); 256 257 bool inSystem1 = !strncmp(serverPath.String(), path1.Path(), length); 258 bool inSystem2 = !strncmp(serverPath.String(), path2.Path(), length); 259 if (inSystem1 != inSystem2) 260 return inSystem1 ? 1 : -1; 261 262 // Check version info 263 264 BFile file1; 265 file1.SetTo(app1, B_READ_ONLY); 266 BFile file2; 267 file2.SetTo(app2, B_READ_ONLY); 268 269 BAppFileInfo appFileInfo1; 270 appFileInfo1.SetTo(&file1); 271 BAppFileInfo appFileInfo2; 272 appFileInfo2.SetTo(&file2); 273 274 time_t modificationTime1 = 0; 275 time_t modificationTime2 = 0; 276 277 file1.GetModificationTime(&modificationTime1); 278 file2.GetModificationTime(&modificationTime2); 279 280 int32 result = 0; 281 282 version_info versionInfo1; 283 version_info versionInfo2; 284 bool hasVersionInfo1 = (appFileInfo1.GetVersionInfo( 285 &versionInfo1, B_APP_VERSION_KIND) == B_OK); 286 bool hasVersionInfo2 = (appFileInfo2.GetVersionInfo( 287 &versionInfo2, B_APP_VERSION_KIND) == B_OK); 288 289 if (hasVersionInfo1) { 290 if (hasVersionInfo2) 291 result = compare_version_infos(versionInfo1, versionInfo2); 292 else 293 result = 1; 294 } else { 295 if (hasVersionInfo2) 296 result = -1; 297 else if (modificationTime1 < modificationTime2) 298 result = -1; 299 else if (modificationTime1 > modificationTime2) 300 result = 1; 301 } 302 303 return result; 304 } 305 306 307 /*! Finds an app by signature on any mounted volume. 308 309 \param signature The app's signature. 310 \param appRef A pointer to a pre-allocated entry_ref to be filled with 311 a reference to the found application's executable. 312 313 \return A status code. 314 \retval B_OK Everything went fine. 315 \retval B_BAD_VALUE: \c NULL \a signature or \a appRef. 316 \retval B_LAUNCH_FAILED_APP_NOT_FOUND: An application with this signature 317 could not be found. 318 */ 319 static status_t 320 query_for_app(const char* signature, entry_ref* appRef) 321 { 322 if (signature == NULL || appRef == NULL) 323 return B_BAD_VALUE; 324 325 status_t error = B_LAUNCH_FAILED_APP_NOT_FOUND; 326 bool caseInsensitive = false; 327 328 while (true) { 329 // search on all volumes 330 BVolumeRoster volumeRoster; 331 BVolume volume; 332 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 333 if (!volume.KnowsQuery()) 334 continue; 335 336 index_info info; 337 if (fs_stat_index(volume.Device(), "BEOS:APP_SIG", &info) != 0) { 338 // This volume doesn't seem to have the index we're looking for; 339 // querying it might need a long time, and we don't care *that* 340 // much... 341 continue; 342 } 343 344 BQuery query; 345 query.SetVolume(&volume); 346 query.PushAttr("BEOS:APP_SIG"); 347 if (!caseInsensitive) 348 query.PushString(signature); 349 else { 350 // second pass, create a case insensitive query string 351 char string[B_MIME_TYPE_LENGTH * 4]; 352 strlcpy(string, "application/", sizeof(string)); 353 354 int32 length = strlen(string); 355 const char* from = signature + length; 356 char* to = string + length; 357 358 for (; from[0]; from++) { 359 if (isalpha(from[0])) { 360 *to++ = '['; 361 *to++ = tolower(from[0]); 362 *to++ = toupper(from[0]); 363 *to++ = ']'; 364 } else 365 *to++ = from[0]; 366 } 367 368 to[0] = '\0'; 369 query.PushString(string); 370 } 371 query.PushOp(B_EQ); 372 373 query.Fetch(); 374 375 // walk through the query 376 bool appFound = false; 377 status_t foundAppError = B_OK; 378 entry_ref ref; 379 while (query.GetNextRef(&ref) == B_OK) { 380 if ((!appFound || compare_queried_apps(appRef, &ref) < 0) 381 && (foundAppError = can_app_be_used(&ref)) == B_OK) { 382 *appRef = ref; 383 appFound = true; 384 } 385 } 386 if (!appFound) { 387 // If the query didn't return any hits, the error is 388 // B_LAUNCH_FAILED_APP_NOT_FOUND, otherwise we return the 389 // result of the last can_app_be_used(). 390 error = foundAppError != B_OK 391 ? foundAppError : B_LAUNCH_FAILED_APP_NOT_FOUND; 392 } else 393 return B_OK; 394 } 395 396 if (!caseInsensitive) 397 caseInsensitive = true; 398 else 399 break; 400 } 401 402 return error; 403 } 404 405 406 // #pragma mark - app_info 407 408 409 app_info::app_info() 410 : 411 thread(-1), 412 team(-1), 413 port(-1), 414 flags(B_REG_DEFAULT_APP_FLAGS), 415 ref() 416 { 417 signature[0] = '\0'; 418 } 419 420 421 app_info::~app_info() 422 { 423 } 424 425 426 // #pragma mark - BRoster::ArgVector 427 428 429 class BRoster::ArgVector { 430 public: 431 ArgVector(); 432 ~ArgVector(); 433 434 status_t Init(int argc, const char* const* args, 435 const entry_ref* appRef, 436 const entry_ref* docRef); 437 void Unset(); 438 inline int Count() const { return fArgc; } 439 inline const char* const* Args() const { return fArgs; } 440 441 private: 442 int fArgc; 443 const char** fArgs; 444 BPath fAppPath; 445 BPath fDocPath; 446 }; 447 448 449 //! Creates an uninitialized ArgVector. 450 BRoster::ArgVector::ArgVector() 451 : 452 fArgc(0), 453 fArgs(NULL), 454 fAppPath(), 455 fDocPath() 456 { 457 } 458 459 460 //! Frees all resources associated with the ArgVector. 461 BRoster::ArgVector::~ArgVector() 462 { 463 Unset(); 464 } 465 466 467 /*! Initilizes the object according to the supplied parameters. 468 469 If the initialization succeeds, the methods Count() and Args() grant 470 access to the argument count and vector created by this methods. 471 \note The returned vector is valid only as long as the elements of the 472 supplied \a args (if any) are valid and this object is not destroyed. 473 This object retains ownership of the vector returned by Args(). 474 In case of error, the value returned by Args() is invalid (or \c NULL). 475 476 The argument vector is created as follows: First element is the path 477 of the entry \a appRef refers to, then follow all elements of \a args 478 and then, if \a args has at least one element and \a docRef can be 479 resolved to a path, the path of the entry \a docRef refers to. That is, 480 if no or an empty \a args vector is supplied, the resulting argument 481 vector contains only one element, the path associated with \a appRef. 482 483 \param argc Specifies the number of elements \a args contains. 484 \param args Argument vector. May be \c NULL. 485 \param appRef entry_ref referring to the entry whose path shall be the 486 first element of the resulting argument vector. 487 \param docRef entry_ref referring to the entry whose path shall be the 488 last element of the resulting argument vector. May be \c NULL. 489 \return 490 - \c B_OK: Everything went fine. 491 - \c B_BAD_VALUE: \c NULL \a appRef. 492 - \c B_ENTRY_NOT_FOUND or other file system error codes: \a appRef could 493 not be resolved to a path. 494 - \c B_NO_MEMORY: Not enough memory to allocate for this operation. 495 */ 496 status_t 497 BRoster::ArgVector::Init(int argc, const char* const* args, 498 const entry_ref* appRef, const entry_ref* docRef) 499 { 500 // unset old values 501 Unset(); 502 status_t error = appRef ? B_OK : B_BAD_VALUE; 503 // get app path 504 if (error == B_OK) 505 error = fAppPath.SetTo(appRef); 506 // determine number of arguments 507 bool hasDocArg = false; 508 if (error == B_OK) { 509 fArgc = 1; 510 if (argc > 0 && args) { 511 fArgc += argc; 512 if (docRef != NULL && fDocPath.SetTo(docRef) == B_OK) { 513 fArgc++; 514 hasDocArg = true; 515 } 516 } 517 fArgs = new(nothrow) const char*[fArgc + 1]; 518 // + 1 for the terminating NULL 519 if (!fArgs) 520 error = B_NO_MEMORY; 521 } 522 // init vector 523 if (error == B_OK) { 524 fArgs[0] = fAppPath.Path(); 525 if (argc > 0 && args != NULL) { 526 for (int i = 0; i < argc; i++) 527 fArgs[i + 1] = args[i]; 528 if (hasDocArg) 529 fArgs[fArgc - 1] = fDocPath.Path(); 530 } 531 // NULL terminate (e.g. required by load_image()) 532 fArgs[fArgc] = NULL; 533 } 534 return error; 535 } 536 537 538 //! Uninitializes the object. 539 void 540 BRoster::ArgVector::Unset() 541 { 542 fArgc = 0; 543 delete[] fArgs; 544 fArgs = NULL; 545 fAppPath.Unset(); 546 fDocPath.Unset(); 547 } 548 549 550 // #pragma mark - BRoster 551 552 553 BRoster::BRoster() 554 : 555 fMessenger(), 556 fMimeMessenger(), 557 fMimeMessengerInitOnce(INIT_ONCE_UNINITIALIZED), 558 fNoRegistrar(false) 559 { 560 _InitMessenger(); 561 } 562 563 564 BRoster::~BRoster() 565 { 566 } 567 568 569 // #pragma mark - Querying for apps 570 571 572 bool 573 BRoster::IsRunning(const char* signature) const 574 { 575 return (TeamFor(signature) >= 0); 576 } 577 578 579 bool 580 BRoster::IsRunning(entry_ref* ref) const 581 { 582 return (TeamFor(ref) >= 0); 583 } 584 585 586 team_id 587 BRoster::TeamFor(const char* signature) const 588 { 589 team_id team; 590 app_info info; 591 status_t error = GetAppInfo(signature, &info); 592 if (error == B_OK) 593 team = info.team; 594 else 595 team = error; 596 597 return team; 598 } 599 600 601 team_id 602 BRoster::TeamFor(entry_ref* ref) const 603 { 604 team_id team; 605 app_info info; 606 status_t error = GetAppInfo(ref, &info); 607 if (error == B_OK) 608 team = info.team; 609 else 610 team = error; 611 return team; 612 } 613 614 615 void 616 BRoster::GetAppList(BList* teamIDList) const 617 { 618 status_t error = (teamIDList ? B_OK : B_BAD_VALUE); 619 // compose the request message 620 BMessage request(B_REG_GET_APP_LIST); 621 622 // send the request 623 BMessage reply; 624 if (error == B_OK) 625 error = fMessenger.SendMessage(&request, &reply); 626 627 // evaluate the reply 628 if (error == B_OK) { 629 if (reply.what == B_REG_SUCCESS) { 630 team_id team; 631 for (int32 i = 0; reply.FindInt32("teams", i, &team) == B_OK; i++) 632 teamIDList->AddItem((void*)(addr_t)team); 633 } else { 634 if (reply.FindInt32("error", &error) != B_OK) 635 error = B_ERROR; 636 DBG(OUT("Roster request unsuccessful: %s\n", strerror(error))); 637 DBG(reply.PrintToStream()); 638 } 639 } else { 640 DBG(OUT("Sending message to roster failed: %s\n", strerror(error))); 641 } 642 } 643 644 645 void 646 BRoster::GetAppList(const char* signature, BList* teamIDList) const 647 { 648 status_t error = B_OK; 649 if (signature == NULL || teamIDList == NULL) 650 error = B_BAD_VALUE; 651 652 // compose the request message 653 BMessage request(B_REG_GET_APP_LIST); 654 if (error == B_OK) 655 error = request.AddString("signature", signature); 656 657 // send the request 658 BMessage reply; 659 if (error == B_OK) 660 error = fMessenger.SendMessage(&request, &reply); 661 662 // evaluate the reply 663 if (error == B_OK) { 664 if (reply.what == B_REG_SUCCESS) { 665 team_id team; 666 for (int32 i = 0; reply.FindInt32("teams", i, &team) == B_OK; i++) 667 teamIDList->AddItem((void*)(addr_t)team); 668 } else if (reply.FindInt32("error", &error) != B_OK) 669 error = B_ERROR; 670 } 671 } 672 673 674 status_t 675 BRoster::GetAppInfo(const char* signature, app_info* info) const 676 { 677 status_t error = B_OK; 678 if (signature == NULL || info == NULL) 679 error = B_BAD_VALUE; 680 681 // compose the request message 682 BMessage request(B_REG_GET_APP_INFO); 683 if (error == B_OK) 684 error = request.AddString("signature", signature); 685 686 // send the request 687 BMessage reply; 688 if (error == B_OK) 689 error = fMessenger.SendMessage(&request, &reply); 690 691 // evaluate the reply 692 if (error == B_OK) { 693 if (reply.what == B_REG_SUCCESS) 694 error = find_message_app_info(&reply, info); 695 else if (reply.FindInt32("error", &error) != B_OK) 696 error = B_ERROR; 697 } 698 699 return error; 700 } 701 702 703 status_t 704 BRoster::GetAppInfo(entry_ref* ref, app_info* info) const 705 { 706 status_t error = (ref && info ? B_OK : B_BAD_VALUE); 707 // compose the request message 708 BMessage request(B_REG_GET_APP_INFO); 709 if (error == B_OK) 710 error = request.AddRef("ref", ref); 711 712 // send the request 713 BMessage reply; 714 if (error == B_OK) 715 error = fMessenger.SendMessage(&request, &reply); 716 717 // evaluate the reply 718 if (error == B_OK) { 719 if (reply.what == B_REG_SUCCESS) 720 error = find_message_app_info(&reply, info); 721 else if (reply.FindInt32("error", &error) != B_OK) 722 error = B_ERROR; 723 } 724 return error; 725 } 726 727 728 status_t 729 BRoster::GetRunningAppInfo(team_id team, app_info* info) const 730 { 731 status_t error = (info ? B_OK : B_BAD_VALUE); 732 if (error == B_OK && team < 0) 733 error = B_BAD_TEAM_ID; 734 // compose the request message 735 BMessage request(B_REG_GET_APP_INFO); 736 if (error == B_OK) 737 error = request.AddInt32("team", team); 738 // send the request 739 BMessage reply; 740 if (error == B_OK) 741 error = fMessenger.SendMessage(&request, &reply); 742 743 // evaluate the reply 744 if (error == B_OK) { 745 if (reply.what == B_REG_SUCCESS) 746 error = find_message_app_info(&reply, info); 747 else if (reply.FindInt32("error", &error) != B_OK) 748 error = B_ERROR; 749 } 750 return error; 751 } 752 753 754 status_t 755 BRoster::GetActiveAppInfo(app_info* info) const 756 { 757 if (info == NULL) 758 return B_BAD_VALUE; 759 760 // compose the request message 761 BMessage request(B_REG_GET_APP_INFO); 762 // send the request 763 BMessage reply; 764 status_t error = fMessenger.SendMessage(&request, &reply); 765 // evaluate the reply 766 if (error == B_OK) { 767 if (reply.what == B_REG_SUCCESS) 768 error = find_message_app_info(&reply, info); 769 else if (reply.FindInt32("error", &error) != B_OK) 770 error = B_ERROR; 771 } 772 return error; 773 } 774 775 776 status_t 777 BRoster::FindApp(const char* mimeType, entry_ref* app) const 778 { 779 if (mimeType == NULL || app == NULL) 780 return B_BAD_VALUE; 781 782 return _ResolveApp(mimeType, NULL, app, NULL, NULL, NULL); 783 } 784 785 786 status_t 787 BRoster::FindApp(entry_ref* ref, entry_ref* app) const 788 { 789 if (ref == NULL || app == NULL) 790 return B_BAD_VALUE; 791 792 entry_ref _ref(*ref); 793 return _ResolveApp(NULL, &_ref, app, NULL, NULL, NULL); 794 } 795 796 797 // #pragma mark - Launching, activating, and broadcasting to apps 798 799 800 status_t 801 BRoster::Broadcast(BMessage* message) const 802 { 803 return Broadcast(message, be_app_messenger); 804 } 805 806 807 status_t 808 BRoster::Broadcast(BMessage* message, BMessenger replyTo) const 809 { 810 status_t error = (message ? B_OK : B_BAD_VALUE); 811 // compose the request message 812 BMessage request(B_REG_BROADCAST); 813 if (error == B_OK) 814 error = request.AddInt32("team", BPrivate::current_team()); 815 if (error == B_OK) 816 error = request.AddMessage("message", message); 817 if (error == B_OK) 818 error = request.AddMessenger("reply_target", replyTo); 819 820 // send the request 821 BMessage reply; 822 if (error == B_OK) 823 error = fMessenger.SendMessage(&request, &reply); 824 825 // evaluate the reply 826 if (error == B_OK && reply.what != B_REG_SUCCESS 827 && reply.FindInt32("error", &error) != B_OK) 828 error = B_ERROR; 829 830 return error; 831 } 832 833 834 status_t 835 BRoster::StartWatching(BMessenger target, uint32 eventMask) const 836 { 837 status_t error = B_OK; 838 // compose the request message 839 BMessage request(B_REG_START_WATCHING); 840 if (error == B_OK) 841 error = request.AddMessenger("target", target); 842 if (error == B_OK) 843 error = request.AddInt32("events", (int32)eventMask); 844 845 // send the request 846 BMessage reply; 847 if (error == B_OK) 848 error = fMessenger.SendMessage(&request, &reply); 849 850 // evaluate the reply 851 if (error == B_OK && reply.what != B_REG_SUCCESS 852 && reply.FindInt32("error", &error) != B_OK) 853 error = B_ERROR; 854 855 return error; 856 } 857 858 859 status_t 860 BRoster::StopWatching(BMessenger target) const 861 { 862 status_t error = B_OK; 863 // compose the request message 864 BMessage request(B_REG_STOP_WATCHING); 865 if (error == B_OK) 866 error = request.AddMessenger("target", target); 867 868 // send the request 869 BMessage reply; 870 if (error == B_OK) 871 error = fMessenger.SendMessage(&request, &reply); 872 873 // evaluate the reply 874 if (error == B_OK && reply.what != B_REG_SUCCESS 875 && reply.FindInt32("error", &error) != B_OK) 876 error = B_ERROR; 877 878 return error; 879 } 880 881 882 status_t 883 BRoster::ActivateApp(team_id team) const 884 { 885 BPrivate::DesktopLink link; 886 887 status_t status = link.InitCheck(); 888 if (status < B_OK) 889 return status; 890 891 // prepare the message 892 status_t error = link.StartMessage(AS_ACTIVATE_APP); 893 if (error != B_OK) 894 return error; 895 896 error = link.Attach(link.ReceiverPort()); 897 if (error != B_OK) 898 return error; 899 900 error = link.Attach(team); 901 if (error != B_OK) 902 return error; 903 904 // send it 905 status_t code; 906 error = link.FlushWithReply(code); 907 if (error != B_OK) 908 return error; 909 910 return code; 911 } 912 913 914 status_t 915 BRoster::Launch(const char* mimeType, BMessage* initialMessage, 916 team_id* _appTeam) const 917 { 918 if (mimeType == NULL) 919 return B_BAD_VALUE; 920 921 BList messageList; 922 if (initialMessage != NULL) 923 messageList.AddItem(initialMessage); 924 925 return _LaunchApp(mimeType, NULL, &messageList, 0, NULL, 926 (const char**)environ, _appTeam); 927 } 928 929 930 status_t 931 BRoster::Launch(const char* mimeType, BList* messageList, 932 team_id* _appTeam) const 933 { 934 if (mimeType == NULL) 935 return B_BAD_VALUE; 936 937 return _LaunchApp(mimeType, NULL, messageList, 0, NULL, 938 (const char**)environ, _appTeam); 939 } 940 941 942 status_t 943 BRoster::Launch(const char* mimeType, int argc, const char* const* args, 944 team_id* _appTeam) const 945 { 946 if (mimeType == NULL) 947 return B_BAD_VALUE; 948 949 return _LaunchApp(mimeType, NULL, NULL, argc, args, (const char**)environ, 950 _appTeam); 951 } 952 953 954 status_t 955 BRoster::Launch(const entry_ref* ref, const BMessage* initialMessage, 956 team_id* _appTeam) const 957 { 958 if (ref == NULL) 959 return B_BAD_VALUE; 960 961 BList messageList; 962 if (initialMessage != NULL) 963 messageList.AddItem(const_cast<BMessage*>(initialMessage)); 964 965 return _LaunchApp(NULL, ref, &messageList, 0, NULL, (const char**)environ, 966 _appTeam); 967 } 968 969 970 status_t 971 BRoster::Launch(const entry_ref* ref, const BList* messageList, 972 team_id* appTeam) const 973 { 974 if (ref == NULL) 975 return B_BAD_VALUE; 976 977 return _LaunchApp(NULL, ref, messageList, 0, NULL, (const char**)environ, 978 appTeam); 979 } 980 981 982 status_t 983 BRoster::Launch(const entry_ref* ref, int argc, const char* const* args, 984 team_id* appTeam) const 985 { 986 if (ref == NULL) 987 return B_BAD_VALUE; 988 989 return _LaunchApp(NULL, ref, NULL, argc, args, (const char**)environ, 990 appTeam); 991 } 992 993 994 #if __GNUC__ == 2 995 // #pragma mark - Binary compatibility 996 997 998 extern "C" status_t 999 Launch__C7BRosterP9entry_refP8BMessagePl(BRoster* roster, entry_ref* ref, 1000 BMessage* initialMessage) 1001 { 1002 return roster->BRoster::Launch(ref, initialMessage, NULL); 1003 } 1004 1005 1006 extern "C" status_t 1007 Launch__C7BRosterPCciPPcPl(BRoster* roster, const char* mimeType, 1008 int argc, char** args, team_id* _appTeam) 1009 { 1010 return roster->BRoster::Launch(mimeType, argc, args, _appTeam); 1011 } 1012 1013 1014 extern "C" status_t 1015 Launch__C7BRosterP9entry_refiPPcPl(BRoster* roster, entry_ref* ref, 1016 int argc, char* const* args, team_id* _appTeam) 1017 { 1018 return roster->BRoster::Launch(ref, argc, args, _appTeam); 1019 } 1020 #endif // __GNUC__ == 2 1021 1022 1023 // #pragma mark - Recent document and app support 1024 1025 1026 void 1027 BRoster::GetRecentDocuments(BMessage* refList, int32 maxCount, 1028 const char* fileType, const char* signature) const 1029 { 1030 if (refList == NULL) 1031 return; 1032 1033 status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE; 1034 1035 // Use the message we've been given for both request and reply 1036 BMessage& message = *refList; 1037 BMessage& reply = *refList; 1038 status_t result; 1039 1040 // Build and send the message, read the reply 1041 if (error == B_OK) { 1042 message.what = B_REG_GET_RECENT_DOCUMENTS; 1043 error = message.AddInt32("max count", maxCount); 1044 } 1045 if (error == B_OK && fileType) 1046 error = message.AddString("file type", fileType); 1047 1048 if (error == B_OK && signature) 1049 error = message.AddString("app sig", signature); 1050 1051 fMessenger.SendMessage(&message, &reply); 1052 if (error == B_OK) { 1053 error = reply.what == B_REG_RESULT 1054 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1055 } 1056 1057 if (error == B_OK) 1058 error = reply.FindInt32("result", &result); 1059 1060 if (error == B_OK) 1061 error = result; 1062 1063 // Clear the result if an error occured 1064 if (error != B_OK && refList != NULL) 1065 refList->MakeEmpty(); 1066 1067 // No return value, how sad :-( 1068 //return error; 1069 } 1070 1071 1072 void 1073 BRoster::GetRecentDocuments(BMessage* refList, int32 maxCount, 1074 const char* fileTypes[], int32 fileTypesCount, 1075 const char* signature) const 1076 { 1077 if (refList == NULL) 1078 return; 1079 1080 status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE; 1081 1082 // Use the message we've been given for both request and reply 1083 BMessage& message = *refList; 1084 BMessage& reply = *refList; 1085 status_t result; 1086 1087 // Build and send the message, read the reply 1088 if (error == B_OK) { 1089 message.what = B_REG_GET_RECENT_DOCUMENTS; 1090 error = message.AddInt32("max count", maxCount); 1091 } 1092 if (error == B_OK && fileTypes) { 1093 for (int i = 0; i < fileTypesCount && error == B_OK; i++) 1094 error = message.AddString("file type", fileTypes[i]); 1095 } 1096 if (error == B_OK && signature) 1097 error = message.AddString("app sig", signature); 1098 1099 fMessenger.SendMessage(&message, &reply); 1100 if (error == B_OK) { 1101 error = reply.what == B_REG_RESULT 1102 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1103 } 1104 if (error == B_OK) 1105 error = reply.FindInt32("result", &result); 1106 1107 if (error == B_OK) 1108 error = result; 1109 1110 // Clear the result if an error occured 1111 if (error != B_OK && refList != NULL) 1112 refList->MakeEmpty(); 1113 1114 // No return value, how sad :-( 1115 //return error; 1116 } 1117 1118 1119 void 1120 BRoster::GetRecentFolders(BMessage* refList, int32 maxCount, 1121 const char* signature) const 1122 { 1123 if (refList == NULL) 1124 return; 1125 1126 status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE; 1127 1128 // Use the message we've been given for both request and reply 1129 BMessage& message = *refList; 1130 BMessage& reply = *refList; 1131 status_t result; 1132 1133 // Build and send the message, read the reply 1134 if (error == B_OK) { 1135 message.what = B_REG_GET_RECENT_FOLDERS; 1136 error = message.AddInt32("max count", maxCount); 1137 } 1138 if (error == B_OK && signature) 1139 error = message.AddString("app sig", signature); 1140 1141 fMessenger.SendMessage(&message, &reply); 1142 if (error == B_OK) { 1143 error = reply.what == B_REG_RESULT 1144 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1145 } 1146 1147 if (error == B_OK) 1148 error = reply.FindInt32("result", &result); 1149 1150 if (error == B_OK) 1151 error = result; 1152 1153 // Clear the result if an error occured 1154 if (error != B_OK && refList != NULL) 1155 refList->MakeEmpty(); 1156 1157 // No return value, how sad :-( 1158 //return error; 1159 } 1160 1161 1162 void 1163 BRoster::GetRecentApps(BMessage* refList, int32 maxCount) const 1164 { 1165 if (refList == NULL) 1166 return; 1167 1168 status_t err = maxCount > 0 ? B_OK : B_BAD_VALUE; 1169 1170 // Use the message we've been given for both request and reply 1171 BMessage& message = *refList; 1172 BMessage& reply = *refList; 1173 status_t result; 1174 1175 // Build and send the message, read the reply 1176 if (!err) { 1177 message.what = B_REG_GET_RECENT_APPS; 1178 err = message.AddInt32("max count", maxCount); 1179 } 1180 fMessenger.SendMessage(&message, &reply); 1181 if (!err) { 1182 err = reply.what == B_REG_RESULT 1183 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1184 } 1185 if (!err) 1186 err = reply.FindInt32("result", &result); 1187 1188 if (!err) 1189 err = result; 1190 1191 // Clear the result if an error occured 1192 if (err && refList) 1193 refList->MakeEmpty(); 1194 1195 // No return value, how sad :-( 1196 //return err; 1197 } 1198 1199 1200 void 1201 BRoster::AddToRecentDocuments(const entry_ref* document, 1202 const char* signature) const 1203 { 1204 status_t error = document ? B_OK : B_BAD_VALUE; 1205 1206 // Use the message we've been given for both request and reply 1207 BMessage message(B_REG_ADD_TO_RECENT_DOCUMENTS); 1208 BMessage reply; 1209 status_t result; 1210 char* callingApplicationSignature = NULL; 1211 1212 // If no signature is supplied, look up the signature of 1213 // the calling app 1214 if (error == B_OK && signature == NULL) { 1215 app_info info; 1216 error = GetRunningAppInfo(be_app->Team(), &info); 1217 if (error == B_OK) 1218 callingApplicationSignature = info.signature; 1219 } 1220 1221 // Build and send the message, read the reply 1222 if (error == B_OK) 1223 error = message.AddRef("ref", document); 1224 1225 if (error == B_OK) { 1226 error = message.AddString("app sig", signature != NULL 1227 ? signature : callingApplicationSignature); 1228 } 1229 fMessenger.SendMessage(&message, &reply); 1230 if (error == B_OK) { 1231 error = reply.what == B_REG_RESULT 1232 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1233 } 1234 if (error == B_OK) 1235 error = reply.FindInt32("result", &result); 1236 1237 if (error == B_OK) 1238 error = result; 1239 1240 if (error != B_OK) { 1241 DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error " 1242 "0x%lx\n", error)); 1243 } 1244 } 1245 1246 1247 void 1248 BRoster::AddToRecentFolders(const entry_ref* folder, 1249 const char* signature) const 1250 { 1251 status_t error = folder ? B_OK : B_BAD_VALUE; 1252 1253 // Use the message we've been given for both request and reply 1254 BMessage message(B_REG_ADD_TO_RECENT_FOLDERS); 1255 BMessage reply; 1256 status_t result; 1257 char* callingApplicationSignature = NULL; 1258 1259 // If no signature is supplied, look up the signature of 1260 // the calling app 1261 if (error == B_OK && signature == NULL) { 1262 app_info info; 1263 error = GetRunningAppInfo(be_app->Team(), &info); 1264 if (error == B_OK) 1265 callingApplicationSignature = info.signature; 1266 } 1267 1268 // Build and send the message, read the reply 1269 if (error == B_OK) 1270 error = message.AddRef("ref", folder); 1271 1272 if (error == B_OK) { 1273 error = message.AddString("app sig", 1274 signature != NULL ? signature : callingApplicationSignature); 1275 } 1276 fMessenger.SendMessage(&message, &reply); 1277 if (error == B_OK) { 1278 error = reply.what == B_REG_RESULT 1279 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 1280 } 1281 if (error == B_OK) 1282 error = reply.FindInt32("result", &result); 1283 1284 if (error == B_OK) 1285 error = result; 1286 1287 if (error != B_OK) { 1288 DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error " 1289 "0x%lx\n", error)); 1290 } 1291 } 1292 1293 // #pragma mark - Private or reserved 1294 1295 1296 /*! Shuts down the system. 1297 1298 When \c synchronous is \c true and the method succeeds, it doesn't return. 1299 1300 \param reboot If \c true, the system will be rebooted instead of being 1301 powered off. 1302 \param confirm If \c true, the user will be asked to confirm to shut down 1303 the system. 1304 \param synchronous If \c false, the method will return as soon as the 1305 shutdown process has been initiated successfully (or an error 1306 occurred). Otherwise the method doesn't return, if successfully. 1307 1308 \return A status code, \c B_OK on success or another error code in case 1309 something went wrong. 1310 \retval B_SHUTTING_DOWN, when there's already a shutdown process in 1311 progress, 1312 \retval B_SHUTDOWN_CANCELLED, when the user cancelled the shutdown process, 1313 */ 1314 status_t 1315 BRoster::_ShutDown(bool reboot, bool confirm, bool synchronous) 1316 { 1317 status_t error = B_OK; 1318 1319 // compose the request message 1320 BMessage request(B_REG_SHUT_DOWN); 1321 if (error == B_OK) 1322 error = request.AddBool("reboot", reboot); 1323 1324 if (error == B_OK) 1325 error = request.AddBool("confirm", confirm); 1326 1327 if (error == B_OK) 1328 error = request.AddBool("synchronous", synchronous); 1329 1330 // send the request 1331 BMessage reply; 1332 if (error == B_OK) 1333 error = fMessenger.SendMessage(&request, &reply); 1334 1335 // evaluate the reply 1336 if (error == B_OK && reply.what != B_REG_SUCCESS 1337 && reply.FindInt32("error", &error) != B_OK) { 1338 error = B_ERROR; 1339 } 1340 1341 return error; 1342 } 1343 1344 1345 /*! (Pre-)Registers an application with the registrar. 1346 1347 This methods is invoked either to register or to pre-register an 1348 application. Full registration is requested by supplying \c true via 1349 \a fullRegistration. 1350 1351 A full registration requires \a signature, \a ref, \a flags, \a team, 1352 \a thread and \a port to contain valid values. No token will be return 1353 via \a pToken. 1354 1355 For a pre-registration \a signature, \a ref, \a flags must be valid. 1356 \a team and \a thread are optional and should be set to -1, if they are 1357 unknown. If no team ID is supplied, \a pToken should be valid and, if the 1358 the pre-registration succeeds, will be filled with a unique token assigned 1359 by the roster. 1360 1361 In both cases the registration may fail, if single/exclusive launch is 1362 requested and an instance of the application is already running. Then 1363 \c B_ALREADY_RUNNING is returned and the team ID of the running instance 1364 is passed back via \a otherTeam, if supplied. 1365 1366 \param signature The application's signature 1367 \param ref An entry_ref referring to the app's executable 1368 \param flags The application's flags 1369 \param team The application's team ID 1370 \param thread The application's main thread 1371 \param port The application's looper port 1372 \param fullRegistration \c true for full, \c false for pre-registration 1373 \param pToken A pointer to a pre-allocated uint32 into which the token 1374 assigned by the registrar is written (may be \c NULL) 1375 \param otherTeam A pointer to a pre-allocated team_id into which the 1376 team ID of the already running instance of a single/exclusive 1377 launch application is written (may be \c NULL) 1378 1379 \return A status code 1380 \retval B_OK Everything went fine. 1381 \retval B_ENTRY_NOT_FOUND \a ref didn't refer to a file. 1382 \retval B_ALREADY_RUNNING The application requested a single/exclusive 1383 launch and an instance was already running. 1384 \retval B_REG_ALREADY_REGISTERED An application with the team ID \a team 1385 was already registered. 1386 */ 1387 status_t 1388 BRoster::_AddApplication(const char* signature, const entry_ref* ref, 1389 uint32 flags, team_id team, thread_id thread, port_id port, 1390 bool fullRegistration, uint32* pToken, team_id* otherTeam) const 1391 { 1392 status_t error = B_OK; 1393 1394 // compose the request message 1395 BMessage request(B_REG_ADD_APP); 1396 if (error == B_OK && signature != NULL) 1397 error = request.AddString("signature", signature); 1398 1399 if (error == B_OK && ref != NULL) 1400 error = request.AddRef("ref", ref); 1401 1402 if (error == B_OK) 1403 error = request.AddInt32("flags", (int32)flags); 1404 1405 if (error == B_OK && team >= 0) 1406 error = request.AddInt32("team", team); 1407 1408 if (error == B_OK && thread >= 0) 1409 error = request.AddInt32("thread", thread); 1410 1411 if (error == B_OK && port >= 0) 1412 error = request.AddInt32("port", port); 1413 1414 if (error == B_OK) 1415 error = request.AddBool("full_registration", fullRegistration); 1416 1417 // send the request 1418 BMessage reply; 1419 if (error == B_OK) 1420 error = fMessenger.SendMessage(&request, &reply); 1421 1422 // evaluate the reply 1423 if (error == B_OK) { 1424 if (reply.what == B_REG_SUCCESS) { 1425 if (!fullRegistration && team < 0) { 1426 uint32 token; 1427 if (reply.FindInt32("token", (int32*)&token) == B_OK) { 1428 if (pToken != NULL) 1429 *pToken = token; 1430 } else 1431 error = B_ERROR; 1432 } 1433 } else { 1434 if (reply.FindInt32("error", &error) != B_OK) 1435 error = B_ERROR; 1436 1437 // get team and token from the reply 1438 if (otherTeam != NULL 1439 && reply.FindInt32("other_team", otherTeam) != B_OK) { 1440 *otherTeam = -1; 1441 } 1442 if (pToken != NULL 1443 && reply.FindInt32("token", (int32*)pToken) != B_OK) { 1444 *pToken = 0; 1445 } 1446 } 1447 } 1448 1449 return error; 1450 } 1451 1452 1453 /*! Sets an application's signature. 1454 1455 The application must be registered or at pre-registered with a valid 1456 team ID. 1457 1458 \param team The app's team ID. 1459 \param signature The app's new signature. 1460 1461 \return A status code. 1462 \retval B_OK Everything went fine. 1463 \retval B_REG_APP_NOT_REGISTERED The supplied team ID did not identify a 1464 registered application. 1465 */ 1466 status_t 1467 BRoster::_SetSignature(team_id team, const char* signature) const 1468 { 1469 status_t error = B_OK; 1470 1471 // compose the request message 1472 BMessage request(B_REG_SET_SIGNATURE); 1473 if (error == B_OK && team >= 0) 1474 error = request.AddInt32("team", team); 1475 1476 if (error == B_OK && signature) 1477 error = request.AddString("signature", signature); 1478 1479 // send the request 1480 BMessage reply; 1481 if (error == B_OK) 1482 error = fMessenger.SendMessage(&request, &reply); 1483 1484 // evaluate the reply 1485 if (error == B_OK && reply.what != B_REG_SUCCESS 1486 && reply.FindInt32("error", &error) != B_OK) { 1487 error = B_ERROR; 1488 } 1489 1490 return error; 1491 } 1492 1493 1494 //! \todo Really needed? 1495 void 1496 BRoster::_SetThread(team_id team, thread_id thread) const 1497 { 1498 } 1499 1500 1501 /*! Sets the team and thread IDs of a pre-registered application. 1502 1503 After an application has been pre-registered via AddApplication(), without 1504 supplying a team ID, the team and thread IDs have to be set using this 1505 method. 1506 1507 \param entryToken The token identifying the application (returned by 1508 AddApplication()) 1509 \param thread The app's thread ID 1510 \param team The app's team ID 1511 1512 \return A status code. 1513 \retval B_OK Everything went fine. 1514 \retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify a 1515 pre-registered application. 1516 */ 1517 status_t 1518 BRoster::_SetThreadAndTeam(uint32 entryToken, thread_id thread, 1519 team_id team) const 1520 { 1521 status_t error = B_OK; 1522 1523 // compose the request message 1524 BMessage request(B_REG_SET_THREAD_AND_TEAM); 1525 if (error == B_OK) 1526 error = request.AddInt32("token", (int32)entryToken); 1527 1528 if (error == B_OK && team >= 0) 1529 error = request.AddInt32("team", team); 1530 1531 if (error == B_OK && thread >= 0) 1532 error = request.AddInt32("thread", thread); 1533 1534 // send the request 1535 BMessage reply; 1536 if (error == B_OK) 1537 error = fMessenger.SendMessage(&request, &reply); 1538 1539 // evaluate the reply 1540 if (error == B_OK && reply.what != B_REG_SUCCESS 1541 && reply.FindInt32("error", &error) != B_OK) 1542 error = B_ERROR; 1543 1544 return error; 1545 } 1546 1547 1548 /*! Completes the registration process for a pre-registered application. 1549 1550 After an application has been pre-registered via AddApplication() and 1551 after assigning it a team ID (via SetThreadAndTeam()) the application is 1552 still pre-registered and must complete the registration. 1553 1554 \param team The app's team ID 1555 \param thread The app's thread ID 1556 \param thread The app looper port 1557 1558 \return A status code. 1559 \retval B_OK Everything went fine. 1560 \retval B_REG_APP_NOT_PRE_REGISTERED \a team did not identify an existing 1561 application or the identified application was already fully 1562 registered. 1563 */ 1564 status_t 1565 BRoster::_CompleteRegistration(team_id team, thread_id thread, 1566 port_id port) const 1567 { 1568 status_t error = B_OK; 1569 1570 // compose the request message 1571 BMessage request(B_REG_COMPLETE_REGISTRATION); 1572 if (error == B_OK && team >= 0) 1573 error = request.AddInt32("team", team); 1574 1575 if (error == B_OK && thread >= 0) 1576 error = request.AddInt32("thread", thread); 1577 1578 if (error == B_OK && port >= 0) 1579 error = request.AddInt32("port", port); 1580 1581 // send the request 1582 BMessage reply; 1583 if (error == B_OK) 1584 error = fMessenger.SendMessage(&request, &reply); 1585 1586 // evaluate the reply 1587 if (error == B_OK && reply.what != B_REG_SUCCESS 1588 && reply.FindInt32("error", &error) != B_OK) { 1589 error = B_ERROR; 1590 } 1591 1592 return error; 1593 } 1594 1595 1596 /*! Returns whether an application is registered. 1597 1598 If the application is indeed pre-registered and \a info is not \c NULL, 1599 the methods fills in the app_info structure pointed to by \a info. 1600 1601 \param ref An entry_ref referring to the app's executable 1602 \param team The app's team ID. May be -1, if \a token is given. 1603 \param token The app's pre-registration token. May be 0, if \a team is 1604 given. 1605 \param preRegistered: Pointer to a pre-allocated bool to be filled in 1606 by this method, indicating whether or not the app was 1607 pre-registered. 1608 \param info A pointer to a pre-allocated app_info structure to be filled 1609 in by this method (may be \c NULL) 1610 1611 \return \c B_OK, if the application is registered and all requested 1612 information could be retrieved, or another error code, if the app 1613 is not registered or an error occurred. 1614 */ 1615 status_t 1616 BRoster::_IsAppRegistered(const entry_ref* ref, team_id team, 1617 uint32 token, bool* preRegistered, app_info* info) const 1618 { 1619 status_t error = B_OK; 1620 1621 // compose the request message 1622 BMessage request(B_REG_IS_APP_REGISTERED); 1623 if (error == B_OK && ref) 1624 error = request.AddRef("ref", ref); 1625 if (error == B_OK && team >= 0) 1626 error = request.AddInt32("team", team); 1627 if (error == B_OK && token > 0) 1628 error = request.AddInt32("token", (int32)token); 1629 1630 // send the request 1631 BMessage reply; 1632 if (error == B_OK) 1633 error = fMessenger.SendMessage(&request, &reply); 1634 1635 // evaluate the reply 1636 bool isRegistered = false; 1637 bool isPreRegistered = false; 1638 if (error == B_OK) { 1639 if (reply.what == B_REG_SUCCESS) { 1640 if (reply.FindBool("registered", &isRegistered) != B_OK 1641 || !isRegistered 1642 || reply.FindBool("pre-registered", &isPreRegistered) != B_OK) { 1643 error = B_ERROR; 1644 } 1645 1646 if (error == B_OK && preRegistered) 1647 *preRegistered = isPreRegistered; 1648 if (error == B_OK && info) 1649 error = find_message_app_info(&reply, info); 1650 } else if (reply.FindInt32("error", &error) != B_OK) 1651 error = B_ERROR; 1652 } 1653 1654 return error; 1655 } 1656 1657 1658 /*! Completely unregisters a pre-registered application. 1659 1660 This method can only be used to unregister applications that don't have 1661 a team ID assigned yet. All other applications must be unregistered via 1662 RemoveApp(). 1663 1664 \param entryToken The token identifying the application (returned by 1665 AddApplication()) 1666 1667 \return A status code. 1668 \retval B_OK Everything went fine. 1669 \retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify 1670 a pre-registered application. 1671 */ 1672 status_t 1673 BRoster::_RemovePreRegApp(uint32 entryToken) const 1674 { 1675 status_t error = B_OK; 1676 1677 // compose the request message 1678 BMessage request(B_REG_REMOVE_PRE_REGISTERED_APP); 1679 if (error == B_OK) 1680 error = request.AddInt32("token", (int32)entryToken); 1681 1682 // send the request 1683 BMessage reply; 1684 if (error == B_OK) 1685 error = fMessenger.SendMessage(&request, &reply); 1686 1687 // evaluate the reply 1688 if (error == B_OK && reply.what != B_REG_SUCCESS 1689 && reply.FindInt32("error", &error) != B_OK) { 1690 error = B_ERROR; 1691 } 1692 1693 return error; 1694 } 1695 1696 1697 /*! Unregisters a (pre-)registered application. 1698 1699 This method must be used to unregister applications that already have 1700 a team ID assigned, i.e. also for pre-registered application for which 1701 SetThreadAndTeam() has already been invoked. 1702 1703 \param team The app's team ID 1704 1705 \return A status code. 1706 \retval B_OK Everything went fine. 1707 \retval B_REG_APP_NOT_REGISTERED The supplied team ID does not identify a 1708 (pre-)registered application. 1709 */ 1710 status_t 1711 BRoster::_RemoveApp(team_id team) const 1712 { 1713 status_t error = B_OK; 1714 1715 // compose the request message 1716 BMessage request(B_REG_REMOVE_APP); 1717 if (error == B_OK && team >= 0) 1718 error = request.AddInt32("team", team); 1719 1720 // send the request 1721 BMessage reply; 1722 if (error == B_OK) 1723 error = fMessenger.SendMessage(&request, &reply); 1724 1725 // evaluate the reply 1726 if (error == B_OK && reply.what != B_REG_SUCCESS 1727 && reply.FindInt32("error", &error) != B_OK) { 1728 error = B_ERROR; 1729 } 1730 1731 return error; 1732 } 1733 1734 1735 void 1736 BRoster::_ApplicationCrashed(team_id team) 1737 { 1738 BPrivate::DesktopLink link; 1739 if (link.InitCheck() != B_OK) 1740 return; 1741 1742 if (link.StartMessage(AS_APP_CRASHED) == B_OK 1743 && link.Attach(team) == B_OK) { 1744 link.Flush(); 1745 } 1746 } 1747 1748 1749 /*! Tells the registrar which application is currently active. 1750 1751 It's called from within the app_server when the active application is 1752 changed. 1753 1754 As it's called in the event loop, it must run asynchronously and cannot 1755 wait for a reply. 1756 */ 1757 status_t 1758 BRoster::_UpdateActiveApp(team_id team) const 1759 { 1760 if (team < B_OK) 1761 return B_BAD_TEAM_ID; 1762 1763 // compose the request message 1764 BMessage request(B_REG_UPDATE_ACTIVE_APP); 1765 status_t status = request.AddInt32("team", team); 1766 if (status < B_OK) 1767 return status; 1768 1769 // send the request 1770 return fMessenger.SendMessage(&request); 1771 } 1772 1773 1774 /*! Launches the application associated with the supplied MIME type or 1775 the entry referred to by the supplied entry_ref. 1776 1777 The application to be started is searched the same way FindApp() does it. 1778 1779 At least one of \a mimeType or \a ref must not be \c NULL. If \a mimeType 1780 is supplied, \a ref is ignored for finding the application. 1781 1782 If \a ref does refer to an application executable, that application is 1783 launched. Otherwise the respective application is searched and launched, 1784 and \a ref is sent to it in a \c B_REFS_RECEIVED message, unless other 1785 arguments are passed via \a argc and \a args -- then the entry_ref is 1786 converted into a path (C-string) and added to the argument vector. 1787 1788 \a messageList contains messages to be sent to the application 1789 "on launch", i.e. before ReadyToRun() is invoked on the BApplication 1790 object. The caller retains ownership of the supplied BList and the 1791 contained BMessages. In case the method fails with \c B_ALREADY_RUNNING 1792 the messages are delivered to the already running instance. The same 1793 applies to the \c B_REFS_RECEIVED message. 1794 1795 The supplied \a argc and \a args are (if containing at least one argument) 1796 put into a \c B_ARGV_RECEIVED message and sent to the launched application 1797 "on launch". The caller retains ownership of the supplied \a args. 1798 In case the method fails with \c B_ALREADY_RUNNING the message is 1799 delivered to the already running instance. The same applies to the 1800 \c B_REFS_RECEIVED message, if no arguments are supplied via \a argc and 1801 \args. 1802 1803 \param mimeType MIME type for which the application shall be launched. 1804 May be \c NULL. 1805 \param ref entry_ref referring to the file for which an application shall 1806 be launched. May be \c NULL. 1807 \param messageList Optional list of messages to be sent to the application 1808 "on launch". May be \c NULL. 1809 \param argc Specifies the number of elements in \a args. 1810 \param args An array of C-strings to be sent as B_ARGV_RECEIVED messaged 1811 to the launched application. 1812 \param appTeam Pointer to a pre-allocated team_id variable to be set to 1813 the team ID of the launched application. 1814 1815 \return A status code. 1816 \retval B_OK Everything went fine. 1817 \retval B_BAD_VALUE \c NULL \a mimeType 1818 \retval B_LAUNCH_FAILED_NO_PREFERRED_APP Neither with the supplied type 1819 nor with its supertype (if the supplied isn't a supertype itself) 1820 a preferred application is associated. 1821 \retval B_LAUNCH_FAILED_APP_NOT_FOUND The supplied type is not installed 1822 or its preferred application could not be found. 1823 \retval B_LAUNCH_FAILED_APP_IN_TRASH The supplied type's preferred 1824 application was in the trash. 1825 \retval B_LAUNCH_FAILED_EXECUTABLE The found application was not 1826 executable. 1827 */ 1828 status_t 1829 BRoster::_LaunchApp(const char* mimeType, const entry_ref* ref, 1830 const BList* messageList, int argc, const char* const* args, 1831 const char** environment, team_id* _appTeam) const 1832 { 1833 DBG(OUT("BRoster::_LaunchApp()")); 1834 1835 if (_appTeam != NULL) { 1836 // we're supposed to set _appTeam to -1 on error; we'll 1837 // reset it later if everything goes well 1838 *_appTeam = -1; 1839 } 1840 1841 if (mimeType == NULL && ref == NULL) 1842 return B_BAD_VALUE; 1843 1844 // use a mutable copy of the document entry_ref 1845 entry_ref _docRef; 1846 entry_ref* docRef = NULL; 1847 if (ref != NULL) { 1848 _docRef = *ref; 1849 docRef = &_docRef; 1850 } 1851 1852 uint32 otherAppFlags = B_REG_DEFAULT_APP_FLAGS; 1853 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS; 1854 bool alreadyRunning = false; 1855 bool wasDocument = true; 1856 status_t error = B_OK; 1857 ArgVector argVector; 1858 team_id team = -1; 1859 1860 do { 1861 // find the app 1862 entry_ref appRef; 1863 char signature[B_MIME_TYPE_LENGTH]; 1864 error = _ResolveApp(mimeType, docRef, &appRef, signature, 1865 &appFlags, &wasDocument); 1866 DBG(OUT(" find app: %s (%lx)\n", strerror(error), error)); 1867 if (error != B_OK) 1868 return error; 1869 1870 // build an argument vector 1871 error = argVector.Init(argc, args, &appRef, 1872 wasDocument ? docRef : NULL); 1873 DBG(OUT(" build argv: %s (%lx)\n", strerror(error), error)); 1874 if (error != B_OK) 1875 return error; 1876 1877 // pre-register the app (but ignore scipts) 1878 uint32 appToken = 0; 1879 app_info appInfo; 1880 bool isScript = wasDocument && docRef != NULL && *docRef == appRef; 1881 if (!isScript && !fNoRegistrar) { 1882 error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1, 1883 false, &appToken, &team); 1884 if (error == B_ALREADY_RUNNING) { 1885 DBG(OUT(" already running\n")); 1886 alreadyRunning = true; 1887 1888 // get the app flags for the running application 1889 error = _IsAppRegistered(&appRef, team, appToken, NULL, 1890 &appInfo); 1891 if (error == B_OK) { 1892 otherAppFlags = appInfo.flags; 1893 team = appInfo.team; 1894 } 1895 } 1896 DBG(OUT(" pre-register: %s (%lx)\n", strerror(error), error)); 1897 } 1898 1899 // launch the app 1900 if (error == B_OK && !alreadyRunning) { 1901 DBG(OUT(" token: %lu\n", appToken)); 1902 // load the app image 1903 thread_id appThread = load_image(argVector.Count(), 1904 const_cast<const char**>(argVector.Args()), environment); 1905 1906 // get the app team 1907 if (appThread >= 0) { 1908 thread_info threadInfo; 1909 error = get_thread_info(appThread, &threadInfo); 1910 if (error == B_OK) 1911 team = threadInfo.team; 1912 } else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE) 1913 error = B_LAUNCH_FAILED_EXECUTABLE; 1914 else 1915 error = appThread; 1916 1917 DBG(OUT(" load image: %s (%lx)\n", strerror(error), error)); 1918 // finish the registration 1919 if (error == B_OK && !isScript && !fNoRegistrar) 1920 error = _SetThreadAndTeam(appToken, appThread, team); 1921 1922 DBG(OUT(" set thread and team: %s (%lx)\n", strerror(error), 1923 error)); 1924 // resume the launched team 1925 if (error == B_OK) 1926 error = resume_thread(appThread); 1927 1928 DBG(OUT(" resume thread: %s (%lx)\n", strerror(error), error)); 1929 // on error: kill the launched team and unregister the app 1930 if (error != B_OK) { 1931 if (appThread >= 0) 1932 kill_thread(appThread); 1933 1934 if (!isScript) { 1935 if (!fNoRegistrar) 1936 _RemovePreRegApp(appToken); 1937 1938 if (!wasDocument) { 1939 // Remove app hint if it's this one 1940 BMimeType appType(signature); 1941 entry_ref hintRef; 1942 1943 if (appType.InitCheck() == B_OK 1944 && appType.GetAppHint(&hintRef) == B_OK 1945 && appRef == hintRef) { 1946 appType.SetAppHint(NULL); 1947 // try again 1948 continue; 1949 } 1950 } 1951 } 1952 } 1953 } 1954 } while (false); 1955 1956 if (alreadyRunning && current_team() == team) { 1957 // The target team is calling us, so we don't send it the message 1958 // to prevent an endless loop 1959 error = B_BAD_VALUE; 1960 } 1961 1962 // send "on launch" messages 1963 if (error == B_OK && !fNoRegistrar) { 1964 // If the target app is B_ARGV_ONLY, only if it is newly launched 1965 // messages are sent to it (namely B_ARGV_RECEIVED and B_READY_TO_RUN). 1966 // An already running B_ARGV_ONLY app won't get any messages. 1967 bool argvOnly = (appFlags & B_ARGV_ONLY) != 0 1968 || (alreadyRunning && (otherAppFlags & B_ARGV_ONLY) != 0); 1969 const BList* _messageList = (argvOnly ? NULL : messageList); 1970 // don't send ref, if it refers to the app or is included in the 1971 // argument vector 1972 const entry_ref* _ref = argvOnly || !wasDocument 1973 || argVector.Count() > 1 ? NULL : docRef; 1974 if (!(argvOnly && alreadyRunning)) { 1975 _SendToRunning(team, argVector.Count(), argVector.Args(), 1976 _messageList, _ref, alreadyRunning); 1977 } 1978 } 1979 1980 // set return values 1981 if (error == B_OK) { 1982 if (alreadyRunning) 1983 error = B_ALREADY_RUNNING; 1984 else if (_appTeam) 1985 *_appTeam = team; 1986 } 1987 1988 DBG(OUT("BRoster::_LaunchApp() done: %s (%lx)\n", 1989 strerror(error), error)); 1990 1991 return error; 1992 } 1993 1994 1995 void 1996 BRoster::_SetAppFlags(team_id team, uint32 flags) const 1997 { 1998 } 1999 2000 2001 void 2002 BRoster::_DumpRoster() const 2003 { 2004 } 2005 2006 2007 /*! Finds an application associated with a MIME type or a file. 2008 2009 It does also supply the caller with some more information about the 2010 application, like signature, app flags and whether the supplied 2011 MIME type/entry_ref already identified an application. 2012 2013 At least one of \a inType or \a ref must not be \c NULL. If \a inType is 2014 supplied, \a ref is ignored. 2015 2016 If \a ref refers to a link, it is updated with the entry_ref for the 2017 resolved entry. 2018 2019 \see FindApp() for how the application is searched. 2020 2021 \a signature is set to a string with length 0, if the found 2022 application has no signature. 2023 2024 \param inType The MIME type for which an application shall be found. 2025 May be \c NULL. 2026 \param ref The file for which an application shall be found. 2027 May be \c NULL. 2028 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2029 a reference to the found application's executable. May be \c NULL. 2030 \param signature A pointer to a pre-allocated char buffer of at 2031 least size \c B_MIME_TYPE_LENGTH to be filled with the signature of 2032 the found application. May be \c NULL. 2033 \param appFlags A pointer to a pre-allocated uint32 variable to be filled 2034 with the app flags of the found application. May be \c NULL. 2035 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2036 \c true, if the supplied file was not identifying an application, 2037 to \c false otherwise. Has no meaning, if a \a inType is supplied. 2038 May be \c NULL. 2039 2040 \return A status code. 2041 \retval B_OK Everything went fine. 2042 \retval B_BAD_VALUE \c NULL \a inType and \a ref. 2043 2044 \see FindApp() for other error codes. 2045 */ 2046 status_t 2047 BRoster::_ResolveApp(const char* inType, entry_ref* ref, 2048 entry_ref* _appRef, char* _signature, uint32* _appFlags, 2049 bool* _wasDocument) const 2050 { 2051 if ((inType == NULL && ref == NULL) 2052 || (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH)) 2053 return B_BAD_VALUE; 2054 2055 // find the app 2056 BMimeType appMeta; 2057 BFile appFile; 2058 entry_ref appRef; 2059 status_t error; 2060 2061 if (inType != NULL) { 2062 error = _TranslateType(inType, &appMeta, &appRef, &appFile); 2063 if (_wasDocument != NULL) 2064 *_wasDocument = !(appMeta == inType); 2065 } else { 2066 error = _TranslateRef(ref, &appMeta, &appRef, &appFile, 2067 _wasDocument); 2068 } 2069 2070 // create meta mime 2071 if (error == B_OK) { 2072 BPath path; 2073 if (path.SetTo(&appRef) == B_OK) 2074 create_app_meta_mime(path.Path(), false, true, false); 2075 } 2076 2077 // set the app hint on the type -- but only if the file has the 2078 // respective signature, otherwise unset the app hint 2079 BAppFileInfo appFileInfo; 2080 if (error == B_OK) { 2081 char signature[B_MIME_TYPE_LENGTH]; 2082 if (appFileInfo.SetTo(&appFile) == B_OK 2083 && appFileInfo.GetSignature(signature) == B_OK) { 2084 if (!strcasecmp(appMeta.Type(), signature)) { 2085 // Only set the app hint if there is none yet 2086 entry_ref dummyRef; 2087 if (appMeta.GetAppHint(&dummyRef) != B_OK) 2088 appMeta.SetAppHint(&appRef); 2089 } else { 2090 appMeta.SetAppHint(NULL); 2091 appMeta.SetTo(signature); 2092 } 2093 } else 2094 appMeta.SetAppHint(NULL); 2095 } 2096 2097 // set the return values 2098 if (error == B_OK) { 2099 if (_appRef) 2100 *_appRef = appRef; 2101 2102 if (_signature != NULL) { 2103 // there's no warranty, that appMeta is valid 2104 if (appMeta.IsValid()) { 2105 strlcpy(_signature, appMeta.Type(), 2106 B_MIME_TYPE_LENGTH); 2107 } else 2108 _signature[0] = '\0'; 2109 } 2110 2111 if (_appFlags != NULL) { 2112 // if an error occurs here, we don't care and just set a default 2113 // value 2114 if (appFileInfo.InitCheck() != B_OK 2115 || appFileInfo.GetAppFlags(_appFlags) != B_OK) { 2116 *_appFlags = B_REG_DEFAULT_APP_FLAGS; 2117 } 2118 } 2119 } else { 2120 // unset the ref on error 2121 if (_appRef != NULL) 2122 *_appRef = appRef; 2123 } 2124 2125 return error; 2126 } 2127 2128 2129 /*! \brief Finds an application associated with a file. 2130 2131 \a appMeta is left unmodified, if the file is executable, but has no 2132 signature. 2133 2134 \see FindApp() for how the application is searched. 2135 2136 If \a ref refers to a link, it is updated with the entry_ref for the 2137 resolved entry. 2138 2139 \param ref The file for which an application shall be found. 2140 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2141 signature of the found application. 2142 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2143 a reference to the found application's executable. 2144 \param appFile A pointer to a pre-allocated BFile to be set to the 2145 executable of the found application. 2146 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2147 \c true, if the supplied file was not identifying an application, 2148 to \c false otherwise. May be \c NULL. 2149 2150 \return A status code. 2151 \retval B_OK: Everything went fine. 2152 \retval B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile. 2153 2154 \see FindApp() for other error codes. 2155 */ 2156 status_t 2157 BRoster::_TranslateRef(entry_ref* ref, BMimeType* appMeta, 2158 entry_ref* appRef, BFile* appFile, bool* _wasDocument) const 2159 { 2160 if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL) 2161 return B_BAD_VALUE; 2162 2163 // resolve ref, if necessary 2164 BEntry entry; 2165 status_t error = entry.SetTo(ref, false); 2166 if (error != B_OK) 2167 return error; 2168 2169 if (entry.IsSymLink()) { 2170 // ref refers to a link 2171 if (entry.SetTo(ref, true) != B_OK || entry.GetRef(ref) != B_OK) 2172 return B_LAUNCH_FAILED_NO_RESOLVE_LINK; 2173 } 2174 2175 // init node 2176 BNode node; 2177 error = node.SetTo(ref); 2178 if (error != B_OK) 2179 return error; 2180 2181 // get permissions 2182 mode_t permissions; 2183 error = node.GetPermissions(&permissions); 2184 if (error != B_OK) 2185 return error; 2186 2187 if ((permissions & S_IXUSR) != 0 && node.IsFile()) { 2188 // node is executable and a file 2189 error = appFile->SetTo(ref, B_READ_ONLY); 2190 if (error != B_OK) 2191 return error; 2192 2193 // get the app's signature via a BAppFileInfo 2194 BAppFileInfo appFileInfo; 2195 error = appFileInfo.SetTo(appFile); 2196 if (error != B_OK) 2197 return error; 2198 2199 // don't worry, if the file doesn't have a signature, just 2200 // unset the supplied object 2201 char type[B_MIME_TYPE_LENGTH]; 2202 if (appFileInfo.GetSignature(type) == B_OK) { 2203 error = appMeta->SetTo(type); 2204 if (error != B_OK) 2205 return error; 2206 } else 2207 appMeta->Unset(); 2208 2209 // If the file type indicates that the file is an application, we've 2210 // definitely got what we're looking for. 2211 bool isDocument = true; 2212 if (_GetFileType(ref, &appFileInfo, type) == B_OK 2213 && strcasecmp(type, B_APP_MIME_TYPE) == 0) { 2214 isDocument = false; 2215 } 2216 2217 // If our file is not an application executable, we probably have a 2218 // script. Check whether the file has a preferred application set. If 2219 // so, we fall through and use the preferred app instead. Otherwise 2220 // we're done. 2221 char preferredApp[B_MIME_TYPE_LENGTH]; 2222 if (!isDocument || appFileInfo.GetPreferredApp(preferredApp) != B_OK) { 2223 *appRef = *ref; 2224 if (_wasDocument != NULL) 2225 *_wasDocument = isDocument; 2226 2227 return B_OK; 2228 } 2229 2230 // Executable file, but not an application, and it has a preferred 2231 // application set. Fall through... 2232 } 2233 2234 // the node is not exectuable or not a file 2235 // init a node info 2236 BNodeInfo nodeInfo; 2237 error = nodeInfo.SetTo(&node); 2238 if (error != B_OK) 2239 return error; 2240 2241 // if the file has a preferred app, let _TranslateType() find 2242 // it for us 2243 char preferredApp[B_MIME_TYPE_LENGTH]; 2244 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 2245 && _TranslateType(preferredApp, appMeta, appRef, appFile) == B_OK) { 2246 if (_wasDocument != NULL) 2247 *_wasDocument = true; 2248 2249 return B_OK; 2250 } 2251 2252 // no preferred app or existing one was not found -- we 2253 // need to get the file's type 2254 2255 // get the type from the file 2256 char fileType[B_MIME_TYPE_LENGTH]; 2257 error = _GetFileType(ref, &nodeInfo, fileType); 2258 if (error != B_OK) 2259 return error; 2260 2261 // now let _TranslateType() do the actual work 2262 error = _TranslateType(fileType, appMeta, appRef, appFile); 2263 if (error != B_OK) 2264 return error; 2265 2266 if (_wasDocument != NULL) 2267 *_wasDocument = true; 2268 2269 return B_OK; 2270 } 2271 2272 2273 /*! Finds an application associated with a MIME type. 2274 2275 \see FindApp() for how the application is searched. 2276 2277 \param mimeType The MIME type for which an application shall be found. 2278 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2279 signature of the found application. 2280 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2281 a reference to the found application's executable. 2282 \param appFile A pointer to a pre-allocated BFile to be set to the 2283 executable of the found application. 2284 2285 \return A status code. 2286 \retval B_OK Everything went fine. 2287 \retval B_BAD_VALUE \c NULL \a mimeType, \a appMeta, \a appRef or 2288 \a appFile. 2289 2290 \see FindApp() for other error codes. 2291 */ 2292 status_t 2293 BRoster::_TranslateType(const char* mimeType, BMimeType* appMeta, 2294 entry_ref* appRef, BFile* appFile) const 2295 { 2296 if (mimeType == NULL || appMeta == NULL || appRef == NULL 2297 || appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) { 2298 return B_BAD_VALUE; 2299 } 2300 2301 // Create a BMimeType and check, if the type is installed. 2302 BMimeType type; 2303 status_t error = type.SetTo(mimeType); 2304 2305 // Get the preferred apps from the sub and super type. 2306 char primarySignature[B_MIME_TYPE_LENGTH]; 2307 char secondarySignature[B_MIME_TYPE_LENGTH]; 2308 primarySignature[0] = '\0'; 2309 secondarySignature[0] = '\0'; 2310 2311 if (error == B_OK) { 2312 BMimeType superType; 2313 if (type.GetSupertype(&superType) == B_OK) 2314 superType.GetPreferredApp(secondarySignature); 2315 if (type.IsInstalled()) { 2316 if (type.GetPreferredApp(primarySignature) != B_OK) { 2317 // The type is installed, but has no preferred app. 2318 primarySignature[0] = '\0'; 2319 } else if (!strcmp(primarySignature, secondarySignature)) { 2320 // Both types have the same preferred app, there is 2321 // no point in testing it twice. 2322 secondarySignature[0] = '\0'; 2323 } 2324 } else { 2325 // The type is not installed. We assume it is an app signature. 2326 strlcpy(primarySignature, mimeType, sizeof(primarySignature)); 2327 } 2328 } 2329 2330 // We will use this BMessage "signatures" to hold all supporting apps 2331 // so we can iterator over them in the preferred order. We include 2332 // the supporting apps in such a way that the configured preferred 2333 // applications for the MIME type are in front of other supporting 2334 // applications for the sub and the super type respectively. 2335 const char* kSigField = "applications"; 2336 BMessage signatures; 2337 bool addedSecondarySignature = false; 2338 if (error == B_OK) { 2339 if (primarySignature[0] != '\0') 2340 error = signatures.AddString(kSigField, primarySignature); 2341 else { 2342 // If there is a preferred app configured for the super type, 2343 // but no preferred type for the sub-type, add the preferred 2344 // super type handler in front of any other handlers. This way 2345 // we fall-back to non-preferred but supporting apps only in the 2346 // case when there is a preferred handler for the sub-type but 2347 // it cannot be resolved (misconfiguration). 2348 if (secondarySignature[0] != '\0') { 2349 error = signatures.AddString(kSigField, secondarySignature); 2350 addedSecondarySignature = true; 2351 } 2352 } 2353 } 2354 2355 BMessage supportingSignatures; 2356 if (error == B_OK 2357 && type.GetSupportingApps(&supportingSignatures) == B_OK) { 2358 int32 subCount; 2359 if (supportingSignatures.FindInt32("be:sub", &subCount) != B_OK) 2360 subCount = 0; 2361 // Add all signatures with direct support for the sub-type. 2362 const char* supportingType; 2363 if (!addedSecondarySignature) { 2364 // Try to add the secondarySignature in front of all other 2365 // supporting apps, if we find it among those. 2366 for (int32 i = 0; error == B_OK && i < subCount 2367 && supportingSignatures.FindString(kSigField, i, 2368 &supportingType) == B_OK; i++) { 2369 if (strcmp(primarySignature, supportingType) != 0 2370 && strcmp(secondarySignature, supportingType) == 0) { 2371 error = signatures.AddString(kSigField, supportingType); 2372 addedSecondarySignature = true; 2373 break; 2374 } 2375 } 2376 } 2377 2378 for (int32 i = 0; error == B_OK && i < subCount 2379 && supportingSignatures.FindString(kSigField, i, 2380 &supportingType) == B_OK; i++) { 2381 if (strcmp(primarySignature, supportingType) != 0 2382 && strcmp(secondarySignature, supportingType) != 0) { 2383 error = signatures.AddString(kSigField, supportingType); 2384 } 2385 } 2386 2387 // Add the preferred type of the super type here before adding 2388 // the other types supporting the super type, but only if we have 2389 // not already added it in case there was no preferred app for the 2390 // sub-type configured. 2391 if (error == B_OK && !addedSecondarySignature 2392 && secondarySignature[0] != '\0') { 2393 error = signatures.AddString(kSigField, secondarySignature); 2394 } 2395 2396 // Add all signatures with support for the super-type. 2397 for (int32 i = subCount; error == B_OK 2398 && supportingSignatures.FindString(kSigField, i, 2399 &supportingType) == B_OK; i++) { 2400 // Don't add the signature if it's one of the preferred apps 2401 // already. 2402 if (strcmp(primarySignature, supportingType) != 0 2403 && strcmp(secondarySignature, supportingType) != 0) { 2404 error = signatures.AddString(kSigField, supportingType); 2405 } 2406 } 2407 } else { 2408 // Failed to get supporting apps, just add the preferred apps. 2409 if (error == B_OK && secondarySignature[0] != '\0') 2410 error = signatures.AddString(kSigField, secondarySignature); 2411 } 2412 2413 if (error != B_OK) 2414 return error; 2415 2416 // Set an error in case we can't resolve a single supporting app. 2417 error = B_LAUNCH_FAILED_NO_PREFERRED_APP; 2418 2419 // See if we can find a good application that is valid from the messege. 2420 const char* signature; 2421 for (int32 i = 0; 2422 signatures.FindString(kSigField, i, &signature) == B_OK; i++) { 2423 if (signature[0] == '\0') 2424 continue; 2425 2426 error = appMeta->SetTo(signature); 2427 2428 // Check, whether the signature is installed and has an app hint 2429 bool appFound = false; 2430 if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) { 2431 // Resolve symbolic links, if necessary 2432 BEntry entry; 2433 if (entry.SetTo(appRef, true) == B_OK && entry.IsFile() 2434 && entry.GetRef(appRef) == B_OK) { 2435 appFound = true; 2436 } else { 2437 // Bad app hint -- remove it 2438 appMeta->SetAppHint(NULL); 2439 } 2440 } 2441 2442 // In case there is no app hint or it is invalid, we need to query for 2443 // the app. 2444 if (error == B_OK && !appFound) 2445 error = query_for_app(appMeta->Type(), appRef); 2446 2447 if (error == B_OK) 2448 error = appFile->SetTo(appRef, B_READ_ONLY); 2449 2450 // check, whether the app can be used 2451 if (error == B_OK) 2452 error = can_app_be_used(appRef); 2453 2454 if (error == B_OK) 2455 break; 2456 } 2457 2458 return error; 2459 } 2460 2461 2462 /*! Gets the type of a file either from the node info or by sniffing. 2463 2464 The method first tries to get the file type from the supplied node info. If 2465 that didn't work, the given entry ref is sniffed. 2466 2467 \param file An entry_ref referring to the file in question. 2468 \param nodeInfo A BNodeInfo initialized to the file. 2469 \param mimeType A pointer to a pre-allocated char buffer of at least size 2470 \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for 2471 the file. 2472 2473 \return A status code. 2474 \retval B_OK Everything went fine. 2475 \retval B_BAD_VALUE \c NULL \a file, \a nodeInfo or \a mimeType. 2476 */ 2477 status_t 2478 BRoster::_GetFileType(const entry_ref* file, BNodeInfo* nodeInfo, 2479 char* mimeType) const 2480 { 2481 // first try the node info 2482 if (nodeInfo->GetType(mimeType) == B_OK) 2483 return B_OK; 2484 2485 // Try to update the file's MIME info and just read the updated type. 2486 // If that fails, sniff manually. 2487 BPath path; 2488 if (path.SetTo(file) != B_OK 2489 || update_mime_info(path.Path(), false, true, false) != B_OK 2490 || nodeInfo->GetType(mimeType) != B_OK) { 2491 BMimeType type; 2492 status_t error = BMimeType::GuessMimeType(file, &type); 2493 if (error != B_OK) 2494 return error; 2495 2496 if (!type.IsValid()) 2497 return B_BAD_VALUE; 2498 2499 strlcpy(mimeType, type.Type(), B_MIME_TYPE_LENGTH); 2500 } 2501 2502 return B_OK; 2503 } 2504 2505 2506 /*! Sends messages to a running team. 2507 2508 In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED, 2509 \c B_READY_TO_RUN and other, arbitrary, ones. 2510 2511 If \a messageList is not \c NULL or empty, those messages are sent first, 2512 then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally 2513 \c B_READ_TO_RUN. 2514 2515 \c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains 2516 more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not 2517 \c NULL. 2518 2519 The ownership of all supplied objects retains to the caller. 2520 2521 \param team The team ID of the target application. 2522 \param argc Number of elements in \a args. 2523 \param args Argument vector to be sent to the target. May be \c NULL. 2524 \param messageList List of BMessages to be sent to the target. May be 2525 \c NULL or empty. 2526 \param ref entry_ref to be sent to the target. May be \c NULL. 2527 \param alreadyRunning \c true, if the target app is not newly launched, 2528 but was already running, \c false otherwise (a \c B_READY_TO_RUN 2529 message will be sent in this case). 2530 2531 \return \c B_OK if everything went fine, or an error code otherwise. 2532 */ 2533 status_t 2534 BRoster::_SendToRunning(team_id team, int argc, const char* const* args, 2535 const BList* messageList, const entry_ref* ref, 2536 bool alreadyRunning) const 2537 { 2538 status_t error = B_OK; 2539 2540 // Construct a messenger to the app: We can't use the public constructor, 2541 // since the target application may be B_ARGV_ONLY. 2542 app_info info; 2543 error = GetRunningAppInfo(team, &info); 2544 if (error == B_OK) { 2545 BMessenger messenger; 2546 BMessenger::Private(messenger).SetTo(team, info.port, 2547 B_PREFERRED_TOKEN); 2548 2549 // send messages from the list 2550 if (messageList != NULL) { 2551 for (int32 i = 0; 2552 BMessage* message = (BMessage*)messageList->ItemAt(i); 2553 i++) { 2554 messenger.SendMessage(message); 2555 } 2556 } 2557 2558 // send B_ARGV_RECEIVED or B_REFS_RECEIVED or B_SILENT_RELAUNCH 2559 // (if already running) 2560 if (args != NULL && argc > 1) { 2561 BMessage message(B_ARGV_RECEIVED); 2562 message.AddInt32("argc", argc); 2563 for (int32 i = 0; i < argc; i++) 2564 message.AddString("argv", args[i]); 2565 2566 // also add current working directory 2567 char cwd[B_PATH_NAME_LENGTH]; 2568 if (getcwd(cwd, B_PATH_NAME_LENGTH) != NULL) 2569 message.AddString("cwd", cwd); 2570 2571 messenger.SendMessage(&message); 2572 } else if (ref != NULL) { 2573 DBG(OUT("_SendToRunning : B_REFS_RECEIVED\n")); 2574 BMessage message(B_REFS_RECEIVED); 2575 message.AddRef("refs", ref); 2576 messenger.SendMessage(&message); 2577 } else if (alreadyRunning && (!messageList || messageList->IsEmpty())) 2578 messenger.SendMessage(B_SILENT_RELAUNCH); 2579 2580 if (!alreadyRunning) { 2581 // send B_READY_TO_RUN 2582 DBG(OUT("_SendToRunning : B_READY_TO_RUN\n")); 2583 messenger.SendMessage(B_READY_TO_RUN); 2584 } 2585 } 2586 2587 return error; 2588 } 2589 2590 2591 /*! Allows to use certain functionality of the BRoster class without 2592 accessing the registrar. 2593 */ 2594 void 2595 BRoster::_SetWithoutRegistrar(bool noRegistrar) 2596 { 2597 fNoRegistrar = noRegistrar; 2598 } 2599 2600 2601 void 2602 BRoster::_InitMessenger() 2603 { 2604 DBG(OUT("BRoster::InitMessengers()\n")); 2605 2606 // find the registrar port 2607 BMessage data; 2608 if (BLaunchRoster().GetData("application/x-vnd.Haiku-registrar", data) 2609 == B_OK) { 2610 port_id port = data.GetInt32("port", -1); 2611 team_id team = data.GetInt32("team", -1); 2612 if (port >= 0) { 2613 DBG(OUT(" found roster port\n")); 2614 2615 BMessenger::Private(fMessenger).SetTo(team, port, 2616 B_PREFERRED_TOKEN); 2617 } 2618 } 2619 2620 DBG(OUT("BRoster::InitMessengers() done\n")); 2621 } 2622 2623 2624 /*static*/ status_t 2625 BRoster::_InitMimeMessenger(void* data) 2626 { 2627 BRoster* roster = (BRoster*)data; 2628 2629 // ask for the MIME messenger 2630 // Generous 1s + 5s timeouts. It could actually be synchronous, but 2631 // timeouts allow us to debug the registrar main thread. 2632 BMessage request(B_REG_GET_MIME_MESSENGER); 2633 BMessage reply; 2634 status_t error = roster->fMessenger.SendMessage(&request, &reply, 2635 1000000LL, 5000000LL); 2636 if (error == B_OK && reply.what == B_REG_SUCCESS) { 2637 DBG(OUT(" got reply from roster\n")); 2638 reply.FindMessenger("messenger", &roster->fMimeMessenger); 2639 } else { 2640 DBG(OUT(" no (useful) reply from roster: error: %lx: %s\n", error, 2641 strerror(error))); 2642 if (error == B_OK) 2643 DBG(reply.PrintToStream()); 2644 } 2645 2646 return error; 2647 } 2648 2649 2650 BMessenger& 2651 BRoster::_MimeMessenger() 2652 { 2653 __init_once(&fMimeMessengerInitOnce, &_InitMimeMessenger, this); 2654 return fMimeMessenger; 2655 } 2656 2657 2658 /*! Sends a request to the roster to add the application with the 2659 given signature to the front of the recent apps list. 2660 */ 2661 void 2662 BRoster::_AddToRecentApps(const char* signature) const 2663 { 2664 status_t error = B_OK; 2665 // compose the request message 2666 BMessage request(B_REG_ADD_TO_RECENT_APPS); 2667 if (error == B_OK) 2668 error = request.AddString("app sig", signature); 2669 2670 // send the request 2671 BMessage reply; 2672 if (error == B_OK) 2673 error = fMessenger.SendMessage(&request, &reply); 2674 2675 // evaluate the reply 2676 status_t result; 2677 if (error == B_OK) { 2678 error = reply.what == B_REG_RESULT 2679 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2680 } 2681 2682 if (error == B_OK) 2683 error = reply.FindInt32("result", &result); 2684 2685 if (error == B_OK) 2686 error = result; 2687 2688 // Nothing to return... how sad :-( 2689 //return error; 2690 } 2691 2692 2693 //! Sends a request to the roster to clear the recent documents list. 2694 void 2695 BRoster::_ClearRecentDocuments() const 2696 { 2697 BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS); 2698 BMessage reply; 2699 fMessenger.SendMessage(&request, &reply); 2700 } 2701 2702 2703 //! Sends a request to the roster to clear the recent documents list. 2704 void 2705 BRoster::_ClearRecentFolders() const 2706 { 2707 BMessage request(B_REG_CLEAR_RECENT_FOLDERS); 2708 BMessage reply; 2709 fMessenger.SendMessage(&request, &reply); 2710 } 2711 2712 2713 //! \brief Sends a request to the roster to clear the recent documents list. 2714 void 2715 BRoster::_ClearRecentApps() const 2716 { 2717 BMessage request(B_REG_CLEAR_RECENT_APPS); 2718 BMessage reply; 2719 fMessenger.SendMessage(&request, &reply); 2720 } 2721 2722 2723 /*! Loads the system's recently used document, folder, and 2724 application lists from the specified file. 2725 2726 \note The current lists are cleared before loading the new lists 2727 2728 \param filename The name of the file to load from 2729 */ 2730 void 2731 BRoster::_LoadRecentLists(const char* filename) const 2732 { 2733 status_t error = B_OK; 2734 2735 // compose the request message 2736 BMessage request(B_REG_LOAD_RECENT_LISTS); 2737 if (error == B_OK) 2738 error = request.AddString("filename", filename); 2739 2740 // send the request 2741 BMessage reply; 2742 if (error == B_OK) 2743 error = fMessenger.SendMessage(&request, &reply); 2744 2745 // evaluate the reply 2746 status_t result; 2747 if (error == B_OK) { 2748 error = reply.what == B_REG_RESULT 2749 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2750 } 2751 if (error == B_OK) 2752 error = reply.FindInt32("result", &result); 2753 2754 if (error == B_OK) 2755 error = result; 2756 2757 // Nothing to return... how sad :-( 2758 //return error; 2759 } 2760 2761 2762 /*! Saves the system's recently used document, folder, and 2763 application lists to the specified file. 2764 2765 \param filename The name of the file to save to 2766 */ 2767 void 2768 BRoster::_SaveRecentLists(const char* filename) const 2769 { 2770 status_t error = B_OK; 2771 2772 // compose the request message 2773 BMessage request(B_REG_SAVE_RECENT_LISTS); 2774 if (error == B_OK) 2775 error = request.AddString("filename", filename); 2776 2777 // send the request 2778 BMessage reply; 2779 if (error == B_OK) 2780 error = fMessenger.SendMessage(&request, &reply); 2781 2782 // evaluate the reply 2783 status_t result; 2784 if (error == B_OK) { 2785 error = reply.what == B_REG_RESULT 2786 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2787 } 2788 if (error == B_OK) 2789 error = reply.FindInt32("result", &result); 2790 2791 if (error == B_OK) 2792 error = result; 2793 2794 // Nothing to return... how sad :-( 2795 //return error; 2796 } 2797