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