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 /*! (Pre-)Registers an application with the registrar. 1347 1348 This methods is invoked either to register or to pre-register an 1349 application. Full registration is requested by supplying \c true via 1350 \a fullRegistration. 1351 1352 A full registration requires \a signature, \a ref, \a flags, \a team, 1353 \a thread and \a port to contain valid values. No token will be return 1354 via \a pToken. 1355 1356 For a pre-registration \a signature, \a ref, \a flags must be valid. 1357 \a team and \a thread are optional and should be set to -1, if they are 1358 unknown. If no team ID is supplied, \a pToken should be valid and, if the 1359 the pre-registration succeeds, will be filled with a unique token assigned 1360 by the roster. 1361 1362 In both cases the registration may fail, if single/exclusive launch is 1363 requested and an instance of the application is already running. Then 1364 \c B_ALREADY_RUNNING is returned and the team ID of the running instance 1365 is passed back via \a otherTeam, if supplied. 1366 1367 \param signature The application's signature 1368 \param ref An entry_ref referring to the app's executable 1369 \param flags The application's flags 1370 \param team The application's team ID 1371 \param thread The application's main thread 1372 \param port The application's looper port 1373 \param fullRegistration \c true for full, \c false for pre-registration 1374 \param pToken A pointer to a pre-allocated uint32 into which the token 1375 assigned by the registrar is written (may be \c NULL) 1376 \param otherTeam A pointer to a pre-allocated team_id into which the 1377 team ID of the already running instance of a single/exclusive 1378 launch application is written (may be \c NULL) 1379 1380 \return A status code 1381 \retval B_OK Everything went fine. 1382 \retval B_ENTRY_NOT_FOUND \a ref didn't refer to a file. 1383 \retval B_ALREADY_RUNNING The application requested a single/exclusive 1384 launch and an instance was already running. 1385 \retval B_REG_ALREADY_REGISTERED An application with the team ID \a team 1386 was already registered. 1387 */ 1388 status_t 1389 BRoster::_AddApplication(const char* signature, const entry_ref* ref, 1390 uint32 flags, team_id team, thread_id thread, port_id port, 1391 bool fullRegistration, uint32* pToken, team_id* otherTeam) const 1392 { 1393 status_t error = B_OK; 1394 1395 // compose the request message 1396 BMessage request(B_REG_ADD_APP); 1397 if (error == B_OK && signature != NULL) 1398 error = request.AddString("signature", signature); 1399 1400 if (error == B_OK && ref != NULL) 1401 error = request.AddRef("ref", ref); 1402 1403 if (error == B_OK) 1404 error = request.AddInt32("flags", (int32)flags); 1405 1406 if (error == B_OK && team >= 0) 1407 error = request.AddInt32("team", team); 1408 1409 if (error == B_OK && thread >= 0) 1410 error = request.AddInt32("thread", thread); 1411 1412 if (error == B_OK && port >= 0) 1413 error = request.AddInt32("port", port); 1414 1415 if (error == B_OK) 1416 error = request.AddBool("full_registration", fullRegistration); 1417 1418 // send the request 1419 BMessage reply; 1420 if (error == B_OK) 1421 error = fMessenger.SendMessage(&request, &reply); 1422 1423 // evaluate the reply 1424 if (error == B_OK) { 1425 if (reply.what == B_REG_SUCCESS) { 1426 if (!fullRegistration && team < 0) { 1427 uint32 token; 1428 if (reply.FindInt32("token", (int32*)&token) == B_OK) { 1429 if (pToken != NULL) 1430 *pToken = token; 1431 } else 1432 error = B_ERROR; 1433 } 1434 } else { 1435 if (reply.FindInt32("error", &error) != B_OK) 1436 error = B_ERROR; 1437 1438 // get team and token from the reply 1439 if (otherTeam != NULL 1440 && reply.FindInt32("other_team", otherTeam) != B_OK) { 1441 *otherTeam = -1; 1442 } 1443 if (pToken != NULL 1444 && reply.FindInt32("token", (int32*)pToken) != B_OK) { 1445 *pToken = 0; 1446 } 1447 } 1448 } 1449 1450 return error; 1451 } 1452 1453 1454 /*! Sets an application's signature. 1455 1456 The application must be registered or at pre-registered with a valid 1457 team ID. 1458 1459 \param team The app's team ID. 1460 \param signature The app's new signature. 1461 1462 \return A status code. 1463 \retval B_OK Everything went fine. 1464 \retval B_REG_APP_NOT_REGISTERED The supplied team ID did not identify a 1465 registered application. 1466 */ 1467 status_t 1468 BRoster::_SetSignature(team_id team, const char* signature) const 1469 { 1470 status_t error = B_OK; 1471 1472 // compose the request message 1473 BMessage request(B_REG_SET_SIGNATURE); 1474 if (team >= 0) 1475 error = request.AddInt32("team", team); 1476 1477 if (error == B_OK && signature) 1478 error = request.AddString("signature", signature); 1479 1480 // send the request 1481 BMessage reply; 1482 if (error == B_OK) 1483 error = fMessenger.SendMessage(&request, &reply); 1484 1485 // evaluate the reply 1486 if (error == B_OK && reply.what != B_REG_SUCCESS 1487 && reply.FindInt32("error", &error) != B_OK) { 1488 error = B_ERROR; 1489 } 1490 1491 return error; 1492 } 1493 1494 1495 //! \todo Really needed? 1496 void 1497 BRoster::_SetThread(team_id team, thread_id thread) const 1498 { 1499 } 1500 1501 1502 /*! Sets the team and thread IDs of a pre-registered application. 1503 1504 After an application has been pre-registered via AddApplication(), without 1505 supplying a team ID, the team and thread IDs have to be set using this 1506 method. 1507 1508 \param entryToken The token identifying the application (returned by 1509 AddApplication()) 1510 \param thread The app's thread ID 1511 \param team The app's team ID 1512 1513 \return A status code. 1514 \retval B_OK Everything went fine. 1515 \retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify a 1516 pre-registered application. 1517 */ 1518 status_t 1519 BRoster::_SetThreadAndTeam(uint32 entryToken, thread_id thread, 1520 team_id team, port_id* _port) const 1521 { 1522 status_t error = B_OK; 1523 1524 // compose the request message 1525 BMessage request(B_REG_SET_THREAD_AND_TEAM); 1526 if (error == B_OK) 1527 error = request.AddInt32("token", (int32)entryToken); 1528 1529 if (error == B_OK && team >= 0) 1530 error = request.AddInt32("team", team); 1531 1532 if (error == B_OK && thread >= 0) 1533 error = request.AddInt32("thread", thread); 1534 1535 // send the request 1536 BMessage reply; 1537 if (error == B_OK) 1538 error = fMessenger.SendMessage(&request, &reply); 1539 1540 // evaluate the reply 1541 if (error == B_OK && reply.what != B_REG_SUCCESS 1542 && reply.FindInt32("error", &error) != B_OK) 1543 error = B_ERROR; 1544 1545 if (error == B_OK && _port != NULL) 1546 *_port = reply.GetInt32("port", -1); 1547 1548 return error; 1549 } 1550 1551 1552 /*! Completes the registration process for a pre-registered application. 1553 1554 After an application has been pre-registered via AddApplication() and 1555 after assigning it a team ID (via SetThreadAndTeam()) the application is 1556 still pre-registered and must complete the registration. 1557 1558 \param team The app's team ID 1559 \param thread The app's thread ID 1560 \param thread The app looper port 1561 1562 \return A status code. 1563 \retval B_OK Everything went fine. 1564 \retval B_REG_APP_NOT_PRE_REGISTERED \a team did not identify an existing 1565 application or the identified application was already fully 1566 registered. 1567 */ 1568 status_t 1569 BRoster::_CompleteRegistration(team_id team, thread_id thread, 1570 port_id port) const 1571 { 1572 status_t error = B_OK; 1573 1574 // compose the request message 1575 BMessage request(B_REG_COMPLETE_REGISTRATION); 1576 if (team >= 0) 1577 error = request.AddInt32("team", team); 1578 1579 if (error == B_OK && thread >= 0) 1580 error = request.AddInt32("thread", thread); 1581 1582 if (error == B_OK && port >= 0) 1583 error = request.AddInt32("port", port); 1584 1585 // send the request 1586 BMessage reply; 1587 if (error == B_OK) 1588 error = fMessenger.SendMessage(&request, &reply); 1589 1590 // evaluate the reply 1591 if (error == B_OK && reply.what != B_REG_SUCCESS 1592 && reply.FindInt32("error", &error) != B_OK) { 1593 error = B_ERROR; 1594 } 1595 1596 return error; 1597 } 1598 1599 1600 /*! Returns whether an application is registered. 1601 1602 If the application is indeed pre-registered and \a info is not \c NULL, 1603 the methods fills in the app_info structure pointed to by \a info. 1604 1605 \param ref An entry_ref referring to the app's executable 1606 \param team The app's team ID. May be -1, if \a token is given. 1607 \param token The app's pre-registration token. May be 0, if \a team is 1608 given. 1609 \param preRegistered: Pointer to a pre-allocated bool to be filled in 1610 by this method, indicating whether or not the app was 1611 pre-registered. 1612 \param info A pointer to a pre-allocated app_info structure to be filled 1613 in by this method (may be \c NULL) 1614 1615 \return \c B_OK, if the application is registered and all requested 1616 information could be retrieved, or another error code, if the app 1617 is not registered or an error occurred. 1618 */ 1619 status_t 1620 BRoster::_IsAppRegistered(const entry_ref* ref, team_id team, 1621 uint32 token, bool* preRegistered, app_info* info) const 1622 { 1623 status_t error = B_OK; 1624 1625 // compose the request message 1626 BMessage request(B_REG_IS_APP_REGISTERED); 1627 if (ref) 1628 error = request.AddRef("ref", ref); 1629 if (error == B_OK && team >= 0) 1630 error = request.AddInt32("team", team); 1631 if (error == B_OK && token > 0) 1632 error = request.AddInt32("token", (int32)token); 1633 1634 // send the request 1635 BMessage reply; 1636 if (error == B_OK) 1637 error = fMessenger.SendMessage(&request, &reply); 1638 1639 // evaluate the reply 1640 bool isRegistered = false; 1641 bool isPreRegistered = false; 1642 if (error == B_OK) { 1643 if (reply.what == B_REG_SUCCESS) { 1644 if (reply.FindBool("registered", &isRegistered) != B_OK 1645 || !isRegistered 1646 || reply.FindBool("pre-registered", &isPreRegistered) != B_OK) { 1647 error = B_ERROR; 1648 } 1649 1650 if (error == B_OK && preRegistered) 1651 *preRegistered = isPreRegistered; 1652 if (error == B_OK && info) 1653 error = find_message_app_info(&reply, info); 1654 } else if (reply.FindInt32("error", &error) != B_OK) 1655 error = B_ERROR; 1656 } 1657 1658 return error; 1659 } 1660 1661 1662 /*! Completely unregisters a pre-registered application. 1663 1664 This method can only be used to unregister applications that don't have 1665 a team ID assigned yet. All other applications must be unregistered via 1666 RemoveApp(). 1667 1668 \param entryToken The token identifying the application (returned by 1669 AddApplication()) 1670 1671 \return A status code. 1672 \retval B_OK Everything went fine. 1673 \retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify 1674 a pre-registered application. 1675 */ 1676 status_t 1677 BRoster::_RemovePreRegApp(uint32 entryToken) const 1678 { 1679 status_t error = B_OK; 1680 1681 // compose the request message 1682 BMessage request(B_REG_REMOVE_PRE_REGISTERED_APP); 1683 if (error == B_OK) 1684 error = request.AddInt32("token", (int32)entryToken); 1685 1686 // send the request 1687 BMessage reply; 1688 if (error == B_OK) 1689 error = fMessenger.SendMessage(&request, &reply); 1690 1691 // evaluate the reply 1692 if (error == B_OK && reply.what != B_REG_SUCCESS 1693 && reply.FindInt32("error", &error) != B_OK) { 1694 error = B_ERROR; 1695 } 1696 1697 return error; 1698 } 1699 1700 1701 /*! Unregisters a (pre-)registered application. 1702 1703 This method must be used to unregister applications that already have 1704 a team ID assigned, i.e. also for pre-registered application for which 1705 SetThreadAndTeam() has already been invoked. 1706 1707 \param team The app's team ID 1708 1709 \return A status code. 1710 \retval B_OK Everything went fine. 1711 \retval B_REG_APP_NOT_REGISTERED The supplied team ID does not identify a 1712 (pre-)registered application. 1713 */ 1714 status_t 1715 BRoster::_RemoveApp(team_id team) const 1716 { 1717 status_t error = B_OK; 1718 1719 // compose the request message 1720 BMessage request(B_REG_REMOVE_APP); 1721 if (team >= 0) 1722 error = request.AddInt32("team", team); 1723 1724 // send the request 1725 BMessage reply; 1726 if (error == B_OK) 1727 error = fMessenger.SendMessage(&request, &reply); 1728 1729 // evaluate the reply 1730 if (error == B_OK && reply.what != B_REG_SUCCESS 1731 && reply.FindInt32("error", &error) != B_OK) { 1732 error = B_ERROR; 1733 } 1734 1735 return error; 1736 } 1737 1738 1739 void 1740 BRoster::_ApplicationCrashed(team_id team) 1741 { 1742 BPrivate::DesktopLink link; 1743 if (link.InitCheck() != B_OK) 1744 return; 1745 1746 if (link.StartMessage(AS_APP_CRASHED) == B_OK 1747 && link.Attach(team) == B_OK) { 1748 link.Flush(); 1749 } 1750 } 1751 1752 1753 /*! Tells the registrar which application is currently active. 1754 1755 It's called from within the app_server when the active application is 1756 changed. 1757 1758 As it's called in the event loop, it must run asynchronously and cannot 1759 wait for a reply. 1760 */ 1761 status_t 1762 BRoster::_UpdateActiveApp(team_id team) const 1763 { 1764 if (team < B_OK) 1765 return B_BAD_TEAM_ID; 1766 1767 // compose the request message 1768 BMessage request(B_REG_UPDATE_ACTIVE_APP); 1769 status_t status = request.AddInt32("team", team); 1770 if (status < B_OK) 1771 return status; 1772 1773 // send the request 1774 return fMessenger.SendMessage(&request); 1775 } 1776 1777 1778 /*! Launches the application associated with the supplied MIME type or 1779 the entry referred to by the supplied entry_ref. 1780 1781 The application to be started is searched the same way FindApp() does it. 1782 1783 At least one of \a mimeType or \a ref must not be \c NULL. If \a mimeType 1784 is supplied, \a ref is ignored for finding the application. 1785 1786 If \a ref does refer to an application executable, that application is 1787 launched. Otherwise the respective application is searched and launched, 1788 and \a ref is sent to it in a \c B_REFS_RECEIVED message, unless other 1789 arguments are passed via \a argc and \a args -- then the entry_ref is 1790 converted into a path (C-string) and added to the argument vector. 1791 1792 \a messageList contains messages to be sent to the application 1793 "on launch", i.e. before ReadyToRun() is invoked on the BApplication 1794 object. The caller retains ownership of the supplied BList and the 1795 contained BMessages. In case the method fails with \c B_ALREADY_RUNNING 1796 the messages are delivered to the already running instance. The same 1797 applies to the \c B_REFS_RECEIVED message. 1798 1799 The supplied \a argc and \a args are (if containing at least one argument) 1800 put into a \c B_ARGV_RECEIVED message and sent to the launched application 1801 "on launch". The caller retains ownership of the supplied \a args. 1802 In case the method fails with \c B_ALREADY_RUNNING the message is 1803 delivered to the already running instance. The same applies to the 1804 \c B_REFS_RECEIVED message, if no arguments are supplied via \a argc and 1805 \args. 1806 1807 If \a launchSuspended is set to true, the main thread of the loaded app 1808 (returned in \a appThread) is kept in the suspended state and not 1809 automatically resumed. 1810 1811 \param mimeType MIME type for which the application shall be launched. 1812 May be \c NULL. 1813 \param ref entry_ref referring to the file for which an application shall 1814 be launched. May be \c NULL. 1815 \param messageList Optional list of messages to be sent to the application 1816 "on launch". May be \c NULL. 1817 \param argc Specifies the number of elements in \a args. 1818 \param args An array of C-strings to be sent as B_ARGV_RECEIVED messaged 1819 to the launched application. 1820 \param appTeam Pointer to a pre-allocated team_id variable to be set to 1821 the team ID of the launched application. 1822 \param appThread Pointer to a pre-allocated thread_id variable to 1823 be set to the thread ID of the launched main thread. 1824 \param launchSuspended Indicates whether to keep the app thread in the 1825 suspended state or resume it. 1826 1827 \return A status code. 1828 \retval B_OK Everything went fine. 1829 \retval B_BAD_VALUE \c NULL \a mimeType 1830 \retval B_LAUNCH_FAILED_NO_PREFERRED_APP Neither with the supplied type 1831 nor with its supertype (if the supplied isn't a supertype itself) 1832 a preferred application is associated. 1833 \retval B_LAUNCH_FAILED_APP_NOT_FOUND The supplied type is not installed 1834 or its preferred application could not be found. 1835 \retval B_LAUNCH_FAILED_APP_IN_TRASH The supplied type's preferred 1836 application was in the trash. 1837 \retval B_LAUNCH_FAILED_EXECUTABLE The found application was not 1838 executable. 1839 */ 1840 status_t 1841 BRoster::_LaunchApp(const char* mimeType, const entry_ref* ref, 1842 const BList* messageList, int argc, const char* const* args, 1843 const char** environment, team_id* _appTeam, thread_id* _appThread, 1844 port_id* _appPort, uint32* _appToken, bool launchSuspended) const 1845 { 1846 DBG(OUT("BRoster::_LaunchApp()")); 1847 1848 if (_appTeam != NULL) { 1849 // we're supposed to set _appTeam to -1 on error; we'll 1850 // reset it later if everything goes well 1851 *_appTeam = -1; 1852 } 1853 1854 if (mimeType == NULL && ref == NULL) 1855 return B_BAD_VALUE; 1856 1857 // use a mutable copy of the document entry_ref 1858 entry_ref _docRef; 1859 entry_ref* docRef = NULL; 1860 if (ref != NULL) { 1861 _docRef = *ref; 1862 docRef = &_docRef; 1863 } 1864 1865 uint32 otherAppFlags = B_REG_DEFAULT_APP_FLAGS; 1866 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS; 1867 bool alreadyRunning = false; 1868 bool wasDocument = true; 1869 status_t error = B_OK; 1870 ArgVector argVector; 1871 team_id team = -1; 1872 thread_id appThread = -1; 1873 port_id appPort = -1; 1874 uint32 appToken = 0; 1875 entry_ref hintRef; 1876 1877 while (true) { 1878 // find the app 1879 entry_ref appRef; 1880 char signature[B_MIME_TYPE_LENGTH]; 1881 error = _ResolveApp(mimeType, docRef, &appRef, signature, 1882 &appFlags, &wasDocument); 1883 DBG(OUT(" find app: %s (%" B_PRIx32 ") %s \n", strerror(error), error, 1884 signature)); 1885 1886 if (error != B_OK) 1887 return error; 1888 1889 // build an argument vector 1890 error = argVector.Init(argc, args, &appRef, 1891 wasDocument ? docRef : NULL); 1892 DBG(OUT(" build argv: %s (%" B_PRIx32 ")\n", strerror(error), error)); 1893 if (error != B_OK) 1894 return error; 1895 1896 // pre-register the app (but ignore scipts) 1897 app_info appInfo; 1898 bool isScript = wasDocument && docRef != NULL && *docRef == appRef; 1899 if (!isScript && !fNoRegistrar) { 1900 error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1, 1901 false, &appToken, &team); 1902 if (error == B_ALREADY_RUNNING) { 1903 DBG(OUT(" already running\n")); 1904 alreadyRunning = true; 1905 1906 // get the app flags for the running application 1907 error = _IsAppRegistered(&appRef, team, appToken, NULL, 1908 &appInfo); 1909 if (error == B_OK) { 1910 otherAppFlags = appInfo.flags; 1911 appPort = appInfo.port; 1912 team = appInfo.team; 1913 } 1914 } 1915 DBG(OUT(" pre-register: %s (%" B_PRIx32 ")\n", strerror(error), 1916 error)); 1917 } 1918 1919 // launch the app 1920 if (error == B_OK && !alreadyRunning) { 1921 DBG(OUT(" token: %" B_PRIu32 "\n", appToken)); 1922 // load the app image 1923 appThread = load_image(argVector.Count(), 1924 const_cast<const char**>(argVector.Args()), environment); 1925 1926 // get the app team 1927 if (appThread >= 0) { 1928 thread_info threadInfo; 1929 error = get_thread_info(appThread, &threadInfo); 1930 if (error == B_OK) 1931 team = threadInfo.team; 1932 } else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE) 1933 error = B_LAUNCH_FAILED_EXECUTABLE; 1934 else 1935 error = appThread; 1936 1937 DBG(OUT(" load image: %s (%" B_PRIx32 ")\n", strerror(error), 1938 error)); 1939 // finish the registration 1940 if (error == B_OK && !isScript && !fNoRegistrar) 1941 error = _SetThreadAndTeam(appToken, appThread, team, &appPort); 1942 1943 DBG(OUT(" set thread and team: %s (%" B_PRIx32 ")\n", 1944 strerror(error), error)); 1945 // resume the launched team 1946 if (error == B_OK && !launchSuspended) 1947 error = resume_thread(appThread); 1948 1949 DBG(OUT(" resume thread: %s (%" B_PRIx32 ")\n", strerror(error), 1950 error)); 1951 // on error: kill the launched team and unregister the app 1952 if (error != B_OK) { 1953 if (appThread >= 0) 1954 kill_thread(appThread); 1955 1956 if (!isScript) { 1957 if (!fNoRegistrar) 1958 _RemovePreRegApp(appToken); 1959 1960 if (!wasDocument) { 1961 // Did we already try this? 1962 if (appRef == hintRef) 1963 break; 1964 1965 // Remove app hint if it's this one 1966 BMimeType appType(signature); 1967 1968 if (appType.InitCheck() == B_OK 1969 && appType.GetAppHint(&hintRef) == B_OK 1970 && appRef == hintRef) { 1971 appType.SetAppHint(NULL); 1972 // try again with the app hint removed 1973 continue; 1974 } 1975 } 1976 } 1977 } 1978 } 1979 // Don't try again 1980 break; 1981 } 1982 1983 if (alreadyRunning && current_team() == team) { 1984 // The target team is calling us, so we don't send it the message 1985 // to prevent an endless loop 1986 error = B_BAD_VALUE; 1987 } 1988 1989 // send "on launch" messages 1990 if (error == B_OK && !fNoRegistrar) { 1991 // If the target app is B_ARGV_ONLY, only if it is newly launched 1992 // messages are sent to it (namely B_ARGV_RECEIVED and B_READY_TO_RUN). 1993 // An already running B_ARGV_ONLY app won't get any messages. 1994 bool argvOnly = (appFlags & B_ARGV_ONLY) != 0 1995 || (alreadyRunning && (otherAppFlags & B_ARGV_ONLY) != 0); 1996 const BList* _messageList = (argvOnly ? NULL : messageList); 1997 // don't send ref, if it refers to the app or is included in the 1998 // argument vector 1999 const entry_ref* _ref = argvOnly || !wasDocument 2000 || argVector.Count() > 1 ? NULL : docRef; 2001 if (!(argvOnly && alreadyRunning)) { 2002 _SendToRunning(team, argVector.Count(), argVector.Args(), 2003 _messageList, _ref, alreadyRunning); 2004 } 2005 } 2006 2007 // set return values 2008 if (error == B_OK) { 2009 if (alreadyRunning) 2010 error = B_ALREADY_RUNNING; 2011 else if (_appTeam) 2012 *_appTeam = team; 2013 2014 if (_appThread != NULL) 2015 *_appThread = appThread; 2016 if (_appPort != NULL) 2017 *_appPort = appPort; 2018 if (_appToken != NULL) 2019 *_appToken = appToken; 2020 } 2021 2022 DBG(OUT("BRoster::_LaunchApp() done: %s (%" B_PRIx32 ")\n", 2023 strerror(error), error)); 2024 2025 return error; 2026 } 2027 2028 2029 void 2030 BRoster::_SetAppFlags(team_id team, uint32 flags) const 2031 { 2032 } 2033 2034 2035 void 2036 BRoster::_DumpRoster() const 2037 { 2038 } 2039 2040 2041 /*! Finds an application associated with a MIME type or a file. 2042 2043 It does also supply the caller with some more information about the 2044 application, like signature, app flags and whether the supplied 2045 MIME type/entry_ref already identified an application. 2046 2047 At least one of \a inType or \a ref must not be \c NULL. If \a inType is 2048 supplied, \a ref is ignored. 2049 2050 If \a ref refers to a link, it is updated with the entry_ref for the 2051 resolved entry. 2052 2053 \see FindApp() for how the application is searched. 2054 2055 \a signature is set to a string with length 0, if the found 2056 application has no signature. 2057 2058 \param inType The MIME type for which an application shall be found. 2059 May be \c NULL. 2060 \param ref The file for which an application shall be found. 2061 May be \c NULL. 2062 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2063 a reference to the found application's executable. May be \c NULL. 2064 \param signature A pointer to a pre-allocated char buffer of at 2065 least size \c B_MIME_TYPE_LENGTH to be filled with the signature of 2066 the found application. May be \c NULL. 2067 \param appFlags A pointer to a pre-allocated uint32 variable to be filled 2068 with the app flags of the found application. May be \c NULL. 2069 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2070 \c true, if the supplied file was not identifying an application, 2071 to \c false otherwise. Has no meaning, if a \a inType is supplied. 2072 May be \c NULL. 2073 2074 \return A status code. 2075 \retval B_OK Everything went fine. 2076 \retval B_BAD_VALUE \c NULL \a inType and \a ref. 2077 2078 \see FindApp() for other error codes. 2079 */ 2080 status_t 2081 BRoster::_ResolveApp(const char* inType, entry_ref* ref, 2082 entry_ref* _appRef, char* _signature, uint32* _appFlags, 2083 bool* _wasDocument) const 2084 { 2085 if ((inType == NULL && ref == NULL) 2086 || (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH)) 2087 return B_BAD_VALUE; 2088 2089 // find the app 2090 BMimeType appMeta; 2091 BFile appFile; 2092 entry_ref appRef; 2093 status_t error; 2094 2095 if (inType != NULL) { 2096 error = _TranslateType(inType, &appMeta, &appRef, &appFile); 2097 if (_wasDocument != NULL) 2098 *_wasDocument = !(appMeta == inType); 2099 } else { 2100 error = _TranslateRef(ref, &appMeta, &appRef, &appFile, 2101 _wasDocument); 2102 } 2103 2104 // create meta mime 2105 if (!fNoRegistrar && error == B_OK) { 2106 BPath path; 2107 if (path.SetTo(&appRef) == B_OK) 2108 create_app_meta_mime(path.Path(), false, true, false); 2109 } 2110 2111 // set the app hint on the type -- but only if the file has the 2112 // respective signature, otherwise unset the app hint 2113 BAppFileInfo appFileInfo; 2114 if (!fNoRegistrar && error == B_OK) { 2115 char signature[B_MIME_TYPE_LENGTH]; 2116 if (appFileInfo.SetTo(&appFile) == B_OK 2117 && appFileInfo.GetSignature(signature) == B_OK) { 2118 if (!strcasecmp(appMeta.Type(), signature)) { 2119 // Only set the app hint if there is none yet 2120 entry_ref dummyRef; 2121 if (appMeta.GetAppHint(&dummyRef) != B_OK) 2122 appMeta.SetAppHint(&appRef); 2123 } else { 2124 appMeta.SetAppHint(NULL); 2125 appMeta.SetTo(signature); 2126 } 2127 } else 2128 appMeta.SetAppHint(NULL); 2129 } 2130 2131 // set the return values 2132 if (error == B_OK) { 2133 if (_appRef) 2134 *_appRef = appRef; 2135 2136 if (_signature != NULL) { 2137 // there's no warranty, that appMeta is valid 2138 if (appMeta.IsValid()) { 2139 strlcpy(_signature, appMeta.Type(), 2140 B_MIME_TYPE_LENGTH); 2141 } else 2142 _signature[0] = '\0'; 2143 } 2144 2145 if (_appFlags != NULL) { 2146 // if an error occurs here, we don't care and just set a default 2147 // value 2148 if (appFileInfo.InitCheck() != B_OK 2149 || appFileInfo.GetAppFlags(_appFlags) != B_OK) { 2150 *_appFlags = B_REG_DEFAULT_APP_FLAGS; 2151 } 2152 } 2153 } else { 2154 // unset the ref on error 2155 if (_appRef != NULL) 2156 *_appRef = appRef; 2157 } 2158 2159 return error; 2160 } 2161 2162 2163 /*! \brief Finds an application associated with a file. 2164 2165 \a appMeta is left unmodified, if the file is executable, but has no 2166 signature. 2167 2168 \see FindApp() for how the application is searched. 2169 2170 If \a ref refers to a link, it is updated with the entry_ref for the 2171 resolved entry. 2172 2173 \param ref The file for which an application shall be found. 2174 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2175 signature of the found application. 2176 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2177 a reference to the found application's executable. 2178 \param appFile A pointer to a pre-allocated BFile to be set to the 2179 executable of the found application. 2180 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2181 \c true, if the supplied file was not identifying an application, 2182 to \c false otherwise. May be \c NULL. 2183 2184 \return A status code. 2185 \retval B_OK: Everything went fine. 2186 \retval B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile. 2187 2188 \see FindApp() for other error codes. 2189 */ 2190 status_t 2191 BRoster::_TranslateRef(entry_ref* ref, BMimeType* appMeta, 2192 entry_ref* appRef, BFile* appFile, bool* _wasDocument) const 2193 { 2194 if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL) 2195 return B_BAD_VALUE; 2196 2197 entry_ref originalRef = *ref; 2198 2199 // resolve ref, if necessary 2200 BEntry entry; 2201 status_t error = entry.SetTo(ref, false); 2202 if (error != B_OK) 2203 return error; 2204 2205 if (entry.IsSymLink()) { 2206 // ref refers to a link 2207 if (entry.SetTo(ref, true) != B_OK || entry.GetRef(ref) != B_OK) 2208 return B_LAUNCH_FAILED_NO_RESOLVE_LINK; 2209 } 2210 2211 // init node 2212 BNode node; 2213 error = node.SetTo(ref); 2214 if (error != B_OK) 2215 return error; 2216 2217 // get permissions 2218 mode_t permissions; 2219 error = node.GetPermissions(&permissions); 2220 if (error != B_OK) 2221 return error; 2222 2223 if ((permissions & S_IXUSR) != 0 && node.IsFile()) { 2224 // node is executable and a file 2225 error = appFile->SetTo(ref, B_READ_ONLY); 2226 if (error != B_OK) 2227 return error; 2228 2229 // get the app's signature via a BAppFileInfo 2230 BAppFileInfo appFileInfo; 2231 error = appFileInfo.SetTo(appFile); 2232 if (error != B_OK) 2233 return error; 2234 2235 // don't worry, if the file doesn't have a signature, just 2236 // unset the supplied object 2237 char type[B_MIME_TYPE_LENGTH]; 2238 if (appFileInfo.GetSignature(type) == B_OK) { 2239 error = appMeta->SetTo(type); 2240 if (error != B_OK) 2241 return error; 2242 } else 2243 appMeta->Unset(); 2244 2245 // If the file type indicates that the file is an application, we've 2246 // definitely got what we're looking for. 2247 bool isDocument = true; 2248 if (_GetFileType(ref, &appFileInfo, type) == B_OK 2249 && strcasecmp(type, B_APP_MIME_TYPE) == 0) { 2250 isDocument = false; 2251 } 2252 2253 // If our file is not an application executable, we probably have a 2254 // script. Check whether the file has a preferred application set. If 2255 // so, we fall through and use the preferred app instead. Otherwise 2256 // we're done. 2257 char preferredApp[B_MIME_TYPE_LENGTH]; 2258 if (!isDocument || appFileInfo.GetPreferredApp(preferredApp) != B_OK) { 2259 // If we were given a symlink, point appRef to it in case its name 2260 // or attributes are relevant. 2261 *appRef = originalRef; 2262 if (_wasDocument != NULL) 2263 *_wasDocument = isDocument; 2264 2265 return B_OK; 2266 } 2267 2268 // Executable file, but not an application, and it has a preferred 2269 // application set. Fall through... 2270 } 2271 2272 // the node is not exectuable or not a file 2273 // init a node info 2274 BNodeInfo nodeInfo; 2275 error = nodeInfo.SetTo(&node); 2276 if (error != B_OK) 2277 return error; 2278 2279 // if the file has a preferred app, let _TranslateType() find 2280 // it for us 2281 char preferredApp[B_MIME_TYPE_LENGTH]; 2282 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 2283 && _TranslateType(preferredApp, appMeta, appRef, appFile) == B_OK) { 2284 if (_wasDocument != NULL) 2285 *_wasDocument = true; 2286 2287 return B_OK; 2288 } 2289 2290 // no preferred app or existing one was not found -- we 2291 // need to get the file's type 2292 2293 // get the type from the file 2294 char fileType[B_MIME_TYPE_LENGTH]; 2295 error = _GetFileType(ref, &nodeInfo, fileType); 2296 if (error != B_OK) 2297 return error; 2298 2299 // now let _TranslateType() do the actual work 2300 error = _TranslateType(fileType, appMeta, appRef, appFile); 2301 if (error != B_OK) 2302 return error; 2303 2304 if (_wasDocument != NULL) 2305 *_wasDocument = true; 2306 2307 return B_OK; 2308 } 2309 2310 2311 /*! Finds an application associated with a MIME type. 2312 2313 \see FindApp() for how the application is searched. 2314 2315 \param mimeType The MIME type for which an application shall be found. 2316 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2317 signature of the found application. 2318 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2319 a reference to the found application's executable. 2320 \param appFile A pointer to a pre-allocated BFile to be set to the 2321 executable of the found application. 2322 2323 \return A status code. 2324 \retval B_OK Everything went fine. 2325 \retval B_BAD_VALUE \c NULL \a mimeType, \a appMeta, \a appRef or 2326 \a appFile. 2327 2328 \see FindApp() for other error codes. 2329 */ 2330 status_t 2331 BRoster::_TranslateType(const char* mimeType, BMimeType* appMeta, 2332 entry_ref* appRef, BFile* appFile) const 2333 { 2334 if (mimeType == NULL || appMeta == NULL || appRef == NULL 2335 || appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) { 2336 return B_BAD_VALUE; 2337 } 2338 2339 // Create a BMimeType and check, if the type is installed. 2340 BMimeType type; 2341 status_t error = type.SetTo(mimeType); 2342 2343 // Get the preferred apps from the sub and super type. 2344 char primarySignature[B_MIME_TYPE_LENGTH]; 2345 char secondarySignature[B_MIME_TYPE_LENGTH]; 2346 primarySignature[0] = '\0'; 2347 secondarySignature[0] = '\0'; 2348 2349 if (error == B_OK) { 2350 BMimeType superType; 2351 if (type.GetSupertype(&superType) == B_OK) 2352 superType.GetPreferredApp(secondarySignature); 2353 if (type.IsInstalled()) { 2354 if (type.GetPreferredApp(primarySignature) != B_OK) { 2355 // The type is installed, but has no preferred app. 2356 primarySignature[0] = '\0'; 2357 } else if (!strcmp(primarySignature, secondarySignature)) { 2358 // Both types have the same preferred app, there is 2359 // no point in testing it twice. 2360 secondarySignature[0] = '\0'; 2361 } 2362 } else { 2363 // The type is not installed. We assume it is an app signature. 2364 strlcpy(primarySignature, mimeType, sizeof(primarySignature)); 2365 } 2366 } 2367 2368 // We will use this BMessage "signatures" to hold all supporting apps 2369 // so we can iterator over them in the preferred order. We include 2370 // the supporting apps in such a way that the configured preferred 2371 // applications for the MIME type are in front of other supporting 2372 // applications for the sub and the super type respectively. 2373 const char* kSigField = "applications"; 2374 BMessage signatures; 2375 bool addedSecondarySignature = false; 2376 if (error == B_OK) { 2377 if (primarySignature[0] != '\0') 2378 error = signatures.AddString(kSigField, primarySignature); 2379 else { 2380 // If there is a preferred app configured for the super type, 2381 // but no preferred type for the sub-type, add the preferred 2382 // super type handler in front of any other handlers. This way 2383 // we fall-back to non-preferred but supporting apps only in the 2384 // case when there is a preferred handler for the sub-type but 2385 // it cannot be resolved (misconfiguration). 2386 if (secondarySignature[0] != '\0') { 2387 error = signatures.AddString(kSigField, secondarySignature); 2388 addedSecondarySignature = true; 2389 } 2390 } 2391 } 2392 2393 BMessage supportingSignatures; 2394 if (error == B_OK 2395 && type.GetSupportingApps(&supportingSignatures) == B_OK) { 2396 int32 subCount; 2397 if (supportingSignatures.FindInt32("be:sub", &subCount) != B_OK) 2398 subCount = 0; 2399 // Add all signatures with direct support for the sub-type. 2400 const char* supportingType; 2401 if (!addedSecondarySignature) { 2402 // Try to add the secondarySignature in front of all other 2403 // supporting apps, if we find it among those. 2404 for (int32 i = 0; error == B_OK && i < subCount 2405 && supportingSignatures.FindString(kSigField, i, 2406 &supportingType) == B_OK; i++) { 2407 if (strcmp(primarySignature, supportingType) != 0 2408 && strcmp(secondarySignature, supportingType) == 0) { 2409 error = signatures.AddString(kSigField, supportingType); 2410 addedSecondarySignature = true; 2411 break; 2412 } 2413 } 2414 } 2415 2416 for (int32 i = 0; error == B_OK && i < subCount 2417 && supportingSignatures.FindString(kSigField, i, 2418 &supportingType) == B_OK; i++) { 2419 if (strcmp(primarySignature, supportingType) != 0 2420 && strcmp(secondarySignature, supportingType) != 0) { 2421 error = signatures.AddString(kSigField, supportingType); 2422 } 2423 } 2424 2425 // Add the preferred type of the super type here before adding 2426 // the other types supporting the super type, but only if we have 2427 // not already added it in case there was no preferred app for the 2428 // sub-type configured. 2429 if (error == B_OK && !addedSecondarySignature 2430 && secondarySignature[0] != '\0') { 2431 error = signatures.AddString(kSigField, secondarySignature); 2432 } 2433 2434 // Add all signatures with support for the super-type. 2435 for (int32 i = subCount; error == B_OK 2436 && supportingSignatures.FindString(kSigField, i, 2437 &supportingType) == B_OK; i++) { 2438 // Don't add the signature if it's one of the preferred apps 2439 // already. 2440 if (strcmp(primarySignature, supportingType) != 0 2441 && strcmp(secondarySignature, supportingType) != 0) { 2442 error = signatures.AddString(kSigField, supportingType); 2443 } 2444 } 2445 } else { 2446 // Failed to get supporting apps, just add the preferred apps. 2447 if (error == B_OK && secondarySignature[0] != '\0') 2448 error = signatures.AddString(kSigField, secondarySignature); 2449 } 2450 2451 if (error != B_OK) 2452 return error; 2453 2454 // Set an error in case we can't resolve a single supporting app. 2455 error = B_LAUNCH_FAILED_NO_PREFERRED_APP; 2456 2457 // See if we can find a good application that is valid from the messege. 2458 const char* signature; 2459 for (int32 i = 0; 2460 signatures.FindString(kSigField, i, &signature) == B_OK; i++) { 2461 if (signature[0] == '\0') 2462 continue; 2463 2464 error = appMeta->SetTo(signature); 2465 2466 // Check, whether the signature is installed and has an app hint 2467 bool appFound = false; 2468 if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) { 2469 // Resolve symbolic links, if necessary 2470 BEntry entry; 2471 if (entry.SetTo(appRef, true) == B_OK && entry.IsFile() 2472 && entry.GetRef(appRef) == B_OK) { 2473 appFound = true; 2474 } else { 2475 // Bad app hint -- remove it 2476 appMeta->SetAppHint(NULL); 2477 } 2478 } 2479 2480 // In case there is no app hint or it is invalid, we need to query for 2481 // the app. 2482 if (error == B_OK && !appFound) 2483 error = query_for_app(appMeta->Type(), appRef); 2484 2485 if (error == B_OK) 2486 error = appFile->SetTo(appRef, B_READ_ONLY); 2487 2488 // check, whether the app can be used 2489 if (error == B_OK) 2490 error = can_app_be_used(appRef); 2491 2492 if (error == B_OK) 2493 break; 2494 } 2495 2496 return error; 2497 } 2498 2499 2500 /*! Gets the type of a file either from the node info or by sniffing. 2501 2502 The method first tries to get the file type from the supplied node info. If 2503 that didn't work, the given entry ref is sniffed. 2504 2505 \param file An entry_ref referring to the file in question. 2506 \param nodeInfo A BNodeInfo initialized to the file. 2507 \param mimeType A pointer to a pre-allocated char buffer of at least size 2508 \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for 2509 the file. 2510 2511 \return A status code. 2512 \retval B_OK Everything went fine. 2513 \retval B_BAD_VALUE \c NULL \a file, \a nodeInfo or \a mimeType. 2514 */ 2515 status_t 2516 BRoster::_GetFileType(const entry_ref* file, BNodeInfo* nodeInfo, 2517 char* mimeType) const 2518 { 2519 // first try the node info 2520 if (nodeInfo->GetType(mimeType) == B_OK) 2521 return B_OK; 2522 2523 if (fNoRegistrar) 2524 return B_NO_INIT; 2525 2526 // Try to update the file's MIME info and just read the updated type. 2527 // If that fails, sniff manually. 2528 BPath path; 2529 if (path.SetTo(file) != B_OK 2530 || update_mime_info(path.Path(), false, true, false) != B_OK 2531 || nodeInfo->GetType(mimeType) != B_OK) { 2532 BMimeType type; 2533 status_t error = BMimeType::GuessMimeType(file, &type); 2534 if (error != B_OK) 2535 return error; 2536 2537 if (!type.IsValid()) 2538 return B_BAD_VALUE; 2539 2540 strlcpy(mimeType, type.Type(), B_MIME_TYPE_LENGTH); 2541 } 2542 2543 return B_OK; 2544 } 2545 2546 2547 /*! Sends messages to a running team. 2548 2549 In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED, 2550 \c B_READY_TO_RUN and other, arbitrary, ones. 2551 2552 If \a messageList is not \c NULL or empty, those messages are sent first, 2553 then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally 2554 \c B_READ_TO_RUN. 2555 2556 \c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains 2557 more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not 2558 \c NULL. 2559 2560 The ownership of all supplied objects retains to the caller. 2561 2562 \param team The team ID of the target application. 2563 \param argc Number of elements in \a args. 2564 \param args Argument vector to be sent to the target. May be \c NULL. 2565 \param messageList List of BMessages to be sent to the target. May be 2566 \c NULL or empty. 2567 \param ref entry_ref to be sent to the target. May be \c NULL. 2568 \param alreadyRunning \c true, if the target app is not newly launched, 2569 but was already running, \c false otherwise (a \c B_READY_TO_RUN 2570 message will be sent in this case). 2571 2572 \return \c B_OK if everything went fine, or an error code otherwise. 2573 */ 2574 status_t 2575 BRoster::_SendToRunning(team_id team, int argc, const char* const* args, 2576 const BList* messageList, const entry_ref* ref, 2577 bool alreadyRunning) const 2578 { 2579 status_t error = B_OK; 2580 2581 // Construct a messenger to the app: We can't use the public constructor, 2582 // since the target application may be B_ARGV_ONLY. 2583 app_info info; 2584 error = GetRunningAppInfo(team, &info); 2585 if (error == B_OK) { 2586 BMessenger messenger; 2587 BMessenger::Private(messenger).SetTo(team, info.port, 2588 B_PREFERRED_TOKEN); 2589 2590 // send messages from the list 2591 if (messageList != NULL) { 2592 for (int32 i = 0; 2593 BMessage* message = (BMessage*)messageList->ItemAt(i); 2594 i++) { 2595 messenger.SendMessage(message); 2596 } 2597 } 2598 2599 // send B_ARGV_RECEIVED or B_REFS_RECEIVED or B_SILENT_RELAUNCH 2600 // (if already running) 2601 if (args != NULL && argc > 1) { 2602 BMessage message(B_ARGV_RECEIVED); 2603 message.AddInt32("argc", argc); 2604 for (int32 i = 0; i < argc; i++) 2605 message.AddString("argv", args[i]); 2606 2607 // also add current working directory 2608 char cwd[B_PATH_NAME_LENGTH]; 2609 if (getcwd(cwd, B_PATH_NAME_LENGTH) != NULL) 2610 message.AddString("cwd", cwd); 2611 2612 messenger.SendMessage(&message); 2613 } else if (ref != NULL) { 2614 DBG(OUT("_SendToRunning : B_REFS_RECEIVED\n")); 2615 BMessage message(B_REFS_RECEIVED); 2616 message.AddRef("refs", ref); 2617 messenger.SendMessage(&message); 2618 } else if (alreadyRunning && (!messageList || messageList->IsEmpty())) 2619 messenger.SendMessage(B_SILENT_RELAUNCH); 2620 2621 if (!alreadyRunning) { 2622 // send B_READY_TO_RUN 2623 DBG(OUT("_SendToRunning : B_READY_TO_RUN\n")); 2624 messenger.SendMessage(B_READY_TO_RUN); 2625 } 2626 } 2627 2628 return error; 2629 } 2630 2631 2632 /*! Allows to use certain functionality of the BRoster class without 2633 accessing the registrar. 2634 */ 2635 void 2636 BRoster::_SetWithoutRegistrar(bool noRegistrar) 2637 { 2638 fNoRegistrar = noRegistrar; 2639 } 2640 2641 2642 void 2643 BRoster::_InitMessenger() 2644 { 2645 DBG(OUT("BRoster::InitMessengers()\n")); 2646 2647 // find the registrar port 2648 2649 #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST 2650 BMessage data; 2651 if (BLaunchRoster().GetData(B_REGISTRAR_SIGNATURE, data) == B_OK) { 2652 port_id port = data.GetInt32("port", -1); 2653 team_id team = data.GetInt32("team", -1); 2654 2655 if (port >= 0 && team != current_team()) { 2656 // Make sure we aren't the registrar ourselves. 2657 2658 DBG(OUT(" found roster port\n")); 2659 2660 BMessenger::Private(fMessenger).SetTo(team, port, 2661 B_PREFERRED_TOKEN); 2662 } 2663 } 2664 #else 2665 port_id rosterPort = find_port(B_REGISTRAR_PORT_NAME); 2666 port_info info; 2667 if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) { 2668 DBG(OUT(" found roster port\n")); 2669 2670 BMessenger::Private(fMessenger).SetTo(info.team, rosterPort, 2671 B_PREFERRED_TOKEN); 2672 } 2673 #endif 2674 2675 DBG(OUT("BRoster::InitMessengers() done\n")); 2676 } 2677 2678 2679 /*static*/ status_t 2680 BRoster::_InitMimeMessenger(void* data) 2681 { 2682 BRoster* roster = (BRoster*)data; 2683 2684 // ask for the MIME messenger 2685 // Generous 1s + 5s timeouts. It could actually be synchronous, but 2686 // timeouts allow us to debug the registrar main thread. 2687 BMessage request(B_REG_GET_MIME_MESSENGER); 2688 BMessage reply; 2689 status_t error = roster->fMessenger.SendMessage(&request, &reply, 2690 1000000LL, 5000000LL); 2691 if (error == B_OK && reply.what == B_REG_SUCCESS) { 2692 DBG(OUT(" got reply from roster\n")); 2693 reply.FindMessenger("messenger", &roster->fMimeMessenger); 2694 } else { 2695 DBG(OUT(" no (useful) reply from roster: error: %" B_PRIx32 ": %s\n", 2696 error, strerror(error))); 2697 if (error == B_OK) 2698 DBG(reply.PrintToStream()); 2699 } 2700 2701 return error; 2702 } 2703 2704 2705 BMessenger& 2706 BRoster::_MimeMessenger() 2707 { 2708 __init_once(&fMimeMessengerInitOnce, &_InitMimeMessenger, this); 2709 return fMimeMessenger; 2710 } 2711 2712 2713 /*! Sends a request to the roster to add the application with the 2714 given signature to the front of the recent apps list. 2715 */ 2716 void 2717 BRoster::_AddToRecentApps(const char* signature) const 2718 { 2719 status_t error = B_OK; 2720 // compose the request message 2721 BMessage request(B_REG_ADD_TO_RECENT_APPS); 2722 if (error == B_OK) 2723 error = request.AddString("app sig", signature); 2724 2725 // send the request 2726 BMessage reply; 2727 if (error == B_OK) 2728 error = fMessenger.SendMessage(&request, &reply); 2729 2730 // evaluate the reply 2731 status_t result; 2732 if (error == B_OK) { 2733 error = reply.what == B_REG_RESULT 2734 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2735 } 2736 2737 if (error == B_OK) 2738 error = reply.FindInt32("result", &result); 2739 2740 if (error == B_OK) 2741 error = result; 2742 2743 // Nothing to return... how sad :-( 2744 //return error; 2745 } 2746 2747 2748 //! Sends a request to the roster to clear the recent documents list. 2749 void 2750 BRoster::_ClearRecentDocuments() const 2751 { 2752 BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS); 2753 BMessage reply; 2754 fMessenger.SendMessage(&request, &reply); 2755 } 2756 2757 2758 //! Sends a request to the roster to clear the recent documents list. 2759 void 2760 BRoster::_ClearRecentFolders() const 2761 { 2762 BMessage request(B_REG_CLEAR_RECENT_FOLDERS); 2763 BMessage reply; 2764 fMessenger.SendMessage(&request, &reply); 2765 } 2766 2767 2768 //! \brief Sends a request to the roster to clear the recent documents list. 2769 void 2770 BRoster::_ClearRecentApps() const 2771 { 2772 BMessage request(B_REG_CLEAR_RECENT_APPS); 2773 BMessage reply; 2774 fMessenger.SendMessage(&request, &reply); 2775 } 2776 2777 2778 /*! Loads the system's recently used document, folder, and 2779 application lists from the specified file. 2780 2781 \note The current lists are cleared before loading the new lists 2782 2783 \param filename The name of the file to load from 2784 */ 2785 void 2786 BRoster::_LoadRecentLists(const char* filename) const 2787 { 2788 status_t error = B_OK; 2789 2790 // compose the request message 2791 BMessage request(B_REG_LOAD_RECENT_LISTS); 2792 if (error == B_OK) 2793 error = request.AddString("filename", filename); 2794 2795 // send the request 2796 BMessage reply; 2797 if (error == B_OK) 2798 error = fMessenger.SendMessage(&request, &reply); 2799 2800 // evaluate the reply 2801 status_t result; 2802 if (error == B_OK) { 2803 error = reply.what == B_REG_RESULT 2804 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2805 } 2806 if (error == B_OK) 2807 error = reply.FindInt32("result", &result); 2808 2809 if (error == B_OK) 2810 error = result; 2811 2812 // Nothing to return... how sad :-( 2813 //return error; 2814 } 2815 2816 2817 /*! Saves the system's recently used document, folder, and 2818 application lists to the specified file. 2819 2820 \param filename The name of the file to save to 2821 */ 2822 void 2823 BRoster::_SaveRecentLists(const char* filename) const 2824 { 2825 status_t error = B_OK; 2826 2827 // compose the request message 2828 BMessage request(B_REG_SAVE_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