1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2002, OpenBeOS 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: TRoster.cpp 23 // Author: Ingo Weinhold (bonefish@users.sf.net) 24 // Description: TRoster is the incarnation of The Roster. It manages the 25 // running applications. 26 //------------------------------------------------------------------------------ 27 28 #include <new> 29 30 #include <Application.h> 31 #include <AppMisc.h> 32 #include <storage_support.h> 33 34 #include <errno.h> 35 #include <stdio.h> 36 37 #include "Debug.h" 38 #include "RegistrarDefs.h" 39 #include "RosterAppInfo.h" 40 #include "RosterSettingsCharStream.h" 41 #include "TRoster.h" 42 #include "EventMaskWatcher.h" 43 44 //------------------------------------------------------------------------------ 45 // Private local function declarations 46 //------------------------------------------------------------------------------ 47 48 static bool larger_index(const recent_entry *entry1, const recent_entry *entry2); 49 50 51 //------------------------------------------------------------------------------ 52 // TRoster 53 //------------------------------------------------------------------------------ 54 55 /*! 56 \class TRoster 57 \brief Implements the application roster. 58 59 This class handles the BRoster requests. For each kind a hook method is 60 implemented to which the registrar looper dispatches the request messages. 61 62 Registered and pre-registered are managed via AppInfoLists. 63 \a fEarlyPreRegisteredApps contains the infos for those application that 64 are pre-registered and currently have no team ID assigned to them yet, 65 whereas the infos of registered and pre-registered applications with a 66 team ID are to be found in \a fRegisteredApps. 67 68 When an application asks whether it is pre-registered or not and there 69 are one or more instances of the application that are pre-registered, but 70 have no team ID assigned yet, the reply to the request has to be 71 postponed until the status of the requesting team is clear. The request 72 message is dequeued from the registrar's message queue and, with 73 additional information (IAPRRequest), added to \a fIAPRRequests for a 74 later reply. 75 76 The field \a fActiveApp identifies the currently active application 77 and \a fLastToken is a counter used to generate unique tokens for 78 pre-registered applications. 79 */ 80 81 //! The maximal period of time an app may be early pre-registered (60 s). 82 const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL; 83 84 const char *TRoster::kDefaultRosterSettingsFile = 85 "/boot/home/config/settings/Roster/OpenBeOSRosterSettings"; 86 87 // constructor 88 /*! \brief Creates a new roster. 89 90 The object is completely initialized and ready to handle requests. 91 */ 92 TRoster::TRoster() 93 : fRegisteredApps(), 94 fEarlyPreRegisteredApps(), 95 fIAPRRequests(), 96 fActiveApp(NULL), 97 fWatchingService(), 98 fRecentApps(), 99 fRecentDocuments(), 100 fRecentFolders(), 101 fLastToken(0) 102 { 103 _LoadRosterSettings(); 104 } 105 106 // destructor 107 /*! \brief Frees all resources associated with this object. 108 */ 109 TRoster::~TRoster() 110 { 111 } 112 113 // HandleAddApplication 114 /*! \brief Handles an AddApplication() request. 115 \param request The request message 116 */ 117 void 118 TRoster::HandleAddApplication(BMessage *request) 119 { 120 FUNCTION_START(); 121 122 status_t error = B_OK; 123 // get the parameters 124 const char *signature; 125 entry_ref ref; 126 uint32 flags; 127 team_id team; 128 thread_id thread; 129 port_id port; 130 bool fullReg; 131 if (request->FindString("signature", &signature) != B_OK) 132 signature = NULL; 133 if (request->FindRef("ref", &ref) != B_OK) 134 SET_ERROR(error, B_BAD_VALUE); 135 if (request->FindInt32("flags", (int32*)&flags) != B_OK) 136 flags = B_REG_DEFAULT_APP_FLAGS; 137 if (request->FindInt32("team", &team) != B_OK) 138 team = -1; 139 if (request->FindInt32("thread", &thread) != B_OK) 140 thread = -1; 141 if (request->FindInt32("port", &port) != B_OK) 142 port = -1; 143 if (request->FindBool("full_registration", &fullReg) != B_OK) 144 fullReg = false; 145 PRINT(("team: %ld, signature: %s\n", team, signature)); 146 PRINT(("full registration: %d\n", fullReg)); 147 // check the parameters 148 team_id otherTeam = -1; 149 uint32 launchFlags = flags & B_LAUNCH_MASK; 150 // entry_ref 151 if (error == B_OK) { 152 // the entry_ref must be valid 153 if (BEntry(&ref).Exists()) { 154 PRINT(("flags: %lx\n", flags)); 155 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name)); 156 // check single/exclusive launchers 157 RosterAppInfo *info = NULL; 158 if ((launchFlags == B_SINGLE_LAUNCH 159 || launchFlags == B_EXCLUSIVE_LAUNCH) 160 && (((info = fRegisteredApps.InfoFor(&ref))) 161 || ((info = fEarlyPreRegisteredApps.InfoFor(&ref))))) { 162 SET_ERROR(error, B_ALREADY_RUNNING); 163 otherTeam = info->team; 164 } 165 } else 166 SET_ERROR(error, B_ENTRY_NOT_FOUND); 167 } 168 // signature 169 if (error == B_OK && signature) { 170 // check exclusive launchers 171 RosterAppInfo *info = NULL; 172 if (launchFlags == B_EXCLUSIVE_LAUNCH 173 && (((info = fRegisteredApps.InfoFor(signature))) 174 || ((info = fEarlyPreRegisteredApps.InfoFor(signature))))) { 175 SET_ERROR(error, B_ALREADY_RUNNING); 176 otherTeam = info->team; 177 } 178 } 179 // If no team ID is given, full registration isn't possible. 180 if (error == B_OK) { 181 if (team < 0) { 182 if (fullReg) 183 SET_ERROR(error, B_BAD_VALUE); 184 } else if (fRegisteredApps.InfoFor(team)) 185 SET_ERROR(error, B_REG_ALREADY_REGISTERED); 186 } 187 // Add the application info. 188 uint32 token = 0; 189 if (error == B_OK) { 190 // alloc and init the info 191 RosterAppInfo *info = new(nothrow) RosterAppInfo; 192 if (info) { 193 info->Init(thread, team, port, flags, &ref, signature); 194 if (fullReg) 195 info->state = APP_STATE_REGISTERED; 196 else 197 info->state = APP_STATE_PRE_REGISTERED; 198 info->registration_time = system_time(); 199 // add it to the right list 200 bool addingSuccess = false; 201 if (team >= 0) 202 { 203 PRINT(("added ref: %ld, %lld, %s\n", info->ref.device, info->ref.directory, info->ref.name)); 204 addingSuccess = (AddApp(info) == B_OK); 205 } 206 else { 207 token = info->token = _NextToken(); 208 addingSuccess = fEarlyPreRegisteredApps.AddInfo(info); 209 PRINT(("added to early pre-regs, token: %lu\n", token)); 210 } 211 if (!addingSuccess) 212 SET_ERROR(error, B_NO_MEMORY); 213 } else 214 SET_ERROR(error, B_NO_MEMORY); 215 // delete the info on failure 216 if (error != B_OK && info) 217 delete info; 218 } 219 // reply to the request 220 if (error == B_OK) { 221 // add to recent apps if successful 222 if (signature && signature[0] != '\0') 223 fRecentApps.Add(signature, flags); 224 else 225 fRecentApps.Add(&ref, flags); 226 // fRecentApps.Print(); 227 228 BMessage reply(B_REG_SUCCESS); 229 // The token is valid only when no team ID has been supplied. 230 if (team < 0) 231 reply.AddInt32("token", (int32)token); 232 request->SendReply(&reply); 233 } else { 234 BMessage reply(B_REG_ERROR); 235 reply.AddInt32("error", error); 236 if (otherTeam >= 0) 237 reply.AddInt32("other_team", otherTeam); 238 request->SendReply(&reply); 239 } 240 241 FUNCTION_END(); 242 } 243 244 // HandleCompleteRegistration 245 /*! \brief Handles a CompleteRegistration() request. 246 \param request The request message 247 */ 248 void 249 TRoster::HandleCompleteRegistration(BMessage *request) 250 { 251 FUNCTION_START(); 252 253 status_t error = B_OK; 254 // get the parameters 255 team_id team; 256 thread_id thread; 257 port_id port; 258 if (request->FindInt32("team", &team) != B_OK) 259 team = -1; 260 if (request->FindInt32("thread", &thread) != B_OK) 261 thread = -1; 262 if (request->FindInt32("port", &port) != B_OK) 263 port = -1; 264 // check the parameters 265 // port 266 if (error == B_OK && port < 0) 267 SET_ERROR(error, B_BAD_VALUE); 268 // thread 269 if (error == B_OK && thread < 0) 270 SET_ERROR(error, B_BAD_VALUE); 271 // team 272 if (error == B_OK) { 273 if (team >= 0) { 274 // everything is fine -- set the values 275 RosterAppInfo *info = fRegisteredApps.InfoFor(team); 276 if (info && info->state == APP_STATE_PRE_REGISTERED) { 277 info->thread = thread; 278 info->port = port; 279 info->state = APP_STATE_REGISTERED; 280 } else 281 SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED); 282 } else 283 SET_ERROR(error, B_BAD_VALUE); 284 } 285 // reply to the request 286 if (error == B_OK) { 287 BMessage reply(B_REG_SUCCESS); 288 request->SendReply(&reply); 289 } else { 290 BMessage reply(B_REG_ERROR); 291 reply.AddInt32("error", error); 292 request->SendReply(&reply); 293 } 294 295 FUNCTION_END(); 296 } 297 298 // HandleIsAppPreRegistered 299 /*! \brief Handles an IsAppPreRegistered() request. 300 \param request The request message 301 */ 302 void 303 TRoster::HandleIsAppPreRegistered(BMessage *request) 304 { 305 FUNCTION_START(); 306 307 status_t error = B_OK; 308 // get the parameters 309 entry_ref ref; 310 team_id team; 311 if (request->FindRef("ref", &ref) != B_OK) 312 SET_ERROR(error, B_BAD_VALUE); 313 if (request->FindInt32("team", &team) != B_OK) 314 team = -1; 315 PRINT(("team: %ld\n", team)); 316 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name)); 317 // check the parameters 318 // entry_ref 319 if (error == B_OK & !BEntry(&ref).Exists()) 320 SET_ERROR(error, B_ENTRY_NOT_FOUND); 321 // team 322 if (error == B_OK && team < 0) 323 SET_ERROR(error, B_BAD_VALUE); 324 // look up the information 325 RosterAppInfo *info = NULL; 326 if (error == B_OK) { 327 if ((info = fRegisteredApps.InfoFor(team)) != NULL) { 328 PRINT(("found team in fRegisteredApps\n")); 329 _ReplyToIAPRRequest(request, info); 330 } else if ((info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL) { 331 PRINT(("found ref in fEarlyRegisteredApps\n")); 332 // pre-registered and has no team ID assigned yet -- queue the 333 // request 334 be_app->DetachCurrentMessage(); 335 IAPRRequest queuedRequest = { ref, team, request }; 336 fIAPRRequests[team] = queuedRequest; 337 } else { 338 PRINT(("didn't find team or ref\n")); 339 // team not registered, ref not early pre-registered 340 _ReplyToIAPRRequest(request, NULL); 341 } 342 } else { 343 // reply to the request on error 344 BMessage reply(B_REG_ERROR); 345 reply.AddInt32("error", error); 346 request->SendReply(&reply); 347 } 348 349 FUNCTION_END(); 350 } 351 352 // HandleRemovePreRegApp 353 /*! \brief Handles a RemovePreRegApp() request. 354 \param request The request message 355 */ 356 void 357 TRoster::HandleRemovePreRegApp(BMessage *request) 358 { 359 FUNCTION_START(); 360 361 status_t error = B_OK; 362 // get the parameters 363 uint32 token; 364 if (request->FindInt32("token", (int32*)&token) != B_OK) 365 SET_ERROR(error, B_BAD_VALUE); 366 // remove the app 367 if (error == B_OK) { 368 RosterAppInfo *info = fEarlyPreRegisteredApps.InfoForToken(token); 369 if (info) { 370 fEarlyPreRegisteredApps.RemoveInfo(info); 371 delete info; 372 } else 373 SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED); 374 } 375 // reply to the request 376 if (error == B_OK) { 377 BMessage reply(B_REG_SUCCESS); 378 request->SendReply(&reply); 379 } else { 380 BMessage reply(B_REG_ERROR); 381 reply.AddInt32("error", error); 382 request->SendReply(&reply); 383 } 384 385 FUNCTION_END(); 386 } 387 388 // HandleRemoveApp 389 /*! \brief Handles a RemoveApp() request. 390 \param request The request message 391 */ 392 void 393 TRoster::HandleRemoveApp(BMessage *request) 394 { 395 FUNCTION_START(); 396 397 status_t error = B_OK; 398 // get the parameters 399 team_id team; 400 if (request->FindInt32("team", &team) != B_OK) 401 team = -1; 402 PRINT(("team: %ld\n", team)); 403 // remove the app 404 if (error == B_OK) { 405 if (RosterAppInfo *info = fRegisteredApps.InfoFor(team)) { 406 RemoveApp(info); 407 delete info; 408 } else 409 SET_ERROR(error, B_REG_APP_NOT_REGISTERED); 410 } 411 // reply to the request 412 if (error == B_OK) { 413 BMessage reply(B_REG_SUCCESS); 414 request->SendReply(&reply); 415 } else { 416 BMessage reply(B_REG_ERROR); 417 reply.AddInt32("error", error); 418 request->SendReply(&reply); 419 } 420 421 FUNCTION_END(); 422 } 423 424 // HandleSetThreadAndTeam 425 /*! \brief Handles a SetThreadAndTeam() request. 426 \param request The request message 427 */ 428 void 429 TRoster::HandleSetThreadAndTeam(BMessage *request) 430 { 431 FUNCTION_START(); 432 433 status_t error = B_OK; 434 // get the parameters 435 team_id team; 436 thread_id thread; 437 uint32 token; 438 if (request->FindInt32("team", &team) != B_OK) 439 team = -1; 440 if (request->FindInt32("thread", &thread) != B_OK) 441 thread = -1; 442 if (request->FindInt32("token", (int32*)&token) != B_OK) 443 SET_ERROR(error, B_BAD_VALUE); 444 // check the parameters 445 // team 446 if (error == B_OK && team < 0) 447 SET_ERROR(error, B_BAD_VALUE); 448 PRINT(("team: %ld, thread: %ld, token: %lu\n", team, thread, token)); 449 // update the app_info 450 if (error == B_OK) { 451 RosterAppInfo *info = fEarlyPreRegisteredApps.InfoForToken(token); 452 if (info) { 453 // Set thread and team, create a port for the application and 454 // move the app_info from the list of the early pre-registered 455 // apps to the list of the (pre-)registered apps. 456 fEarlyPreRegisteredApps.RemoveInfo(info); 457 info->team = team; 458 info->thread = thread; 459 // create and transfer the port 460 info->port = create_port(B_REG_APP_LOOPER_PORT_CAPACITY, 461 kRAppLooperPortName); 462 if (info->port < 0) 463 SET_ERROR(error, info->port); 464 if (error == B_OK) 465 SET_ERROR(error, set_port_owner(info->port, team)); 466 // add the info to the registered apps list 467 if (error == B_OK) 468 SET_ERROR(error, AddApp(info)); 469 // cleanup on failure 470 if (error != B_OK) { 471 if (info->port >= 0) 472 delete_port(info->port); 473 delete info; 474 } 475 // handle a pending IsAppPreRegistered() request 476 IAPRRequestMap::iterator it = fIAPRRequests.find(team); 477 if (it != fIAPRRequests.end()) { 478 IAPRRequest &request = it->second; 479 if (error == B_OK) 480 _ReplyToIAPRRequest(request.request, info); 481 delete request.request; 482 fIAPRRequests.erase(it); 483 } 484 } else 485 SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED); 486 } 487 // reply to the request 488 if (error == B_OK) { 489 BMessage reply(B_REG_SUCCESS); 490 request->SendReply(&reply); 491 } else { 492 BMessage reply(B_REG_ERROR); 493 reply.AddInt32("error", error); 494 request->SendReply(&reply); 495 } 496 497 FUNCTION_END(); 498 } 499 500 // HandleSetSignature 501 /*! \brief Handles a SetSignature() request. 502 \param request The request message 503 */ 504 void 505 TRoster::HandleSetSignature(BMessage *request) 506 { 507 FUNCTION_START(); 508 509 status_t error = B_OK; 510 // get the parameters 511 team_id team; 512 const char *signature; 513 if (request->FindInt32("team", &team) != B_OK) 514 error = B_BAD_VALUE; 515 if (request->FindString("signature", &signature) != B_OK) 516 error = B_BAD_VALUE; 517 // find the app and set the signature 518 if (error == B_OK) { 519 if (RosterAppInfo *info = fRegisteredApps.InfoFor(team)) 520 strcpy(info->signature, signature); 521 else 522 SET_ERROR(error, B_REG_APP_NOT_REGISTERED); 523 } 524 // reply to the request 525 if (error == B_OK) { 526 BMessage reply(B_REG_SUCCESS); 527 request->SendReply(&reply); 528 } else { 529 BMessage reply(B_REG_ERROR); 530 reply.AddInt32("error", error); 531 request->SendReply(&reply); 532 } 533 534 FUNCTION_END(); 535 } 536 537 // HandleGetAppInfo 538 /*! \brief Handles a Get{Running,Active,}AppInfo() request. 539 \param request The request message 540 */ 541 void 542 TRoster::HandleGetAppInfo(BMessage *request) 543 { 544 FUNCTION_START(); 545 546 status_t error = B_OK; 547 // get the parameters 548 team_id team; 549 entry_ref ref; 550 const char *signature; 551 bool hasTeam = true; 552 bool hasRef = true; 553 bool hasSignature = true; 554 if (request->FindInt32("team", &team) != B_OK) 555 hasTeam = false; 556 if (request->FindRef("ref", &ref) != B_OK) 557 hasRef = false; 558 if (request->FindString("signature", &signature) != B_OK) 559 hasSignature = false; 560 if (hasTeam) 561 PRINT(("team: %ld\n", team)); 562 if (hasRef) 563 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name)); 564 if (hasSignature) 565 PRINT(("signature: %s\n", signature)); 566 // get the info 567 RosterAppInfo *info = NULL; 568 if (error == B_OK) { 569 if (hasTeam) { 570 info = fRegisteredApps.InfoFor(team); 571 if (info == NULL) 572 SET_ERROR(error, B_BAD_TEAM_ID); 573 } else if (hasRef) { 574 info = fRegisteredApps.InfoFor(&ref); 575 if (info == NULL) 576 SET_ERROR(error, B_ERROR); 577 } else if (hasSignature) { 578 info = fRegisteredApps.InfoFor(signature); 579 if (info == NULL) 580 SET_ERROR(error, B_ERROR); 581 } else { 582 // If neither of those has been supplied, the active application 583 // info is requested. 584 if (fActiveApp) 585 info = fActiveApp; 586 else 587 SET_ERROR(error, B_ERROR); 588 } 589 } 590 // reply to the request 591 if (error == B_OK) { 592 BMessage reply(B_REG_SUCCESS); 593 _AddMessageAppInfo(&reply, info); 594 request->SendReply(&reply); 595 } else { 596 BMessage reply(B_REG_ERROR); 597 reply.AddInt32("error", error); 598 request->SendReply(&reply); 599 } 600 601 FUNCTION_END(); 602 } 603 604 // HandleGetAppList 605 /*! \brief Handles a GetAppList() request. 606 \param request The request message 607 */ 608 void 609 TRoster::HandleGetAppList(BMessage *request) 610 { 611 FUNCTION_START(); 612 613 status_t error = B_OK; 614 // get the parameters 615 const char *signature; 616 if (request->FindString("signature", &signature) != B_OK) 617 signature = NULL; 618 // reply to the request 619 if (error == B_OK) { 620 BMessage reply(B_REG_SUCCESS); 621 // get the list 622 for (AppInfoList::Iterator it(fRegisteredApps.It()); 623 RosterAppInfo *info = *it; 624 ++it) { 625 if (!signature || !strcmp(signature, info->signature)) 626 reply.AddInt32("teams", info->team); 627 } 628 request->SendReply(&reply); 629 } else { 630 BMessage reply(B_REG_ERROR); 631 reply.AddInt32("error", error); 632 request->SendReply(&reply); 633 } 634 635 FUNCTION_END(); 636 } 637 638 // HandleActivateApp 639 /*! \brief Handles a ActivateApp() request. 640 \param request The request message 641 */ 642 void 643 TRoster::HandleActivateApp(BMessage *request) 644 { 645 FUNCTION_START(); 646 647 status_t error = B_OK; 648 // get the parameters 649 team_id team; 650 if (request->FindInt32("team", &team) != B_OK) 651 error = B_BAD_VALUE; 652 // activate the app 653 if (error == B_OK) { 654 if (RosterAppInfo *info = fRegisteredApps.InfoFor(team)) 655 ActivateApp(info); 656 else 657 error = B_BAD_TEAM_ID; 658 } 659 // reply to the request 660 if (error == B_OK) { 661 BMessage reply(B_REG_SUCCESS); 662 request->SendReply(&reply); 663 } else { 664 BMessage reply(B_REG_ERROR); 665 reply.AddInt32("error", error); 666 request->SendReply(&reply); 667 } 668 669 FUNCTION_END(); 670 } 671 672 // HandleBroadcast 673 /*! \brief Handles a Broadcast() request. 674 \param request The request message 675 */ 676 void 677 TRoster::HandleBroadcast(BMessage *request) 678 { 679 FUNCTION_START(); 680 681 status_t error = B_OK; 682 // get the parameters 683 team_id team; 684 BMessage message; 685 BMessenger replyTarget; 686 if (request->FindInt32("team", &team) != B_OK) 687 team = -1; 688 if (error == B_OK && request->FindMessage("message", &message) != B_OK) 689 error = B_BAD_VALUE; 690 if (error == B_OK 691 && request->FindMessenger("reply_target", &replyTarget) != B_OK) { 692 error = B_BAD_VALUE; 693 } 694 // reply to the request -- do this first, don't let the inquirer wait 695 if (error == B_OK) { 696 BMessage reply(B_REG_SUCCESS); 697 request->SendReply(&reply); 698 } else { 699 BMessage reply(B_REG_ERROR); 700 reply.AddInt32("error", error); 701 request->SendReply(&reply); 702 } 703 // broadcast the message 704 team_id registrarTeam = BPrivate::current_team(); 705 if (error == B_OK) { 706 for (AppInfoList::Iterator it = fRegisteredApps.It(); 707 it.IsValid(); 708 ++it) { 709 // don't send the message to the requesting team or the registrar 710 if ((*it)->team != team && (*it)->team != registrarTeam) { 711 BMessenger messenger((*it)->team, (*it)->port, 0, true); 712 messenger.SendMessage(&message, replyTarget, 0); 713 } 714 } 715 } 716 717 FUNCTION_END(); 718 } 719 720 // HandleStartWatching 721 /*! \brief Handles a StartWatching() request. 722 \param request The request message 723 */ 724 void 725 TRoster::HandleStartWatching(BMessage *request) 726 { 727 FUNCTION_START(); 728 729 status_t error = B_OK; 730 // get the parameters 731 BMessenger target; 732 uint32 events; 733 if (error == B_OK && request->FindMessenger("target", &target) != B_OK) 734 error = B_BAD_VALUE; 735 if (request->FindInt32("events", (int32*)&events) != B_OK) 736 error = B_BAD_VALUE; 737 // add the new watcher 738 if (error == B_OK) { 739 Watcher *watcher = new(nothrow) EventMaskWatcher(target, events); 740 if (watcher) { 741 if (!fWatchingService.AddWatcher(watcher)) { 742 error = B_NO_MEMORY; 743 delete watcher; 744 } 745 } else 746 error = B_NO_MEMORY; 747 } 748 // reply to the request 749 if (error == B_OK) { 750 BMessage reply(B_REG_SUCCESS); 751 request->SendReply(&reply); 752 } else { 753 BMessage reply(B_REG_ERROR); 754 reply.AddInt32("error", error); 755 request->SendReply(&reply); 756 } 757 758 FUNCTION_END(); 759 } 760 761 // HandleStopWatching 762 /*! \brief Handles a StopWatching() request. 763 \param request The request message 764 */ 765 void 766 TRoster::HandleStopWatching(BMessage *request) 767 { 768 FUNCTION_START(); 769 770 status_t error = B_OK; 771 // get the parameters 772 BMessenger target; 773 if (error == B_OK && request->FindMessenger("target", &target) != B_OK) 774 error = B_BAD_VALUE; 775 // remove the watcher 776 if (error == B_OK) { 777 if (!fWatchingService.RemoveWatcher(target)) 778 error = B_BAD_VALUE; 779 } 780 // reply to the request 781 if (error == B_OK) { 782 BMessage reply(B_REG_SUCCESS); 783 request->SendReply(&reply); 784 } else { 785 BMessage reply(B_REG_ERROR); 786 reply.AddInt32("error", error); 787 request->SendReply(&reply); 788 } 789 790 FUNCTION_END(); 791 } 792 793 // HandleGetRecentDocuments 794 /*! \brief Handles a GetRecentDocuments() request. 795 \param request The request message 796 */ 797 void 798 TRoster::HandleGetRecentDocuments(BMessage *request) 799 { 800 FUNCTION_START(); 801 _HandleGetRecentEntries(request); 802 FUNCTION_END(); 803 } 804 805 // HandleGetRecentFolders 806 /*! \brief Handles a GetRecentFolders() request. 807 \param request The request message 808 */ 809 void 810 TRoster::HandleGetRecentFolders(BMessage *request) 811 { 812 FUNCTION_START(); 813 _HandleGetRecentEntries(request); 814 FUNCTION_END(); 815 } 816 817 // HandleGetRecentApps 818 /*! \brief Handles a GetRecentApps() request. 819 \param request The request message 820 */ 821 void 822 TRoster::HandleGetRecentApps(BMessage *request) 823 { 824 FUNCTION_START(); 825 826 if (!request) { 827 D(PRINT(("WARNING: TRoster::HandleGetRecentApps(NULL) called\n"))); 828 return; 829 } 830 831 int32 maxCount; 832 BMessage reply(B_REG_RESULT); 833 834 status_t error = request->FindInt32("max count", &maxCount); 835 if (!error) 836 error = fRecentApps.Get(maxCount, &reply); 837 reply.AddInt32("result", error); 838 request->SendReply(&reply); 839 840 FUNCTION_END(); 841 } 842 843 // HandleAddToRecentDocuments 844 /*! \brief Handles an AddToRecentDocuments() request. 845 \param request The request message 846 */ 847 void 848 TRoster::HandleAddToRecentDocuments(BMessage *request) 849 { 850 FUNCTION_START(); 851 852 if (!request) { 853 D(PRINT(("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n"))); 854 return; 855 } 856 857 entry_ref ref; 858 const char *appSig; 859 BMessage reply(B_REG_RESULT); 860 861 status_t error = request->FindRef("ref", &ref); 862 if (!error) 863 error = request->FindString("app sig", &appSig); 864 if (!error) 865 error = fRecentDocuments.Add(&ref, appSig); 866 reply.AddInt32("result", error); 867 request->SendReply(&reply); 868 869 FUNCTION_END(); 870 } 871 872 // HandleAddToRecentFolders 873 /*! \brief Handles an AddToRecentFolders() request. 874 \param request The request message 875 */ 876 void 877 TRoster::HandleAddToRecentFolders(BMessage *request) 878 { 879 FUNCTION_START(); 880 881 if (!request) { 882 D(PRINT(("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n"))); 883 return; 884 } 885 886 entry_ref ref; 887 const char *appSig; 888 BMessage reply(B_REG_RESULT); 889 890 status_t error = request->FindRef("ref", &ref); 891 if (!error) 892 error = request->FindString("app sig", &appSig); 893 if (!error) 894 error = fRecentFolders.Add(&ref, appSig); 895 reply.AddInt32("result", error); 896 request->SendReply(&reply); 897 898 FUNCTION_END(); 899 } 900 901 // HandleAddToRecentApps 902 /*! \brief Handles an AddToRecentApps() request. 903 \param request The request message 904 */ 905 void 906 TRoster::HandleAddToRecentApps(BMessage *request) 907 { 908 FUNCTION_START(); 909 910 if (!request) { 911 D(PRINT(("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n"))); 912 return; 913 } 914 915 const char *appSig; 916 BMessage reply(B_REG_RESULT); 917 918 status_t error = request->FindString("app sig", &appSig); 919 if (!error) 920 error = fRecentApps.Add(appSig); 921 reply.AddInt32("result", error); 922 request->SendReply(&reply); 923 924 FUNCTION_END(); 925 } 926 927 void 928 TRoster::HandleLoadRecentLists(BMessage *request) 929 { 930 FUNCTION_START(); 931 932 if (!request) { 933 D(PRINT(("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n"))); 934 return; 935 } 936 937 const char *filename; 938 BMessage reply(B_REG_RESULT); 939 940 status_t error = request->FindString("filename", &filename); 941 if (!error) 942 error = _LoadRosterSettings(filename); 943 reply.AddInt32("result", error); 944 request->SendReply(&reply); 945 946 FUNCTION_END(); 947 } 948 949 void 950 TRoster::HandleSaveRecentLists(BMessage *request) 951 { 952 FUNCTION_START(); 953 954 if (!request) { 955 D(PRINT(("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n"))); 956 return; 957 } 958 959 const char *filename; 960 BMessage reply(B_REG_RESULT); 961 962 status_t error = request->FindString("filename", &filename); 963 if (!error) 964 error = _SaveRosterSettings(filename); 965 reply.AddInt32("result", error); 966 request->SendReply(&reply); 967 968 FUNCTION_END(); 969 } 970 971 // ClearRecentDocuments 972 /*! \brief Clears the current list of recent documents 973 */ 974 void 975 TRoster::ClearRecentDocuments() 976 { 977 fRecentDocuments.Clear(); 978 } 979 980 // ClearRecentFolders 981 /*! \brief Clears the current list of recent folders 982 */ 983 void 984 TRoster::ClearRecentFolders() 985 { 986 fRecentFolders.Clear(); 987 } 988 989 // ClearRecentApps 990 /*! \brief Clears the current list of recent apps 991 */ 992 void 993 TRoster::ClearRecentApps() 994 { 995 fRecentApps.Clear(); 996 } 997 998 // Init 999 /*! \brief Initializes the roster. 1000 1001 Currently only adds the registrar to the roster. 1002 The application must already be running, more precisly Run() must have 1003 been called. 1004 1005 \return 1006 - \c B_OK: Everything went fine. 1007 - an error code 1008 */ 1009 status_t 1010 TRoster::Init() 1011 { 1012 status_t error = B_OK; 1013 // create the info 1014 RosterAppInfo *info = new(nothrow) RosterAppInfo; 1015 if (!info) 1016 error = B_NO_MEMORY; 1017 // get the app's ref 1018 entry_ref ref; 1019 if (error == B_OK) 1020 error = get_app_ref(&ref); 1021 // init and add the info 1022 if (error == B_OK) { 1023 info->Init(be_app->Thread(), be_app->Team(), be_app_messenger.fPort, 1024 B_EXCLUSIVE_LAUNCH, &ref, kRegistrarSignature); 1025 info->state = APP_STATE_REGISTERED; 1026 info->registration_time = system_time(); 1027 error = AddApp(info); 1028 } 1029 // cleanup on error 1030 if (error != B_OK && info) 1031 delete info; 1032 return error; 1033 } 1034 1035 // AddApp 1036 /*! \brief Add the supplied app info to the list of (pre-)registered apps. 1037 1038 \param info The app info to be added 1039 */ 1040 status_t 1041 TRoster::AddApp(RosterAppInfo *info) 1042 { 1043 status_t error = (info ? B_OK : B_BAD_VALUE); 1044 if (info) { 1045 if (fRegisteredApps.AddInfo(info)) 1046 _AppAdded(info); 1047 else 1048 error = B_NO_MEMORY; 1049 } 1050 return error; 1051 } 1052 1053 // RemoveApp 1054 /*! \brief Removes the supplied app info from the list of (pre-)registered 1055 apps. 1056 1057 \param info The app info to be removed 1058 */ 1059 void 1060 TRoster::RemoveApp(RosterAppInfo *info) 1061 { 1062 if (info) { 1063 if (fRegisteredApps.RemoveInfo(info)) { 1064 info->state = APP_STATE_UNREGISTERED; 1065 _AppRemoved(info); 1066 } 1067 } 1068 } 1069 1070 // ActivateApp 1071 /*! \brief Activates the application identified by \a info. 1072 1073 The currently activate application is deactivated and the one whose 1074 info is supplied is activated. \a info may be \c NULL, which only 1075 deactivates the currently active application. 1076 1077 \param info The info of the app to be activated 1078 */ 1079 void 1080 TRoster::ActivateApp(RosterAppInfo *info) 1081 { 1082 if (info != fActiveApp) { 1083 // deactivate the currently active app 1084 RosterAppInfo *oldActiveApp = fActiveApp; 1085 fActiveApp = NULL; 1086 if (oldActiveApp) 1087 _AppDeactivated(oldActiveApp); 1088 // activate the new app 1089 if (info) { 1090 info = fActiveApp; 1091 _AppActivated(info); 1092 } 1093 } 1094 } 1095 1096 // CheckSanity 1097 /*! \brief Checks whether the (pre-)registered applications are still running. 1098 1099 This is necessary, since killed applications don't unregister properly. 1100 */ 1101 void 1102 TRoster::CheckSanity() 1103 { 1104 // not early (pre-)registered applications 1105 AppInfoList obsoleteApps; 1106 for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) { 1107 team_info teamInfo; 1108 if (get_team_info((*it)->team, &teamInfo) != B_OK) 1109 obsoleteApps.AddInfo(*it); 1110 } 1111 // remove the apps 1112 for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) { 1113 RemoveApp(*it); 1114 delete *it; 1115 } 1116 // early pre-registered applications 1117 obsoleteApps.MakeEmpty(); 1118 bigtime_t timeLimit = system_time() - kMaximalEarlyPreRegistrationPeriod; 1119 for (AppInfoList::Iterator it = fEarlyPreRegisteredApps.It(); 1120 it.IsValid(); 1121 ++it) { 1122 if ((*it)->registration_time < timeLimit) 1123 obsoleteApps.AddInfo(*it); 1124 } 1125 // remove the apps 1126 for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) { 1127 fEarlyPreRegisteredApps.RemoveInfo(*it); 1128 delete *it; 1129 } 1130 } 1131 1132 1133 // _AppAdded 1134 /*! \brief Hook method invoked, when an application has been added. 1135 \param info The RosterAppInfo of the added application. 1136 */ 1137 void 1138 TRoster::_AppAdded(RosterAppInfo *info) 1139 { 1140 // notify the watchers 1141 BMessage message(B_SOME_APP_LAUNCHED); 1142 _AddMessageWatchingInfo(&message, info); 1143 EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED); 1144 fWatchingService.NotifyWatchers(&message, &filter); 1145 } 1146 1147 // _AppRemoved 1148 /*! \brief Hook method invoked, when an application has been removed. 1149 \param info The RosterAppInfo of the removed application. 1150 */ 1151 void 1152 TRoster::_AppRemoved(RosterAppInfo *info) 1153 { 1154 if (info) { 1155 // deactivate the app, if it was the active one 1156 if (info == fActiveApp) 1157 ActivateApp(NULL); 1158 // notify the watchers 1159 BMessage message(B_SOME_APP_QUIT); 1160 _AddMessageWatchingInfo(&message, info); 1161 EventMaskWatcherFilter filter(B_REQUEST_QUIT); 1162 fWatchingService.NotifyWatchers(&message, &filter); 1163 } 1164 } 1165 1166 // _AppActivated 1167 /*! \brief Hook method invoked, when an application has been activated. 1168 \param info The RosterAppInfo of the activated application. 1169 */ 1170 void 1171 TRoster::_AppActivated(RosterAppInfo *info) 1172 { 1173 if (info) { 1174 if (info->state == APP_STATE_REGISTERED 1175 || info->state == APP_STATE_PRE_REGISTERED) { 1176 // send B_APP_ACTIVATED to the app 1177 BMessenger messenger(info->team, info->port, 0, true); 1178 BMessage message(B_APP_ACTIVATED); 1179 message.AddBool("active", true); 1180 messenger.SendMessage(&message); 1181 // notify the watchers 1182 BMessage watcherMessage(B_SOME_APP_ACTIVATED); 1183 _AddMessageWatchingInfo(&watcherMessage, info); 1184 EventMaskWatcherFilter filter(B_REQUEST_ACTIVATED); 1185 fWatchingService.NotifyWatchers(&watcherMessage, &filter); 1186 } 1187 } 1188 } 1189 1190 // _AppDeactivated 1191 /*! \brief Hook method invoked, when an application has been deactivated. 1192 \param info The RosterAppInfo of the deactivated application. 1193 */ 1194 void 1195 TRoster::_AppDeactivated(RosterAppInfo *info) 1196 { 1197 if (info) { 1198 if (info->state == APP_STATE_REGISTERED 1199 || info->state == APP_STATE_PRE_REGISTERED) { 1200 // send B_APP_ACTIVATED to the app 1201 BMessenger messenger(info->team, info->port, 0, true); 1202 BMessage message(B_APP_ACTIVATED); 1203 message.AddBool("active", false); 1204 messenger.SendMessage(&message); 1205 } 1206 } 1207 } 1208 1209 // _AddMessageAppInfo 1210 /*! \brief Adds an app_info to a message. 1211 1212 The info is added as a flat_app_info to a field "app_info" with the type 1213 \c B_REG_APP_INFO_TYPE. 1214 1215 \param message The message 1216 \param info The app_info. 1217 \return \c B_OK if everything went fine, an error code otherwise. 1218 */ 1219 status_t 1220 TRoster::_AddMessageAppInfo(BMessage *message, const app_info *info) 1221 { 1222 // An app_info is not completely flat. The entry_ref contains a string 1223 // pointer. Therefore we flatten the info. 1224 flat_app_info flatInfo; 1225 flatInfo.info = *info; 1226 // set the ref name to NULL and copy it into the flat structure 1227 flatInfo.info.ref.name = NULL; 1228 flatInfo.ref_name[0] = '\0'; 1229 if (info->ref.name) 1230 strcpy(flatInfo.ref_name, info->ref.name); 1231 // add the flat info 1232 return message->AddData("app_info", B_REG_APP_INFO_TYPE, &flatInfo, 1233 sizeof(flat_app_info)); 1234 } 1235 1236 // _AddMessageWatchingInfo 1237 /*! \brief Adds application monitoring related fields to a message. 1238 \param message The message. 1239 \param info The app_info of the concerned application. 1240 \return \c B_OK if everything went fine, an error code otherwise. 1241 */ 1242 status_t 1243 TRoster::_AddMessageWatchingInfo(BMessage *message, const app_info *info) 1244 { 1245 status_t error = B_OK; 1246 if (error == B_OK) 1247 error = message->AddString("be:signature", info->signature); 1248 if (error == B_OK) 1249 error = message->AddInt32("be:team", info->team); 1250 if (error == B_OK) 1251 error = message->AddInt32("be:thread", info->thread); 1252 if (error == B_OK) 1253 error = message->AddInt32("be:flags", (int32)info->flags); 1254 if (error == B_OK) 1255 error = message->AddRef("be:ref", &info->ref); 1256 return error; 1257 } 1258 1259 // _NextToken 1260 /*! \brief Returns the next available token. 1261 \return The token. 1262 */ 1263 uint32 1264 TRoster::_NextToken() 1265 { 1266 return ++fLastToken; 1267 } 1268 1269 // _ReplyToIAPRRequest 1270 /*! \brief Sends a reply message to a IsAppPreRegistered() request. 1271 1272 The message to be sent is a simple \c B_REG_SUCCESS message containing 1273 a "pre-registered" field, that sais whether or not the application is 1274 pre-registered. It will be set to \c false, unless an \a info is supplied 1275 and the application this info refers to is pre-registered. 1276 1277 \param request The request message to be replied to 1278 \param info The RosterAppInfo of the application in question 1279 (may be \c NULL) 1280 */ 1281 void 1282 TRoster::_ReplyToIAPRRequest(BMessage *request, const RosterAppInfo *info) 1283 { 1284 // pre-registered or registered? 1285 bool preRegistered = false; 1286 if (info) { 1287 switch (info->state) { 1288 case APP_STATE_PRE_REGISTERED: 1289 preRegistered = true; 1290 break; 1291 case APP_STATE_UNREGISTERED: 1292 case APP_STATE_REGISTERED: 1293 preRegistered = false; 1294 break; 1295 } 1296 } 1297 // send reply 1298 BMessage reply(B_REG_SUCCESS); 1299 reply.AddBool("pre-registered", preRegistered); 1300 PRINT(("_ReplyToIAPRRequest(): pre-registered: %d\n", preRegistered)); 1301 if (preRegistered) 1302 _AddMessageAppInfo(&reply, info); 1303 request->SendReply(&reply); 1304 } 1305 1306 // _HandleGetRecentEntries 1307 /*! \brief Handles requests for both GetRecentDocuments() and 1308 GetRecentFolders(). 1309 */ 1310 void 1311 TRoster::_HandleGetRecentEntries(BMessage *request) 1312 { 1313 FUNCTION_START(); 1314 if (!request) { 1315 D(PRINT(("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n"))); 1316 return; 1317 } 1318 1319 int32 maxCount; 1320 BMessage reply(B_REG_RESULT); 1321 char **fileTypes = NULL; 1322 int32 fileTypesCount = 0; 1323 char *appSig = NULL; 1324 1325 status_t error = request->FindInt32("max count", &maxCount); 1326 // Look for optional file type(s) 1327 if (!error) { 1328 type_code typeFound; 1329 status_t typeError = request->GetInfo("file type", &typeFound, &fileTypesCount); 1330 if (!typeError) 1331 typeError = typeFound == B_STRING_TYPE ? B_OK : B_BAD_TYPE; 1332 if (!typeError) { 1333 fileTypes = new(nothrow) char*[fileTypesCount]; 1334 typeError = fileTypes ? B_OK : B_NO_MEMORY; 1335 } 1336 if (!typeError) { 1337 for (int i = 0; !error && i < fileTypesCount; i++) { 1338 const char *type; 1339 if (request->FindString("file type", i, &type) == B_OK) { 1340 fileTypes[i] = new(nothrow) char[B_MIME_TYPE_LENGTH]; 1341 error = fileTypes[i] ? B_OK : B_NO_MEMORY; 1342 // Yes, I do mean to use "error" here, not "typeError" 1343 BPrivate::Storage::to_lower(type, fileTypes[i]); 1344 // Types are expected to be lowercase 1345 } 1346 } 1347 } 1348 } 1349 // Look for optional app sig 1350 if (!error) { 1351 const char *sig; 1352 error = request->FindString("app sig", &sig); 1353 if (!error) { 1354 appSig = new(nothrow) char[B_MIME_TYPE_LENGTH]; 1355 error = appSig ? B_OK : B_NO_MEMORY; 1356 BPrivate::Storage::to_lower(sig, appSig); 1357 } else if (error == B_NAME_NOT_FOUND) 1358 error = B_OK; 1359 } 1360 if (!error) { 1361 switch (request->what) { 1362 case B_REG_GET_RECENT_DOCUMENTS: 1363 error = fRecentDocuments.Get(maxCount, (const char**)fileTypes, 1364 fileTypesCount, appSig, &reply); 1365 D(fRecentDocuments.Print()); 1366 break; 1367 1368 case B_REG_GET_RECENT_FOLDERS: 1369 error = fRecentFolders.Get(maxCount, (const char**)fileTypes, 1370 fileTypesCount, appSig, &reply); 1371 D(fRecentFolders.Print()); 1372 break; 1373 1374 default: 1375 D(PRINT(("WARNING: TRoster::_HandleGetRecentEntries(): unexpected " 1376 "request->what value of 0x%lx\n", request->what))); 1377 error = B_BAD_VALUE; 1378 break; 1379 } 1380 } 1381 reply.AddInt32("result", error); 1382 // Clean up before sending a reply 1383 delete [] appSig; 1384 if (fileTypes) { 1385 for (int i = 0; i < fileTypesCount; i++) 1386 delete [] fileTypes[i]; 1387 delete fileTypes; 1388 fileTypes = NULL; 1389 } 1390 request->SendReply(&reply); 1391 1392 FUNCTION_END(); 1393 } 1394 1395 status_t 1396 TRoster::_LoadRosterSettings(const char *path) 1397 { 1398 const char *settingsPath = path ? path : kDefaultRosterSettingsFile; 1399 1400 RosterSettingsCharStream stream; 1401 status_t error; 1402 BFile file; 1403 1404 error = file.SetTo(settingsPath, B_READ_ONLY); 1405 off_t size; 1406 if (!error) 1407 error = file.GetSize(&size); 1408 char *data; 1409 if (!error) { 1410 data = new(nothrow) char[size]; 1411 error = data ? B_OK : B_NO_MEMORY; 1412 } 1413 if (!error) { 1414 ssize_t bytes = file.Read(data, size); 1415 error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR); 1416 } 1417 if (!error) 1418 error = stream.SetTo(std::string(data)); 1419 if (!error) { 1420 // Clear the current lists as 1421 // we'll be manually building them up 1422 fRecentDocuments.Clear(); 1423 fRecentFolders.Clear(); 1424 fRecentApps.Clear(); 1425 1426 // Now we just walk through the file and read in the info 1427 while (true) { 1428 status_t streamError; 1429 char str[B_PATH_NAME_LENGTH]; 1430 1431 1432 // (RecentDoc | RecentFolder | RecentApp) 1433 streamError = stream.GetString(str); 1434 if (!streamError) { 1435 enum EntryType { 1436 etDoc, 1437 etFolder, 1438 etApp, 1439 etSomethingIsAmiss, 1440 } type; 1441 1442 if (strcmp(str, "RecentDoc") == 0) { 1443 type = etDoc; 1444 } else if (strcmp(str, "RecentFolder") == 0) { 1445 type = etFolder; 1446 } else if (strcmp(str, "RecentApp") == 0) { 1447 type = etApp; 1448 } else { 1449 type = etSomethingIsAmiss; 1450 } 1451 1452 switch (type) { 1453 case etDoc: 1454 case etFolder: 1455 { 1456 // For curing laziness 1457 std::list<recent_entry*> *list = (type == etDoc) 1458 ? &fRecentDocuments.fEntryList 1459 : &fRecentFolders.fEntryList; 1460 1461 char path[B_PATH_NAME_LENGTH]; 1462 char app[B_PATH_NAME_LENGTH]; 1463 char rank[B_PATH_NAME_LENGTH]; 1464 entry_ref ref; 1465 uint32 index = 0; 1466 1467 // Convert the given path to an entry ref 1468 streamError = stream.GetString(path); 1469 if (!streamError) 1470 streamError = get_ref_for_path(path, &ref); 1471 1472 // Add a new entry to the list for each application 1473 // signature and rank we find 1474 while (!streamError) { 1475 if (!streamError) 1476 streamError = stream.GetString(app); 1477 if (!streamError) { 1478 BPrivate::Storage::to_lower(app); 1479 streamError = stream.GetString(rank); 1480 } 1481 if (!streamError) { 1482 index = strtoul(rank, NULL, 10); 1483 if (index == ULONG_MAX) 1484 streamError = errno; 1485 } 1486 recent_entry *entry = NULL; 1487 if (!streamError) { 1488 entry = new(nothrow) recent_entry(&ref, app, index); 1489 streamError = entry ? B_OK : B_NO_MEMORY; 1490 } 1491 if (!streamError) { 1492 printf("pushing entry, leaf == '%s', app == '%s', index == %ld\n", 1493 entry->ref.name, entry->sig.c_str(), entry->index); 1494 1495 list->push_back(entry); 1496 } 1497 } 1498 1499 if (streamError) { 1500 printf("entry error 0x%lx\n", streamError); 1501 if (streamError != RosterSettingsCharStream::kEndOfLine 1502 && streamError != RosterSettingsCharStream::kEndOfStream) 1503 stream.SkipLine(); 1504 } 1505 1506 break; 1507 } 1508 1509 1510 case etApp: 1511 { 1512 char app[B_PATH_NAME_LENGTH]; 1513 streamError = stream.GetString(app); 1514 if (!streamError) { 1515 BPrivate::Storage::to_lower(app); 1516 fRecentApps.fAppList.push_back(app); 1517 } else 1518 stream.SkipLine(); 1519 break; 1520 } 1521 1522 default: 1523 // Something was amiss; skip to the next line 1524 stream.SkipLine(); 1525 break; 1526 } 1527 1528 } 1529 1530 if (streamError == RosterSettingsCharStream::kEndOfStream) 1531 break; 1532 } 1533 1534 // Now we must sort our lists of documents and folders by the 1535 // indicies we read for each entry (largest index first) 1536 fRecentDocuments.fEntryList.sort(larger_index); 1537 fRecentFolders.fEntryList.sort(larger_index); 1538 1539 printf("----------------------------------------------------------------------\n"); 1540 fRecentDocuments.Print(); 1541 printf("----------------------------------------------------------------------\n"); 1542 fRecentFolders.Print(); 1543 printf("----------------------------------------------------------------------\n"); 1544 fRecentApps.Print(); 1545 printf("----------------------------------------------------------------------\n"); 1546 } 1547 if (error) 1548 D(PRINT(("WARNING: TRoster::_LoadRosterSettings(): error loading roster settings " 1549 "from '%s', 0x%lx\n", settingsPath, error))); 1550 return error; 1551 } 1552 1553 status_t 1554 TRoster::_SaveRosterSettings(const char *path) 1555 { 1556 const char *settingsPath = path ? path : kDefaultRosterSettingsFile; 1557 1558 status_t error; 1559 FILE* file; 1560 1561 file = fopen(settingsPath, "w+"); 1562 error = file ? B_OK : errno; 1563 if (!error) { 1564 status_t saveError; 1565 saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc"); 1566 if (saveError) 1567 D(PRINT(("TRoster::_SaveRosterSettings(): recent documents save failed " 1568 "with error 0x%lx\n", saveError))); 1569 saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder"); 1570 if (saveError) 1571 D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed " 1572 "with error 0x%lx\n", saveError))); 1573 saveError = fRecentApps.Save(file); 1574 if (saveError) 1575 D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed " 1576 "with error 0x%lx\n", saveError))); 1577 fclose(file); 1578 } 1579 1580 return error; 1581 } 1582 1583 1584 //------------------------------------------------------------------------------ 1585 // Private local functions 1586 //------------------------------------------------------------------------------ 1587 1588 /*! \brief Returns true if entry1's index is larger than entry2's index. 1589 1590 Also returns true if either entry is \c NULL. 1591 1592 Used for sorting the recent entry lists loaded from disk into the 1593 proper order. 1594 */ 1595 bool 1596 larger_index(const recent_entry *entry1, const recent_entry *entry2) 1597 { 1598 if (entry1 && entry2) 1599 return entry1->index > entry2->index; 1600 else 1601 return true; 1602 } 1603 1604