/*
 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files or portions
 * thereof (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice
 *    in the  binary, as well as this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided with
 *    the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */
 
 
/* to comply with the license above, do not remove the following line */
char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002, 2003 "
	"Marcus Overhagen <Marcus@Overhagen.de>";
 
 
#include <stdio.h>
#include <string.h>
 
#include <Alert.h>
#include <Autolock.h>
#include <Directory.h>
#include <Roster.h>
#include <MediaDefs.h>
#include <MediaFormats.h>
#include <Messenger.h>
#include <Server.h>
 
#include <syscalls.h>
 
#include "AppManager.h"
#include "BufferManager.h"
#include "DataExchange.h"
#include "MediaDebug.h"
#include "MediaFilesManager.h"
#include "MediaMisc.h"
#include "NodeManager.h"
#include "NotificationManager.h"
#include "ServerInterface.h"
#include "media_server.h"
 
 
AppManager* gAppManager;
BufferManager* gBufferManager;
MediaFilesManager* gMediaFilesManager;
NodeManager* gNodeManager;
NotificationManager* gNotificationManager;
 
 
#define REPLY_TIMEOUT ((bigtime_t)500000)
 
 
class ServerApp : public BServer {
public:
								ServerApp(status_t& error);
	virtual						~ServerApp();
 
protected:
	virtual void				ArgvReceived(int32 argc, char** argv);
	virtual void				ReadyToRun();
	virtual bool				QuitRequested();
	virtual void				MessageReceived(BMessage* message);
 
private:
			void				_HandleMessage(int32 code, const void* data,
									size_t size);
			void				_LaunchAddOnServer();
			void				_QuitAddOnServer();
 
private:
			port_id				_ControlPort() const { return fControlPort; }
 
			static	int32		_ControlThread(void* arg);
 
