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