2020-04-24 17:48:22 +02:00
|
|
|
module server.server;
|
2020-04-16 15:02:34 +02:00
|
|
|
|
2020-04-16 16:35:07 +02:00
|
|
|
import utils.debugging : debugPrint;
|
|
|
|
import std.conv : to;
|
2020-05-01 09:55:50 +02:00
|
|
|
import std.socket : Socket, AddressFamily, SocketType, ProtocolType, parseAddress;
|
2020-04-16 17:05:56 +02:00
|
|
|
import core.thread : Thread;
|
2020-04-17 18:47:05 +02:00
|
|
|
import std.stdio : writeln, File;
|
2020-04-18 15:25:22 +02:00
|
|
|
import std.json : JSONValue, parseJSON, JSONException, JSONType, toJSON;
|
2020-05-01 09:38:17 +02:00
|
|
|
import std.string : cmp, strip;
|
2020-05-01 09:55:50 +02:00
|
|
|
import handlers.handler : MessageHandler;
|
|
|
|
import listeners.listener : BesterListener;
|
|
|
|
import connection.connection : BesterConnection;
|
2020-05-04 15:22:49 +02:00
|
|
|
import server.informer.informer : BesterInformer;
|
2020-04-20 17:18:40 +02:00
|
|
|
|
2020-04-24 19:19:53 +02:00
|
|
|
public final class BesterServer
|
2020-04-16 15:02:34 +02:00
|
|
|
{
|
2020-04-17 13:46:54 +02:00
|
|
|
/**
|
|
|
|
* Message handlers
|
|
|
|
*
|
|
|
|
* Associative array of `payloadType (string)`:`MessageHandler`
|
|
|
|
* TODO: Implement this
|
|
|
|
*/
|
2020-04-24 17:48:22 +02:00
|
|
|
public MessageHandler[] handlers;
|
2020-04-16 16:35:07 +02:00
|
|
|
|
2020-04-16 16:39:31 +02:00
|
|
|
/* The server's socket */
|
|
|
|
private Socket serverSocket;
|
2020-04-20 18:55:48 +02:00
|
|
|
/* TODO: The above to be replaced */
|
|
|
|
|
2020-04-20 19:23:59 +02:00
|
|
|
/* Socket listeners for incoming connections */
|
2020-04-20 18:55:48 +02:00
|
|
|
private BesterListener[] listeners;
|
2020-04-16 16:39:31 +02:00
|
|
|
|
2020-04-19 18:53:16 +02:00
|
|
|
/* Connected clients */
|
2020-04-20 18:48:06 +02:00
|
|
|
public BesterConnection[] clients;
|
2020-04-19 18:53:16 +02:00
|
|
|
|
2020-05-04 15:17:31 +02:00
|
|
|
/* The informer server */
|
|
|
|
private BesterInformer informer;
|
|
|
|
|
2020-05-02 22:43:05 +02:00
|
|
|
/**
|
|
|
|
* Returns a list of BesterConnection objects that
|
|
|
|
* match the usernames provided.
|
|
|
|
*
|
|
|
|
* @param usernames Array of strings of usernames to match to
|
|
|
|
*/
|
2020-04-25 18:58:32 +02:00
|
|
|
public BesterConnection[] getClients(string[] usernames)
|
|
|
|
{
|
2020-04-25 19:36:23 +02:00
|
|
|
/* List of authenticated users matching `usernames` */
|
|
|
|
BesterConnection[] matchedUsers;
|
2020-04-25 20:30:49 +02:00
|
|
|
|
2020-04-28 17:44:28 +02:00
|
|
|
//debugPrint("All clients (ever connected): " ~ to!(string)(clients));
|
|
|
|
|
2020-04-25 22:20:12 +02:00
|
|
|
/* Search through the provided usernames */
|
|
|
|
for(ulong i = 0; i < usernames.length; i++)
|
|
|
|
{
|
2020-04-25 22:33:35 +02:00
|
|
|
for(ulong k = 0; k < clients.length; k++)
|
2020-04-25 20:30:49 +02:00
|
|
|
{
|
2020-04-25 22:20:12 +02:00
|
|
|
/* The potentially-matched user */
|
|
|
|
BesterConnection potentialMatch = clients[k];
|
|
|
|
|
|
|
|
/* Check if the user is authenticated */
|
2020-04-27 16:34:14 +02:00
|
|
|
if(potentialMatch.getType() == BesterConnection.Scope.CLIENT && cmp(potentialMatch.getCredentials()[0], usernames[i]) == 0)
|
2020-04-25 20:30:49 +02:00
|
|
|
{
|
2020-04-25 22:20:12 +02:00
|
|
|
matchedUsers ~= potentialMatch;
|
|
|
|
}
|
2020-04-25 20:30:49 +02:00
|
|
|
}
|
2020-04-25 19:36:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return matchedUsers;
|
2020-04-25 18:58:32 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 20:08:55 +02:00
|
|
|
public void addListener(BesterListener listener)
|
2020-04-16 16:35:07 +02:00
|
|
|
{
|
2020-04-20 20:08:55 +02:00
|
|
|
this.listeners ~= listener;
|
2020-04-20 19:07:29 +02:00
|
|
|
}
|
2020-04-17 20:23:22 +02:00
|
|
|
|
2020-04-20 19:07:29 +02:00
|
|
|
this(JSONValue config)
|
|
|
|
{
|
2020-04-17 20:40:02 +02:00
|
|
|
/* TODO: Bounds check and JSON type check */
|
2020-04-20 20:30:17 +02:00
|
|
|
//debugPrint("Setting up socket...");
|
|
|
|
//setupServerSocket(config["network"]);
|
2020-04-17 20:40:02 +02:00
|
|
|
|
|
|
|
/* TODO: Bounds check and JSON type check */
|
2020-04-17 18:47:05 +02:00
|
|
|
debugPrint("Setting up message handlers...");
|
2020-04-17 20:40:02 +02:00
|
|
|
setupHandlers(config["handlers"]);
|
2020-04-16 16:39:31 +02:00
|
|
|
}
|
|
|
|
|
2020-04-17 20:40:02 +02:00
|
|
|
private void setupHandlers(JSONValue handlerBlock)
|
2020-04-17 13:46:54 +02:00
|
|
|
{
|
|
|
|
/* TODO: Implement me */
|
2020-04-17 18:07:13 +02:00
|
|
|
debugPrint("Constructing message handlers...");
|
2020-04-30 15:55:40 +02:00
|
|
|
handlers = MessageHandler.constructHandlers(this, handlerBlock);
|
2020-04-17 13:46:54 +02:00
|
|
|
}
|
|
|
|
|
2020-04-17 20:23:22 +02:00
|
|
|
/* Setup the server socket */
|
|
|
|
private void setupServerSocket(JSONValue networkBlock)
|
2020-04-16 16:39:31 +02:00
|
|
|
{
|
2020-04-17 20:23:22 +02:00
|
|
|
string bindAddress;
|
|
|
|
ushort listenPort;
|
|
|
|
|
|
|
|
JSONValue jsonAddress, jsonPort;
|
|
|
|
|
2020-05-01 09:45:44 +02:00
|
|
|
debugPrint(networkBlock);
|
2020-04-17 20:23:22 +02:00
|
|
|
|
|
|
|
/* TODO: Bounds check */
|
|
|
|
jsonAddress = networkBlock["address"];
|
|
|
|
jsonPort = networkBlock["port"];
|
|
|
|
|
|
|
|
bindAddress = jsonAddress.str;
|
|
|
|
listenPort = cast(ushort)jsonPort.integer;
|
|
|
|
|
|
|
|
debugPrint("Binding to address: " ~ bindAddress ~ " and port " ~ to!(string)(listenPort));
|
|
|
|
|
2020-04-16 16:39:31 +02:00
|
|
|
/* Create a socket */
|
2020-04-16 16:41:56 +02:00
|
|
|
serverSocket = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
|
2020-04-16 16:43:00 +02:00
|
|
|
serverSocket.bind(parseAddress(bindAddress, listenPort));
|
2020-04-16 16:39:31 +02:00
|
|
|
}
|
|
|
|
|
2020-05-04 15:17:31 +02:00
|
|
|
private void startListeners()
|
2020-04-16 16:39:31 +02:00
|
|
|
{
|
2020-04-20 20:30:17 +02:00
|
|
|
for(ulong i = 0; i < listeners.length; i++)
|
2020-04-16 17:05:56 +02:00
|
|
|
{
|
2020-05-01 09:39:41 +02:00
|
|
|
debugPrint("Starting listener \"" ~ listeners[i].toString() ~ "\"...");
|
2020-04-20 20:30:17 +02:00
|
|
|
listeners[i].start();
|
2020-04-16 17:05:56 +02:00
|
|
|
}
|
2020-05-04 15:17:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts the BesterInformer.
|
|
|
|
*/
|
|
|
|
private void startInformer()
|
|
|
|
{
|
2020-05-04 15:17:53 +02:00
|
|
|
informer = new BesterInformer(this);
|
2020-05-04 15:17:31 +02:00
|
|
|
informer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start listen loop */
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
/* Start the listeners */
|
|
|
|
startListeners();
|
|
|
|
|
|
|
|
/* Start the informer */
|
|
|
|
startInformer();
|
2020-04-16 17:05:56 +02:00
|
|
|
}
|
2020-04-16 23:25:22 +02:00
|
|
|
|
|
|
|
/* Authenticate the user */
|
|
|
|
public bool authenticate(string username, string password)
|
|
|
|
{
|
|
|
|
/* TODO: Implement me */
|
2020-04-17 11:57:06 +02:00
|
|
|
debugPrint("Attempting to authenticate:\n\nUsername: " ~ username ~ "\nPassword: " ~ password);
|
2020-04-19 18:53:16 +02:00
|
|
|
|
|
|
|
/* If the authentication went through */
|
|
|
|
bool authed = true;
|
|
|
|
|
2020-05-01 09:38:17 +02:00
|
|
|
/* Strip the username of whitespace (TODO: Should we?) */
|
|
|
|
username = strip(username);
|
|
|
|
|
|
|
|
/* Make sure username and password are not empty */
|
|
|
|
if(cmp(username, "") != 0 && cmp(password, "") != 0)
|
2020-04-19 18:53:16 +02:00
|
|
|
{
|
2020-05-01 09:38:17 +02:00
|
|
|
|
2020-04-19 18:53:16 +02:00
|
|
|
}
|
2020-05-01 09:38:17 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
authed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return authed;
|
2020-04-16 23:25:22 +02:00
|
|
|
}
|
2020-04-19 13:37:26 +02:00
|
|
|
|
2020-04-19 14:45:53 +02:00
|
|
|
/* Returns the MessageHandler object of the requested type */
|
|
|
|
public MessageHandler findHandler(string payloadType)
|
|
|
|
{
|
|
|
|
/* The found MessageHandler */
|
|
|
|
MessageHandler foundHandler;
|
|
|
|
|
|
|
|
for(uint i = 0; i < handlers.length; i++)
|
|
|
|
{
|
|
|
|
if(cmp(handlers[i].getPluginName(), payloadType) == 0)
|
|
|
|
{
|
|
|
|
foundHandler = handlers[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return foundHandler;
|
|
|
|
}
|
|
|
|
|
2020-04-19 14:31:24 +02:00
|
|
|
public static bool isBuiltInCommand(string command)
|
2020-04-19 13:37:26 +02:00
|
|
|
{
|
2020-04-19 13:44:57 +02:00
|
|
|
/* Whether or not `payloadType` is a built-in command */
|
|
|
|
bool isBuiltIn = true;
|
|
|
|
|
2020-04-19 14:31:24 +02:00
|
|
|
|
2020-04-19 13:44:57 +02:00
|
|
|
return isBuiltIn;
|
2020-04-19 13:37:26 +02:00
|
|
|
}
|
2020-05-07 19:01:00 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops the server.
|
|
|
|
*/
|
|
|
|
private void shutdown()
|
|
|
|
{
|
|
|
|
/* Stop the informer service */
|
|
|
|
informer.shutdown();
|
|
|
|
|
2020-05-07 19:08:19 +02:00
|
|
|
/* Shutdown all the listeners */
|
|
|
|
shutdownListeners();
|
|
|
|
|
|
|
|
/* Shutdown all the clients */
|
|
|
|
shutdownClients();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void shutdownListeners()
|
|
|
|
{
|
|
|
|
/* Shutdown all the listeners */
|
|
|
|
for(ulong i = 0; i < listeners.length; i++)
|
|
|
|
{
|
|
|
|
listeners[i].shutdown();
|
|
|
|
}
|
|
|
|
}
|
2020-05-07 19:01:00 +02:00
|
|
|
|
2020-05-07 19:08:19 +02:00
|
|
|
private void shutdownClients()
|
|
|
|
{
|
|
|
|
/* Shutdown all the clients */
|
|
|
|
for(ulong i = 0; i < clients.length; i++)
|
|
|
|
{
|
|
|
|
clients[i].shutdown();
|
|
|
|
}
|
2020-05-07 19:01:00 +02:00
|
|
|
}
|
2020-04-16 17:05:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|