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 // send the request 1428 BMessage reply; 1429 if (error == B_OK) 1430 error = fMessenger.SendMessage(&request, &reply); 1431 // evaluate the reply 1432 if (error == B_OK) { 1433 if (reply.what == B_REG_SUCCESS) { 1434 if (!fullReg && team < 0) { 1435 uint32 token; 1436 if (reply.FindInt32("token", (int32*)&token) == B_OK) { 1437 if (pToken) 1438 *pToken = token; 1439 } else 1440 error = B_ERROR; 1441 } 1442 } else { 1443 if (reply.FindInt32("error", &error) != B_OK) 1444 error = B_ERROR; 1445 if (otherTeam) 1446 reply.FindInt32("other_team", otherTeam); 1447 } 1448 } 1449 return error; 1450 } 1451 1452 // SetSignature 1453 /*! \brief Sets an application's signature. 1454 1455 The application must be registered or at pre-registered with a valid 1456 team ID. 1457 1458 \param team The app's team ID. 1459 \param mimeSig The app's new signature. 1460 \return 1461 - \c B_OK: Everything went fine. 1462 - \c B_REG_APP_NOT_REGISTERED: The supplied team ID does not identify a 1463 registered application. 1464 */ 1465 status_t 1466 BRoster::_SetSignature(team_id team, const char *mimeSig) const 1467 { 1468 status_t error = B_OK; 1469 // compose the request message 1470 BMessage request(B_REG_SET_SIGNATURE); 1471 if (error == B_OK && team >= 0) 1472 error = request.AddInt32("team", team); 1473 if (error == B_OK && mimeSig) 1474 error = request.AddString("signature", mimeSig); 1475 1476 // send the request 1477 BMessage reply; 1478 if (error == B_OK) 1479 error = fMessenger.SendMessage(&request, &reply); 1480 1481 // evaluate the reply 1482 if (error == B_OK && reply.what != B_REG_SUCCESS 1483 && reply.FindInt32("error", &error) != B_OK) 1484 error = B_ERROR; 1485 1486 return error; 1487 } 1488 1489 // SetThread 1490 /*! 1491 \todo Really needed? 1492 */ 1493 void 1494 BRoster::_SetThread(team_id team, thread_id thread) const 1495 { 1496 } 1497 1498 // SetThreadAndTeam 1499 /*! \brief Sets the team and thread IDs of a pre-registered application. 1500 1501 After an application has been pre-registered via AddApplication(), without 1502 supplying a team ID, the team and thread IDs have to be set using this 1503 method. 1504 1505 \param entryToken The token identifying the application (returned by 1506 AddApplication()) 1507 \param thread The app's thread ID 1508 \param team The app's team ID 1509 \return 1510 - \c B_OK: Everything went fine. 1511 - \c B_REG_APP_NOT_PRE_REGISTERED: The supplied token does not identify a 1512 pre-registered application. 1513 */ 1514 status_t 1515 BRoster::_SetThreadAndTeam(uint32 entryToken, thread_id thread, 1516 team_id team) const 1517 { 1518 status_t error = B_OK; 1519 // compose the request message 1520 BMessage request(B_REG_SET_THREAD_AND_TEAM); 1521 if (error == B_OK) 1522 error = request.AddInt32("token", (int32)entryToken); 1523 if (error == B_OK && team >= 0) 1524 error = request.AddInt32("team", team); 1525 if (error == B_OK && thread >= 0) 1526 error = request.AddInt32("thread", thread); 1527 1528 // send the request 1529 BMessage reply; 1530 if (error == B_OK) 1531 error = fMessenger.SendMessage(&request, &reply); 1532 1533 // evaluate the reply 1534 if (error == B_OK && reply.what != B_REG_SUCCESS 1535 && reply.FindInt32("error", &error) != B_OK) 1536 error = B_ERROR; 1537 1538 return error; 1539 } 1540 1541 // CompleteRegistration 1542 /*! \brief Completes the registration process for a pre-registered application. 1543 1544 After an application has been pre-registered via AddApplication() and 1545 after assigning it a team ID (via SetThreadAndTeam()) the application is 1546 still pre-registered and must complete the registration. 1547 1548 \param team The app's team ID 1549 \param thread The app's thread ID 1550 \param thread The app looper port 1551 \return 1552 - \c B_OK: Everything went fine. 1553 - \c B_REG_APP_NOT_PRE_REGISTERED: \a team does not identify an existing 1554 application or the identified application is already fully registered. 1555 */ 1556 status_t 1557 BRoster::_CompleteRegistration(team_id team, thread_id thread, 1558 port_id port) const 1559 { 1560 status_t error = B_OK; 1561 // compose the request message 1562 BMessage request(B_REG_COMPLETE_REGISTRATION); 1563 if (error == B_OK && team >= 0) 1564 error = request.AddInt32("team", team); 1565 if (error == B_OK && thread >= 0) 1566 error = request.AddInt32("thread", thread); 1567 if (error == B_OK && port >= 0) 1568 error = request.AddInt32("port", port); 1569 1570 // send the request 1571 BMessage reply; 1572 if (error == B_OK) 1573 error = fMessenger.SendMessage(&request, &reply); 1574 1575 // evaluate the reply 1576 if (error == B_OK && reply.what != B_REG_SUCCESS 1577 && reply.FindInt32("error", &error) != B_OK) 1578 error = B_ERROR; 1579 1580 return error; 1581 } 1582 1583 // IsAppPreRegistered 1584 /*! \brief Returns whether an application is pre-registered. 1585 1586 If the application is indeed pre-registered and \a info is not \c NULL, 1587 the methods fills in the app_info structure pointed to by \a info. 1588 1589 \param ref An entry_ref referring to the app's executable 1590 \param team The app's team ID 1591 \param info A pointer to a pre-allocated app_info structure to be filled 1592 in by this method (may be \c NULL) 1593 \return \c true, if the application is pre-registered, \c false if not. 1594 */ 1595 bool 1596 BRoster::_IsAppPreRegistered(const entry_ref *ref, team_id team, 1597 app_info *info) const 1598 { 1599 status_t error = B_OK; 1600 // compose the request message 1601 BMessage request(B_REG_IS_APP_PRE_REGISTERED); 1602 if (error == B_OK && ref) 1603 error = request.AddRef("ref", ref); 1604 if (error == B_OK && team >= 0) 1605 error = request.AddInt32("team", team); 1606 // send the request 1607 BMessage reply; 1608 if (error == B_OK) 1609 error = fMessenger.SendMessage(&request, &reply); 1610 1611 // evaluate the reply 1612 bool isPreRegistered = false; 1613 if (error == B_OK) { 1614 if (reply.what == B_REG_SUCCESS) { 1615 if (reply.FindBool("pre-registered", &isPreRegistered) != B_OK) 1616 error = B_ERROR; 1617 if (error == B_OK && isPreRegistered && info) 1618 error = find_message_app_info(&reply, info); 1619 } else if (reply.FindInt32("error", &error) != B_OK) 1620 error = B_ERROR; 1621 } 1622 return error == B_OK && isPreRegistered; 1623 } 1624 1625 // RemovePreRegApp 1626 /*! \brief Completely unregisters a pre-registered application. 1627 1628 This method can only be used to unregister applications that don't have 1629 a team ID assigned yet. All other applications must be unregistered via 1630 RemoveApp(). 1631 1632 \param entryToken The token identifying the application (returned by 1633 AddApplication()) 1634 \return 1635 - \c B_OK: Everything went fine. 1636 - \c B_REG_APP_NOT_PRE_REGISTERED: The supplied token does not identify a 1637 pre-registered application. 1638 */ 1639 status_t 1640 BRoster::_RemovePreRegApp(uint32 entryToken) const 1641 { 1642 status_t error = B_OK; 1643 // compose the request message 1644 BMessage request(B_REG_REMOVE_PRE_REGISTERED_APP); 1645 if (error == B_OK) 1646 error = request.AddInt32("token", (int32)entryToken); 1647 1648 // send the request 1649 BMessage reply; 1650 if (error == B_OK) 1651 error = fMessenger.SendMessage(&request, &reply); 1652 1653 // evaluate the reply 1654 if (error == B_OK && reply.what != B_REG_SUCCESS 1655 && reply.FindInt32("error", &error) != B_OK) 1656 error = B_ERROR; 1657 1658 return error; 1659 } 1660 1661 // RemoveApp 1662 /*! \brief Unregisters a (pre-)registered application. 1663 1664 This method must be used to unregister applications that already have 1665 a team ID assigned, i.e. also for pre-registered application for which 1666 SetThreadAndTeam() has already been invoked. 1667 1668 \param team The app's team ID 1669 \return 1670 - \c B_OK: Everything went fine. 1671 - \c B_REG_APP_NOT_REGISTERED: The supplied team ID does not identify a 1672 (pre-)registered application. 1673 */ 1674 status_t 1675 BRoster::_RemoveApp(team_id team) const 1676 { 1677 status_t error = B_OK; 1678 // compose the request message 1679 BMessage request(B_REG_REMOVE_APP); 1680 if (error == B_OK && team >= 0) 1681 error = request.AddInt32("team", team); 1682 1683 // send the request 1684 BMessage reply; 1685 if (error == B_OK) 1686 error = fMessenger.SendMessage(&request, &reply); 1687 1688 // evaluate the reply 1689 if (error == B_OK && reply.what != B_REG_SUCCESS 1690 && reply.FindInt32("error", &error) != B_OK) 1691 error = B_ERROR; 1692 1693 return error; 1694 } 1695 1696 1697 void 1698 BRoster::_ApplicationCrashed(team_id team) 1699 { 1700 BPrivate::DesktopLink link; 1701 if (link.InitCheck() != B_OK) 1702 return; 1703 1704 if (link.StartMessage(AS_APP_CRASHED) == B_OK 1705 && link.Attach(team) == B_OK) 1706 link.Flush(); 1707 } 1708 1709 1710 // _LaunchApp 1711 /*! \brief Launches the application associated with the supplied MIME type or 1712 the entry referred to by the supplied entry_ref. 1713 1714 The application to be started is searched the same way FindApp() does it. 1715 1716 At least one of \a mimeType or \a ref must not be \c NULL. If \a mimeType 1717 is supplied, \a ref is ignored for finding the application. 1718 1719 If \a ref does refer to an application executable, that application is 1720 launched. Otherwise the respective application is searched and launched, 1721 and \a ref is sent to it in a \c B_REFS_RECEIVED message, unless other 1722 arguments are passed via \a argc and \a args -- then the entry_ref is 1723 converted into a path (C-string) and added to the argument vector. 1724 1725 \a messageList contains messages to be sent to the application 1726 "on launch", i.e. before ReadyToRun() is invoked on the BApplication 1727 object. The caller retains ownership of the supplied BList and the 1728 contained BMessages. In case the method fails with \c B_ALREADY_RUNNING 1729 the messages are delivered to the already running instance. The same 1730 applies to the \c B_REFS_RECEIVED message. 1731 1732 The supplied \a argc and \a args are (if containing at least one argument) 1733 put into a \c B_ARGV_RECEIVED message and sent to the launched application 1734 "on launch". The caller retains ownership of the supplied \a args. 1735 In case the method fails with \c B_ALREADY_RUNNING the message is 1736 delivered to the already running instance. The same applies to the 1737 \c B_REFS_RECEIVED message, if no arguments are supplied via \a argc and 1738 \args. 1739 1740 \param mimeType MIME type for which the application shall be launched. 1741 May be \c NULL. 1742 \param ref entry_ref referring to the file for which an application shall 1743 be launched. May be \c NULL. 1744 \param messageList Optional list of messages to be sent to the application 1745 "on launch". May be \c NULL. 1746 \param argc Specifies the number of elements in \a args. 1747 \param args An array of C-strings to be sent as B_ARGV_RECEIVED messaged 1748 to the launched application. 1749 \param appTeam Pointer to a pre-allocated team_id variable to be set to 1750 the team ID of the launched application. 1751 \return 1752 - \c B_OK: Everything went fine. 1753 - \c B_BAD_VALUE: \c NULL \a mimeType and \a ref. 1754 - \c B_LAUNCH_FAILED_NO_PREFERRED_APP: Neither with the supplied type nor 1755 with its supertype (if the supplied isn't a supertype itself) a 1756 preferred application is associated. 1757 - \c B_LAUNCH_FAILED_APP_NOT_FOUND: The supplied type is not installed or 1758 its preferred application could not be found. 1759 - \c B_LAUNCH_FAILED_APP_IN_TRASH: The supplied type's preferred 1760 application is in trash. 1761 - \c B_LAUNCH_FAILED_EXECUTABLE: The found application is not executable. 1762 - other error codes 1763 */ 1764 status_t 1765 BRoster::_LaunchApp(const char *mimeType, const entry_ref *ref, 1766 const BList *messageList, int argc, 1767 const char *const *args, team_id *_appTeam) const 1768 { 1769 DBG(OUT("BRoster::_LaunchApp()")); 1770 1771 if (_appTeam != NULL) { 1772 // we're supposed to set _appTeam to -1 on error; we'll 1773 // reset it later if everything goes well 1774 *_appTeam = -1; 1775 } 1776 1777 if (mimeType == NULL && ref == NULL) 1778 return B_BAD_VALUE; 1779 1780 // use a mutable copy of the document entry_ref 1781 entry_ref _docRef; 1782 entry_ref *docRef = NULL; 1783 if (ref != NULL) { 1784 _docRef = *ref; 1785 docRef = &_docRef; 1786 } 1787 1788 // find the app 1789 entry_ref appRef; 1790 char signature[B_MIME_TYPE_LENGTH]; 1791 uint32 appFlags = B_REG_DEFAULT_APP_FLAGS; 1792 bool wasDocument = true; 1793 status_t error = _ResolveApp(mimeType, docRef, &appRef, signature, &appFlags, 1794 &wasDocument); 1795 DBG(OUT(" find app: %s (%lx)\n", strerror(error), error)); 1796 1797 // build an argument vector 1798 ArgVector argVector; 1799 if (error == B_OK) { 1800 error = argVector.Init(argc, args, &appRef, 1801 (wasDocument ? docRef : NULL)); 1802 } 1803 DBG(OUT(" build argv: %s (%lx)\n", strerror(error), error)); 1804 1805 // pre-register the app (but ignore scipts) 1806 app_info appInfo; 1807 bool isScript = wasDocument && docRef != NULL && *docRef == appRef; 1808 bool alreadyRunning = false; 1809 uint32 appToken = 0; 1810 team_id team = -1; 1811 uint32 otherAppFlags = B_REG_DEFAULT_APP_FLAGS; 1812 if (error == B_OK && !isScript) { 1813 error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1, false, 1814 &appToken, &team); 1815 if (error == B_ALREADY_RUNNING) { 1816 DBG(OUT(" already running\n")); 1817 alreadyRunning = true; 1818 error = B_OK; 1819 // get the app flags for the running application 1820 if (GetRunningAppInfo(team, &appInfo) == B_OK) 1821 otherAppFlags = appInfo.flags; 1822 } 1823 DBG(OUT(" pre-register: %s (%lx)\n", strerror(error), error)); 1824 } 1825 1826 // launch the app 1827 if (error == B_OK && !alreadyRunning) { 1828 DBG(OUT(" token: %lu\n", appToken)); 1829 // load the app image 1830 thread_id appThread = load_image(argVector.Count(), 1831 const_cast<const char**>(argVector.Args()), 1832 const_cast<const char**>(environ)); 1833 1834 // get the app team 1835 if (appThread >= 0) { 1836 thread_info threadInfo; 1837 error = get_thread_info(appThread, &threadInfo); 1838 if (error == B_OK) 1839 team = threadInfo.team; 1840 } else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE) 1841 error = B_LAUNCH_FAILED_EXECUTABLE; 1842 else 1843 error = appThread; 1844 1845 DBG(OUT(" load image: %s (%lx)\n", strerror(error), error)); 1846 // finish the registration 1847 if (error == B_OK && !isScript) 1848 error = _SetThreadAndTeam(appToken, appThread, team); 1849 1850 DBG(OUT(" set thread and team: %s (%lx)\n", strerror(error), error)); 1851 // resume the launched team 1852 if (error == B_OK) 1853 error = resume_thread(appThread); 1854 1855 DBG(OUT(" resume thread: %s (%lx)\n", strerror(error), error)); 1856 // on error: kill the launched team and unregister the app 1857 if (error != B_OK) { 1858 if (appThread >= 0) 1859 kill_thread(appThread); 1860 if (!isScript) 1861 _RemovePreRegApp(appToken); 1862 } 1863 } 1864 1865 if (alreadyRunning && current_team() == team) { 1866 // The target team is calling us, so we don't send it the message 1867 // to prevent an endless loop 1868 error = B_BAD_VALUE; 1869 } 1870 1871 // send "on launch" messages 1872 if (error == B_OK) { 1873 // If the target app is B_ARGV_ONLY almost no messages are sent to it. 1874 // More precisely, the launched app gets at least B_ARGV_RECEIVED and 1875 // B_READY_TO_RUN, an already running app gets nothing. 1876 bool argvOnly = (appFlags & B_ARGV_ONLY) 1877 || (alreadyRunning && (otherAppFlags & B_ARGV_ONLY)); 1878 const BList *_messageList = (argvOnly ? NULL : messageList); 1879 // don't send ref, if it refers to the app or is included in the 1880 // argument vector 1881 const entry_ref *_ref = argvOnly || !wasDocument 1882 || argVector.Count() > 1 ? NULL : docRef; 1883 if (!(argvOnly && alreadyRunning)) { 1884 _SendToRunning(team, argVector.Count(), argVector.Args(), 1885 _messageList, _ref, !alreadyRunning); 1886 } 1887 } 1888 1889 // set return values 1890 if (error == B_OK) { 1891 if (alreadyRunning) 1892 error = B_ALREADY_RUNNING; 1893 else if (_appTeam) 1894 *_appTeam = team; 1895 } 1896 1897 DBG(OUT("BRoster::_LaunchApp() done: %s (%lx)\n", 1898 strerror(error), error)); 1899 return error; 1900 } 1901 1902 // SetAppFlags 1903 void 1904 BRoster::_SetAppFlags(team_id team, uint32 flags) const 1905 { 1906 } 1907 1908 // DumpRoster 1909 void 1910 BRoster::_DumpRoster() const 1911 { 1912 } 1913 1914 // _ResolveApp 1915 /*! \brief Finds an application associated with a MIME type or a file. 1916 1917 It does also supply the caller with some more information about the 1918 application, like signature, app flags and whether the supplied 1919 MIME type/entry_ref already identified an application. 1920 1921 At least one of \a inType or \a ref must not be \c NULL. If \a inType is 1922 supplied, \a ref is ignored. 1923 1924 If \a ref refers to a link, it is updated with the entry_ref for the 1925 resolved entry. 1926 1927 \see FindApp() for how the application is searched. 1928 1929 \a appSig is set to a string with length 0, if the found application 1930 has no signature. 1931 1932 \param inType The MIME type for which an application shall be found. 1933 May be \c NULL. 1934 \param ref The file for which an application shall be found. 1935 May be \c NULL. 1936 \param appRef A pointer to a pre-allocated entry_ref to be filled with 1937 a reference to the found application's executable. May be \c NULL. 1938 \param appSig A pointer to a pre-allocated char buffer of at least size 1939 \c B_MIME_TYPE_LENGTH to be filled with the signature of the found 1940 application. May be \c NULL. 1941 \param appFlags A pointer to a pre-allocated uint32 variable to be filled 1942 with the app flags of the found application. May be \c NULL. 1943 \param wasDocument A pointer to a pre-allocated bool variable to be set to 1944 \c true, if the supplied file was not identifying an application, 1945 to \c false otherwise. Has no meaning, if a \a inType is supplied. 1946 May be \c NULL. 1947 \return 1948 - \c B_OK: Everything went fine. 1949 - \c B_BAD_VALUE: \c NULL \a inType and \a ref. 1950 - \see FindApp() for other error codes. 1951 */ 1952 status_t 1953 BRoster::_ResolveApp(const char *inType, entry_ref *ref, 1954 entry_ref* _appRef, char* _appSig, uint32* _appFlags, 1955 bool* _wasDocument) const 1956 { 1957 if ((inType == NULL && ref == NULL) 1958 || (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH)) 1959 return B_BAD_VALUE; 1960 1961 // find the app 1962 BMimeType appMeta; 1963 BFile appFile; 1964 entry_ref appRef; 1965 status_t error; 1966 1967 if (inType) 1968 error = _TranslateType(inType, &appMeta, &appRef, &appFile); 1969 else { 1970 error = _TranslateRef(ref, &appMeta, &appRef, &appFile, 1971 _wasDocument); 1972 } 1973 1974 // create meta mime 1975 if (error == B_OK) { 1976 BPath path; 1977 if (path.SetTo(&appRef) == B_OK) 1978 create_app_meta_mime(path.Path(), false, true, false); 1979 } 1980 1981 // set the app hint on the type -- but only if the file has the 1982 // respective signature, otherwise unset the app hint 1983 BAppFileInfo appFileInfo; 1984 if (error == B_OK) { 1985 char signature[B_MIME_TYPE_LENGTH]; 1986 if (appFileInfo.SetTo(&appFile) == B_OK 1987 && appFileInfo.GetSignature(signature) == B_OK) { 1988 if (!strcasecmp(appMeta.Type(), signature)) 1989 appMeta.SetAppHint(&appRef); 1990 else { 1991 appMeta.SetAppHint(NULL); 1992 appMeta.SetTo(signature); 1993 } 1994 } else 1995 appMeta.SetAppHint(NULL); 1996 } 1997 1998 // set the return values 1999 if (error == B_OK) { 2000 if (_appRef) 2001 *_appRef = appRef; 2002 2003 if (_appSig) { 2004 // there's no warranty, that appMeta is valid 2005 if (appMeta.IsValid()) 2006 strcpy(_appSig, appMeta.Type()); 2007 else 2008 _appSig[0] = '\0'; 2009 } 2010 2011 if (_appFlags) { 2012 // if an error occurs here, we don't care and just set a default 2013 // value 2014 if (appFileInfo.InitCheck() != B_OK 2015 || appFileInfo.GetAppFlags(_appFlags) != B_OK) { 2016 *_appFlags = B_REG_DEFAULT_APP_FLAGS; 2017 } 2018 } 2019 } else { 2020 // unset the ref on error 2021 if (_appRef) 2022 *_appRef = appRef; 2023 } 2024 2025 return error; 2026 } 2027 2028 // _TranslateRef 2029 /*! \brief Finds an application associated with a file. 2030 2031 \a appMeta is left unmodified, if the file is executable, but has no 2032 signature. 2033 2034 \see FindApp() for how the application is searched. 2035 2036 If \a ref refers to a link, it is updated with the entry_ref for the 2037 resolved entry. 2038 2039 \param ref The file for which an application shall be found. 2040 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2041 signature of the found application. 2042 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2043 a reference to the found application's executable. 2044 \param appFile A pointer to a pre-allocated BFile to be set to the 2045 executable of the found application. 2046 \param wasDocument A pointer to a pre-allocated bool variable to be set to 2047 \c true, if the supplied file was not identifying an application, 2048 to \c false otherwise. May be \c NULL. 2049 \return 2050 - \c B_OK: Everything went fine. 2051 - \c B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile. 2052 - \see FindApp() for other error codes. 2053 */ 2054 status_t 2055 BRoster::_TranslateRef(entry_ref *ref, BMimeType *appMeta, 2056 entry_ref *appRef, BFile *appFile, bool *_wasDocument) const 2057 { 2058 if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL) 2059 return B_BAD_VALUE; 2060 2061 // resolve ref, if necessary 2062 BEntry entry; 2063 status_t error = entry.SetTo(ref, false); 2064 if (error == B_OK && entry.IsSymLink()) { 2065 // ref refers to a link 2066 error = entry.SetTo(ref, true); 2067 if (error == B_OK) 2068 error = entry.GetRef(ref); 2069 if (error != B_OK) 2070 error = B_LAUNCH_FAILED_NO_RESOLVE_LINK; 2071 } 2072 2073 // init node 2074 BNode node; 2075 if (error == B_OK) 2076 error = node.SetTo(ref); 2077 // get permissions 2078 mode_t permissions; 2079 if (error == B_OK) 2080 error = node.GetPermissions(&permissions); 2081 if (error == B_OK) { 2082 if ((permissions & S_IXUSR) && node.IsFile()) { 2083 // node is executable and a file -- we're done 2084 *appRef = *ref; 2085 error = appFile->SetTo(appRef, B_READ_ONLY); 2086 2087 // get the app's signature via a BAppFileInfo 2088 BAppFileInfo appFileInfo; 2089 if (error == B_OK) 2090 error = appFileInfo.SetTo(appFile); 2091 2092 char type[B_MIME_TYPE_LENGTH]; 2093 bool isDocument = true; 2094 2095 if (error == B_OK) { 2096 // don't worry, if the file doesn't have a signature, just 2097 // unset the supplied object 2098 if (appFileInfo.GetSignature(type) == B_OK) 2099 error = appMeta->SetTo(type); 2100 else 2101 appMeta->Unset(); 2102 if (appFileInfo.GetType(type) == B_OK 2103 && !strcasecmp(type, B_APP_MIME_TYPE)) 2104 isDocument = false; 2105 } 2106 if (_wasDocument) 2107 *_wasDocument = isDocument; 2108 } else { 2109 // the node is not exectuable or not a file 2110 // init a node info 2111 BNodeInfo nodeInfo; 2112 if (error == B_OK) 2113 error = nodeInfo.SetTo(&node); 2114 char preferredApp[B_MIME_TYPE_LENGTH]; 2115 if (error == B_OK) { 2116 // if the file has a preferred app, let _TranslateType() find 2117 // it for us 2118 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK) { 2119 error = _TranslateType(preferredApp, appMeta, appRef, 2120 appFile); 2121 } else { 2122 // no preferred app -- we need to get the file's type 2123 char fileType[B_MIME_TYPE_LENGTH]; 2124 // get the type from the file, or guess a type 2125 if (nodeInfo.GetType(fileType) != B_OK) 2126 error = _SniffFile(ref, &nodeInfo, fileType); 2127 // now let _TranslateType() do the actual work 2128 if (error == B_OK) { 2129 error = _TranslateType(fileType, appMeta, appRef, 2130 appFile); 2131 } 2132 } 2133 } 2134 if (_wasDocument) 2135 *_wasDocument = true; 2136 } 2137 } 2138 return error; 2139 } 2140 2141 // _TranslateType 2142 /*! \brief Finds an application associated with a MIME type. 2143 2144 \see FindApp() for how the application is searched. 2145 2146 \param mimeType The MIME type for which an application shall be found. 2147 \param appMeta A pointer to a pre-allocated BMimeType to be set to the 2148 signature of the found application. 2149 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2150 a reference to the found application's executable. 2151 \param appFile A pointer to a pre-allocated BFile to be set to the 2152 executable of the found application. 2153 \return 2154 - \c B_OK: Everything went fine. 2155 - \c B_BAD_VALUE: \c NULL \a mimeType, \a appMeta, \a appRef or \a appFile. 2156 - \see FindApp() for other error codes. 2157 */ 2158 status_t 2159 BRoster::_TranslateType(const char *mimeType, BMimeType *appMeta, 2160 entry_ref *appRef, BFile *appFile) const 2161 { 2162 if (mimeType == NULL || appMeta == NULL || appRef == NULL 2163 || appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) 2164 return B_BAD_VALUE; 2165 2166 // create a BMimeType and check, if the type is installed 2167 BMimeType type; 2168 status_t error = type.SetTo(mimeType); 2169 2170 // get the preferred app 2171 char primarySignature[B_MIME_TYPE_LENGTH]; 2172 char secondarySignature[B_MIME_TYPE_LENGTH]; 2173 primarySignature[0] = '\0'; 2174 secondarySignature[0] = '\0'; 2175 2176 if (error == B_OK) { 2177 if (type.IsInstalled()) { 2178 BMimeType superType; 2179 if (type.GetSupertype(&superType) == B_OK) 2180 superType.GetPreferredApp(secondarySignature); 2181 2182 if (type.GetPreferredApp(primarySignature) != B_OK 2183 && !secondarySignature[0]) { 2184 // The type is installed, but has no preferred app. 2185 // In fact it might be an app signature and even having a 2186 // valid app hint. Nevertheless we fail. 2187 error = B_LAUNCH_FAILED_NO_PREFERRED_APP; 2188 } else if (!strcmp(primarySignature, secondarySignature)) { 2189 // Both types have the same preferred app, there is 2190 // no point in testing it twice. 2191 secondarySignature[0] = '\0'; 2192 } 2193 } else { 2194 // The type is not installed. We assume it is an app signature. 2195 strcpy(primarySignature, mimeType); 2196 } 2197 } 2198 2199 if (error != B_OK) 2200 return error; 2201 2202 // see if we can find the application and if it's valid, try 2203 // both preferred app signatures, if available (from type and 2204 // super type) 2205 2206 status_t primaryError = B_OK; 2207 2208 for (int32 tries = 0; tries < 2; tries++) { 2209 const char* signature = tries == 0 ? primarySignature : secondarySignature; 2210 if (signature[0] == '\0') 2211 continue; 2212 2213 error = appMeta->SetTo(signature); 2214 2215 // check, whether the signature is installed and has an app hint 2216 bool appFound = false; 2217 if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) { 2218 // resolve symbolic links, if necessary 2219 BEntry entry; 2220 if (entry.SetTo(appRef, true) == B_OK && entry.IsFile() 2221 && entry.GetRef(appRef) == B_OK) { 2222 appFound = true; 2223 } else 2224 appMeta->SetAppHint(NULL); // bad app hint -- remove it 2225 } 2226 2227 // in case there is no app hint or it is invalid, we need to query for the 2228 // app 2229 if (error == B_OK && !appFound) 2230 error = query_for_app(appMeta->Type(), appRef); 2231 if (error == B_OK) 2232 error = appFile->SetTo(appRef, B_READ_ONLY); 2233 // check, whether the app can be used 2234 if (error == B_OK) 2235 error = can_app_be_used(appRef); 2236 2237 if (error != B_OK) { 2238 if (tries == 0) 2239 primaryError = error; 2240 else if (primarySignature[0]) 2241 error = primaryError; 2242 } else 2243 break; 2244 } 2245 2246 return error; 2247 } 2248 2249 // _SniffFile 2250 /*! \brief Sniffs the MIME type for a file. 2251 2252 Also updates the file's MIME info, if possible. 2253 2254 \param file An entry_ref referring to the file in question. 2255 \param nodeInfo A BNodeInfo initialized to the file. 2256 \param mimeType A pointer to a pre-allocated char buffer of at least size 2257 \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for 2258 the file. 2259 \return 2260 - \c B_OK: Everything went fine. 2261 - \c B_BAD_VALUE: \c NULL \a file, \a nodeInfo or \a mimeType. 2262 - other errors 2263 */ 2264 status_t 2265 BRoster::_SniffFile(const entry_ref *file, BNodeInfo *nodeInfo, 2266 char *mimeType) const 2267 { 2268 status_t error = (file && nodeInfo && mimeType ? B_OK : B_BAD_VALUE); 2269 if (error == B_OK) { 2270 // Try to update the file's MIME info and just read the updated type. 2271 // If that fails, sniff manually. 2272 BPath path; 2273 if (path.SetTo(file) != B_OK 2274 || update_mime_info(path.Path(), false, true, false) != B_OK 2275 || nodeInfo->GetType(mimeType) != B_OK) { 2276 BMimeType type; 2277 error = BMimeType::GuessMimeType(file, &type); 2278 if (error == B_OK && type.IsValid()) 2279 strcpy(mimeType, type.Type()); 2280 } 2281 } 2282 return error; 2283 } 2284 2285 // _SendToRunning 2286 /*! \brief Sends messages to a running team. 2287 2288 In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED, 2289 \c B_READY_TO_RUN and other, arbitrary, ones. 2290 2291 If \a messageList is not \c NULL or empty, those messages are sent first, 2292 then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally 2293 \c B_READ_TO_RUN. 2294 2295 \c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains 2296 more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not 2297 \c NULL. 2298 2299 The ownership of all supplied objects retains to the caller. 2300 2301 \param team The team ID of the target application. 2302 \param argc Number of elements in \a args. 2303 \param args Argument vector to be sent to the target. May be \c NULL. 2304 \param messageList List of BMessages to be sent to the target. May be 2305 \c NULL or empty. 2306 \param ref entry_ref to be sent to the target. May be \c NULL. 2307 \param readyToRun \c true, if a \c B_READY_TO_RUN message shall be sent, 2308 \c false otherwise. 2309 \return 2310 - \c B_OK: Everything went fine. 2311 - an error code otherwise 2312 */ 2313 status_t 2314 BRoster::_SendToRunning(team_id team, int argc, const char *const *args, 2315 const BList *messageList, const entry_ref *ref, 2316 bool readyToRun) const 2317 { 2318 status_t error = B_OK; 2319 // Construct a messenger to the app: We can't use the public constructor, 2320 // since the target application may be B_ARGV_ONLY. 2321 app_info info; 2322 error = GetRunningAppInfo(team, &info); 2323 if (error == B_OK) { 2324 BMessenger messenger; 2325 BMessenger::Private(messenger).SetTo(team, info.port, B_PREFERRED_TOKEN); 2326 // send messages from the list 2327 if (messageList) { 2328 for (int32 i = 0; 2329 BMessage *message = (BMessage*)messageList->ItemAt(i); 2330 i++) { 2331 messenger.SendMessage(message); 2332 } 2333 } 2334 // send B_ARGV_RECEIVED 2335 if (args && argc > 1) { 2336 BMessage message(B_ARGV_RECEIVED); 2337 message.AddInt32("argc", argc); 2338 for (int32 i = 0; i < argc; i++) 2339 message.AddString("argv", args[i]); 2340 messenger.SendMessage(&message); 2341 } 2342 // send B_REFS_RECEIVED 2343 if (ref) { 2344 BMessage message(B_REFS_RECEIVED); 2345 message.AddRef("refs", ref); 2346 messenger.SendMessage(&message); 2347 } 2348 // send B_READY_TO_RUN 2349 if (readyToRun) 2350 messenger.SendMessage(B_READY_TO_RUN); 2351 } 2352 return error; 2353 } 2354 2355 2356 void 2357 BRoster::_InitMessengers() 2358 { 2359 DBG(OUT("BRoster::InitMessengers()\n")); 2360 2361 // find the registrar port 2362 port_id rosterPort = find_port(BPrivate::get_roster_port_name()); 2363 port_info info; 2364 if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) { 2365 DBG(OUT(" found roster port\n")); 2366 // ask for the MIME messenger 2367 BMessenger::Private(fMessenger).SetTo(info.team, rosterPort, 2368 B_PREFERRED_TOKEN); 2369 BMessage reply; 2370 status_t error = fMessenger.SendMessage(B_REG_GET_MIME_MESSENGER, &reply); 2371 if (error == B_OK && reply.what == B_REG_SUCCESS) { 2372 DBG(OUT(" got reply from roster\n")); 2373 reply.FindMessenger("messenger", &fMimeMessenger); 2374 } else { 2375 DBG(OUT(" no (useful) reply from roster: error: %lx: %s\n", error, 2376 strerror(error))); 2377 if (error == B_OK) 2378 DBG(reply.PrintToStream()); 2379 } 2380 } 2381 DBG(OUT("BRoster::InitMessengers() done\n")); 2382 } 2383 2384 2385 /*! \brief Sends a request to the roster to add the application with the 2386 given signature to the front of the recent apps list. 2387 */ 2388 void 2389 BRoster::_AddToRecentApps(const char *appSig) const 2390 { 2391 status_t error = B_OK; 2392 // compose the request message 2393 BMessage request(B_REG_ADD_TO_RECENT_APPS); 2394 if (error == B_OK) 2395 error = request.AddString("app sig", appSig); 2396 // send the request 2397 BMessage reply; 2398 if (error == B_OK) 2399 error = fMessenger.SendMessage(&request, &reply); 2400 // evaluate the reply 2401 status_t result; 2402 if (error == B_OK) 2403 error = reply.what == B_REG_RESULT ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2404 if (error == B_OK) 2405 error = reply.FindInt32("result", &result); 2406 if (error == B_OK) 2407 error = result; 2408 // Nothing to return... how sad :-( 2409 // return error; 2410 } 2411 2412 /*! \brief Sends a request to the roster to clear the recent 2413 documents list. 2414 */ 2415 void 2416 BRoster::_ClearRecentDocuments() const 2417 { 2418 BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS); 2419 BMessage reply; 2420 fMessenger.SendMessage(&request, &reply); 2421 } 2422 2423 /*! \brief Sends a request to the roster to clear the recent 2424 documents list. 2425 */ 2426 void 2427 BRoster::_ClearRecentFolders() const 2428 { 2429 BMessage request(B_REG_CLEAR_RECENT_FOLDERS); 2430 BMessage reply; 2431 fMessenger.SendMessage(&request, &reply); 2432 } 2433 2434 /*! \brief Sends a request to the roster to clear the recent 2435 documents list. 2436 */ 2437 void 2438 BRoster::_ClearRecentApps() const 2439 { 2440 BMessage request(B_REG_CLEAR_RECENT_APPS); 2441 BMessage reply; 2442 fMessenger.SendMessage(&request, &reply); 2443 } 2444 2445 // LoadRecentLists 2446 /*! \brief Loads the system's recently used document, folder, and 2447 application lists from the specified file. 2448 2449 \note The current lists are cleared before loading the new lists 2450 2451 \param filename The name of the file to load from 2452 */ 2453 void 2454 BRoster::_LoadRecentLists(const char *filename) const 2455 { 2456 status_t error = B_OK; 2457 // compose the request message 2458 BMessage request(B_REG_LOAD_RECENT_LISTS); 2459 if (error == B_OK) 2460 error = request.AddString("filename", filename); 2461 // send the request 2462 BMessage reply; 2463 if (error == B_OK) 2464 error = fMessenger.SendMessage(&request, &reply); 2465 // evaluate the reply 2466 status_t result; 2467 if (error == B_OK) 2468 error = reply.what == B_REG_RESULT ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2469 if (error == B_OK) 2470 error = reply.FindInt32("result", &result); 2471 if (error == B_OK) 2472 error = result; 2473 // Nothing to return... how sad :-( 2474 // return error; 2475 } 2476 2477 // SaveRecentLists 2478 /*! \brief Saves the system's recently used document, folder, and 2479 application lists to the specified file. 2480 2481 \param filename The name of the file to save to 2482 */ 2483 void 2484 BRoster::_SaveRecentLists(const char *filename) const 2485 { 2486 status_t error = B_OK; 2487 // compose the request message 2488 BMessage request(B_REG_SAVE_RECENT_LISTS); 2489 if (error == B_OK) 2490 error = request.AddString("filename", filename); 2491 // send the request 2492 BMessage reply; 2493 if (error == B_OK) 2494 error = fMessenger.SendMessage(&request, &reply); 2495 // evaluate the reply 2496 status_t result; 2497 if (error == B_OK) 2498 error = reply.what == B_REG_RESULT ? (status_t)B_OK : (status_t)B_BAD_REPLY; 2499 if (error == B_OK) 2500 error = reply.FindInt32("result", &result); 2501 if (error == B_OK) 2502 error = result; 2503 // Nothing to return... how sad :-( 2504 // return error; 2505 } 2506 2507 2508 // #pragma mark - Helper functions 2509 2510 2511 /*! \brief Extracts an app_info from a BMessage. 2512 2513 The function searchs for a field "app_info" typed B_REG_APP_INFO_TYPE 2514 and initializes \a info with the found data. 2515 2516 \param message The message 2517 \param info A pointer to a pre-allocated app_info to be filled in with the 2518 info found in the message. 2519 \return 2520 - \c B_OK: Everything went fine. 2521 - \c B_BAD_VALUE: \c NULL \a message or \a info. 2522 - other error codes 2523 */ 2524 static 2525 status_t 2526 find_message_app_info(BMessage *message, app_info *info) 2527 { 2528 status_t error = (message && info ? B_OK : B_BAD_VALUE); 2529 const flat_app_info *flatInfo = NULL; 2530 ssize_t size = 0; 2531 // find the flat app info in the message 2532 if (error == B_OK) { 2533 error = message->FindData("app_info", B_REG_APP_INFO_TYPE, 2534 (const void**)&flatInfo, &size); 2535 } 2536 // unflatten the flat info 2537 if (error == B_OK) { 2538 if (size == sizeof(flat_app_info)) { 2539 memcpy(info, &flatInfo->info, sizeof(app_info)); 2540 info->ref.name = NULL; 2541 if (strlen(flatInfo->ref_name) > 0) 2542 info->ref.set_name(flatInfo->ref_name); 2543 } else 2544 error = B_ERROR; 2545 } 2546 return error; 2547 } 2548 2549 // query_for_app 2550 /*! \brief Finds an app by signature on any mounted volume. 2551 \param signature The app's signature. 2552 \param appRef A pointer to a pre-allocated entry_ref to be filled with 2553 a reference to the found application's executable. 2554 \return 2555 - \c B_OK: Everything went fine. 2556 - \c B_BAD_VALUE: \c NULL \a signature or \a appRef. 2557 - B_LAUNCH_FAILED_APP_NOT_FOUND: An application with this signature 2558 could not be found. 2559 - other error codes 2560 */ 2561 static 2562 status_t 2563 query_for_app(const char *signature, entry_ref *appRef) 2564 { 2565 if (signature == NULL || appRef == NULL) 2566 return B_BAD_VALUE; 2567 2568 status_t error = B_LAUNCH_FAILED_APP_NOT_FOUND; 2569 bool caseInsensitive = false; 2570 2571 while (true) { 2572 // search on all volumes 2573 BVolumeRoster volumeRoster; 2574 BVolume volume; 2575 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2576 if (!volume.KnowsQuery()) 2577 continue; 2578 2579 index_info info; 2580 if (fs_stat_index(volume.Device(), "BEOS:APP_SIG", &info) != 0) { 2581 // This volume doesn't seem to have the index we're looking for; 2582 // querying it might need a long time, and we don't care *that* 2583 // much... 2584 continue; 2585 } 2586 2587 BQuery query; 2588 query.SetVolume(&volume); 2589 query.PushAttr("BEOS:APP_SIG"); 2590 if (!caseInsensitive) 2591 query.PushString(signature); 2592 else { 2593 // second pass, create a case insensitive query string 2594 char string[B_MIME_TYPE_LENGTH * 4]; 2595 strcpy(string, "application/"); 2596 2597 int32 length = strlen(string); 2598 const char *from = signature + length; 2599 char *to = string + length; 2600 2601 for (; from[0]; from++) { 2602 if (isalpha(from[0])) { 2603 *to++ = '['; 2604 *to++ = tolower(from[0]); 2605 *to++ = toupper(from[0]); 2606 *to++ = ']'; 2607 } else 2608 *to++ = from[0]; 2609 } 2610 2611 to[0] = '\0'; 2612 query.PushString(string); 2613 } 2614 query.PushOp(B_EQ); 2615 2616 query.Fetch(); 2617 2618 // walk through the query 2619 bool appFound = false; 2620 status_t foundAppError = B_OK; 2621 entry_ref ref; 2622 while (query.GetNextRef(&ref) == B_OK) { 2623 if ((!appFound || compare_app_versions(appRef, &ref) < 0) 2624 && (foundAppError = can_app_be_used(&ref)) == B_OK) { 2625 *appRef = ref; 2626 appFound = true; 2627 } 2628 } 2629 if (!appFound) { 2630 // If the query didn't return any hits, the error is 2631 // B_LAUNCH_FAILED_APP_NOT_FOUND, otherwise we return the 2632 // result of the last can_app_be_used(). 2633 error = foundAppError != B_OK 2634 ? foundAppError : B_LAUNCH_FAILED_APP_NOT_FOUND; 2635 } else 2636 return B_OK; 2637 } 2638 2639 if (!caseInsensitive) 2640 caseInsensitive = true; 2641 else 2642 break; 2643 } 2644 2645 return error; 2646 } 2647 2648 2649 // can_app_be_used 2650 /*! \brief Checks whether or not an application can be used. 2651 2652 Currently it is only checked whether the application is in the trash. 2653 2654 \param ref An entry_ref referring to the application executable. 2655 \return 2656 - \c B_OK: The application can be used. 2657 - \c B_ENTRY_NOT_FOUND: \a ref doesn't refer to and existing entry. 2658 - \c B_IS_A_DIRECTORY: \a ref refers to a directory. 2659 - \c B_LAUNCH_FAILED_APP_IN_TRASH: The application executable is in the 2660 trash. 2661 - other error codes specifying why the application cannot be used. 2662 */ 2663 static 2664 status_t 2665 can_app_be_used(const entry_ref *ref) 2666 { 2667 status_t error = (ref ? B_OK : B_BAD_VALUE); 2668 // check whether the file exists and is a file. 2669 BEntry entry; 2670 if (error == B_OK) 2671 error = entry.SetTo(ref, true); 2672 if (error == B_OK && !entry.Exists()) 2673 error = B_ENTRY_NOT_FOUND; 2674 if (error == B_OK && !entry.IsFile()) 2675 error = B_IS_A_DIRECTORY; 2676 // check whether the file is in trash 2677 BPath trashPath; 2678 BDirectory directory; 2679 if (error == B_OK 2680 && find_directory(B_TRASH_DIRECTORY, &trashPath) == B_OK 2681 && directory.SetTo(trashPath.Path()) == B_OK 2682 && directory.Contains(&entry)) { 2683 error = B_LAUNCH_FAILED_APP_IN_TRASH; 2684 } 2685 return error; 2686 } 2687 2688 // compare_version_infos 2689 /*! \brief Compares the supplied version infos. 2690 \param info1 The first info. 2691 \param info2 The second info. 2692 \return \c -1, if the first info is less than the second one, \c 1, if 2693 the first one is greater than the second one, and \c 0, if both 2694 are equal. 2695 */ 2696 static 2697 int32 2698 compare_version_infos(const version_info &info1, const version_info &info2) 2699 { 2700 int32 result = 0; 2701 if (info1.major < info2.major) 2702 result = -1; 2703 else if (info1.major > info2.major) 2704 result = 1; 2705 else if (info1.middle < info2.middle) 2706 result = -1; 2707 else if (info1.middle > info2.middle) 2708 result = 1; 2709 else if (info1.minor < info2.minor) 2710 result = -1; 2711 else if (info1.minor > info2.minor) 2712 result = 1; 2713 else if (info1.variety < info2.variety) 2714 result = -1; 2715 else if (info1.variety > info2.variety) 2716 result = 1; 2717 else if (info1.internal < info2.internal) 2718 result = -1; 2719 else if (info1.internal > info2.internal) 2720 result = 1; 2721 return result; 2722 } 2723 2724 // compare_app_versions 2725 /*! \brief Compares the version of two applications. 2726 2727 If both files have a version info, then those are compared. 2728 If one file has a version info, it is said to be greater. If both 2729 files have no version info, their modification times are compared. 2730 2731 \param app1 An entry_ref referring to the first application. 2732 \param app2 An entry_ref referring to the second application. 2733 \return \c -1, if the first application version is less than the second 2734 one, \c 1, if the first one is greater than the second one, and 2735 \c 0, if both are equal. 2736 */ 2737 static 2738 int32 2739 compare_app_versions(const entry_ref *app1, const entry_ref *app2) 2740 { 2741 BFile file1, file2; 2742 BAppFileInfo appFileInfo1, appFileInfo2; 2743 file1.SetTo(app1, B_READ_ONLY); 2744 file2.SetTo(app2, B_READ_ONLY); 2745 appFileInfo1.SetTo(&file1); 2746 appFileInfo2.SetTo(&file2); 2747 time_t modificationTime1 = 0; 2748 time_t modificationTime2 = 0; 2749 file1.GetModificationTime(&modificationTime1); 2750 file2.GetModificationTime(&modificationTime2); 2751 int32 result = 0; 2752 version_info versionInfo1, versionInfo2; 2753 bool hasVersionInfo1 = (appFileInfo1.GetVersionInfo( 2754 &versionInfo1, B_APP_VERSION_KIND) == B_OK); 2755 bool hasVersionInfo2 = (appFileInfo2.GetVersionInfo( 2756 &versionInfo2, B_APP_VERSION_KIND) == B_OK); 2757 if (hasVersionInfo1) { 2758 if (hasVersionInfo2) 2759 result = compare_version_infos(versionInfo1, versionInfo2); 2760 else 2761 result = 1; 2762 } else { 2763 if (hasVersionInfo2) 2764 result = -1; 2765 else if (modificationTime1 < modificationTime2) 2766 result = -1; 2767 else if (modificationTime1 > modificationTime2) 2768 result = 1; 2769 } 2770 return result; 2771 } 2772 2773