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