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