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/*!	(Pre-)Registers an application with the registrar.
1347
1348	This methods is invoked either to register or to pre-register an
1349	application. Full registration is requested by supplying \c true via
1350	\a fullRegistration.
1351
1352	A full registration requires \a signature, \a ref, \a flags, \a team,
1353	\a thread and \a port to contain valid values. No token will be return
1354	via \a pToken.
1355
1356	For a pre-registration \a signature, \a ref, \a flags must be valid.
1357	\a team and \a thread are optional and should be set to -1, if they are
1358	unknown. If no team ID is supplied, \a pToken should be valid and, if the
1359	the pre-registration succeeds, will be filled with a unique token assigned
1360	by the roster.
1361
1362	In both cases the registration may fail, if single/exclusive launch is
1363	requested and an instance of the application is already running. Then
1364	\c B_ALREADY_RUNNING is returned and the team ID of the running instance
1365	is passed back via \a otherTeam, if supplied.
1366
1367	\param signature The application's signature
1368	\param ref An entry_ref referring to the app's executable
1369	\param flags The application's flags
1370	\param team The application's team ID
1371	\param thread The application's main thread
1372	\param port The application's looper port
1373	\param fullRegistration \c true for full, \c false for pre-registration
1374	\param pToken A pointer to a pre-allocated uint32 into which the token
1375	       assigned by the registrar is written (may be \c NULL)
1376	\param otherTeam A pointer to a pre-allocated team_id into which the
1377	       team ID of the already running instance of a single/exclusive
1378	       launch application is written (may be \c NULL)
1379
1380	\return A status code
1381	\retval B_OK Everything went fine.
1382	\retval B_ENTRY_NOT_FOUND \a ref didn't refer to a file.
1383	\retval B_ALREADY_RUNNING The application requested a single/exclusive
1384	        launch and an instance was already running.
1385	\retval B_REG_ALREADY_REGISTERED An application with the team ID \a team
1386	        was already registered.
1387*/
1388status_t
1389BRoster::_AddApplication(const char* signature, const entry_ref* ref,
1390	uint32 flags, team_id team, thread_id thread, port_id port,
1391	bool fullRegistration, uint32* pToken, team_id* otherTeam) const
1392{
1393	status_t error = B_OK;
1394
1395	// compose the request message
1396	BMessage request(B_REG_ADD_APP);
1397	if (error == B_OK && signature != NULL)
1398		error = request.AddString("signature", signature);
1399
1400	if (error == B_OK && ref != NULL)
1401		error = request.AddRef("ref", ref);
1402
1403	if (error == B_OK)
1404		error = request.AddInt32("flags", (int32)flags);
1405
1406	if (error == B_OK && team >= 0)
1407		error = request.AddInt32("team", team);
1408
1409	if (error == B_OK && thread >= 0)
1410		error = request.AddInt32("thread", thread);
1411
1412	if (error == B_OK && port >= 0)
1413		error = request.AddInt32("port", port);
1414
1415	if (error == B_OK)
1416		error = request.AddBool("full_registration", fullRegistration);
1417
1418	// send the request
1419	BMessage reply;
1420	if (error == B_OK)
1421		error = fMessenger.SendMessage(&request, &reply);
1422
1423	// evaluate the reply
1424	if (error == B_OK) {
1425		if (reply.what == B_REG_SUCCESS) {
1426			if (!fullRegistration && team < 0) {
1427				uint32 token;
1428				if (reply.FindInt32("token", (int32*)&token) == B_OK) {
1429					if (pToken != NULL)
1430						*pToken = token;
1431				} else
1432					error = B_ERROR;
1433			}
1434		} else {
1435			if (reply.FindInt32("error", &error) != B_OK)
1436				error = B_ERROR;
1437
1438			// get team and token from the reply
1439			if (otherTeam != NULL
1440				&& reply.FindInt32("other_team", otherTeam) != B_OK) {
1441				*otherTeam = -1;
1442			}
1443			if (pToken != NULL
1444				&& reply.FindInt32("token", (int32*)pToken) != B_OK) {
1445				*pToken = 0;
1446			}
1447		}
1448	}
1449
1450	return error;
1451}
1452
1453
1454/*!	Sets an application's signature.
1455
1456	The application must be registered or at pre-registered with a valid
1457	team ID.
1458
1459	\param team The app's team ID.
1460	\param signature The app's new signature.
1461
1462	\return A status code.
1463	\retval B_OK Everything went fine.
1464	\retval B_REG_APP_NOT_REGISTERED The supplied team ID did not identify a
1465	        registered application.
1466*/
1467status_t
1468BRoster::_SetSignature(team_id team, const char* signature) const
1469{
1470	status_t error = B_OK;
1471
1472	// compose the request message
1473	BMessage request(B_REG_SET_SIGNATURE);
1474	if (team >= 0)
1475		error = request.AddInt32("team", team);
1476
1477	if (error == B_OK && signature)
1478		error = request.AddString("signature", signature);
1479
1480	// send the request
1481	BMessage reply;
1482	if (error == B_OK)
1483		error = fMessenger.SendMessage(&request, &reply);
1484
1485	// evaluate the reply
1486	if (error == B_OK && reply.what != B_REG_SUCCESS
1487		&& reply.FindInt32("error", &error) != B_OK) {
1488		error = B_ERROR;
1489	}
1490
1491	return error;
1492}
1493
1494
1495//!	\todo Really needed?
1496void
1497BRoster::_SetThread(team_id team, thread_id thread) const
1498{
1499}
1500
1501
1502/*!	Sets the team and thread IDs of a pre-registered application.
1503
1504	After an application has been pre-registered via AddApplication(), without
1505	supplying a team ID, the team and thread IDs have to be set using this
1506	method.
1507
1508	\param entryToken The token identifying the application (returned by
1509	       AddApplication())
1510	\param thread The app's thread ID
1511	\param team The app's team ID
1512
1513	\return A status code.
1514	\retval B_OK Everything went fine.
1515	\retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify a
1516	        pre-registered application.
1517*/
1518status_t
1519BRoster::_SetThreadAndTeam(uint32 entryToken, thread_id thread,
1520	team_id team, port_id* _port) const
1521{
1522	status_t error = B_OK;
1523
1524	// compose the request message
1525	BMessage request(B_REG_SET_THREAD_AND_TEAM);
1526	if (error == B_OK)
1527		error = request.AddInt32("token", (int32)entryToken);
1528
1529	if (error == B_OK && team >= 0)
1530		error = request.AddInt32("team", team);
1531
1532	if (error == B_OK && thread >= 0)
1533		error = request.AddInt32("thread", thread);
1534
1535	// send the request
1536	BMessage reply;
1537	if (error == B_OK)
1538		error = fMessenger.SendMessage(&request, &reply);
1539
1540	// evaluate the reply
1541	if (error == B_OK && reply.what != B_REG_SUCCESS
1542		&& reply.FindInt32("error", &error) != B_OK)
1543		error = B_ERROR;
1544
1545	if (error == B_OK && _port != NULL)
1546		*_port = reply.GetInt32("port", -1);
1547
1548	return error;
1549}
1550
1551
1552/*!	Completes the registration process for a pre-registered application.
1553
1554	After an application has been pre-registered via AddApplication() and
1555	after assigning it a team ID (via SetThreadAndTeam()) the application is
1556	still pre-registered and must complete the registration.
1557
1558	\param team The app's team ID
1559	\param thread The app's thread ID
1560	\param thread The app looper port
1561
1562	\return A status code.
1563	\retval B_OK Everything went fine.
1564	\retval B_REG_APP_NOT_PRE_REGISTERED \a team did not identify an existing
1565	        application or the identified application was already fully
1566	        registered.
1567*/
1568status_t
1569BRoster::_CompleteRegistration(team_id team, thread_id thread,
1570	port_id port) const
1571{
1572	status_t error = B_OK;
1573
1574	// compose the request message
1575	BMessage request(B_REG_COMPLETE_REGISTRATION);
1576	if (team >= 0)
1577		error = request.AddInt32("team", team);
1578
1579	if (error == B_OK && thread >= 0)
1580		error = request.AddInt32("thread", thread);
1581
1582	if (error == B_OK && port >= 0)
1583		error = request.AddInt32("port", port);
1584
1585	// send the request
1586	BMessage reply;
1587	if (error == B_OK)
1588		error = fMessenger.SendMessage(&request, &reply);
1589
1590	// evaluate the reply
1591	if (error == B_OK && reply.what != B_REG_SUCCESS
1592		&& reply.FindInt32("error", &error) != B_OK) {
1593		error = B_ERROR;
1594	}
1595
1596	return error;
1597}
1598
1599
1600/*!	Returns whether an application is registered.
1601
1602	If the application is indeed pre-registered and \a info is not \c NULL,
1603	the methods fills in the app_info structure pointed to by \a info.
1604
1605	\param ref An entry_ref referring to the app's executable
1606	\param team The app's team ID. May be -1, if \a token is given.
1607	\param token The app's pre-registration token. May be 0, if \a team is
1608	       given.
1609	\param preRegistered: Pointer to a pre-allocated bool to be filled in
1610	       by this method, indicating whether or not the app was
1611	       pre-registered.
1612	\param info A pointer to a pre-allocated app_info structure to be filled
1613	       in by this method (may be \c NULL)
1614
1615	\return \c B_OK, if the application is registered and all requested
1616	        information could be retrieved, or another error code, if the app
1617	        is not registered or an error occurred.
1618*/
1619status_t
1620BRoster::_IsAppRegistered(const entry_ref* ref, team_id team,
1621	uint32 token, bool* preRegistered, app_info* info) const
1622{
1623	status_t error = B_OK;
1624
1625	// compose the request message
1626	BMessage request(B_REG_IS_APP_REGISTERED);
1627	if (ref)
1628		error = request.AddRef("ref", ref);
1629	if (error == B_OK && team >= 0)
1630		error = request.AddInt32("team", team);
1631	if (error == B_OK && token > 0)
1632		error = request.AddInt32("token", (int32)token);
1633
1634	// send the request
1635	BMessage reply;
1636	if (error == B_OK)
1637		error = fMessenger.SendMessage(&request, &reply);
1638
1639	// evaluate the reply
1640	bool isRegistered = false;
1641	bool isPreRegistered = false;
1642	if (error == B_OK) {
1643		if (reply.what == B_REG_SUCCESS) {
1644			if (reply.FindBool("registered", &isRegistered) != B_OK
1645				|| !isRegistered
1646				|| reply.FindBool("pre-registered", &isPreRegistered) != B_OK) {
1647				error = B_ERROR;
1648			}
1649
1650			if (error == B_OK && preRegistered)
1651				*preRegistered = isPreRegistered;
1652			if (error == B_OK && info)
1653				error = find_message_app_info(&reply, info);
1654		} else if (reply.FindInt32("error", &error) != B_OK)
1655			error = B_ERROR;
1656	}
1657
1658	return error;
1659}
1660
1661
1662/*!	Completely unregisters a pre-registered application.
1663
1664	This method can only be used to unregister applications that don't have
1665	a team ID assigned yet. All other applications must be unregistered via
1666	RemoveApp().
1667
1668	\param entryToken The token identifying the application (returned by
1669	       AddApplication())
1670
1671	\return A status code.
1672	\retval B_OK Everything went fine.
1673	\retval B_REG_APP_NOT_PRE_REGISTERED The supplied token did not identify
1674	        a pre-registered application.
1675*/
1676status_t
1677BRoster::_RemovePreRegApp(uint32 entryToken) const
1678{
1679	status_t error = B_OK;
1680
1681	// compose the request message
1682	BMessage request(B_REG_REMOVE_PRE_REGISTERED_APP);
1683	if (error == B_OK)
1684		error = request.AddInt32("token", (int32)entryToken);
1685
1686	// send the request
1687	BMessage reply;
1688	if (error == B_OK)
1689		error = fMessenger.SendMessage(&request, &reply);
1690
1691	// evaluate the reply
1692	if (error == B_OK && reply.what != B_REG_SUCCESS
1693		&& reply.FindInt32("error", &error) != B_OK) {
1694		error = B_ERROR;
1695	}
1696
1697	return error;
1698}
1699
1700
1701/*!	Unregisters a (pre-)registered application.
1702
1703	This method must be used to unregister applications that already have
1704	a team ID assigned, i.e. also for pre-registered application for which
1705	SetThreadAndTeam() has already been invoked.
1706
1707	\param team The app's team ID
1708
1709	\return A status code.
1710	\retval B_OK Everything went fine.
1711	\retval B_REG_APP_NOT_REGISTERED The supplied team ID does not identify a
1712	        (pre-)registered application.
1713*/
1714status_t
1715BRoster::_RemoveApp(team_id team) const
1716{
1717	status_t error = B_OK;
1718
1719	// compose the request message
1720	BMessage request(B_REG_REMOVE_APP);
1721	if (team >= 0)
1722		error = request.AddInt32("team", team);
1723
1724	// send the request
1725	BMessage reply;
1726	if (error == B_OK)
1727		error = fMessenger.SendMessage(&request, &reply);
1728
1729	// evaluate the reply
1730	if (error == B_OK && reply.what != B_REG_SUCCESS
1731		&& reply.FindInt32("error", &error) != B_OK) {
1732		error = B_ERROR;
1733	}
1734
1735	return error;
1736}
1737
1738
1739void
1740BRoster::_ApplicationCrashed(team_id team)
1741{
1742	BPrivate::DesktopLink link;
1743	if (link.InitCheck() != B_OK)
1744		return;
1745
1746	if (link.StartMessage(AS_APP_CRASHED) == B_OK
1747		&& link.Attach(team) == B_OK) {
1748		link.Flush();
1749	}
1750}
1751
1752
1753/*!	Tells the registrar which application is currently active.
1754
1755	It's called from within the app_server when the active application is
1756	changed.
1757
1758	As it's called in the event loop, it must run asynchronously and cannot
1759	wait for a reply.
1760*/
1761status_t
1762BRoster::_UpdateActiveApp(team_id team) const
1763{
1764	if (team < B_OK)
1765		return B_BAD_TEAM_ID;
1766
1767	// compose the request message
1768	BMessage request(B_REG_UPDATE_ACTIVE_APP);
1769	status_t status = request.AddInt32("team", team);
1770	if (status < B_OK)
1771		return status;
1772
1773	// send the request
1774	return fMessenger.SendMessage(&request);
1775}
1776
1777
1778/*!	Launches the application associated with the supplied MIME type or
1779	the entry referred to by the supplied entry_ref.
1780
1781	The application to be started is searched the same way FindApp() does it.
1782
1783	At least one of \a mimeType or \a ref must not be \c NULL. If \a mimeType
1784	is supplied, \a ref is ignored for finding the application.
1785
1786	If \a ref does refer to an application executable, that application is
1787	launched. Otherwise the respective application is searched and launched,
1788	and \a ref is sent to it in a \c B_REFS_RECEIVED message, unless other
1789	arguments are passed via \a argc and \a args -- then the entry_ref is
1790	converted into a path (C-string) and added to the argument vector.
1791
1792	\a messageList contains messages to be sent to the application
1793	"on launch", i.e. before ReadyToRun() is invoked on the BApplication
1794	object. The caller retains ownership of the supplied BList and the
1795	contained BMessages. In case the method fails with \c B_ALREADY_RUNNING
1796	the messages are delivered to the already running instance. The same
1797	applies to the \c B_REFS_RECEIVED message.
1798
1799	The supplied \a argc and \a args are (if containing at least one argument)
1800	put into a \c B_ARGV_RECEIVED message and sent to the launched application
1801	"on launch". The caller retains ownership of the supplied \a args.
1802	In case the method fails with \c B_ALREADY_RUNNING the message is
1803	delivered to the already running instance. The same applies to the
1804	\c B_REFS_RECEIVED message, if no arguments are supplied via \a argc and
1805	\args.
1806
1807	If \a launchSuspended is set to true, the main thread of the loaded app
1808	(returned in \a appThread) is kept in the suspended state and not
1809	automatically resumed.
1810
1811	\param mimeType MIME type for which the application shall be launched.
1812	       May be \c NULL.
1813	\param ref entry_ref referring to the file for which an application shall
1814	       be launched. May be \c NULL.
1815	\param messageList Optional list of messages to be sent to the application
1816	       "on launch". May be \c NULL.
1817	\param argc Specifies the number of elements in \a args.
1818	\param args An array of C-strings to be sent as B_ARGV_RECEIVED messaged
1819	       to the launched application.
1820	\param appTeam Pointer to a pre-allocated team_id variable to be set to
1821	       the team ID of the launched application.
1822	\param appThread Pointer to a pre-allocated thread_id variable to
1823		   be set to the thread ID of the launched main thread.
1824	\param launchSuspended Indicates whether to keep the app thread in the
1825		   suspended state or resume it.
1826
1827	\return A status code.
1828	\retval B_OK Everything went fine.
1829	\retval B_BAD_VALUE \c NULL \a mimeType
1830	\retval B_LAUNCH_FAILED_NO_PREFERRED_APP Neither with the supplied type
1831	        nor with its supertype (if the supplied isn't a supertype itself)
1832	        a preferred application is associated.
1833	\retval B_LAUNCH_FAILED_APP_NOT_FOUND The supplied type is not installed
1834	        or its preferred application could not be found.
1835	\retval B_LAUNCH_FAILED_APP_IN_TRASH The supplied type's preferred
1836	        application was in the trash.
1837	\retval B_LAUNCH_FAILED_EXECUTABLE The found application was not
1838	        executable.
1839*/
1840status_t
1841BRoster::_LaunchApp(const char* mimeType, const entry_ref* ref,
1842	const BList* messageList, int argc, const char* const* args,
1843	const char** environment, team_id* _appTeam, thread_id* _appThread,
1844	port_id* _appPort, uint32* _appToken, bool launchSuspended) const
1845{
1846	DBG(OUT("BRoster::_LaunchApp()"));
1847
1848	if (_appTeam != NULL) {
1849		// we're supposed to set _appTeam to -1 on error; we'll
1850		// reset it later if everything goes well
1851		*_appTeam = -1;
1852	}
1853
1854	if (mimeType == NULL && ref == NULL)
1855		return B_BAD_VALUE;
1856
1857	// use a mutable copy of the document entry_ref
1858	entry_ref _docRef;
1859	entry_ref* docRef = NULL;
1860	if (ref != NULL) {
1861		_docRef = *ref;
1862		docRef = &_docRef;
1863	}
1864
1865	uint32 otherAppFlags = B_REG_DEFAULT_APP_FLAGS;
1866	uint32 appFlags = B_REG_DEFAULT_APP_FLAGS;
1867	bool alreadyRunning = false;
1868	bool wasDocument = true;
1869	status_t error = B_OK;
1870	ArgVector argVector;
1871	team_id team = -1;
1872	thread_id appThread = -1;
1873	port_id appPort = -1;
1874	uint32 appToken = 0;
1875
1876	while (true) {
1877		// find the app
1878		entry_ref appRef;
1879		char signature[B_MIME_TYPE_LENGTH];
1880		error = _ResolveApp(mimeType, docRef, &appRef, signature,
1881			&appFlags, &wasDocument);
1882		DBG(OUT("  find app: %s (%" B_PRIx32 ") %s \n", strerror(error), error,
1883			signature));
1884		if (error != B_OK)
1885			return error;
1886
1887		// build an argument vector
1888		error = argVector.Init(argc, args, &appRef,
1889			wasDocument ? docRef : NULL);
1890		DBG(OUT("  build argv: %s (%" B_PRIx32 ")\n", strerror(error), error));
1891		if (error != B_OK)
1892			return error;
1893
1894		// pre-register the app (but ignore scipts)
1895		app_info appInfo;
1896		bool isScript = wasDocument && docRef != NULL && *docRef == appRef;
1897		if (!isScript && !fNoRegistrar) {
1898			error = _AddApplication(signature, &appRef, appFlags, -1, -1, -1,
1899				false, &appToken, &team);
1900			if (error == B_ALREADY_RUNNING) {
1901				DBG(OUT("  already running\n"));
1902				alreadyRunning = true;
1903
1904				// get the app flags for the running application
1905				error = _IsAppRegistered(&appRef, team, appToken, NULL,
1906					&appInfo);
1907				if (error == B_OK) {
1908					otherAppFlags = appInfo.flags;
1909					appPort = appInfo.port;
1910					team = appInfo.team;
1911				}
1912			}
1913			DBG(OUT("  pre-register: %s (%" B_PRIx32 ")\n", strerror(error),
1914				error));
1915		}
1916
1917		// launch the app
1918		if (error == B_OK && !alreadyRunning) {
1919			DBG(OUT("  token: %" B_PRIu32 "\n", appToken));
1920			// load the app image
1921			appThread = load_image(argVector.Count(),
1922				const_cast<const char**>(argVector.Args()), environment);
1923
1924			// get the app team
1925			if (appThread >= 0) {
1926				thread_info threadInfo;
1927				error = get_thread_info(appThread, &threadInfo);
1928				if (error == B_OK)
1929					team = threadInfo.team;
1930			} else if (wasDocument && appThread == B_NOT_AN_EXECUTABLE)
1931				error = B_LAUNCH_FAILED_EXECUTABLE;
1932			else
1933				error = appThread;
1934
1935			DBG(OUT("  load image: %s (%" B_PRIx32 ")\n", strerror(error),
1936				error));
1937			// finish the registration
1938			if (error == B_OK && !isScript && !fNoRegistrar)
1939				error = _SetThreadAndTeam(appToken, appThread, team, &appPort);
1940
1941			DBG(OUT("  set thread and team: %s (%" B_PRIx32 ")\n",
1942				strerror(error), error));
1943			// resume the launched team
1944			if (error == B_OK && !launchSuspended)
1945				error = resume_thread(appThread);
1946
1947			DBG(OUT("  resume thread: %s (%" B_PRIx32 ")\n", strerror(error),
1948				error));
1949			// on error: kill the launched team and unregister the app
1950			if (error != B_OK) {
1951				if (appThread >= 0)
1952					kill_thread(appThread);
1953
1954				if (!isScript) {
1955					if (!fNoRegistrar)
1956						_RemovePreRegApp(appToken);
1957
1958					if (!wasDocument) {
1959						// Remove app hint if it's this one
1960						BMimeType appType(signature);
1961						entry_ref hintRef;
1962
1963						if (appType.InitCheck() == B_OK
1964							&& appType.GetAppHint(&hintRef) == B_OK
1965							&& appRef == hintRef) {
1966							appType.SetAppHint(NULL);
1967							// try again
1968							continue;
1969						}
1970					}
1971				}
1972			}
1973		}
1974		// Don't try again
1975		break;
1976	}
1977
1978	if (alreadyRunning && current_team() == team) {
1979		// The target team is calling us, so we don't send it the message
1980		// to prevent an endless loop
1981		error = B_BAD_VALUE;
1982	}
1983
1984	// send "on launch" messages
1985	if (error == B_OK && !fNoRegistrar) {
1986		// If the target app is B_ARGV_ONLY, only if it is newly launched
1987		// messages are sent to it (namely B_ARGV_RECEIVED and B_READY_TO_RUN).
1988		// An already running B_ARGV_ONLY app won't get any messages.
1989		bool argvOnly = (appFlags & B_ARGV_ONLY) != 0
1990			|| (alreadyRunning && (otherAppFlags & B_ARGV_ONLY) != 0);
1991		const BList* _messageList = (argvOnly ? NULL : messageList);
1992		// don't send ref, if it refers to the app or is included in the
1993		// argument vector
1994		const entry_ref* _ref = argvOnly || !wasDocument
1995			|| argVector.Count() > 1 ? NULL : docRef;
1996		if (!(argvOnly && alreadyRunning)) {
1997			_SendToRunning(team, argVector.Count(), argVector.Args(),
1998				_messageList, _ref, alreadyRunning);
1999		}
2000	}
2001
2002	// set return values
2003	if (error == B_OK) {
2004		if (alreadyRunning)
2005			error = B_ALREADY_RUNNING;
2006		else if (_appTeam)
2007			*_appTeam = team;
2008
2009		if (_appThread != NULL)
2010			*_appThread = appThread;
2011		if (_appPort != NULL)
2012			*_appPort = appPort;
2013		if (_appToken != NULL)
2014			*_appToken = appToken;
2015	}
2016
2017	DBG(OUT("BRoster::_LaunchApp() done: %s (%" B_PRIx32 ")\n",
2018		strerror(error), error));
2019
2020	return error;
2021}
2022
2023
2024void
2025BRoster::_SetAppFlags(team_id team, uint32 flags) const
2026{
2027}
2028
2029
2030void
2031BRoster::_DumpRoster() const
2032{
2033}
2034
2035
2036/*!	Finds an application associated with a MIME type or a file.
2037
2038	It does also supply the caller with some more information about the
2039	application, like signature, app flags and whether the supplied
2040	MIME type/entry_ref already identified an application.
2041
2042	At least one of \a inType or \a ref must not be \c NULL. If \a inType is
2043	supplied, \a ref is ignored.
2044
2045	If \a ref refers to a link, it is updated with the entry_ref for the
2046	resolved entry.
2047
2048	\see FindApp() for how the application is searched.
2049
2050	\a signature is set to a string with length 0, if the found
2051	application has no signature.
2052
2053	\param inType The MIME type for which an application shall be found.
2054	       May be \c NULL.
2055	\param ref The file for which an application shall be found.
2056	       May be \c NULL.
2057	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2058	       a reference to the found application's executable. May be \c NULL.
2059	\param signature A pointer to a pre-allocated char buffer of at
2060	       least size \c B_MIME_TYPE_LENGTH to be filled with the signature of
2061	       the found application. May be \c NULL.
2062	\param appFlags A pointer to a pre-allocated uint32 variable to be filled
2063	       with the app flags of the found application. May be \c NULL.
2064	\param wasDocument A pointer to a pre-allocated bool variable to be set to
2065	       \c true, if the supplied file was not identifying an application,
2066	       to \c false otherwise. Has no meaning, if a \a inType is supplied.
2067	       May be \c NULL.
2068
2069	\return A status code.
2070	\retval B_OK Everything went fine.
2071	\retval B_BAD_VALUE \c NULL \a inType and \a ref.
2072
2073	\see FindApp() for other error codes.
2074*/
2075status_t
2076BRoster::_ResolveApp(const char* inType, entry_ref* ref,
2077	entry_ref* _appRef, char* _signature, uint32* _appFlags,
2078	bool* _wasDocument) const
2079{
2080	if ((inType == NULL && ref == NULL)
2081		|| (inType != NULL && strlen(inType) >= B_MIME_TYPE_LENGTH))
2082		return B_BAD_VALUE;
2083
2084	// find the app
2085	BMimeType appMeta;
2086	BFile appFile;
2087	entry_ref appRef;
2088	status_t error;
2089
2090	if (inType != NULL) {
2091		error = _TranslateType(inType, &appMeta, &appRef, &appFile);
2092		if (_wasDocument != NULL)
2093			*_wasDocument = !(appMeta == inType);
2094	} else {
2095		error = _TranslateRef(ref, &appMeta, &appRef, &appFile,
2096			_wasDocument);
2097	}
2098
2099	// create meta mime
2100	if (!fNoRegistrar && error == B_OK) {
2101		BPath path;
2102		if (path.SetTo(&appRef) == B_OK)
2103			create_app_meta_mime(path.Path(), false, true, false);
2104	}
2105
2106	// set the app hint on the type -- but only if the file has the
2107	// respective signature, otherwise unset the app hint
2108	BAppFileInfo appFileInfo;
2109	if (!fNoRegistrar && error == B_OK) {
2110		char signature[B_MIME_TYPE_LENGTH];
2111		if (appFileInfo.SetTo(&appFile) == B_OK
2112			&& appFileInfo.GetSignature(signature) == B_OK) {
2113			if (!strcasecmp(appMeta.Type(), signature)) {
2114				// Only set the app hint if there is none yet
2115				entry_ref dummyRef;
2116				if (appMeta.GetAppHint(&dummyRef) != B_OK)
2117					appMeta.SetAppHint(&appRef);
2118			} else {
2119				appMeta.SetAppHint(NULL);
2120				appMeta.SetTo(signature);
2121			}
2122		} else
2123			appMeta.SetAppHint(NULL);
2124	}
2125
2126	// set the return values
2127	if (error == B_OK) {
2128		if (_appRef)
2129			*_appRef = appRef;
2130
2131		if (_signature != NULL) {
2132			// there's no warranty, that appMeta is valid
2133			if (appMeta.IsValid()) {
2134				strlcpy(_signature, appMeta.Type(),
2135					B_MIME_TYPE_LENGTH);
2136			} else
2137				_signature[0] = '\0';
2138		}
2139
2140		if (_appFlags != NULL) {
2141			// if an error occurs here, we don't care and just set a default
2142			// value
2143			if (appFileInfo.InitCheck() != B_OK
2144				|| appFileInfo.GetAppFlags(_appFlags) != B_OK) {
2145				*_appFlags = B_REG_DEFAULT_APP_FLAGS;
2146			}
2147		}
2148	} else {
2149		// unset the ref on error
2150		if (_appRef != NULL)
2151			*_appRef = appRef;
2152	}
2153
2154	return error;
2155}
2156
2157
2158/*!	\brief Finds an application associated with a file.
2159
2160	\a appMeta is left unmodified, if the file is executable, but has no
2161	signature.
2162
2163	\see FindApp() for how the application is searched.
2164
2165	If \a ref refers to a link, it is updated with the entry_ref for the
2166	resolved entry.
2167
2168	\param ref The file for which an application shall be found.
2169	\param appMeta A pointer to a pre-allocated BMimeType to be set to the
2170	       signature of the found application.
2171	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2172	       a reference to the found application's executable.
2173	\param appFile A pointer to a pre-allocated BFile to be set to the
2174	       executable of the found application.
2175	\param wasDocument A pointer to a pre-allocated bool variable to be set to
2176	       \c true, if the supplied file was not identifying an application,
2177	       to \c false otherwise. May be \c NULL.
2178
2179	\return A status code.
2180	\retval B_OK: Everything went fine.
2181	\retval B_BAD_VALUE: \c NULL \a ref, \a appMeta, \a appRef or \a appFile.
2182
2183	\see FindApp() for other error codes.
2184*/
2185status_t
2186BRoster::_TranslateRef(entry_ref* ref, BMimeType* appMeta,
2187	entry_ref* appRef, BFile* appFile, bool* _wasDocument) const
2188{
2189	if (ref == NULL || appMeta == NULL || appRef == NULL || appFile == NULL)
2190		return B_BAD_VALUE;
2191
2192	// resolve ref, if necessary
2193	BEntry entry;
2194	status_t error = entry.SetTo(ref, false);
2195	if (error != B_OK)
2196		return error;
2197
2198	if (entry.IsSymLink()) {
2199		// ref refers to a link
2200		if (entry.SetTo(ref, true) != B_OK || entry.GetRef(ref) != B_OK)
2201			return B_LAUNCH_FAILED_NO_RESOLVE_LINK;
2202	}
2203
2204	// init node
2205	BNode node;
2206	error = node.SetTo(ref);
2207	if (error != B_OK)
2208		return error;
2209
2210	// get permissions
2211	mode_t permissions;
2212	error = node.GetPermissions(&permissions);
2213	if (error != B_OK)
2214		return error;
2215
2216	if ((permissions & S_IXUSR) != 0 && node.IsFile()) {
2217		// node is executable and a file
2218		error = appFile->SetTo(ref, B_READ_ONLY);
2219		if (error != B_OK)
2220			return error;
2221
2222		// get the app's signature via a BAppFileInfo
2223		BAppFileInfo appFileInfo;
2224		error = appFileInfo.SetTo(appFile);
2225		if (error != B_OK)
2226			return error;
2227
2228		// don't worry, if the file doesn't have a signature, just
2229		// unset the supplied object
2230		char type[B_MIME_TYPE_LENGTH];
2231		if (appFileInfo.GetSignature(type) == B_OK) {
2232			error = appMeta->SetTo(type);
2233			if (error != B_OK)
2234				return error;
2235		} else
2236			appMeta->Unset();
2237
2238		// If the file type indicates that the file is an application, we've
2239		// definitely got what we're looking for.
2240		bool isDocument = true;
2241		if (_GetFileType(ref, &appFileInfo, type) == B_OK
2242			&& strcasecmp(type, B_APP_MIME_TYPE) == 0) {
2243			isDocument = false;
2244		}
2245
2246		// If our file is not an application executable, we probably have a
2247		// script. Check whether the file has a preferred application set. If
2248		// so, we fall through and use the preferred app instead. Otherwise
2249		// we're done.
2250		char preferredApp[B_MIME_TYPE_LENGTH];
2251		if (!isDocument || appFileInfo.GetPreferredApp(preferredApp) != B_OK) {
2252			*appRef = *ref;
2253			if (_wasDocument != NULL)
2254				*_wasDocument = isDocument;
2255
2256			return B_OK;
2257		}
2258
2259		// Executable file, but not an application, and it has a preferred
2260		// application set. Fall through...
2261	}
2262
2263	// the node is not exectuable or not a file
2264	// init a node info
2265	BNodeInfo nodeInfo;
2266	error = nodeInfo.SetTo(&node);
2267	if (error != B_OK)
2268		return error;
2269
2270	// if the file has a preferred app, let _TranslateType() find
2271	// it for us
2272	char preferredApp[B_MIME_TYPE_LENGTH];
2273	if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
2274		&& _TranslateType(preferredApp, appMeta, appRef, appFile) == B_OK) {
2275		if (_wasDocument != NULL)
2276			*_wasDocument = true;
2277
2278		return B_OK;
2279	}
2280
2281	// no preferred app or existing one was not found -- we
2282	// need to get the file's type
2283
2284	// get the type from the file
2285	char fileType[B_MIME_TYPE_LENGTH];
2286	error = _GetFileType(ref, &nodeInfo, fileType);
2287	if (error != B_OK)
2288		return error;
2289
2290	// now let _TranslateType() do the actual work
2291	error = _TranslateType(fileType, appMeta, appRef, appFile);
2292	if (error != B_OK)
2293		return error;
2294
2295	if (_wasDocument != NULL)
2296		*_wasDocument = true;
2297
2298	return B_OK;
2299}
2300
2301
2302/*!	Finds an application associated with a MIME type.
2303
2304	\see FindApp() for how the application is searched.
2305
2306	\param mimeType The MIME type for which an application shall be found.
2307	\param appMeta A pointer to a pre-allocated BMimeType to be set to the
2308	       signature of the found application.
2309	\param appRef A pointer to a pre-allocated entry_ref to be filled with
2310	       a reference to the found application's executable.
2311	\param appFile A pointer to a pre-allocated BFile to be set to the
2312	       executable of the found application.
2313
2314	\return A status code.
2315	\retval B_OK Everything went fine.
2316	\retval B_BAD_VALUE \c NULL \a mimeType, \a appMeta, \a appRef or
2317	        \a appFile.
2318
2319	\see FindApp() for other error codes.
2320*/
2321status_t
2322BRoster::_TranslateType(const char* mimeType, BMimeType* appMeta,
2323	entry_ref* appRef, BFile* appFile) const
2324{
2325	if (mimeType == NULL || appMeta == NULL || appRef == NULL
2326		|| appFile == NULL || strlen(mimeType) >= B_MIME_TYPE_LENGTH) {
2327		return B_BAD_VALUE;
2328	}
2329
2330	// Create a BMimeType and check, if the type is installed.
2331	BMimeType type;
2332	status_t error = type.SetTo(mimeType);
2333
2334	// Get the preferred apps from the sub and super type.
2335	char primarySignature[B_MIME_TYPE_LENGTH];
2336	char secondarySignature[B_MIME_TYPE_LENGTH];
2337	primarySignature[0] = '\0';
2338	secondarySignature[0] = '\0';
2339
2340	if (error == B_OK) {
2341		BMimeType superType;
2342		if (type.GetSupertype(&superType) == B_OK)
2343			superType.GetPreferredApp(secondarySignature);
2344		if (type.IsInstalled()) {
2345			if (type.GetPreferredApp(primarySignature) != B_OK) {
2346				// The type is installed, but has no preferred app.
2347				primarySignature[0] = '\0';
2348			} else if (!strcmp(primarySignature, secondarySignature)) {
2349				// Both types have the same preferred app, there is
2350				// no point in testing it twice.
2351				secondarySignature[0] = '\0';
2352			}
2353		} else {
2354			// The type is not installed. We assume it is an app signature.
2355			strlcpy(primarySignature, mimeType, sizeof(primarySignature));
2356		}
2357	}
2358
2359	// We will use this BMessage "signatures" to hold all supporting apps
2360	// so we can iterator over them in the preferred order. We include
2361	// the supporting apps in such a way that the configured preferred
2362	// applications for the MIME type are in front of other supporting
2363	// applications for the sub and the super type respectively.
2364	const char* kSigField = "applications";
2365	BMessage signatures;
2366	bool addedSecondarySignature = false;
2367	if (error == B_OK) {
2368		if (primarySignature[0] != '\0')
2369			error = signatures.AddString(kSigField, primarySignature);
2370		else {
2371			// If there is a preferred app configured for the super type,
2372			// but no preferred type for the sub-type, add the preferred
2373			// super type handler in front of any other handlers. This way
2374			// we fall-back to non-preferred but supporting apps only in the
2375			// case when there is a preferred handler for the sub-type but
2376			// it cannot be resolved (misconfiguration).
2377			if (secondarySignature[0] != '\0') {
2378				error = signatures.AddString(kSigField, secondarySignature);
2379				addedSecondarySignature = true;
2380			}
2381		}
2382	}
2383
2384	BMessage supportingSignatures;
2385	if (error == B_OK
2386		&& type.GetSupportingApps(&supportingSignatures) == B_OK) {
2387		int32 subCount;
2388		if (supportingSignatures.FindInt32("be:sub", &subCount) != B_OK)
2389			subCount = 0;
2390		// Add all signatures with direct support for the sub-type.
2391		const char* supportingType;
2392		if (!addedSecondarySignature) {
2393			// Try to add the secondarySignature in front of all other
2394			// supporting apps, if we find it among those.
2395			for (int32 i = 0; error == B_OK && i < subCount
2396					&& supportingSignatures.FindString(kSigField, i,
2397						&supportingType) == B_OK; i++) {
2398				if (strcmp(primarySignature, supportingType) != 0
2399					&& strcmp(secondarySignature, supportingType) == 0) {
2400					error = signatures.AddString(kSigField, supportingType);
2401					addedSecondarySignature = true;
2402					break;
2403				}
2404			}
2405		}
2406
2407		for (int32 i = 0; error == B_OK && i < subCount
2408				&& supportingSignatures.FindString(kSigField, i,
2409					&supportingType) == B_OK; i++) {
2410			if (strcmp(primarySignature, supportingType) != 0
2411				&& strcmp(secondarySignature, supportingType) != 0) {
2412				error = signatures.AddString(kSigField, supportingType);
2413			}
2414		}
2415
2416		// Add the preferred type of the super type here before adding
2417		// the other types supporting the super type, but only if we have
2418		// not already added it in case there was no preferred app for the
2419		// sub-type configured.
2420		if (error == B_OK && !addedSecondarySignature
2421			&& secondarySignature[0] != '\0') {
2422			error = signatures.AddString(kSigField, secondarySignature);
2423		}
2424
2425		// Add all signatures with support for the super-type.
2426		for (int32 i = subCount; error == B_OK
2427				&& supportingSignatures.FindString(kSigField, i,
2428					&supportingType) == B_OK; i++) {
2429			// Don't add the signature if it's one of the preferred apps
2430			// already.
2431			if (strcmp(primarySignature, supportingType) != 0
2432				&& strcmp(secondarySignature, supportingType) != 0) {
2433				error = signatures.AddString(kSigField, supportingType);
2434			}
2435		}
2436	} else {
2437		// Failed to get supporting apps, just add the preferred apps.
2438		if (error == B_OK && secondarySignature[0] != '\0')
2439			error = signatures.AddString(kSigField, secondarySignature);
2440	}
2441
2442	if (error != B_OK)
2443		return error;
2444
2445	// Set an error in case we can't resolve a single supporting app.
2446	error = B_LAUNCH_FAILED_NO_PREFERRED_APP;
2447
2448	// See if we can find a good application that is valid from the messege.
2449	const char* signature;
2450	for (int32 i = 0;
2451		signatures.FindString(kSigField, i, &signature) == B_OK; i++) {
2452		if (signature[0] == '\0')
2453			continue;
2454
2455		error = appMeta->SetTo(signature);
2456
2457		// Check, whether the signature is installed and has an app hint
2458		bool appFound = false;
2459		if (error == B_OK && appMeta->GetAppHint(appRef) == B_OK) {
2460			// Resolve symbolic links, if necessary
2461			BEntry entry;
2462			if (entry.SetTo(appRef, true) == B_OK && entry.IsFile()
2463				&& entry.GetRef(appRef) == B_OK) {
2464				appFound = true;
2465			} else {
2466				// Bad app hint -- remove it
2467				appMeta->SetAppHint(NULL);
2468			}
2469		}
2470
2471		// In case there is no app hint or it is invalid, we need to query for
2472		// the app.
2473		if (error == B_OK && !appFound)
2474			error = query_for_app(appMeta->Type(), appRef);
2475
2476		if (error == B_OK)
2477			error = appFile->SetTo(appRef, B_READ_ONLY);
2478
2479		// check, whether the app can be used
2480		if (error == B_OK)
2481			error = can_app_be_used(appRef);
2482
2483		if (error == B_OK)
2484			break;
2485	}
2486
2487	return error;
2488}
2489
2490
2491/*!	Gets the type of a file either from the node info or by sniffing.
2492
2493	The method first tries to get the file type from the supplied node info. If
2494	that didn't work, the given entry ref is sniffed.
2495
2496	\param file An entry_ref referring to the file in question.
2497	\param nodeInfo A BNodeInfo initialized to the file.
2498	\param mimeType A pointer to a pre-allocated char buffer of at least size
2499	       \c B_MIME_TYPE_LENGTH to be filled with the MIME type sniffed for
2500	       the file.
2501
2502	\return A status code.
2503	\retval B_OK Everything went fine.
2504	\retval B_BAD_VALUE \c NULL \a file, \a nodeInfo or \a mimeType.
2505*/
2506status_t
2507BRoster::_GetFileType(const entry_ref* file, BNodeInfo* nodeInfo,
2508	char* mimeType) const
2509{
2510	// first try the node info
2511	if (nodeInfo->GetType(mimeType) == B_OK)
2512		return B_OK;
2513
2514	if (fNoRegistrar)
2515		return B_NO_INIT;
2516
2517	// Try to update the file's MIME info and just read the updated type.
2518	// If that fails, sniff manually.
2519	BPath path;
2520	if (path.SetTo(file) != B_OK
2521		|| update_mime_info(path.Path(), false, true, false) != B_OK
2522		|| nodeInfo->GetType(mimeType) != B_OK) {
2523		BMimeType type;
2524		status_t error = BMimeType::GuessMimeType(file, &type);
2525		if (error != B_OK)
2526			return error;
2527
2528		if (!type.IsValid())
2529			return B_BAD_VALUE;
2530
2531		strlcpy(mimeType, type.Type(), B_MIME_TYPE_LENGTH);
2532	}
2533
2534	return B_OK;
2535}
2536
2537
2538/*!	Sends messages to a running team.
2539
2540	In particular those messages are \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED,
2541	\c B_READY_TO_RUN and other, arbitrary, ones.
2542
2543	If \a messageList is not \c NULL or empty, those messages are sent first,
2544	then follow \c B_ARGV_RECEIVED, \c B_REFS_RECEIVED and finally
2545	\c B_READ_TO_RUN.
2546
2547	\c B_ARGV_RECEIVED is sent only, if \a args is not \c NULL and contains
2548	more than one element. \c B_REFS_RECEIVED is sent only, if \a ref is not
2549	\c NULL.
2550
2551	The ownership of all supplied objects retains to the caller.
2552
2553	\param team The team ID of the target application.
2554	\param argc Number of elements in \a args.
2555	\param args Argument vector to be sent to the target. May be \c NULL.
2556	\param messageList List of BMessages to be sent to the target. May be
2557	       \c NULL or empty.
2558	\param ref entry_ref to be sent to the target. May be \c NULL.
2559	\param alreadyRunning \c true, if the target app is not newly launched,
2560	       but was already running, \c false otherwise (a \c B_READY_TO_RUN
2561	       message will be sent in this case).
2562
2563	\return \c B_OK if everything went fine, or an error code otherwise.
2564*/
2565status_t
2566BRoster::_SendToRunning(team_id team, int argc, const char* const* args,
2567	const BList* messageList, const entry_ref* ref,
2568	bool alreadyRunning) const
2569{
2570	status_t error = B_OK;
2571
2572	// Construct a messenger to the app: We can't use the public constructor,
2573	// since the target application may be B_ARGV_ONLY.
2574	app_info info;
2575	error = GetRunningAppInfo(team, &info);
2576	if (error == B_OK) {
2577		BMessenger messenger;
2578		BMessenger::Private(messenger).SetTo(team, info.port,
2579			B_PREFERRED_TOKEN);
2580
2581		// send messages from the list
2582		if (messageList != NULL) {
2583			for (int32 i = 0;
2584					BMessage* message = (BMessage*)messageList->ItemAt(i);
2585					i++) {
2586				messenger.SendMessage(message);
2587			}
2588		}
2589
2590		// send B_ARGV_RECEIVED or B_REFS_RECEIVED or B_SILENT_RELAUNCH
2591		// (if already running)
2592		if (args != NULL && argc > 1) {
2593			BMessage message(B_ARGV_RECEIVED);
2594			message.AddInt32("argc", argc);
2595			for (int32 i = 0; i < argc; i++)
2596				message.AddString("argv", args[i]);
2597
2598			// also add current working directory
2599			char cwd[B_PATH_NAME_LENGTH];
2600			if (getcwd(cwd, B_PATH_NAME_LENGTH) != NULL)
2601				message.AddString("cwd", cwd);
2602
2603			messenger.SendMessage(&message);
2604		} else if (ref != NULL) {
2605			DBG(OUT("_SendToRunning : B_REFS_RECEIVED\n"));
2606			BMessage message(B_REFS_RECEIVED);
2607			message.AddRef("refs", ref);
2608			messenger.SendMessage(&message);
2609		} else if (alreadyRunning && (!messageList || messageList->IsEmpty()))
2610			messenger.SendMessage(B_SILENT_RELAUNCH);
2611
2612		if (!alreadyRunning) {
2613			// send B_READY_TO_RUN
2614			DBG(OUT("_SendToRunning : B_READY_TO_RUN\n"));
2615			messenger.SendMessage(B_READY_TO_RUN);
2616		}
2617	}
2618
2619	return error;
2620}
2621
2622
2623/*!	Allows to use certain functionality of the BRoster class without
2624	accessing the registrar.
2625*/
2626void
2627BRoster::_SetWithoutRegistrar(bool noRegistrar)
2628{
2629	fNoRegistrar = noRegistrar;
2630}
2631
2632
2633void
2634BRoster::_InitMessenger()
2635{
2636	DBG(OUT("BRoster::InitMessengers()\n"));
2637
2638	// find the registrar port
2639
2640#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
2641	BMessage data;
2642	if (BLaunchRoster().GetData(B_REGISTRAR_SIGNATURE, data) == B_OK) {
2643		port_id port = data.GetInt32("port", -1);
2644		team_id team = data.GetInt32("team", -1);
2645
2646		if (port >= 0 && team != current_team()) {
2647			// Make sure we aren't the registrar ourselves.
2648
2649			DBG(OUT("  found roster port\n"));
2650
2651			BMessenger::Private(fMessenger).SetTo(team, port,
2652				B_PREFERRED_TOKEN);
2653		}
2654	}
2655#else
2656	port_id rosterPort = find_port(B_REGISTRAR_PORT_NAME);
2657	port_info info;
2658	if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) {
2659		DBG(OUT("  found roster port\n"));
2660
2661		BMessenger::Private(fMessenger).SetTo(info.team, rosterPort,
2662			B_PREFERRED_TOKEN);
2663	}
2664#endif
2665
2666	DBG(OUT("BRoster::InitMessengers() done\n"));
2667}
2668
2669
2670/*static*/ status_t
2671BRoster::_InitMimeMessenger(void* data)
2672{
2673	BRoster* roster = (BRoster*)data;
2674
2675	// ask for the MIME messenger
2676	// Generous 1s + 5s timeouts. It could actually be synchronous, but
2677	// timeouts allow us to debug the registrar main thread.
2678	BMessage request(B_REG_GET_MIME_MESSENGER);
2679	BMessage reply;
2680	status_t error = roster->fMessenger.SendMessage(&request, &reply,
2681		1000000LL, 5000000LL);
2682	if (error == B_OK && reply.what == B_REG_SUCCESS) {
2683		DBG(OUT("  got reply from roster\n"));
2684			reply.FindMessenger("messenger", &roster->fMimeMessenger);
2685	} else {
2686		DBG(OUT("  no (useful) reply from roster: error: %" B_PRIx32 ": %s\n",
2687			error, strerror(error)));
2688		if (error == B_OK)
2689			DBG(reply.PrintToStream());
2690	}
2691
2692	return error;
2693}
2694
2695
2696BMessenger&
2697BRoster::_MimeMessenger()
2698{
2699	__init_once(&fMimeMessengerInitOnce, &_InitMimeMessenger, this);
2700	return fMimeMessenger;
2701}
2702
2703
2704/*!	Sends a request to the roster to add the application with the
2705	given signature to the front of the recent apps list.
2706*/
2707void
2708BRoster::_AddToRecentApps(const char* signature) const
2709{
2710	status_t error = B_OK;
2711	// compose the request message
2712	BMessage request(B_REG_ADD_TO_RECENT_APPS);
2713	if (error == B_OK)
2714		error = request.AddString("app sig", signature);
2715
2716	// send the request
2717	BMessage reply;
2718	if (error == B_OK)
2719		error = fMessenger.SendMessage(&request, &reply);
2720
2721	// evaluate the reply
2722	status_t result;
2723	if (error == B_OK) {
2724		error = reply.what == B_REG_RESULT
2725			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2726	}
2727
2728	if (error == B_OK)
2729		error = reply.FindInt32("result", &result);
2730
2731	if (error == B_OK)
2732		error = result;
2733
2734	// Nothing to return... how sad :-(
2735	//return error;
2736}
2737
2738
2739//!	Sends a request to the roster to clear the recent documents list.
2740void
2741BRoster::_ClearRecentDocuments() const
2742{
2743	BMessage request(B_REG_CLEAR_RECENT_DOCUMENTS);
2744	BMessage reply;
2745	fMessenger.SendMessage(&request, &reply);
2746}
2747
2748
2749//!	Sends a request to the roster to clear the recent documents list.
2750void
2751BRoster::_ClearRecentFolders() const
2752{
2753	BMessage request(B_REG_CLEAR_RECENT_FOLDERS);
2754	BMessage reply;
2755	fMessenger.SendMessage(&request, &reply);
2756}
2757
2758
2759//! \brief Sends a request to the roster to clear the recent documents list.
2760void
2761BRoster::_ClearRecentApps() const
2762{
2763	BMessage request(B_REG_CLEAR_RECENT_APPS);
2764	BMessage reply;
2765	fMessenger.SendMessage(&request, &reply);
2766}
2767
2768
2769/*!	Loads the system's recently used document, folder, and
2770	application lists from the specified file.
2771
2772	\note The current lists are cleared before loading the new lists
2773
2774	\param filename The name of the file to load from
2775*/
2776void
2777BRoster::_LoadRecentLists(const char* filename) const
2778{
2779	status_t error = B_OK;
2780
2781	// compose the request message
2782	BMessage request(B_REG_LOAD_RECENT_LISTS);
2783	if (error == B_OK)
2784		error = request.AddString("filename", filename);
2785
2786	// send the request
2787	BMessage reply;
2788	if (error == B_OK)
2789		error = fMessenger.SendMessage(&request, &reply);
2790
2791	// evaluate the reply
2792	status_t result;
2793	if (error == B_OK) {
2794		error = reply.what == B_REG_RESULT
2795			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2796	}
2797	if (error == B_OK)
2798		error = reply.FindInt32("result", &result);
2799
2800	if (error == B_OK)
2801		error = result;
2802
2803	// Nothing to return... how sad :-(
2804	//return error;
2805}
2806
2807
2808/*!	Saves the system's recently used document, folder, and
2809	application lists to the specified file.
2810
2811	\param filename The name of the file to save to
2812*/
2813void
2814BRoster::_SaveRecentLists(const char* filename) const
2815{
2816	status_t error = B_OK;
2817
2818	// compose the request message
2819	BMessage request(B_REG_SAVE_RECENT_LISTS);
2820	if (error == B_OK)
2821		error = request.AddString("filename", filename);
2822
2823	// send the request
2824	BMessage reply;
2825	if (error == B_OK)
2826		error = fMessenger.SendMessage(&request, &reply);
2827
2828	// evaluate the reply
2829	status_t result;
2830	if (error == B_OK) {
2831		error = reply.what == B_REG_RESULT
2832			? (status_t)B_OK : (status_t)B_BAD_REPLY;
2833	}
2834	if (error == B_OK)
2835		error = reply.FindInt32("result", &result);
2836
2837	if (error == B_OK)
2838		error = result;
2839
2840	// Nothing to return... how sad :-(
2841	//return error;
2842}
2843