1/*
2 * Copyright 2001-2008, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7/*!	TRoster is the incarnation of The Roster. It manages the running
8	applications.
9*/
10
11
12#include "TRoster.h"
13
14#include <new>
15
16#include <errno.h>
17#include <stdio.h>
18#include <string.h>
19#include <strings.h>
20
21#include <Application.h>
22#include <AutoDeleter.h>
23#include <Autolock.h>
24#include <Directory.h>
25#include <File.h>
26#include <FindDirectory.h>
27#include <Path.h>
28
29#include <AppMisc.h>
30#include <MessagePrivate.h>
31#include <MessengerPrivate.h>
32#include <RosterPrivate.h>
33#include <ServerProtocol.h>
34#include <storage_support.h>
35
36#include "AppInfoListMessagingTargetSet.h"
37#include "Debug.h"
38#include "EventMaskWatcher.h"
39#include "MessageDeliverer.h"
40#include "RegistrarDefs.h"
41#include "RosterAppInfo.h"
42#include "RosterSettingsCharStream.h"
43
44using std::nothrow;
45using namespace BPrivate;
46
47
48/*!	\class TRoster
49	\brief Implements the application roster.
50
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.
53
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.
59
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.
66
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.
70*/
71
72//! The maximal period of time an app may be early pre-registered (60 s).
73const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
74
75
76//	#pragma mark - Private local functions
77
78
79/*!	\brief Returns the path to the default roster settings.
80
81	\param path BPath to be set to the roster settings path.
82	\param createDirectory makes sure the target directory exists if \c true.
83
84	\return the settings path as C string (\code path.Path() \endcode).
85*/
86static const char*
87get_default_roster_settings_path(BPath& path, bool createDirectory)
88{
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);
91	if (error == B_OK)
92		error = path.Append("system/registrar");
93	if (error == B_OK && createDirectory)
94		error = create_directory(path.Path(), 0777);
95	if (error == B_OK)
96		error = path.Append("RosterSettings");
97
98	return path.Path();
99}
100
101
102/*! \brief Returns true if entry1's index is larger than entry2's index.
103
104	Also returns true if either entry is \c NULL.
105
106	Used for sorting the recent entry lists loaded from disk into the
107	proper order.
108*/
109bool
110larger_index(const recent_entry* entry1, const recent_entry* entry2)
111{
112	if (entry1 && entry2)
113		return entry1->index > entry2->index;
114
115	return true;
116}
117
118
119//	#pragma mark -
120
121
122/*!	\brief Creates a new roster.
123
124	The object is completely initialized and ready to handle requests.
125*/
126TRoster::TRoster()
127	:
128	fLock("roster"),
129	fRegisteredApps(),
130	fEarlyPreRegisteredApps(),
131	fIARRequestsByID(),
132	fIARRequestsByToken(),
133	fActiveApp(NULL),
134	fWatchingService(),
135	fRecentApps(),
136	fRecentDocuments(),
137	fRecentFolders(),
138	fLastToken(0),
139	fShuttingDown(false)
140{
141	find_directory(B_SYSTEM_DIRECTORY, &fSystemAppPath);
142	find_directory(B_SYSTEM_SERVERS_DIRECTORY, &fSystemServerPath);
143}
144
145
146/*!	\brief Frees all resources associated with this object.
147*/
148TRoster::~TRoster()
149{
150}
151
152
153/*!	\brief Handles an AddApplication() request.
154	\param request The request message
155*/
156void
157TRoster::HandleAddApplication(BMessage* request)
158{
159	FUNCTION_START();
160
161	BAutolock _(fLock);
162
163	status_t error = B_OK;
164	// get the parameters
165	const char* signature;
166	entry_ref ref;
167	uint32 flags;
168	team_id team;
169	thread_id thread;
170	port_id port;
171	bool fullReg;
172	if (request->FindString("signature", &signature) != B_OK)
173		signature = NULL;
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)
179		team = -1;
180	if (request->FindInt32("thread", &thread) != B_OK)
181		thread = -1;
182	if (request->FindInt32("port", &port) != B_OK)
183		port = -1;
184	if (request->FindBool("full_registration", &fullReg) != B_OK)
185		fullReg = false;
186
187	PRINT("team: %" B_PRId32 ", signature: %s\n", team, signature);
188	PRINT("full registration: %d\n", fullReg);
189
190	if (fShuttingDown)
191		error = B_SHUTTING_DOWN;
192
193	// check the parameters
194	team_id otherTeam = -1;
195	uint32 token = 0;
196
197	uint32 launchFlags = flags & B_LAUNCH_MASK;
198	BEntry entry(&ref);
199	if (!entry.Exists())
200		SET_ERROR(error, B_ENTRY_NOT_FOUND);
201
202	if (error == B_OK)
203		_ValidateRunning(ref, signature);
204
205	// entry_ref
206	if (error == B_OK) {
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;
218			token = info->token;
219		}
220	}
221
222	// signature
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;
231			token = info->token;
232		}
233	}
234
235	// If no team ID is given, full registration isn't possible.
236	if (error == B_OK) {
237		if (team < 0) {
238			if (fullReg)
239				SET_ERROR(error, B_BAD_VALUE);
240		} else if (fRegisteredApps.InfoFor(team))
241			SET_ERROR(error, B_REG_ALREADY_REGISTERED);
242	}
243
244	// Add the application info.
245	if (error == B_OK) {
246		// alloc and init the info
247		RosterAppInfo* info = new(nothrow) RosterAppInfo;
248		if (info) {
249			info->Init(thread, team, port, flags, &ref, signature);
250			if (fullReg)
251				info->state = APP_STATE_REGISTERED;
252			else
253				info->state = APP_STATE_PRE_REGISTERED;
254			info->registration_time = system_time();
255			// add it to the right list
256			bool addingSuccess = false;
257			if (team >= 0) {
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)
262					_AppAdded(info);
263			} else {
264				token = info->token = _NextToken();
265				addingSuccess = fEarlyPreRegisteredApps.AddInfo(info);
266				PRINT("added to early pre-regs, token: %" B_PRIu32 "\n", token);
267			}
268			if (!addingSuccess)
269				SET_ERROR(error, B_NO_MEMORY);
270		} else
271			SET_ERROR(error, B_NO_MEMORY);
272		// delete the info on failure
273		if (error != B_OK && info)
274			delete info;
275	}
276
277	// reply to the request
278	if (error == B_OK) {
279		// add to recent apps if successful
280		if (signature && signature[0] != '\0')
281			fRecentApps.Add(signature, flags);
282		else
283			fRecentApps.Add(&ref, flags);
284
285		BMessage reply(B_REG_SUCCESS);
286		// The token is valid only when no team ID has been supplied.
287		if (team < 0)
288			reply.AddInt32("token", (int32)token);
289		request->SendReply(&reply);
290	} else {
291		BMessage reply(B_REG_ERROR);
292		reply.AddInt32("error", error);
293		if (otherTeam >= 0)
294			reply.AddInt32("other_team", otherTeam);
295		if (token > 0)
296			reply.AddInt32("token", (int32)token);
297		request->SendReply(&reply);
298	}
299
300	FUNCTION_END();
301}
302
303
304/*!	\brief Handles a CompleteRegistration() request.
305	\param request The request message
306*/
307void
308TRoster::HandleCompleteRegistration(BMessage* request)
309{
310	FUNCTION_START();
311
312	BAutolock _(fLock);
313
314	status_t error = B_OK;
315	// get the parameters
316	team_id team;
317	thread_id thread;
318	port_id port;
319	if (request->FindInt32("team", &team) != B_OK)
320		team = -1;
321	if (request->FindInt32("thread", &thread) != B_OK)
322		thread = -1;
323	if (request->FindInt32("port", &port) != B_OK)
324		port = -1;
325
326	if (fShuttingDown)
327		error = B_SHUTTING_DOWN;
328
329	// check the parameters
330	// port
331	if (error == B_OK && port < 0)
332		SET_ERROR(error, B_BAD_VALUE);
333
334	// thread
335	if (error == B_OK && thread < 0)
336		SET_ERROR(error, B_BAD_VALUE);
337
338	// team
339	if (error == B_OK) {
340		if (team >= 0) {
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;
345				info->port = port;
346				info->state = APP_STATE_REGISTERED;
347				_AppAdded(info);
348			} else
349				SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
350		} else
351			SET_ERROR(error, B_BAD_VALUE);
352	}
353
354	// reply to the request
355	if (error == B_OK) {
356		BMessage reply(B_REG_SUCCESS);
357		request->SendReply(&reply);
358	} else {
359		BMessage reply(B_REG_ERROR);
360		reply.AddInt32("error", error);
361		request->SendReply(&reply);
362	}
363
364	FUNCTION_END();
365}
366
367
368/*!	\brief Handles an IsAppRegistered() request.
369	\param request The request message
370*/
371void
372TRoster::HandleIsAppRegistered(BMessage* request)
373{
374	FUNCTION_START();
375
376	BAutolock _(fLock);
377
378	status_t error = B_OK;
379	// get the parameters
380	entry_ref ref;
381	team_id team;
382	uint32 token;
383	if (request->FindRef("ref", &ref) != B_OK)
384		SET_ERROR(error, B_BAD_VALUE);
385	if (request->FindInt32("team", &team) != B_OK)
386		team = -1;
387	if (request->FindInt32("token", (int32*)&token) != B_OK)
388		token = 0;
389
390	PRINT("team: %" B_PRId32 ", token: %" B_PRIu32 "\n", team, token);
391	PRINT("ref: %" B_PRId32 ", %" B_PRId64 ", %s\n", ref.device, ref.directory,
392		ref.name);
393
394	// check the parameters
395	// entry_ref
396	if (error == B_OK && !BEntry(&ref).Exists())
397		SET_ERROR(error, B_ENTRY_NOT_FOUND);
398	// team/token
399	if (error == B_OK && team < 0 && token == 0)
400		SET_ERROR(error, B_BAD_VALUE);
401
402	// look up the information
403	RosterAppInfo* info = NULL;
404	if (error == B_OK) {
405		if ((info = fRegisteredApps.InfoFor(team)) != NULL) {
406			PRINT("found team in fRegisteredApps\n");
407			_ReplyToIARRequest(request, info);
408		} else if (token > 0
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
412			// request
413			be_app->DetachCurrentMessage();
414			_AddIARRequest(fIARRequestsByToken, token, request);
415		} else if (team >= 0
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
419			// request
420			be_app->DetachCurrentMessage();
421			_AddIARRequest(fIARRequestsByID, team, request);
422		} else {
423			PRINT("didn't find team or ref\n");
424			// team not registered, ref/token not early pre-registered
425			_ReplyToIARRequest(request, NULL);
426		}
427	} else {
428		// reply to the request on error
429		BMessage reply(B_REG_ERROR);
430		reply.AddInt32("error", error);
431		request->SendReply(&reply);
432	}
433
434	FUNCTION_END();
435}
436
437
438/*!	\brief Handles a RemovePreRegApp() request.
439	\param request The request message
440*/
441void
442TRoster::HandleRemovePreRegApp(BMessage* request)
443{
444	FUNCTION_START();
445
446	BAutolock _(fLock);
447
448	status_t error = B_OK;
449	// get the parameters
450	uint32 token;
451	if (request->FindInt32("token", (int32*)&token) != B_OK)
452		SET_ERROR(error, B_BAD_VALUE);
453	// remove the app
454	if (error == B_OK) {
455		RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
456		if (info) {
457			fEarlyPreRegisteredApps.RemoveInfo(info);
458			delete info;
459		} else
460			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
461	}
462	// reply to the request
463	if (error == B_OK) {
464		BMessage reply(B_REG_SUCCESS);
465		request->SendReply(&reply);
466	} else {
467		BMessage reply(B_REG_ERROR);
468		reply.AddInt32("error", error);
469		request->SendReply(&reply);
470	}
471
472	FUNCTION_END();
473}
474
475
476/*!	\brief Handles a RemoveApp() request.
477	\param request The request message
478*/
479void
480TRoster::HandleRemoveApp(BMessage* request)
481{
482	FUNCTION_START();
483
484	BAutolock _(fLock);
485
486	status_t error = B_OK;
487	// get the parameters
488	team_id team;
489	error = request->FindInt32("team", &team);
490	PRINT("team: %" B_PRId32 "\n", team);
491
492	// remove the app
493	if (error == B_OK) {
494		if (RosterAppInfo* info = fRegisteredApps.InfoFor(team)) {
495			RemoveApp(info);
496			delete info;
497		} else
498			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
499	}
500	// reply to the request
501	if (error == B_OK) {
502		BMessage reply(B_REG_SUCCESS);
503		request->SendReply(&reply);
504	} else {
505		BMessage reply(B_REG_ERROR);
506		reply.AddInt32("error", error);
507		request->SendReply(&reply);
508	}
509
510	FUNCTION_END();
511}
512
513
514/*!	\brief Handles a SetThreadAndTeam() request.
515	\param request The request message
516*/
517void
518TRoster::HandleSetThreadAndTeam(BMessage* request)
519{
520	FUNCTION_START();
521
522	BAutolock _(fLock);
523
524	status_t error = B_OK;
525
526	// get the parameters
527	team_id team;
528	thread_id thread;
529	uint32 token;
530	if (request->FindInt32("team", &team) != B_OK)
531		team = -1;
532	if (request->FindInt32("thread", &thread) != B_OK)
533		thread = -1;
534	if (request->FindInt32("token", (int32*)&token) != B_OK)
535		SET_ERROR(error, B_BAD_VALUE);
536
537	// check the parameters
538	// team
539	if (error == B_OK && team < 0)
540		SET_ERROR(error, B_BAD_VALUE);
541
542	PRINT("team: %" B_PRId32 ", thread: %" B_PRId32 ", token: %" B_PRIu32 "\n",
543		team, thread, token);
544
545	port_id port = -1;
546
547	// update the app_info
548	if (error == B_OK) {
549		RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
550		if (info != NULL) {
551			// Set thread and team, create a port for the application and
552			// move the app_info from the list of the early pre-registered
553			// apps to the list of the (pre-)registered apps.
554			fEarlyPreRegisteredApps.RemoveInfo(info);
555			info->team = team;
556			info->thread = thread;
557			// create and transfer the port
558			info->port = port = create_port(B_REG_APP_LOOPER_PORT_CAPACITY,
559				kRAppLooperPortName);
560			if (info->port < 0)
561				SET_ERROR(error, info->port);
562			if (error == B_OK)
563				SET_ERROR(error, set_port_owner(info->port, team));
564			// add the info to the registered apps list
565			if (error == B_OK)
566				SET_ERROR(error, AddApp(info));
567			// cleanup on failure
568			if (error != B_OK) {
569				if (info->port >= 0)
570					delete_port(info->port);
571				delete info;
572				info = NULL;
573			}
574			// handle pending IsAppRegistered() requests
575			IARRequestMap::iterator it = fIARRequestsByID.find(team);
576			if (it != fIARRequestsByID.end()) {
577				BMessageQueue* requests = it->second;
578				if (error == B_OK)
579					_ReplyToIARRequests(requests, info);
580				delete requests;
581				fIARRequestsByID.erase(it);
582			}
583
584			it = fIARRequestsByToken.find((int32)token);
585			if (it != fIARRequestsByToken.end()) {
586				BMessageQueue* requests = it->second;
587				if (error == B_OK)
588					_ReplyToIARRequests(requests, info);
589				delete requests;
590				fIARRequestsByToken.erase(it);
591			}
592		} else
593			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
594	}
595	// reply to the request
596	if (error == B_OK) {
597		BMessage reply(B_REG_SUCCESS);
598		reply.AddInt32("port", port);
599		request->SendReply(&reply);
600	} else {
601		BMessage reply(B_REG_ERROR);
602		reply.AddInt32("error", error);
603		request->SendReply(&reply);
604	}
605
606	FUNCTION_END();
607}
608
609
610/*!	\brief Handles a SetSignature() request.
611	\param request The request message
612*/
613void
614TRoster::HandleSetSignature(BMessage* request)
615{
616	FUNCTION_START();
617
618	BAutolock _(fLock);
619
620	status_t error = B_OK;
621	// get the parameters
622	team_id team;
623	const char* signature;
624	if (request->FindInt32("team", &team) != B_OK)
625		error = B_BAD_VALUE;
626	if (request->FindString("signature", &signature) != B_OK)
627		error = B_BAD_VALUE;
628	// find the app and set the signature
629	if (error == B_OK) {
630		if (RosterAppInfo* info = fRegisteredApps.InfoFor(team))
631			strcpy(info->signature, signature);
632		else
633			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
634	}
635	// reply to the request
636	if (error == B_OK) {
637		BMessage reply(B_REG_SUCCESS);
638		request->SendReply(&reply);
639	} else {
640		BMessage reply(B_REG_ERROR);
641		reply.AddInt32("error", error);
642		request->SendReply(&reply);
643	}
644
645	FUNCTION_END();
646}
647
648
649/*!	\brief Handles a Get{Running,Active,}AppInfo() request.
650	\param request The request message
651*/
652void
653TRoster::HandleGetAppInfo(BMessage* request)
654{
655	FUNCTION_START();
656
657	BAutolock _(fLock);
658
659	// get the parameters
660	team_id team;
661	entry_ref ref;
662	const char* signature;
663	bool hasTeam = true;
664	bool hasRef = true;
665	bool hasSignature = true;
666	if (request->FindInt32("team", &team) != B_OK)
667		hasTeam = false;
668	if (request->FindRef("ref", &ref) != B_OK)
669		hasRef = false;
670	if (request->FindString("signature", &signature) != B_OK)
671		hasSignature = false;
672
673	if (hasTeam)
674		PRINT("team: %" B_PRId32 "\n", team);
675	if (hasRef) {
676		PRINT("ref: %" B_PRId32 ", %" B_PRId64 ", %s\n", ref.device,
677			ref.directory, ref.name);
678	}
679	if (hasSignature)
680		PRINT("signature: %s\n", signature);
681
682	// get the info
683	RosterAppInfo* info = NULL;
684	status_t error = B_OK;
685	if (hasTeam) {
686		info = fRegisteredApps.InfoFor(team);
687		if (info == NULL)
688			SET_ERROR(error, B_BAD_TEAM_ID);
689	} else if (hasRef) {
690		info = fRegisteredApps.InfoFor(&ref);
691		if (info == NULL)
692			SET_ERROR(error, B_ERROR);
693	} else if (hasSignature) {
694		info = fRegisteredApps.InfoFor(signature);
695		if (info == NULL)
696			SET_ERROR(error, B_ERROR);
697	} else {
698		// If neither of those has been supplied, the active application
699		// info is requested.
700		if (fActiveApp)
701			info = fActiveApp;
702		else
703			SET_ERROR(error, B_ERROR);
704	}
705
706	// reply to the request
707	if (error == B_OK) {
708		BMessage reply(B_REG_SUCCESS);
709		_AddMessageAppInfo(&reply, info);
710		request->SendReply(&reply);
711	} else {
712		BMessage reply(B_REG_ERROR);
713		reply.AddInt32("error", error);
714		request->SendReply(&reply);
715	}
716
717	FUNCTION_END();
718}
719
720
721/*!	\brief Handles a GetAppList() request.
722	\param request The request message
723*/
724void
725TRoster::HandleGetAppList(BMessage* request)
726{
727	FUNCTION_START();
728
729	BAutolock _(fLock);
730
731	// get the parameters
732	const char* signature;
733	if (request->FindString("signature", &signature) != B_OK)
734		signature = NULL;
735
736	// reply to the request
737	BMessage reply(B_REG_SUCCESS);
738	// get the list
739	for (AppInfoList::Iterator it(fRegisteredApps.It());
740		 RosterAppInfo* info = *it;
741		 ++it) {
742		if (info->state != APP_STATE_REGISTERED)
743			continue;
744		if (signature == NULL || strcasecmp(signature, info->signature) == 0)
745			reply.AddInt32("teams", info->team);
746	}
747	request->SendReply(&reply);
748
749	FUNCTION_END();
750}
751
752
753/*!	\brief Handles a _UpdateActiveApp() request.
754
755	This is sent from the app_server when the current active application
756	is changed.
757
758	\param request The request message
759*/
760void
761TRoster::HandleUpdateActiveApp(BMessage* request)
762{
763	FUNCTION_START();
764
765	BAutolock _(fLock);
766
767	// get the parameters
768	status_t error = B_OK;
769	team_id team;
770	if (request->FindInt32("team", &team) != B_OK)
771		error = B_BAD_VALUE;
772
773	// activate the app
774	if (error == B_OK) {
775		if (RosterAppInfo* info = fRegisteredApps.InfoFor(team))
776			UpdateActiveApp(info);
777		else
778			error = B_BAD_TEAM_ID;
779	}
780
781	// reply to the request
782	if (request->IsSourceWaiting()) {
783		if (error == B_OK) {
784			BMessage reply(B_REG_SUCCESS);
785			request->SendReply(&reply);
786		} else {
787			BMessage reply(B_REG_ERROR);
788			reply.AddInt32("error", error);
789			request->SendReply(&reply);
790		}
791	}
792
793	FUNCTION_END();
794}
795
796
797/*!	\brief Handles a Broadcast() request.
798	\param request The request message
799*/
800void
801TRoster::HandleBroadcast(BMessage* request)
802{
803	FUNCTION_START();
804
805	BAutolock _(fLock);
806
807	status_t error = B_OK;
808	// get the parameters
809	team_id team;
810	BMessage message;
811	BMessenger replyTarget;
812	if (request->FindInt32("team", &team) != B_OK)
813		team = -1;
814	if (error == B_OK && request->FindMessage("message", &message) != B_OK)
815		error = B_BAD_VALUE;
816	if (error == B_OK
817		&& request->FindMessenger("reply_target", &replyTarget) != B_OK) {
818		error = B_BAD_VALUE;
819	}
820
821	// reply to the request -- do this first, don't let the inquirer wait
822	if (error == B_OK) {
823		BMessage reply(B_REG_SUCCESS);
824		request->SendReply(&reply);
825	} else {
826		BMessage reply(B_REG_ERROR);
827		reply.AddInt32("error", error);
828		request->SendReply(&reply);
829	}
830
831	// broadcast the message
832	if (error == B_OK) {
833		// the target set (excludes the registrar and the requesting team)
834		class BroadcastMessagingTargetSet
835			: public AppInfoListMessagingTargetSet {
836			public:
837				BroadcastMessagingTargetSet(AppInfoList& list, team_id team)
838					: AppInfoListMessagingTargetSet(list, true),
839					  fTeam(team)
840				{
841				}
842
843				virtual bool Filter(const RosterAppInfo* info)
844				{
845					return AppInfoListMessagingTargetSet::Filter(info)
846						&& (info->team != fTeam);
847				}
848
849			private:
850				team_id	fTeam;
851		} targetSet(fRegisteredApps, team);
852
853		if (targetSet.HasNext()) {
854			// set the reply target
855			BMessage::Private(message).SetReply(replyTarget);
856
857			// send the messages
858			MessageDeliverer::Default()->DeliverMessage(&message, targetSet);
859		}
860	}
861
862	FUNCTION_END();
863}
864
865
866/*!	\brief Handles a StartWatching() request.
867	\param request The request message
868*/
869void
870TRoster::HandleStartWatching(BMessage* request)
871{
872	FUNCTION_START();
873
874	BAutolock _(fLock);
875
876	status_t error = B_OK;
877	// get the parameters
878	BMessenger target;
879	uint32 events;
880	if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
881		error = B_BAD_VALUE;
882	if (request->FindInt32("events", (int32*)&events) != B_OK)
883		error = B_BAD_VALUE;
884	// add the new watcher
885	if (error == B_OK) {
886		Watcher* watcher = new(nothrow) EventMaskWatcher(target, events);
887		if (watcher) {
888			if (!fWatchingService.AddWatcher(watcher)) {
889				error = B_NO_MEMORY;
890				delete watcher;
891			}
892		} else
893			error = B_NO_MEMORY;
894	}
895	// reply to the request
896	if (error == B_OK) {
897		BMessage reply(B_REG_SUCCESS);
898		request->SendReply(&reply);
899	} else {
900		BMessage reply(B_REG_ERROR);
901		reply.AddInt32("error", error);
902		request->SendReply(&reply);
903	}
904
905	FUNCTION_END();
906}
907
908
909/*!	\brief Handles a StopWatching() request.
910	\param request The request message
911*/
912void
913TRoster::HandleStopWatching(BMessage* request)
914{
915	FUNCTION_START();
916
917	BAutolock _(fLock);
918
919	status_t error = B_OK;
920	// get the parameters
921	BMessenger target;
922	if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
923		error = B_BAD_VALUE;
924	// remove the watcher
925	if (error == B_OK) {
926		if (!fWatchingService.RemoveWatcher(target))
927			error = B_BAD_VALUE;
928	}
929	// reply to the request
930	if (error == B_OK) {
931		BMessage reply(B_REG_SUCCESS);
932		request->SendReply(&reply);
933	} else {
934		BMessage reply(B_REG_ERROR);
935		reply.AddInt32("error", error);
936		request->SendReply(&reply);
937	}
938
939	FUNCTION_END();
940}
941
942
943/*!	\brief Handles a GetRecentDocuments() request.
944	\param request The request message
945*/
946void
947TRoster::HandleGetRecentDocuments(BMessage* request)
948{
949	FUNCTION_START();
950
951	BAutolock _(fLock);
952
953	_HandleGetRecentEntries(request);
954
955	FUNCTION_END();
956}
957
958
959/*!	\brief Handles a GetRecentFolders() request.
960	\param request The request message
961*/
962void
963TRoster::HandleGetRecentFolders(BMessage* request)
964{
965	FUNCTION_START();
966
967	BAutolock _(fLock);
968
969	_HandleGetRecentEntries(request);
970
971	FUNCTION_END();
972}
973
974
975/*!	\brief Handles a GetRecentApps() request.
976	\param request The request message
977*/
978void
979TRoster::HandleGetRecentApps(BMessage* request)
980{
981	FUNCTION_START();
982
983	BAutolock _(fLock);
984
985	if (!request) {
986		D(PRINT("WARNING: TRoster::HandleGetRecentApps(NULL) called\n"));
987		return;
988	}
989
990	int32 maxCount;
991	BMessage reply(B_REG_RESULT);
992
993	status_t error = request->FindInt32("max count", &maxCount);
994	if (!error)
995		error = fRecentApps.Get(maxCount, &reply);
996	reply.AddInt32("result", error);
997	request->SendReply(&reply);
998
999	FUNCTION_END();
1000}
1001
1002
1003/*!	\brief Handles an AddToRecentDocuments() request.
1004	\param request The request message
1005*/
1006void
1007TRoster::HandleAddToRecentDocuments(BMessage* request)
1008{
1009	FUNCTION_START();
1010
1011	BAutolock _(fLock);
1012
1013	if (!request) {
1014		D(PRINT("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n"));
1015		return;
1016	}
1017
1018	entry_ref ref;
1019	const char* appSig;
1020	BMessage reply(B_REG_RESULT);
1021
1022	status_t error = request->FindRef("ref", &ref);
1023	if (!error)
1024		error = request->FindString("app sig", &appSig);
1025	if (!error)
1026		error = fRecentDocuments.Add(&ref, appSig);
1027	reply.AddInt32("result", error);
1028	request->SendReply(&reply);
1029
1030	FUNCTION_END();
1031}
1032
1033
1034/*!	\brief Handles an AddToRecentFolders() request.
1035	\param request The request message
1036*/
1037void
1038TRoster::HandleAddToRecentFolders(BMessage* request)
1039{
1040	FUNCTION_START();
1041
1042	BAutolock _(fLock);
1043
1044	if (!request) {
1045		D(PRINT("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n"));
1046		return;
1047	}
1048
1049	entry_ref ref;
1050	const char* appSig;
1051	BMessage reply(B_REG_RESULT);
1052
1053	status_t error = request->FindRef("ref", &ref);
1054	if (!error)
1055		error = request->FindString("app sig", &appSig);
1056	if (!error)
1057		error = fRecentFolders.Add(&ref, appSig);
1058	reply.AddInt32("result", error);
1059	request->SendReply(&reply);
1060
1061	FUNCTION_END();
1062}
1063
1064
1065/*!	\brief Handles an AddToRecentApps() request.
1066	\param request The request message
1067*/
1068void
1069TRoster::HandleAddToRecentApps(BMessage* request)
1070{
1071	FUNCTION_START();
1072
1073	BAutolock _(fLock);
1074
1075	if (!request) {
1076		D(PRINT("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n"));
1077		return;
1078	}
1079
1080	const char* appSig;
1081	BMessage reply(B_REG_RESULT);
1082
1083	status_t error = request->FindString("app sig", &appSig);
1084	if (!error)
1085		error = fRecentApps.Add(appSig);
1086	reply.AddInt32("result", error);
1087	request->SendReply(&reply);
1088
1089	FUNCTION_END();
1090}
1091
1092
1093void
1094TRoster::HandleLoadRecentLists(BMessage* request)
1095{
1096	FUNCTION_START();
1097
1098	BAutolock _(fLock);
1099
1100	if (!request) {
1101		D(PRINT("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n"));
1102		return;
1103	}
1104
1105	const char* filename;
1106	BMessage reply(B_REG_RESULT);
1107
1108	status_t error = request->FindString("filename", &filename);
1109	if (!error)
1110		error = _LoadRosterSettings(filename);
1111	reply.AddInt32("result", error);
1112	request->SendReply(&reply);
1113
1114	FUNCTION_END();
1115}
1116
1117
1118void
1119TRoster::HandleSaveRecentLists(BMessage* request)
1120{
1121	FUNCTION_START();
1122
1123	BAutolock _(fLock);
1124
1125	if (!request) {
1126		D(PRINT("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n"));
1127		return;
1128	}
1129
1130	const char* filename;
1131	BMessage reply(B_REG_RESULT);
1132
1133	status_t error = request->FindString("filename", &filename);
1134	if (!error)
1135		error = _SaveRosterSettings(filename);
1136	reply.AddInt32("result", error);
1137	request->SendReply(&reply);
1138
1139	FUNCTION_END();
1140}
1141
1142
1143void
1144TRoster::HandleRestartAppServer(BMessage* request)
1145{
1146	BAutolock _(fLock);
1147
1148	// TODO: if an app_server is still running, stop it first
1149
1150	const char* pathString;
1151	if (request->FindString("path", &pathString) != B_OK)
1152		pathString = "/boot/system/servers";
1153	BPath path(pathString);
1154	path.Append("app_server");
1155	// NOTE: its required at some point that the binary name is "app_server"
1156
1157	const char **argv = new const char * [2];
1158	argv[0] = strdup(path.Path());
1159	argv[1] = NULL;
1160
1161	thread_id threadId = load_image(1, argv, (const char**)environ);
1162	int i;
1163	for (i = 0; i < 1; i++)
1164		delete argv[i];
1165	delete [] argv;
1166
1167	resume_thread(threadId);
1168	// give the server some time to create the server port
1169	snooze(100000);
1170
1171	// notify all apps
1172	// TODO: whats about ourself?
1173	AppInfoListMessagingTargetSet targetSet(fRegisteredApps);
1174	if (targetSet.HasNext()) {
1175		// send the messages
1176		BMessage message(kMsgAppServerRestarted);
1177		MessageDeliverer::Default()->DeliverMessage(&message, targetSet);
1178	}
1179}
1180
1181
1182/*!	\brief Clears the current list of recent documents
1183*/
1184void
1185TRoster::ClearRecentDocuments()
1186{
1187	BAutolock _(fLock);
1188
1189	fRecentDocuments.Clear();
1190}
1191
1192
1193/*!	\brief Clears the current list of recent folders
1194*/
1195void
1196TRoster::ClearRecentFolders()
1197{
1198	BAutolock _(fLock);
1199
1200	fRecentFolders.Clear();
1201}
1202
1203
1204/*!	\brief Clears the current list of recent apps
1205*/
1206void
1207TRoster::ClearRecentApps()
1208{
1209	BAutolock _(fLock);
1210
1211	fRecentApps.Clear();
1212}
1213
1214
1215/*!	\brief Initializes the roster.
1216
1217	Currently only adds the registrar to the roster.
1218	The application must already be running, more precisly Run() must have
1219	been called.
1220
1221	\return
1222	- \c B_OK: Everything went fine.
1223	- an error code
1224*/
1225status_t
1226TRoster::Init()
1227{
1228	// check lock initialization
1229	if (fLock.InitCheck() < 0)
1230		return fLock.InitCheck();
1231
1232	// create the info
1233	RosterAppInfo* info = new(nothrow) RosterAppInfo;
1234	if (info == NULL)
1235		return B_NO_MEMORY;
1236
1237	// get the app's ref
1238	entry_ref ref;
1239	status_t error = get_app_ref(&ref);
1240
1241	// init and add the info
1242	if (error == B_OK) {
1243		info->Init(be_app->Thread(), be_app->Team(),
1244			BMessenger::Private(be_app_messenger).Port(),
1245			B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP, &ref, B_REGISTRAR_SIGNATURE);
1246		info->state = APP_STATE_REGISTERED;
1247		info->registration_time = system_time();
1248		error = AddApp(info);
1249	}
1250
1251	if (error == B_OK)
1252		_LoadRosterSettings();
1253
1254	// cleanup on error
1255	if (error != B_OK)
1256		delete info;
1257
1258	return error;
1259}
1260
1261
1262/*!	\brief Add the supplied app info to the list of (pre-)registered apps.
1263
1264	\param info The app info to be added
1265*/
1266status_t
1267TRoster::AddApp(RosterAppInfo* info)
1268{
1269	BAutolock _(fLock);
1270
1271	status_t error = (info ? B_OK : B_BAD_VALUE);
1272	if (info) {
1273		if (!fRegisteredApps.AddInfo(info))
1274			error = B_NO_MEMORY;
1275	}
1276	return error;
1277}
1278
1279
1280/*!	\brief Removes the supplied app info from the list of (pre-)registered
1281	apps.
1282
1283	\param info The app info to be removed
1284*/
1285void
1286TRoster::RemoveApp(RosterAppInfo* info)
1287{
1288	BAutolock _(fLock);
1289
1290	if (info) {
1291		if (fRegisteredApps.RemoveInfo(info)) {
1292			if (info->state == APP_STATE_REGISTERED) {
1293				info->state = APP_STATE_UNREGISTERED;
1294				_AppRemoved(info);
1295			}
1296		}
1297	}
1298}
1299
1300
1301/*!	\brief Activates the application identified by \a info.
1302
1303	The currently active application is deactivated and the one whose
1304	info is supplied is activated. \a info may be \c NULL, which only
1305	deactivates the currently active application.
1306
1307	\param info The info of the app to be activated
1308*/
1309void
1310TRoster::UpdateActiveApp(RosterAppInfo* info)
1311{
1312	BAutolock _(fLock);
1313
1314	if (info != fActiveApp) {
1315		// deactivate the currently active app
1316		RosterAppInfo* oldActiveApp = fActiveApp;
1317		fActiveApp = NULL;
1318		if (oldActiveApp)
1319			_AppDeactivated(oldActiveApp);
1320
1321		// activate the new app
1322		if (info) {
1323			fActiveApp = info;
1324			_AppActivated(info);
1325		}
1326	}
1327}
1328
1329
1330/*!	\brief Checks whether the (pre-)registered applications are still running.
1331
1332	This is necessary, since killed applications don't unregister properly.
1333*/
1334void
1335TRoster::CheckSanity()
1336{
1337	BAutolock _(fLock);
1338
1339	// not early (pre-)registered applications
1340	AppInfoList obsoleteApps;
1341	for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) {
1342		if (!(*it)->IsRunning())
1343			obsoleteApps.AddInfo(*it);
1344	}
1345
1346	// remove the apps
1347	for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
1348		RemoveApp(*it);
1349		delete *it;
1350	}
1351	obsoleteApps.MakeEmpty(false);
1352		// don't delete infos a second time
1353
1354	// early pre-registered applications
1355	bigtime_t timeLimit = system_time() - kMaximalEarlyPreRegistrationPeriod;
1356	for (AppInfoList::Iterator it = fEarlyPreRegisteredApps.It();
1357		 it.IsValid();
1358		 ++it) {
1359		if ((*it)->registration_time < timeLimit)
1360			obsoleteApps.AddInfo(*it);
1361	}
1362
1363	// remove the apps
1364	for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
1365		fEarlyPreRegisteredApps.RemoveInfo(*it);
1366		delete *it;
1367	}
1368	obsoleteApps.MakeEmpty(false);
1369		// don't delete infos a second time
1370}
1371
1372
1373/*!	\brief Tells the roster whether a shutdown process is in progess at the
1374		   moment.
1375
1376	After this method is called with \a shuttingDown == \c true, no more
1377	applications can be created.
1378
1379	\param shuttingDown \c true, to indicate the start of the shutdown process,
1380		   \c false to signalling its end.
1381*/
1382void
1383TRoster::SetShuttingDown(bool shuttingDown)
1384{
1385	BAutolock _(fLock);
1386
1387	fShuttingDown = shuttingDown;
1388
1389	if (shuttingDown)
1390		_SaveRosterSettings();
1391}
1392
1393
1394/*!	\brief Returns lists of applications to be asked to quit on shutdown.
1395
1396	\param userApps List of RosterAppInfos identifying the user applications.
1397		   Those will be ask to quit first.
1398	\param systemApps List of RosterAppInfos identifying the system applications
1399		   (like Tracker and Deskbar), which will be asked to quit after the
1400		   user applications are gone.
1401	\param vitalSystemApps A set of team_ids identifying teams that must not
1402		   be terminated (app server and registrar).
1403	\return \c B_OK, if everything went fine, another error code otherwise.
1404*/
1405status_t
1406TRoster::GetShutdownApps(AppInfoList& userApps, AppInfoList& systemApps,
1407	AppInfoList& backgroundApps, HashSet<HashKey32<team_id> >& vitalSystemApps)
1408{
1409	BAutolock _(fLock);
1410
1411	status_t error = B_OK;
1412
1413	// get the vital system apps:
1414	// * ourself
1415	// * kernel team
1416	// * app server
1417	// * debug server
1418
1419	// ourself
1420	vitalSystemApps.Add(be_app->Team());
1421
1422	// kernel team
1423	team_info teamInfo;
1424	if (get_team_info(B_SYSTEM_TEAM, &teamInfo) == B_OK)
1425		vitalSystemApps.Add(teamInfo.team);
1426
1427	// app server
1428	RosterAppInfo* info
1429		= fRegisteredApps.InfoFor("application/x-vnd.haiku-app_server");
1430	if (info != NULL)
1431		vitalSystemApps.Add(info->team);
1432
1433	// debug server
1434	info = fRegisteredApps.InfoFor("application/x-vnd.haiku-debug_server");
1435	if (info != NULL)
1436		vitalSystemApps.Add(info->team);
1437
1438	// populate the other groups
1439	for (AppInfoList::Iterator it(fRegisteredApps.It());
1440			RosterAppInfo* info = *it; ++it) {
1441		if (!vitalSystemApps.Contains(info->team)) {
1442			RosterAppInfo* clonedInfo = info->Clone();
1443			if (clonedInfo) {
1444				if (_IsSystemApp(info)) {
1445					if (!systemApps.AddInfo(clonedInfo))
1446						error = B_NO_MEMORY;
1447				} else if (info->flags & B_BACKGROUND_APP) {
1448					if (!backgroundApps.AddInfo(clonedInfo))
1449						error = B_NO_MEMORY;
1450				} else {
1451					if (!userApps.AddInfo(clonedInfo))
1452						error = B_NO_MEMORY;
1453				}
1454
1455				if (error != B_OK)
1456					delete clonedInfo;
1457			} else
1458				error = B_NO_MEMORY;
1459		}
1460
1461		if (error != B_OK)
1462			break;
1463	}
1464
1465	// Special case, we add the input server to vital apps here so it is
1466	// not excluded in the lists above
1467	info = fRegisteredApps.InfoFor("application/x-vnd.Be-input_server");
1468	if (info != NULL)
1469		vitalSystemApps.Add(info->team);
1470
1471	// clean up on error
1472	if (error != B_OK) {
1473		userApps.MakeEmpty(true);
1474		systemApps.MakeEmpty(true);
1475	}
1476
1477	return error;
1478}
1479
1480
1481status_t
1482TRoster::AddAppInfo(AppInfoList& apps, team_id team)
1483{
1484	BAutolock _(fLock);
1485
1486	for (AppInfoList::Iterator it(fRegisteredApps.It());
1487			RosterAppInfo* info = *it; ++it) {
1488		if (info->team == team) {
1489			RosterAppInfo* clonedInfo = info->Clone();
1490			status_t error = B_NO_MEMORY;
1491			if (clonedInfo != NULL) {
1492				if (!apps.AddInfo(clonedInfo))
1493					delete clonedInfo;
1494				else
1495					error = B_OK;
1496			}
1497			return error;
1498		}
1499	}
1500
1501	return B_BAD_TEAM_ID;
1502}
1503
1504
1505status_t
1506TRoster::AddWatcher(Watcher* watcher)
1507{
1508	BAutolock _(fLock);
1509
1510	if (!watcher)
1511		return B_BAD_VALUE;
1512
1513	if (!fWatchingService.AddWatcher(watcher))
1514		return B_NO_MEMORY;
1515
1516	return B_OK;
1517}
1518
1519
1520void
1521TRoster::RemoveWatcher(Watcher* watcher)
1522{
1523	BAutolock _(fLock);
1524
1525	if (watcher)
1526		fWatchingService.RemoveWatcher(watcher, false);
1527}
1528
1529
1530/*!	\brief Hook method invoked, when an application has been fully registered.
1531	\param info The RosterAppInfo of the added application.
1532*/
1533void
1534TRoster::_AppAdded(RosterAppInfo* info)
1535{
1536	// notify the watchers
1537	BMessage message(B_SOME_APP_LAUNCHED);
1538	_AddMessageWatchingInfo(&message, info);
1539	EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED);
1540	fWatchingService.NotifyWatchers(&message, &filter);
1541}
1542
1543
1544/*!	\brief Hook method invoked, when a fully registered application has been
1545		removed.
1546	\param info The RosterAppInfo of the removed application.
1547*/
1548void
1549TRoster::_AppRemoved(RosterAppInfo* info)
1550{
1551	if (info) {
1552		// deactivate the app, if it was the active one
1553		if (info == fActiveApp)
1554			UpdateActiveApp(NULL);
1555
1556		// notify the watchers
1557		BMessage message(B_SOME_APP_QUIT);
1558		_AddMessageWatchingInfo(&message, info);
1559		EventMaskWatcherFilter filter(B_REQUEST_QUIT);
1560		fWatchingService.NotifyWatchers(&message, &filter);
1561	}
1562}
1563
1564
1565/*!	\brief Hook method invoked, when an application has been activated.
1566	\param info The RosterAppInfo of the activated application.
1567*/
1568void
1569TRoster::_AppActivated(RosterAppInfo* info)
1570{
1571	if (info != NULL && info->state == APP_STATE_REGISTERED) {
1572		// send B_APP_ACTIVATED to the app
1573		BMessenger messenger;
1574		BMessenger::Private messengerPrivate(messenger);
1575		messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
1576		BMessage message(B_APP_ACTIVATED);
1577		message.AddBool("active", true);
1578		// not sure, if it makes sense to use the MessageDeliverer here
1579		MessageDeliverer::Default()->DeliverMessage(&message, messenger);
1580
1581		// notify the watchers
1582		BMessage watcherMessage(B_SOME_APP_ACTIVATED);
1583		_AddMessageWatchingInfo(&watcherMessage, info);
1584		EventMaskWatcherFilter filter(B_REQUEST_ACTIVATED);
1585		fWatchingService.NotifyWatchers(&watcherMessage, &filter);
1586	}
1587}
1588
1589
1590/*!	\brief Hook method invoked, when an application has been deactivated.
1591	\param info The RosterAppInfo of the deactivated application.
1592*/
1593void
1594TRoster::_AppDeactivated(RosterAppInfo* info)
1595{
1596	if (info != NULL && info->state == APP_STATE_REGISTERED) {
1597		// send B_APP_ACTIVATED to the app
1598		BMessenger messenger;
1599		BMessenger::Private messengerPrivate(messenger);
1600		messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
1601		BMessage message(B_APP_ACTIVATED);
1602		message.AddBool("active", false);
1603		// not sure, if it makes sense to use the MessageDeliverer here
1604		MessageDeliverer::Default()->DeliverMessage(&message, messenger);
1605	}
1606}
1607
1608
1609/*!	\brief Adds an app_info to a message.
1610
1611	The info is added as a flat_app_info to a field "app_info" with the type
1612	\c B_REG_APP_INFO_TYPE.
1613
1614	\param message The message
1615	\param info The app_info.
1616	\return \c B_OK if everything went fine, an error code otherwise.
1617*/
1618status_t
1619TRoster::_AddMessageAppInfo(BMessage* message, const app_info* info)
1620{
1621	// An app_info is not completely flat. The entry_ref contains a string
1622	// pointer. Therefore we flatten the info.
1623	flat_app_info flatInfo;
1624	flatInfo.thread = info->thread;
1625	flatInfo.team = info->team;
1626	flatInfo.port = info->port;
1627	flatInfo.flags = info->flags;
1628	flatInfo.ref_device = info->ref.device;
1629	flatInfo.ref_directory = info->ref.directory;
1630	memcpy(flatInfo.signature, info->signature, B_MIME_TYPE_LENGTH);
1631
1632	// set the ref name to NULL and copy it into the flat structure
1633	flatInfo.ref_name[0] = '\0';
1634	if (info->ref.name)
1635		strcpy(flatInfo.ref_name, info->ref.name);
1636
1637	// add the flat info
1638	return message->AddData("app_info", B_REG_APP_INFO_TYPE, &flatInfo,
1639		sizeof(flat_app_info));
1640}
1641
1642
1643/*!	\brief Adds application monitoring related fields to a message.
1644	\param message The message.
1645	\param info The app_info of the concerned application.
1646	\return \c B_OK if everything went fine, an error code otherwise.
1647*/
1648status_t
1649TRoster::_AddMessageWatchingInfo(BMessage* message, const app_info* info)
1650{
1651	status_t error = B_OK;
1652	if (error == B_OK)
1653		error = message->AddString("be:signature", info->signature);
1654	if (error == B_OK)
1655		error = message->AddInt32("be:team", info->team);
1656	if (error == B_OK)
1657		error = message->AddInt32("be:thread", info->thread);
1658	if (error == B_OK)
1659		error = message->AddInt32("be:flags", (int32)info->flags);
1660	if (error == B_OK)
1661		error = message->AddRef("be:ref", &info->ref);
1662	return error;
1663}
1664
1665
1666/*!	\brief Returns the next available token.
1667	\return The token.
1668*/
1669uint32
1670TRoster::_NextToken()
1671{
1672	return ++fLastToken;
1673}
1674
1675
1676/*!	\brief Adds an IsAppRegistered() request to the given map.
1677
1678	If something goes wrong, the method deletes the request.
1679
1680	\param map The map the request shall be added to.
1681	\param key The key under which to add the request.
1682	\param request The request message to be added.
1683*/
1684void
1685TRoster::_AddIARRequest(IARRequestMap& map, int32 key, BMessage* request)
1686{
1687	IARRequestMap::iterator it = map.find(key);
1688	BMessageQueue* requests = NULL;
1689	if (it == map.end()) {
1690		requests = new(nothrow) BMessageQueue();
1691		if (!requests) {
1692			delete request;
1693			return;
1694		}
1695
1696		map[key] = requests;
1697	} else
1698		requests = it->second;
1699
1700	requests->AddMessage(request);
1701}
1702
1703
1704/*!	\brief Invokes _ReplyToIARRequest() for all messages in the given
1705		   message queue.
1706
1707	\param requests The request messages to be replied to
1708	\param info The RosterAppInfo of the application in question
1709		   (may be \c NULL)
1710*/
1711void
1712TRoster::_ReplyToIARRequests(BMessageQueue* requests, const RosterAppInfo* info)
1713{
1714	while (BMessage* request = requests->NextMessage()) {
1715		_ReplyToIARRequest(request, info);
1716		delete request;
1717	}
1718}
1719
1720
1721/*!	\brief Sends a reply message to an IsAppRegistered() request.
1722
1723	The message to be sent is a simple \c B_REG_SUCCESS message containing
1724	a "pre-registered" field, that says whether or not the application is
1725	pre-registered. It will be set to \c false, unless an \a info is supplied
1726	and the application this info refers to is pre-registered.
1727
1728	\param request The request message to be replied to
1729	\param info The RosterAppInfo of the application in question
1730		   (may be \c NULL)
1731*/
1732void
1733TRoster::_ReplyToIARRequest(BMessage* request, const RosterAppInfo* info)
1734{
1735	// pre-registered or registered?
1736	bool preRegistered = false;
1737	if (info) {
1738		switch (info->state) {
1739			case APP_STATE_PRE_REGISTERED:
1740				preRegistered = true;
1741				break;
1742			case APP_STATE_UNREGISTERED:
1743			case APP_STATE_REGISTERED:
1744				preRegistered = false;
1745				break;
1746		}
1747	}
1748	// send reply
1749	BMessage reply(B_REG_SUCCESS);
1750	reply.AddBool("registered", (bool)info);
1751	reply.AddBool("pre-registered", preRegistered);
1752	PRINT("_ReplyToIARRequest(): pre-registered: %d\n", preRegistered);
1753	if (info)
1754		_AddMessageAppInfo(&reply, info);
1755	request->SendReply(&reply);
1756}
1757
1758
1759/*! \brief Handles requests for both GetRecentDocuments() and
1760	GetRecentFolders().
1761*/
1762void
1763TRoster::_HandleGetRecentEntries(BMessage* request)
1764{
1765	FUNCTION_START();
1766	if (!request) {
1767		D(PRINT("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n"));
1768		return;
1769	}
1770
1771	int32 maxCount;
1772	BMessage reply(B_REG_RESULT);
1773	char** fileTypes = NULL;
1774	int32 fileTypesCount = 0;
1775	char* appSig = NULL;
1776
1777	status_t error = request->FindInt32("max count", &maxCount);
1778	// Look for optional file type(s)
1779	if (!error) {
1780		type_code typeFound;
1781		status_t typeError = request->GetInfo("file type", &typeFound,
1782			&fileTypesCount);
1783		if (!typeError)
1784			typeError = typeFound == B_STRING_TYPE ? B_OK : B_BAD_TYPE;
1785		if (!typeError) {
1786			fileTypes = new(nothrow) char*[fileTypesCount];
1787			typeError = fileTypes ? B_OK : B_NO_MEMORY;
1788		}
1789		if (!typeError) {
1790			for (int i = 0; !error && i < fileTypesCount; i++) {
1791				const char* type;
1792				if (request->FindString("file type", i, &type) == B_OK) {
1793					fileTypes[i] = new(nothrow) char[B_MIME_TYPE_LENGTH];
1794					error = fileTypes[i] ? B_OK : B_NO_MEMORY;
1795						// Yes, I do mean to use "error" here, not "typeError"
1796					BPrivate::Storage::to_lower(type, fileTypes[i]);
1797						// Types are expected to be lowercase
1798				}
1799			}
1800		}
1801	}
1802	// Look for optional app sig
1803	if (!error) {
1804		const char* sig;
1805		error = request->FindString("app sig", &sig);
1806		if (!error) {
1807			appSig = new(nothrow) char[B_MIME_TYPE_LENGTH];
1808			error = appSig ? B_OK : B_NO_MEMORY;
1809			BPrivate::Storage::to_lower(sig, appSig);
1810		} else if (error == B_NAME_NOT_FOUND)
1811			error = B_OK;
1812	}
1813	if (!error) {
1814		switch (request->what) {
1815			case B_REG_GET_RECENT_DOCUMENTS:
1816				error = fRecentDocuments.Get(maxCount, (const char**)fileTypes,
1817					fileTypesCount, appSig, &reply);
1818				D(fRecentDocuments.Print());
1819		   	    break;
1820
1821			case B_REG_GET_RECENT_FOLDERS:
1822				error = fRecentFolders.Get(maxCount, (const char**)fileTypes,
1823					fileTypesCount, appSig, &reply);
1824				D(fRecentFolders.Print());
1825			    break;
1826
1827			default:
1828				D(PRINT("WARNING: TRoster::_HandleGetRecentEntries(): "
1829					"unexpected request->what value of 0x%" B_PRIx32 "\n",
1830					request->what));
1831				error = B_BAD_VALUE;
1832				break;
1833		}
1834	}
1835	reply.AddInt32("result", error);
1836	// Clean up before sending a reply
1837	delete [] appSig;
1838	if (fileTypes) {
1839		for (int i = 0; i < fileTypesCount; i++)
1840			delete [] fileTypes[i];
1841		delete[] fileTypes;
1842		fileTypes = NULL;
1843	}
1844	request->SendReply(&reply);
1845
1846	FUNCTION_END();
1847}
1848
1849
1850/*!
1851	\brief Checks all registered apps for \a ref and \a signature if
1852		they are still alive, and removes those that aren't.
1853*/
1854void
1855TRoster::_ValidateRunning(const entry_ref& ref, const char* signature)
1856{
1857	while (true) {
1858		// get info via ref or signature
1859		RosterAppInfo* info = fRegisteredApps.InfoFor(&ref);
1860		if (info == NULL && signature != NULL)
1861			info = fRegisteredApps.InfoFor(signature);
1862
1863		// if app is alive or does not exist, we can exit
1864		if (info == NULL || info->IsRunning())
1865			return;
1866
1867		RemoveApp(info);
1868		delete info;
1869	}
1870}
1871
1872
1873bool
1874TRoster::_IsSystemApp(RosterAppInfo* info) const
1875{
1876	BPath path;
1877	if (path.SetTo(&info->ref) != B_OK || path.GetParent(&path) != B_OK)
1878		return false;
1879
1880	return !strcmp(path.Path(), fSystemAppPath.Path())
1881		|| !strcmp(path.Path(), fSystemServerPath.Path());
1882}
1883
1884
1885status_t
1886TRoster::_LoadRosterSettings(const char* path)
1887{
1888	BPath _path;
1889	const char* settingsPath
1890		= path ? path : get_default_roster_settings_path(_path, false);
1891
1892	RosterSettingsCharStream stream;
1893	status_t error;
1894	BFile file;
1895
1896	error = file.SetTo(settingsPath, B_READ_ONLY);
1897	off_t size;
1898	if (!error)
1899		error = file.GetSize(&size);
1900
1901	char* data = NULL;
1902
1903	if (!error) {
1904		data = new(nothrow) char[size + 1];
1905		error = data ? B_OK : B_NO_MEMORY;
1906	}
1907	if (!error) {
1908		ssize_t bytes = file.Read(data, size);
1909		error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR);
1910	}
1911	if (!error) {
1912		data[size] = 0;
1913		error = stream.SetTo(std::string(data));
1914	}
1915
1916	delete[] data;
1917
1918	if (!error) {
1919		// Clear the current lists as
1920		// we'll be manually building them up
1921		fRecentDocuments.Clear();
1922		fRecentFolders.Clear();
1923		fRecentApps.Clear();
1924
1925		// Now we just walk through the file and read in the info
1926		while (true) {
1927			status_t streamError;
1928			char str[B_PATH_NAME_LENGTH];
1929
1930			// (RecentDoc | RecentFolder | RecentApp)
1931			streamError = stream.GetString(str);
1932			if (!streamError) {
1933				enum EntryType {
1934					etDoc,
1935					etFolder,
1936					etApp,
1937					etSomethingIsAmiss,
1938				} type;
1939
1940				if (strcmp(str, "RecentDoc") == 0)
1941					type = etDoc;
1942				else if (strcmp(str, "RecentFolder") == 0)
1943					type = etFolder;
1944				else if (strcmp(str, "RecentApp") == 0)
1945					type = etApp;
1946				else
1947					type = etSomethingIsAmiss;
1948
1949				switch (type) {
1950					case etDoc:
1951					case etFolder:
1952					{
1953						// For curing laziness
1954						std::list<recent_entry*>* list = type == etDoc
1955							? &fRecentDocuments.fEntryList
1956							: &fRecentFolders.fEntryList;
1957
1958						char path[B_PATH_NAME_LENGTH];
1959						char app[B_PATH_NAME_LENGTH];
1960						char rank[B_PATH_NAME_LENGTH];
1961						entry_ref ref;
1962						ulong index = 0;
1963
1964						// Convert the given path to an entry ref
1965						streamError = stream.GetString(path);
1966						if (!streamError)
1967							streamError = get_ref_for_path(path, &ref);
1968
1969						// Add a new entry to the list for each application
1970						// signature and rank we find
1971						while (!streamError) {
1972							if (!streamError)
1973								streamError = stream.GetString(app);
1974							if (!streamError) {
1975								BPrivate::Storage::to_lower(app);
1976								streamError = stream.GetString(rank);
1977							}
1978							if (!streamError) {
1979								index = strtoul(rank, NULL, 10);
1980								if (index == ULONG_MAX)
1981									streamError = errno;
1982							}
1983							recent_entry* entry = NULL;
1984							if (!streamError) {
1985								entry = new(nothrow) recent_entry(&ref, app,
1986									index);
1987								streamError = entry ? B_OK : B_NO_MEMORY;
1988							}
1989							if (!streamError) {
1990								D(printf("pushing entry, leaf == '%s', app == "
1991									"'%s', index == %" B_PRId32 "\n",
1992									entry->ref.name, entry->sig.c_str(),
1993									entry->index));
1994
1995								list->push_back(entry);
1996							}
1997						}
1998
1999						if (streamError) {
2000							D(printf("entry error 0x%" B_PRIx32 "\n",
2001								streamError));
2002							if (streamError
2003									!= RosterSettingsCharStream::kEndOfLine
2004							    && streamError
2005							    	!= RosterSettingsCharStream::kEndOfStream)
2006							stream.SkipLine();
2007						}
2008
2009						break;
2010					}
2011
2012
2013					case etApp:
2014					{
2015						char app[B_PATH_NAME_LENGTH];
2016						streamError = stream.GetString(app);
2017						if (!streamError) {
2018							BPrivate::Storage::to_lower(app);
2019							fRecentApps.fAppList.push_back(app);
2020						} else
2021							stream.SkipLine();
2022						break;
2023					}
2024
2025					default:
2026						// Something was amiss; skip to the next line
2027						stream.SkipLine();
2028						break;
2029				}
2030
2031			}
2032
2033			if (streamError == RosterSettingsCharStream::kEndOfStream)
2034				break;
2035		}
2036
2037		// Now we must sort our lists of documents and folders by the
2038		// indicies we read for each entry (largest index first)
2039		fRecentDocuments.fEntryList.sort(larger_index);
2040		fRecentFolders.fEntryList.sort(larger_index);
2041
2042		D(
2043			printf("----------------------------------------------------------------------\n");
2044			fRecentDocuments.Print();
2045			printf("----------------------------------------------------------------------\n");
2046			fRecentFolders.Print();
2047			printf("----------------------------------------------------------------------\n");
2048			fRecentApps.Print();
2049			printf("----------------------------------------------------------------------\n");
2050		);
2051	}
2052	if (error) {
2053		D(PRINT("WARNING: TRoster::_LoadRosterSettings(): error loading roster "
2054			"settings from '%s', 0x%" B_PRIx32 "\n", settingsPath, error));
2055	}
2056	return error;
2057}
2058
2059
2060status_t
2061TRoster::_SaveRosterSettings(const char* path)
2062{
2063	BPath _path;
2064	const char* settingsPath
2065		= path != NULL ? path : get_default_roster_settings_path(_path, true);
2066
2067	status_t error;
2068	FILE* file;
2069
2070	file = fopen(settingsPath, "w+");
2071	error = file ? B_OK : errno;
2072	if (!error) {
2073		status_t saveError;
2074		saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc");
2075		if (saveError) {
2076			D(PRINT("TRoster::_SaveRosterSettings(): recent documents save "
2077				"failed with error 0x%" B_PRIx32 "\n", saveError));
2078		}
2079		saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder");
2080		if (saveError) {
2081			D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2082				"failed with error 0x%" B_PRIx32 "\n", saveError));
2083		}
2084		saveError = fRecentApps.Save(file);
2085		if (saveError) {
2086			D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2087				"failed with error 0x%" B_PRIx32 "\n", saveError));
2088		}
2089		fclose(file);
2090	}
2091
2092	return error;
2093}
2094