1//	LaunchTesterHelper.cpp
2
3#include <stdio.h>
4#include <string.h>
5
6#include <Autolock.h>
7#include <Entry.h>
8#include <List.h>
9#include <Message.h>
10#include <Messenger.h>
11#include <Path.h>
12
13#include "LaunchTesterHelper.h"
14#include "RosterTestAppDefs.h"
15
16/////////////////
17// LaunchContext
18
19const char *LaunchContext::kStandardArgv[] = {
20	"Some", "nice", "arguments"
21};
22const int32 LaunchContext::kStandardArgc
23	= sizeof(kStandardArgv) / sizeof(const char*);
24
25// Message
26class LaunchContext::Message {
27public:
28	BMessage	message;
29	bigtime_t	when;
30};
31
32// Sleeper
33class LaunchContext::Sleeper {
34public:
35	Sleeper() : fMessageCode(0), fSemaphore(-1) {}
36
37	~Sleeper()
38	{
39		delete_sem(fSemaphore);
40	}
41
42	status_t Init(uint32 messageCode)
43	{
44		fMessageCode = messageCode;
45		fSemaphore = create_sem(0, "sleeper sem");
46		return (fSemaphore >= 0 ? B_OK : fSemaphore);
47	}
48
49	uint32 MessageCode() const { return fMessageCode; }
50
51	status_t Sleep(bigtime_t timeout = B_INFINITE_TIMEOUT)
52	{
53		return acquire_sem_etc(fSemaphore, 1, B_RELATIVE_TIMEOUT, timeout);
54	}
55
56	status_t WakeUp()
57	{
58		return release_sem(fSemaphore);
59	}
60
61private:
62	uint32		fMessageCode;
63	sem_id		fSemaphore;
64};
65
66// AppInfo
67class LaunchContext::AppInfo {
68public:
69	AppInfo(team_id team)
70		: fTeam(team),
71		  fMessenger(),
72		  fMessages(),
73		  fTerminating(false)
74	{
75	}
76
77	~AppInfo()
78	{
79		for (int32 i = 0; LaunchContext::Message *message = MessageAt(i); i++)
80			delete message;
81	}
82
83	team_id Team() const
84	{
85		return fTeam;
86	}
87
88	void SetMessenger(BMessenger messenger)
89	{
90		fMessenger = messenger;
91	}
92
93	BMessenger Messenger() const
94	{
95		return fMessenger;
96	}
97
98	void Terminate()
99	{
100		if (!fTerminating && fMessenger.IsValid())
101		{
102			fTerminating = true;
103			fMessenger.SendMessage(B_QUIT_REQUESTED);
104		}
105	}
106
107	void AddMessage(const BMessage &_message)
108	{
109		LaunchContext::Message *message = new LaunchContext::Message;
110		message->message = _message;
111		message->when = system_time();
112		fMessages.AddItem(message);
113		NotifySleepers(_message.what);
114	}
115
116	LaunchContext::Message *RemoveMessage(int32 index)
117	{
118		return (LaunchContext::Message*)fMessages.RemoveItem(index);
119	}
120
121	LaunchContext::Message *MessageAt(int32 index) const
122	{
123		return (LaunchContext::Message*)fMessages.ItemAt(index);
124	}
125
126	LaunchContext::Message *FindMessage(uint32 messageCode,
127										int32 startIndex = 0) const
128	{
129		LaunchContext::Message *message = NULL;
130		for (int32 i = startIndex; (message = MessageAt(i)) != NULL; i++) {
131			if (message->message.what == messageCode)
132				break;
133		}
134		return message;
135	}
136
137	void AddSleeper(Sleeper *sleeper)
138	{
139		fSleepers.AddItem(sleeper);
140	}
141
142	void RemoveSleeper(Sleeper *sleeper)
143	{
144		fSleepers.RemoveItem(sleeper);
145	}
146
147	void NotifySleepers(uint32 messageCode)
148	{
149		for (int32 i = 0;
150			 Sleeper *sleeper = (Sleeper*)fSleepers.ItemAt(i);
151			 i++) {
152			if (sleeper->MessageCode() == messageCode)
153				sleeper->WakeUp();
154		}
155	}
156
157private:
158	team_id		fTeam;
159	BMessenger	fMessenger;
160	BList		fMessages;
161	bool		fTerminating;
162	BList		fSleepers;
163};
164
165// constructor
166LaunchContext::LaunchContext()
167	: fAppInfos(),
168	  fSleepers(),
169	  fLock(),
170	  fAppThread(B_ERROR),
171	  fTerminator(B_ERROR),
172	  fTerminating(false)
173{
174	RosterLaunchApp *app = dynamic_cast<RosterLaunchApp*>(be_app);
175	app->SetLaunchContext(this);
176	app->Unlock();
177	fAppThread = spawn_thread(AppThreadEntry, "app thread", B_NORMAL_PRIORITY,
178							  this);
179	if (fAppThread >= 0) {
180		status_t error = resume_thread(fAppThread);
181		if (error != B_OK)
182			printf("ERROR: Couldn't resume app thread: %s\n", strerror(error));
183	} else
184		printf("ERROR: Couldn't spawn app thread: %s\n", strerror(fAppThread));
185}
186
187// destructor
188LaunchContext::~LaunchContext()
189{
190	Terminate();
191	// cleanup
192	for (int32 i = 0; AppInfo *info = AppInfoAt(i); i++)
193		delete info;
194	for (int32 i = 0;
195		 BMessage *message = (BMessage*)fStandardMessages.ItemAt(i);
196		 i++) {
197		delete message;
198	}
199}
200
201// ()
202status_t
203LaunchContext::operator()(LaunchCaller &caller, const char *type,
204						  team_id *team)
205{
206	BMessage message1(MSG_1);
207	BMessage message2(MSG_2);
208	BMessage message3(MSG_3);
209	BList messages;
210	messages.AddItem(&message1);
211	messages.AddItem(&message2);
212	messages.AddItem(&message3);
213	return (*this)(caller, type, &messages, kStandardArgc, kStandardArgv,
214				   team);
215}
216
217// ()
218status_t
219LaunchContext::operator()(LaunchCaller &caller, const char *type,
220						  BList *messages, int32 argc, const char **argv,
221						  team_id *team)
222{
223	BAutolock _lock(fLock);
224	status_t result = caller(type, messages, argc, argv, team);
225	if (result == B_OK && team)
226		CreateAppInfo(*team);
227	return result;
228}
229
230// HandleMessage
231void
232LaunchContext::HandleMessage(BMessage *message)
233{
234//printf("LaunchContext::HandleMessage(%6ld: %.4s)\n",
235//message->ReturnAddress().Team(), (char*)&message->what);
236//if (message->what == MSG_MESSAGE_RECEIVED) {
237//	BMessage sentMessage;
238//	message->FindMessage("message", &sentMessage);
239//	team_id sender = -1;
240//	message->FindInt32("sender", &sender);
241//	printf("  <- %6ld: %.4s\n", sender, (char*)&sentMessage.what);
242//}
243
244	BAutolock _lock(fLock);
245	switch (message->what) {
246		case MSG_STARTED:
247		case MSG_TERMINATED:
248		case MSG_MAIN_ARGS:
249		case MSG_ARGV_RECEIVED:
250		case MSG_REFS_RECEIVED:
251		case MSG_MESSAGE_RECEIVED:
252		case MSG_QUIT_REQUESTED:
253		case MSG_READY_TO_RUN:
254		case MSG_1:
255		case MSG_2:
256		case MSG_3:
257		case MSG_REPLY:
258		{
259			BMessenger messenger = message->ReturnAddress();
260			// add the message to the respective team's message list
261			// Note: catch messages that have not been sent by us or the
262			// remote team. The R5 registrar seems to send a B_REPLY message
263			// sometimes.
264			team_id sender = -1;
265			bool dontIgnore = false;
266			if (message->FindInt32("sender", &sender) != B_OK
267				|| sender == be_app->Team()
268				|| sender == messenger.Team()
269				|| (message->FindBool("don't ignore", &dontIgnore) == B_OK)
270					&& dontIgnore) {
271				AppInfo *info = CreateAppInfo(messenger);
272				info->AddMessage(*message);
273				if (fTerminating)
274					TerminateApp(info);
275			}
276			NotifySleepers(message->what);
277			break;
278		}
279		default:
280			break;
281	}
282}
283
284// Terminate
285void
286LaunchContext::Terminate()
287{
288	fLock.Lock();
289	if (!fTerminating) {
290		fTerminating = true;
291		// tell all test apps to quit
292		for (int32 i = 0; AppInfo *info = AppInfoAt(i); i++)
293			TerminateApp(info);
294		// start terminator
295		fTerminator = spawn_thread(TerminatorEntry, "terminator",
296								   B_NORMAL_PRIORITY, this);
297		if (fTerminator >= 0) {
298			status_t error = resume_thread(fTerminator);
299			if (error != B_OK) {
300				printf("ERROR: Couldn't resume terminator thread: %s\n",
301					   strerror(error));
302			}
303		} else {
304			printf("ERROR: Couldn't spawn terminator thread: %s\n",
305				   strerror(fTerminator));
306		}
307	}
308	fLock.Unlock();
309	// wait for the app to terminate
310	int32 dummy;
311	wait_for_thread(fAppThread, &dummy);
312	wait_for_thread(fTerminator, &dummy);
313}
314
315// TerminateApp
316void
317LaunchContext::TerminateApp(team_id team, bool wait)
318{
319	fLock.Lock();
320	if (AppInfo *info = AppInfoFor(team))
321		TerminateApp(info);
322	fLock.Unlock();
323	if (wait)
324		WaitForMessage(team, MSG_TERMINATED);
325}
326
327// TeamAt
328team_id
329LaunchContext::TeamAt(int32 index) const
330{
331	BAutolock _lock(fLock);
332	team_id team = B_ERROR;
333	if (AppInfo *info = AppInfoAt(index))
334		team = info->Team();
335	return team;
336}
337
338// AppMessengerFor
339BMessenger
340LaunchContext::AppMessengerFor(team_id team) const
341{
342	BAutolock _lock(fLock);
343	BMessenger result;
344	if (AppInfoFor(team)) {
345		// We need to do some hacking.
346		BMessenger messenger;
347		struct fake_messenger {
348			port_id	fPort;
349			int32	fHandlerToken;
350			team_id	fTeam;
351			int32	extra0;
352			int32	extra1;
353			bool	fPreferredTarget;
354			bool	extra2;
355			bool	extra3;
356			bool	extra4;
357		} &fake = *(fake_messenger*)&messenger;
358		status_t error = B_OK;
359		fake.fTeam = team;
360		// find app looper port
361		bool found = false;
362		int32 cookie = 0;
363		port_info info;
364		while (error == B_OK && !found) {
365			error = get_next_port_info(fake.fTeam, &cookie, &info);
366			found = (error == B_OK
367					 && (!strcmp("AppLooperPort", info.name)
368					 	 || !strcmp("rAppLooperPort", info.name)));
369		}
370		// init messenger
371		if (error == B_OK) {
372			fake.fPort = info.port;
373			fake.fHandlerToken = 0;
374			fake.fPreferredTarget = true;
375		}
376		if (error == B_OK)
377			result = messenger;
378	}
379	return result;
380}
381
382// NextMessageFrom
383BMessage*
384LaunchContext::NextMessageFrom(team_id team, int32 &cookie, bigtime_t *time)
385{
386	BAutolock _lock(fLock);
387	BMessage *message = NULL;
388	if (AppInfo *info = AppInfoFor(team)) {
389		if (Message *contextMessage = info->MessageAt(cookie++))
390			message = &contextMessage->message;
391	}
392	return message;
393}
394
395// CheckNextMessage
396bool
397LaunchContext::CheckNextMessage(LaunchCaller &caller, team_id team,
398								int32 &cookie, uint32 what)
399{
400	BMessage *message = NextMessageFrom(team, cookie);
401	return (message && message->what == what);
402}
403
404// CheckMainArgsMessage
405bool
406LaunchContext::CheckMainArgsMessage(LaunchCaller &caller, team_id team,
407								int32 &cookie, const entry_ref *appRef,
408								bool useRef)
409{
410	int32 argc = 0;
411	const char **argv = NULL;
412	if (caller.SupportsArgv()) {
413		argc = kStandardArgc;
414		argv = kStandardArgv;
415	}
416	return CheckMainArgsMessage(caller, team, cookie, appRef, argc, argv,
417								useRef);
418}
419
420// CheckMainArgsMessage
421bool
422LaunchContext::CheckMainArgsMessage(LaunchCaller &caller, team_id team,
423								int32 &cookie, const entry_ref *appRef,
424								int32 argc, const char **argv, bool useRef)
425{
426	useRef &= caller.SupportsArgv() && argv && argc > 0;
427	const entry_ref *ref = (useRef ? caller.Ref() : NULL);
428	return CheckArgsMessage(caller, team, cookie, appRef, ref, argc, argv,
429							MSG_MAIN_ARGS);
430}
431
432// CheckArgvMessage
433bool
434LaunchContext::CheckArgvMessage(LaunchCaller &caller, team_id team,
435								int32 &cookie, const entry_ref *appRef,
436								bool useRef)
437{
438	bool result = true;
439	if (caller.SupportsArgv()) {
440		result = CheckArgvMessage(caller, team, cookie, appRef, kStandardArgc,
441								  kStandardArgv, useRef);
442	}
443	return result;
444}
445
446// CheckArgvMessage
447bool
448LaunchContext::CheckArgvMessage(LaunchCaller &caller, team_id team,
449								int32 &cookie, const entry_ref *appRef,
450								int32 argc, const char **argv, bool useRef)
451{
452	const entry_ref *ref = (useRef ? caller.Ref() : NULL);
453	return CheckArgvMessage(caller, team, cookie, appRef, ref , argc, argv);
454}
455
456// CheckArgvMessage
457bool
458LaunchContext::CheckArgvMessage(LaunchCaller &caller, team_id team,
459								int32 &cookie, const entry_ref *appRef,
460								const entry_ref *ref , int32 argc,
461								const char **argv)
462{
463	return CheckArgsMessage(caller, team, cookie, appRef, ref, argc, argv,
464							MSG_ARGV_RECEIVED);
465}
466
467// CheckArgsMessage
468bool
469LaunchContext::CheckArgsMessage(LaunchCaller &caller, team_id team,
470								int32 &cookie, const entry_ref *appRef,
471								const entry_ref *ref , int32 argc,
472								const char **argv, uint32 messageCode)
473{
474	BMessage *message = NextMessageFrom(team, cookie);
475	bool result = (message && message->what == messageCode);
476	int32 additionalRef = (ref ? 1 : 0);
477	// check argc
478	int32 foundArgc = -1;
479	if (result) {
480		result = (message->FindInt32("argc", &foundArgc) == B_OK
481				  && foundArgc == argc + 1 + additionalRef);
482if (!result)
483printf("argc differ: %ld vs %ld + 1 + %ld\n", foundArgc, argc, additionalRef);
484	}
485	// check argv[0] (the app file name)
486	if (result) {
487		BPath path;
488		const char *arg = NULL;
489		result = (path.SetTo(appRef) == B_OK
490				  && message->FindString("argv", 0, &arg) == B_OK
491				  && path == arg);
492if (!result)
493printf("app paths differ: `%s' vs `%s'\n", arg, path.Path());
494	}
495	// check remaining argv
496	for (int32 i = 1; result && i < argc; i++) {
497		const char *arg = NULL;
498		result = (message->FindString("argv", i, &arg) == B_OK
499				  && !strcmp(arg, argv[i - 1]));
500if (!result)
501printf("arg[%ld] differ: `%s' vs `%s'\n", i, arg, argv[i - 1]);
502	}
503	// check additional ref
504	if (result && additionalRef) {
505		BPath path;
506		const char *arg = NULL;
507		result = (path.SetTo(ref) == B_OK
508				  && message->FindString("argv", argc + 1, &arg) == B_OK
509				  && path == arg);
510if (!result)
511printf("document paths differ: `%s' vs `%s'\n", arg, path.Path());
512	}
513	return result;
514}
515
516// CheckMessageMessages
517bool
518LaunchContext::CheckMessageMessages(LaunchCaller &caller, team_id team,
519									int32 &cookie)
520{
521	BAutolock _lock(fLock);
522	bool result = true;
523	for (int32 i = 0; i < 3; i++)
524		result &= CheckMessageMessage(caller, team, cookie, i);
525	return result;
526}
527
528// CheckMessageMessage
529bool
530LaunchContext::CheckMessageMessage(LaunchCaller &caller, team_id team,
531								   int32 &cookie, int32 index)
532{
533	bool result = true;
534	if (caller.SupportsMessages() > index && index < 3) {
535		uint32 commands[] = { MSG_1, MSG_2, MSG_3 };
536		BMessage message(commands[index]);
537		result = CheckMessageMessage(caller, team, cookie, &message);
538	}
539	return result;
540}
541
542// CheckMessageMessage
543bool
544LaunchContext::CheckMessageMessage(LaunchCaller &caller, team_id team,
545								   int32 &cookie,
546								   const BMessage *expectedMessage)
547{
548	BMessage *message = NextMessageFrom(team, cookie);
549	bool result = (message && message->what == MSG_MESSAGE_RECEIVED);
550	if (result) {
551		BMessage sentMessage;
552		result = (message->FindMessage("message", &sentMessage) == B_OK
553				  && sentMessage.what == expectedMessage->what);
554	}
555	return result;
556}
557
558// CheckRefsMessage
559bool
560LaunchContext::CheckRefsMessage(LaunchCaller &caller, team_id team,
561								int32 &cookie)
562{
563	bool result = true;
564	if (caller.SupportsRefs())
565		result = CheckRefsMessage(caller, team, cookie, caller.Ref());
566	return result;
567}
568
569// CheckRefsMessage
570bool
571LaunchContext::CheckRefsMessage(LaunchCaller &caller, team_id team,
572								int32 &cookie, const entry_ref *refs,
573								int32 count)
574{
575	BMessage *message = NextMessageFrom(team, cookie);
576	bool result = (message && message->what == MSG_REFS_RECEIVED);
577	if (result) {
578		entry_ref ref;
579		for (int32 i = 0; result && i < count; i++) {
580			result = (message->FindRef("refs", i, &ref) == B_OK
581					  && ref == refs[i]);
582		}
583	}
584	return result;
585}
586
587// WaitForMessage
588bool
589LaunchContext::WaitForMessage(uint32 messageCode, bool fromNow,
590							  bigtime_t timeout)
591{
592	status_t error = B_ERROR;
593	fLock.Lock();
594	error = B_OK;
595	if (fromNow || !FindMessage(messageCode)) {
596		// add sleeper
597		Sleeper *sleeper = new Sleeper;
598		error = sleeper->Init(messageCode);
599		if (error == B_OK) {
600			AddSleeper(sleeper);
601			fLock.Unlock();
602			// sleep
603			error = sleeper->Sleep(timeout);
604			fLock.Lock();
605			// remove sleeper
606			RemoveSleeper(sleeper);
607			delete sleeper;
608		}
609	}
610	fLock.Unlock();
611	return (error == B_OK);
612}
613
614// WaitForMessage
615bool
616LaunchContext::WaitForMessage(team_id team, uint32 messageCode, bool fromNow,
617							  bigtime_t timeout, int32 startIndex)
618{
619	status_t error = B_ERROR;
620	fLock.Lock();
621	if (AppInfo *info = AppInfoFor(team)) {
622		error = B_OK;
623		if (fromNow || !info->FindMessage(messageCode, startIndex)) {
624			// add sleeper
625			Sleeper *sleeper = new Sleeper;
626			error = sleeper->Init(messageCode);
627			if (error == B_OK) {
628				info->AddSleeper(sleeper);
629				fLock.Unlock();
630				// sleep
631				error = sleeper->Sleep(timeout);
632				fLock.Lock();
633				// remove sleeper
634				info->RemoveSleeper(sleeper);
635				delete sleeper;
636			}
637		}
638	}
639	fLock.Unlock();
640	return (error == B_OK);
641}
642
643// StandardMessages
644BList*
645LaunchContext::StandardMessages()
646{
647	if (fStandardMessages.IsEmpty()) {
648		fStandardMessages.AddItem(new BMessage(MSG_1));
649		fStandardMessages.AddItem(new BMessage(MSG_2));
650		fStandardMessages.AddItem(new BMessage(MSG_3));
651	}
652	return &fStandardMessages;
653}
654
655// AppInfoAt
656LaunchContext::AppInfo*
657LaunchContext::AppInfoAt(int32 index) const
658{
659	return (AppInfo*)fAppInfos.ItemAt(index);
660}
661
662// AppInfoFor
663LaunchContext::AppInfo*
664LaunchContext::AppInfoFor(team_id team) const
665{
666	for (int32 i = 0; AppInfo *info = AppInfoAt(i); i++) {
667		if (info->Team() == team)
668			return info;
669	}
670	return NULL;
671}
672
673// CreateAppInfo
674LaunchContext::AppInfo*
675LaunchContext::CreateAppInfo(BMessenger messenger)
676{
677	return CreateAppInfo(messenger.Team(), &messenger);
678}
679
680// CreateAppInfo
681LaunchContext::AppInfo*
682LaunchContext::CreateAppInfo(team_id team, const BMessenger *messenger)
683{
684	AppInfo *info = AppInfoFor(team);
685	if (!info) {
686		info = new AppInfo(team);
687		fAppInfos.AddItem(info);
688	}
689	if (messenger && !info->Messenger().IsValid())
690		info->SetMessenger(*messenger);
691	return info;
692}
693
694// TerminateApp
695void
696LaunchContext::TerminateApp(AppInfo *info)
697{
698	if (info)
699		info->Terminate();
700}
701
702// Terminator
703int32
704LaunchContext::Terminator()
705{
706	bool allGone = false;
707	while (!allGone) {
708		allGone = true;
709		fLock.Lock();
710		for (int32 i = 0; AppInfo *info = AppInfoAt(i); i++) {
711			team_info teamInfo;
712			allGone &= (get_team_info(info->Team(), &teamInfo) != B_OK);
713		}
714		fLock.Unlock();
715		if (!allGone)
716			snooze(10000);
717	}
718	be_app->PostMessage(B_QUIT_REQUESTED, be_app);
719	return 0;
720}
721
722// AppThreadEntry
723int32
724LaunchContext::AppThreadEntry(void *)
725{
726	be_app->Lock();
727	be_app->Run();
728	return 0;
729}
730
731// TerminatorEntry
732int32
733LaunchContext::TerminatorEntry(void *data)
734{
735	return ((LaunchContext*)data)->Terminator();
736}
737
738// FindMessage
739LaunchContext::Message*
740LaunchContext::FindMessage(uint32 messageCode)
741{
742	BAutolock _lock(fLock);
743	Message *message = NULL;
744	AppInfo *info = NULL;
745	for (int32 i = 0; !message && (info = AppInfoAt(i)) != NULL; i++)
746		message = info->FindMessage(messageCode);
747	return message;
748}
749
750// AddSleeper
751void
752LaunchContext::AddSleeper(Sleeper *sleeper)
753{
754	fSleepers.AddItem(sleeper);
755}
756
757// RemoveSleeper
758void
759LaunchContext::RemoveSleeper(Sleeper *sleeper)
760{
761	fSleepers.RemoveItem(sleeper);
762}
763
764// NotifySleepers
765void
766LaunchContext::NotifySleepers(uint32 messageCode)
767{
768	for (int32 i = 0; Sleeper *sleeper = (Sleeper*)fSleepers.ItemAt(i); i++) {
769		if (sleeper->MessageCode() == messageCode)
770			sleeper->WakeUp();
771	}
772}
773
774
775///////////////////
776// RosterLaunchApp
777
778// constructor
779RosterLaunchApp::RosterLaunchApp(const char *signature)
780	: BApplication(signature),
781	  fLaunchContext(NULL),
782	  fHandler(NULL)
783{
784}
785
786// destructor
787RosterLaunchApp::~RosterLaunchApp()
788{
789	SetHandler(NULL);
790}
791
792// MessageReceived
793void
794RosterLaunchApp::MessageReceived(BMessage *message)
795{
796	if (fLaunchContext)
797		fLaunchContext->HandleMessage(message);
798}
799
800// SetLaunchContext
801void
802RosterLaunchApp::SetLaunchContext(LaunchContext *context)
803{
804	fLaunchContext = context;
805}
806
807// GetLaunchContext
808LaunchContext *
809RosterLaunchApp::GetLaunchContext() const
810{
811	return fLaunchContext;
812}
813
814// SetHandler
815void
816RosterLaunchApp::SetHandler(BHandler *handler)
817{
818	Lock();
819	if (fHandler) {
820		RemoveHandler(fHandler);
821		delete fHandler;
822		fHandler = NULL;
823	}
824	if (handler) {
825		fHandler = handler;
826		AddHandler(handler);
827	}
828	Unlock();
829}
830
831
832//////////////////////////
833// RosterBroadcastHandler
834
835// constructor
836RosterBroadcastHandler::RosterBroadcastHandler()
837{
838}
839
840// MessageReceived
841void
842RosterBroadcastHandler::MessageReceived(BMessage *message)
843{
844	RosterLaunchApp *app = dynamic_cast<RosterLaunchApp*>(be_app);
845	if (LaunchContext *launchContext = app->GetLaunchContext()) {
846		message->AddInt32("original what", (int32)message->what);
847		message->what = MSG_REPLY;
848		launchContext->HandleMessage(message);
849	}
850}
851
852