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