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