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
20#include <Application.h>
21#include <AutoDeleter.h>
22#include <Autolock.h>
23#include <Directory.h>
24#include <File.h>
25#include <FindDirectory.h>
26#include <Path.h>
27
28#include <AppMisc.h>
29#include <MessagePrivate.h>
30#include <MessengerPrivate.h>
31#include <RosterPrivate.h>
32#include <ServerProtocol.h>
33#include <storage_support.h>
34
35#include "AppInfoListMessagingTargetSet.h"
36#include "Debug.h"
37#include "EventMaskWatcher.h"
38#include "MessageDeliverer.h"
39#include "RegistrarDefs.h"
40#include "RosterAppInfo.h"
41#include "RosterSettingsCharStream.h"
42
43using std::nothrow;
44using namespace BPrivate;
45
46
47/*!	\class TRoster
48	\brief Implements the application roster.
49
50	This class handles the BRoster requests. For each kind a hook method is
51	implemented to which the registrar looper dispatches the request messages.
52
53	Registered and pre-registered are managed via AppInfoLists.
54	\a fEarlyPreRegisteredApps contains the infos for those application that
55	are pre-registered and currently have no team ID assigned to them yet,
56	whereas the infos of registered and pre-registered applications with a
57	team ID are to be found in \a fRegisteredApps.
58
59	When an application asks whether it is pre-registered or not and there
60	are one or more instances of the application that are pre-registered, but
61	have no team ID assigned yet, the reply to the request has to be
62	postponed until the status of the requesting team is clear. The request
63	message is dequeued from the registrar's message queue and added to
64	\a fIARRequestsByID for a later reply.
65
66	The field \a fActiveApp identifies the currently active application
67	and \a fLastToken is a counter used to generate unique tokens for
68	pre-registered applications.
69*/
70
71//! The maximal period of time an app may be early pre-registered (60 s).
72const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
73
74
75//	#pragma mark - Private local functions
76
77
78/*!	\brief Returns the path to the default roster settings.
79
80	\param path BPath to be set to the roster settings path.
81	\param createDirectory makes sure the target directory exists if \c true.
82
83	\return the settings path as C string (\code path.Path() \endcode).
84*/
85static const char*
86get_default_roster_settings_path(BPath& path, bool createDirectory)
87{
88	// get the path of the settings dir and append the subpath of our file
89	status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
90	if (error == B_OK)
91		error = path.Append("system/registrar");
92	if (error == B_OK && createDirectory)
93		error = create_directory(path.Path(), 0777);
94	if (error == B_OK)
95		error = path.Append("RosterSettings");
96
97	return path.Path();
98}
99
100
101/*! \brief Returns true if entry1's index is larger than entry2's index.
102
103	Also returns true if either entry is \c NULL.
104
105	Used for sorting the recent entry lists loaded from disk into the
106	proper order.
107*/
108bool
109larger_index(const recent_entry* entry1, const recent_entry* entry2)
110{
111	if (entry1 && entry2)
112		return entry1->index > entry2->index;
113
114	return true;
115}
116
117
118//	#pragma mark -
119
120
121/*!	\brief Creates a new roster.
122
123	The object is completely initialized and ready to handle requests.
124*/
125TRoster::TRoster()
126	:
127	fLock("roster"),
128	fRegisteredApps(),
129	fEarlyPreRegisteredApps(),
130	fIARRequestsByID(),
131	fIARRequestsByToken(),
132	fActiveApp(NULL),
133	fWatchingService(),
134	fRecentApps(),
135	fRecentDocuments(),
136	fRecentFolders(),
137	fLastToken(0),
138	fShuttingDown(false)
139{
140	find_directory(B_SYSTEM_DIRECTORY, &fSystemAppPath);
141	find_directory(B_SYSTEM_SERVERS_DIRECTORY, &fSystemServerPath);
142}
143
144
145/*!	\brief Frees all resources associated with this object.
146*/
147TRoster::~TRoster()
148{
149}
150
151
152/*!	\brief Handles an AddApplication() request.
153	\param request The request message
154*/
155void
156TRoster::HandleAddApplication(BMessage* request)
157{
158	FUNCTION_START();
159
160	BAutolock _(fLock);
161
162	status_t error = B_OK;
163	// get the parameters
164	const char* signature;
165	entry_ref ref;
166	uint32 flags;
167	team_id team;
168	thread_id thread;
169	port_id port;
170	bool fullReg;
171	if (request->FindString("signature", &signature) != B_OK)
172		signature = NULL;
173	if (request->FindRef("ref", &ref) != B_OK)
174		SET_ERROR(error, B_BAD_VALUE);
175	if (request->FindInt32("flags", (int32*)&flags) != B_OK)
176		flags = B_REG_DEFAULT_APP_FLAGS;
177	if (request->FindInt32("team", &team) != B_OK)
178		team = -1;
179	if (request->FindInt32("thread", &thread) != B_OK)
180		thread = -1;
181	if (request->FindInt32("port", &port) != B_OK)
182		port = -1;
183	if (request->FindBool("full_registration", &fullReg) != B_OK)
184		fullReg = false;
185
186	PRINT("team: %ld, signature: %s\n", team, signature);
187	PRINT("full registration: %d\n", fullReg);
188
189	if (fShuttingDown)
190		error = B_SHUTTING_DOWN;
191
192	// check the parameters
193	team_id otherTeam = -1;
194	uint32 token = 0;
195
196	uint32 launchFlags = flags & B_LAUNCH_MASK;
197	BEntry entry(&ref);
198	if (!entry.Exists())
199		SET_ERROR(error, B_ENTRY_NOT_FOUND);
200
201	if (error == B_OK)
202		_ValidateRunning(ref, signature);
203
204	// entry_ref
205	if (error == B_OK) {
206		PRINT("flags: %lx\n", flags);
207		PRINT("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name);
208		// check single/exclusive launchers
209		RosterAppInfo* info = NULL;
210		if ((launchFlags == B_SINGLE_LAUNCH
211			 || launchFlags ==  B_EXCLUSIVE_LAUNCH)
212			&& ((info = fRegisteredApps.InfoFor(&ref)) != NULL
213				|| (info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL)) {
214			SET_ERROR(error, B_ALREADY_RUNNING);
215			otherTeam = info->team;
216			token = info->token;
217		}
218	}
219
220	// signature
221	if (error == B_OK && signature) {
222		// check exclusive launchers
223		RosterAppInfo* info = NULL;
224		if (launchFlags == B_EXCLUSIVE_LAUNCH
225			&& (((info = fRegisteredApps.InfoFor(signature)))
226				|| ((info = fEarlyPreRegisteredApps.InfoFor(signature))))) {
227			SET_ERROR(error, B_ALREADY_RUNNING);
228			otherTeam = info->team;
229			token = info->token;
230		}
231	}
232
233	// If no team ID is given, full registration isn't possible.
234	if (error == B_OK) {
235		if (team < 0) {
236			if (fullReg)
237				SET_ERROR(error, B_BAD_VALUE);
238		} else if (fRegisteredApps.InfoFor(team))
239			SET_ERROR(error, B_REG_ALREADY_REGISTERED);
240	}
241
242	// Add the application info.
243	if (error == B_OK) {
244		// alloc and init the info
245		RosterAppInfo* info = new(nothrow) RosterAppInfo;
246		if (info) {
247			info->Init(thread, team, port, flags, &ref, signature);
248			if (fullReg)
249				info->state = APP_STATE_REGISTERED;
250			else
251				info->state = APP_STATE_PRE_REGISTERED;
252			info->registration_time = system_time();
253			// add it to the right list
254			bool addingSuccess = false;
255			if (team >= 0) {
256				PRINT("added ref: %ld, %lld, %s\n", info->ref.device,
257					info->ref.directory, info->ref.name);
258				addingSuccess = (AddApp(info) == B_OK);
259				if (addingSuccess && fullReg)
260					_AppAdded(info);
261			} else {
262				token = info->token = _NextToken();
263				addingSuccess = fEarlyPreRegisteredApps.AddInfo(info);
264				PRINT("added to early pre-regs, token: %lu\n", token);
265			}
266			if (!addingSuccess)
267				SET_ERROR(error, B_NO_MEMORY);
268		} else
269			SET_ERROR(error, B_NO_MEMORY);
270		// delete the info on failure
271		if (error != B_OK && info)
272			delete info;
273	}
274
275	// reply to the request
276	if (error == B_OK) {
277		// add to recent apps if successful
278		if (signature && signature[0] != '\0')
279			fRecentApps.Add(signature, flags);
280		else
281			fRecentApps.Add(&ref, flags);
282
283		BMessage reply(B_REG_SUCCESS);
284		// The token is valid only when no team ID has been supplied.
285		if (team < 0)
286			reply.AddInt32("token", (int32)token);
287		request->SendReply(&reply);
288	} else {
289		BMessage reply(B_REG_ERROR);
290		reply.AddInt32("error", error);
291		if (otherTeam >= 0)
292			reply.AddInt32("other_team", otherTeam);
293		if (token > 0)
294			reply.AddInt32("token", (int32)token);
295		request->SendReply(&reply);
296	}
297
298	FUNCTION_END();
299}
300
301
302/*!	\brief Handles a CompleteRegistration() request.
303	\param request The request message
304*/
305void
306TRoster::HandleCompleteRegistration(BMessage* request)
307{
308	FUNCTION_START();
309
310	BAutolock _(fLock);
311
312	status_t error = B_OK;
313	// get the parameters
314	team_id team;
315	thread_id thread;
316	port_id port;
317	if (request->FindInt32("team", &team) != B_OK)
318		team = -1;
319	if (request->FindInt32("thread", &thread) != B_OK)
320		thread = -1;
321	if (request->FindInt32("port", &port) != B_OK)
322		port = -1;
323
324	if (fShuttingDown)
325		error = B_SHUTTING_DOWN;
326
327	// check the parameters
328	// port
329	if (error == B_OK && port < 0)
330		SET_ERROR(error, B_BAD_VALUE);
331
332	// thread
333	if (error == B_OK && thread < 0)
334		SET_ERROR(error, B_BAD_VALUE);
335
336	// team
337	if (error == B_OK) {
338		if (team >= 0) {
339			// everything is fine -- set the values
340			RosterAppInfo* info = fRegisteredApps.InfoFor(team);
341			if (info && info->state == APP_STATE_PRE_REGISTERED) {
342				info->thread = thread;
343				info->port = port;
344				info->state = APP_STATE_REGISTERED;
345				_AppAdded(info);
346			} else
347				SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
348		} else
349			SET_ERROR(error, B_BAD_VALUE);
350	}
351
352	// reply to the request
353	if (error == B_OK) {
354		BMessage reply(B_REG_SUCCESS);
355		request->SendReply(&reply);
356	} else {
357		BMessage reply(B_REG_ERROR);
358		reply.AddInt32("error", error);
359		request->SendReply(&reply);
360	}
361
362	FUNCTION_END();
363}
364
365
366/*!	\brief Handles an IsAppRegistered() request.
367	\param request The request message
368*/
369void
370TRoster::HandleIsAppRegistered(BMessage* request)
371{
372	FUNCTION_START();
373
374	BAutolock _(fLock);
375
376	status_t error = B_OK;
377	// get the parameters
378	entry_ref ref;
379	team_id team;
380	uint32 token;
381	if (request->FindRef("ref", &ref) != B_OK)
382		SET_ERROR(error, B_BAD_VALUE);
383	if (request->FindInt32("team", &team) != B_OK)
384		team = -1;
385	if (request->FindInt32("token", (int32*)&token) != B_OK)
386		token = 0;
387
388	PRINT("team: %ld, token: %lu\n", team, token);
389	PRINT("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name);
390
391	// check the parameters
392	// entry_ref
393	if (error == B_OK && !BEntry(&ref).Exists())
394		SET_ERROR(error, B_ENTRY_NOT_FOUND);
395	// team/token
396	if (error == B_OK && team < 0 && token == 0)
397		SET_ERROR(error, B_BAD_VALUE);
398
399	// look up the information
400	RosterAppInfo* info = NULL;
401	if (error == B_OK) {
402		if ((info = fRegisteredApps.InfoFor(team)) != NULL) {
403			PRINT("found team in fRegisteredApps\n");
404			_ReplyToIARRequest(request, info);
405		} else if (token > 0
406			&& (info = fEarlyPreRegisteredApps.InfoForToken(token)) != NULL) {
407			PRINT("found ref in fEarlyRegisteredApps (by token)\n");
408			// pre-registered and has no team ID assigned yet -- queue the
409			// request
410			be_app->DetachCurrentMessage();
411			_AddIARRequest(fIARRequestsByToken, token, request);
412		} else if (team >= 0
413			&& (info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL) {
414			PRINT("found ref in fEarlyRegisteredApps (by ref)\n");
415			// pre-registered and has no team ID assigned yet -- queue the
416			// request
417			be_app->DetachCurrentMessage();
418			_AddIARRequest(fIARRequestsByID, team, request);
419		} else {
420			PRINT("didn't find team or ref\n");
421			// team not registered, ref/token not early pre-registered
422			_ReplyToIARRequest(request, NULL);
423		}
424	} else {
425		// reply to the request on error
426		BMessage reply(B_REG_ERROR);
427		reply.AddInt32("error", error);
428		request->SendReply(&reply);
429	}
430
431	FUNCTION_END();
432}
433
434
435/*!	\brief Handles a RemovePreRegApp() request.
436	\param request The request message
437*/
438void
439TRoster::HandleRemovePreRegApp(BMessage* request)
440{
441	FUNCTION_START();
442
443	BAutolock _(fLock);
444
445	status_t error = B_OK;
446	// get the parameters
447	uint32 token;
448	if (request->FindInt32("token", (int32*)&token) != B_OK)
449		SET_ERROR(error, B_BAD_VALUE);
450	// remove the app
451	if (error == B_OK) {
452		RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
453		if (info) {
454			fEarlyPreRegisteredApps.RemoveInfo(info);
455			delete info;
456		} else
457			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
458	}
459	// reply to the request
460	if (error == B_OK) {
461		BMessage reply(B_REG_SUCCESS);
462		request->SendReply(&reply);
463	} else {
464		BMessage reply(B_REG_ERROR);
465		reply.AddInt32("error", error);
466		request->SendReply(&reply);
467	}
468
469	FUNCTION_END();
470}
471
472
473/*!	\brief Handles a RemoveApp() request.
474	\param request The request message
475*/
476void
477TRoster::HandleRemoveApp(BMessage* request)
478{
479	FUNCTION_START();
480
481	BAutolock _(fLock);
482
483	status_t error = B_OK;
484	// get the parameters
485	team_id team;
486	if (request->FindInt32("team", &team) != B_OK)
487		team = -1;
488
489	PRINT("team: %ld\n", team);
490
491	// remove the app
492	if (error == B_OK) {
493		if (RosterAppInfo* info = fRegisteredApps.InfoFor(team)) {
494			RemoveApp(info);
495			delete info;
496		} else
497			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
498	}
499	// reply to the request
500	if (error == B_OK) {
501		BMessage reply(B_REG_SUCCESS);
502		request->SendReply(&reply);
503	} else {
504		BMessage reply(B_REG_ERROR);
505		reply.AddInt32("error", error);
506		request->SendReply(&reply);
507	}
508
509	FUNCTION_END();
510}
511
512
513/*!	\brief Handles a SetThreadAndTeam() request.
514	\param request The request message
515*/
516void
517TRoster::HandleSetThreadAndTeam(BMessage* request)
518{
519	FUNCTION_START();
520
521	BAutolock _(fLock);
522
523	status_t error = B_OK;
524
525	// get the parameters
526	team_id team;
527	thread_id thread;
528	uint32 token;
529	if (request->FindInt32("team", &team) != B_OK)
530		team = -1;
531	if (request->FindInt32("thread", &thread) != B_OK)
532		thread = -1;
533	if (request->FindInt32("token", (int32*)&token) != B_OK)
534		SET_ERROR(error, B_BAD_VALUE);
535
536	// check the parameters
537	// team
538	if (error == B_OK && team < 0)
539		SET_ERROR(error, B_BAD_VALUE);
540
541	PRINT("team: %ld, thread: %ld, token: %lu\n", team, thread, token);
542
543	// update the app_info
544	if (error == B_OK) {
545		RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
546		if (info) {
547			// Set thread and team, create a port for the application and
548			// move the app_info from the list of the early pre-registered
549			// apps to the list of the (pre-)registered apps.
550			fEarlyPreRegisteredApps.RemoveInfo(info);
551			info->team = team;
552			info->thread = thread;
553			// create and transfer the port
554			info->port = create_port(B_REG_APP_LOOPER_PORT_CAPACITY,
555									 kRAppLooperPortName);
556			if (info->port < 0)
557				SET_ERROR(error, info->port);
558			if (error == B_OK)
559				SET_ERROR(error, set_port_owner(info->port, team));
560			// add the info to the registered apps list
561			if (error == B_OK)
562				SET_ERROR(error, AddApp(info));
563			// cleanup on failure
564			if (error != B_OK) {
565				if (info->port >= 0)
566					delete_port(info->port);
567				delete info;
568				info = NULL;
569			}
570			// handle pending IsAppRegistered() requests
571			IARRequestMap::iterator it = fIARRequestsByID.find(team);
572			if (it != fIARRequestsByID.end()) {
573				BMessageQueue* requests = it->second;
574				if (error == B_OK)
575					_ReplyToIARRequests(requests, info);
576				delete requests;
577				fIARRequestsByID.erase(it);
578			}
579
580			it = fIARRequestsByToken.find((int32)token);
581			if (it != fIARRequestsByToken.end()) {
582				BMessageQueue* requests = it->second;
583				if (error == B_OK)
584					_ReplyToIARRequests(requests, info);
585				delete requests;
586				fIARRequestsByToken.erase(it);
587			}
588		} else
589			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
590	}
591	// reply to the request
592	if (error == B_OK) {
593		BMessage reply(B_REG_SUCCESS);
594		request->SendReply(&reply);
595	} else {
596		BMessage reply(B_REG_ERROR);
597		reply.AddInt32("error", error);
598		request->SendReply(&reply);
599	}
600
601	FUNCTION_END();
602}
603
604
605/*!	\brief Handles a SetSignature() request.
606	\param request The request message
607*/
608void
609TRoster::HandleSetSignature(BMessage* request)
610{
611	FUNCTION_START();
612
613	BAutolock _(fLock);
614
615	status_t error = B_OK;
616	// get the parameters
617	team_id team;
618	const char* signature;
619	if (request->FindInt32("team", &team) != B_OK)
620		error = B_BAD_VALUE;
621	if (request->FindString("signature", &signature) != B_OK)
622		error = B_BAD_VALUE;
623	// find the app and set the signature
624	if (error == B_OK) {
625		if (RosterAppInfo* info = fRegisteredApps.InfoFor(team))
626			strcpy(info->signature, signature);
627		else
628			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
629	}
630	// reply to the request
631	if (error == B_OK) {
632		BMessage reply(B_REG_SUCCESS);
633		request->SendReply(&reply);
634	} else {
635		BMessage reply(B_REG_ERROR);
636		reply.AddInt32("error", error);
637		request->SendReply(&reply);
638	}
639
640	FUNCTION_END();
641}
642
643
644/*!	\brief Handles a Get{Running,Active,}AppInfo() request.
645	\param request The request message
646*/
647void
648TRoster::HandleGetAppInfo(BMessage* request)
649{
650	FUNCTION_START();
651
652	BAutolock _(fLock);
653
654	status_t error = B_OK;
655	// get the parameters
656	team_id team;
657	entry_ref ref;
658	const char* signature;
659	bool hasTeam = true;
660	bool hasRef = true;
661	bool hasSignature = true;
662	if (request->FindInt32("team", &team) != B_OK)
663		hasTeam = false;
664	if (request->FindRef("ref", &ref) != B_OK)
665		hasRef = false;
666	if (request->FindString("signature", &signature) != B_OK)
667		hasSignature = false;
668
669if (hasTeam)
670PRINT("team: %ld\n", team);
671if (hasRef)
672PRINT("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name);
673if (hasSignature)
674PRINT("signature: %s\n", signature);
675
676	// get the info
677	RosterAppInfo* info = NULL;
678	if (error == B_OK) {
679		if (hasTeam) {
680			info = fRegisteredApps.InfoFor(team);
681			if (info == NULL)
682				SET_ERROR(error, B_BAD_TEAM_ID);
683		} else if (hasRef) {
684			info = fRegisteredApps.InfoFor(&ref);
685			if (info == NULL)
686				SET_ERROR(error, B_ERROR);
687		} else if (hasSignature) {
688			info = fRegisteredApps.InfoFor(signature);
689			if (info == NULL)
690				SET_ERROR(error, B_ERROR);
691		} else {
692			// If neither of those has been supplied, the active application
693			// info is requested.
694			if (fActiveApp)
695				info = fActiveApp;
696			else
697				SET_ERROR(error, B_ERROR);
698		}
699	}
700	// reply to the request
701	if (error == B_OK) {
702		BMessage reply(B_REG_SUCCESS);
703		_AddMessageAppInfo(&reply, info);
704		request->SendReply(&reply);
705	} else {
706		BMessage reply(B_REG_ERROR);
707		reply.AddInt32("error", error);
708		request->SendReply(&reply);
709	}
710
711	FUNCTION_END();
712}
713
714
715/*!	\brief Handles a GetAppList() request.
716	\param request The request message
717*/
718void
719TRoster::HandleGetAppList(BMessage* request)
720{
721	FUNCTION_START();
722
723	BAutolock _(fLock);
724
725	status_t error = B_OK;
726	// get the parameters
727	const char* signature;
728	if (request->FindString("signature", &signature) != B_OK)
729		signature = NULL;
730	// reply to the request
731	if (error == B_OK) {
732		BMessage reply(B_REG_SUCCESS);
733		// get the list
734		for (AppInfoList::Iterator it(fRegisteredApps.It());
735			 RosterAppInfo* info = *it;
736			 ++it) {
737			if (info->state != APP_STATE_REGISTERED)
738				continue;
739			if (!signature || !strcasecmp(signature, info->signature))
740				reply.AddInt32("teams", info->team);
741		}
742		request->SendReply(&reply);
743	} else {
744		BMessage reply(B_REG_ERROR);
745		reply.AddInt32("error", error);
746		request->SendReply(&reply);
747	}
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.Sem() < 0)
1230		return fLock.Sem();
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, kRegistrarSignature);
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, hash_set<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.insert(be_app->Team());
1421
1422	// kernel team
1423	team_info teamInfo;
1424	if (get_team_info(B_SYSTEM_TEAM, &teamInfo) == B_OK)
1425		vitalSystemApps.insert(teamInfo.team);
1426
1427	// app server
1428	port_id appServerPort = find_port(SERVER_PORT_NAME);
1429	port_info portInfo;
1430	if (appServerPort >= 0
1431		&& get_port_info(appServerPort, &portInfo) == B_OK) {
1432		vitalSystemApps.insert(portInfo.team);
1433	}
1434
1435	// debug server
1436	RosterAppInfo* info =
1437		fRegisteredApps.InfoFor("application/x-vnd.haiku-debug_server");
1438	if (info)
1439		vitalSystemApps.insert(info->team);
1440
1441	// populate the other groups
1442	for (AppInfoList::Iterator it(fRegisteredApps.It());
1443		 RosterAppInfo* info = *it;
1444		 ++it) {
1445		if (vitalSystemApps.find(info->team) == vitalSystemApps.end()) {
1446			RosterAppInfo* clonedInfo = info->Clone();
1447			if (clonedInfo) {
1448				if (_IsSystemApp(info)) {
1449					if (!systemApps.AddInfo(clonedInfo))
1450						error = B_NO_MEMORY;
1451				} else if (info->flags & B_BACKGROUND_APP) {
1452					if (!backgroundApps.AddInfo(clonedInfo))
1453						error = B_NO_MEMORY;
1454				} else {
1455					if (!userApps.AddInfo(clonedInfo))
1456						error = B_NO_MEMORY;
1457				}
1458
1459				if (error != B_OK)
1460					delete clonedInfo;
1461			} else
1462				error = B_NO_MEMORY;
1463		}
1464
1465		if (error != B_OK)
1466			break;
1467	}
1468
1469	// Special case, we add the input server to vital apps here so it is
1470	// not excluded in the lists above
1471	info = fRegisteredApps.InfoFor("application/x-vnd.Be-input_server");
1472 	if (info)
1473 		vitalSystemApps.insert(info->team);
1474
1475	// clean up on error
1476	if (error != B_OK) {
1477		userApps.MakeEmpty(true);
1478		systemApps.MakeEmpty(true);
1479	}
1480
1481	return error;
1482}
1483
1484
1485status_t
1486TRoster::AddWatcher(Watcher* watcher)
1487{
1488	BAutolock _(fLock);
1489
1490	if (!watcher)
1491		return B_BAD_VALUE;
1492
1493	if (!fWatchingService.AddWatcher(watcher))
1494		return B_NO_MEMORY;
1495
1496	return B_OK;
1497}
1498
1499
1500void
1501TRoster::RemoveWatcher(Watcher* watcher)
1502{
1503	BAutolock _(fLock);
1504
1505	if (watcher)
1506		fWatchingService.RemoveWatcher(watcher, false);
1507}
1508
1509
1510/*!	\brief Hook method invoked, when an application has been fully registered.
1511	\param info The RosterAppInfo of the added application.
1512*/
1513void
1514TRoster::_AppAdded(RosterAppInfo* info)
1515{
1516	// notify the watchers
1517	BMessage message(B_SOME_APP_LAUNCHED);
1518	_AddMessageWatchingInfo(&message, info);
1519	EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED);
1520	fWatchingService.NotifyWatchers(&message, &filter);
1521}
1522
1523
1524/*!	\brief Hook method invoked, when a fully registered application has been
1525		removed.
1526	\param info The RosterAppInfo of the removed application.
1527*/
1528void
1529TRoster::_AppRemoved(RosterAppInfo* info)
1530{
1531	if (info) {
1532		// deactivate the app, if it was the active one
1533		if (info == fActiveApp)
1534			UpdateActiveApp(NULL);
1535
1536		// notify the watchers
1537		BMessage message(B_SOME_APP_QUIT);
1538		_AddMessageWatchingInfo(&message, info);
1539		EventMaskWatcherFilter filter(B_REQUEST_QUIT);
1540		fWatchingService.NotifyWatchers(&message, &filter);
1541	}
1542}
1543
1544
1545/*!	\brief Hook method invoked, when an application has been activated.
1546	\param info The RosterAppInfo of the activated application.
1547*/
1548void
1549TRoster::_AppActivated(RosterAppInfo* info)
1550{
1551	if (info != NULL && info->state == APP_STATE_REGISTERED) {
1552		// send B_APP_ACTIVATED to the app
1553		BMessenger messenger;
1554		BMessenger::Private messengerPrivate(messenger);
1555		messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
1556		BMessage message(B_APP_ACTIVATED);
1557		message.AddBool("active", true);
1558		// not sure, if it makes sense to use the MessageDeliverer here
1559		MessageDeliverer::Default()->DeliverMessage(&message, messenger);
1560
1561		// notify the watchers
1562		BMessage watcherMessage(B_SOME_APP_ACTIVATED);
1563		_AddMessageWatchingInfo(&watcherMessage, info);
1564		EventMaskWatcherFilter filter(B_REQUEST_ACTIVATED);
1565		fWatchingService.NotifyWatchers(&watcherMessage, &filter);
1566	}
1567}
1568
1569
1570/*!	\brief Hook method invoked, when an application has been deactivated.
1571	\param info The RosterAppInfo of the deactivated application.
1572*/
1573void
1574TRoster::_AppDeactivated(RosterAppInfo* info)
1575{
1576	if (info != NULL && info->state == APP_STATE_REGISTERED) {
1577		// send B_APP_ACTIVATED to the app
1578		BMessenger messenger;
1579		BMessenger::Private messengerPrivate(messenger);
1580		messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
1581		BMessage message(B_APP_ACTIVATED);
1582		message.AddBool("active", false);
1583		// not sure, if it makes sense to use the MessageDeliverer here
1584		MessageDeliverer::Default()->DeliverMessage(&message, messenger);
1585	}
1586}
1587
1588
1589/*!	\brief Adds an app_info to a message.
1590
1591	The info is added as a flat_app_info to a field "app_info" with the type
1592	\c B_REG_APP_INFO_TYPE.
1593
1594	\param message The message
1595	\param info The app_info.
1596	\return \c B_OK if everything went fine, an error code otherwise.
1597*/
1598status_t
1599TRoster::_AddMessageAppInfo(BMessage* message, const app_info* info)
1600{
1601	// An app_info is not completely flat. The entry_ref contains a string
1602	// pointer. Therefore we flatten the info.
1603	flat_app_info flatInfo;
1604	flatInfo.info = *info;
1605
1606	// set the ref name to NULL and copy it into the flat structure
1607	flatInfo.info.ref.name = NULL;
1608	flatInfo.ref_name[0] = '\0';
1609	if (info->ref.name)
1610		strcpy(flatInfo.ref_name, info->ref.name);
1611
1612	// add the flat info
1613	return message->AddData("app_info", B_REG_APP_INFO_TYPE, &flatInfo,
1614		sizeof(flat_app_info));
1615}
1616
1617
1618/*!	\brief Adds application monitoring related fields to a message.
1619	\param message The message.
1620	\param info The app_info of the concerned application.
1621	\return \c B_OK if everything went fine, an error code otherwise.
1622*/
1623status_t
1624TRoster::_AddMessageWatchingInfo(BMessage* message, const app_info* info)
1625{
1626	status_t error = B_OK;
1627	if (error == B_OK)
1628		error = message->AddString("be:signature", info->signature);
1629	if (error == B_OK)
1630		error = message->AddInt32("be:team", info->team);
1631	if (error == B_OK)
1632		error = message->AddInt32("be:thread", info->thread);
1633	if (error == B_OK)
1634		error = message->AddInt32("be:flags", (int32)info->flags);
1635	if (error == B_OK)
1636		error = message->AddRef("be:ref", &info->ref);
1637	return error;
1638}
1639
1640
1641/*!	\brief Returns the next available token.
1642	\return The token.
1643*/
1644uint32
1645TRoster::_NextToken()
1646{
1647	return ++fLastToken;
1648}
1649
1650
1651/*!	\brief Adds an IsAppRegistered() request to the given map.
1652
1653	If something goes wrong, the method deletes the request.
1654
1655	\param map The map the request shall be added to.
1656	\param key The key under which to add the request.
1657	\param request The request message to be added.
1658*/
1659void
1660TRoster::_AddIARRequest(IARRequestMap& map, int32 key, BMessage* request)
1661{
1662	IARRequestMap::iterator it = map.find(key);
1663	BMessageQueue* requests = NULL;
1664	if (it == map.end()) {
1665		requests = new(nothrow) BMessageQueue();
1666		if (!requests) {
1667			delete request;
1668			return;
1669		}
1670
1671		map[key] = requests;
1672	} else
1673		requests = it->second;
1674
1675	requests->AddMessage(request);
1676}
1677
1678
1679/*!	\brief Invokes _ReplyToIARRequest() for all messages in the given
1680		   message queue.
1681
1682	\param requests The request messages to be replied to
1683	\param info The RosterAppInfo of the application in question
1684		   (may be \c NULL)
1685*/
1686void
1687TRoster::_ReplyToIARRequests(BMessageQueue* requests, const RosterAppInfo* info)
1688{
1689	while (BMessage* request = requests->NextMessage()) {
1690		_ReplyToIARRequest(request, info);
1691		delete request;
1692	}
1693}
1694
1695
1696/*!	\brief Sends a reply message to an IsAppRegistered() request.
1697
1698	The message to be sent is a simple \c B_REG_SUCCESS message containing
1699	a "pre-registered" field, that says whether or not the application is
1700	pre-registered. It will be set to \c false, unless an \a info is supplied
1701	and the application this info refers to is pre-registered.
1702
1703	\param request The request message to be replied to
1704	\param info The RosterAppInfo of the application in question
1705		   (may be \c NULL)
1706*/
1707void
1708TRoster::_ReplyToIARRequest(BMessage* request, const RosterAppInfo* info)
1709{
1710	// pre-registered or registered?
1711	bool preRegistered = false;
1712	if (info) {
1713		switch (info->state) {
1714			case APP_STATE_PRE_REGISTERED:
1715				preRegistered = true;
1716				break;
1717			case APP_STATE_UNREGISTERED:
1718			case APP_STATE_REGISTERED:
1719				preRegistered = false;
1720				break;
1721		}
1722	}
1723	// send reply
1724	BMessage reply(B_REG_SUCCESS);
1725	reply.AddBool("registered", (bool)info);
1726	reply.AddBool("pre-registered", preRegistered);
1727	PRINT("_ReplyToIARRequest(): pre-registered: %d\n", preRegistered);
1728	if (info)
1729		_AddMessageAppInfo(&reply, info);
1730	request->SendReply(&reply);
1731}
1732
1733
1734/*! \brief Handles requests for both GetRecentDocuments() and
1735	GetRecentFolders().
1736*/
1737void
1738TRoster::_HandleGetRecentEntries(BMessage* request)
1739{
1740	FUNCTION_START();
1741	if (!request) {
1742		D(PRINT("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n"));
1743		return;
1744	}
1745
1746	int32 maxCount;
1747	BMessage reply(B_REG_RESULT);
1748	char** fileTypes = NULL;
1749	int32 fileTypesCount = 0;
1750	char* appSig = NULL;
1751
1752	status_t error = request->FindInt32("max count", &maxCount);
1753	// Look for optional file type(s)
1754	if (!error) {
1755		type_code typeFound;
1756		status_t typeError = request->GetInfo("file type", &typeFound,
1757			&fileTypesCount);
1758		if (!typeError)
1759			typeError = typeFound == B_STRING_TYPE ? B_OK : B_BAD_TYPE;
1760		if (!typeError) {
1761			fileTypes = new(nothrow) char*[fileTypesCount];
1762			typeError = fileTypes ? B_OK : B_NO_MEMORY;
1763		}
1764		if (!typeError) {
1765			for (int i = 0; !error && i < fileTypesCount; i++) {
1766				const char* type;
1767				if (request->FindString("file type", i, &type) == B_OK) {
1768					fileTypes[i] = new(nothrow) char[B_MIME_TYPE_LENGTH];
1769					error = fileTypes[i] ? B_OK : B_NO_MEMORY;
1770						// Yes, I do mean to use "error" here, not "typeError"
1771					BPrivate::Storage::to_lower(type, fileTypes[i]);
1772						// Types are expected to be lowercase
1773				}
1774			}
1775		}
1776	}
1777	// Look for optional app sig
1778	if (!error) {
1779		const char* sig;
1780		error = request->FindString("app sig", &sig);
1781		if (!error) {
1782			appSig = new(nothrow) char[B_MIME_TYPE_LENGTH];
1783			error = appSig ? B_OK : B_NO_MEMORY;
1784			BPrivate::Storage::to_lower(sig, appSig);
1785		} else if (error == B_NAME_NOT_FOUND)
1786			error = B_OK;
1787	}
1788	if (!error) {
1789		switch (request->what) {
1790			case B_REG_GET_RECENT_DOCUMENTS:
1791				error = fRecentDocuments.Get(maxCount, (const char**)fileTypes,
1792					fileTypesCount, appSig, &reply);
1793				D(fRecentDocuments.Print());
1794		   	    break;
1795
1796			case B_REG_GET_RECENT_FOLDERS:
1797				error = fRecentFolders.Get(maxCount, (const char**)fileTypes,
1798					fileTypesCount, appSig, &reply);
1799				D(fRecentFolders.Print());
1800			    break;
1801
1802			default:
1803				D(PRINT("WARNING: TRoster::_HandleGetRecentEntries(): "
1804					"unexpected request->what value of 0x%lx\n",
1805					request->what));
1806				error = B_BAD_VALUE;
1807				break;
1808		}
1809	}
1810	reply.AddInt32("result", error);
1811	// Clean up before sending a reply
1812	delete [] appSig;
1813	if (fileTypes) {
1814		for (int i = 0; i < fileTypesCount; i++)
1815			delete [] fileTypes[i];
1816		delete[] fileTypes;
1817		fileTypes = NULL;
1818	}
1819	request->SendReply(&reply);
1820
1821	FUNCTION_END();
1822}
1823
1824
1825/*!
1826	\brief Checks all registered apps for \a ref and \a signature if
1827		they are still alive, and removes those that aren't.
1828*/
1829void
1830TRoster::_ValidateRunning(const entry_ref& ref, const char* signature)
1831{
1832	while (true) {
1833		// get info via ref or signature
1834		RosterAppInfo* info = fRegisteredApps.InfoFor(&ref);
1835		if (info == NULL && signature != NULL)
1836			info = fRegisteredApps.InfoFor(signature);
1837
1838		// if app is alive or does not exist, we can exit
1839		if (info == NULL || info->IsRunning())
1840			return;
1841
1842		RemoveApp(info);
1843		delete info;
1844	}
1845}
1846
1847
1848bool
1849TRoster::_IsSystemApp(RosterAppInfo* info) const
1850{
1851	BPath path;
1852	if (path.SetTo(&info->ref) != B_OK || path.GetParent(&path) != B_OK)
1853		return false;
1854
1855	return !strcmp(path.Path(), fSystemAppPath.Path())
1856		|| !strcmp(path.Path(), fSystemServerPath.Path());
1857}
1858
1859
1860status_t
1861TRoster::_LoadRosterSettings(const char* path)
1862{
1863	BPath _path;
1864	const char* settingsPath
1865		= path ? path : get_default_roster_settings_path(_path, false);
1866
1867	RosterSettingsCharStream stream;
1868	status_t error;
1869	BFile file;
1870
1871	error = file.SetTo(settingsPath, B_READ_ONLY);
1872	off_t size;
1873	if (!error)
1874		error = file.GetSize(&size);
1875
1876	char* data = NULL;
1877
1878	if (!error) {
1879		data = new(nothrow) char[size + 1];
1880		error = data ? B_OK : B_NO_MEMORY;
1881	}
1882	if (!error) {
1883		ssize_t bytes = file.Read(data, size);
1884		error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR);
1885	}
1886	if (!error) {
1887		data[size] = 0;
1888		error = stream.SetTo(std::string(data));
1889	}
1890
1891	delete[] data;
1892
1893	if (!error) {
1894		// Clear the current lists as
1895		// we'll be manually building them up
1896		fRecentDocuments.Clear();
1897		fRecentFolders.Clear();
1898		fRecentApps.Clear();
1899
1900		// Now we just walk through the file and read in the info
1901		while (true) {
1902			status_t streamError;
1903			char str[B_PATH_NAME_LENGTH];
1904
1905			// (RecentDoc | RecentFolder | RecentApp)
1906			streamError = stream.GetString(str);
1907			if (!streamError) {
1908				enum EntryType {
1909					etDoc,
1910					etFolder,
1911					etApp,
1912					etSomethingIsAmiss,
1913				} type;
1914
1915				if (strcmp(str, "RecentDoc") == 0)
1916					type = etDoc;
1917				else if (strcmp(str, "RecentFolder") == 0)
1918					type = etFolder;
1919				else if (strcmp(str, "RecentApp") == 0)
1920					type = etApp;
1921				else
1922					type = etSomethingIsAmiss;
1923
1924				switch (type) {
1925					case etDoc:
1926					case etFolder:
1927					{
1928						// For curing laziness
1929						std::list<recent_entry*>* list = type == etDoc
1930							? &fRecentDocuments.fEntryList
1931							: &fRecentFolders.fEntryList;
1932
1933						char path[B_PATH_NAME_LENGTH];
1934						char app[B_PATH_NAME_LENGTH];
1935						char rank[B_PATH_NAME_LENGTH];
1936						entry_ref ref;
1937						ulong index = 0;
1938
1939						// Convert the given path to an entry ref
1940						streamError = stream.GetString(path);
1941						if (!streamError)
1942							streamError = get_ref_for_path(path, &ref);
1943
1944						// Add a new entry to the list for each application
1945						// signature and rank we find
1946						while (!streamError) {
1947							if (!streamError)
1948								streamError = stream.GetString(app);
1949							if (!streamError) {
1950								BPrivate::Storage::to_lower(app);
1951								streamError = stream.GetString(rank);
1952							}
1953							if (!streamError) {
1954								index = strtoul(rank, NULL, 10);
1955								if (index == ULONG_MAX)
1956									streamError = errno;
1957							}
1958							recent_entry* entry = NULL;
1959							if (!streamError) {
1960								entry = new(nothrow) recent_entry(&ref, app,
1961									index);
1962								streamError = entry ? B_OK : B_NO_MEMORY;
1963							}
1964							if (!streamError) {
1965								D(printf("pushing entry, leaf == '%s', app == "
1966									"'%s', index == %ld\n", entry->ref.name,
1967									entry->sig.c_str(), entry->index));
1968
1969								list->push_back(entry);
1970							}
1971						}
1972
1973						if (streamError) {
1974							D(printf("entry error 0x%lx\n", streamError));
1975							if (streamError
1976									!= RosterSettingsCharStream::kEndOfLine
1977							    && streamError
1978							    	!= RosterSettingsCharStream::kEndOfStream)
1979							stream.SkipLine();
1980						}
1981
1982						break;
1983					}
1984
1985
1986					case etApp:
1987					{
1988						char app[B_PATH_NAME_LENGTH];
1989						streamError = stream.GetString(app);
1990						if (!streamError) {
1991							BPrivate::Storage::to_lower(app);
1992							fRecentApps.fAppList.push_back(app);
1993						} else
1994							stream.SkipLine();
1995						break;
1996					}
1997
1998					default:
1999						// Something was amiss; skip to the next line
2000						stream.SkipLine();
2001						break;
2002				}
2003
2004			}
2005
2006			if (streamError == RosterSettingsCharStream::kEndOfStream)
2007				break;
2008		}
2009
2010		// Now we must sort our lists of documents and folders by the
2011		// indicies we read for each entry (largest index first)
2012		fRecentDocuments.fEntryList.sort(larger_index);
2013		fRecentFolders.fEntryList.sort(larger_index);
2014
2015		D(
2016			printf("----------------------------------------------------------------------\n");
2017			fRecentDocuments.Print();
2018			printf("----------------------------------------------------------------------\n");
2019			fRecentFolders.Print();
2020			printf("----------------------------------------------------------------------\n");
2021			fRecentApps.Print();
2022			printf("----------------------------------------------------------------------\n");
2023		);
2024	}
2025	if (error) {
2026		D(PRINT("WARNING: TRoster::_LoadRosterSettings(): error loading roster "
2027			"settings from '%s', 0x%lx\n", settingsPath, error));
2028	}
2029	return error;
2030}
2031
2032
2033status_t
2034TRoster::_SaveRosterSettings(const char* path)
2035{
2036	BPath _path;
2037	const char* settingsPath
2038		= path != NULL ? path : get_default_roster_settings_path(_path, true);
2039
2040	status_t error;
2041	FILE* file;
2042
2043	file = fopen(settingsPath, "w+");
2044	error = file ? B_OK : errno;
2045	if (!error) {
2046		status_t saveError;
2047		saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc");
2048		if (saveError) {
2049			D(PRINT("TRoster::_SaveRosterSettings(): recent documents save "
2050				"failed with error 0x%lx\n", saveError));
2051		}
2052		saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder");
2053		if (saveError) {
2054			D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2055				"failed with error 0x%lx\n", saveError));
2056		}
2057		saveError = fRecentApps.Save(file);
2058		if (saveError) {
2059			D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
2060				"failed with error 0x%lx\n", saveError));
2061		}
2062		fclose(file);
2063	}
2064
2065	return error;
2066}
2067