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