root/src/add-ons/kernel/file_systems/netfs/server/NetFSServer.cpp
// NetFSServer.cpp

#include <errno.h>
#include <netdb.h>
#include <new>
#include <stdio.h>
#include <string.h>

#ifdef HAIKU_TARGET_PLATFORM_BEOS
#       include <socket.h>
#else
#       include <unistd.h>
#       include <netinet/in.h>
#       include <sys/socket.h>
#endif

#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Node.h>
#include <Path.h>
#include <util/DoublyLinkedList.h>

#include "Connection.h"
#include "ConnectionListener.h"
#include "DebugSupport.h"
#include "DriverSettings.h"
#include "FDManager.h"
#include "InsecureChannel.h"
#include "NetFSDefs.h"
#include "NetFSServer.h"
#include "NetFSServerRosterDefs.h"
#include "RequestChannel.h"
#include "Requests.h"
#include "SecurityContext.h"
#include "StatisticsManager.h"
#include "TaskManager.h"
#include "Utils.h"
#include "VolumeManager.h"

static const char* kSettingsDirName                             = "netfs";
static const char* kSettingsFileName                    = "netfs_server";
static const char* kFallbackSettingsFileName    = "netfs_server_fallback";

// usage
static const char* kUsage =
"Usage: netfs_server <options>\n"
"options:\n"
"  --dont-broadcast  - don't use broadcasting to announce the server's\n"
"                      availability to clients\n"
"  -h, --help        - print this text\n"
;

// ConnectionInitializer
class NetFSServer::ConnectionInitializer {
public:
        ConnectionInitializer(NetFSServer* server,
                ConnectionListener* connectionListener, Connection* connection)
                : fServer(server),
                  fConnectionListener(connectionListener),
                  fConnection(connection),
                  fThread(-1)
        {
        }

        ~ConnectionInitializer()
        {
                delete fConnection;
        }

        status_t Run()
        {
                fThread = spawn_thread(&_ThreadEntry, "connection initializer",
                        B_NORMAL_PRIORITY, this);
                if (fThread < 0)
                        return fThread;
                resume_thread(fThread);
                return B_OK;
        }

private:
        static int32 _ThreadEntry(void* data)
        {
                return ((ConnectionInitializer*)data)->_Thread();
        }

        int32 _Thread()
        {
                // finish connection initialization
                User* user = NULL;
                status_t error = fConnectionListener->FinishInitialization(
                        fConnection, fServer->GetSecurityContext(), &user);
                // create a client connection
                ClientConnection* clientConnection = NULL;
                if (error == B_OK) {
                        clientConnection = new(std::nothrow) ClientConnection(fConnection,
                                fServer->GetSecurityContext(), user, fServer);
                        if (!clientConnection)
                                error = B_NO_MEMORY;
                }
                if (error == B_OK) {
                        fConnection = NULL;     // connection belongs to client connection now
                        error = clientConnection->Init();
                }
                // add the client connection to the server
                if (error == B_OK)
                        error = fServer->_AddClientConnection(clientConnection);
                // cleanup on error
                if (error != B_OK)
                        delete clientConnection;
                delete this;
                return 0;
        }

private:
        NetFSServer*            fServer;
        ConnectionListener*     fConnectionListener;
        Connection*                     fConnection;
        thread_id                       fThread;
};


// ServerInfoSender
class NetFSServer::ServerInfoSender : public Task {
public:
        ServerInfoSender(int socket, const ServerInfo& serverInfo)
                : Task("server info sender"),
                  fChannel(new(std::nothrow) InsecureChannel(socket)),
                  fServerInfo(serverInfo)
        {
                if (!fChannel)
                        closesocket(socket);
        }

        ~ServerInfoSender()
        {
                delete fChannel;
        }

        status_t Init()
        {
                if (!fChannel)
                        return B_NO_MEMORY;

                return B_OK;
        }

        virtual void Stop()
        {
                if (fChannel)
                        fChannel->Close();
        }

        virtual status_t Execute()
        {
                if (!fChannel) {
                        SetDone(true);
                        return B_NO_INIT;
                }

                RequestChannel requestChannel(fChannel);

                // create the server info request
                ServerInfoRequest request;
                request.serverInfo = fServerInfo;

                // send the request
                status_t error = requestChannel.SendRequest(&request);
                if (error != B_OK) {
                        ERROR("ServerInfoSender: ERROR: Failed to send request: %s\n",
                                strerror(error));
                }

                SetDone(true);
                return B_OK;
        }

private:
        Channel*                fChannel;
        ServerInfo              fServerInfo;
};


// NetFSServer

