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