			BLocker				fLocker;
			port_id				fControlPort;
			thread_id			fControlThread;
};
 
 
ServerApp::ServerApp(status_t& error)
 	:
	BServer(B_MEDIA_SERVER_SIGNATURE, true, &error),
	fLocker("media server locker")
{
 	gNotificationManager = new NotificationManager;
 	gBufferManager = new BufferManager;
	gAppManager = new AppManager;
	gNodeManager = new NodeManager;
	gMediaFilesManager = new MediaFilesManager;
 
	fControlPort = create_port(64, MEDIA_SERVER_PORT_NAME);
	fControlThread = spawn_thread(_ControlThread, "media_server control", 105,
		this);
	resume_thread(fControlThread);
 
	if (be_roster->StartWatching(BMessenger(this, this),
			B_REQUEST_QUIT) != B_OK) {
		TRACE("ServerApp: Can't find the registrar.");
	}
}
 
 
ServerApp::~ServerApp()
{
	TRACE("ServerApp::~ServerApp()\n");
 
	delete_port(fControlPort);
	wait_for_thread(fControlThread, NULL);
 
	if (be_roster->StopWatching(BMessenger(this, this)) != B_OK)
		TRACE("ServerApp: Can't unregister roster notifications.");
 
	delete gNotificationManager;
	delete gBufferManager;
	delete gAppManager;
	delete gNodeManager;
	delete gMediaFilesManager;
}
 
 
void
ServerApp::ReadyToRun()
{
	gNodeManager->LoadState();
 
	// make sure any previous media_addon_server is gone
	_QuitAddOnServer();
	// and start a new one
	_LaunchAddOnServer();
 
}
 
 
bool
ServerApp::QuitRequested()
{
	TRACE("ServerApp::QuitRequested()\n");
	gMediaFilesManager->SaveState();
	gNodeManager->SaveState();
 
	_QuitAddOnServer();
 
	return true;
}
 
 
void
ServerApp::ArgvReceived(int32 argc, char **argv)
{
	for (int arg = 1; arg < argc; arg++) {
		if (strstr(argv[arg], "dump") != NULL) {
			gAppManager->Dump();
			gNodeManager->Dump();
			gBufferManager->Dump();
			gNotificationManager->Dump();
			gMediaFilesManager->Dump();
		}
		if (strstr(argv[arg], "buffer") != NULL)
			gBufferManager->Dump();
		if (strstr(argv[arg], "node") != NULL)
			gNodeManager->Dump();
		if (strstr(argv[arg], "files") != NULL)
			gMediaFilesManager->Dump();
		if (strstr(argv[arg], "quit") != NULL)
			PostMessage(B_QUIT_REQUESTED);
	}
}
 
 
void
ServerApp::_LaunchAddOnServer()
{
	// Try to launch media_addon_server by mime signature.
	// If it fails (for example on the Live CD, where the executable
	// hasn't yet been mimesetted), try from this application's
	// directory
	status_t err = be_roster->Launch(B_MEDIA_ADDON_SERVER_SIGNATURE);
	if (err == B_OK)
		return;
 
	app_info info;
	BEntry entry;
	BDirectory dir;
	entry_ref ref;
 
	err = GetAppInfo(&info);
	err |= entry.SetTo(&info.ref);
	err |= entry.GetParent(&entry);
	err |= dir.SetTo(&entry);
	err |= entry.SetTo(&dir, "media_addon_server");
	err |= entry.GetRef(&ref);
 
	if (err == B_OK)
		be_roster->Launch(&ref);
	if (err == B_OK)
		return;
 
	BAlert* alert = new BAlert("media_server", "Launching media_addon_server "
		"failed.\n\nmedia_server will terminate", "OK");
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go();
	fprintf(stderr, "Launching media_addon_server (%s) failed: %s\n",
		B_MEDIA_ADDON_SERVER_SIGNATURE, strerror(err));
	exit(1);
}
 
 
void
ServerApp::_QuitAddOnServer()
{
	// nothing to do if it's already terminated
	if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
		return;
 
	// send a quit request to the media_addon_server
	BMessenger msger(B_MEDIA_ADDON_SERVER_SIGNATURE);
	if (!msger.IsValid()) {
		ERROR("Trouble terminating media_addon_server. Messenger invalid\n");
	} else {
		BMessage msg(B_QUIT_REQUESTED);
		status_t err = msger.SendMessage(&msg, (BHandler *)NULL, 2000000);
			// 2 sec timeout
		if (err != B_OK) {
			ERROR("Trouble terminating media_addon_server (2): %s\n",
				strerror(err));
		}
	}
 
	// wait 5 seconds for it to terminate
	for (int i = 0; i < 50; i++) {
		if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
			return;
		snooze(100000); // 100 ms
	}
 
	// try to kill it (or many of them), up to 10 seconds
	for (int i = 0; i < 50; i++) {
		team_id id = be_roster->TeamFor(B_MEDIA_ADDON_SERVER_SIGNATURE);
		if (id < 0)
			break;
		kill_team(id);
		snooze(200000); // 200 ms
	}
 
	if (be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE)) {
		ERROR("Trouble terminating media_addon_server, it's still running\n");
	}
}
 
 
void
ServerApp::_HandleMessage(int32 code, const void* data, size_t size)
{
	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " enter\n", code);
	switch (code) {
		case SERVER_CHANGE_FLAVOR_INSTANCES_COUNT:
		{
			const server_change_flavor_instances_count_request& request
				= *static_cast<
					const server_change_flavor_instances_count_request*>(data);
			server_change_flavor_instances_count_reply reply;
			status_t status = B_BAD_VALUE;
 
			if (request.delta == 1) {
				status = gNodeManager->IncrementFlavorInstancesCount(
					request.add_on_id, request.flavor_id, request.team);
			} else if (request.delta == -1) {
				status = gNodeManager->DecrementFlavorInstancesCount(
					request.add_on_id, request.flavor_id, request.team);
			}
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_RESCAN_DEFAULTS:
		{
			gNodeManager->RescanDefaultNodes();
			break;
		}
 
		case SERVER_REGISTER_APP:
		{
			const server_register_app_request& request = *static_cast<
				const server_register_app_request*>(data);
			server_register_app_reply reply;
 
			status_t status = gAppManager->RegisterTeam(request.team,
				request.messenger);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_UNREGISTER_APP:
		{
			const server_unregister_app_request& request = *static_cast<
				const server_unregister_app_request*>(data);
			server_unregister_app_reply reply;
 
			status_t status = gAppManager->UnregisterTeam(request.team);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_ADD_ON_REF:
		{
			const server_get_add_on_ref_request& request = *static_cast<
				const server_get_add_on_ref_request*>(data);
			server_get_add_on_ref_reply reply;
 
			entry_ref ref;
			reply.result = gNodeManager->GetAddOnRef(request.add_on_id, &ref);
			reply.ref = ref;
 
			request.SendReply(reply.result, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_NODE_ID_FOR:
		{
			const server_node_id_for_request& request
				= *static_cast<const server_node_id_for_request*>(data);
			server_node_id_for_reply reply;
 
			status_t status = gNodeManager->FindNodeID(request.port,
				&reply.node_id);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_LIVE_NODE_INFO:
		{
			const server_get_live_node_info_request& request = *static_cast<
				const server_get_live_node_info_request*>(data);
			server_get_live_node_info_reply reply;
 
			status_t status = gNodeManager->GetLiveNodeInfo(request.node,
				&reply.live_info);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_LIVE_NODES:
		{
			const server_get_live_nodes_request& request
				= *static_cast<const server_get_live_nodes_request*>(data);
			server_get_live_nodes_reply reply;
			LiveNodeList nodes;
 
			status_t status = gNodeManager->GetLiveNodes(nodes,
				request.max_count,
				request.has_input ? &request.input_format : NULL,
				request.has_output ? &request.output_format : NULL,
				request.has_name ? request.name : NULL, request.require_kinds);
 
			reply.count = nodes.size();
			reply.area = -1;
 
			live_node_info* infos = reply.live_info;
			area_id area = -1;
 
			if (reply.count > MAX_LIVE_INFO) {
				// We create an area here, and transfer it to the client
				size_t size = (reply.count * sizeof(live_node_info)
					+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
 
				area = create_area("get live nodes", (void**)&infos,
					B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
				if (area < 0) {
					reply.area = area;
					reply.count = 0;
				}
			}
 
			for (int32 index = 0; index < reply.count; index++)
				infos[index] = nodes[index];
 
			if (area >= 0) {
				// transfer the area to the target team
				reply.area = _kern_transfer_area(area, &reply.address,
					B_ANY_ADDRESS, request.team);
				if (reply.area < 0) {
					delete_area(area);
					reply.count = 0;
				}
			}
 
			status = request.SendReply(status, &reply, sizeof(reply));
			if (status != B_OK && reply.area >= 0) {
				// if we couldn't send the message, delete the area
				delete_area(reply.area);
			}
			break;
		}
 
		case SERVER_GET_NODE_FOR:
		{
			const server_get_node_for_request& request
				= *static_cast<const server_get_node_for_request*>(data);
			server_get_node_for_reply reply;
 
			status_t status = gNodeManager->GetCloneForID(request.node_id,
				request.team, &reply.clone);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_RELEASE_NODE:
		{
			const server_release_node_request& request
				= *static_cast<const server_release_node_request*>(data);
			server_release_node_reply reply;
 
			status_t status = gNodeManager->ReleaseNode(request.node,
				request.team);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
		
		case SERVER_RELEASE_NODE_ALL:
		{
			const server_release_node_request& request
				= *static_cast<const server_release_node_request*>(data);
			server_release_node_reply reply;
 
			status_t status = gNodeManager->ReleaseNodeAll(request.node.node);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_REGISTER_NODE:
		{
			const server_register_node_request& request
				= *static_cast<const server_register_node_request*>(data);
			server_register_node_reply reply;
 
			status_t status = gNodeManager->RegisterNode(request.add_on_id,
				request.flavor_id, request.name, request.kinds, request.port,
				request.team, request.timesource_id, &reply.node_id);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_UNREGISTER_NODE:
		{
			const server_unregister_node_request& request
				= *static_cast<const server_unregister_node_request*>(data);
			server_unregister_node_reply reply;
 
			status_t status = gNodeManager->UnregisterNode(request.node_id,
				request.team, &reply.add_on_id, &reply.flavor_id);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_PUBLISH_INPUTS:
		{
			const server_publish_inputs_request& request
				= *static_cast<const server_publish_inputs_request*>(data);
			server_publish_inputs_reply reply;
			status_t status;
 
			if (request.count <= MAX_INPUTS) {
				status = gNodeManager->PublishInputs(request.node,
					request.inputs, request.count);
			} else {
				media_input* inputs;
				area_id clone;
				clone = clone_area("media_inputs clone", (void**)&inputs,
					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
				if (clone < B_OK) {
					ERROR("SERVER_PUBLISH_INPUTS: failed to clone area, "
						"error %#" B_PRIx32 "\n", clone);
					status = clone;
				} else {
					status = gNodeManager->PublishInputs(request.node, inputs,
						request.count);
					delete_area(clone);
				}
			}
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_PUBLISH_OUTPUTS:
		{
			const server_publish_outputs_request& request
				= *static_cast<const server_publish_outputs_request*>(data);
			server_publish_outputs_reply reply;
			status_t status;
 
			if (request.count <= MAX_OUTPUTS) {
				status = gNodeManager->PublishOutputs(request.node,
					request.outputs, request.count);
			} else {
				media_output* outputs;
				area_id clone;
				clone = clone_area("media_outputs clone", (void**)&outputs,
					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
				if (clone < B_OK) {
					ERROR("SERVER_PUBLISH_OUTPUTS: failed to clone area, "
						"error %#" B_PRIx32 "\n", clone);
					status = clone;
				} else {
					status = gNodeManager->PublishOutputs(request.node, outputs,
						request.count);
					delete_area(clone);
				}
			}
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_NODE:
		{
			const server_get_node_request& request
				= *static_cast<const server_get_node_request*>(data);
			server_get_node_reply reply;
 
			status_t status = gNodeManager->GetClone(request.type, request.team,
				&reply.node, reply.input_name, &reply.input_id);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_SET_NODE:
		{
			const server_set_node_request& request
				= *static_cast<const server_set_node_request*>(data);
			server_set_node_reply reply;
 
			status_t status = gNodeManager->SetDefaultNode(request.type,
				request.use_node ? &request.node : NULL,
				request.use_dni ? &request.dni : NULL,
				request.use_input ?  &request.input : NULL);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_DORMANT_NODE_FOR:
		{
			const server_get_dormant_node_for_request& request
				= *static_cast<const server_get_dormant_node_for_request*>(
					data);
			server_get_dormant_node_for_reply reply;
 
			status_t status = gNodeManager->GetDormantNodeInfo(request.node,
				&reply.node_info);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_INSTANCES_FOR:
		{
			const server_get_instances_for_request& request
				= *static_cast<const server_get_instances_for_request*>(data);
			server_get_instances_for_reply reply;
 
			status_t status = gNodeManager->GetInstances(request.add_on_id,
				request.flavor_id, reply.node_id, &reply.count,
				min_c(request.max_count, MAX_NODE_ID));
			if (reply.count == MAX_NODE_ID
				&& request.max_count > MAX_NODE_ID) {
				// TODO: might be fixed by using an area
				PRINT(1, "Warning: SERVER_GET_INSTANCES_FOR: returning "
					"possibly truncated list of node id's\n");
			}
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_SET_NODE_TIMESOURCE:
		{
			const server_set_node_timesource_request& request
				= *static_cast<const server_set_node_timesource_request*>(data);
			server_set_node_timesource_reply reply;
			status_t result = gNodeManager->SetNodeTimeSource(request.node_id,
				request.timesource_id);
			request.SendReply(result, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_REGISTER_ADD_ON:
		{
			const server_register_add_on_request& request = *static_cast<
				const server_register_add_on_request*>(data);
			server_register_add_on_reply reply;
 
			gNodeManager->RegisterAddOn(request.ref, &reply.add_on_id);
			request.SendReply(B_OK, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_UNREGISTER_ADD_ON:
		{
			const server_unregister_add_on_command& request = *static_cast<
				const server_unregister_add_on_command*>(data);
			gNodeManager->UnregisterAddOn(request.add_on_id);
			break;
		}
 
		case SERVER_REGISTER_DORMANT_NODE:
		{
			const server_register_dormant_node_command& command
				= *static_cast<const server_register_dormant_node_command*>(
					data);
			if (command.purge_id > 0)
				gNodeManager->InvalidateDormantFlavorInfo(command.purge_id);
 
			dormant_flavor_info dormantFlavorInfo;
			status_t status = dormantFlavorInfo.Unflatten(command.type,
				command.flattened_data, command.flattened_size);
			if (status == B_OK)
				gNodeManager->AddDormantFlavorInfo(dormantFlavorInfo);
			break;
		}
 
		case SERVER_GET_DORMANT_NODES:
		{
			const server_get_dormant_nodes_request& request
				= *static_cast<const server_get_dormant_nodes_request*>(data);
 
			server_get_dormant_nodes_reply reply;
			reply.count = request.max_count;
 
			dormant_node_info* infos
				= new(std::nothrow) dormant_node_info[reply.count];
			if (infos != NULL) {
				reply.result = gNodeManager->GetDormantNodes(infos,
					&reply.count,
					request.has_input ? &request.input_format : NULL,
					request.has_output ? &request.output_format : NULL,
					request.has_name ? request.name : NULL,
					request.require_kinds, request.deny_kinds);
			} else
				reply.result = B_NO_MEMORY;
 
			if (reply.result != B_OK)
				reply.count = 0;
 
			request.SendReply(reply.result, &reply, sizeof(reply));
			if (reply.count > 0) {
				write_port(request.reply_port, 0, infos,
					reply.count * sizeof(dormant_node_info));
			}
			delete[] infos;
			break;
		}
 
		case SERVER_GET_DORMANT_FLAVOR_INFO:
		{
			const server_get_dormant_flavor_info_request& request
				= *static_cast<const server_get_dormant_flavor_info_request*>(
					data);
			dormant_flavor_info dormantFlavorInfo;
 
			status_t status = gNodeManager->GetDormantFlavorInfoFor(
				request.add_on_id, request.flavor_id, &dormantFlavorInfo);
			if (status != B_OK) {
				server_get_dormant_flavor_info_reply reply;
				reply.result = status;
				request.SendReply(reply.result, &reply, sizeof(reply));
			} else {
				size_t replySize
					= sizeof(server_get_dormant_flavor_info_reply)
						+ dormantFlavorInfo.FlattenedSize();
				server_get_dormant_flavor_info_reply* reply
					= (server_get_dormant_flavor_info_reply*)malloc(
						replySize);
				if (reply != NULL) {
					reply->type = dormantFlavorInfo.TypeCode();
					reply->flattened_size = dormantFlavorInfo.FlattenedSize();
					reply->result = dormantFlavorInfo.Flatten(
						reply->flattened_data, reply->flattened_size);
 
					request.SendReply(reply->result, reply, replySize);
					free(reply);
				} else {
					server_get_dormant_flavor_info_reply reply;
					reply.result = B_NO_MEMORY;
					request.SendReply(reply.result, &reply, sizeof(reply));
				}
			}
			break;
		}
 
		case SERVER_SET_NODE_CREATOR:
		{
			const server_set_node_creator_request& request
				= *static_cast<const server_set_node_creator_request*>(data);
			server_set_node_creator_reply reply;
			status_t status = gNodeManager->SetNodeCreator(request.node,
				request.creator);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_SHARED_BUFFER_AREA:
		{
			const server_get_shared_buffer_area_request& request
				= *static_cast<const server_get_shared_buffer_area_request*>(
					data);
			server_get_shared_buffer_area_reply reply;
 
			reply.area = gBufferManager->SharedBufferListArea();
			request.SendReply(reply.area >= 0 ? B_OK : reply.area, &reply,
				sizeof(reply));
			break;
		}
 
		case SERVER_REGISTER_BUFFER:
		{
			const server_register_buffer_request& request
				= *static_cast<const server_register_buffer_request*>(data);
			server_register_buffer_reply reply;
			status_t status;
 
			if (request.info.buffer == 0) {
				reply.info = request.info;
				// size, offset, flags, area is kept
				// get a new beuffer id into reply.info.buffer
				status = gBufferManager->RegisterBuffer(request.team,
					request.info.size, request.info.flags,
					request.info.offset, request.info.area,
					&reply.info.buffer);
			} else {
				reply.info = request.info; // buffer id is kept
				status = gBufferManager->RegisterBuffer(request.team,
					request.info.buffer, &reply.info.size, &reply.info.flags,
					&reply.info.offset, &reply.info.area);
			}
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_UNREGISTER_BUFFER:
		{
			const server_unregister_buffer_command& request = *static_cast<
				const server_unregister_buffer_command*>(data);
 
			gBufferManager->UnregisterBuffer(request.team, request.buffer_id);
			break;
		}
 
		case SERVER_GET_MEDIA_FILE_TYPES:
		{
			const server_get_media_types_request& request
				= *static_cast<const server_get_media_types_request*>(data);
 
			server_get_media_types_reply reply;
			area_id area = gMediaFilesManager->GetTypesArea(reply.count);
			if (area >= 0) {
				// transfer the area to the target team
				reply.area = _kern_transfer_area(area, &reply.address,
					B_ANY_ADDRESS, request.team);
				if (reply.area < 0) {
					delete_area(area);
					reply.area = B_ERROR;
					reply.count = 0;
				}
			}
 
			status_t status = request.SendReply(
				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
			if (status != B_OK) {
				// if we couldn't send the message, delete the area
				delete_area(reply.area);
			}
			break;
		}
 
		case SERVER_GET_MEDIA_FILE_ITEMS:
		{
			const server_get_media_items_request& request
				= *static_cast<const server_get_media_items_request*>(data);
 
			server_get_media_items_reply reply;
			area_id area = gMediaFilesManager->GetItemsArea(request.type,
				reply.count);
			if (area >= 0) {
				// transfer the area to the target team
				reply.area = _kern_transfer_area(area, &reply.address,
					B_ANY_ADDRESS, request.team);
				if (reply.area < 0) {
					delete_area(area);
					reply.area = B_ERROR;
					reply.count = 0;
				}
			} else
				reply.area = area;
 
			status_t status = request.SendReply(
				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
			if (status != B_OK) {
				// if we couldn't send the message, delete the area
				delete_area(reply.area);
			}
			break;
		}
 
		case SERVER_GET_REF_FOR:
		{
			const server_get_ref_for_request& request
				= *static_cast<const server_get_ref_for_request*>(data);
			server_get_ref_for_reply reply;
			entry_ref* ref;
 
			status_t status = gMediaFilesManager->GetRefFor(request.type,
				request.item, &ref);
			if (status == B_OK)
				reply.ref = *ref;
 
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_SET_REF_FOR:
		{
			const server_set_ref_for_request& request
				= *static_cast<const server_set_ref_for_request*>(data);
			server_set_ref_for_reply reply;
			entry_ref ref = request.ref;
 
			status_t status = gMediaFilesManager->SetRefFor(request.type,
				request.item, ref);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_INVALIDATE_MEDIA_ITEM:
		{
			const server_invalidate_item_request& request
				= *static_cast<const server_invalidate_item_request*>(data);
			server_invalidate_item_reply reply;
 
			status_t status = gMediaFilesManager->InvalidateItem(request.type,
				request.item);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_REMOVE_MEDIA_ITEM:
		{
			const server_remove_media_item_request& request
				= *static_cast<const server_remove_media_item_request*>(data);
			server_remove_media_item_reply reply;
 
			status_t status = gMediaFilesManager->RemoveItem(request.type,
				request.item);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_GET_ITEM_AUDIO_GAIN:
		{
			const server_get_item_audio_gain_request& request
				= *static_cast<const server_get_item_audio_gain_request*>(data);
			server_get_item_audio_gain_reply reply;
 
			status_t status = gMediaFilesManager->GetAudioGainFor(request.type,
				request.item, &reply.gain);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		case SERVER_SET_ITEM_AUDIO_GAIN:
		{
			const server_set_item_audio_gain_request& request
				= *static_cast<const server_set_item_audio_gain_request*>(data);
			server_set_ref_for_reply reply;
 
			status_t status = gMediaFilesManager->SetAudioGainFor(request.type,
				request.item, request.gain);
			request.SendReply(status, &reply, sizeof(reply));
			break;
		}
 
		default:
			printf("media_server: received unknown message code %#08" B_PRIx32
				"\n", code);
	}
	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " leave\n", code);
}
 
 
status_t
ServerApp::_ControlThread(void* _server)
{
	ServerApp* server = (ServerApp*)_server;
 
	char data[B_MEDIA_MESSAGE_SIZE];
	ssize_t size;
	int32 code;
	while ((size = read_port_etc(server->_ControlPort(), &code, data,
			sizeof(data), 0, 0)) > 0) {
		server->_HandleMessage(code, data, size);
	}
 
	return B_OK;
}
 
 
void
ServerApp::MessageReceived(BMessage* msg)
{
	TRACE("ServerApp::MessageReceived %" B_PRIu32 " enter\n", msg->what);
	switch (msg->what) {
		case MEDIA_SERVER_REQUEST_NOTIFICATIONS:
		case MEDIA_SERVER_CANCEL_NOTIFICATIONS:
		case MEDIA_SERVER_SEND_NOTIFICATIONS:
			gNotificationManager->EnqueueMessage(msg);
			break;
 
		case MEDIA_FILES_MANAGER_SAVE_TIMER:
			gMediaFilesManager->TimerMessage();
			break;
 
		case MEDIA_SERVER_ADD_SYSTEM_BEEP_EVENT:
			gMediaFilesManager->HandleAddSystemBeepEvent(msg);
			break;
 
		case MEDIA_SERVER_RESCAN_COMPLETED:
			gAppManager->NotifyRosters();
			break;
 
		case B_SOME_APP_QUIT:
		{
			BString mimeSig;
			if (msg->FindString("be:signature", &mimeSig) != B_OK)
				return;
 
			if (mimeSig == B_MEDIA_ADDON_SERVER_SIGNATURE)
				gNodeManager->CleanupDormantFlavorInfos();
 
			team_id id;
			if (msg->FindInt32("team", &id) == B_OK
					&& gAppManager->HasTeam(id)) {
				gAppManager->UnregisterTeam(id);
			}
			break;
		}
 
		default:
			BApplication::MessageReceived(msg);
			TRACE("\nmedia_server: unknown message received!\n");
			break;
	}
	TRACE("ServerApp::MessageReceived %" B_PRIu32 " leave\n", msg->what);
}
 
 
//	#pragma mark -
 
 
int
main()
{
	status_t status;
	ServerApp app(status);
 
	if (status == B_OK)
		app.Run();
 
	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
}

V614 Uninitialized variable 'status' used.