// constructor
NetFSServer::NetFSServer(bool useBroadcasting)
        :
        BApplication(kNetFSServerSignature),
        fSecurityContext(NULL),
        fConnectionListenerFactory(),
        fConnectionListener(NULL),
        fLock("netfs server"),
        fClientConnections(),
        fVolumeManager(NULL),
        fClosedConnections(),
        fClosedConnectionsSemaphore(-1),
        fConnectionListenerThread(-1),
        fConnectionDeleter(-1),
        fBroadcaster(-1),
        fBroadcastingSocket(-1),
        fBroadcasterSemaphore(-1),
        fServerInfoConnectionListener(-1),
        fServerInfoConnectionListenerSocket(-1),
        fServerInfoUpdated(0),
        fUseBroadcasting(useBroadcasting),
        fTerminating(false)
{
}

// destructor
NetFSServer::~NetFSServer()
{
        fTerminating = true;
        // stop the connection listener
        if (fConnectionListener)
                fConnectionListener->StopListening();
        if (fConnectionListenerThread >= 0) {
                int32 result;
                wait_for_thread(fConnectionListenerThread, &result);
        }
        delete fConnectionListener;

        // delete the broadcaster semaphore
        if (fBroadcasterSemaphore >= 0)
                delete_sem(fBroadcasterSemaphore);

        // terminate the broadcaster
        if (fBroadcaster >= 0) {
                safe_closesocket(fBroadcastingSocket);

                // interrupt the thread in case it is currently snoozing
                suspend_thread(fBroadcaster);
                int32 result;
                wait_for_thread(fBroadcaster, &result);
        }

        // terminate the server info connection listener
        _ExitServerInfoConnectionListener();

        // terminate the connection deleter
        if (fClosedConnectionsSemaphore >= 0)
                delete_sem(fClosedConnectionsSemaphore);
        if (fConnectionDeleter >= 0) {
                int32 result;
                wait_for_thread(fConnectionDeleter, &result);
        }

        // blow away all remaining connections
        AutoLocker<Locker> _(fLock);
        // open connections
        for (int32 i = 0;
                 ClientConnection* connection
                        = (ClientConnection*)fClientConnections.ItemAt(i);
                 i++) {
                connection->Close();
                delete connection;
        }

        // closed connections
        for (int32 i = 0;
                 ClientConnection* connection
                        = (ClientConnection*)fClosedConnections.ItemAt(i);
                 i++) {
                delete connection;
        }
        VolumeManager::DeleteDefault();
        FDManager::DeleteDefault();
        delete fSecurityContext;
}

// Init
status_t
NetFSServer::Init()
{
        // init the settings
        status_t error = _InitSettings();
        if (error != B_OK)
                return error;

        // create the FD manager
        error = FDManager::CreateDefault();
        if (error != B_OK)
                return error;

        // create the volume manager
        error = VolumeManager::CreateDefault();
        if (error != B_OK)
                return error;
        fVolumeManager = VolumeManager::GetDefault();

        // create a connection listener
//      error = fConnectionListenerFactory.CreateConnectionListener(
//              "port", NULL, &fConnectionListener);
        error = fConnectionListenerFactory.CreateConnectionListener(
                "insecure", NULL, &fConnectionListener);
        if (error != B_OK)
                return error;

        // spawn the connection listener thread
        fConnectionListenerThread = spawn_thread(&_ConnectionListenerEntry,
                "connection listener", B_NORMAL_PRIORITY, this);
        if (fConnectionListenerThread < 0)
                return fConnectionListenerThread;

        // create the closed connections semaphore
        fClosedConnectionsSemaphore = create_sem(0, "closed connections");
        if (fClosedConnectionsSemaphore < 0)
                return fClosedConnectionsSemaphore;

        // spawn the connection deleter
        fConnectionDeleter = spawn_thread(&_ConnectionDeleterEntry,
                "connection deleter", B_NORMAL_PRIORITY, this);
        if (fConnectionDeleter < 0)
                return fConnectionDeleter;

        // init the server info connection listener
        error = _InitServerInfoConnectionListener();
        if (error != B_OK)
                return error;

        // create the broadcaster semaphore
        fBroadcasterSemaphore = create_sem(0, "broadcaster snooze");

        // spawn the broadcaster
        if (fUseBroadcasting) {
                fBroadcaster = spawn_thread(&_BroadcasterEntry, "broadcaster",
                        B_NORMAL_PRIORITY, this);
                if (fBroadcaster < 0) {
                        WARN("NetFSServer::Init(): Failed to spawn broadcaster thread "
                                "(%s). Continuing anyway.\n", strerror(fBroadcaster));
                }
        }
        return B_OK;
}

// Run
thread_id
NetFSServer::Run()
{
        // start the connection listener
        resume_thread(fConnectionListenerThread);

        // start the connection deleter
        resume_thread(fConnectionDeleter);

        // start the server info connection listener
        resume_thread(fServerInfoConnectionListener);

        // start the broadcaster
        resume_thread(fBroadcaster);

        return BApplication::Run();
}

