2 * Copyright 2001-2008, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
7 /*! TRoster is the incarnation of The Roster. It manages the running
21 #include <Application.h>
22 #include <AutoDeleter.h>
24 #include <Directory.h>
26 #include <FindDirectory.h>
30 #include <MessagePrivate.h>
31 #include <MessengerPrivate.h>
32 #include <RosterPrivate.h>
33 #include <ServerProtocol.h>
34 #include <storage_support.h>
36 #include "AppInfoListMessagingTargetSet.h"
38 #include "EventMaskWatcher.h"
39 #include "MessageDeliverer.h"
40 #include "RegistrarDefs.h"
41 #include "RosterAppInfo.h"
42 #include "RosterSettingsCharStream.h"
45 using namespace BPrivate
;
49 \brief Implements the application roster.
51 This class handles the BRoster requests. For each kind a hook method is
52 implemented to which the registrar looper dispatches the request messages.
54 Registered and pre-registered are managed via AppInfoLists.
55 \a fEarlyPreRegisteredApps contains the infos for those application that
56 are pre-registered and currently have no team ID assigned to them yet,
57 whereas the infos of registered and pre-registered applications with a
58 team ID are to be found in \a fRegisteredApps.
60 When an application asks whether it is pre-registered or not and there
61 are one or more instances of the application that are pre-registered, but
62 have no team ID assigned yet, the reply to the request has to be
63 postponed until the status of the requesting team is clear. The request
64 message is dequeued from the registrar's message queue and added to
65 \a fIARRequestsByID for a later reply.
67 The field \a fActiveApp identifies the currently active application
68 and \a fLastToken is a counter used to generate unique tokens for
69 pre-registered applications.
72 //! The maximal period of time an app may be early pre-registered (60 s).
73 const bigtime_t kMaximalEarlyPreRegistrationPeriod
= 60000000LL;
76 // #pragma mark - Private local functions
79 /*! \brief Returns the path to the default roster settings.
81 \param path BPath to be set to the roster settings path.
82 \param createDirectory makes sure the target directory exists if \c true.
84 \return the settings path as C string (\code path.Path() \endcode).
87 get_default_roster_settings_path(BPath
& path
, bool createDirectory
)
89 // get the path of the settings dir and append the subpath of our file
90 status_t error
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
92 error
= path
.Append("system/registrar");
93 if (error
== B_OK
&& createDirectory
)
94 error
= create_directory(path
.Path(), 0777);
96 error
= path
.Append("RosterSettings");
102 /*! \brief Returns true if entry1's index is larger than entry2's index.
104 Also returns true if either entry is \c NULL.
106 Used for sorting the recent entry lists loaded from disk into the
110 larger_index(const recent_entry
* entry1
, const recent_entry
* entry2
)
112 if (entry1
&& entry2
)
113 return entry1
->index
> entry2
->index
;
122 /*! \brief Creates a new roster.
124 The object is completely initialized and ready to handle requests.
130 fEarlyPreRegisteredApps(),
132 fIARRequestsByToken(),
141 find_directory(B_SYSTEM_DIRECTORY
, &fSystemAppPath
);
142 find_directory(B_SYSTEM_SERVERS_DIRECTORY
, &fSystemServerPath
);
146 /*! \brief Frees all resources associated with this object.
153 /*! \brief Handles an AddApplication() request.
154 \param request The request message
157 TRoster::HandleAddApplication(BMessage
* request
)
163 status_t error
= B_OK
;
164 // get the parameters
165 const char* signature
;
172 if (request
->FindString("signature", &signature
) != B_OK
)
174 if (request
->FindRef("ref", &ref
) != B_OK
)
175 SET_ERROR(error
, B_BAD_VALUE
);
176 if (request
->FindInt32("flags", (int32
*)&flags
) != B_OK
)
177 flags
= B_REG_DEFAULT_APP_FLAGS
;
178 if (request
->FindInt32("team", &team
) != B_OK
)
180 if (request
->FindInt32("thread", &thread
) != B_OK
)
182 if (request
->FindInt32("port", &port
) != B_OK
)
184 if (request
->FindBool("full_registration", &fullReg
) != B_OK
)
187 PRINT("team: %" B_PRId32
", signature: %s\n", team
, signature
);
188 PRINT("full registration: %d\n", fullReg
);
191 error
= B_SHUTTING_DOWN
;
193 // check the parameters
194 team_id otherTeam
= -1;
197 uint32 launchFlags
= flags
& B_LAUNCH_MASK
;
200 SET_ERROR(error
, B_ENTRY_NOT_FOUND
);
203 _ValidateRunning(ref
, signature
);
207 PRINT("flags: %" B_PRIx32
"\n", flags
);
208 PRINT("ref: %" B_PRId32
", %" B_PRId64
", %s\n", ref
.device
,
209 ref
.directory
, ref
.name
);
210 // check single/exclusive launchers
211 RosterAppInfo
* info
= NULL
;
212 if ((launchFlags
== B_SINGLE_LAUNCH
213 || launchFlags
== B_EXCLUSIVE_LAUNCH
)
214 && ((info
= fRegisteredApps
.InfoFor(&ref
)) != NULL
215 || (info
= fEarlyPreRegisteredApps
.InfoFor(&ref
)) != NULL
)) {
216 SET_ERROR(error
, B_ALREADY_RUNNING
);
217 otherTeam
= info
->team
;
223 if (error
== B_OK
&& signature
) {
224 // check exclusive launchers
225 RosterAppInfo
* info
= NULL
;
226 if (launchFlags
== B_EXCLUSIVE_LAUNCH
227 && (((info
= fRegisteredApps
.InfoFor(signature
)))
228 || ((info
= fEarlyPreRegisteredApps
.InfoFor(signature
))))) {
229 SET_ERROR(error
, B_ALREADY_RUNNING
);
230 otherTeam
= info
->team
;
235 // If no team ID is given, full registration isn't possible.
239 SET_ERROR(error
, B_BAD_VALUE
);
240 } else if (fRegisteredApps
.InfoFor(team
))
241 SET_ERROR(error
, B_REG_ALREADY_REGISTERED
);
244 // Add the application info.
246 // alloc and init the info
247 RosterAppInfo
* info
= new(nothrow
) RosterAppInfo
;
249 info
->Init(thread
, team
, port
, flags
, &ref
, signature
);
251 info
->state
= APP_STATE_REGISTERED
;
253 info
->state
= APP_STATE_PRE_REGISTERED
;
254 info
->registration_time
= system_time();
255 // add it to the right list
256 bool addingSuccess
= false;
258 PRINT("added ref: %" B_PRId32
", %" B_PRId64
", %s\n",
259 info
->ref
.device
, info
->ref
.directory
, info
->ref
.name
);
260 addingSuccess
= (AddApp(info
) == B_OK
);
261 if (addingSuccess
&& fullReg
)
264 token
= info
->token
= _NextToken();
265 addingSuccess
= fEarlyPreRegisteredApps
.AddInfo(info
);
266 PRINT("added to early pre-regs, token: %" B_PRIu32
"\n", token
);
269 SET_ERROR(error
, B_NO_MEMORY
);
271 SET_ERROR(error
, B_NO_MEMORY
);
272 // delete the info on failure
273 if (error
!= B_OK
&& info
)
277 // reply to the request
279 // add to recent apps if successful
280 if (signature
&& signature
[0] != '\0')
281 fRecentApps
.Add(signature
, flags
);
283 fRecentApps
.Add(&ref
, flags
);
285 BMessage
reply(B_REG_SUCCESS
);
286 // The token is valid only when no team ID has been supplied.
288 reply
.AddInt32("token", (int32
)token
);
289 request
->SendReply(&reply
);
291 BMessage
reply(B_REG_ERROR
);
292 reply
.AddInt32("error", error
);
294 reply
.AddInt32("other_team", otherTeam
);
296 reply
.AddInt32("token", (int32
)token
);
297 request
->SendReply(&reply
);
304 /*! \brief Handles a CompleteRegistration() request.
305 \param request The request message
308 TRoster::HandleCompleteRegistration(BMessage
* request
)
314 status_t error
= B_OK
;
315 // get the parameters
319 if (request
->FindInt32("team", &team
) != B_OK
)
321 if (request
->FindInt32("thread", &thread
) != B_OK
)
323 if (request
->FindInt32("port", &port
) != B_OK
)
327 error
= B_SHUTTING_DOWN
;
329 // check the parameters
331 if (error
== B_OK
&& port
< 0)
332 SET_ERROR(error
, B_BAD_VALUE
);
335 if (error
== B_OK
&& thread
< 0)
336 SET_ERROR(error
, B_BAD_VALUE
);
341 // everything is fine -- set the values
342 RosterAppInfo
* info
= fRegisteredApps
.InfoFor(team
);
343 if (info
&& info
->state
== APP_STATE_PRE_REGISTERED
) {
344 info
->thread
= thread
;
346 info
->state
= APP_STATE_REGISTERED
;
349 SET_ERROR(error
, B_REG_APP_NOT_PRE_REGISTERED
);
351 SET_ERROR(error
, B_BAD_VALUE
);
354 // reply to the request
356 BMessage
reply(B_REG_SUCCESS
);
357 request
->SendReply(&reply
);
359 BMessage
reply(B_REG_ERROR
);
360 reply
.AddInt32("error", error
);
361 request
->SendReply(&reply
);
368 /*! \brief Handles an IsAppRegistered() request.
369 \param request The request message
372 TRoster::HandleIsAppRegistered(BMessage
* request
)
378 status_t error
= B_OK
;
379 // get the parameters
383 if (request
->FindRef("ref", &ref
) != B_OK
)
384 SET_ERROR(error
, B_BAD_VALUE
);
385 if (request
->FindInt32("team", &team
) != B_OK
)
387 if (request
->FindInt32("token", (int32
*)&token
) != B_OK
)
390 PRINT("team: %" B_PRId32
", token: %" B_PRIu32
"\n", team
, token
);
391 PRINT("ref: %" B_PRId32
", %" B_PRId64
", %s\n", ref
.device
, ref
.directory
,
394 // check the parameters
396 if (error
== B_OK
&& !BEntry(&ref
).Exists())
397 SET_ERROR(error
, B_ENTRY_NOT_FOUND
);
399 if (error
== B_OK
&& team
< 0 && token
== 0)
400 SET_ERROR(error
, B_BAD_VALUE
);
402 // look up the information
403 RosterAppInfo
* info
= NULL
;
405 if ((info
= fRegisteredApps
.InfoFor(team
)) != NULL
) {
406 PRINT("found team in fRegisteredApps\n");
407 _ReplyToIARRequest(request
, info
);
409 && (info
= fEarlyPreRegisteredApps
.InfoForToken(token
)) != NULL
) {
410 PRINT("found ref in fEarlyRegisteredApps (by token)\n");
411 // pre-registered and has no team ID assigned yet -- queue the
413 be_app
->DetachCurrentMessage();
414 _AddIARRequest(fIARRequestsByToken
, token
, request
);
416 && (info
= fEarlyPreRegisteredApps
.InfoFor(&ref
)) != NULL
) {
417 PRINT("found ref in fEarlyRegisteredApps (by ref)\n");
418 // pre-registered and has no team ID assigned yet -- queue the
420 be_app
->DetachCurrentMessage();
421 _AddIARRequest(fIARRequestsByID
, team
, request
);
423 PRINT("didn't find team or ref\n");
424 // team not registered, ref/token not early pre-registered
425 _ReplyToIARRequest(request
, NULL
);
428 // reply to the request on error
429 BMessage
reply(B_REG_ERROR
);
430 reply
.AddInt32("error", error
);
431 request
->SendReply(&reply
);
438 /*! \brief Handles a RemovePreRegApp() request.
439 \param request The request message
442 TRoster::HandleRemovePreRegApp(BMessage
* request
)
448 status_t error
= B_OK
;
449 // get the parameters
451 if (request
->FindInt32("token", (int32
*)&token
) != B_OK
)
452 SET_ERROR(error
, B_BAD_VALUE
);
455 RosterAppInfo
* info
= fEarlyPreRegisteredApps
.InfoForToken(token
);
457 fEarlyPreRegisteredApps
.RemoveInfo(info
);
460 SET_ERROR(error
, B_REG_APP_NOT_PRE_REGISTERED
);
462 // reply to the request
464 BMessage
reply(B_REG_SUCCESS
);
465 request
->SendReply(&reply
);
467 BMessage
reply(B_REG_ERROR
);
468 reply
.AddInt32("error", error
);
469 request
->SendReply(&reply
);
476 /*! \brief Handles a RemoveApp() request.
477 \param request The request message
480 TRoster::HandleRemoveApp(BMessage
* request
)
486 status_t error
= B_OK
;
487 // get the parameters
489 if (request
->FindInt32("team", &team
) != B_OK
)
492 PRINT("team: %" B_PRId32
"\n", team
);
496 if (RosterAppInfo
* info
= fRegisteredApps
.InfoFor(team
)) {
500 SET_ERROR(error
, B_REG_APP_NOT_REGISTERED
);
502 // reply to the request
504 BMessage
reply(B_REG_SUCCESS
);
505 request
->SendReply(&reply
);
507 BMessage
reply(B_REG_ERROR
);
508 reply
.AddInt32("error", error
);
509 request
->SendReply(&reply
);
516 /*! \brief Handles a SetThreadAndTeam() request.
517 \param request The request message
520 TRoster::HandleSetThreadAndTeam(BMessage
* request
)
526 status_t error
= B_OK
;
528 // get the parameters
532 if (request
->FindInt32("team", &team
) != B_OK
)
534 if (request
->FindInt32("thread", &thread
) != B_OK
)
536 if (request
->FindInt32("token", (int32
*)&token
) != B_OK
)
537 SET_ERROR(error
, B_BAD_VALUE
);
539 // check the parameters
541 if (error
== B_OK
&& team
< 0)
542 SET_ERROR(error
, B_BAD_VALUE
);
544 PRINT("team: %" B_PRId32
", thread: %" B_PRId32
", token: %" B_PRIu32
"\n",
545 team
, thread
, token
);
549 // update the app_info
551 RosterAppInfo
* info
= fEarlyPreRegisteredApps
.InfoForToken(token
);
553 // Set thread and team, create a port for the application and
554 // move the app_info from the list of the early pre-registered
555 // apps to the list of the (pre-)registered apps.
556 fEarlyPreRegisteredApps
.RemoveInfo(info
);
558 info
->thread
= thread
;
559 // create and transfer the port
560 info
->port
= port
= create_port(B_REG_APP_LOOPER_PORT_CAPACITY
,
561 kRAppLooperPortName
);
563 SET_ERROR(error
, info
->port
);
565 SET_ERROR(error
, set_port_owner(info
->port
, team
));
566 // add the info to the registered apps list
568 SET_ERROR(error
, AddApp(info
));
569 // cleanup on failure
572 delete_port(info
->port
);
576 // handle pending IsAppRegistered() requests
577 IARRequestMap::iterator it
= fIARRequestsByID
.find(team
);
578 if (it
!= fIARRequestsByID
.end()) {
579 BMessageQueue
* requests
= it
->second
;
581 _ReplyToIARRequests(requests
, info
);
583 fIARRequestsByID
.erase(it
);
586 it
= fIARRequestsByToken
.find((int32
)token
);
587 if (it
!= fIARRequestsByToken
.end()) {
588 BMessageQueue
* requests
= it
->second
;
590 _ReplyToIARRequests(requests
, info
);
592 fIARRequestsByToken
.erase(it
);
595 SET_ERROR(error
, B_REG_APP_NOT_PRE_REGISTERED
);
597 // reply to the request
599 BMessage
reply(B_REG_SUCCESS
);
600 reply
.AddInt32("port", port
);
601 request
->SendReply(&reply
);
603 BMessage
reply(B_REG_ERROR
);
604 reply
.AddInt32("error", error
);
605 request
->SendReply(&reply
);
612 /*! \brief Handles a SetSignature() request.
613 \param request The request message
616 TRoster::HandleSetSignature(BMessage
* request
)
622 status_t error
= B_OK
;
623 // get the parameters
625 const char* signature
;
626 if (request
->FindInt32("team", &team
) != B_OK
)
628 if (request
->FindString("signature", &signature
) != B_OK
)
630 // find the app and set the signature
632 if (RosterAppInfo
* info
= fRegisteredApps
.InfoFor(team
))
633 strcpy(info
->signature
, signature
);
635 SET_ERROR(error
, B_REG_APP_NOT_REGISTERED
);
637 // reply to the request
639 BMessage
reply(B_REG_SUCCESS
);
640 request
->SendReply(&reply
);
642 BMessage
reply(B_REG_ERROR
);
643 reply
.AddInt32("error", error
);
644 request
->SendReply(&reply
);
651 /*! \brief Handles a Get{Running,Active,}AppInfo() request.
652 \param request The request message
655 TRoster::HandleGetAppInfo(BMessage
* request
)
661 status_t error
= B_OK
;
662 // get the parameters
665 const char* signature
;
668 bool hasSignature
= true;
669 if (request
->FindInt32("team", &team
) != B_OK
)
671 if (request
->FindRef("ref", &ref
) != B_OK
)
673 if (request
->FindString("signature", &signature
) != B_OK
)
674 hasSignature
= false;
677 PRINT("team: %" B_PRId32
"\n", team
);
679 PRINT("ref: %" B_PRId32
", %" B_PRId64
", %s\n", ref
.device
, ref
.directory
,
682 PRINT("signature: %s\n", signature
);
685 RosterAppInfo
* info
= NULL
;
688 info
= fRegisteredApps
.InfoFor(team
);
690 SET_ERROR(error
, B_BAD_TEAM_ID
);
692 info
= fRegisteredApps
.InfoFor(&ref
);
694 SET_ERROR(error
, B_ERROR
);
695 } else if (hasSignature
) {
696 info
= fRegisteredApps
.InfoFor(signature
);
698 SET_ERROR(error
, B_ERROR
);
700 // If neither of those has been supplied, the active application
701 // info is requested.
705 SET_ERROR(error
, B_ERROR
);
708 // reply to the request
710 BMessage
reply(B_REG_SUCCESS
);
711 _AddMessageAppInfo(&reply
, info
);
712 request
->SendReply(&reply
);
714 BMessage
reply(B_REG_ERROR
);
715 reply
.AddInt32("error", error
);
716 request
->SendReply(&reply
);
723 /*! \brief Handles a GetAppList() request.
724 \param request The request message
727 TRoster::HandleGetAppList(BMessage
* request
)
733 status_t error
= B_OK
;
734 // get the parameters
735 const char* signature
;
736 if (request
->FindString("signature", &signature
) != B_OK
)
738 // reply to the request
740 BMessage
reply(B_REG_SUCCESS
);
742 for (AppInfoList::Iterator
it(fRegisteredApps
.It());
743 RosterAppInfo
* info
= *it
;
745 if (info
->state
!= APP_STATE_REGISTERED
)
747 if (!signature
|| !strcasecmp(signature
, info
->signature
))
748 reply
.AddInt32("teams", info
->team
);
750 request
->SendReply(&reply
);
752 BMessage
reply(B_REG_ERROR
);
753 reply
.AddInt32("error", error
);
754 request
->SendReply(&reply
);
761 /*! \brief Handles a _UpdateActiveApp() request.
763 This is sent from the app_server when the current active application
766 \param request The request message
769 TRoster::HandleUpdateActiveApp(BMessage
* request
)
775 // get the parameters
776 status_t error
= B_OK
;
778 if (request
->FindInt32("team", &team
) != B_OK
)
783 if (RosterAppInfo
* info
= fRegisteredApps
.InfoFor(team
))
784 UpdateActiveApp(info
);
786 error
= B_BAD_TEAM_ID
;
789 // reply to the request
790 if (request
->IsSourceWaiting()) {
792 BMessage
reply(B_REG_SUCCESS
);
793 request
->SendReply(&reply
);
795 BMessage
reply(B_REG_ERROR
);
796 reply
.AddInt32("error", error
);
797 request
->SendReply(&reply
);
805 /*! \brief Handles a Broadcast() request.
806 \param request The request message
809 TRoster::HandleBroadcast(BMessage
* request
)
815 status_t error
= B_OK
;
816 // get the parameters
819 BMessenger replyTarget
;
820 if (request
->FindInt32("team", &team
) != B_OK
)
822 if (error
== B_OK
&& request
->FindMessage("message", &message
) != B_OK
)
825 && request
->FindMessenger("reply_target", &replyTarget
) != B_OK
) {
829 // reply to the request -- do this first, don't let the inquirer wait
831 BMessage
reply(B_REG_SUCCESS
);
832 request
->SendReply(&reply
);
834 BMessage
reply(B_REG_ERROR
);
835 reply
.AddInt32("error", error
);
836 request
->SendReply(&reply
);
839 // broadcast the message
841 // the target set (excludes the registrar and the requesting team)
842 class BroadcastMessagingTargetSet
843 : public AppInfoListMessagingTargetSet
{
845 BroadcastMessagingTargetSet(AppInfoList
& list
, team_id team
)
846 : AppInfoListMessagingTargetSet(list
, true),
851 virtual bool Filter(const RosterAppInfo
* info
)
853 return AppInfoListMessagingTargetSet::Filter(info
)
854 && (info
->team
!= fTeam
);
859 } targetSet(fRegisteredApps
, team
);
861 if (targetSet
.HasNext()) {
862 // set the reply target
863 BMessage::Private(message
).SetReply(replyTarget
);
866 MessageDeliverer::Default()->DeliverMessage(&message
, targetSet
);
874 /*! \brief Handles a StartWatching() request.
875 \param request The request message
878 TRoster::HandleStartWatching(BMessage
* request
)
884 status_t error
= B_OK
;
885 // get the parameters
888 if (error
== B_OK
&& request
->FindMessenger("target", &target
) != B_OK
)
890 if (request
->FindInt32("events", (int32
*)&events
) != B_OK
)
892 // add the new watcher
894 Watcher
* watcher
= new(nothrow
) EventMaskWatcher(target
, events
);
896 if (!fWatchingService
.AddWatcher(watcher
)) {
903 // reply to the request
905 BMessage
reply(B_REG_SUCCESS
);
906 request
->SendReply(&reply
);
908 BMessage
reply(B_REG_ERROR
);
909 reply
.AddInt32("error", error
);
910 request
->SendReply(&reply
);
917 /*! \brief Handles a StopWatching() request.
918 \param request The request message
921 TRoster::HandleStopWatching(BMessage
* request
)
927 status_t error
= B_OK
;
928 // get the parameters
930 if (error
== B_OK
&& request
->FindMessenger("target", &target
) != B_OK
)
932 // remove the watcher
934 if (!fWatchingService
.RemoveWatcher(target
))
937 // reply to the request
939 BMessage
reply(B_REG_SUCCESS
);
940 request
->SendReply(&reply
);
942 BMessage
reply(B_REG_ERROR
);
943 reply
.AddInt32("error", error
);
944 request
->SendReply(&reply
);
951 /*! \brief Handles a GetRecentDocuments() request.
952 \param request The request message
955 TRoster::HandleGetRecentDocuments(BMessage
* request
)
961 _HandleGetRecentEntries(request
);
967 /*! \brief Handles a GetRecentFolders() request.
968 \param request The request message
971 TRoster::HandleGetRecentFolders(BMessage
* request
)
977 _HandleGetRecentEntries(request
);
983 /*! \brief Handles a GetRecentApps() request.
984 \param request The request message
987 TRoster::HandleGetRecentApps(BMessage
* request
)
994 D(PRINT("WARNING: TRoster::HandleGetRecentApps(NULL) called\n"));
999 BMessage
reply(B_REG_RESULT
);
1001 status_t error
= request
->FindInt32("max count", &maxCount
);
1003 error
= fRecentApps
.Get(maxCount
, &reply
);
1004 reply
.AddInt32("result", error
);
1005 request
->SendReply(&reply
);
1011 /*! \brief Handles an AddToRecentDocuments() request.
1012 \param request The request message
1015 TRoster::HandleAddToRecentDocuments(BMessage
* request
)
1022 D(PRINT("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n"));
1028 BMessage
reply(B_REG_RESULT
);
1030 status_t error
= request
->FindRef("ref", &ref
);
1032 error
= request
->FindString("app sig", &appSig
);
1034 error
= fRecentDocuments
.Add(&ref
, appSig
);
1035 reply
.AddInt32("result", error
);
1036 request
->SendReply(&reply
);
1042 /*! \brief Handles an AddToRecentFolders() request.
1043 \param request The request message
1046 TRoster::HandleAddToRecentFolders(BMessage
* request
)
1053 D(PRINT("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n"));
1059 BMessage
reply(B_REG_RESULT
);
1061 status_t error
= request
->FindRef("ref", &ref
);
1063 error
= request
->FindString("app sig", &appSig
);
1065 error
= fRecentFolders
.Add(&ref
, appSig
);
1066 reply
.AddInt32("result", error
);
1067 request
->SendReply(&reply
);
1073 /*! \brief Handles an AddToRecentApps() request.
1074 \param request The request message
1077 TRoster::HandleAddToRecentApps(BMessage
* request
)
1084 D(PRINT("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n"));
1089 BMessage
reply(B_REG_RESULT
);
1091 status_t error
= request
->FindString("app sig", &appSig
);
1093 error
= fRecentApps
.Add(appSig
);
1094 reply
.AddInt32("result", error
);
1095 request
->SendReply(&reply
);
1102 TRoster::HandleLoadRecentLists(BMessage
* request
)
1109 D(PRINT("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n"));
1113 const char* filename
;
1114 BMessage
reply(B_REG_RESULT
);
1116 status_t error
= request
->FindString("filename", &filename
);
1118 error
= _LoadRosterSettings(filename
);
1119 reply
.AddInt32("result", error
);
1120 request
->SendReply(&reply
);
1127 TRoster::HandleSaveRecentLists(BMessage
* request
)
1134 D(PRINT("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n"));
1138 const char* filename
;
1139 BMessage
reply(B_REG_RESULT
);
1141 status_t error
= request
->FindString("filename", &filename
);
1143 error
= _SaveRosterSettings(filename
);
1144 reply
.AddInt32("result", error
);
1145 request
->SendReply(&reply
);
1152 TRoster::HandleRestartAppServer(BMessage
* request
)
1156 // TODO: if an app_server is still running, stop it first
1158 const char* pathString
;
1159 if (request
->FindString("path", &pathString
) != B_OK
)
1160 pathString
= "/boot/system/servers";
1161 BPath
path(pathString
);
1162 path
.Append("app_server");
1163 // NOTE: its required at some point that the binary name is "app_server"
1165 const char **argv
= new const char * [2];
1166 argv
[0] = strdup(path
.Path());
1169 thread_id threadId
= load_image(1, argv
, (const char**)environ
);
1171 for (i
= 0; i
< 1; i
++)
1175 resume_thread(threadId
);
1176 // give the server some time to create the server port
1180 // TODO: whats about ourself?
1181 AppInfoListMessagingTargetSet
targetSet(fRegisteredApps
);
1182 if (targetSet
.HasNext()) {
1183 // send the messages
1184 BMessage
message(kMsgAppServerRestarted
);
1185 MessageDeliverer::Default()->DeliverMessage(&message
, targetSet
);
1190 /*! \brief Clears the current list of recent documents
1193 TRoster::ClearRecentDocuments()
1197 fRecentDocuments
.Clear();
1201 /*! \brief Clears the current list of recent folders
1204 TRoster::ClearRecentFolders()
1208 fRecentFolders
.Clear();
1212 /*! \brief Clears the current list of recent apps
1215 TRoster::ClearRecentApps()
1219 fRecentApps
.Clear();
1223 /*! \brief Initializes the roster.
1225 Currently only adds the registrar to the roster.
1226 The application must already be running, more precisly Run() must have
1230 - \c B_OK: Everything went fine.
1236 // check lock initialization
1237 if (fLock
.Sem() < 0)
1241 RosterAppInfo
* info
= new(nothrow
) RosterAppInfo
;
1245 // get the app's ref
1247 status_t error
= get_app_ref(&ref
);
1249 // init and add the info
1250 if (error
== B_OK
) {
1251 info
->Init(be_app
->Thread(), be_app
->Team(),
1252 BMessenger::Private(be_app_messenger
).Port(),
1253 B_EXCLUSIVE_LAUNCH
| B_BACKGROUND_APP
, &ref
, B_REGISTRAR_SIGNATURE
);
1254 info
->state
= APP_STATE_REGISTERED
;
1255 info
->registration_time
= system_time();
1256 error
= AddApp(info
);
1260 _LoadRosterSettings();
1270 /*! \brief Add the supplied app info to the list of (pre-)registered apps.
1272 \param info The app info to be added
1275 TRoster::AddApp(RosterAppInfo
* info
)
1279 status_t error
= (info
? B_OK
: B_BAD_VALUE
);
1281 if (!fRegisteredApps
.AddInfo(info
))
1282 error
= B_NO_MEMORY
;
1288 /*! \brief Removes the supplied app info from the list of (pre-)registered
1291 \param info The app info to be removed
1294 TRoster::RemoveApp(RosterAppInfo
* info
)
1299 if (fRegisteredApps
.RemoveInfo(info
)) {
1300 if (info
->state
== APP_STATE_REGISTERED
) {
1301 info
->state
= APP_STATE_UNREGISTERED
;
1309 /*! \brief Activates the application identified by \a info.
1311 The currently active application is deactivated and the one whose
1312 info is supplied is activated. \a info may be \c NULL, which only
1313 deactivates the currently active application.
1315 \param info The info of the app to be activated
1318 TRoster::UpdateActiveApp(RosterAppInfo
* info
)
1322 if (info
!= fActiveApp
) {
1323 // deactivate the currently active app
1324 RosterAppInfo
* oldActiveApp
= fActiveApp
;
1327 _AppDeactivated(oldActiveApp
);
1329 // activate the new app
1332 _AppActivated(info
);
1338 /*! \brief Checks whether the (pre-)registered applications are still running.
1340 This is necessary, since killed applications don't unregister properly.
1343 TRoster::CheckSanity()
1347 // not early (pre-)registered applications
1348 AppInfoList obsoleteApps
;
1349 for (AppInfoList::Iterator it
= fRegisteredApps
.It(); it
.IsValid(); ++it
) {
1350 if (!(*it
)->IsRunning())
1351 obsoleteApps
.AddInfo(*it
);
1355 for (AppInfoList::Iterator it
= obsoleteApps
.It(); it
.IsValid(); ++it
) {
1359 obsoleteApps
.MakeEmpty(false);
1360 // don't delete infos a second time
1362 // early pre-registered applications
1363 bigtime_t timeLimit
= system_time() - kMaximalEarlyPreRegistrationPeriod
;
1364 for (AppInfoList::Iterator it
= fEarlyPreRegisteredApps
.It();
1367 if ((*it
)->registration_time
< timeLimit
)
1368 obsoleteApps
.AddInfo(*it
);
1372 for (AppInfoList::Iterator it
= obsoleteApps
.It(); it
.IsValid(); ++it
) {
1373 fEarlyPreRegisteredApps
.RemoveInfo(*it
);
1376 obsoleteApps
.MakeEmpty(false);
1377 // don't delete infos a second time
1381 /*! \brief Tells the roster whether a shutdown process is in progess at the
1384 After this method is called with \a shuttingDown == \c true, no more
1385 applications can be created.
1387 \param shuttingDown \c true, to indicate the start of the shutdown process,
1388 \c false to signalling its end.
1391 TRoster::SetShuttingDown(bool shuttingDown
)
1395 fShuttingDown
= shuttingDown
;
1398 _SaveRosterSettings();
1402 /*! \brief Returns lists of applications to be asked to quit on shutdown.
1404 \param userApps List of RosterAppInfos identifying the user applications.
1405 Those will be ask to quit first.
1406 \param systemApps List of RosterAppInfos identifying the system applications
1407 (like Tracker and Deskbar), which will be asked to quit after the
1408 user applications are gone.
1409 \param vitalSystemApps A set of team_ids identifying teams that must not
1410 be terminated (app server and registrar).
1411 \return \c B_OK, if everything went fine, another error code otherwise.
1414 TRoster::GetShutdownApps(AppInfoList
& userApps
, AppInfoList
& systemApps
,
1415 AppInfoList
& backgroundApps
, hash_set
<team_id
>& vitalSystemApps
)
1419 status_t error
= B_OK
;
1421 // get the vital system apps:
1428 vitalSystemApps
.insert(be_app
->Team());
1432 if (get_team_info(B_SYSTEM_TEAM
, &teamInfo
) == B_OK
)
1433 vitalSystemApps
.insert(teamInfo
.team
);
1437 = fRegisteredApps
.InfoFor("application/x-vnd.haiku-app_server");
1439 vitalSystemApps
.insert(info
->team
);
1442 info
= fRegisteredApps
.InfoFor("application/x-vnd.haiku-debug_server");
1444 vitalSystemApps
.insert(info
->team
);
1446 // populate the other groups
1447 for (AppInfoList::Iterator
it(fRegisteredApps
.It());
1448 RosterAppInfo
* info
= *it
; ++it
) {
1449 if (vitalSystemApps
.find(info
->team
) == vitalSystemApps
.end()) {
1450 RosterAppInfo
* clonedInfo
= info
->Clone();
1452 if (_IsSystemApp(info
)) {
1453 if (!systemApps
.AddInfo(clonedInfo
))
1454 error
= B_NO_MEMORY
;
1455 } else if (info
->flags
& B_BACKGROUND_APP
) {
1456 if (!backgroundApps
.AddInfo(clonedInfo
))
1457 error
= B_NO_MEMORY
;
1459 if (!userApps
.AddInfo(clonedInfo
))
1460 error
= B_NO_MEMORY
;
1466 error
= B_NO_MEMORY
;
1473 // Special case, we add the input server to vital apps here so it is
1474 // not excluded in the lists above
1475 info
= fRegisteredApps
.InfoFor("application/x-vnd.Be-input_server");
1477 vitalSystemApps
.insert(info
->team
);
1479 // clean up on error
1480 if (error
!= B_OK
) {
1481 userApps
.MakeEmpty(true);
1482 systemApps
.MakeEmpty(true);
1490 TRoster::AddWatcher(Watcher
* watcher
)
1497 if (!fWatchingService
.AddWatcher(watcher
))
1505 TRoster::RemoveWatcher(Watcher
* watcher
)
1510 fWatchingService
.RemoveWatcher(watcher
, false);
1514 /*! \brief Hook method invoked, when an application has been fully registered.
1515 \param info The RosterAppInfo of the added application.
1518 TRoster::_AppAdded(RosterAppInfo
* info
)
1520 // notify the watchers
1521 BMessage
message(B_SOME_APP_LAUNCHED
);
1522 _AddMessageWatchingInfo(&message
, info
);
1523 EventMaskWatcherFilter
filter(B_REQUEST_LAUNCHED
);
1524 fWatchingService
.NotifyWatchers(&message
, &filter
);
1528 /*! \brief Hook method invoked, when a fully registered application has been
1530 \param info The RosterAppInfo of the removed application.
1533 TRoster::_AppRemoved(RosterAppInfo
* info
)
1536 // deactivate the app, if it was the active one
1537 if (info
== fActiveApp
)
1538 UpdateActiveApp(NULL
);
1540 // notify the watchers
1541 BMessage
message(B_SOME_APP_QUIT
);
1542 _AddMessageWatchingInfo(&message
, info
);
1543 EventMaskWatcherFilter
filter(B_REQUEST_QUIT
);
1544 fWatchingService
.NotifyWatchers(&message
, &filter
);
1549 /*! \brief Hook method invoked, when an application has been activated.
1550 \param info The RosterAppInfo of the activated application.
1553 TRoster::_AppActivated(RosterAppInfo
* info
)
1555 if (info
!= NULL
&& info
->state
== APP_STATE_REGISTERED
) {
1556 // send B_APP_ACTIVATED to the app
1557 BMessenger messenger
;
1558 BMessenger::Private
messengerPrivate(messenger
);
1559 messengerPrivate
.SetTo(info
->team
, info
->port
, B_NULL_TOKEN
);
1560 BMessage
message(B_APP_ACTIVATED
);
1561 message
.AddBool("active", true);
1562 // not sure, if it makes sense to use the MessageDeliverer here
1563 MessageDeliverer::Default()->DeliverMessage(&message
, messenger
);
1565 // notify the watchers
1566 BMessage
watcherMessage(B_SOME_APP_ACTIVATED
);
1567 _AddMessageWatchingInfo(&watcherMessage
, info
);
1568 EventMaskWatcherFilter
filter(B_REQUEST_ACTIVATED
);
1569 fWatchingService
.NotifyWatchers(&watcherMessage
, &filter
);
1574 /*! \brief Hook method invoked, when an application has been deactivated.
1575 \param info The RosterAppInfo of the deactivated application.
1578 TRoster::_AppDeactivated(RosterAppInfo
* info
)
1580 if (info
!= NULL
&& info
->state
== APP_STATE_REGISTERED
) {
1581 // send B_APP_ACTIVATED to the app
1582 BMessenger messenger
;
1583 BMessenger::Private
messengerPrivate(messenger
);
1584 messengerPrivate
.SetTo(info
->team
, info
->port
, B_NULL_TOKEN
);
1585 BMessage
message(B_APP_ACTIVATED
);
1586 message
.AddBool("active", false);
1587 // not sure, if it makes sense to use the MessageDeliverer here
1588 MessageDeliverer::Default()->DeliverMessage(&message
, messenger
);
1593 /*! \brief Adds an app_info to a message.
1595 The info is added as a flat_app_info to a field "app_info" with the type
1596 \c B_REG_APP_INFO_TYPE.
1598 \param message The message
1599 \param info The app_info.
1600 \return \c B_OK if everything went fine, an error code otherwise.
1603 TRoster::_AddMessageAppInfo(BMessage
* message
, const app_info
* info
)
1605 // An app_info is not completely flat. The entry_ref contains a string
1606 // pointer. Therefore we flatten the info.
1607 flat_app_info flatInfo
;
1608 flatInfo
.info
= *info
;
1610 // set the ref name to NULL and copy it into the flat structure
1611 flatInfo
.info
.ref
.name
= NULL
;
1612 flatInfo
.ref_name
[0] = '\0';
1614 strcpy(flatInfo
.ref_name
, info
->ref
.name
);
1616 // add the flat info
1617 return message
->AddData("app_info", B_REG_APP_INFO_TYPE
, &flatInfo
,
1618 sizeof(flat_app_info
));
1622 /*! \brief Adds application monitoring related fields to a message.
1623 \param message The message.
1624 \param info The app_info of the concerned application.
1625 \return \c B_OK if everything went fine, an error code otherwise.
1628 TRoster::_AddMessageWatchingInfo(BMessage
* message
, const app_info
* info
)
1630 status_t error
= B_OK
;
1632 error
= message
->AddString("be:signature", info
->signature
);
1634 error
= message
->AddInt32("be:team", info
->team
);
1636 error
= message
->AddInt32("be:thread", info
->thread
);
1638 error
= message
->AddInt32("be:flags", (int32
)info
->flags
);
1640 error
= message
->AddRef("be:ref", &info
->ref
);
1645 /*! \brief Returns the next available token.
1649 TRoster::_NextToken()
1651 return ++fLastToken
;
1655 /*! \brief Adds an IsAppRegistered() request to the given map.
1657 If something goes wrong, the method deletes the request.
1659 \param map The map the request shall be added to.
1660 \param key The key under which to add the request.
1661 \param request The request message to be added.
1664 TRoster::_AddIARRequest(IARRequestMap
& map
, int32 key
, BMessage
* request
)
1666 IARRequestMap::iterator it
= map
.find(key
);
1667 BMessageQueue
* requests
= NULL
;
1668 if (it
== map
.end()) {
1669 requests
= new(nothrow
) BMessageQueue();
1675 map
[key
] = requests
;
1677 requests
= it
->second
;
1679 requests
->AddMessage(request
);
1683 /*! \brief Invokes _ReplyToIARRequest() for all messages in the given
1686 \param requests The request messages to be replied to
1687 \param info The RosterAppInfo of the application in question
1691 TRoster::_ReplyToIARRequests(BMessageQueue
* requests
, const RosterAppInfo
* info
)
1693 while (BMessage
* request
= requests
->NextMessage()) {
1694 _ReplyToIARRequest(request
, info
);
1700 /*! \brief Sends a reply message to an IsAppRegistered() request.
1702 The message to be sent is a simple \c B_REG_SUCCESS message containing
1703 a "pre-registered" field, that says whether or not the application is
1704 pre-registered. It will be set to \c false, unless an \a info is supplied
1705 and the application this info refers to is pre-registered.
1707 \param request The request message to be replied to
1708 \param info The RosterAppInfo of the application in question
1712 TRoster::_ReplyToIARRequest(BMessage
* request
, const RosterAppInfo
* info
)
1714 // pre-registered or registered?
1715 bool preRegistered
= false;
1717 switch (info
->state
) {
1718 case APP_STATE_PRE_REGISTERED
:
1719 preRegistered
= true;
1721 case APP_STATE_UNREGISTERED
:
1722 case APP_STATE_REGISTERED
:
1723 preRegistered
= false;
1728 BMessage
reply(B_REG_SUCCESS
);
1729 reply
.AddBool("registered", (bool)info
);
1730 reply
.AddBool("pre-registered", preRegistered
);
1731 PRINT("_ReplyToIARRequest(): pre-registered: %d\n", preRegistered
);
1733 _AddMessageAppInfo(&reply
, info
);
1734 request
->SendReply(&reply
);
1738 /*! \brief Handles requests for both GetRecentDocuments() and
1742 TRoster::_HandleGetRecentEntries(BMessage
* request
)
1746 D(PRINT("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n"));
1751 BMessage
reply(B_REG_RESULT
);
1752 char** fileTypes
= NULL
;
1753 int32 fileTypesCount
= 0;
1754 char* appSig
= NULL
;
1756 status_t error
= request
->FindInt32("max count", &maxCount
);
1757 // Look for optional file type(s)
1759 type_code typeFound
;
1760 status_t typeError
= request
->GetInfo("file type", &typeFound
,
1763 typeError
= typeFound
== B_STRING_TYPE
? B_OK
: B_BAD_TYPE
;
1765 fileTypes
= new(nothrow
) char*[fileTypesCount
];
1766 typeError
= fileTypes
? B_OK
: B_NO_MEMORY
;
1769 for (int i
= 0; !error
&& i
< fileTypesCount
; i
++) {
1771 if (request
->FindString("file type", i
, &type
) == B_OK
) {
1772 fileTypes
[i
] = new(nothrow
) char[B_MIME_TYPE_LENGTH
];
1773 error
= fileTypes
[i
] ? B_OK
: B_NO_MEMORY
;
1774 // Yes, I do mean to use "error" here, not "typeError"
1775 BPrivate::Storage::to_lower(type
, fileTypes
[i
]);
1776 // Types are expected to be lowercase
1781 // Look for optional app sig
1784 error
= request
->FindString("app sig", &sig
);
1786 appSig
= new(nothrow
) char[B_MIME_TYPE_LENGTH
];
1787 error
= appSig
? B_OK
: B_NO_MEMORY
;
1788 BPrivate::Storage::to_lower(sig
, appSig
);
1789 } else if (error
== B_NAME_NOT_FOUND
)
1793 switch (request
->what
) {
1794 case B_REG_GET_RECENT_DOCUMENTS
:
1795 error
= fRecentDocuments
.Get(maxCount
, (const char**)fileTypes
,
1796 fileTypesCount
, appSig
, &reply
);
1797 D(fRecentDocuments
.Print());
1800 case B_REG_GET_RECENT_FOLDERS
:
1801 error
= fRecentFolders
.Get(maxCount
, (const char**)fileTypes
,
1802 fileTypesCount
, appSig
, &reply
);
1803 D(fRecentFolders
.Print());
1807 D(PRINT("WARNING: TRoster::_HandleGetRecentEntries(): "
1808 "unexpected request->what value of 0x%" B_PRIx32
"\n",
1810 error
= B_BAD_VALUE
;
1814 reply
.AddInt32("result", error
);
1815 // Clean up before sending a reply
1818 for (int i
= 0; i
< fileTypesCount
; i
++)
1819 delete [] fileTypes
[i
];
1823 request
->SendReply(&reply
);
1830 \brief Checks all registered apps for \a ref and \a signature if
1831 they are still alive, and removes those that aren't.
1834 TRoster::_ValidateRunning(const entry_ref
& ref
, const char* signature
)
1837 // get info via ref or signature
1838 RosterAppInfo
* info
= fRegisteredApps
.InfoFor(&ref
);
1839 if (info
== NULL
&& signature
!= NULL
)
1840 info
= fRegisteredApps
.InfoFor(signature
);
1842 // if app is alive or does not exist, we can exit
1843 if (info
== NULL
|| info
->IsRunning())
1853 TRoster::_IsSystemApp(RosterAppInfo
* info
) const
1856 if (path
.SetTo(&info
->ref
) != B_OK
|| path
.GetParent(&path
) != B_OK
)
1859 return !strcmp(path
.Path(), fSystemAppPath
.Path())
1860 || !strcmp(path
.Path(), fSystemServerPath
.Path());
1865 TRoster::_LoadRosterSettings(const char* path
)
1868 const char* settingsPath
1869 = path
? path
: get_default_roster_settings_path(_path
, false);
1871 RosterSettingsCharStream stream
;
1875 error
= file
.SetTo(settingsPath
, B_READ_ONLY
);
1878 error
= file
.GetSize(&size
);
1883 data
= new(nothrow
) char[size
+ 1];
1884 error
= data
? B_OK
: B_NO_MEMORY
;
1887 ssize_t bytes
= file
.Read(data
, size
);
1888 error
= bytes
< 0 ? bytes
: (bytes
== size
? B_OK
: B_FILE_ERROR
);
1892 error
= stream
.SetTo(std::string(data
));
1898 // Clear the current lists as
1899 // we'll be manually building them up
1900 fRecentDocuments
.Clear();
1901 fRecentFolders
.Clear();
1902 fRecentApps
.Clear();
1904 // Now we just walk through the file and read in the info
1906 status_t streamError
;
1907 char str
[B_PATH_NAME_LENGTH
];
1909 // (RecentDoc | RecentFolder | RecentApp)
1910 streamError
= stream
.GetString(str
);
1919 if (strcmp(str
, "RecentDoc") == 0)
1921 else if (strcmp(str
, "RecentFolder") == 0)
1923 else if (strcmp(str
, "RecentApp") == 0)
1926 type
= etSomethingIsAmiss
;
1932 // For curing laziness
1933 std::list
<recent_entry
*>* list
= type
== etDoc
1934 ? &fRecentDocuments
.fEntryList
1935 : &fRecentFolders
.fEntryList
;
1937 char path
[B_PATH_NAME_LENGTH
];
1938 char app
[B_PATH_NAME_LENGTH
];
1939 char rank
[B_PATH_NAME_LENGTH
];
1943 // Convert the given path to an entry ref
1944 streamError
= stream
.GetString(path
);
1946 streamError
= get_ref_for_path(path
, &ref
);
1948 // Add a new entry to the list for each application
1949 // signature and rank we find
1950 while (!streamError
) {
1952 streamError
= stream
.GetString(app
);
1954 BPrivate::Storage::to_lower(app
);
1955 streamError
= stream
.GetString(rank
);
1958 index
= strtoul(rank
, NULL
, 10);
1959 if (index
== ULONG_MAX
)
1960 streamError
= errno
;
1962 recent_entry
* entry
= NULL
;
1964 entry
= new(nothrow
) recent_entry(&ref
, app
,
1966 streamError
= entry
? B_OK
: B_NO_MEMORY
;
1969 D(printf("pushing entry, leaf == '%s', app == "
1970 "'%s', index == %" B_PRId32
"\n",
1971 entry
->ref
.name
, entry
->sig
.c_str(),
1974 list
->push_back(entry
);
1979 D(printf("entry error 0x%" B_PRIx32
"\n",
1982 != RosterSettingsCharStream::kEndOfLine
1984 != RosterSettingsCharStream::kEndOfStream
)
1994 char app
[B_PATH_NAME_LENGTH
];
1995 streamError
= stream
.GetString(app
);
1997 BPrivate::Storage::to_lower(app
);
1998 fRecentApps
.fAppList
.push_back(app
);
2005 // Something was amiss; skip to the next line
2012 if (streamError
== RosterSettingsCharStream::kEndOfStream
)
2016 // Now we must sort our lists of documents and folders by the
2017 // indicies we read for each entry (largest index first)
2018 fRecentDocuments
.fEntryList
.sort(larger_index
);
2019 fRecentFolders
.fEntryList
.sort(larger_index
);
2022 printf("----------------------------------------------------------------------\n");
2023 fRecentDocuments
.Print();
2024 printf("----------------------------------------------------------------------\n");
2025 fRecentFolders
.Print();
2026 printf("----------------------------------------------------------------------\n");
2027 fRecentApps
.Print();
2028 printf("----------------------------------------------------------------------\n");
2032 D(PRINT("WARNING: TRoster::_LoadRosterSettings(): error loading roster "
2033 "settings from '%s', 0x%" B_PRIx32
"\n", settingsPath
, error
));
2040 TRoster::_SaveRosterSettings(const char* path
)
2043 const char* settingsPath
2044 = path
!= NULL
? path
: get_default_roster_settings_path(_path
, true);
2049 file
= fopen(settingsPath
, "w+");
2050 error
= file
? B_OK
: errno
;
2053 saveError
= fRecentDocuments
.Save(file
, "Recent documents", "RecentDoc");
2055 D(PRINT("TRoster::_SaveRosterSettings(): recent documents save "
2056 "failed with error 0x%" B_PRIx32
"\n", saveError
));
2058 saveError
= fRecentFolders
.Save(file
, "Recent folders", "RecentFolder");
2060 D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2061 "failed with error 0x%" B_PRIx32
"\n", saveError
));
2063 saveError
= fRecentApps
.Save(file
);
2065 D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2066 "failed with error 0x%" B_PRIx32
"\n", saveError
));