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