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 1876 do { 1877 // find the app 1878 entry_ref appRef; 1879 char signature[B_MIME_TYPE_LENGTH]; 1880 error = _ResolveApp(mimeType, docRef, &appRef, signature, 1881 &appFlags, &wasDocument); 1882 DBG(OUT(" find app: %s (%" B_PRIx32 ") %s \n", strerror(error), error, 1883 signature)); 1884 if (error != B_OK) 1885 return error; 1886 1887 // build an argument vector 1888 error = argVector.Init(argc, args, &appRef, 1889 wasDocument ? docRef : NULL); 1890 DBG(OUT(" build argv: %s (%" B_PRIx32 ")\n", strerror(error), error)); 1891 if (error != B_OK) 1892 return error; 1893 1894 // pre-register the app (but ignore scipts) 1895 app_info appInfo; 1896 bool isScript = wasDocument && docRef != NULL && *docRef == appRef; 1897 if (!isScript && !fNoRegistrar) { 1898 error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1, 1899 false, &appToken, &team); 1900 if (error == B_ALREADY_RUNNING) { 1901 DBG(OUT(" already running\n")); 1902 alreadyRunning = true; 1903 1904 // get the app flags for the running application 1905 error = _IsAppRegistered(&appRef, team, appToken, NULL, 1906 &appInfo); 1907 if (error == B_OK) { 1908 otherAppFlags = appInfo.flags; 1909 appPort = appInfo.port; 1910 team = appInfo.team; 1911 } 1912 } 1913 DBG(OUT(" pre-register: %s (%" B_PRIx32 ")\n", strerror(error), 1914 error)); 1915 } 1916 1917 // launch the app 1918 if (error == B_OK && !alreadyRunning) { 1919 DBG(OUT(" token: %" B_PRIu32 "\n", appToken)); 1920 // load the app image 1921 appThread = load_image(argVector.Count(), 1922 const_cast<const char**>(argVector.Args()), environment); 1923 1924 // get the app team 1925 if (appThread >= 0) { 1926 thread_info threadInfo; 1927 error = get_thread_info(appThread, &threadInfo); 1928 if (error == B_OK) 1929 team = threadInfo.team; 1930 } else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE) 1931 error = B_LAUNCH_FAILED_EXECUTABLE; 1932 else 1933 error = appThread; 1934 1935 DBG(OUT(" load image: %s (%" B_PRIx32 ")\n", strerror(error), 1936 error)); 1937 // finish the registration 1938 if (error == B_OK && !isScript && !fNoRegistrar) 1939 error = _SetThreadAndTeam(appToken, appThread, team, &appPort); 1940 1941 DBG(OUT(" set thread and team: %s (%" B_PRIx32 ")\n", 1942 strerror(error), error)); 1943 // resume the launched team 1944 if (error == B_OK && !launchSuspended) 1945 error = resume_thread(appThread); 1946 1947 DBG(OUT(" resume thread: %s (%" B_PRIx32 ")\n", strerror(error), 1948 error)); 1949 // on error: kill the launched team and unregister the app 1950 if (error != B_OK) { 1951 if (appThread >= 0) 1952 kill_thread(appThread); 1953 1954 if (!isScript) { 1955 if (!fNoRegistrar) 1956 _RemovePreRegApp(appToken); 1957 1958 if (!wasDocument) { 1959 // Remove app hint if it's this one 1960 BMimeType appType(signature); 1961 entry_ref hintRef; 1962 1963 if (appType.InitCheck() == B_OK 1964 && appType.GetAppHint(&hintRef) == B_OK 1965 && appRef == hintRef) { 1966 appType.SetAppHint(NULL); 1967 // try again 1968 continue; 1969 } 1970 } 1971 } 1972 } 1973 } 1974 } while (false); 1975 1976 if (alreadyRunning && current_team() == team) { 1977 // The target team is calling us, so we don't send it the message 1978 // to prevent an endless loop 1979 error = B_BAD_VALUE; 1980 } 1981 1982 // send "on launch" messages 1983 if (error == B_OK && !fNoRegistrar) { 1984 // If the target app is B_ARGV_ONLY, only if it is newly launched 1985 // messages are sent to it (namely B_ARGV_RECEIVED and B_READY_TO_RUN). 1986 // An already running B_ARGV_ONLY app won't get any messages. 1987 bool argvOnly = (appFlags & B_ARGV_ONLY) != 0 1988 || (alreadyRunning && (otherAppFlags & B_ARGV_ONLY) != 0); 1989 const BList* _messageList = (argvOnly ? NULL : messageList); 1990 // don't send ref, if it refers to the app or is included in the 1991 // argument vector 1992 const entry_ref* _ref = argvOnly || !wasDocument 1993 || argVector.Count() > 1 ? NULL : docRef; 1994 if (!(argvOnly && alreadyRunning)) { 1995 _SendToRunning(team, argVector.Count(), argVector.Args(), 1996 _messageList, _ref, alreadyRunning); 1997 } 1998 } 1999 2000 // set return values 2001 if (error == B_OK) { 2002 if (alreadyRunning) 2003 error = B_ALREADY_RUNNING; 2004 else if (_appTeam) 2005 *_appTeam = team; 2006 2007 if (_appThread != NULL) 2008 *_appThread = appThread; 2009 if (_appPort != NULL) 2010 *_appPort = appPort; 2011 if (_appToken != NULL) 2012 *_appToken = appToken; 2013 } 2014 2015 DBG(OUT("BRoster::_LaunchApp() done: %s (%" B_PRIx32 ")\n", 2016 strerror(error), error)); 2017 2018 return error; 2019 } 2020 2021 2022 void 2023 BRoster::_SetAppFlags(team_id team, uint32 flags) const 2024 { 2025 } 2026 2027 2028 void 2029 BRoster::_DumpRoster() const 2030 { 2031 } 2032 2033 2034 /*! Finds an application associated with a MIME type or a file. 2035 2036 It does also supply the caller with some more information about the 2037 application, like signature, app flags and whether the supplied 2038 MIME type/entry_ref already identified an application. 2039 2040 At least one of \a inType or \a ref must not be \c NULL. If \a inType is 2041 supplied, \a ref is ignored. 2042 2043 If \a ref refers to a link, it is updated with the entry_ref for the 2044 resolved entry. 2045 2046 \see FindApp() for how the application is searched. 2047 2048 \a signature is set to a string with length 0, if the found 2049 application has no signature. 2050 2051 \param inType The MIME type for which an application shall be found. 2052 May be \c NULL. 2053 \param ref The file for which an application shall be found. 2054 May be \c NULL. 2055 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2056 a reference to the found application's executable. May be \c NULL. 2057 \param signature A pointer to a pre-allocated char buffer of at 2058 least size \c B_MIME_TYPE_LENGTH to be filled with the signature of 2059 the found application. May be \c NULL. 2060 \param appFlags A pointer to a pre-allocated uint32 variable to be filled 2061 with the app flags of the found application. May be \c NULL. 2062 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2063 \c true, if the supplied file was not identifying an application, 2064 to \c false otherwise. Has no meaning, if a \a inType is supplied. 2065 May be \c NULL. 2066 2067 \return A status code. 2068 \retval B_OK Everything went fine. 2069 \retval B_BAD_VALUE \c NULL \a inType and \a ref. 2070 2071 \see FindApp() for other error codes. 2072 */ 2073 status_t 2074 BRoster::_ResolveApp(const char* inType, entry_ref* ref, 2075 entry_ref* _appRef, char* _signature, uint32* _appFlags, 2076 bool* _wasDocument) const 2077 { 2078 if ((inType == NULL && ref == NULL) 2079 || (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH)) 2080 return B_BAD_VALUE; 2081 2082 // find the app 2083 BMimeType appMeta; 2084 BFile appFile; 2085 entry_ref appRef; 2086 status_t error; 2087 2088 if (inType != NULL) { 2089 error = _TranslateType(inType, &appMeta, &appRef, &appFile); 2090 if (_wasDocument != NULL) 2091 *_wasDocument = !(appMeta == inType); 2092 } else { 2093 error = _TranslateRef(ref, &appMeta, &appRef, &appFile, 2094 _wasDocument); 2095 } 2096 2097 // create meta mime 2098 if (!fNoRegistrar && error == B_OK) { 2099 BPath path; 2100 if (path.SetTo(&appRef) == B_OK) 2101 create_app_meta_mime(path.Path(), false, true, false); 2102 } 2103 2104 // set the app hint on the type -- but only if the file has the 2105 // respective signature, otherwise unset the app hint 2106 BAppFileInfo appFileInfo; 2107 if (!fNoRegistrar && error == B_OK) { 2108 char signature[B_MIME_TYPE_LENGTH]; 2109 if (appFileInfo.SetTo(&appFile) == B_OK 2110 && appFileInfo.GetSignature(signature) == B_OK) { 2111 if (!strcasecmp(appMeta.Type(), signature)) { 2112 // Only set the app hint if there is none yet 2113 entry_ref dummyRef; 2114 if (appMeta.GetAppHint(&dummyRef) != B_OK) 2115 appMeta.SetAppHint(&appRef); 2116 } else { 2117 appMeta.SetAppHint(NULL); 2118 appMeta.SetTo(signature); 2119 } 2120 } else 2121 appMeta.SetAppHint(NULL); 2122 } 2123 2124 // set the return values 2125 if (error == B_OK) { 2126 if (_appRef) 2127 *_appRef = appRef; 2128 2129 if (_signature != NULL) { 2130 // there's no warranty, that appMeta is valid 2131 if (appMeta.IsValid()) { 2132 strlcpy(_signature, appMeta.Type(), 2133 B_MIME_TYPE_LENGTH); 2134 } else 2135 _signature[0] = '\0'; 2136 } 2137 2138 if (_appFlags != NULL) { 2139 // if an error occurs here, we don't care and just set a default 2140 // value 2141 if (appFileInfo.InitCheck() != B_OK 2142 || appFileInfo.GetAppFlags(_appFlags) != B_OK) { 2143 *_appFlags = B_REG_DEFAULT_APP_FLAGS; 2144 } 2145 } 2146 } else { 2147 // unset the ref on error 2148 if (_appRef != NULL) 2149 *_appRef = appRef; 2150 } 2151 2152 return error; 2153 } 2154 2155 2156 /*! \brief Finds an application associated with a file. 2157 2158 \a appMeta is left unmodified, if the file is executable, but has no 2159 signature. 2160 2161 \see FindApp() for how the application is searched. 2162 2163 If \a ref refers to a link, it is updated with the entry_ref for the 2164 resolved entry. 2165 2166 \param ref The file for which an application shall be found. 2167 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2168 signature of the found application. 2169 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2170 a reference to the found application's executable. 2171 \param appFile A pointer to a pre-allocated BFile to be set to the 2172 executable of the found application. 2173 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2174 \c true, if the supplied file was not identifying an application, 2175 to \c false otherwise. May be \c NULL. 2176 2177 \return A status code. 2178 \retval B_OK: Everything went fine. 2179 \retval B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile. 2180 2181 \see FindApp() for other error codes. 2182 */ 2183 status_t 2184 BRoster::_TranslateRef(entry_ref* ref, BMimeType* appMeta, 2185 entry_ref* appRef, BFile* appFile, bool* _wasDocument) const 2186 { 2187 if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL) 2188 return B_BAD_VALUE; 2189 2190 // resolve ref, if necessary 2191 BEntry entry; 2192 status_t error = entry.SetTo(ref, false); 2193 if (error != B_OK) 2194 return error; 2195 2196 if (entry.IsSymLink()) { 2197 // ref refers to a link 2198 if (entry.SetTo(ref, true) != B_OK || entry.GetRef(ref) != B_OK) 2199 return B_LAUNCH_FAILED_NO_RESOLVE_LINK; 2200 } 2201 2202 // init node 2203 BNode node; 2204 error = node.SetTo(ref); 2205 if (error != B_OK) 2206 return error; 2207 2208 // get permissions 2209 mode_t permissions; 2210 error = node.GetPermissions(&permissions); 2211 if (error != B_OK) 2212 return error; 2213 2214 if ((permissions & S_IXUSR) != 0 && node.IsFile()) { 2215 // node is executable and a file 2216 error = appFile->SetTo(ref, B_READ_ONLY); 2217 if (error != B_OK) 2218 return error; 2219 2220 // get the app's signature via a BAppFileInfo 2221 BAppFileInfo appFileInfo; 2222 error = appFileInfo.SetTo(appFile); 2223 if (error != B_OK) 2224 return error; 2225 2226 // don't worry, if the file doesn't have a signature, just 2227 // unset the supplied object 2228 char type[B_MIME_TYPE_LENGTH]; 2229 if (appFileInfo.GetSignature(type) == B_OK) { 2230 error = appMeta->SetTo(type); 2231 if (error != B_OK) 2232 return error; 2233 } else 2234 appMeta->Unset(); 2235 2236 // If the file type indicates that the file is an application, we've 2237 // definitely got what we're looking for. 2238 bool isDocument = true; 2239 if (_GetFileType(ref, &appFileInfo, type) == B_OK 2240 && strcasecmp(type, B_APP_MIME_TYPE) == 0) { 2241 isDocument = false; 2242 } 2243 2244 // If our file is not an application executable, we probably have a 2245 // script. Check whether the file has a preferred application set. If 2246 // so, we fall through and use the preferred app instead. Otherwise 2247 // we're done. 2248 char preferredApp[B_MIME_TYPE_LENGTH]; 2249 if (!isDocument || appFileInfo.GetPreferredApp(preferredApp) != B_OK) { 2250 *appRef = *ref; 2251 if (_wasDocument != NULL) 2252 *_wasDocument = isDocument; 2253 2254 return B_OK; 2255 } 2256 2257 // Executable file, but not an application, and it has a preferred 2258 // application set. Fall through... 2259 } 2260 2261 // the node is not exectuable or not a file 2262 // init a node info 2263 BNodeInfo nodeInfo; 2264 error = nodeInfo.SetTo(&node); 2265 if (error != B_OK) 2266 return error; 2267 2268 // if the file has a preferred app, let _TranslateType() find 2269 // it for us 2270 char preferredApp[B_MIME_TYPE_LENGTH]; 2271 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 2272 && _TranslateType(preferredApp, appMeta, appRef, appFile) == B_OK) { 2273 if (_wasDocument != NULL) 2274 *_wasDocument = true; 2275 2276 return B_OK; 2277 } 2278 2279 // no preferred app or existing one was not found -- we 2280 // need to get the file's type 2281 2282 // get the type from the file 2283 char fileType[B_MIME_TYPE_LENGTH]; 2284 error = _GetFileType(ref, &nodeInfo, fileType); 2285 if (error != B_OK) 2286 return error; 2287 2288 // now let _TranslateType() do the actual work 2289 error = _TranslateType(fileType, appMeta, appRef, appFile); 2290 if (error != B_OK) 2291 return error; 2292 2293 if (_wasDocument != NULL) 2294 *_wasDocument = true; 2295 2296 return B_OK; 2297 } 2298 2299 2300 /*! Finds an application associated with a MIME type. 2301 2302 \see FindApp() for how the application is searched. 2303 2304 \param mimeType The MIME type for which an application shall be found. 2305 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2306 signature of the found application. 2307 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2308 a reference to the found application's executable. 2309 \param appFile A pointer to a pre-allocated BFile to be set to the 2310 executable of the found application. 2311 2312 \return A status code. 2313 \retval B_OK Everything went fine. 2314 \retval B_BAD_VALUE \c NULL \a mimeType, \a appMeta, \a appRef or 2315 \a appFile. 2316 2317 \see FindApp() for other error codes. 2318 */ 2319 status_t 2320 BRoster::_TranslateType(const char* mimeType, BMimeType* appMeta, 2321 entry_ref* appRef, BFile* appFile) const 2322 { 2323 if (mimeType == NULL || appMeta == NULL || appRef == NULL 2324 || appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) { 2325 return B_BAD_VALUE; 2326 } 2327 2328 // Create a BMimeType and check, if the type is installed. 2329 BMimeType type; 2330 status_t error = type.SetTo(mimeType); 2331 2332 // Get the preferred apps from the sub and super type. 2333 char primarySignature[B_MIME_TYPE_LENGTH]; 2334 char secondarySignature[B_MIME_TYPE_LENGTH]; 2335 primarySignature[0] = '\0'; 2336 secondarySignature[0] = '\0'; 2337 2338 if (error == B_OK) { 2339 BMimeType superType; 2340 if (type.GetSupertype(&superType) == B_OK) 2341 superType.GetPreferredApp(secondarySignature); 2342 if (type.IsInstalled()) { 2343 if (type.GetPreferredApp(primarySignature) != B_OK) { 2344 // The type is installed, but has no preferred app. 2345 primarySignature[0] = '\0'; 2346 } else if (!strcmp(primarySignature, secondarySignature)) { 2347 // Both types have the same preferred app, there is 2348 // no point in testing it twice. 2349 secondarySignature[0] = '\0'; 2350 } 2351 } else { 2352 // The type is not installed. We assume it is an app signature. 2353 strlcpy(primarySignature, mimeType, sizeof(primarySignature)); 2354 } 2355 } 2356 2357 // We will use this BMessage "signatures" to hold all supporting apps 2358 // so we can iterator over them in the preferred order. We include 2359 // the supporting apps in such a way that the configured preferred 2360 // applications for the MIME type are in front of other supporting 2361 // applications for the sub and the super type respectively. 2362 const char* kSigField = "applications"; 2363 BMessage signatures; 2364 bool addedSecondarySignature = false; 2365 if (error == B_OK) { 2366 if (primarySignature[0] != '\0') 2367 error = signatures.AddString(kSigField, primarySignature); 2368 else { 2369 // If there is a preferred app configured for the super type, 2370 // but no preferred type for the sub-type, add the preferred 2371 // super type handler in front of any other handlers. This way 2372 // we fall-back to non-preferred but supporting apps only in the 2373 // case when there is a preferred handler for the sub-type but 2374 // it cannot be resolved (misconfiguration). 2375 if (secondarySignature[0] != '\0') { 2376 error = signatures.AddString(kSigField, secondarySignature); 2377 addedSecondarySignature = true; 2378 } 2379 } 2380 } 2381 2382 BMessage supportingSignatures; 2383 if (error == B_OK 2384 && type.GetSupportingApps(&supportingSignatures) == B_OK) { 2385 int32 subCount; 2386 if (supportingSignatures.FindInt32("be:sub", &subCount) != B_OK) 2387 subCount = 0; 2388 // Add all signatures with direct support for the sub-type. 2389 const char* supportingType; 2390 if (!addedSecondarySignature) { 2391 // Try to add the secondarySignature in front of all other 2392 // supporting apps, if we find it among those. 2393 for (int32 i = 0; error == B_OK && i < subCount 2394 && supportingSignatures.FindString(kSigField, i, 2395 &supportingType) == B_OK; i++) { 2396 if (strcmp(primarySignature, supportingType) != 0 2397 && strcmp(secondarySignature, supportingType) == 0) { 2398 error = signatures.AddString(kSigField, supportingType); 2399 addedSecondarySignature = true; 2400 break; 2401 } 2402 } 2403 } 2404 2405 for (int32 i = 0; error == B_OK && i < subCount 2406 && supportingSignatures.FindString(kSigField, i, 2407 &supportingType) == B_OK; i++) { 2408 if (strcmp(primarySignature, supportingType) != 0 2409 && strcmp(secondarySignature, supportingType) != 0) { 2410 error = signatures.AddString(kSigField, supportingType); 2411 } 2412 } 2413 2414 // Add the preferred type of the super type here before adding 2415 // the other types supporting the super type, but only if we have 2416 // not already added it in case there was no preferred app for the 2417 // sub-type configured. 2418 if (error == B_OK && !addedSecondarySignature 2419 && secondarySignature[0] != '\0') { 2420 error = signatures.AddString(kSigField, secondarySignature); 2421 } 2422 2423 // Add all signatures with support for the super-type. 2424 for (int32 i = subCount; error == B_OK 2425 && supportingSignatures.FindString(kSigField, i, 2426 &supportingType) == B_OK; i++) { 2427 // Don't add the signature if it's one of the preferred apps 2428 // already. 2429 if (strcmp(primarySignature, supportingType) != 0 2430 && strcmp(secondarySignature, supportingType) != 0) { 2431 error = signatures.AddString(kSigField, supportingType); 2432 } 2433 } 2434 } else { 2435 // Failed to get supporting apps, just add the preferred apps. 2436 if (error == B_OK && secondarySignature[0] != '\0') 2437 error = signatures.AddString(kSigField, secondarySignature); 2438 } 2439 2440 if (error != B_OK) 2441 return error; 2442 2443 // Set an error in case we can't resolve a single supporting app. 2444 error = B_LAUNCH_FAILED_NO_PREFERRED_APP; 2445 2446 // See if we can find a good application that is valid from the messege. 2447 const char* signature; 2448 for (int32 i = 0; 2449 signatures.FindString(kSigField, i, &signature) == B_OK; i++) { 2450 if (signature[0] == '\0') 2451 continue; 2452 2453 error = appMeta->SetTo(signature); 2454 2455 // Check, whether the signature is installed and has an app hint 2456 bool appFound = false; 2457 if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) { 2458 // Resolve symbolic links, if necessary 2459 BEntry entry; 2460 if (entry.SetTo(appRef, true) == B_OK && entry.IsFile() 2461 && entry.GetRef(appRef) == B_OK) { 2462 appFound = true; 2463 } else { 2464 // Bad app hint -- remove it 2465 appMeta->SetAppHint(NULL); 2466 } 2467 } 2468 2469 // In case there is no app hint or it is invalid, we need to query for 2470 // the app. 2471 if (error == B_OK && !appFound) 2472 error = query_for_app(appMeta->Type(), appRef); 2473 2474 if (error == B_OK) 2475 error = appFile->SetTo(appRef, B_READ_ONLY); 2476 2477 // check, whether the app can be used 2478 if (error == B_OK) 2479 error = can_app_be_used(appRef); 2480 2481 if (error == B_OK) 2482 break; 2483 } 2484 2485 return error; 2486 } 2487 2488 2489 /*! Gets the type of a file either from the node info or by sniffing. 2490 2491 The method first tries to get the file type from the supplied node info. If 2492 that didn't work, the given entry ref is sniffed. 2493 2494 \param file An entry_ref referring to the file in question. 2495 \param nodeInfo A BNodeInfo initialized to the file. 2496 \param mimeType A pointer to a pre-allocated char buffer of at least size 2497 \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for 2498 the file. 2499 2500 \return A status code. 2501 \retval B_OK Everything went fine. 2502 \retval B_BAD_VALUE \c NULL \a file, \a nodeInfo or \a mimeType. 2503 */ 2504 status_t 2505 BRoster::_GetFileType(const entry_ref* file, BNodeInfo* nodeInfo, 2506 char* mimeType) const 2507 { 2508 // first try the node info 2509 if (nodeInfo->GetType(mimeType) == B_OK) 2510 return B_OK; 2511 2512 if (fNoRegistrar) 2513 return B_NO_INIT; 2514 2515 // Try to update the file's MIME info and just read the updated type. 2516 // If that fails, sniff manually. 2517 BPath path; 2518 if (path.SetTo(file) != B_OK 2519 || update_mime_info(path.Path(), false, true, false) != B_OK 2520 || nodeInfo->GetType(mimeType) != B_OK) { 2521 BMimeType type; 2522 status_t error = BMimeType::GuessMimeType(file, &type); 2523 if (error != B_OK) 2524 return error; 2525 2526 if (!type.IsValid()) 2527 return B_BAD_VALUE; 2528 2529 strlcpy(mimeType, type.Type(), B_MIME_TYPE_LENGTH); 2530 } 2531 2532 return B_OK; 2533 } 2534 2535 2536 /*! Sends messages to a running team. 2537 2538 In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED, 2539 \c B_READY_TO_RUN and other, arbitrary, ones. 2540 2541 If \a messageList is not \c NULL or empty, those messages are sent first, 2542 then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally 2543 \c B_READ_TO_RUN. 2544 2545 \c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains 2546 more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not 2547 \c NULL. 2548 2549 The ownership of all supplied objects retains to the caller. 2550 2551 \param team The team ID of the target application. 2552 \param argc Number of elements in \a args. 2553 \param args Argument vector to be sent to the target. May be \c NULL. 2554 \param messageList List of BMessages to be sent to the target. May be 2555 \c NULL or empty. 2556 \param ref entry_ref to be sent to the target. May be \c NULL. 2557 \param alreadyRunning \c true, if the target app is not newly launched, 2558 but was already running, \c false otherwise (a \c B_READY_TO_RUN 2559 message will be sent in this case). 2560 2561 \return \c B_OK if everything went fine, or an error code otherwise. 2562 */ 2563 status_t 2564 BRoster::_SendToRunning(team_id team, int argc, const char* const* args, 2565 const BList* messageList, const entry_ref* ref, 2566 bool alreadyRunning) const 2567 { 2568 status_t error = B_OK; 2569 2570 // Construct a messenger to the app: We can't use the public constructor, 2571 // since the target application may be B_ARGV_ONLY. 2572 app_info info; 2573 error = GetRunningAppInfo(team, &info); 2574 if (error == B_OK) { 2575 BMessenger messenger; 2576 BMessenger::Private(messenger).SetTo(team, info.port, 2577 B_PREFERRED_TOKEN); 2578 2579 // send messages from the list 2580 if (messageList != NULL) { 2581 for (int32 i = 0; 2582 BMessage* message = (BMessage*)messageList->ItemAt(i); 2583 i++) { 2584 messenger.SendMessage(message); 2585 } 2586 } 2587 2588 // send B_ARGV_RECEIVED or B_REFS_RECEIVED or B_SILENT_RELAUNCH 2589 // (if already running) 2590 if (args != NULL && argc > 1) { 2591 BMessage message(B_ARGV_RECEIVED); 2592 message.AddInt32("argc", argc); 2593 for (int32 i = 0; i < argc; i++) 2594 message.AddString("argv", args[i]); 2595 2596 // also add current working directory 2597 char cwd[B_PATH_NAME_LENGTH]; 2598 if (getcwd(cwd, B_PATH_NAME_LENGTH) != NULL) 2599 message.AddString("cwd", cwd); 2600 2601 messenger.SendMessage(&message); 2602 } else if (ref != NULL) { 2603 DBG(OUT("_SendToRunning : B_REFS_RECEIVED\n")); 2604 BMessage message(B_REFS_RECEIVED); 2605 message.AddRef("refs", ref); 2606 messenger.SendMessage(&message); 2607 } else if (alreadyRunning && (!messageList || messageList->IsEmpty())) 2608 messenger.SendMessage(B_SILENT_RELAUNCH); 2609 2610 if (!alreadyRunning) { 2611 // send B_READY_TO_RUN 2612 DBG(OUT("_SendToRunning : B_READY_TO_RUN\n")); 2613 messenger.SendMessage(B_READY_TO_RUN); 2614 } 2615 } 2616 2617 return error; 2618 } 2619 2620 2621 /*! Allows to use certain functionality of the BRoster class without 2622 accessing the registrar. 2623 */ 2624 void 2625 BRoster::_SetWithoutRegistrar(bool noRegistrar) 2626 { 2627 fNoRegistrar = noRegistrar; 2628 } 2629 2630 2631 void 2632 BRoster::_InitMessenger() 2633 { 2634 DBG(OUT("BRoster::InitMessengers()\n")); 2635 2636 // find the registrar port 2637 2638 #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST 2639 BMessage data; 2640 if (BLaunchRoster().GetData(B_REGISTRAR_SIGNATURE, data) == B_OK) { 2641 port_id port = data.GetInt32("port", -1); 2642 team_id team = data.GetInt32("team", -1); 2643 2644 if (port >= 0 && team != current_team()) { 2645 // Make sure we aren't the registrar ourselves. 2646 2647 DBG(OUT(" found roster port\n")); 2648 2649 BMessenger::Private(fMessenger).SetTo(team, port, 2650 B_PREFERRED_TOKEN); 2651 } 2652 } 2653 #else 2654 port_id rosterPort = find_port(B_REGISTRAR_PORT_NAME); 2655 port_info info; 2656 if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) { 2657 DBG(OUT(" found roster port\n")); 2658 2659 BMessenger::Private(fMessenger).SetTo(info.team, rosterPort, 2660 B_PREFERRED_TOKEN); 2661 } 2662 #endif 2663 2664 DBG(OUT("BRoster::InitMessengers() done\n")); 2665 } 2666 2667 2668 /*static*/ status_t 2669 BRoster::_InitMimeMessenger(void* data) 2670 { 2671 BRoster* roster = (BRoster*)data; 2672 2673 // ask for the MIME messenger 2674 // Generous 1s + 5s timeouts. It could actually be synchronous, but 2675 // timeouts allow us to debug the registrar main thread. 2676 BMessage request(B_REG_GET_MIME_MESSENGER); 2677 BMessage reply; 2678 status_t error = roster->fMessenger.SendMessage(&request, &reply, 2679 1000000LL, 5000000LL); 2680 if (error == B_OK && reply.what == B_REG_SUCCESS) { 2681 DBG(OUT(" got reply from roster\n")); 2682 reply.FindMessenger("messenger", &roster->fMimeMessenger); 2683 } else { 2684 DBG(OUT(" no (useful) reply from roster: error: %" B_PRIx32 ": %s\n", 2685 error, strerror(error))); 2686 if (error == B_OK) 2687 DBG(reply.PrintToStream()); 2688 } 2689 2690 return error; 2691 } 2692 2693 2694 BMessenger& 2695 BRoster::_MimeMessenger() 2696 { 2697 __init_once(&fMimeMessengerInitOnce, &_InitMimeMessenger, this); 2698 return fMimeMessenger; 2699 } 2700 2701 2702 /*! Sends a request to the roster to add the application with the 2703 given signature to the front of the recent apps list. 2704 */ 2705 void 2706 BRoster::_AddToRecentApps(const char* signature) const 2707 { 2708 status_t error = B_OK; 2709 // compose the request message 2710 BMessage request(B_REG_ADD_TO_RECENT_APPS); 2711 if (error == B_OK) 2712 error = request.AddString("app sig", signature); 2713 2714 // send the request 2715 BMessage reply; 2716 if (error == B_OK) 2717 error = fMessenger.SendMessage(&request, &reply); 2718 2719 // evaluate the reply 2720 status_t result; 2721 if (error == B_OK) { 2722 error = reply.what == B_REG_RESULT 2723 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2724 } 2725 2726 if (error == B_OK) 2727 error = reply.FindInt32("result", &result); 2728 2729 if (error == B_OK) 2730 error = result; 2731 2732 // Nothing to return... how sad :-( 2733 //return error; 2734 } 2735 2736 2737 //! Sends a request to the roster to clear the recent documents list. 2738 void 2739 BRoster::_ClearRecentDocuments() const 2740 { 2741 BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS); 2742 BMessage reply; 2743 fMessenger.SendMessage(&request, &reply); 2744 } 2745 2746 2747 //! Sends a request to the roster to clear the recent documents list. 2748 void 2749 BRoster::_ClearRecentFolders() const 2750 { 2751 BMessage request(B_REG_CLEAR_RECENT_FOLDERS); 2752 BMessage reply; 2753 fMessenger.SendMessage(&request, &reply); 2754 } 2755 2756 2757 //! \brief Sends a request to the roster to clear the recent documents list. 2758 void 2759 BRoster::_ClearRecentApps() const 2760 { 2761 BMessage request(B_REG_CLEAR_RECENT_APPS); 2762 BMessage reply; 2763 fMessenger.SendMessage(&request, &reply); 2764 } 2765 2766 2767 /*! Loads the system's recently used document, folder, and 2768 application lists from the specified file. 2769 2770 \note The current lists are cleared before loading the new lists 2771 2772 \param filename The name of the file to load from 2773 */ 2774 void 2775 BRoster::_LoadRecentLists(const char* filename) const 2776 { 2777 status_t error = B_OK; 2778 2779 // compose the request message 2780 BMessage request(B_REG_LOAD_RECENT_LISTS); 2781 if (error == B_OK) 2782 error = request.AddString("filename", filename); 2783 2784 // send the request 2785 BMessage reply; 2786 if (error == B_OK) 2787 error = fMessenger.SendMessage(&request, &reply); 2788 2789 // evaluate the reply 2790 status_t result; 2791 if (error == B_OK) { 2792 error = reply.what == B_REG_RESULT 2793 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2794 } 2795 if (error == B_OK) 2796 error = reply.FindInt32("result", &result); 2797 2798 if (error == B_OK) 2799 error = result; 2800 2801 // Nothing to return... how sad :-( 2802 //return error; 2803 } 2804 2805 2806 /*! Saves the system's recently used document, folder, and 2807 application lists to the specified file. 2808 2809 \param filename The name of the file to save to 2810 */ 2811 void 2812 BRoster::_SaveRecentLists(const char* filename) const 2813 { 2814 status_t error = B_OK; 2815 2816 // compose the request message 2817 BMessage request(B_REG_SAVE_RECENT_LISTS); 2818 if (error == B_OK) 2819 error = request.AddString("filename", filename); 2820 2821 // send the request 2822 BMessage reply; 2823 if (error == B_OK) 2824 error = fMessenger.SendMessage(&request, &reply); 2825 2826 // evaluate the reply 2827 status_t result; 2828 if (error == B_OK) { 2829 error = reply.what == B_REG_RESULT 2830 ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2831 } 2832 if (error == B_OK) 2833 error = reply.FindInt32("result", &result); 2834 2835 if (error == B_OK) 2836 error = result; 2837 2838 // Nothing to return... how sad :-( 2839 //return error; 2840 } 2841