xref: /haiku/src/servers/registrar/TRoster.cpp (revision e5430a086c769ea76c3944046b1f07cf049c1ae0)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		TRoster.cpp
23 //	Author:			Ingo Weinhold (bonefish@users.sf.net)
24 //	Description:	TRoster is the incarnation of The Roster. It manages the
25 //					running applications.
26 //------------------------------------------------------------------------------
27 
28 #include <new>
29 
30 #include <Application.h>
31 #include <AppMisc.h>
32 #include <File.h>
33 #include <storage_support.h>
34 
35 #include <errno.h>
36 #include <stdio.h>
37 
38 #include "Debug.h"
39 #include "RegistrarDefs.h"
40 #include "RosterAppInfo.h"
41 #include "RosterSettingsCharStream.h"
42 #include "TRoster.h"
43 #include "EventMaskWatcher.h"
44 
45 //------------------------------------------------------------------------------
46 // Private local function declarations
47 //------------------------------------------------------------------------------
48 
49 static bool larger_index(const recent_entry *entry1, const recent_entry *entry2);
50 
51 
52 //------------------------------------------------------------------------------
53 // TRoster
54 //------------------------------------------------------------------------------
55 
56 /*!
57 	\class TRoster
58 	\brief Implements the application roster.
59 
60 	This class handles the BRoster requests. For each kind a hook method is
61 	implemented to which the registrar looper dispatches the request messages.
62 
63 	Registered and pre-registered are managed via AppInfoLists.
64 	\a fEarlyPreRegisteredApps contains the infos for those application that
65 	are pre-registered and currently have no team ID assigned to them yet,
66 	whereas the infos of registered and pre-registered applications with a
67 	team ID are to be found in \a fRegisteredApps.
68 
69 	When an application asks whether it is pre-registered or not and there
70 	are one or more instances of the application that are pre-registered, but
71 	have no team ID assigned yet, the reply to the request has to be
72 	postponed until the status of the requesting team is clear. The request
73 	message is dequeued from the registrar's message queue and, with
74 	additional information (IAPRRequest), added to \a fIAPRRequests for a
75 	later reply.
76 
77 	The field \a fActiveApp identifies the currently active application
78 	and \a fLastToken is a counter used to generate unique tokens for
79 	pre-registered applications.
80 */
81 
82 //! The maximal period of time an app may be early pre-registered (60 s).
83 const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
84 
85 const char *TRoster::kDefaultRosterSettingsFile =
86 	"/boot/home/config/settings/Roster/OpenBeOSRosterSettings";
87 
88 // constructor
89 /*!	\brief Creates a new roster.
90 
91 	The object is completely initialized and ready to handle requests.
92 */
93 TRoster::TRoster()
94 	   : fRegisteredApps(),
95 		 fEarlyPreRegisteredApps(),
96 		 fIAPRRequests(),
97 		 fActiveApp(NULL),
98 		 fWatchingService(),
99 		 fRecentApps(),
100 		 fRecentDocuments(),
101 		 fRecentFolders(),
102 		 fLastToken(0)
103 {
104 	_LoadRosterSettings();
105 }
106 
107 // destructor
108 /*!	\brief Frees all resources associated with this object.
109 */
110 TRoster::~TRoster()
111 {
112 }
113 
114 // HandleAddApplication
115 /*!	\brief Handles an AddApplication() request.
116 	\param request The request message
117 */
118 void
119 TRoster::HandleAddApplication(BMessage *request)
120 {
121 	FUNCTION_START();
122 
123 	status_t error = B_OK;
124 	// get the parameters
125 	const char *signature;
126 	entry_ref ref;
127 	uint32 flags;
128 	team_id team;
129 	thread_id thread;
130 	port_id port;
131 	bool fullReg;
132 	if (request->FindString("signature", &signature) != B_OK)
133 		signature = NULL;
134 	if (request->FindRef("ref", &ref) != B_OK)
135 		SET_ERROR(error, B_BAD_VALUE);
136 	if (request->FindInt32("flags", (int32*)&flags) != B_OK)
137 		flags = B_REG_DEFAULT_APP_FLAGS;
138 	if (request->FindInt32("team", &team) != B_OK)
139 		team = -1;
140 	if (request->FindInt32("thread", &thread) != B_OK)
141 		thread = -1;
142 	if (request->FindInt32("port", &port) != B_OK)
143 		port = -1;
144 	if (request->FindBool("full_registration", &fullReg) != B_OK)
145 		fullReg = false;
146 PRINT(("team: %ld, signature: %s\n", team, signature));
147 PRINT(("full registration: %d\n", fullReg));
148 	// check the parameters
149 	team_id otherTeam = -1;
150 	uint32 launchFlags = flags & B_LAUNCH_MASK;
151 	// entry_ref
152 	if (error == B_OK) {
153 		// the entry_ref must be valid
154 		if (BEntry(&ref).Exists()) {
155 PRINT(("flags: %lx\n", flags));
156 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
157 			// check single/exclusive launchers
158 			RosterAppInfo *info = NULL;
159 			if ((launchFlags == B_SINGLE_LAUNCH
160 				 || launchFlags ==  B_EXCLUSIVE_LAUNCH)
161 				&& (((info = fRegisteredApps.InfoFor(&ref)))
162 					|| ((info = fEarlyPreRegisteredApps.InfoFor(&ref))))) {
163 				SET_ERROR(error, B_ALREADY_RUNNING);
164 				otherTeam = info->team;
165 			}
166 		} else
167 			SET_ERROR(error, B_ENTRY_NOT_FOUND);
168 	}
169 	// signature
170 	if (error == B_OK && signature) {
171 		// check exclusive launchers
172 		RosterAppInfo *info = NULL;
173 		if (launchFlags == B_EXCLUSIVE_LAUNCH
174 			&& (((info = fRegisteredApps.InfoFor(signature)))
175 				|| ((info = fEarlyPreRegisteredApps.InfoFor(signature))))) {
176 			SET_ERROR(error, B_ALREADY_RUNNING);
177 			otherTeam = info->team;
178 		}
179 	}
180 	// If no team ID is given, full registration isn't possible.
181 	if (error == B_OK) {
182 		if (team < 0) {
183 			if (fullReg)
184 				SET_ERROR(error, B_BAD_VALUE);
185 		} else if (fRegisteredApps.InfoFor(team))
186 			SET_ERROR(error, B_REG_ALREADY_REGISTERED);
187 	}
188 	// Add the application info.
189 	uint32 token = 0;
190 	if (error == B_OK) {
191 		// alloc and init the info
192 		RosterAppInfo *info = new(nothrow) RosterAppInfo;
193 		if (info) {
194 			info->Init(thread, team, port, flags, &ref, signature);
195 			if (fullReg)
196 				info->state = APP_STATE_REGISTERED;
197 			else
198 				info->state = APP_STATE_PRE_REGISTERED;
199 			info->registration_time = system_time();
200 			// add it to the right list
201 			bool addingSuccess = false;
202 			if (team >= 0)
203 {
204 PRINT(("added ref: %ld, %lld, %s\n", info->ref.device, info->ref.directory, info->ref.name));
205 				addingSuccess = (AddApp(info) == B_OK);
206 }
207 			else {
208 				token = info->token = _NextToken();
209 				addingSuccess = fEarlyPreRegisteredApps.AddInfo(info);
210 PRINT(("added to early pre-regs, token: %lu\n", token));
211 			}
212 			if (!addingSuccess)
213 				SET_ERROR(error, B_NO_MEMORY);
214 		} else
215 			SET_ERROR(error, B_NO_MEMORY);
216 		// delete the info on failure
217 		if (error != B_OK && info)
218 			delete info;
219 	}
220 	// reply to the request
221 	if (error == B_OK) {
222 		// add to recent apps if successful
223 		if (signature && signature[0] != '\0')
224 			fRecentApps.Add(signature, flags);
225 		else
226 			fRecentApps.Add(&ref, flags);
227 //		fRecentApps.Print();
228 
229 		BMessage reply(B_REG_SUCCESS);
230 		// The token is valid only when no team ID has been supplied.
231 		if (team < 0)
232 			reply.AddInt32("token", (int32)token);
233 		request->SendReply(&reply);
234 	} else {
235 		BMessage reply(B_REG_ERROR);
236 		reply.AddInt32("error", error);
237 		if (otherTeam >= 0)
238 			reply.AddInt32("other_team", otherTeam);
239 		request->SendReply(&reply);
240 	}
241 
242 	FUNCTION_END();
243 }
244 
245 // HandleCompleteRegistration
246 /*!	\brief Handles a CompleteRegistration() request.
247 	\param request The request message
248 */
249 void
250 TRoster::HandleCompleteRegistration(BMessage *request)
251 {
252 	FUNCTION_START();
253 
254 	status_t error = B_OK;
255 	// get the parameters
256 	team_id team;
257 	thread_id thread;
258 	port_id port;
259 	if (request->FindInt32("team", &team) != B_OK)
260 		team = -1;
261 	if (request->FindInt32("thread", &thread) != B_OK)
262 		thread = -1;
263 	if (request->FindInt32("port", &port) != B_OK)
264 		port = -1;
265 	// check the parameters
266 	// port
267 	if (error == B_OK && port < 0)
268 		SET_ERROR(error, B_BAD_VALUE);
269 	// thread
270 	if (error == B_OK && thread < 0)
271 		SET_ERROR(error, B_BAD_VALUE);
272 	// team
273 	if (error == B_OK) {
274 		if (team >= 0) {
275 			// everything is fine -- set the values
276 			RosterAppInfo *info = fRegisteredApps.InfoFor(team);
277 			if (info && info->state == APP_STATE_PRE_REGISTERED) {
278 				info->thread = thread;
279 				info->port = port;
280 				info->state = APP_STATE_REGISTERED;
281 			} else
282 				SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
283 		} else
284 			SET_ERROR(error, B_BAD_VALUE);
285 	}
286 	// reply to the request
287 	if (error == B_OK) {
288 		BMessage reply(B_REG_SUCCESS);
289 		request->SendReply(&reply);
290 	} else {
291 		BMessage reply(B_REG_ERROR);
292 		reply.AddInt32("error", error);
293 		request->SendReply(&reply);
294 	}
295 
296 	FUNCTION_END();
297 }
298 
299 // HandleIsAppPreRegistered
300 /*!	\brief Handles an IsAppPreRegistered() request.
301 	\param request The request message
302 */
303 void
304 TRoster::HandleIsAppPreRegistered(BMessage *request)
305 {
306 	FUNCTION_START();
307 
308 	status_t error = B_OK;
309 	// get the parameters
310 	entry_ref ref;
311 	team_id team;
312 	if (request->FindRef("ref", &ref) != B_OK)
313 		SET_ERROR(error, B_BAD_VALUE);
314 	if (request->FindInt32("team", &team) != B_OK)
315 		team = -1;
316 PRINT(("team: %ld\n", team));
317 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
318 	// check the parameters
319 	// entry_ref
320 	if (error == B_OK & !BEntry(&ref).Exists())
321 		SET_ERROR(error, B_ENTRY_NOT_FOUND);
322 	// team
323 	if (error == B_OK && team < 0)
324 		SET_ERROR(error, B_BAD_VALUE);
325 	// look up the information
326 	RosterAppInfo *info = NULL;
327 	if (error == B_OK) {
328 		if ((info = fRegisteredApps.InfoFor(team)) != NULL) {
329 PRINT(("found team in fRegisteredApps\n"));
330 			_ReplyToIAPRRequest(request, info);
331 		} else if ((info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL) {
332 PRINT(("found ref in fEarlyRegisteredApps\n"));
333 			// pre-registered and has no team ID assigned yet -- queue the
334 			// request
335 			be_app->DetachCurrentMessage();
336 			IAPRRequest queuedRequest = { ref, team, request };
337 			fIAPRRequests[team] = queuedRequest;
338 		} else {
339 PRINT(("didn't find team or ref\n"));
340 			// team not registered, ref not early pre-registered
341 			_ReplyToIAPRRequest(request, NULL);
342 		}
343 	} else {
344 		// reply to the request on error
345 		BMessage reply(B_REG_ERROR);
346 		reply.AddInt32("error", error);
347 		request->SendReply(&reply);
348 	}
349 
350 	FUNCTION_END();
351 }
352 
353 // HandleRemovePreRegApp
354 /*!	\brief Handles a RemovePreRegApp() request.
355 	\param request The request message
356 */
357 void
358 TRoster::HandleRemovePreRegApp(BMessage *request)
359 {
360 	FUNCTION_START();
361 
362 	status_t error = B_OK;
363 	// get the parameters
364 	uint32 token;
365 	if (request->FindInt32("token", (int32*)&token) != B_OK)
366 		SET_ERROR(error, B_BAD_VALUE);
367 	// remove the app
368 	if (error == B_OK) {
369 		RosterAppInfo *info = fEarlyPreRegisteredApps.InfoForToken(token);
370 		if (info) {
371 			fEarlyPreRegisteredApps.RemoveInfo(info);
372 			delete info;
373 		} else
374 			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
375 	}
376 	// reply to the request
377 	if (error == B_OK) {
378 		BMessage reply(B_REG_SUCCESS);
379 		request->SendReply(&reply);
380 	} else {
381 		BMessage reply(B_REG_ERROR);
382 		reply.AddInt32("error", error);
383 		request->SendReply(&reply);
384 	}
385 
386 	FUNCTION_END();
387 }
388 
389 // HandleRemoveApp
390 /*!	\brief Handles a RemoveApp() request.
391 	\param request The request message
392 */
393 void
394 TRoster::HandleRemoveApp(BMessage *request)
395 {
396 	FUNCTION_START();
397 
398 	status_t error = B_OK;
399 	// get the parameters
400 	team_id team;
401 	if (request->FindInt32("team", &team) != B_OK)
402 		team = -1;
403 PRINT(("team: %ld\n", team));
404 	// remove the app
405 	if (error == B_OK) {
406 		if (RosterAppInfo *info = fRegisteredApps.InfoFor(team)) {
407 			RemoveApp(info);
408 			delete info;
409 		} else
410 			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
411 	}
412 	// reply to the request
413 	if (error == B_OK) {
414 		BMessage reply(B_REG_SUCCESS);
415 		request->SendReply(&reply);
416 	} else {
417 		BMessage reply(B_REG_ERROR);
418 		reply.AddInt32("error", error);
419 		request->SendReply(&reply);
420 	}
421 
422 	FUNCTION_END();
423 }
424 
425 // HandleSetThreadAndTeam
426 /*!	\brief Handles a SetThreadAndTeam() request.
427 	\param request The request message
428 */
429 void
430 TRoster::HandleSetThreadAndTeam(BMessage *request)
431 {
432 	FUNCTION_START();
433 
434 	status_t error = B_OK;
435 	// get the parameters
436 	team_id team;
437 	thread_id thread;
438 	uint32 token;
439 	if (request->FindInt32("team", &team) != B_OK)
440 		team = -1;
441 	if (request->FindInt32("thread", &thread) != B_OK)
442 		thread = -1;
443 	if (request->FindInt32("token", (int32*)&token) != B_OK)
444 		SET_ERROR(error, B_BAD_VALUE);
445 	// check the parameters
446 	// team
447 	if (error == B_OK && team < 0)
448 		SET_ERROR(error, B_BAD_VALUE);
449 PRINT(("team: %ld, thread: %ld, token: %lu\n", team, thread, token));
450 	// update the app_info
451 	if (error == B_OK) {
452 		RosterAppInfo *info = fEarlyPreRegisteredApps.InfoForToken(token);
453 		if (info) {
454 			// Set thread and team, create a port for the application and
455 			// move the app_info from the list of the early pre-registered
456 			// apps to the list of the (pre-)registered apps.
457 			fEarlyPreRegisteredApps.RemoveInfo(info);
458 			info->team = team;
459 			info->thread = thread;
460 			// create and transfer the port
461 			info->port = create_port(B_REG_APP_LOOPER_PORT_CAPACITY,
462 									 kRAppLooperPortName);
463 			if (info->port < 0)
464 				SET_ERROR(error, info->port);
465 			if (error == B_OK)
466 				SET_ERROR(error, set_port_owner(info->port, team));
467 			// add the info to the registered apps list
468 			if (error == B_OK)
469 				SET_ERROR(error, AddApp(info));
470 			// cleanup on failure
471 			if (error != B_OK) {
472 				if (info->port >= 0)
473 					delete_port(info->port);
474 				delete info;
475 			}
476 			// handle a pending IsAppPreRegistered() request
477 			IAPRRequestMap::iterator it = fIAPRRequests.find(team);
478 			if (it != fIAPRRequests.end()) {
479 				IAPRRequest &request = it->second;
480 				if (error == B_OK)
481 					_ReplyToIAPRRequest(request.request, info);
482 				delete request.request;
483 				fIAPRRequests.erase(it);
484 			}
485 		} else
486 			SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
487 	}
488 	// reply to the request
489 	if (error == B_OK) {
490 		BMessage reply(B_REG_SUCCESS);
491 		request->SendReply(&reply);
492 	} else {
493 		BMessage reply(B_REG_ERROR);
494 		reply.AddInt32("error", error);
495 		request->SendReply(&reply);
496 	}
497 
498 	FUNCTION_END();
499 }
500 
501 // HandleSetSignature
502 /*!	\brief Handles a SetSignature() request.
503 	\param request The request message
504 */
505 void
506 TRoster::HandleSetSignature(BMessage *request)
507 {
508 	FUNCTION_START();
509 
510 	status_t error = B_OK;
511 	// get the parameters
512 	team_id team;
513 	const char *signature;
514 	if (request->FindInt32("team", &team) != B_OK)
515 		error = B_BAD_VALUE;
516 	if (request->FindString("signature", &signature) != B_OK)
517 		error = B_BAD_VALUE;
518 	// find the app and set the signature
519 	if (error == B_OK) {
520 		if (RosterAppInfo *info = fRegisteredApps.InfoFor(team))
521 			strcpy(info->signature, signature);
522 		else
523 			SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
524 	}
525 	// reply to the request
526 	if (error == B_OK) {
527 		BMessage reply(B_REG_SUCCESS);
528 		request->SendReply(&reply);
529 	} else {
530 		BMessage reply(B_REG_ERROR);
531 		reply.AddInt32("error", error);
532 		request->SendReply(&reply);
533 	}
534 
535 	FUNCTION_END();
536 }
537 
538 // HandleGetAppInfo
539 /*!	\brief Handles a Get{Running,Active,}AppInfo() request.
540 	\param request The request message
541 */
542 void
543 TRoster::HandleGetAppInfo(BMessage *request)
544 {
545 	FUNCTION_START();
546 
547 	status_t error = B_OK;
548 	// get the parameters
549 	team_id team;
550 	entry_ref ref;
551 	const char *signature;
552 	bool hasTeam = true;
553 	bool hasRef = true;
554 	bool hasSignature = true;
555 	if (request->FindInt32("team", &team) != B_OK)
556 		hasTeam = false;
557 	if (request->FindRef("ref", &ref) != B_OK)
558 		hasRef = false;
559 	if (request->FindString("signature", &signature) != B_OK)
560 		hasSignature = false;
561 if (hasTeam)
562 PRINT(("team: %ld\n", team));
563 if (hasRef)
564 PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
565 if (hasSignature)
566 PRINT(("signature: %s\n", signature));
567 	// get the info
568 	RosterAppInfo *info = NULL;
569 	if (error == B_OK) {
570 		if (hasTeam) {
571 			info = fRegisteredApps.InfoFor(team);
572 			if (info == NULL)
573 				SET_ERROR(error, B_BAD_TEAM_ID);
574 		} else if (hasRef) {
575 			info = fRegisteredApps.InfoFor(&ref);
576 			if (info == NULL)
577 				SET_ERROR(error, B_ERROR);
578 		} else if (hasSignature) {
579 			info = fRegisteredApps.InfoFor(signature);
580 			if (info == NULL)
581 				SET_ERROR(error, B_ERROR);
582 		} else {
583 			// If neither of those has been supplied, the active application
584 			// info is requested.
585 			if (fActiveApp)
586 				info = fActiveApp;
587 			else
588 				SET_ERROR(error, B_ERROR);
589 		}
590 	}
591 	// reply to the request
592 	if (error == B_OK) {
593 		BMessage reply(B_REG_SUCCESS);
594 		_AddMessageAppInfo(&reply, info);
595 		request->SendReply(&reply);
596 	} else {
597 		BMessage reply(B_REG_ERROR);
598 		reply.AddInt32("error", error);
599 		request->SendReply(&reply);
600 	}
601 
602 	FUNCTION_END();
603 }
604 
605 // HandleGetAppList
606 /*!	\brief Handles a GetAppList() request.
607 	\param request The request message
608 */
609 void
610 TRoster::HandleGetAppList(BMessage *request)
611 {
612 	FUNCTION_START();
613 
614 	status_t error = B_OK;
615 	// get the parameters
616 	const char *signature;
617 	if (request->FindString("signature", &signature) != B_OK)
618 		signature = NULL;
619 	// reply to the request
620 	if (error == B_OK) {
621 		BMessage reply(B_REG_SUCCESS);
622 		// get the list
623 		for (AppInfoList::Iterator it(fRegisteredApps.It());
624 			 RosterAppInfo *info = *it;
625 			 ++it) {
626 			if (!signature || !strcmp(signature, info->signature))
627 				reply.AddInt32("teams", info->team);
628 		}
629 		request->SendReply(&reply);
630 	} else {
631 		BMessage reply(B_REG_ERROR);
632 		reply.AddInt32("error", error);
633 		request->SendReply(&reply);
634 	}
635 
636 	FUNCTION_END();
637 }
638 
639 // HandleActivateApp
640 /*!	\brief Handles a ActivateApp() request.
641 	\param request The request message
642 */
643 void
644 TRoster::HandleActivateApp(BMessage *request)
645 {
646 	FUNCTION_START();
647 
648 	status_t error = B_OK;
649 	// get the parameters
650 	team_id team;
651 	if (request->FindInt32("team", &team) != B_OK)
652 		error = B_BAD_VALUE;
653 	// activate the app
654 	if (error == B_OK) {
655 		if (RosterAppInfo *info = fRegisteredApps.InfoFor(team))
656 			ActivateApp(info);
657 		else
658 			error = B_BAD_TEAM_ID;
659 	}
660 	// reply to the request
661 	if (error == B_OK) {
662 		BMessage reply(B_REG_SUCCESS);
663 		request->SendReply(&reply);
664 	} else {
665 		BMessage reply(B_REG_ERROR);
666 		reply.AddInt32("error", error);
667 		request->SendReply(&reply);
668 	}
669 
670 	FUNCTION_END();
671 }
672 
673 // HandleBroadcast
674 /*!	\brief Handles a Broadcast() request.
675 	\param request The request message
676 */
677 void
678 TRoster::HandleBroadcast(BMessage *request)
679 {
680 	FUNCTION_START();
681 
682 	status_t error = B_OK;
683 	// get the parameters
684 	team_id team;
685 	BMessage message;
686 	BMessenger replyTarget;
687 	if (request->FindInt32("team", &team) != B_OK)
688 		team = -1;
689 	if (error == B_OK && request->FindMessage("message", &message) != B_OK)
690 		error = B_BAD_VALUE;
691 	if (error == B_OK
692 		&& request->FindMessenger("reply_target", &replyTarget) != B_OK) {
693 		error = B_BAD_VALUE;
694 	}
695 	// reply to the request -- do this first, don't let the inquirer wait
696 	if (error == B_OK) {
697 		BMessage reply(B_REG_SUCCESS);
698 		request->SendReply(&reply);
699 	} else {
700 		BMessage reply(B_REG_ERROR);
701 		reply.AddInt32("error", error);
702 		request->SendReply(&reply);
703 	}
704 	// broadcast the message
705 	team_id registrarTeam = BPrivate::current_team();
706 	if (error == B_OK) {
707 		for (AppInfoList::Iterator it = fRegisteredApps.It();
708 			 it.IsValid();
709 			 ++it) {
710 			// don't send the message to the requesting team or the registrar
711 			if ((*it)->team != team && (*it)->team != registrarTeam) {
712 				BMessenger messenger((*it)->team, (*it)->port, 0, true);
713 				messenger.SendMessage(&message, replyTarget, 0);
714 			}
715 		}
716 	}
717 
718 	FUNCTION_END();
719 }
720 
721 // HandleStartWatching
722 /*!	\brief Handles a StartWatching() request.
723 	\param request The request message
724 */
725 void
726 TRoster::HandleStartWatching(BMessage *request)
727 {
728 	FUNCTION_START();
729 
730 	status_t error = B_OK;
731 	// get the parameters
732 	BMessenger target;
733 	uint32 events;
734 	if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
735 		error = B_BAD_VALUE;
736 	if (request->FindInt32("events", (int32*)&events) != B_OK)
737 		error = B_BAD_VALUE;
738 	// add the new watcher
739 	if (error == B_OK) {
740 		Watcher *watcher = new(nothrow) EventMaskWatcher(target, events);
741 		if (watcher) {
742 			if (!fWatchingService.AddWatcher(watcher)) {
743 				error = B_NO_MEMORY;
744 				delete watcher;
745 			}
746 		} else
747 			error = B_NO_MEMORY;
748 	}
749 	// reply to the request
750 	if (error == B_OK) {
751 		BMessage reply(B_REG_SUCCESS);
752 		request->SendReply(&reply);
753 	} else {
754 		BMessage reply(B_REG_ERROR);
755 		reply.AddInt32("error", error);
756 		request->SendReply(&reply);
757 	}
758 
759 	FUNCTION_END();
760 }
761 
762 // HandleStopWatching
763 /*!	\brief Handles a StopWatching() request.
764 	\param request The request message
765 */
766 void
767 TRoster::HandleStopWatching(BMessage *request)
768 {
769 	FUNCTION_START();
770 
771 	status_t error = B_OK;
772 	// get the parameters
773 	BMessenger target;
774 	if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
775 		error = B_BAD_VALUE;
776 	// remove the watcher
777 	if (error == B_OK) {
778 		if (!fWatchingService.RemoveWatcher(target))
779 			error = B_BAD_VALUE;
780 	}
781 	// reply to the request
782 	if (error == B_OK) {
783 		BMessage reply(B_REG_SUCCESS);
784 		request->SendReply(&reply);
785 	} else {
786 		BMessage reply(B_REG_ERROR);
787 		reply.AddInt32("error", error);
788 		request->SendReply(&reply);
789 	}
790 
791 	FUNCTION_END();
792 }
793 
794 // HandleGetRecentDocuments
795 /*!	\brief Handles a GetRecentDocuments() request.
796 	\param request The request message
797 */
798 void
799 TRoster::HandleGetRecentDocuments(BMessage *request)
800 {
801 	FUNCTION_START();
802 	_HandleGetRecentEntries(request);
803 	FUNCTION_END();
804 }
805 
806 // HandleGetRecentFolders
807 /*!	\brief Handles a GetRecentFolders() request.
808 	\param request The request message
809 */
810 void
811 TRoster::HandleGetRecentFolders(BMessage *request)
812 {
813 	FUNCTION_START();
814 	_HandleGetRecentEntries(request);
815 	FUNCTION_END();
816 }
817 
818 // HandleGetRecentApps
819 /*!	\brief Handles a GetRecentApps() request.
820 	\param request The request message
821 */
822 void
823 TRoster::HandleGetRecentApps(BMessage *request)
824 {
825 	FUNCTION_START();
826 
827 	if (!request) {
828 		D(PRINT(("WARNING: TRoster::HandleGetRecentApps(NULL) called\n")));
829 		return;
830 	}
831 
832 	int32 maxCount;
833 	BMessage reply(B_REG_RESULT);
834 
835 	status_t error = request->FindInt32("max count", &maxCount);
836 	if (!error)
837 		error = fRecentApps.Get(maxCount, &reply);
838 	reply.AddInt32("result", error);
839 	request->SendReply(&reply);
840 
841 	FUNCTION_END();
842 }
843 
844 // HandleAddToRecentDocuments
845 /*!	\brief Handles an AddToRecentDocuments() request.
846 	\param request The request message
847 */
848 void
849 TRoster::HandleAddToRecentDocuments(BMessage *request)
850 {
851 	FUNCTION_START();
852 
853 	if (!request) {
854 		D(PRINT(("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n")));
855 		return;
856 	}
857 
858 	entry_ref ref;
859 	const char *appSig;
860 	BMessage reply(B_REG_RESULT);
861 
862 	status_t error = request->FindRef("ref", &ref);
863 	if (!error)
864 		error = request->FindString("app sig", &appSig);
865 	if (!error)
866 		error = fRecentDocuments.Add(&ref, appSig);
867 	reply.AddInt32("result", error);
868 	request->SendReply(&reply);
869 
870 	FUNCTION_END();
871 }
872 
873 // HandleAddToRecentFolders
874 /*!	\brief Handles an AddToRecentFolders() request.
875 	\param request The request message
876 */
877 void
878 TRoster::HandleAddToRecentFolders(BMessage *request)
879 {
880 	FUNCTION_START();
881 
882 	if (!request) {
883 		D(PRINT(("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n")));
884 		return;
885 	}
886 
887 	entry_ref ref;
888 	const char *appSig;
889 	BMessage reply(B_REG_RESULT);
890 
891 	status_t error = request->FindRef("ref", &ref);
892 	if (!error)
893 		error = request->FindString("app sig", &appSig);
894 	if (!error)
895 		error = fRecentFolders.Add(&ref, appSig);
896 	reply.AddInt32("result", error);
897 	request->SendReply(&reply);
898 
899 	FUNCTION_END();
900 }
901 
902 // HandleAddToRecentApps
903 /*!	\brief Handles an AddToRecentApps() request.
904 	\param request The request message
905 */
906 void
907 TRoster::HandleAddToRecentApps(BMessage *request)
908 {
909 	FUNCTION_START();
910 
911 	if (!request) {
912 		D(PRINT(("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n")));
913 		return;
914 	}
915 
916 	const char *appSig;
917 	BMessage reply(B_REG_RESULT);
918 
919 	status_t error = request->FindString("app sig", &appSig);
920 	if (!error)
921 		error = fRecentApps.Add(appSig);
922 	reply.AddInt32("result", error);
923 	request->SendReply(&reply);
924 
925 	FUNCTION_END();
926 }
927 
928 void
929 TRoster::HandleLoadRecentLists(BMessage *request)
930 {
931 	FUNCTION_START();
932 
933 	if (!request) {
934 		D(PRINT(("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n")));
935 		return;
936 	}
937 
938 	const char *filename;
939 	BMessage reply(B_REG_RESULT);
940 
941 	status_t error = request->FindString("filename", &filename);
942 	if (!error)
943 		error = _LoadRosterSettings(filename);
944 	reply.AddInt32("result", error);
945 	request->SendReply(&reply);
946 
947 	FUNCTION_END();
948 }
949 
950 void
951 TRoster::HandleSaveRecentLists(BMessage *request)
952 {
953 	FUNCTION_START();
954 
955 	if (!request) {
956 		D(PRINT(("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n")));
957 		return;
958 	}
959 
960 	const char *filename;
961 	BMessage reply(B_REG_RESULT);
962 
963 	status_t error = request->FindString("filename", &filename);
964 	if (!error)
965 		error = _SaveRosterSettings(filename);
966 	reply.AddInt32("result", error);
967 	request->SendReply(&reply);
968 
969 	FUNCTION_END();
970 }
971 
972 // ClearRecentDocuments
973 /*!	\brief Clears the current list of recent documents
974 */
975 void
976 TRoster::ClearRecentDocuments()
977 {
978 	fRecentDocuments.Clear();
979 }
980 
981 // ClearRecentFolders
982 /*!	\brief Clears the current list of recent folders
983 */
984 void
985 TRoster::ClearRecentFolders()
986 {
987 	fRecentFolders.Clear();
988 }
989 
990 // ClearRecentApps
991 /*!	\brief Clears the current list of recent apps
992 */
993 void
994 TRoster::ClearRecentApps()
995 {
996 	fRecentApps.Clear();
997 }
998 
999 // Init
1000 /*!	\brief Initializes the roster.
1001 
1002 	Currently only adds the registrar to the roster.
1003 	The application must already be running, more precisly Run() must have
1004 	been called.
1005 
1006 	\return
1007 	- \c B_OK: Everything went fine.
1008 	- an error code
1009 */
1010 status_t
1011 TRoster::Init()
1012 {
1013 	status_t error = B_OK;
1014 	// create the info
1015 	RosterAppInfo *info = new(nothrow) RosterAppInfo;
1016 	if (!info)
1017 		error = B_NO_MEMORY;
1018 	// get the app's ref
1019 	entry_ref ref;
1020 	if (error == B_OK)
1021 		error = get_app_ref(&ref);
1022 	// init and add the info
1023 	if (error == B_OK) {
1024 		info->Init(be_app->Thread(), be_app->Team(), be_app_messenger.fPort,
1025 				   B_EXCLUSIVE_LAUNCH, &ref, kRegistrarSignature);
1026 		info->state = APP_STATE_REGISTERED;
1027 		info->registration_time = system_time();
1028 		error = AddApp(info);
1029 	}
1030 	// cleanup on error
1031 	if (error != B_OK && info)
1032 		delete info;
1033 	return error;
1034 }
1035 
1036 // AddApp
1037 /*!	\brief Add the supplied app info to the list of (pre-)registered apps.
1038 
1039 	\param info The app info to be added
1040 */
1041 status_t
1042 TRoster::AddApp(RosterAppInfo *info)
1043 {
1044 	status_t error = (info ? B_OK : B_BAD_VALUE);
1045 	if (info) {
1046 		if (fRegisteredApps.AddInfo(info))
1047 			_AppAdded(info);
1048 		else
1049 			error = B_NO_MEMORY;
1050 	}
1051 	return error;
1052 }
1053 
1054 // RemoveApp
1055 /*!	\brief Removes the supplied app info from the list of (pre-)registered
1056 	apps.
1057 
1058 	\param info The app info to be removed
1059 */
1060 void
1061 TRoster::RemoveApp(RosterAppInfo *info)
1062 {
1063 	if (info) {
1064 		if (fRegisteredApps.RemoveInfo(info)) {
1065 			info->state = APP_STATE_UNREGISTERED;
1066 			_AppRemoved(info);
1067 		}
1068 	}
1069 }
1070 
1071 // ActivateApp
1072 /*!	\brief Activates the application identified by \a info.
1073 
1074 	The currently activate application is deactivated and the one whose
1075 	info is supplied is activated. \a info may be \c NULL, which only
1076 	deactivates the currently active application.
1077 
1078 	\param info The info of the app to be activated
1079 */
1080 void
1081 TRoster::ActivateApp(RosterAppInfo *info)
1082 {
1083 	if (info != fActiveApp) {
1084 		// deactivate the currently active app
1085 		RosterAppInfo *oldActiveApp = fActiveApp;
1086 		fActiveApp = NULL;
1087 		if (oldActiveApp)
1088 			_AppDeactivated(oldActiveApp);
1089 		// activate the new app
1090 		if (info) {
1091 			info = fActiveApp;
1092 			_AppActivated(info);
1093 		}
1094 	}
1095 }
1096 
1097 // CheckSanity
1098 /*!	\brief Checks whether the (pre-)registered applications are still running.
1099 
1100 	This is necessary, since killed applications don't unregister properly.
1101 */
1102 void
1103 TRoster::CheckSanity()
1104 {
1105 	// not early (pre-)registered applications
1106 	AppInfoList obsoleteApps;
1107 	for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) {
1108 		team_info teamInfo;
1109 		if (get_team_info((*it)->team, &teamInfo) != B_OK)
1110 			obsoleteApps.AddInfo(*it);
1111 	}
1112 	// remove the apps
1113 	for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
1114 		RemoveApp(*it);
1115 		delete *it;
1116 	}
1117 	// early pre-registered applications
1118 	obsoleteApps.MakeEmpty();
1119 	bigtime_t timeLimit = system_time() - kMaximalEarlyPreRegistrationPeriod;
1120 	for (AppInfoList::Iterator it = fEarlyPreRegisteredApps.It();
1121 		 it.IsValid();
1122 		 ++it) {
1123 		if ((*it)->registration_time < timeLimit)
1124 			obsoleteApps.AddInfo(*it);
1125 	}
1126 	// remove the apps
1127 	for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
1128 		fEarlyPreRegisteredApps.RemoveInfo(*it);
1129 		delete *it;
1130 	}
1131 }
1132 
1133 
1134 // _AppAdded
1135 /*!	\brief Hook method invoked, when an application has been added.
1136 	\param info The RosterAppInfo of the added application.
1137 */
1138 void
1139 TRoster::_AppAdded(RosterAppInfo *info)
1140 {
1141 	// notify the watchers
1142 	BMessage message(B_SOME_APP_LAUNCHED);
1143 	_AddMessageWatchingInfo(&message, info);
1144 	EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED);
1145 	fWatchingService.NotifyWatchers(&message, &filter);
1146 }
1147 
1148 // _AppRemoved
1149 /*!	\brief Hook method invoked, when an application has been removed.
1150 	\param info The RosterAppInfo of the removed application.
1151 */
1152 void
1153 TRoster::_AppRemoved(RosterAppInfo *info)
1154 {
1155 	if (info) {
1156 		// deactivate the app, if it was the active one
1157 		if (info == fActiveApp)
1158 			ActivateApp(NULL);
1159 		// notify the watchers
1160 		BMessage message(B_SOME_APP_QUIT);
1161 		_AddMessageWatchingInfo(&message, info);
1162 		EventMaskWatcherFilter filter(B_REQUEST_QUIT);
1163 		fWatchingService.NotifyWatchers(&message, &filter);
1164 	}
1165 }
1166 
1167 // _AppActivated
1168 /*!	\brief Hook method invoked, when an application has been activated.
1169 	\param info The RosterAppInfo of the activated application.
1170 */
1171 void
1172 TRoster::_AppActivated(RosterAppInfo *info)
1173 {
1174 	if (info) {
1175 		if (info->state == APP_STATE_REGISTERED
1176 			|| info->state == APP_STATE_PRE_REGISTERED) {
1177 			// send B_APP_ACTIVATED to the app
1178 			BMessenger messenger(info->team, info->port, 0, true);
1179 			BMessage message(B_APP_ACTIVATED);
1180 			message.AddBool("active", true);
1181 			messenger.SendMessage(&message);
1182 			// notify the watchers
1183 			BMessage watcherMessage(B_SOME_APP_ACTIVATED);
1184 			_AddMessageWatchingInfo(&watcherMessage, info);
1185 			EventMaskWatcherFilter filter(B_REQUEST_ACTIVATED);
1186 			fWatchingService.NotifyWatchers(&watcherMessage, &filter);
1187 		}
1188 	}
1189 }
1190 
1191 // _AppDeactivated
1192 /*!	\brief Hook method invoked, when an application has been deactivated.
1193 	\param info The RosterAppInfo of the deactivated application.
1194 */
1195 void
1196 TRoster::_AppDeactivated(RosterAppInfo *info)
1197 {
1198 	if (info) {
1199 		if (info->state == APP_STATE_REGISTERED
1200 			|| info->state == APP_STATE_PRE_REGISTERED) {
1201 			// send B_APP_ACTIVATED to the app
1202 			BMessenger messenger(info->team, info->port, 0, true);
1203 			BMessage message(B_APP_ACTIVATED);
1204 			message.AddBool("active", false);
1205 			messenger.SendMessage(&message);
1206 		}
1207 	}
1208 }
1209 
1210 // _AddMessageAppInfo
1211 /*!	\brief Adds an app_info to a message.
1212 
1213 	The info is added as a flat_app_info to a field "app_info" with the type
1214 	\c B_REG_APP_INFO_TYPE.
1215 
1216 	\param message The message
1217 	\param info The app_info.
1218 	\return \c B_OK if everything went fine, an error code otherwise.
1219 */
1220 status_t
1221 TRoster::_AddMessageAppInfo(BMessage *message, const app_info *info)
1222 {
1223 	// An app_info is not completely flat. The entry_ref contains a string
1224 	// pointer. Therefore we flatten the info.
1225 	flat_app_info flatInfo;
1226 	flatInfo.info = *info;
1227 	// set the ref name to NULL and copy it into the flat structure
1228 	flatInfo.info.ref.name = NULL;
1229 	flatInfo.ref_name[0] = '\0';
1230 	if (info->ref.name)
1231 		strcpy(flatInfo.ref_name, info->ref.name);
1232 	// add the flat info
1233 	return message->AddData("app_info", B_REG_APP_INFO_TYPE, &flatInfo,
1234 							sizeof(flat_app_info));
1235 }
1236 
1237 // _AddMessageWatchingInfo
1238 /*!	\brief Adds application monitoring related fields to a message.
1239 	\param message The message.
1240 	\param info The app_info of the concerned application.
1241 	\return \c B_OK if everything went fine, an error code otherwise.
1242 */
1243 status_t
1244 TRoster::_AddMessageWatchingInfo(BMessage *message, const app_info *info)
1245 {
1246 	status_t error = B_OK;
1247 	if (error == B_OK)
1248 		error = message->AddString("be:signature", info->signature);
1249 	if (error == B_OK)
1250 		error = message->AddInt32("be:team", info->team);
1251 	if (error == B_OK)
1252 		error = message->AddInt32("be:thread", info->thread);
1253 	if (error == B_OK)
1254 		error = message->AddInt32("be:flags", (int32)info->flags);
1255 	if (error == B_OK)
1256 		error = message->AddRef("be:ref", &info->ref);
1257 	return error;
1258 }
1259 
1260 // _NextToken
1261 /*!	\brief Returns the next available token.
1262 	\return The token.
1263 */
1264 uint32
1265 TRoster::_NextToken()
1266 {
1267 	return ++fLastToken;
1268 }
1269 
1270 // _ReplyToIAPRRequest
1271 /*!	\brief Sends a reply message to a IsAppPreRegistered() request.
1272 
1273 	The message to be sent is a simple \c B_REG_SUCCESS message containing
1274 	a "pre-registered" field, that sais whether or not the application is
1275 	pre-registered. It will be set to \c false, unless an \a info is supplied
1276 	and the application this info refers to is pre-registered.
1277 
1278 	\param request The request message to be replied to
1279 	\param info The RosterAppInfo of the application in question
1280 		   (may be \c NULL)
1281 */
1282 void
1283 TRoster::_ReplyToIAPRRequest(BMessage *request, const RosterAppInfo *info)
1284 {
1285 	// pre-registered or registered?
1286 	bool preRegistered = false;
1287 	if (info) {
1288 		switch (info->state) {
1289 			case APP_STATE_PRE_REGISTERED:
1290 				preRegistered = true;
1291 				break;
1292 			case APP_STATE_UNREGISTERED:
1293 			case APP_STATE_REGISTERED:
1294 				preRegistered = false;
1295 				break;
1296 		}
1297 	}
1298 	// send reply
1299 	BMessage reply(B_REG_SUCCESS);
1300 	reply.AddBool("pre-registered", preRegistered);
1301 PRINT(("_ReplyToIAPRRequest(): pre-registered: %d\n", preRegistered));
1302 	if (preRegistered)
1303 		_AddMessageAppInfo(&reply, info);
1304 	request->SendReply(&reply);
1305 }
1306 
1307 // _HandleGetRecentEntries
1308 /*! \brief Handles requests for both GetRecentDocuments() and
1309 	GetRecentFolders().
1310 */
1311 void
1312 TRoster::_HandleGetRecentEntries(BMessage *request)
1313 {
1314 	FUNCTION_START();
1315 	if (!request) {
1316 		D(PRINT(("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n")));
1317 		return;
1318 	}
1319 
1320 	int32 maxCount;
1321 	BMessage reply(B_REG_RESULT);
1322 	char **fileTypes = NULL;
1323 	int32 fileTypesCount = 0;
1324 	char *appSig = NULL;
1325 
1326 	status_t error = request->FindInt32("max count", &maxCount);
1327 	// Look for optional file type(s)
1328 	if (!error) {
1329 		type_code typeFound;
1330 		status_t typeError = request->GetInfo("file type", &typeFound, &fileTypesCount);
1331 		if (!typeError)
1332 			typeError = typeFound == B_STRING_TYPE ? B_OK : B_BAD_TYPE;
1333 		if (!typeError) {
1334 			fileTypes = new(nothrow) char*[fileTypesCount];
1335 			typeError = fileTypes ? B_OK : B_NO_MEMORY;
1336 		}
1337 		if (!typeError) {
1338 			for (int i = 0; !error && i < fileTypesCount; i++) {
1339 				const char *type;
1340 				if (request->FindString("file type", i, &type) == B_OK) {
1341 					fileTypes[i] = new(nothrow) char[B_MIME_TYPE_LENGTH];
1342 					error = fileTypes[i] ? B_OK : B_NO_MEMORY;
1343 						// Yes, I do mean to use "error" here, not "typeError"
1344 					BPrivate::Storage::to_lower(type, fileTypes[i]);
1345 						// Types are expected to be lowercase
1346 				}
1347 			}
1348 		}
1349 	}
1350 	// Look for optional app sig
1351 	if (!error) {
1352 		const char *sig;
1353 		error = request->FindString("app sig", &sig);
1354 		if (!error) {
1355 			appSig = new(nothrow) char[B_MIME_TYPE_LENGTH];
1356 			error = appSig ? B_OK : B_NO_MEMORY;
1357 			BPrivate::Storage::to_lower(sig, appSig);
1358 		} else if (error == B_NAME_NOT_FOUND)
1359 			error = B_OK;
1360 	}
1361 	if (!error) {
1362 		switch (request->what) {
1363 			case B_REG_GET_RECENT_DOCUMENTS:
1364 				error = fRecentDocuments.Get(maxCount, (const char**)fileTypes,
1365 		   	                                 fileTypesCount, appSig, &reply);
1366 				D(fRecentDocuments.Print());
1367 		   	    break;
1368 
1369 			case B_REG_GET_RECENT_FOLDERS:
1370 				error = fRecentFolders.Get(maxCount, (const char**)fileTypes,
1371 			                               fileTypesCount, appSig, &reply);
1372 				D(fRecentFolders.Print());
1373 			    break;
1374 
1375 			default:
1376 				D(PRINT(("WARNING: TRoster::_HandleGetRecentEntries(): unexpected "
1377 				         "request->what value of 0x%lx\n", request->what)));
1378 				error = B_BAD_VALUE;
1379 				break;
1380 		}
1381 	}
1382 	reply.AddInt32("result", error);
1383 	// Clean up before sending a reply
1384 	delete [] appSig;
1385 	if (fileTypes) {
1386 		for (int i = 0; i < fileTypesCount; i++)
1387 			delete [] fileTypes[i];
1388 		delete fileTypes;
1389 		fileTypes = NULL;
1390 	}
1391 	request->SendReply(&reply);
1392 
1393 	FUNCTION_END();
1394 }
1395 
1396 status_t
1397 TRoster::_LoadRosterSettings(const char *path)
1398 {
1399 	const char *settingsPath = path ? path : kDefaultRosterSettingsFile;
1400 
1401 	RosterSettingsCharStream stream;
1402 	status_t error;
1403 	BFile file;
1404 
1405 	error = file.SetTo(settingsPath, B_READ_ONLY);
1406 	off_t size;
1407 	if (!error)
1408 		error = file.GetSize(&size);
1409 	char *data;
1410 	if (!error) {
1411 		data = new(nothrow) char[size];
1412 		error = data ? B_OK : B_NO_MEMORY;
1413 	}
1414 	if (!error) {
1415 		ssize_t bytes = file.Read(data, size);
1416 		error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR);
1417 	}
1418 	if (!error)
1419 		error = stream.SetTo(std::string(data));
1420 	if (!error) {
1421 		// Clear the current lists as
1422 		// we'll be manually building them up
1423 		fRecentDocuments.Clear();
1424 		fRecentFolders.Clear();
1425 		fRecentApps.Clear();
1426 
1427 		// Now we just walk through the file and read in the info
1428 		while (true) {
1429 			status_t streamError;
1430 			char str[B_PATH_NAME_LENGTH];
1431 
1432 
1433 			// (RecentDoc | RecentFolder | RecentApp)
1434 			streamError = stream.GetString(str);
1435 			if (!streamError) {
1436 				enum EntryType {
1437 					etDoc,
1438 					etFolder,
1439 					etApp,
1440 					etSomethingIsAmiss,
1441 				} type;
1442 
1443 				if (strcmp(str, "RecentDoc") == 0) {
1444 					type = etDoc;
1445 				} else if (strcmp(str, "RecentFolder") == 0) {
1446 					type = etFolder;
1447 				} else if (strcmp(str, "RecentApp") == 0) {
1448 					type = etApp;
1449 				} else {
1450 					type = etSomethingIsAmiss;
1451 				}
1452 
1453 				switch (type) {
1454 					case etDoc:
1455 					case etFolder:
1456 					{
1457 						// For curing laziness
1458 						std::list<recent_entry*> *list = (type == etDoc)
1459 						                                 ? &fRecentDocuments.fEntryList
1460 						                                 : &fRecentFolders.fEntryList;
1461 
1462 						char path[B_PATH_NAME_LENGTH];
1463 						char app[B_PATH_NAME_LENGTH];
1464 						char rank[B_PATH_NAME_LENGTH];
1465 						entry_ref ref;
1466 						uint32 index = 0;
1467 
1468 						// Convert the given path to an entry ref
1469 						streamError = stream.GetString(path);
1470 						if (!streamError)
1471 							streamError = get_ref_for_path(path, &ref);
1472 
1473 						// Add a new entry to the list for each application
1474 						// signature and rank we find
1475 						while (!streamError) {
1476 							if (!streamError)
1477 								streamError = stream.GetString(app);
1478 							if (!streamError) {
1479 								BPrivate::Storage::to_lower(app);
1480 								streamError = stream.GetString(rank);
1481 							}
1482 							if (!streamError) {
1483 								index = strtoul(rank, NULL, 10);
1484 								if (index == ULONG_MAX)
1485 									streamError = errno;
1486 							}
1487 							recent_entry *entry = NULL;
1488 							if (!streamError) {
1489 								entry = new(nothrow) recent_entry(&ref, app, index);
1490 								streamError = entry ? B_OK : B_NO_MEMORY;
1491 							}
1492 							if (!streamError) {
1493 								printf("pushing entry, leaf == '%s', app == '%s', index == %ld\n",
1494 								       entry->ref.name, entry->sig.c_str(), entry->index);
1495 
1496 								list->push_back(entry);
1497 							}
1498 						}
1499 
1500 						if (streamError) {
1501 							printf("entry error 0x%lx\n", streamError);
1502 							if (streamError != RosterSettingsCharStream::kEndOfLine
1503 							    && streamError != RosterSettingsCharStream::kEndOfStream)
1504 							stream.SkipLine();
1505 						}
1506 
1507 						break;
1508 					}
1509 
1510 
1511 					case etApp:
1512 					{
1513 						char app[B_PATH_NAME_LENGTH];
1514 						streamError = stream.GetString(app);
1515 						if (!streamError) {
1516 							BPrivate::Storage::to_lower(app);
1517 							fRecentApps.fAppList.push_back(app);
1518 						} else
1519 							stream.SkipLine();
1520 						break;
1521 					}
1522 
1523 					default:
1524 						// Something was amiss; skip to the next line
1525 						stream.SkipLine();
1526 						break;
1527 				}
1528 
1529 			}
1530 
1531 			if (streamError == RosterSettingsCharStream::kEndOfStream)
1532 				break;
1533 		}
1534 
1535 		// Now we must sort our lists of documents and folders by the
1536 		// indicies we read for each entry (largest index first)
1537 		fRecentDocuments.fEntryList.sort(larger_index);
1538 		fRecentFolders.fEntryList.sort(larger_index);
1539 
1540 		printf("----------------------------------------------------------------------\n");
1541 		fRecentDocuments.Print();
1542 		printf("----------------------------------------------------------------------\n");
1543 		fRecentFolders.Print();
1544 		printf("----------------------------------------------------------------------\n");
1545 		fRecentApps.Print();
1546 		printf("----------------------------------------------------------------------\n");
1547 	}
1548 	if (error)
1549 		D(PRINT(("WARNING: TRoster::_LoadRosterSettings(): error loading roster settings "
1550 		         "from '%s', 0x%lx\n", settingsPath, error)));
1551 	return error;
1552 }
1553 
1554 status_t
1555 TRoster::_SaveRosterSettings(const char *path)
1556 {
1557 	const char *settingsPath = path ? path : kDefaultRosterSettingsFile;
1558 
1559 	status_t error;
1560 	FILE* file;
1561 
1562 	file = fopen(settingsPath, "w+");
1563 	error = file ? B_OK : errno;
1564 	if (!error) {
1565 		status_t saveError;
1566 		saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc");
1567 		if (saveError)
1568 			D(PRINT(("TRoster::_SaveRosterSettings(): recent documents save failed "
1569 			         "with error 0x%lx\n", saveError)));
1570 		saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder");
1571 		if (saveError)
1572 			D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed "
1573 			         "with error 0x%lx\n", saveError)));
1574 		saveError = fRecentApps.Save(file);
1575 		if (saveError)
1576 			D(PRINT(("TRoster::_SaveRosterSettings(): recent folders save failed "
1577 			         "with error 0x%lx\n", saveError)));
1578 		fclose(file);
1579 	}
1580 
1581 	return error;
1582 }
1583 
1584 
1585 //------------------------------------------------------------------------------
1586 // Private local functions
1587 //------------------------------------------------------------------------------
1588 
1589 /*! \brief Returns true if entry1's index is larger than entry2's index.
1590 
1591 	Also returns true if either entry is \c NULL.
1592 
1593 	Used for sorting the recent entry lists loaded from disk into the
1594 	proper order.
1595 */
1596 bool
1597 larger_index(const recent_entry *entry1, const recent_entry *entry2)
1598 {
1599 	if (entry1 && entry2)
1600 		return entry1->index > entry2->index;
1601 	else
1602 		return true;
1603 }
1604 
1605