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