1/*
2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Ingo Weinhold, ingo_weinhold@gmx.de
8 */
9
10
11#include <Roster.h>
12
13#include <ctype.h>
14#include <new>
15#include <stdio.h>
16#include <stdlib.h>
17#include <strings.h>
18#include <unistd.h>
19
20#include <AppFileInfo.h>
21#include <Application.h>
22#include <Bitmap.h>
23#include <Directory.h>
24#include <File.h>
25#include <FindDirectory.h>
26#include <fs_index.h>
27#include <fs_info.h>
28#include <image.h>
29#include <List.h>
30#include <Mime.h>
31#include <Node.h>
32#include <NodeInfo.h>
33#include <OS.h>
34#include <Path.h>
35#include <Query.h>
36#include <RegistrarDefs.h>
37#include <String.h>
38#include <Volume.h>
39#include <VolumeRoster.h>
40
41#include <locks.h>
42
43#include <AppMisc.h>
44#include <DesktopLink.h>
45#include <LaunchRoster.h>
46#include <MessengerPrivate.h>
47#include <PortLink.h>
48#include <RosterPrivate.h>
49#include <ServerProtocol.h>
50
51
52using namespace std;
53using namespace BPrivate;
54
55
56// debugging
57//#define DBG(x) x
58#define DBG(x)
59#ifdef DEBUG_PRINTF
60#	define OUT DEBUG_PRINTF
61#else
62#	define OUT printf
63#endif
64
65
66const BRoster* be_roster;
67
68
69//	#pragma mark - Helper functions
70
71
72/*!	Extracts an app_info from a BMessage.
73
74	The function searchs for a field "app_info" typed B_REG_APP_INFO_TYPE
75	and initializes \a info with the found data.
76
77	\param message The message
78	\param info A pointer to a pre-allocated app_info to be filled in with the
79	       info found in the message.
80
81	\return A status code.
82	\retval B_OK Everything went fine.
83	\retval B_BAD_VALUE \c NULL \a message or \a info.
84*/
85static status_t
86find_message_app_info(BMessage* message, app_info* info)
87{
88	status_t error = (message && info ? B_OK : B_BAD_VALUE);
89	const flat_app_info* flatInfo = NULL;
90	ssize_t size = 0;
91	// find the flat app info in the message
92	if (error == B_OK) {
93		error = message->FindData("app_info", B_REG_APP_INFO_TYPE,
94			(const void**)&flatInfo, &size);
95	}
96	// unflatten the flat info
97	if (error == B_OK) {
98		if (size == sizeof(flat_app_info)) {
99			info->thread = flatInfo->thread;
100			info->team = flatInfo->team;
101			info->port = flatInfo->port;
102			info->flags = flatInfo->flags;
103			info->ref.device = flatInfo->ref_device;
104			info->ref.directory = flatInfo->ref_directory;
105			info->ref.name = NULL;
106			memcpy(info->signature, flatInfo->signature, B_MIME_TYPE_LENGTH);
107			if (strlen(flatInfo->ref_name) > 0)
108				info->ref.set_name(flatInfo->ref_name);
109		} else
110			error = B_ERROR;
111	}
112
113	return error;
114}
115
116
117/*!	Checks whether or not an application can be used.
118
119	Currently it is only checked whether the application is in the trash.
120
121	\param ref An entry_ref referring to the application executable.
122
123	\return A status code, \c B_OK on success oir other error codes specifying
124	        why the application cannot be used.
125	\retval B_OK The application can be used.
126	\retval B_ENTRY_NOT_FOUND \a ref doesn't refer to and existing entry.
127	\retval B_IS_A_DIRECTORY \a ref refers to a directory.
128	\retval B_LAUNCH_FAILED_APP_IN_TRASH The application executable is in the
129	        trash.
130*/
131static status_t
132can_app_be_used(const entry_ref* ref)
133{
134	status_t error = (ref ? B_OK : B_BAD_VALUE);
135	// check whether the file exists and is a file.
136	BEntry entry;
137	if (error == B_OK)
138		error = entry.SetTo(ref, true);
139
140	if (error == B_OK && !entry.Exists())
141		error = B_ENTRY_NOT_FOUND;
142
143	if (error == B_OK && !entry.IsFile())
144		error = B_IS_A_DIRECTORY;
145
146	// check whether the file is in trash
147	BPath trashPath;
148	BDirectory directory;
149	BVolume volume;
150	if (error == B_OK
151		&& volume.SetTo(ref->device) == B_OK
152		&& find_directory(B_TRASH_DIRECTORY, &trashPath, false, &volume)
153			== B_OK
154		&& directory.SetTo(trashPath.Path()) == B_OK
155		&& directory.Contains(&entry)) {
156		error = B_LAUNCH_FAILED_APP_IN_TRASH;
157	}
158
159	return error;
160}
161
162
163/*!	Compares the supplied version infos.
164
165	\param info1 The first info.
166	\param info2 The second info.
167
168	\return \c -1, if the first info is less than the second one, \c 1, if
169	        the first one is greater than the second one, and \c 0, if both
170	        are equal.
171*/
172static int32
173compare_version_infos(const version_info& info1, const version_info& info2)
174{
175	int32 result = 0;
176	if (info1.major < info2.major)
177		result = -1;
178	else if (info1.major > info2.major)
179		result = 1;
180	else if (info1.middle < info2.middle)
181		result = -1;
182	else if (info1.middle > info2.middle)
183		result = 1;
184	else if (info1.minor < info2.minor)
185		result = -1;
186	else if (info1.minor > info2.minor)
187		result = 1;
188	else if (info1.variety < info2.variety)
189		result = -1;
190	else if (info1.variety > info2.variety)
191		result = 1;
192	else if (info1.internal < info2.internal)
193		result = -1;
194	else if (info1.internal > info2.internal)
195		result = 1;
196
197	return result;
198}
199
200
201/*!	Compares two applications to decide which one should be rather
202	returned as a query result.
203
204	First, it checks if both apps are in the path, and prefers the app that
205	appears earlier.
206
207	If both files have a version info, then those are compared.
208	If one file has a version info, it is said to be greater. If both
209	files have no version info, their modification times are compared.
210
211	\param app1 An entry_ref referring to the first application.
212	\param app2 An entry_ref referring to the second application.
213	\return \c -1, if the first application version is less than the second
214	        one, \c 1, if the first one is greater than the second one, and
215	        \c 0, if both are equal.
216*/
217static int32
218compare_queried_apps(const entry_ref* app1, const entry_ref* app2)
219{
220	BPath path1(app1);
221	BPath path2(app2);
222
223	// Check search path
224
225	const char* searchPathes = getenv("PATH");
226	if (searchPathes != NULL) {
227		char* searchBuffer = strdup(searchPathes);
228		if (searchBuffer != NULL) {
229			char* last;
230			const char* path = strtok_r(searchBuffer, ":", &last);
231			while (path != NULL) {
232				// Check if any app path matches
233				size_t length = strlen(path);
234				bool found1 = !strncmp(path, path1.Path(), length)
235					&& path1.Path()[length] == '/';
236				bool found2 = !strncmp(path, path2.Path(), length)
237					&& path2.Path()[length] == '/';;
238
239				if (found1 != found2) {
240					free(searchBuffer);
241					return found1 ? 1 : -1;
242				}
243
244				path = strtok_r(NULL, ":", &last);
245			}
246
247			free(searchBuffer);
248		}
249	}
250
251	// Check system servers folder
252	BPath path;
253	find_directory(B_SYSTEM_SERVERS_DIRECTORY, &path);
254	BString serverPath(path.Path());
255	serverPath << '/';
256	size_t length = serverPath.Length();
257
258	bool inSystem1 = !strncmp(serverPath.String(), path1.Path(), length);
259	bool inSystem2 = !strncmp(serverPath.String(), path2.Path(), length);
260	if (inSystem1 != inSystem2)
261		return inSystem1 ? 1 : -1;
262
263	// Check version info
264
265	BFile file1;
266	file1.SetTo(app1, B_READ_ONLY);
267	BFile file2;
268	file2.SetTo(app2, B_READ_ONLY);
269
270	BAppFileInfo appFileInfo1;
271	appFileInfo1.SetTo(&file1);
272	BAppFileInfo appFileInfo2;
273	appFileInfo2.SetTo(&file2);
274
275	time_t modificationTime1 = 0;
276	time_t modificationTime2 = 0;
277
278	file1.GetModificationTime(&modificationTime1);
279	file2.GetModificationTime(&modificationTime2);
280
281	int32 result = 0;
282
283	version_info versionInfo1;
284	version_info versionInfo2;
285	bool hasVersionInfo1 = (appFileInfo1.GetVersionInfo(
286		&versionInfo1, B_APP_VERSION_KIND) == B_OK);
287	bool hasVersionInfo2 = (appFileInfo2.GetVersionInfo(
288		&versionInfo2, B_APP_VERSION_KIND) == B_OK);
289
290	if (hasVersionInfo1) {
291		if (hasVersionInfo2)
292			result = compare_version_infos(versionInfo1, versionInfo2);
293		else
294			result = 1;
295	} else {
296		if (hasVersionInfo2)
297			result = -1;
298		else if (modificationTime1 < modificationTime2)
299			result = -1;
300		else if (modificationTime1 > modificationTime2)
301			result = 1;
302	}
303
304	return result;
305}
306
307
308/*!	Finds an app by signature on any mounted volume.
309
310	\param signature The app's signature.
311	\param appRef A pointer to a pre-allocated entry_ref to be filled with
312	       a reference to the found application's executable.
313
314	\return A status code.
315	\retval B_OK Everything went fine.
316	\retval B_BAD_VALUE: \c NULL \a signature or \a appRef.
317	\retval B_LAUNCH_FAILED_APP_NOT_FOUND: An application with this signature
318	        could not be found.
319*/
320static status_t
321query_for_app(const char* signature, entry_ref* appRef)
322{
323	if (signature == NULL || appRef == NULL)
324		return B_BAD_VALUE;
325
326	status_t error = B_LAUNCH_FAILED_APP_NOT_FOUND;
327	bool caseInsensitive = false;
328
329	while (true) {
330		// search on all volumes
331		BVolumeRoster volumeRoster;
332		BVolume volume;
333		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
334			if (!volume.KnowsQuery())
335				continue;
336
337			index_info info;
338			if (fs_stat_index(volume.Device(), "BEOS:APP_SIG", &info) != 0) {
339				// This volume doesn't seem to have the index we're looking for;
340				// querying it might need a long time, and we don't care *that*
341				// much...
342				continue;
343			}
344
345			BQuery query;
346			query.SetVolume(&volume);
347			query.PushAttr("BEOS:APP_SIG");
348			if (!caseInsensitive)
349				query.PushString(signature);
350			else {
351				// second pass, create a case insensitive query string
352				char string[B_MIME_TYPE_LENGTH * 4];
353				strlcpy(string, "application/", sizeof(string));
354
355				int32 length = strlen(string);
356				const char* from = signature + length;
357				char* to = string + length;
358
359				for (; from[0]; from++) {
360					if (isalpha(from[0])) {
361						*to++ = '[';
362						*to++ = tolower(from[0]);
363						*to++ = toupper(from[0]);
364						*to++ = ']';
365					} else
366						*to++ = from[0];
367				}
368
369				to[0] = '\0';
370				query.PushString(string);
371			}
372			query.PushOp(B_EQ);
373
374			query.Fetch();
375
376			// walk through the query
377			bool appFound = false;
378			status_t foundAppError = B_OK;
379			entry_ref ref;
380			while (query.GetNextRef(&ref) == B_OK) {
381				if ((!appFound || compare_queried_apps(appRef, &ref) < 0)
382					&& (foundAppError = can_app_be_used(&ref)) == B_OK) {
383					*appRef = ref;
384					appFound = true;
385				}
386			}
387			if (!appFound) {
388				// If the query didn't return any hits, the error is
389				// B_LAUNCH_FAILED_APP_NOT_FOUND, otherwise we return the
390				// result of the last can_app_be_used().
391				error = foundAppError != B_OK
392					? foundAppError : B_LAUNCH_FAILED_APP_NOT_FOUND;
393			} else
394				return B_OK;
395		}
396
397		if (!caseInsensitive)
398			caseInsensitive = true;
399		else
400			break;
401	}
402
403	return error;
404}
405
406
407//	#pragma mark - app_info
408
409
410app_info::app_info()
411	:
412	thread(-1),
413	team(-1),
414	port(-1),
415	flags(B_REG_DEFAULT_APP_FLAGS),
416	ref()
417{
418	signature[0] = '\0';
419}
420
421
422app_info::~app_info()
423{
424}
425
426
427//	#pragma mark - BRoster::ArgVector
428
429
430class BRoster::ArgVector {
431public:
432								ArgVector();
433								~ArgVector();
434
435			status_t			Init(int argc, const char* const* args,
436									const entry_ref* appRef,
437									const entry_ref* docRef);
438			void				Unset();
439	inline	int					Count() const { return fArgc; }
440	inline	const char* const*	Args() const { return fArgs; }
441
442private:
443			int					fArgc;
444			const char**		fArgs;
445			BPath				fAppPath;
446			BPath				fDocPath;
447};
448
449
450//!	Creates an uninitialized ArgVector.
451BRoster::ArgVector::ArgVector()
452	:
453	fArgc(0),
454	fArgs(NULL),
455	fAppPath(),
456	fDocPath()
457{
458}
459
460
461//!	Frees all resources associated with the ArgVector.
462BRoster::ArgVector::~ArgVector()
463{
464	Unset();
465}
466
467
468/*!	Initilizes the object according to the supplied parameters.
469
470	If the initialization succeeds, the methods Count() and Args() grant
471	access to the argument count and vector created by this methods.
472	\note The returned vector is valid only as long as the elements of the
473	supplied \a args (if any) are valid and this object is not destroyed.
474	This object retains ownership of the vector returned by Args().
475	In case of error, the value returned by Args() is invalid (or \c NULL).
476
477	The argument vector is created as follows: First element is the path
478	of the entry \a appRef refers to, then follow all elements of \a args
479	and then, if \a args has at least one element and \a docRef can be
480	resolved to a path, the path of the entry \a docRef refers to. That is,
481	if no or an empty \a args vector is supplied, the resulting argument
482	vector contains only one element, the path associated with \a appRef.
483
484	\param argc Specifies the number of elements \a args contains.
485	\param args Argument vector. May be \c NULL.
486	\param appRef entry_ref referring to the entry whose path shall be the
487	       first element of the resulting argument vector.
488	\param docRef entry_ref referring to the entry whose path shall be the
489	       last element of the resulting argument vector. May be \c NULL.
490	\return
491	- \c B_OK: Everything went fine.
492	- \c B_BAD_VALUE: \c NULL \a appRef.
493	- \c B_ENTRY_NOT_FOUND or other file system error codes: \a appRef could
494	  not be resolved to a path.
495	- \c B_NO_MEMORY: Not enough memory to allocate for this operation.
496*/
497status_t
498BRoster::ArgVector::Init(int argc, const char* const* args,
499	const entry_ref* appRef, const entry_ref* docRef)
500{
501	// unset old values
502	Unset();
503	status_t error = appRef ? B_OK : B_BAD_VALUE;
504	// get app path
505	if (error == B_OK)
506		error = fAppPath.SetTo(appRef);
507	// determine number of arguments
508	bool hasDocArg = false;
509	if (error == B_OK) {
510		fArgc = 1;
511		if (argc > 0 && args) {
512			fArgc += argc;
513			if (docRef != NULL && fDocPath.SetTo(docRef) == B_OK) {
514				fArgc++;
515				hasDocArg = true;
516			}
517		}
518		fArgs = new(nothrow) const char*[fArgc + 1];
519			// + 1 for the terminating NULL
520		if (!fArgs)
521			error = B_NO_MEMORY;
522	}
523	// init vector
524	if (error == B_OK) {
525		fArgs[0] = fAppPath.Path();
526		if (argc > 0 && args != NULL) {
527			for (int i = 0; i < argc; i++)
528				fArgs[i + 1] = args[i];
529			if (hasDocArg)
530				fArgs[fArgc - 1] = fDocPath.Path();
531		}
532		// NULL terminate (e.g. required by load_image())
533		fArgs[fArgc] = NULL;
534	}
535	return error;
536}
537
538
539//!	Uninitializes the object.
540void
541BRoster::ArgVector::Unset()
542{
543	fArgc = 0;
544	delete[] fArgs;
545	fArgs = NULL;
546	fAppPath.Unset();
547	fDocPath.Unset();
548}
549
550
551//	#pragma mark - BRoster
552
553
554BRoster::BRoster()
555	:
556	fMessenger(),
557	fMimeMessenger(),
558	fMimeMessengerInitOnce(INIT_ONCE_UNINITIALIZED),
559	fNoRegistrar(false)
560{
561	_InitMessenger();
562}
563
564
565BRoster::~BRoster()
566{
567}
568
569
570//	#pragma mark - Querying for apps
571
572
573bool
574BRoster::IsRunning(const char* signature) const
575{
576	return (TeamFor(signature) >= 0);
577}
578
579
580bool
581BRoster::IsRunning(entry_ref* ref) const
582{
583	return (TeamFor(ref) >= 0);
584}
585
586
587team_id
588BRoster::TeamFor(const char* signature) const
589{
590	team_id team;
591	app_info info;
592	status_t error = GetAppInfo(signature, &info);
593	if (error == B_OK)
594		team = info.team;
595	else
596		team = error;
597
598	return team;
599}
600
601
602team_id
603BRoster::TeamFor(entry_ref* ref) const
604{
605	team_id team;
606	app_info info;
607	status_t error = GetAppInfo(ref, &info);
608	if (error == B_OK)
609		team = info.team;
610	else
611		team = error;
612	return team;
613}
614
615
616void
617BRoster::GetAppList(BList* teamIDList) const
618{
619	status_t error = (teamIDList ? B_OK : B_BAD_VALUE);
620	// compose the request message
621	BMessage request(B_REG_GET_APP_LIST);
622
623	// send the request
624	BMessage reply;
625	if (error == B_OK)
626		error = fMessenger.SendMessage(&request, &reply);
627
628	// evaluate the reply
629	if (error == B_OK) {
630		if (reply.what == B_REG_SUCCESS) {
631			team_id team;
632			for (int32 i = 0; reply.FindInt32("teams", i, &team) == B_OK; i++)
633				teamIDList->AddItem((void*)(addr_t)team);
634		} else {
635			if (reply.FindInt32("error", &error) != B_OK)
636				error = B_ERROR;
637			DBG(OUT("Roster request unsuccessful: %s\n", strerror(error)));
638			DBG(reply.PrintToStream());
639		}
640	} else {
641		DBG(OUT("Sending message to roster failed: %s\n", strerror(error)));
642	}
643}
644
645
646void
647BRoster::GetAppList(const char* signature, BList* teamIDList) const
648{
649	status_t error = B_OK;
650	if (signature == NULL || teamIDList == NULL)
651		error = B_BAD_VALUE;
652
653	// compose the request message
654	BMessage request(B_REG_GET_APP_LIST);
655	if (error == B_OK)
656		error = request.AddString("signature", signature);
657
658	// send the request
659	BMessage reply;
660	if (error == B_OK)
661		error = fMessenger.SendMessage(&request, &reply);
662
663	// evaluate the reply
664	if (error == B_OK) {
665		if (reply.what == B_REG_SUCCESS) {
666			team_id team;
667			for (int32 i = 0; reply.FindInt32("teams", i, &team) == B_OK; i++)
668				teamIDList->AddItem((void*)(addr_t)team);
669		} else if (reply.FindInt32("error", &error) != B_OK)
670			error = B_ERROR;
671	}
672}
673
674
675status_t
676BRoster::GetAppInfo(const char* signature, app_info* info) const
677{
678	status_t error = B_OK;
679	if (signature == NULL || info == NULL)
680		error = B_BAD_VALUE;
681
682	// compose the request message
683	BMessage request(B_REG_GET_APP_INFO);
684	if (error == B_OK)
685		error = request.AddString("signature", signature);
686
687	// send the request
688	BMessage reply;
689	if (error == B_OK)
690		error = fMessenger.SendMessage(&request, &reply);
691
692	// evaluate the reply
693	if (error == B_OK) {
694		if (reply.what == B_REG_SUCCESS)
695			error = find_message_app_info(&reply, info);
696		else if (reply.FindInt32("error", &error) != B_OK)
697			error = B_ERROR;
698	}
699
700	return error;
701}
702
703
704status_t
705BRoster::GetAppInfo(entry_ref* ref, app_info* info) const
706{
707	status_t error = (ref && info ? B_OK : B_BAD_VALUE);
708	// compose the request message
709	BMessage request(B_REG_GET_APP_INFO);
710	if (error == B_OK)
711		error = request.AddRef("ref", ref);
712
713	// send the request
714	BMessage reply;
715	if (error == B_OK)
716		error = fMessenger.SendMessage(&request, &reply);
717
718	// evaluate the reply
719	if (error == B_OK) {
720		if (reply.what == B_REG_SUCCESS)
721			error = find_message_app_info(&reply, info);
722		else if (reply.FindInt32("error", &error) != B_OK)
723			error = B_ERROR;
724	}
725	return error;
726}
727
728
729status_t
730BRoster::GetRunningAppInfo(team_id team, app_info* info) const
731{
732	status_t error = (info ? B_OK : B_BAD_VALUE);
733	if (error == B_OK && team < 0)
734		error = B_BAD_TEAM_ID;
735	// compose the request message
736	BMessage request(B_REG_GET_APP_INFO);
737	if (error == B_OK)
738		error = request.AddInt32("team", team);
739	// send the request
740	BMessage reply;
741	if (error == B_OK)
742		error = fMessenger.SendMessage(&request, &reply);
743
744	// evaluate the reply
745	if (error == B_OK) {
746		if (reply.what == B_REG_SUCCESS)
747			error = find_message_app_info(&reply, info);
748		else if (reply.FindInt32("error", &error) != B_OK)
749			error = B_ERROR;
750	}
751	return error;
752}
753
754
755status_t
756BRoster::GetActiveAppInfo(app_info* info) const
757{
758	if (info == NULL)
759		return B_BAD_VALUE;
760
761	// compose the request message
762	BMessage request(B_REG_GET_APP_INFO);
763	// send the request
764	BMessage reply;
765	status_t error = fMessenger.SendMessage(&request, &reply);
766	// evaluate the reply
767	if (error == B_OK) {
768		if (reply.what == B_REG_SUCCESS)
769			error = find_message_app_info(&reply, info);
770		else if (reply.FindInt32("error", &error) != B_OK)
771			error = B_ERROR;
772	}
773	return error;
774}
775
776
777status_t
778BRoster::FindApp(const char* mimeType, entry_ref* app) const
779{
780	if (mimeType == NULL || app == NULL)
781		return B_BAD_VALUE;
782
783	return _ResolveApp(mimeType, NULL, app, NULL, NULL, NULL);
784}
785
786
787status_t
788BRoster::FindApp(entry_ref* ref, entry_ref* app) const
789{
790	if (ref == NULL || app == NULL)
791		return B_BAD_VALUE;
792
793	entry_ref _ref(*ref);
794	return _ResolveApp(NULL, &_ref, app, NULL, NULL, NULL);
795}
796
797
798//	#pragma mark - Launching, activating, and broadcasting to apps
799
800
801status_t
802BRoster::Broadcast(BMessage* message) const
803{
804	return Broadcast(message, be_app_messenger);
805}
806
807
808status_t
809BRoster::Broadcast(BMessage* message, BMessenger replyTo) const
810{
811	status_t error = (message ? B_OK : B_BAD_VALUE);
812	// compose the request message
813	BMessage request(B_REG_BROADCAST);
814	if (error == B_OK)
815		error = request.AddInt32("team", BPrivate::current_team());
816	if (error == B_OK)
817		error = request.AddMessage("message", message);
818	if (error == B_OK)
819		error = request.AddMessenger("reply_target", replyTo);
820
821	// send the request
822	BMessage reply;
823	if (error == B_OK)
824		error = fMessenger.SendMessage(&request, &reply);
825
826	// evaluate the reply
827	if (error == B_OK && reply.what != B_REG_SUCCESS
828		&& reply.FindInt32("error", &error) != B_OK)
829		error = B_ERROR;
830
831	return error;
832}
833
834
835status_t
836BRoster::StartWatching(BMessenger target, uint32 eventMask) const
837{
838	status_t error = B_OK;
839	// compose the request message
840	BMessage request(B_REG_START_WATCHING);
841	if (error == B_OK)
842		error = request.AddMessenger("target", target);
843	if (error == B_OK)
844		error = request.AddInt32("events", (int32)eventMask);
845
846	// send the request
847	BMessage reply;
848	if (error == B_OK)
849		error = fMessenger.SendMessage(&request, &reply);
850
851	// evaluate the reply
852	if (error == B_OK && reply.what != B_REG_SUCCESS
853		&& reply.FindInt32("error", &error) != B_OK)
854		error = B_ERROR;
855
856	return error;
857}
858
859
860status_t
861BRoster::StopWatching(BMessenger target) const
862{
863	status_t error = B_OK;
864	// compose the request message
865	BMessage request(B_REG_STOP_WATCHING);
866	if (error == B_OK)
867		error = request.AddMessenger("target", target);
868
869	// send the request
870	BMessage reply;
871	if (error == B_OK)
872		error = fMessenger.SendMessage(&request, &reply);
873
874	// evaluate the reply
875	if (error == B_OK && reply.what != B_REG_SUCCESS
876		&& reply.FindInt32("error", &error) != B_OK)
877		error = B_ERROR;
878
879	return error;
880}
881
882
883status_t
884BRoster::ActivateApp(team_id team) const
885{
886	BPrivate::DesktopLink link;
887
888	status_t status = link.InitCheck();
889	if (status < B_OK)
890		return status;
891
892	// prepare the message
893	status_t error = link.StartMessage(AS_ACTIVATE_APP);
894	if (error != B_OK)
895		return error;
896
897	error = link.Attach(link.ReceiverPort());
898	if (error != B_OK)
899		return error;
900
901	error = link.Attach(team);
902	if (error != B_OK)
903		return error;
904
905	// send it
906	status_t code;
907	error = link.FlushWithReply(code);
908	if (error != B_OK)
909		return error;
910
911	return code;
912}
913
914
915status_t
916BRoster::Launch(const char* mimeType, BMessage* initialMessage,
917	team_id* _appTeam) const
918{
919	if (mimeType == NULL)
920		return B_BAD_VALUE;
921
922	BList messageList;
923	if (initialMessage != NULL)
924		messageList.AddItem(initialMessage);
925
926	return _LaunchApp(mimeType, NULL, &messageList, 0, NULL,
927		(const char**)environ, _appTeam, NULL, NULL, NULL, false);
928}
929
930
931status_t
932BRoster::Launch(const char* mimeType, BList* messageList,
933	team_id* _appTeam) const
934{
935	if (mimeType == NULL)
936		return B_BAD_VALUE;
937
938	return _LaunchApp(mimeType, NULL, messageList, 0, NULL,
939		(const char**)environ, _appTeam, NULL, NULL, NULL, false);
940}
941
942
943status_t
944BRoster::Launch(const char* mimeType, int argc, const char* const* args,
945	team_id* _appTeam) const
946{
947	if (mimeType == NULL)
948		return B_BAD_VALUE;
949
950	return _LaunchApp(mimeType, NULL, NULL, argc, args, (const char**)environ,
951		_appTeam, NULL, NULL, NULL, false);
952}
953
954
955status_t
956BRoster::Launch(const entry_ref* ref, const BMessage* initialMessage,
957	team_id* _appTeam) const
958{
959	if (ref == NULL)
960		return B_BAD_VALUE;
961
962	BList messageList;
963	if (initialMessage != NULL)
964		messageList.AddItem(const_cast<BMessage*>(initialMessage));
965
966	return _LaunchApp(NULL, ref, &messageList, 0, NULL, (const char**)environ,
967		_appTeam, NULL, NULL, NULL, false);
968}
969
970
971status_t
972BRoster::Launch(const entry_ref* ref, const BList* messageList,
973	team_id* appTeam) const
974{
975	if (ref == NULL)
976		return B_BAD_VALUE;
977
978	return _LaunchApp(NULL, ref, messageList, 0, NULL, (const char**)environ,
979		appTeam, NULL, NULL, NULL, false);
980}
981
982
983status_t
984BRoster::Launch(const entry_ref* ref, int argc, const char* const* args,
985	team_id* appTeam) const
986{
987	if (ref == NULL)
988		return B_BAD_VALUE;
989
990	return _LaunchApp(NULL, ref, NULL, argc, args, (const char**)environ,
991		appTeam, NULL, NULL, NULL, false);
992}
993
994
995#if __GNUC__ == 2
996// #pragma mark - Binary compatibility
997
998
999extern "C" status_t
1000Launch__C7BRosterP9entry_refP8BMessagePl(BRoster* roster, entry_ref* ref,
1001	BMessage* initialMessage)
1002{
1003	return roster->BRoster::Launch(ref, initialMessage, NULL);
1004}
1005
1006
1007extern "C" status_t
1008Launch__C7BRosterPCciPPcPl(BRoster* roster, const char* mimeType,
1009	int argc, char** args, team_id* _appTeam)
1010{
1011	return roster->BRoster::Launch(mimeType, argc, args, _appTeam);
1012}
1013
1014
1015extern "C" status_t
1016Launch__C7BRosterP9entry_refiPPcPl(BRoster* roster, entry_ref* ref,
1017	int argc, char* const* args, team_id* _appTeam)
1018{
1019	return roster->BRoster::Launch(ref, argc, args, _appTeam);
1020}
1021#endif	// __GNUC__ == 2
1022
1023
1024//	#pragma mark - Recent document and app support
1025
1026
1027void
1028BRoster::GetRecentDocuments(BMessage* refList, int32 maxCount,
1029	const char* fileType, const char* signature) const
1030{
1031	if (refList == NULL)
1032		return;
1033
1034	status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE;
1035
1036	// Use the message we've been given for both request and reply
1037	BMessage& message = *refList;
1038	BMessage& reply = *refList;
1039	status_t result;
1040
1041	// Build and send the message, read the reply
1042	if (error == B_OK) {
1043		message.what = B_REG_GET_RECENT_DOCUMENTS;
1044		error = message.AddInt32("max count", maxCount);
1045	}
1046	if (error == B_OK && fileType)
1047		error = message.AddString("file type", fileType);
1048
1049	if (error == B_OK && signature)
1050		error = message.AddString("app sig", signature);
1051
1052	fMessenger.SendMessage(&message, &reply);
1053	if (error == B_OK) {
1054		error = reply.what == B_REG_RESULT
1055			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1056	}
1057
1058	if (error == B_OK)
1059		error = reply.FindInt32("result", &result);
1060
1061	if (error == B_OK)
1062		error = result;
1063
1064	// Clear the result if an error occured
1065	if (error != B_OK && refList != NULL)
1066		refList->MakeEmpty();
1067
1068	// No return value, how sad :-(
1069	//return error;
1070}
1071
1072
1073void
1074BRoster::GetRecentDocuments(BMessage* refList, int32 maxCount,
1075	const char* fileTypes[], int32 fileTypesCount,
1076	const char* signature) const
1077{
1078	if (refList == NULL)
1079		return;
1080
1081	status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE;
1082
1083	// Use the message we've been given for both request and reply
1084	BMessage& message = *refList;
1085	BMessage& reply = *refList;
1086	status_t result;
1087
1088	// Build and send the message, read the reply
1089	if (error == B_OK) {
1090		message.what = B_REG_GET_RECENT_DOCUMENTS;
1091		error = message.AddInt32("max count", maxCount);
1092	}
1093	if (error == B_OK && fileTypes) {
1094		for (int i = 0; i < fileTypesCount && error == B_OK; i++)
1095			error = message.AddString("file type", fileTypes[i]);
1096	}
1097	if (error == B_OK && signature)
1098		error = message.AddString("app sig", signature);
1099
1100	fMessenger.SendMessage(&message, &reply);
1101	if (error == B_OK) {
1102		error = reply.what == B_REG_RESULT
1103			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1104	}
1105	if (error == B_OK)
1106		error = reply.FindInt32("result", &result);
1107
1108	if (error == B_OK)
1109		error = result;
1110
1111	// Clear the result if an error occured
1112	if (error != B_OK && refList != NULL)
1113		refList->MakeEmpty();
1114
1115	// No return value, how sad :-(
1116	//return error;
1117}
1118
1119
1120void
1121BRoster::GetRecentFolders(BMessage* refList, int32 maxCount,
1122	const char* signature) const
1123{
1124	if (refList == NULL)
1125		return;
1126
1127	status_t error = maxCount > 0 ? B_OK : B_BAD_VALUE;
1128
1129	// Use the message we've been given for both request and reply
1130	BMessage& message = *refList;
1131	BMessage& reply = *refList;
1132	status_t result;
1133
1134	// Build and send the message, read the reply
1135	if (error == B_OK) {
1136		message.what = B_REG_GET_RECENT_FOLDERS;
1137		error = message.AddInt32("max count", maxCount);
1138	}
1139	if (error == B_OK && signature)
1140		error = message.AddString("app sig", signature);
1141
1142	fMessenger.SendMessage(&message, &reply);
1143	if (error == B_OK) {
1144		error = reply.what == B_REG_RESULT
1145			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1146	}
1147
1148	if (error == B_OK)
1149		error = reply.FindInt32("result", &result);
1150
1151	if (error == B_OK)
1152		error = result;
1153
1154	// Clear the result if an error occured
1155	if (error != B_OK && refList != NULL)
1156		refList->MakeEmpty();
1157
1158	// No return value, how sad :-(
1159	//return error;
1160}
1161
1162
1163void
1164BRoster::GetRecentApps(BMessage* refList, int32 maxCount) const
1165{
1166	if (refList == NULL)
1167		return;
1168
1169	status_t err = maxCount > 0 ? B_OK : B_BAD_VALUE;
1170
1171	// Use the message we've been given for both request and reply
1172	BMessage& message = *refList;
1173	BMessage& reply = *refList;
1174	status_t result;
1175
1176	// Build and send the message, read the reply
1177	if (!err) {
1178		message.what = B_REG_GET_RECENT_APPS;
1179		err = message.AddInt32("max count", maxCount);
1180	}
1181	fMessenger.SendMessage(&message, &reply);
1182	if (!err) {
1183		err = reply.what == B_REG_RESULT
1184			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1185	}
1186	if (!err)
1187		err = reply.FindInt32("result", &result);
1188
1189	if (!err)
1190		err = result;
1191
1192	// Clear the result if an error occured
1193	if (err && refList)
1194		refList->MakeEmpty();
1195
1196	// No return value, how sad :-(
1197	//return err;
1198}
1199
1200
1201void
1202BRoster::AddToRecentDocuments(const entry_ref* document,
1203	const char* signature) const
1204{
1205	status_t error = document ? B_OK : B_BAD_VALUE;
1206
1207	// Use the message we've been given for both request and reply
1208	BMessage message(B_REG_ADD_TO_RECENT_DOCUMENTS);
1209	BMessage reply;
1210	status_t result;
1211	char* callingApplicationSignature = NULL;
1212
1213	// If no signature is supplied, look up the signature of
1214	// the calling app
1215	if (error == B_OK && signature == NULL) {
1216		app_info info;
1217		error = GetRunningAppInfo(be_app->Team(), &info);
1218		if (error == B_OK)
1219			callingApplicationSignature = info.signature;
1220	}
1221
1222	// Build and send the message, read the reply
1223	if (error == B_OK)
1224		error = message.AddRef("ref", document);
1225
1226	if (error == B_OK) {
1227		error = message.AddString("app sig", signature != NULL
1228			? signature : callingApplicationSignature);
1229	}
1230	fMessenger.SendMessage(&message, &reply);
1231	if (error == B_OK) {
1232		error = reply.what == B_REG_RESULT
1233			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1234	}
1235	if (error == B_OK)
1236		error = reply.FindInt32("result", &result);
1237
1238	if (error == B_OK)
1239		error = result;
1240
1241	if (error != B_OK) {
1242		DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error "
1243			"0x%" B_PRIx32 "\n", error));
1244	}
1245}
1246
1247
1248void
1249BRoster::AddToRecentFolders(const entry_ref* folder,
1250	const char* signature) const
1251{
1252	status_t error = folder ? B_OK : B_BAD_VALUE;
1253
1254	// Use the message we've been given for both request and reply
1255	BMessage message(B_REG_ADD_TO_RECENT_FOLDERS);
1256	BMessage reply;
1257	status_t result;
1258	char* callingApplicationSignature = NULL;
1259
1260	// If no signature is supplied, look up the signature of
1261	// the calling app
1262	if (error == B_OK && signature == NULL) {
1263		app_info info;
1264		error = GetRunningAppInfo(be_app->Team(), &info);
1265		if (error == B_OK)
1266			callingApplicationSignature = info.signature;
1267	}
1268
1269	// Build and send the message, read the reply
1270	if (error == B_OK)
1271		error = message.AddRef("ref", folder);
1272
1273	if (error == B_OK) {
1274		error = message.AddString("app sig",
1275			signature != NULL ? signature : callingApplicationSignature);
1276	}
1277	fMessenger.SendMessage(&message, &reply);
1278	if (error == B_OK) {
1279		error = reply.what == B_REG_RESULT
1280			? (status_t)B_OK : (status_t)B_BAD_REPLY;
1281	}
1282	if (error == B_OK)
1283		error = reply.FindInt32("result", &result);
1284
1285	if (error == B_OK)
1286		error = result;
1287
1288	if (error != B_OK) {
1289		DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error "
1290			"0x%" B_PRIx32 "\n", error));
1291	}
1292}
1293
1294//	#pragma mark - Private or reserved
1295
1296
1297/*!	Shuts down the system.
1298
1299	When \c synchronous is \c true and the method succeeds, it doesn't return.
1300
1301	\param reboot If \c true, the system will be rebooted instead of being
1302	       powered off.
1303	\param confirm If \c true, the user will be asked to confirm to shut down
1304	       the system.
1305	\param synchronous If \c false, the method will return as soon as the
1306	       shutdown process has been initiated successfully (or an error
1307	       occurred). Otherwise the method doesn't return, if successfully.
1308
1309	\return A status code, \c B_OK on success or another error code in case
1310	        something went wrong.
1311	\retval B_SHUTTING_DOWN, when there's already a shutdown process in
1312	        progress,
1313	\retval B_SHUTDOWN_CANCELLED, when the user cancelled the shutdown process,
1314*/
1315status_t
1316BRoster::_ShutDown(bool reboot, bool confirm, bool synchronous)
1317{
1318	status_t error = B_OK;
1319
1320	// compose the request message
1321	BMessage request(B_REG_SHUT_DOWN);
1322	if (error == B_OK)
1323		error = request.AddBool("reboot", reboot);
1324
1325	if (error == B_OK)
1326		error = request.AddBool("confirm", confirm);
1327
1328	if (error == B_OK)
1329		error = request.AddBool("synchronous", synchronous);
1330
1331	// send the request
1332	BMessage reply;
1333	if (error == B_OK)
1334		error = fMessenger.SendMessage(&request, &reply);
1335
1336	// evaluate the reply
1337	if (error == B_OK && reply.what != B_REG_SUCCESS
1338		&& reply.FindInt32("error", &error) != B_OK) {
1339		error = B_ERROR;
1340	}
1341
1342	return error;
1343}
1344
1345
1346/*!	Checks whether a shutdown process is in progress.
1347
1348	\param inProgress: Pointer to a pre-allocated bool to be filled in
1349	       by this method, indicating whether or not a shutdown process
1350	       is in progress.
1351	\return A status code, \c B_OK on success or another error code in case
1352	        something went wrong.
1353*/
1354status_t
1355BRoster::_IsShutDownInProgress(bool* inProgress)
1356{
1357	status_t error = B_OK;
1358
1359	// compose the request message
1360	BMessage request(B_REG_IS_SHUT_DOWN_IN_PROGRESS);
1361
1362	// send the request
1363	BMessage reply;
1364	if (error == B_OK)
1365		error = fMessenger.SendMessage(&request, &reply);
1366
1367	// evaluate the reply
1368	if (error == B_OK) {
1369		if (reply.what == B_REG_SUCCESS) {
1370			if (inProgress != NULL
1371				&& reply.FindBool("in-progress", inProgress) != B_OK) {
1372				error = B_ERROR;
1373			}
1374		} else if (reply.FindInt32("error", &error) != B_OK)
1375			error = B_ERROR;
1376	}
1377
1378	return error;
1379}
1380
1381
1382
1383/*!	(Pre-)Registers an application with the registrar.
1384
1385	This methods is invoked either to register or to pre-register an
1386	application. Full registration is requested by supplying \c true via
1387	\a fullRegistration.
1388
1389	A full registration requires \a signature, \a ref, \a flags, \a team,
1390	\a thread and \a port to contain valid values. No token will be return
1391	via \a pToken.
1392
1393	For a pre-registration \a signature, \a ref, \a flags must be valid.
1394	\a team and \a thread are optional and should be set to -1, if they are
1395	unknown. If no team ID is supplied, \a pToken should be valid and, if the
1396	the pre-registration succeeds, will be filled with a unique token assigned
1397	by the roster.
1398
1399	In both cases the registration may fail, if single/exclusive launch is
1400	requested and an instance of the application is already running. Then
1401	\c B_ALREADY_RUNNING is returned and the team ID of the running instance
1402	is passed back via \a otherTeam, if supplied.
1403
1404	\param signature The application's signature
1405	\param ref An entry_ref referring to the app's executable
1406	\param flags The application's flags
1407	\param team The application's team ID
1408	\param thread The application's main thread
1409	\param port The application's looper port
1410	\param fullRegistration \c true for full, \c false for pre-registration
1411	\param pToken A pointer to a pre-allocated uint32 into which the token
1412	       assigned by the registrar is written (may be \c NULL)
1413	\param otherTeam A pointer to a pre-allocated team_id into which the
1414	       team ID of the already running instance of a single/exclusive
1415	       launch application is written (may be \c NULL)
1416
1417	\return A status code
1418	\retval B_OK Everything went fine.
1419	\retval B_ENTRY_NOT_FOUND \a ref didn't refer to a file.
1420	\retval B_ALREADY_RUNNING The application requested a single/exclusive
1421	        launch and an instance was already running.
1422	\retval B_REG_ALREADY_REGISTERED An application with the team ID \a team
1423	        was already registered.
1424*/
1425status_t
1426BRoster::_AddApplication(const char* signature, const entry_ref* ref,
1427	uint32 flags, team_id team, thread_id thread, port_id port,
1428	bool fullRegistration, uint32* pToken, team_id* otherTeam) const
1429{
1430	status_t error = B_OK;
1431
1432	// compose the request message
1433	BMessage request(B_REG_ADD_APP);
1434	if (error == B_OK && signature != NULL)
1435		error = request.AddString("signature", signature);
1436
1437	if (error == B_OK && ref != NULL)
1438		error = request.AddRef("ref", ref);
1439
1440	if (error == B_OK)
1441		error = request.AddInt32("flags", (int32)flags);
1442
1443	if (error == B_OK && team >= 0)
1444		error = request.AddInt32("team", team);
1445
1446	if (error == B_OK && thread >= 0)
1447		error = request.AddInt32("thread", thread);
1448
1449	if (error == B_OK && port >= 0)
1450		error = request.AddInt32("port", port);
1451
1452	if (error == B_OK)
1453		error = request.AddBool("full_registration", fullRegistration);
1454
1455	// send the request
1456	BMessage reply;
1457	if (error == B_OK)
1458		error = fMessenger.SendMessage(&request, &reply);
1459
1460	// evaluate the reply
1461	if (error == B_OK) {
1462		if (reply.what == B_REG_SUCCESS) {
1463			if (!fullRegistration && team < 0) {
1464				uint32 token;
1465				if (reply.FindInt32("token", (int32*)&token) == B_OK) {
1466					if (pToken != NULL)
1467						*pToken = token;
1468				} else
1469					error = B_ERROR;
1470			}
1471		} else {
1472			if (reply.FindInt32("error", &error) != B_OK)
1473				error = B_ERROR;
1474
1475			// get team and token from the reply
1476			if (otherTeam != NULL
1477				&& reply.FindInt32("other_team", otherTeam) != B_OK) {
1478				*otherTeam = -1;
1479			}
1480			if (pToken != NULL
1481				&& reply.FindInt32("token", (int32*)pToken) != B_OK) {
1482				*pToken = 0;
1483			}
1484		}
1485	}
1486
1487	return error;
1488}
1489
1490
1491/*!	Sets an application's signature.
1492
1493	The application must be registered or at pre-registered with a valid
1494	team ID.
1495
1496	\param team The app's team ID.
1497	\param signature The app's new signature.
1498
1499	\return A status code.
1500	\retval B_OK Everything went fine.
1501	\retval B_REG_APP_NOT_REGISTERED The supplied team ID did not identify a
1502	        registered application.
1503*/
1504status_t
1505BRoster::_SetSignature(team_id team, const char* signature) const
1506{
1507	status_t error = B_OK;
1508
1509	// compose the request message
1510	BMessage request(B_REG_SET_SIGNATURE);
1511	if (team >= 0)
1512		error = request.AddInt32("team", team);
1513
1514	if (error == B_OK && signature)
1515		error = request.AddString("signature", signature);
1516
1517	// send the request
1518	BMessage reply;
1519	if (error == B_OK)
1520		error = fMessenger.SendMessage(&request, &reply);
1521
1522	// evaluate the reply
1523	if (error == B_OK && reply.what != B_REG_SUCCESS
1524		&& reply.FindInt32("error", &error) != B_OK) {
1525		error = B_ERROR;
1526	}
1527
1528	return error;
1529}
1530
1531
1532//!	\todo Really needed?
1533void
1534BRoster::_SetThread(team_id team, thread_id thread) const
1535{
1536}
1537
1538
1539/*!	Sets the team and thread IDs of a pre-registered application.
1540
1541	After an application has been pre-registered via AddApplication(), without
1542	supplying a team ID, the team and thread IDs have to be set using this
1543	method.
1544
1545	\param entryToken The token identifying the application (returned by
1546	       AddApplication())
1547	\param thread The app's thread ID
1548	\param team The app's team ID
1549
1550	\return A status code.
1551	\retval B_OK Everything went fine.
1552	\retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify a
1553	        pre-registered application.
1554*/
1555status_t
1556BRoster::_SetThreadAndTeam(uint32 entryToken, thread_id thread,
1557	team_id team, port_id* _port) const
1558{
1559	status_t error = B_OK;
1560
1561	// compose the request message
1562	BMessage request(B_REG_SET_THREAD_AND_TEAM);
1563	if (error == B_OK)
1564		error = request.AddInt32("token", (int32)entryToken);
1565
1566	if (error == B_OK && team >= 0)
1567		error = request.AddInt32("team", team);
1568
1569	if (error == B_OK && thread >= 0)
1570		error = request.AddInt32("thread", thread);
1571
1572	// send the request
1573	BMessage reply;
1574	if (error == B_OK)
1575		error = fMessenger.SendMessage(&request, &reply);
1576
1577	// evaluate the reply
1578	if (error == B_OK && reply.what != B_REG_SUCCESS
1579		&& reply.FindInt32("error", &error) != B_OK)
1580		error = B_ERROR;
1581
1582	if (error == B_OK && _port != NULL)
1583		*_port = reply.GetInt32("port", -1);
1584
1585	return error;
1586}
1587
1588
1589/*!	Completes the registration process for a pre-registered application.
1590
1591	After an application has been pre-registered via AddApplication() and
1592	after assigning it a team ID (via SetThreadAndTeam()) the application is
1593	still pre-registered and must complete the registration.
1594
1595	\param team The app's team ID
1596	\param thread The app's thread ID
1597	\param thread The app looper port
1598
1599	\return A status code.
1600	\retval B_OK Everything went fine.
1601	\retval B_REG_APP_NOT_PRE_REGISTERED \a team did not identify an existing
1602	        application or the identified application was already fully
1603	        registered.
1604*/
1605status_t
1606BRoster::_CompleteRegistration(team_id team, thread_id thread,
1607	port_id port) const
1608{
1609	status_t error = B_OK;
1610
1611	// compose the request message
1612	BMessage request(B_REG_COMPLETE_REGISTRATION);
1613	if (team >= 0)
1614		error = request.AddInt32("team", team);
1615
1616	if (error == B_OK && thread >= 0)
1617		error = request.AddInt32("thread", thread);
1618
1619	if (error == B_OK && port >= 0)
1620		error = request.AddInt32("port", port);
1621
1622	// send the request
1623	BMessage reply;
1624	if (error == B_OK)
1625		error = fMessenger.SendMessage(&request, &reply);
1626
1627	// evaluate the reply
1628	if (error == B_OK && reply.what != B_REG_SUCCESS
1629		&& reply.FindInt32("error", &error) != B_OK) {
1630		error = B_ERROR;
1631	}
1632
1633	return error;
1634}
1635
1636
1637/*!	Returns whether an application is registered.
1638
1639	If the application is indeed pre-registered and \a info is not \c NULL,
1640	the methods fills in the app_info structure pointed to by \a info.
1641
1642	\param ref An entry_ref referring to the app's executable
1643	\param team The app's team ID. May be -1, if \a token is given.
1644	\param token The app's pre-registration token. May be 0, if \a team is
1645	       given.
1646	\param preRegistered: Pointer to a pre-allocated bool to be filled in
1647	       by this method, indicating whether or not the app was
1648	       pre-registered.
1649	\param info A pointer to a pre-allocated app_info structure to be filled
1650	       in by this method (may be \c NULL)
1651
1652	\return \c B_OK, if the application is registered and all requested
1653	        information could be retrieved, or another error code, if the app
1654	        is not registered or an error occurred.
1655*/
1656status_t
1657BRoster::_IsAppRegistered(const entry_ref* ref, team_id team,
1658	uint32 token, bool* preRegistered, app_info* info) const
1659{
1660	status_t error = B_OK;
1661
1662	// compose the request message
1663	BMessage request(B_REG_IS_APP_REGISTERED);
1664	if (ref)
1665		error = request.AddRef("ref", ref);
1666	if (error == B_OK && team >= 0)
1667		error = request.AddInt32("team", team);
1668	if (error == B_OK && token > 0)
1669		error = request.AddInt32("token", (int32)token);
1670
1671	// send the request
1672	BMessage reply;
1673	if (error == B_OK)
1674		error = fMessenger.SendMessage(&request, &reply);
1675
1676	// evaluate the reply
1677	bool isRegistered = false;
1678	bool isPreRegistered = false;
1679	if (error == B_OK) {
1680		if (reply.what == B_REG_SUCCESS) {
1681			if (reply.FindBool("registered", &isRegistered) != B_OK
1682				|| !isRegistered
1683				|| reply.FindBool("pre-registered", &isPreRegistered) != B_OK) {
1684				error = B_ERROR;
1685			}
1686
1687			if (error == B_OK && preRegistered)
1688				*preRegistered = isPreRegistered;
1689			if (error == B_OK && info)
1690				error = find_message_app_info(&reply, info);
1691		} else if (reply.FindInt32("error", &error) != B_OK)
1692			error = B_ERROR;
1693	}
1694
1695	return error;
1696}
1697
1698
1699/*!	Completely unregisters a pre-registered application.
1700
1701	This method can only be used to unregister applications that don't have
1702	a team ID assigned yet. All other applications must be unregistered via
1703	RemoveApp().
1704
1705	\param entryToken The token identifying the application (returned by
1706	       AddApplication())
1707
1708	\return A status code.
1709	\retval B_OK Everything went fine.
1710	\retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify
1711	        a pre-registered application.
1712*/
1713status_t
1714BRoster::_RemovePreRegApp(uint32 entryToken) const
1715{
1716	status_t error = B_OK;
1717
1718	// compose the request message
1719	BMessage request(B_REG_REMOVE_PRE_REGISTERED_APP);
1720	if (error == B_OK)
1721		error = request.AddInt32("token", (int32)entryToken);
1722
1723	// send the request
1724	BMessage reply;
1725	if (error == B_OK)
1726		error = fMessenger.SendMessage(&request, &reply);
1727
1728	// evaluate the reply
1729	if (error == B_OK && reply.what != B_REG_SUCCESS
1730		&& reply.FindInt32("error", &error) != B_OK) {
1731		error = B_ERROR;
1732	}
1733
1734	return error;
1735}
1736
1737
1738/*!	Unregisters a (pre-)registered application.
1739
1740	This method must be used to unregister applications that already have
1741	a team ID assigned, i.e. also for pre-registered application for which
1742	SetThreadAndTeam() has already been invoked.
1743
1744	\param team The app's team ID
1745
1746	\return A status code.
1747	\retval B_OK Everything went fine.
1748	\retval B_REG_APP_NOT_REGISTERED The supplied team ID does not identify a
1749	        (pre-)registered application.
1750*/
1751status_t
1752BRoster::_RemoveApp(team_id team) const
1753{
1754	status_t error = B_OK;
1755
1756	// compose the request message
1757	BMessage request(B_REG_REMOVE_APP);
1758	if (team >= 0)
1759		error = request.AddInt32("team", team);
1760
1761	// send the request
1762	BMessage reply;
1763	if (error == B_OK)
1764		error = fMessenger.SendMessage(&request, &reply);
1765
1766	// evaluate the reply
1767	if (error == B_OK && reply.what != B_REG_SUCCESS
1768		&& reply.FindInt32("error", &error) != B_OK) {
1769		error = B_ERROR;
1770	}
1771
1772	return error;
1773}
1774
1775
1776void
1777BRoster::_ApplicationCrashed(team_id team)
1778{
1779	BPrivate::DesktopLink link;
1780	if (link.InitCheck() != B_OK)
1781		return;
1782
1783	if (link.StartMessage(AS_APP_CRASHED) == B_OK
1784		&& link.Attach(team) == B_OK) {
1785		link.Flush();
1786	}
1787}
1788
1789
1790/*!	Tells the registrar which application is currently active.
1791
1792	It's called from within the app_server when the active application is
1793	changed.
1794
1795	As it's called in the event loop, it must run asynchronously and cannot
1796	wait for a reply.
1797*/
1798status_t
1799BRoster::_UpdateActiveApp(team_id team) const
1800{
1801	if (team < B_OK)
1802		return B_BAD_TEAM_ID;
1803
1804	// compose the request message
1805	BMessage request(B_REG_UPDATE_ACTIVE_APP);
1806	status_t status = request.AddInt32("team", team);
1807	if (status < B_OK)
1808		return status;
1809
1810	// send the request
1811	return fMessenger.SendMessage(&request);
1812}
1813
1814
1815/*!	Launches the application associated with the supplied MIME type or
1816	the entry referred to by the supplied entry_ref.
1817
1818	The application to be started is searched the same way FindApp() does it.
1819
1820	At least one of \a mimeType or \a ref must not be \c NULL. If \a mimeType
1821	is supplied, \a ref is ignored for finding the application.
1822
1823	If \a ref does refer to an application executable, that application is
1824	launched. Otherwise the respective application is searched and launched,
1825	and \a ref is sent to it in a \c B_REFS_RECEIVED message, unless other
1826	arguments are passed via \a argc and \a args -- then the entry_ref is
1827	converted into a path (C-string) and added to the argument vector.
1828
1829	\a messageList contains messages to be sent to the application
1830	"on launch", i.e. before ReadyToRun() is invoked on the BApplication
1831	object. The caller retains ownership of the supplied BList and the
1832	contained BMessages. In case the method fails with \c B_ALREADY_RUNNING
1833	the messages are delivered to the already running instance. The same
1834	applies to the \c B_REFS_RECEIVED message.
1835
1836	The supplied \a argc and \a args are (if containing at least one argument)
1837	put into a \c B_ARGV_RECEIVED message and sent to the launched application
1838	"on launch". The caller retains ownership of the supplied \a args.
1839	In case the method fails with \c B_ALREADY_RUNNING the message is
1840	delivered to the already running instance. The same applies to the
1841	\c B_REFS_RECEIVED message, if no arguments are supplied via \a argc and
1842	\args.
1843
1844	If \a launchSuspended is set to true, the main thread of the loaded app
1845	(returned in \a appThread) is kept in the suspended state and not
1846	automatically resumed.
1847
1848	\param mimeType MIME type for which the application shall be launched.
1849	       May be \c NULL.
1850	\param ref entry_ref referring to the file for which an application shall
1851	       be launched. May be \c NULL.
1852	\param messageList Optional list of messages to be sent to the application
1853	       "on launch". May be \c NULL.
1854	\param argc Specifies the number of elements in \a args.
1855	\param args An array of C-strings to be sent as B_ARGV_RECEIVED messaged
1856	       to the launched application.
1857	\param appTeam Pointer to a pre-allocated team_id variable to be set to
1858	       the team ID of the launched application.
1859	\param appThread Pointer to a pre-allocated thread_id variable to
1860		   be set to the thread ID of the launched main thread.
1861	\param launchSuspended Indicates whether to keep the app thread in the
1862		   suspended state or resume it.
1863
1864	\return A status code.
1865	\retval B_OK Everything went fine.
1866	\retval B_BAD_VALUE \c NULL \a mimeType
1867	\retval B_LAUNCH_FAILED_NO_PREFERRED_APP Neither with the supplied type
1868	        nor with its supertype (if the supplied isn't a supertype itself)
1869	        a preferred application is associated.
1870	\retval B_LAUNCH_FAILED_APP_NOT_FOUND The supplied type is not installed
1871	        or its preferred application could not be found.
1872	\retval B_LAUNCH_FAILED_APP_IN_TRASH The supplied type's preferred
1873	        application was in the trash.
1874	\retval B_LAUNCH_FAILED_EXECUTABLE The found application was not
1875	        executable.
1876*/
1877status_t
1878BRoster::_LaunchApp(const char* mimeType, const entry_ref* ref,
1879	const BList* messageList, int argc, const char* const* args,
1880	const char** environment, team_id* _appTeam, thread_id* _appThread,
1881	port_id* _appPort, uint32* _appToken, bool launchSuspended) const
1882{
1883	DBG(OUT("BRoster::_LaunchApp()"));
1884
1885	if (_appTeam != NULL) {
1886		// we're supposed to set _appTeam to -1 on error; we'll
1887		// reset it later if everything goes well
1888		*_appTeam = -1;
1889	}
1890
1891	if (mimeType == NULL && ref == NULL)
1892		return B_BAD_VALUE;
1893
1894	// use a mutable copy of the document entry_ref
1895	entry_ref _docRef;
1896	entry_ref* docRef = NULL;
1897	if (ref != NULL) {
1898		_docRef = *ref;
1899		docRef = &_docRef;
1900	}
1901
1902	uint32 otherAppFlags = B_REG_DEFAULT_APP_FLAGS;
1903	uint32 appFlags = B_REG_DEFAULT_APP_FLAGS;
1904	bool alreadyRunning = false;
1905	bool wasDocument = true;
1906	status_t error = B_OK;
1907	ArgVector argVector;
1908	team_id team = -1;
1909	thread_id appThread = -1;
1910	port_id appPort = -1;
1911	uint32 appToken = 0;
1912	entry_ref hintRef;
1913
1914	while (true) {
1915		// find the app
1916		entry_ref appRef;
1917		char signature[B_MIME_TYPE_LENGTH];
1918		error = _ResolveApp(mimeType, docRef, &appRef, signature,
1919			&appFlags, &wasDocument);
1920		DBG(OUT("  find app: %s (%" B_PRIx32 ") %s \n", strerror(error), error,
1921			signature));
1922
1923		if (error != B_OK)
1924			return error;
1925
1926		// build an argument vector
1927		error = argVector.Init(argc, args, &appRef,
1928			wasDocument ? docRef : NULL);
1929		DBG(OUT("  build argv: %s (%" B_PRIx32 ")\n", strerror(error), error));
1930		if (error != B_OK)
1931			return error;
1932
1933		// pre-register the app (but ignore scipts)
1934		app_info appInfo;
1935		bool isScript = wasDocument && docRef != NULL && *docRef == appRef;
1936		if (!isScript && !fNoRegistrar) {
1937			error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1,
1938				false, &appToken, &team);
1939			if (error == B_ALREADY_RUNNING) {
1940				DBG(OUT("  already running\n"));
1941				alreadyRunning = true;
1942
1943				// get the app flags for the running application
1944				error = _IsAppRegistered(&appRef, team, appToken, NULL,
1945					&appInfo);
1946				if (error == B_OK) {
1947					otherAppFlags = appInfo.flags;
1948					appPort = appInfo.port;
1949					team = appInfo.team;
1950				}
1951			}
1952			DBG(OUT("  pre-register: %s (%" B_PRIx32 ")\n", strerror(error),
1953				error));
1954		}
1955
1956		// launch the app
1957		if (error == B_OK && !alreadyRunning) {
1958			DBG(OUT("  token: %" B_PRIu32 "\n", appToken));
1959			// load the app image
1960			appThread = load_image(argVector.Count(),
1961				const_cast<const char**>(argVector.Args()), environment);
1962
1963			// get the app team
1964			if (appThread >= 0) {
1965				thread_info threadInfo;
1966				error = get_thread_info(appThread, &threadInfo);
1967				if (error == B_OK)
1968					team = threadInfo.team;
1969			} else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE)
1970				error = B_LAUNCH_FAILED_EXECUTABLE;
1971			else
1972				error = appThread;
1973
1974			DBG(OUT("  load image: %s (%" B_PRIx32 ")\n", strerror(error),
1975				error));
1976			// finish the registration
1977			if (error == B_OK && !isScript && !fNoRegistrar)
1978				error = _SetThreadAndTeam(appToken, appThread, team, &appPort);
1979
1980			DBG(OUT("  set thread and team: %s (%" B_PRIx32 ")\n",
1981				strerror(error), error));
1982			// resume the launched team
1983			if (error == B_OK && !launchSuspended)
1984				error = resume_thread(appThread);
1985
1986			DBG(OUT("  resume thread: %s (%" B_PRIx32 ")\n", strerror(error),
1987				error));
1988			// on error: kill the launched team and unregister the app
1989			if (error != B_OK) {
1990				if (appThread >= 0)
1991					kill_thread(appThread);
1992
1993				if (!isScript) {
1994					if (!fNoRegistrar)
1995						_RemovePreRegApp(appToken);
1996
1997					if (!wasDocument) {
1998						// Did we already try this?
1999						if (appRef == hintRef)
2000							break;
2001
2002						// Remove app hint if it's this one
2003						BMimeType appType(signature);
2004
2005						if (appType.InitCheck() == B_OK
2006							&& appType.GetAppHint(&hintRef) == B_OK
2007							&& appRef == hintRef) {
2008							appType.SetAppHint(NULL);
2009							// try again with the app hint removed
2010							continue;
2011						}
2012					}
2013				}
2014			}
2015		}
2016		// Don't try again
2017		break;
2018	}
2019
2020	if (alreadyRunning && current_team() == team) {
2021		// The target team is calling us, so we don't send it the message
2022		// to prevent an endless loop
2023		error = B_BAD_VALUE;
2024	}
2025
2026	// send "on launch" messages
2027	if (error == B_OK && !fNoRegistrar) {
2028		// If the target app is B_ARGV_ONLY, only if it is newly launched
2029		// messages are sent to it (namely B_ARGV_RECEIVED and B_READY_TO_RUN).
2030		// An already running B_ARGV_ONLY app won't get any messages.
2031		bool argvOnly = (appFlags & B_ARGV_ONLY) != 0
2032			|| (alreadyRunning && (otherAppFlags & B_ARGV_ONLY) != 0);
2033		const BList* _messageList = (argvOnly ? NULL : messageList);
2034		// don't send ref, if it refers to the app or is included in the
2035		// argument vector
2036		const entry_ref* _ref = argvOnly || !wasDocument
2037			|| argVector.Count() > 1 ? NULL : docRef;
2038		if (!(argvOnly && alreadyRunning)) {
2039			_SendToRunning(team, argVector.Count(), argVector.Args(),
2040				_messageList, _ref, alreadyRunning);
2041		}
2042	}
2043
2044	// set return values
2045	if (error == B_OK) {
2046		if (alreadyRunning)
2047			error = B_ALREADY_RUNNING;
2048		else if (_appTeam)
2049			*_appTeam = team;
2050
2051		if (_appThread != NULL)
2052			*_appThread = appThread;
2053		if (_appPort != NULL)
2054			*_appPort = appPort;
2055		if (_appToken != NULL)
2056			*_appToken = appToken;
2057	}
2058
2059	DBG(OUT("BRoster::_LaunchApp() done: %s (%" B_PRIx32 ")\n",
2060		strerror(error), error));
2061
2062	return error;
2063}
2064
2065
2066void
2067BRoster::_SetAppFlags(team_id team, uint32 flags) const
2068{
2069}
2070
2071
2072void
2073BRoster::_DumpRoster() const
2074{
2075}
2076
2077
2078/*!	Finds an application associated with a MIME type or a file.
2079
2080	It does also supply the caller with some more information about the
2081	application, like signature, app flags and whether the supplied
2082	MIME type/entry_ref already identified an application.
2083
2084	At least one of \a inType or \a ref must not be \c NULL. If \a inType is
2085	supplied, \a ref is ignored.
2086
2087	If \a ref refers to a link, it is updated with the entry_ref for the
2088	resolved entry.
2089
2090	\see FindApp() for how the application is searched.
2091
2092	\a signature is set to a string with length 0, if the found
2093	application has no signature.
2094
2095	\param inType The MIME type for which an application shall be found.
2096	       May be \c NULL.
2097	\param ref The file for which an application shall be found.
2098	       May be \c NULL.
2099	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2100	       a reference to the found application's executable. May be \c NULL.
2101	\param signature A pointer to a pre-allocated char buffer of at
2102	       least size \c B_MIME_TYPE_LENGTH to be filled with the signature of
2103	       the found application. May be \c NULL.
2104	\param appFlags A pointer to a pre-allocated uint32 variable to be filled
2105	       with the app flags of the found application. May be \c NULL.
2106	\param wasDocument A pointer to a pre-allocated bool variable to be set to
2107	       \c true, if the supplied file was not identifying an application,
2108	       to \c false otherwise. Has no meaning, if a \a inType is supplied.
2109	       May be \c NULL.
2110
2111	\return A status code.
2112	\retval B_OK Everything went fine.
2113	\retval B_BAD_VALUE \c NULL \a inType and \a ref.
2114
2115	\see FindApp() for other error codes.
2116*/
2117status_t
2118BRoster::_ResolveApp(const char* inType, entry_ref* ref,
2119	entry_ref* _appRef, char* _signature, uint32* _appFlags,
2120	bool* _wasDocument) const
2121{
2122	if ((inType == NULL && ref == NULL)
2123		|| (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH))
2124		return B_BAD_VALUE;
2125
2126	// find the app
2127	BMimeType appMeta;
2128	BFile appFile;
2129	entry_ref appRef;
2130	status_t error;
2131
2132	if (inType != NULL) {
2133		error = _TranslateType(inType, &appMeta, &appRef, &appFile);
2134		if (_wasDocument != NULL)
2135			*_wasDocument = !(appMeta == inType);
2136	} else {
2137		error = _TranslateRef(ref, &appMeta, &appRef, &appFile,
2138			_wasDocument);
2139	}
2140
2141	// create meta mime
2142	if (!fNoRegistrar && error == B_OK) {
2143		BPath path;
2144		if (path.SetTo(&appRef) == B_OK)
2145			create_app_meta_mime(path.Path(), false, true, false);
2146	}
2147
2148	// set the app hint on the type -- but only if the file has the
2149	// respective signature, otherwise unset the app hint
2150	BAppFileInfo appFileInfo;
2151	if (!fNoRegistrar && error == B_OK) {
2152		char signature[B_MIME_TYPE_LENGTH];
2153		if (appFileInfo.SetTo(&appFile) == B_OK
2154			&& appFileInfo.GetSignature(signature) == B_OK) {
2155			if (!strcasecmp(appMeta.Type(), signature)) {
2156				// Only set the app hint if there is none yet
2157				entry_ref dummyRef;
2158				if (appMeta.GetAppHint(&dummyRef) != B_OK)
2159					appMeta.SetAppHint(&appRef);
2160			} else {
2161				appMeta.SetAppHint(NULL);
2162				appMeta.SetTo(signature);
2163			}
2164		} else
2165			appMeta.SetAppHint(NULL);
2166	}
2167
2168	// set the return values
2169	if (error == B_OK) {
2170		if (_appRef)
2171			*_appRef = appRef;
2172
2173		if (_signature != NULL) {
2174			// there's no warranty, that appMeta is valid
2175			if (appMeta.IsValid()) {
2176				strlcpy(_signature, appMeta.Type(),
2177					B_MIME_TYPE_LENGTH);
2178			} else
2179				_signature[0] = '\0';
2180		}
2181
2182		if (_appFlags != NULL) {
2183			// if an error occurs here, we don't care and just set a default
2184			// value
2185			if (appFileInfo.InitCheck() != B_OK
2186				|| appFileInfo.GetAppFlags(_appFlags) != B_OK) {
2187				*_appFlags = B_REG_DEFAULT_APP_FLAGS;
2188			}
2189		}
2190	} else {
2191		// unset the ref on error
2192		if (_appRef != NULL)
2193			*_appRef = appRef;
2194	}
2195
2196	return error;
2197}
2198
2199
2200/*!	\brief Finds an application associated with a file.
2201
2202	\a appMeta is left unmodified, if the file is executable, but has no
2203	signature.
2204
2205	\see FindApp() for how the application is searched.
2206
2207	If \a ref refers to a link, it is updated with the entry_ref for the
2208	resolved entry.
2209
2210	\param ref The file for which an application shall be found.
2211	\param appMeta A pointer to a pre-allocated BMimeType to be set to the
2212	       signature of the found application.
2213	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2214	       a reference to the found application's executable.
2215	\param appFile A pointer to a pre-allocated BFile to be set to the
2216	       executable of the found application.
2217	\param wasDocument A pointer to a pre-allocated bool variable to be set to
2218	       \c true, if the supplied file was not identifying an application,
2219	       to \c false otherwise. May be \c NULL.
2220
2221	\return A status code.
2222	\retval B_OK: Everything went fine.
2223	\retval B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile.
2224
2225	\see FindApp() for other error codes.
2226*/
2227status_t
2228BRoster::_TranslateRef(entry_ref* ref, BMimeType* appMeta,
2229	entry_ref* appRef, BFile* appFile, bool* _wasDocument) const
2230{
2231	if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL)
2232		return B_BAD_VALUE;
2233
2234	entry_ref originalRef = *ref;
2235
2236	// resolve ref, if necessary
2237	BEntry entry;
2238	status_t error = entry.SetTo(ref, false);
2239	if (error != B_OK)
2240		return error;
2241
2242	if (entry.IsSymLink()) {
2243		// ref refers to a link
2244		if (entry.SetTo(ref, true) != B_OK || entry.GetRef(ref) != B_OK)
2245			return B_LAUNCH_FAILED_NO_RESOLVE_LINK;
2246	}
2247
2248	// init node
2249	BNode node;
2250	error = node.SetTo(ref);
2251	if (error != B_OK)
2252		return error;
2253
2254	// get permissions
2255	mode_t permissions;
2256	error = node.GetPermissions(&permissions);
2257	if (error != B_OK)
2258		return error;
2259
2260	if ((permissions & S_IXUSR) != 0 && node.IsFile()) {
2261		// node is executable and a file
2262		error = appFile->SetTo(ref, B_READ_ONLY);
2263		if (error != B_OK)
2264			return error;
2265
2266		// get the app's signature via a BAppFileInfo
2267		BAppFileInfo appFileInfo;
2268		error = appFileInfo.SetTo(appFile);
2269		if (error != B_OK)
2270			return error;
2271
2272		// don't worry, if the file doesn't have a signature, just
2273		// unset the supplied object
2274		char type[B_MIME_TYPE_LENGTH];
2275		if (appFileInfo.GetSignature(type) == B_OK) {
2276			error = appMeta->SetTo(type);
2277			if (error != B_OK)
2278				return error;
2279		} else
2280			appMeta->Unset();
2281
2282		// If the file type indicates that the file is an application, we've
2283		// definitely got what we're looking for.
2284		bool isDocument = true;
2285		if (_GetFileType(ref, &appFileInfo, type) == B_OK
2286			&& strcasecmp(type, B_APP_MIME_TYPE) == 0) {
2287			isDocument = false;
2288		}
2289
2290		// If our file is not an application executable, we probably have a
2291		// script. Check whether the file has a preferred application set. If
2292		// so, we fall through and use the preferred app instead. Otherwise
2293		// we're done.
2294		char preferredApp[B_MIME_TYPE_LENGTH];
2295		if (!isDocument || appFileInfo.GetPreferredApp(preferredApp) != B_OK) {
2296			// If we were given a symlink, point appRef to it in case its name
2297			// or attributes are relevant.
2298			*appRef = originalRef;
2299			if (_wasDocument != NULL)
2300				*_wasDocument = isDocument;
2301
2302			return B_OK;
2303		}
2304
2305		// Executable file, but not an application, and it has a preferred
2306		// application set. Fall through...
2307	}
2308
2309	// the node is not exectuable or not a file
2310	// init a node info
2311	BNodeInfo nodeInfo;
2312	error = nodeInfo.SetTo(&node);
2313	if (error != B_OK)
2314		return error;
2315
2316	// if the file has a preferred app, let _TranslateType() find
2317	// it for us
2318	char preferredApp[B_MIME_TYPE_LENGTH];
2319	if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
2320		&& _TranslateType(preferredApp, appMeta, appRef, appFile) == B_OK) {
2321		if (_wasDocument != NULL)
2322			*_wasDocument = true;
2323
2324		return B_OK;
2325	}
2326
2327	// no preferred app or existing one was not found -- we
2328	// need to get the file's type
2329
2330	// get the type from the file
2331	char fileType[B_MIME_TYPE_LENGTH];
2332	error = _GetFileType(ref, &nodeInfo, fileType);
2333	if (error != B_OK)
2334		return error;
2335
2336	// now let _TranslateType() do the actual work
2337	error = _TranslateType(fileType, appMeta, appRef, appFile);
2338	if (error != B_OK)
2339		return error;
2340
2341	if (_wasDocument != NULL)
2342		*_wasDocument = true;
2343
2344	return B_OK;
2345}
2346
2347
2348/*!	Finds an application associated with a MIME type.
2349
2350	\see FindApp() for how the application is searched.
2351
2352	\param mimeType The MIME type for which an application shall be found.
2353	\param appMeta A pointer to a pre-allocated BMimeType to be set to the
2354	       signature of the found application.
2355	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2356	       a reference to the found application's executable.
2357	\param appFile A pointer to a pre-allocated BFile to be set to the
2358	       executable of the found application.
2359
2360	\return A status code.
2361	\retval B_OK Everything went fine.
2362	\retval B_BAD_VALUE \c NULL \a mimeType, \a appMeta, \a appRef or
2363	        \a appFile.
2364
2365	\see FindApp() for other error codes.
2366*/
2367status_t
2368BRoster::_TranslateType(const char* mimeType, BMimeType* appMeta,
2369	entry_ref* appRef, BFile* appFile) const
2370{
2371	if (mimeType == NULL || appMeta == NULL || appRef == NULL
2372		|| appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) {
2373		return B_BAD_VALUE;
2374	}
2375
2376	// Create a BMimeType and check, if the type is installed.
2377	BMimeType type;
2378	status_t error = type.SetTo(mimeType);
2379
2380	// Get the preferred apps from the sub and super type.
2381	char primarySignature[B_MIME_TYPE_LENGTH];
2382	char secondarySignature[B_MIME_TYPE_LENGTH];
2383	primarySignature[0] = '\0';
2384	secondarySignature[0] = '\0';
2385
2386	if (error == B_OK) {
2387		BMimeType superType;
2388		if (type.GetSupertype(&superType) == B_OK)
2389			superType.GetPreferredApp(secondarySignature);
2390		if (type.IsInstalled()) {
2391			if (type.GetPreferredApp(primarySignature) != B_OK) {
2392				// The type is installed, but has no preferred app.
2393				primarySignature[0] = '\0';
2394			} else if (!strcmp(primarySignature, secondarySignature)) {
2395				// Both types have the same preferred app, there is
2396				// no point in testing it twice.
2397				secondarySignature[0] = '\0';
2398			}
2399		} else {
2400			// The type is not installed. We assume it is an app signature.
2401			strlcpy(primarySignature, mimeType, sizeof(primarySignature));
2402		}
2403	}
2404
2405	// We will use this BMessage "signatures" to hold all supporting apps
2406	// so we can iterator over them in the preferred order. We include
2407	// the supporting apps in such a way that the configured preferred
2408	// applications for the MIME type are in front of other supporting
2409	// applications for the sub and the super type respectively.
2410	const char* kSigField = "applications";
2411	BMessage signatures;
2412	bool addedSecondarySignature = false;
2413	if (error == B_OK) {
2414		if (primarySignature[0] != '\0')
2415			error = signatures.AddString(kSigField, primarySignature);
2416		else {
2417			// If there is a preferred app configured for the super type,
2418			// but no preferred type for the sub-type, add the preferred
2419			// super type handler in front of any other handlers. This way
2420			// we fall-back to non-preferred but supporting apps only in the
2421			// case when there is a preferred handler for the sub-type but
2422			// it cannot be resolved (misconfiguration).
2423			if (secondarySignature[0] != '\0') {
2424				error = signatures.AddString(kSigField, secondarySignature);
2425				addedSecondarySignature = true;
2426			}
2427		}
2428	}
2429
2430	BMessage supportingSignatures;
2431	if (error == B_OK
2432		&& type.GetSupportingApps(&supportingSignatures) == B_OK) {
2433		int32 subCount;
2434		if (supportingSignatures.FindInt32("be:sub", &subCount) != B_OK)
2435			subCount = 0;
2436		// Add all signatures with direct support for the sub-type.
2437		const char* supportingType;
2438		if (!addedSecondarySignature) {
2439			// Try to add the secondarySignature in front of all other
2440			// supporting apps, if we find it among those.
2441			for (int32 i = 0; error == B_OK && i < subCount
2442					&& supportingSignatures.FindString(kSigField, i,
2443						&supportingType) == B_OK; i++) {
2444				if (strcmp(primarySignature, supportingType) != 0
2445					&& strcmp(secondarySignature, supportingType) == 0) {
2446					error = signatures.AddString(kSigField, supportingType);
2447					addedSecondarySignature = true;
2448					break;
2449				}
2450			}
2451		}
2452
2453		for (int32 i = 0; error == B_OK && i < subCount
2454				&& supportingSignatures.FindString(kSigField, i,
2455					&supportingType) == B_OK; i++) {
2456			if (strcmp(primarySignature, supportingType) != 0
2457				&& strcmp(secondarySignature, supportingType) != 0) {
2458				error = signatures.AddString(kSigField, supportingType);
2459			}
2460		}
2461
2462		// Add the preferred type of the super type here before adding
2463		// the other types supporting the super type, but only if we have
2464		// not already added it in case there was no preferred app for the
2465		// sub-type configured.
2466		if (error == B_OK && !addedSecondarySignature
2467			&& secondarySignature[0] != '\0') {
2468			error = signatures.AddString(kSigField, secondarySignature);
2469		}
2470
2471		// Add all signatures with support for the super-type.
2472		for (int32 i = subCount; error == B_OK
2473				&& supportingSignatures.FindString(kSigField, i,
2474					&supportingType) == B_OK; i++) {
2475			// Don't add the signature if it's one of the preferred apps
2476			// already.
2477			if (strcmp(primarySignature, supportingType) != 0
2478				&& strcmp(secondarySignature, supportingType) != 0) {
2479				error = signatures.AddString(kSigField, supportingType);
2480			}
2481		}
2482	} else {
2483		// Failed to get supporting apps, just add the preferred apps.
2484		if (error == B_OK && secondarySignature[0] != '\0')
2485			error = signatures.AddString(kSigField, secondarySignature);
2486	}
2487
2488	if (error != B_OK)
2489		return error;
2490
2491	// Set an error in case we can't resolve a single supporting app.
2492	error = B_LAUNCH_FAILED_NO_PREFERRED_APP;
2493
2494	// See if we can find a good application that is valid from the messege.
2495	const char* signature;
2496	for (int32 i = 0;
2497		signatures.FindString(kSigField, i, &signature) == B_OK; i++) {
2498		if (signature[0] == '\0')
2499			continue;
2500
2501		error = appMeta->SetTo(signature);
2502
2503		// Check, whether the signature is installed and has an app hint
2504		bool appFound = false;
2505		if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) {
2506			// Resolve symbolic links, if necessary
2507			BEntry entry;
2508			if (entry.SetTo(appRef, true) == B_OK && entry.IsFile()
2509				&& entry.GetRef(appRef) == B_OK) {
2510				appFound = true;
2511			} else {
2512				// Bad app hint -- remove it
2513				appMeta->SetAppHint(NULL);
2514			}
2515		}
2516
2517		// In case there is no app hint or it is invalid, we need to query for
2518		// the app.
2519		if (error == B_OK && !appFound)
2520			error = query_for_app(appMeta->Type(), appRef);
2521
2522		if (error == B_OK)
2523			error = appFile->SetTo(appRef, B_READ_ONLY);
2524
2525		// check, whether the app can be used
2526		if (error == B_OK)
2527			error = can_app_be_used(appRef);
2528
2529		if (error == B_OK)
2530			break;
2531	}
2532
2533	return error;
2534}
2535
2536
2537/*!	Gets the type of a file either from the node info or by sniffing.
2538
2539	The method first tries to get the file type from the supplied node info. If
2540	that didn't work, the given entry ref is sniffed.
2541
2542	\param file An entry_ref referring to the file in question.
2543	\param nodeInfo A BNodeInfo initialized to the file.
2544	\param mimeType A pointer to a pre-allocated char buffer of at least size
2545	       \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for
2546	       the file.
2547
2548	\return A status code.
2549	\retval B_OK Everything went fine.
2550	\retval B_BAD_VALUE \c NULL \a file, \a nodeInfo or \a mimeType.
2551*/
2552status_t
2553BRoster::_GetFileType(const entry_ref* file, BNodeInfo* nodeInfo,
2554	char* mimeType) const
2555{
2556	// first try the node info
2557	if (nodeInfo->GetType(mimeType) == B_OK)
2558		return B_OK;
2559
2560	if (fNoRegistrar)
2561		return B_NO_INIT;
2562
2563	// Try to update the file's MIME info and just read the updated type.
2564	// If that fails, sniff manually.
2565	BPath path;
2566	if (path.SetTo(file) != B_OK
2567		|| update_mime_info(path.Path(), false, true, false) != B_OK
2568		|| nodeInfo->GetType(mimeType) != B_OK) {
2569		BMimeType type;
2570		status_t error = BMimeType::GuessMimeType(file, &type);
2571		if (error != B_OK)
2572			return error;
2573
2574		if (!type.IsValid())
2575			return B_BAD_VALUE;
2576
2577		strlcpy(mimeType, type.Type(), B_MIME_TYPE_LENGTH);
2578	}
2579
2580	return B_OK;
2581}
2582
2583
2584/*!	Sends messages to a running team.
2585
2586	In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED,
2587	\c B_READY_TO_RUN and other, arbitrary, ones.
2588
2589	If \a messageList is not \c NULL or empty, those messages are sent first,
2590	then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally
2591	\c B_READ_TO_RUN.
2592
2593	\c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains
2594	more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not
2595	\c NULL.
2596
2597	The ownership of all supplied objects retains to the caller.
2598
2599	\param team The team ID of the target application.
2600	\param argc Number of elements in \a args.
2601	\param args Argument vector to be sent to the target. May be \c NULL.
2602	\param messageList List of BMessages to be sent to the target. May be
2603	       \c NULL or empty.
2604	\param ref entry_ref to be sent to the target. May be \c NULL.
2605	\param alreadyRunning \c true, if the target app is not newly launched,
2606	       but was already running, \c false otherwise (a \c B_READY_TO_RUN
2607	       message will be sent in this case).
2608
2609	\return \c B_OK if everything went fine, or an error code otherwise.
2610*/
2611status_t
2612BRoster::_SendToRunning(team_id team, int argc, const char* const* args,
2613	const BList* messageList, const entry_ref* ref,
2614	bool alreadyRunning) const
2615{
2616	status_t error = B_OK;
2617
2618	// Construct a messenger to the app: We can't use the public constructor,
2619	// since the target application may be B_ARGV_ONLY.
2620	app_info info;
2621	error = GetRunningAppInfo(team, &info);
2622	if (error == B_OK) {
2623		BMessenger messenger;
2624		BMessenger::Private(messenger).SetTo(team, info.port,
2625			B_PREFERRED_TOKEN);
2626
2627		// send messages from the list
2628		if (messageList != NULL) {
2629			for (int32 i = 0;
2630					BMessage* message = (BMessage*)messageList->ItemAt(i);
2631					i++) {
2632				messenger.SendMessage(message);
2633			}
2634		}
2635
2636		// send B_ARGV_RECEIVED or B_REFS_RECEIVED or B_SILENT_RELAUNCH
2637		// (if already running)
2638		if (args != NULL && argc > 1) {
2639			BMessage message(B_ARGV_RECEIVED);
2640			message.AddInt32("argc", argc);
2641			for (int32 i = 0; i < argc; i++)
2642				message.AddString("argv", args[i]);
2643
2644			// also add current working directory
2645			char cwd[B_PATH_NAME_LENGTH];
2646			if (getcwd(cwd, B_PATH_NAME_LENGTH) != NULL)
2647				message.AddString("cwd", cwd);
2648
2649			messenger.SendMessage(&message);
2650		} else if (ref != NULL) {
2651			DBG(OUT("_SendToRunning : B_REFS_RECEIVED\n"));
2652			BMessage message(B_REFS_RECEIVED);
2653			message.AddRef("refs", ref);
2654			messenger.SendMessage(&message);
2655		} else if (alreadyRunning && (!messageList || messageList->IsEmpty()))
2656			messenger.SendMessage(B_SILENT_RELAUNCH);
2657
2658		if (!alreadyRunning) {
2659			// send B_READY_TO_RUN
2660			DBG(OUT("_SendToRunning : B_READY_TO_RUN\n"));
2661			messenger.SendMessage(B_READY_TO_RUN);
2662		}
2663	}
2664
2665	return error;
2666}
2667
2668
2669/*!	Allows to use certain functionality of the BRoster class without
2670	accessing the registrar.
2671*/
2672void
2673BRoster::_SetWithoutRegistrar(bool noRegistrar)
2674{
2675	fNoRegistrar = noRegistrar;
2676}
2677
2678
2679void
2680BRoster::_InitMessenger()
2681{
2682	DBG(OUT("BRoster::InitMessengers()\n"));
2683
2684	// find the registrar port
2685
2686#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
2687	BMessage data;
2688	if (BLaunchRoster().GetData(B_REGISTRAR_SIGNATURE, data) == B_OK) {
2689		port_id port = data.GetInt32("port", -1);
2690		team_id team = data.GetInt32("team", -1);
2691
2692		if (port >= 0 && team != current_team()) {
2693			// Make sure we aren't the registrar ourselves.
2694
2695			DBG(OUT("  found roster port\n"));
2696
2697			BMessenger::Private(fMessenger).SetTo(team, port,
2698				B_PREFERRED_TOKEN);
2699		}
2700	}
2701#else
2702	port_id rosterPort = find_port(B_REGISTRAR_PORT_NAME);
2703	port_info info;
2704	if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) {
2705		DBG(OUT("  found roster port\n"));
2706
2707		BMessenger::Private(fMessenger).SetTo(info.team, rosterPort,
2708			B_PREFERRED_TOKEN);
2709	}
2710#endif
2711
2712	DBG(OUT("BRoster::InitMessengers() done\n"));
2713}
2714
2715
2716/*static*/ status_t
2717BRoster::_InitMimeMessenger(void* data)
2718{
2719	BRoster* roster = (BRoster*)data;
2720
2721	// ask for the MIME messenger
2722	// Generous 1s + 5s timeouts. It could actually be synchronous, but
2723	// timeouts allow us to debug the registrar main thread.
2724	BMessage request(B_REG_GET_MIME_MESSENGER);
2725	BMessage reply;
2726	status_t error = roster->fMessenger.SendMessage(&request, &reply,
2727		1000000LL, 5000000LL);
2728	if (error == B_OK && reply.what == B_REG_SUCCESS) {
2729		DBG(OUT("  got reply from roster\n"));
2730			reply.FindMessenger("messenger", &roster->fMimeMessenger);
2731	} else {
2732		DBG(OUT("  no (useful) reply from roster: error: %" B_PRIx32 ": %s\n",
2733			error, strerror(error)));
2734		if (error == B_OK)
2735			DBG(reply.PrintToStream());
2736	}
2737
2738	return error;
2739}
2740
2741
2742BMessenger&
2743BRoster::_MimeMessenger()
2744{
2745	__init_once(&fMimeMessengerInitOnce, &_InitMimeMessenger, this);
2746	return fMimeMessenger;
2747}
2748
2749
2750/*!	Sends a request to the roster to add the application with the
2751	given signature to the front of the recent apps list.
2752*/
2753void
2754BRoster::_AddToRecentApps(const char* signature) const
2755{
2756	status_t error = B_OK;
2757	// compose the request message
2758	BMessage request(B_REG_ADD_TO_RECENT_APPS);
2759	if (error == B_OK)
2760		error = request.AddString("app sig", signature);
2761
2762	// send the request
2763	BMessage reply;
2764	if (error == B_OK)
2765		error = fMessenger.SendMessage(&request, &reply);
2766
2767	// evaluate the reply
2768	status_t result;
2769	if (error == B_OK) {
2770		error = reply.what == B_REG_RESULT
2771			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2772	}
2773
2774	if (error == B_OK)
2775		error = reply.FindInt32("result", &result);
2776
2777	if (error == B_OK)
2778		error = result;
2779
2780	// Nothing to return... how sad :-(
2781	//return error;
2782}
2783
2784
2785//!	Sends a request to the roster to clear the recent documents list.
2786void
2787BRoster::_ClearRecentDocuments() const
2788{
2789	BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS);
2790	BMessage reply;
2791	fMessenger.SendMessage(&request, &reply);
2792}
2793
2794
2795//!	Sends a request to the roster to clear the recent documents list.
2796void
2797BRoster::_ClearRecentFolders() const
2798{
2799	BMessage request(B_REG_CLEAR_RECENT_FOLDERS);
2800	BMessage reply;
2801	fMessenger.SendMessage(&request, &reply);
2802}
2803
2804
2805//! \brief Sends a request to the roster to clear the recent documents list.
2806void
2807BRoster::_ClearRecentApps() const
2808{
2809	BMessage request(B_REG_CLEAR_RECENT_APPS);
2810	BMessage reply;
2811	fMessenger.SendMessage(&request, &reply);
2812}
2813
2814
2815/*!	Loads the system's recently used document, folder, and
2816	application lists from the specified file.
2817
2818	\note The current lists are cleared before loading the new lists
2819
2820	\param filename The name of the file to load from
2821*/
2822void
2823BRoster::_LoadRecentLists(const char* filename) const
2824{
2825	status_t error = B_OK;
2826
2827	// compose the request message
2828	BMessage request(B_REG_LOAD_RECENT_LISTS);
2829	if (error == B_OK)
2830		error = request.AddString("filename", filename);
2831
2832	// send the request
2833	BMessage reply;
2834	if (error == B_OK)
2835		error = fMessenger.SendMessage(&request, &reply);
2836
2837	// evaluate the reply
2838	status_t result;
2839	if (error == B_OK) {
2840		error = reply.what == B_REG_RESULT
2841			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2842	}
2843	if (error == B_OK)
2844		error = reply.FindInt32("result", &result);
2845
2846	if (error == B_OK)
2847		error = result;
2848
2849	// Nothing to return... how sad :-(
2850	//return error;
2851}
2852
2853
2854/*!	Saves the system's recently used document, folder, and
2855	application lists to the specified file.
2856
2857	\param filename The name of the file to save to
2858*/
2859void
2860BRoster::_SaveRecentLists(const char* filename) const
2861{
2862	status_t error = B_OK;
2863
2864	// compose the request message
2865	BMessage request(B_REG_SAVE_RECENT_LISTS);
2866	if (error == B_OK)
2867		error = request.AddString("filename", filename);
2868
2869	// send the request
2870	BMessage reply;
2871	if (error == B_OK)
2872		error = fMessenger.SendMessage(&request, &reply);
2873
2874	// evaluate the reply
2875	status_t result;
2876	if (error == B_OK) {
2877		error = reply.what == B_REG_RESULT
2878			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2879	}
2880	if (error == B_OK)
2881		error = reply.FindInt32("result", &result);
2882
2883	if (error == B_OK)
2884		error = result;
2885
2886	// Nothing to return... how sad :-(
2887	//return error;
2888}
2889