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