// MessageReceived
void
NetFSServer::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case NETFS_REQUEST_GET_MESSENGER:
                {
                        // for the time being we process all requests here
                        BMessage reply;
                        reply.AddMessenger("messenger", be_app_messenger);
                        _SendReply(message, &reply);
                        break;
                }

                case NETFS_REQUEST_ADD_USER:
                {
                        // get user name and password
                        const char* user;
                        const char* password;
                        if (message->FindString("user", &user) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }
                        if (message->FindString("password", &password) != B_OK)
                                password = NULL;

                        // add the user
                        status_t error = fSecurityContext->AddUser(user, password);
                        _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_REMOVE_USER:
                {
                        // get user name
                        const char* userName;
                        if (message->FindString("user", &userName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // remove the user
                        User* user;
                        status_t error = fSecurityContext->RemoveUser(userName, &user);
                        if (error == B_OK) {
                                // propagate the information to the client connections
                                AutoLocker<Locker> _(fLock);
                                for (int32 i = 0;
                                         ClientConnection* connection
                                                = (ClientConnection*)fClientConnections.ItemAt(i);
                                         i++) {
                                        connection->UserRemoved(user);
                                }

                                user->ReleaseReference();
                        }

                        _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_USERS:
                {
                        // get the users
                        BMessage reply;
                        BMessage users;
                        status_t error = fSecurityContext->GetUsers(&users);
                        if (error == B_OK)
                                error = reply.AddMessage("users", &users);

                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_USER_STATISTICS:
                {
                        // get user name
                        const char* userName;
                        if (message->FindString("user", &userName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // get the user
                        User* user = fSecurityContext->FindUser(userName);
                        if (!user) {
                                _SendReply(message, B_ENTRY_NOT_FOUND);
                                break;
                        }
                        BReference<User> userReference(user, true);

                        // get the statistics
                        BMessage statistics;
                        status_t error = StatisticsManager::GetDefault()
                                ->GetUserStatistics(user, &statistics);

                        // prepare the reply
                        BMessage reply;
                        if (error == B_OK)
                                error = reply.AddMessage("statistics", &statistics);

                        // send the reply
                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_ADD_SHARE:
                {
                        // get share name and path
                        const char* share;
                        const char* path;
                        if (message->FindString("share", &share) != B_OK
                                || message->FindString("path", &path) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // add the share
                        status_t error = fSecurityContext->AddShare(share, path);

                        if (error == B_OK)
                                _ServerInfoUpdated();

                        _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_REMOVE_SHARE:
                {
                        // get share name
                        const char* shareName;
                        if (message->FindString("share", &shareName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // remove the share
                        Share* share;
                        status_t error = fSecurityContext->RemoveShare(shareName, &share);
                        if (error == B_OK) {
                                // propagate the information to the client connections
                                AutoLocker<Locker> _(fLock);
                                for (int32 i = 0;
                                         ClientConnection* connection
                                                = (ClientConnection*)fClientConnections.ItemAt(i);
                                         i++) {
                                        connection->ShareRemoved(share);
                                }

                                share->ReleaseReference();
                        }

                        if (error == B_OK)
                                _ServerInfoUpdated();

                        _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_SHARES:
                {
                        // get the shares
                        BMessage reply;
                        BMessage shares;
                        status_t error = fSecurityContext->GetShares(&shares);
                        if (error == B_OK)
                                error = reply.AddMessage("shares", &shares);

                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_SHARE_USERS:
                {
                        // get share name
                        const char* shareName;
                        if (message->FindString("share", &shareName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        AutoLocker<Locker> securityContextLocker(fSecurityContext);

                        // get the share
                        Share* share = fSecurityContext->FindShare(shareName);
                        if (!share) {
                                _SendReply(message, B_ENTRY_NOT_FOUND);
                                break;
                        }
                        BReference<Share> shareReference(share, true);

                        // get all users
                        BMessage allUsers;
                        status_t error = fSecurityContext->GetUsers(&allUsers);
                        if (error != B_OK) {
                                _SendReply(message, error);
                                break;
                        }

                        // filter the users with mount permission
                        BMessage users;
                        const char* userName;
                        for (int32 i = 0;
                                 allUsers.FindString("users", i, &userName) == B_OK;
                                 i++) {
                                if (User* user = fSecurityContext->FindUser(userName)) {
                                        // get the user's permissions
                                        Permissions permissions = fSecurityContext
                                                ->GetNodePermissions(share->GetPath(), user);
                                        user->ReleaseReference();

                                        // add the user, if they have the permission to mount the
                                        // share
                                        if (permissions.ImpliesMountSharePermission()) {
                                                error = users.AddString("users", userName);
                                                if (error != B_OK) {
                                                        _SendReply(message, error);
                                                        break;
                                                }
                                        }
                                }
                        }

                        securityContextLocker.Unlock();

                        // prepare the reply
                        BMessage reply;
                        if (error == B_OK)
                                error = reply.AddMessage("users", &users);

                        // send the reply
                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_SHARE_STATISTICS:
                {
                        // get share name
                        const char* shareName;
                        if (message->FindString("share", &shareName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // get the share
                        Share* share = fSecurityContext->FindShare(shareName);
                        if (!share) {
                                _SendReply(message, B_ENTRY_NOT_FOUND);
                                break;
                        }
                        BReference<Share> shareReference(share, true);

                        // get the statistics
                        BMessage statistics;
                        status_t error = StatisticsManager::GetDefault()
                                ->GetShareStatistics(share, &statistics);

                        // prepare the reply
                        BMessage reply;
                        if (error == B_OK)
                                error = reply.AddMessage("statistics", &statistics);

                        // send the reply
                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_SET_USER_PERMISSIONS:
                {
                        // get share and user name, and the permissions
                        const char* shareName;
                        const char* userName;
                        uint32 permissions;
                        if (message->FindString("share", &shareName) != B_OK
                                || message->FindString("user", &userName) != B_OK
                                || message->FindInt32("permissions", (int32*)&permissions)
                                        != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // get the share and the user
                        Share* share = fSecurityContext->FindShare(shareName);
                        User* user = fSecurityContext->FindUser(userName);
                        BReference<Share> shareReference(share);
                        BReference<User> userReference(user);
                        if (!share || !user) {
                                _SendReply(message, B_ENTRY_NOT_FOUND);
                                break;
                        }

                        // set the permissions
                        status_t error = B_OK;
                        if (permissions == 0) {
                                fSecurityContext->ClearNodePermissions(share->GetPath(), user);
                        } else {
                                error = fSecurityContext->SetNodePermissions(share->GetPath(),
                                        user, permissions);
                        }

                        if (error == B_OK) {
                                // propagate the information to the client connections
                                AutoLocker<Locker> _(fLock);
                                for (int32 i = 0;
                                         ClientConnection* connection
                                                = (ClientConnection*)fClientConnections.ItemAt(i);
                                         i++) {
                                        connection->UserPermissionsChanged(share, user,
                                                permissions);
                                }
                        }

                        _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_GET_USER_PERMISSIONS:
                {
                        // get share and user name
                        const char* shareName;
                        const char* userName;
                        if (message->FindString("share", &shareName) != B_OK
                                || message->FindString("user", &userName) != B_OK) {
                                _SendReply(message, B_BAD_VALUE);
                                break;
                        }

                        // get the share and the user
                        Share* share = fSecurityContext->FindShare(shareName);
                        User* user = fSecurityContext->FindUser(userName);
                        BReference<Share> shareReference(share);
                        BReference<User> userReference(user);
                        if (!share || !user) {
                                _SendReply(message, B_ENTRY_NOT_FOUND);
                                break;
                        }

                        // get the permissions
                        Permissions permissions = fSecurityContext->GetNodePermissions(
                                share->GetPath(), user);

                        // prepare the reply
                        BMessage reply;
                        status_t error = reply.AddInt32("permissions",
                                (int32)permissions.GetPermissions());

                        // send it
                        if (error == B_OK)
                                _SendReply(message, &reply);
                        else
                                _SendReply(message, error);
                        break;
                }

                case NETFS_REQUEST_SAVE_SETTINGS:
                {
                        status_t error = _SaveSettings();

                        // send a reply
                        _SendReply(message, error);
                        break;
                }
        }
}

// GetVolumeManager
VolumeManager*
NetFSServer::GetVolumeManager() const
{
        return fVolumeManager;
}

// GetSecurityContext
SecurityContext*
NetFSServer::GetSecurityContext() const
{
        return fSecurityContext;
}

// _AddClientConnection
status_t
NetFSServer::_AddClientConnection(ClientConnection* clientConnection)
{
        if (!clientConnection)
                return B_BAD_VALUE;
        AutoLocker<Locker> locker(fLock);
        if (!fClientConnections.AddItem(clientConnection))
                return B_NO_MEMORY;
        return B_OK;
}

// ClientConnectionClosed
void
NetFSServer::ClientConnectionClosed(ClientConnection* connection, bool broken)
{
        PRINT("NetFSServer::ClientConnectionClosed(%d)\n", broken);
        if (!connection)
                return;
        AutoLocker<Locker> locker(fLock);
        if (!fClientConnections.RemoveItem(connection))
                return;
        if (!fClosedConnections.AddItem(connection)) {
                // out of memory: Try to delete the connection right now.,
                // There's a certain chance that we'll access free()d memory in the
                // process, but things are apparently bad enough, anyway.
                locker.Unlock();
                delete connection;
                return;
        }
        release_sem(fClosedConnectionsSemaphore);
}

// _LoadSecurityContext
status_t
NetFSServer::_LoadSecurityContext(SecurityContext** _securityContext)
{
        // create a security context
        SecurityContext* securityContext = new(std::nothrow) SecurityContext;
        if (!securityContext)
                return B_NO_MEMORY;
        status_t error = securityContext->InitCheck();
        if (error != B_OK) {
                delete securityContext;
                return error;
        }
        ObjectDeleter<SecurityContext> securityContextDeleter(securityContext);

        // load the fallback settings, if present
        BPath path;
        DriverSettings settings;

        if (_GetSettingsDirPath(&path, false) == B_OK
                        && path.Append(kFallbackSettingsFileName) == B_OK
                        && settings.Load(path.Path()) == B_OK) {
                // load users
                DriverParameter parameter;
                for (DriverParameterIterator it = settings.GetParameterIterator("user");
                         it.GetNext(&parameter);) {
                        const char* userName = parameter.ValueAt(0);
                        const char* password = parameter.GetParameterValue("password");
                        if (!userName) {
                                WARN("Skipping nameless user settings entry.\n");
                                continue;
                        }
//                      PRINT(("user: %s, password: %s\n", parameter.ValueAt(0),
//                              parameter.GetParameterValue("password")));
                        error = securityContext->AddUser(userName, password);
                        if (error != B_OK)
                                ERROR("ERROR: Failed to add user `%s'\n", userName);
                }

                // load shares
                for (DriverParameterIterator it = settings.GetParameterIterator("share");
                         it.GetNext(&parameter);) {
                        const char* shareName = parameter.ValueAt(0);
                        const char* path = parameter.GetParameterValue("path");
                        if (!shareName || !path) {
                                WARN("settings: Skipping invalid share settings entry (no name"
                                        " or no path).\n");
                                continue;
                        }
//                      PRINT(("share: %s, path: %s\n", parameter.ValueAt(0),
//                              parameter.GetParameterValue("path")));
                        Share* share;
                        error = securityContext->AddShare(shareName, path, &share);
                        if (error != B_OK) {
                                ERROR("ERROR: Failed to add share `%s'\n", shareName);
                                continue;
                        }
                        BReference<Share> shareReference(share, true);
                        DriverParameter userParameter;
                        // iterate through the share users
                        for (DriverParameterIterator userIt
                                        = parameter.GetParameterIterator("user");
                                 userIt.GetNext(&userParameter);) {
                                const char* userName = userParameter.ValueAt(0);
//                              PRINT(("  user: %s\n", userName));
                                User* user = securityContext->FindUser(userName);
                                if (!user) {
                                        ERROR("ERROR: Undefined user `%s'.\n", userName);
                                        continue;
                                }
                                BReference<User> userReference(user, true);
                                DriverParameter permissionsParameter;
                                if (!userParameter.FindParameter("permissions",
                                                &permissionsParameter)) {
                                        continue;
                                }
                                Permissions permissions;
                                for (int32 i = 0; i < permissionsParameter.CountValues(); i++) {
                                        const char* permission = permissionsParameter.ValueAt(i);
//                                      PRINT(("    permission: %s\n", permission));
                                        if (strcmp(permission, "mount") == 0) {
                                                permissions.AddPermissions(MOUNT_SHARE_PERMISSION);
                                        } else if (strcmp(permission, "query") == 0) {
                                                permissions.AddPermissions(QUERY_SHARE_PERMISSION);
                                        } else if (strcmp(permission, "read") == 0) {
                                                permissions.AddPermissions(READ_PERMISSION
                                                        | READ_DIR_PERMISSION | RESOLVE_DIR_ENTRY_PERMISSION);
                                        } else if (strcmp(permission, "write") == 0) {
                                                permissions.AddPermissions(WRITE_PERMISSION
                                                        | WRITE_DIR_PERMISSION);
                                        } else if (strcmp(permission, "all") == 0) {
                                                permissions.AddPermissions(ALL_PERMISSIONS);
                                        }
                                }
                                error = securityContext->SetNodePermissions(share->GetPath(), user,
                                        permissions);
                                if (error != B_OK) {
                                        ERROR("ERROR: Failed to set permissions for share `%s'\n",
                                                share->GetName());
                                }
                        }
                }
        }

        securityContextDeleter.Detach();
        *_securityContext = securityContext;
        return B_OK;
}

// _InitSettings
status_t
NetFSServer::_InitSettings()
{
        status_t error = _LoadSettings();
        if (error != B_OK) {
                WARN("NetFSServer::_InitSettings(): WARNING: Failed to load settings "
                        "file: %s - falling back to driver settings.\n", strerror(error));

                // fall back to the driver settings file
                error = _LoadSecurityContext(&fSecurityContext);
                if (error != B_OK) {
                        WARN("NetFSServer::_InitSettings(): WARNING: Failed to load "
                                "settings from driver settings: %s\n", strerror(error));

                        // use defaults
                        // create a security context
                        fSecurityContext = new(std::nothrow) SecurityContext;
                        if (!fSecurityContext)
                                return B_NO_MEMORY;
                        error = fSecurityContext->InitCheck();
                        if (error != B_OK)
                                return error;
                }
        }

        return B_OK;
}

// _LoadSettings
status_t
NetFSServer::_LoadSettings()
{
        // get the settings file path
        BPath filePath;
        status_t error = _GetSettingsFilePath(&filePath, false);
        if (error != B_OK)
                RETURN_ERROR(error);

        // if existing load the settings
        BEntry bEntry;
        if (FDManager::SetEntry(&bEntry, filePath.Path()) != B_OK
                || !bEntry.Exists()) {
                return B_ENTRY_NOT_FOUND;
        }

        // open the settings file
        BFile file;
        error = FDManager::SetFile(&file, filePath.Path(), B_READ_ONLY);
        if (error != B_OK)
                RETURN_ERROR(error);

        // read the settings
        BMessage settings;
        error = settings.Unflatten(&file);
        if (error != B_OK)
                RETURN_ERROR(error);

        // get the security context archive
        BMessage securityContextArchive;
        error = settings.FindMessage("security context",
                &securityContextArchive);
        if (error != B_OK)
                RETURN_ERROR(error);

        // create a security context
        SecurityContext* securityContext
                = new(std::nothrow) SecurityContext(&securityContextArchive);
        if (!securityContext)
                RETURN_ERROR(B_NO_MEMORY);
        ObjectDeleter<SecurityContext> securityContextDeleter(securityContext);
        error = securityContext->InitCheck();
        if (error != B_OK)
                RETURN_ERROR(error);

        // set it
        delete fSecurityContext;
        fSecurityContext = securityContext;
        securityContextDeleter.Detach();

        return B_OK;
}

// _SaveSettings
status_t
NetFSServer::_SaveSettings()
{
        AutoLocker<Locker> locker(fSecurityContext);

        // create the settings archive
        BMessage settings;

        // archive the security context
        BMessage securityContextArchive;
        status_t error = fSecurityContext->Archive(&securityContextArchive, true);
        if (error != B_OK)
                RETURN_ERROR(error);

        // add it to the settings archive
        error = settings.AddMessage("security context", &securityContextArchive);
        if (error != B_OK)
                RETURN_ERROR(error);

        // open the settings file
        BPath filePath;
        error = _GetSettingsFilePath(&filePath, true);
        if (error != B_OK)
                RETURN_ERROR(error);
        BFile file;
        error = FDManager::SetFile(&file, filePath.Path(),
                B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
        if (error != B_OK)
                RETURN_ERROR(error);

        // write to the settings file
        error = settings.Flatten(&file);
        if (error != B_OK)
                RETURN_ERROR(error);

        return B_OK;
}

// _GetSettingsDirPath
status_t
NetFSServer::_GetSettingsDirPath(BPath* path, bool create)
{
        // get the user settings directory
        BPath settingsDir;
        status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &settingsDir,
                create);
        if (error != B_OK)
                RETURN_ERROR(error);

        // create our subdir, if not existing and desired
        error = path->SetTo(settingsDir.Path(), kSettingsDirName);
        if (error != B_OK)
                RETURN_ERROR(error);
        BEntry bEntry;
        if (create
                && (FDManager::SetEntry(&bEntry, settingsDir.Path()) != B_OK
                        || !bEntry.Exists())) {
                error = create_directory(path->Path(), S_IRWXU | S_IRWXG | S_IRWXO);
                if (error != B_OK)
                        RETURN_ERROR(error);
        }

        return B_OK;
}

// _GetSettingsFilePath
status_t
NetFSServer::_GetSettingsFilePath(BPath* path, bool createDir)
{
        // get settings dir
        BPath dirPath;
        status_t error = _GetSettingsDirPath(&dirPath, createDir);
        if (error != B_OK)
                return error;

        // construct the file path
        return path->SetTo(dirPath.Path(), kSettingsFileName);
}

// _InitServerInfoConnectionListener
status_t
NetFSServer::_InitServerInfoConnectionListener()
{
        // spawn the listener thread
        fServerInfoConnectionListener = spawn_thread(
                &_ServerInfoConnectionListenerEntry,
                "server info connection listener", B_NORMAL_PRIORITY, this);
        if (fServerInfoConnectionListener < 0)
                return fServerInfoConnectionListener;
        // create a listener socket
        fServerInfoConnectionListenerSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (fServerInfoConnectionListenerSocket < 0)
                return errno;
        // bind it to the port
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(kDefaultServerInfoPort);
        addr.sin_addr.s_addr = INADDR_ANY;
        if (bind(fServerInfoConnectionListenerSocket, (sockaddr*)&addr,
                sizeof(addr)) < 0) {
                return errno;
        }
        // start listening
        if (listen(fServerInfoConnectionListenerSocket, 5) < 0)
                return errno;
        return B_OK;
}

// _ExitServerInfoConnectionListener
void
NetFSServer::_ExitServerInfoConnectionListener()
{
        // close the socket
        safe_closesocket(fServerInfoConnectionListenerSocket);
        // wait for the listener
        if (fServerInfoConnectionListener >= 0) {
                int32 result;
                wait_for_thread(fServerInfoConnectionListener, &result);
        }
}

// _ConnectionListenerEntry
int32
NetFSServer::_ConnectionListenerEntry(void* data)
{
        return ((NetFSServer*)data)->_ConnectionListener();
}

// _ConnectionListener
int32
NetFSServer::_ConnectionListener()
{
        // start listening for connections
        Connection* connection = NULL;
        status_t error = B_OK;
        do {
                error = fConnectionListener->Listen(&connection);
                if (error == B_OK) {
                        ConnectionInitializer* initializer
                                = new(std::nothrow) ConnectionInitializer(this, fConnectionListener,
                                        connection);
                        if (initializer) {
                                if (initializer->Run() != B_OK) {
                                        ERROR("Failed to run connection initializer.\n")
                                        delete initializer;
                                }
                        } else {
                                ERROR("Failed to create connection initializer.\n")
                                delete connection;
                        }

                }
        } while (error == B_OK && !fTerminating);

        return 0;
}

// _ConnectionDeleterEntry
int32
NetFSServer::_ConnectionDeleterEntry(void* data)
{
        return ((NetFSServer*)data)->_ConnectionDeleter();
}

// _ConnectionDeleter
int32
NetFSServer::_ConnectionDeleter()
{
        while (!fTerminating) {
                status_t error = acquire_sem(fClosedConnectionsSemaphore);
                ClientConnection* connection = NULL;
                if (error == B_OK) {
                        AutoLocker<Locker> _(fLock);
                        connection = (ClientConnection*)fClosedConnections.RemoveItem((int32)0);
                }
                if (connection)
                        delete connection;
        }
        return 0;
}

// _BroadcasterEntry
int32
NetFSServer::_BroadcasterEntry(void* data)
{
        return ((NetFSServer*)data)->_Broadcaster();
}

// _Broadcaster
int32
NetFSServer::_Broadcaster()
{
        // create the socket
        fBroadcastingSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (fBroadcastingSocket < 0) {
                WARN("NetFSServer::_Broadcaster(): WARN: Failed to init broadcasting: "
                        "%s.\n", strerror(errno));
                return errno;
        }

        // set the socket broadcast option
        #ifndef HAIKU_TARGET_PLATFORM_BEOS
                int soBroadcastValue = 1;
                if (setsockopt(fBroadcastingSocket, SOL_SOCKET, SO_BROADCAST,
                        &soBroadcastValue, sizeof(soBroadcastValue)) < 0) {
                        WARN("NetFSServer::_Broadcaster(): WARN: Failed to set "
                                "SO_BROADCAST on socket: %s.\n", strerror(errno));
                }
        #endif

        // prepare the broadcast message
        BroadcastMessage message;
        message.magic = B_HOST_TO_BENDIAN_INT32(BROADCAST_MESSAGE_MAGIC);
        message.protocolVersion = B_HOST_TO_BENDIAN_INT32(NETFS_PROTOCOL_VERSION);

        bool update = false;
        while (!fTerminating) {
                // set tick/update
                uint32 messageCode = (update ? BROADCAST_MESSAGE_SERVER_UPDATE
                        : BROADCAST_MESSAGE_SERVER_TICK);
                message.message = B_HOST_TO_BENDIAN_INT32(messageCode);

                // send broadcasting message
                sockaddr_in addr;
                addr.sin_family = AF_INET;
                addr.sin_port = htons(kDefaultBroadcastPort);
                addr.sin_addr.s_addr = INADDR_BROADCAST;
                int addrSize = sizeof(addr);
                ssize_t bytesSent = sendto(fBroadcastingSocket, &message,
                        sizeof(message), 0, (sockaddr*)&addr, addrSize);
                if (bytesSent < 0) {
                        WARN("NetFSServer::_Broadcaster(): WARN: sending failed: %s.\n",
                                strerror(errno));
                        return errno;
                }

                // snooze a bit
                // we snooze a minimal interval to avoid shooting updates like a
                // machine gun
                snooze(kMinBroadcastingInterval);
                bigtime_t remainingTime = kBroadcastingInterval
                        - kMinBroadcastingInterval;

                // snooze the rest blocking on our semaphore (if it exists)
                if (fBroadcasterSemaphore >= 0) {
                        status_t snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1,
                                B_RELATIVE_TIMEOUT, remainingTime);

                        // set the semaphore count back to zero
                        while (snoozeError == B_OK) {
                                snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1,
                                        B_RELATIVE_TIMEOUT, 0);
                        }
                } else
                        snooze(remainingTime);

                update = atomic_and(&fServerInfoUpdated, 0);
        }

        // close the socket
        safe_closesocket(fBroadcastingSocket);
        return B_OK;
}

// _ServerInfoConnectionListenerEntry
int32
NetFSServer::_ServerInfoConnectionListenerEntry(void* data)
{
        return ((NetFSServer*)data)->_ServerInfoConnectionListener();
}

// _ServerInfoConnectionListener
int32
NetFSServer::_ServerInfoConnectionListener()
{
        if (fServerInfoConnectionListenerSocket < 0)
                return B_BAD_VALUE;

        TaskManager taskManager;

        // accept a incoming connection
        while (!fTerminating) {
                int fd = -1;
                do {
                        taskManager.RemoveDoneTasks();

                        fd = accept(fServerInfoConnectionListenerSocket, NULL, 0);
                        if (fd < 0) {
                                status_t error = errno;
                                if (error != B_INTERRUPTED)
                                        return error;
                                if (fTerminating)
                                        return B_OK;
                        }
                } while (fd < 0);

                // get a fresh server info
                ServerInfo info;
                status_t error = _GetServerInfo(info);
                if (error != B_OK) {
                        closesocket(fd);
                        return error;
                }

                // create a server info sender thread
                ServerInfoSender* sender = new(std::nothrow) ServerInfoSender(fd, info);
                if (sender == NULL) {
                        closesocket(fd);
                        delete sender;
                        return B_NO_MEMORY;
                }
                if ((error = sender->Init()) != B_OK) {
                        closesocket(fd);
                        delete sender;
                        return error;
                }
                taskManager.RunTask(sender);
        }

        return B_OK;
}

// _GetServerInfo
status_t
NetFSServer::_GetServerInfo(ServerInfo& serverInfo)
{
        // set the server name and the connection method
        char hostName[1024];
        if (gethostname(hostName, sizeof(hostName)) < 0) {
                ERROR("NetFSServer::_GetServerInfo(): ERROR: Failed to get host "
                        "name.");
                return B_ERROR;
        }
        status_t error = serverInfo.SetServerName(hostName);
// TODO: Set the actually used connection method!
        if (error == B_OK)
                error = serverInfo.SetConnectionMethod("insecure");
        if (error != B_OK)
                return error;

        // get the shares from the security context
        BMessage shares;
        error = fSecurityContext->GetShares(&shares);
        if (error != B_OK)
                return error;

        // add the shares
        const char* shareName;
        for (int32 i = 0; shares.FindString("shares", i, &shareName) == B_OK; i++) {
                error = serverInfo.AddShare(shareName);
                if (error != B_OK)
                        return error;
        }
        return B_OK;
}

// _ServerInfoUpdated
void
NetFSServer::_ServerInfoUpdated()
{
        atomic_or(&fServerInfoUpdated, 1);
        release_sem(fBroadcasterSemaphore);
}

// _SendReply
void
NetFSServer::_SendReply(BMessage* message, BMessage* reply, status_t error)
{
        // no reply is specified, if no data have to be sent
        BMessage stackReply;
        if (!reply)
                reply = &stackReply;

        reply->AddInt32("error", error);
        message->SendReply(reply, (BHandler*)NULL, 0LL);
}

// _SendReply
void
NetFSServer::_SendReply(BMessage* message, status_t error)
{
        _SendReply(message, NULL, error);
}


// #pragma mark -

// print_usage
static
void
print_usage(bool error)
{
        fputs(kUsage, (error ? stderr : stdout));
}

// main
int
main(int argc, char** argv)
{
        #ifdef DEBUG_OBJECT_TRACKING
                ObjectTracker::InitDefault();
        #endif

        // parse the arguments
        bool broadcast = true;
        for (int argi = 1; argi < argc; argi++) {
                const char* arg = argv[argi];
                if (strcmp(arg, "--dont-broadcast") == 0) {
                        broadcast = false;
                } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
                        print_usage(false);
                        return 0;
                } else {
                        print_usage(true);
                        return 1;
                }
        }

        // create the statistics manager
        status_t error = StatisticsManager::CreateDefault();
        if (error != B_OK) {
                fprintf(stderr, "Failed to create statistics manager: %s\n",
                        strerror(error));
                return 1;
        }

        // init and run the server
        {
                NetFSServer server(broadcast);
                error = server.Init();
                if (error != B_OK) {
                        fprintf(stderr, "Failed to initialize server: %s\n",
                                strerror(error));
                        return 1;
                }
                server.Run();
        }

        // delete the statistics manager
        StatisticsManager::DeleteDefault();

        #ifdef DEBUG_OBJECT_TRACKING
                ObjectTracker::ExitDefault();
        #endif

        return 0;
}