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