2020-09-23 09:37:18 +02:00
|
|
|
/**
|
|
|
|
* DServer
|
|
|
|
*
|
|
|
|
* Represents a server instance.
|
|
|
|
*
|
|
|
|
* Holds a list of DConnections,
|
|
|
|
* configuration parameters and
|
|
|
|
* more.
|
|
|
|
*/
|
|
|
|
|
|
|
|
module dnetd.dserver;
|
|
|
|
|
|
|
|
import core.thread : Thread;
|
2020-09-23 21:49:56 +02:00
|
|
|
import std.socket : Address, Socket, AddressFamily, SocketType, ProtocolType;
|
2020-09-23 09:37:18 +02:00
|
|
|
import dnetd.dconnection;
|
2020-09-23 18:52:11 +02:00
|
|
|
import dnetd.dchannel;
|
|
|
|
import std.string : cmp;
|
|
|
|
import core.sync.mutex : Mutex;
|
2020-09-25 12:53:34 +02:00
|
|
|
import std.stdio;
|
2020-09-28 11:42:14 +02:00
|
|
|
import std.conv : to;
|
2020-10-02 16:11:27 +02:00
|
|
|
import dnetd.dconfig;
|
2020-10-05 15:59:08 +02:00
|
|
|
import dnetd.dlink;
|
2020-11-03 10:56:58 +02:00
|
|
|
import dnetd.dlistener;
|
2020-10-29 11:24:12 +02:00
|
|
|
import gogga;
|
2020-09-23 09:37:18 +02:00
|
|
|
|
|
|
|
public class DServer : Thread
|
|
|
|
{
|
2020-10-02 16:11:27 +02:00
|
|
|
/* Server configuration */
|
2021-01-29 16:44:31 +02:00
|
|
|
private DGeneralConfig generalConfig;
|
2020-10-02 16:11:27 +02:00
|
|
|
|
2020-09-23 11:20:09 +02:00
|
|
|
/**
|
|
|
|
* Connection queue
|
|
|
|
*/
|
2020-09-23 09:37:18 +02:00
|
|
|
private DConnection[] connectionQueue;
|
2020-09-27 18:31:34 +02:00
|
|
|
private Mutex connectionLock;
|
2020-09-23 18:52:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Channels
|
|
|
|
*/
|
|
|
|
private DChannel[] channels;
|
|
|
|
private Mutex channelLock;
|
2020-10-05 15:59:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Meyer linking subsystem
|
|
|
|
*/
|
|
|
|
private DMeyer meyerSS;
|
2020-09-23 09:37:18 +02:00
|
|
|
|
2020-11-03 10:56:58 +02:00
|
|
|
/**
|
|
|
|
* The listeners attached to this server
|
|
|
|
*/
|
|
|
|
private DListener[] listeners;
|
|
|
|
|
2020-10-02 16:11:27 +02:00
|
|
|
/* TODO: Implement new constructor */
|
2021-01-29 16:44:31 +02:00
|
|
|
this(DGeneralConfig generalConfig)
|
2020-09-23 09:37:18 +02:00
|
|
|
{
|
|
|
|
/* Set the function to be called on thread start */
|
2020-12-20 17:47:15 +02:00
|
|
|
super(&startListeners);
|
2020-10-02 16:11:27 +02:00
|
|
|
|
2021-01-29 16:44:31 +02:00
|
|
|
/* Set the server's general config */
|
|
|
|
this.generalConfig = generalConfig;
|
2020-09-23 09:37:18 +02:00
|
|
|
|
2020-12-20 17:39:56 +02:00
|
|
|
/* Construct the listeners */
|
2021-01-29 16:44:31 +02:00
|
|
|
initListeners(generalConfig.getAddresses());
|
2020-09-23 09:37:18 +02:00
|
|
|
|
|
|
|
/* Initialize the server */
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
2020-12-20 17:44:22 +02:00
|
|
|
/**
|
|
|
|
* Given an array of Address(es) this will construct all
|
|
|
|
* the corresponding listsners (DListener) and append them
|
|
|
|
* to the array
|
|
|
|
*/
|
|
|
|
private void initListeners(Address[] listenAddresses)
|
2020-12-20 17:39:56 +02:00
|
|
|
{
|
|
|
|
gprintln("Constructing "~to!(string)(listenAddresses.length)~" listsners...");
|
|
|
|
|
|
|
|
foreach(Address listenAddress; listenAddresses)
|
|
|
|
{
|
|
|
|
gprintln("Constructing listener for address '"~to!(string)(listenAddress)~"'");
|
|
|
|
|
|
|
|
import std.socket : AddressInfo;
|
|
|
|
AddressInfo addrInfo;
|
|
|
|
|
|
|
|
/* Set the address (and port) to the current one along with address family */
|
|
|
|
addrInfo.address = listenAddress;
|
|
|
|
addrInfo.family = listenAddress.addressFamily;
|
|
|
|
|
2020-12-20 17:42:23 +02:00
|
|
|
/* Set standard (it will always be TCP and in stream access mode) */
|
2020-12-20 17:39:56 +02:00
|
|
|
addrInfo.protocol = ProtocolType.TCP;
|
|
|
|
addrInfo.type = SocketType.STREAM;
|
|
|
|
|
|
|
|
/* Construct the listener */
|
|
|
|
listeners ~= new DListener(this, addrInfo);
|
|
|
|
gprintln("Listener for '"~to!(string)(listenAddress)~"' constructed");
|
|
|
|
}
|
|
|
|
|
|
|
|
gprintln("Listener construction complete.");
|
|
|
|
}
|
|
|
|
|
2020-12-20 17:47:15 +02:00
|
|
|
/**
|
|
|
|
* Starts all the listeners
|
|
|
|
*/
|
|
|
|
private void startListeners()
|
|
|
|
{
|
|
|
|
foreach(DListener listener; listeners)
|
|
|
|
{
|
|
|
|
/* Start the listener */
|
2020-12-20 18:12:11 +02:00
|
|
|
gprintln("Starting listener "~to!(string)(listener)~"...");
|
2020-12-20 17:47:15 +02:00
|
|
|
listener.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-29 16:44:31 +02:00
|
|
|
public DGeneralConfig getGeneralConfig()
|
2020-10-02 16:11:27 +02:00
|
|
|
{
|
2021-01-29 16:44:31 +02:00
|
|
|
return generalConfig;
|
2020-10-02 16:11:27 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 09:37:18 +02:00
|
|
|
private void init()
|
|
|
|
{
|
|
|
|
/* Setup queues */
|
|
|
|
initQueues();
|
2020-09-23 18:52:11 +02:00
|
|
|
|
|
|
|
/* Setup locks */
|
|
|
|
initLocks();
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates all needed queues
|
|
|
|
* and their mutexes
|
|
|
|
*/
|
|
|
|
private void initQueues()
|
|
|
|
{
|
|
|
|
/* TODO: Implement me */
|
|
|
|
}
|
2020-09-23 18:52:11 +02:00
|
|
|
|
|
|
|
private void initLocks()
|
|
|
|
{
|
2020-09-27 18:31:34 +02:00
|
|
|
/* Initialize the connection lock */
|
|
|
|
connectionLock = new Mutex();
|
|
|
|
|
|
|
|
/* Initialize the channel lock */
|
2020-09-23 18:52:11 +02:00
|
|
|
channelLock = new Mutex();
|
|
|
|
}
|
2020-09-23 09:37:18 +02:00
|
|
|
|
2020-10-05 15:59:08 +02:00
|
|
|
public DMeyer getMeyer()
|
|
|
|
{
|
|
|
|
return meyerSS;
|
|
|
|
}
|
|
|
|
|
2021-01-29 16:44:31 +02:00
|
|
|
public DMeyer getLinkManager()
|
2020-09-23 09:37:18 +02:00
|
|
|
{
|
2021-01-29 16:44:31 +02:00
|
|
|
return getMeyer();
|
|
|
|
}
|
2020-10-05 15:59:08 +02:00
|
|
|
|
2021-01-29 16:44:31 +02:00
|
|
|
public void attachLinkManager(DMeyer linkManager)
|
|
|
|
{
|
|
|
|
meyerSS = linkManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startServer()
|
|
|
|
{
|
2020-12-20 17:47:15 +02:00
|
|
|
/* Start the listener starter */
|
2020-09-23 09:37:18 +02:00
|
|
|
start();
|
2021-01-29 21:21:59 +02:00
|
|
|
|
|
|
|
/* Start outbound server linking */
|
|
|
|
startOutboundLinks();
|
|
|
|
gprintln("poes");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This will look at the link manager (DMeyer)
|
|
|
|
* and loop through each DLink in it and
|
|
|
|
* establish an outbound connection and therefore
|
|
|
|
* connection handler for each of them
|
|
|
|
*/
|
|
|
|
private void startOutboundLinks()
|
|
|
|
{
|
2021-01-30 13:41:38 +02:00
|
|
|
DLink[] serverLinks = meyerSS.getLinks();
|
2021-01-29 21:21:59 +02:00
|
|
|
|
|
|
|
/* Start all DLink outbound handlers (TODO: Move to DServer) */
|
|
|
|
foreach(DLink link; serverLinks)
|
|
|
|
{
|
|
|
|
link.start();
|
|
|
|
}
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
public void addChannel(DConnection causer, DChannel channel)
|
|
|
|
{
|
|
|
|
/* Lock the channels list */
|
2020-09-28 19:23:04 +02:00
|
|
|
// channelLock.lock();
|
2020-09-23 18:52:11 +02:00
|
|
|
|
|
|
|
channels ~= channel;
|
|
|
|
|
|
|
|
/* TODO: Use causer */
|
|
|
|
|
|
|
|
/* Unlock the channels list */
|
2020-09-28 19:23:04 +02:00
|
|
|
// channelLock.unlock();
|
2020-09-23 18:52:11 +02:00
|
|
|
}
|
|
|
|
|
2020-09-28 11:56:59 +02:00
|
|
|
public void addConnection(DConnection connection)
|
|
|
|
{
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* Add to the connection queue */
|
|
|
|
connectionQueue ~= connection;
|
2020-10-29 11:24:12 +02:00
|
|
|
gprintln("Added connection to queue "~to!(string)(connection));
|
2020-09-28 11:56:59 +02:00
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO Remove connection */
|
2020-09-28 12:50:40 +02:00
|
|
|
public void removeConnection(DConnection connection)
|
2020-09-28 11:56:59 +02:00
|
|
|
{
|
2020-09-28 12:50:40 +02:00
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* The new connection queue */
|
|
|
|
DConnection[] connectionQueueNew;
|
|
|
|
|
|
|
|
foreach(DConnection currentConnection; connectionQueue)
|
|
|
|
{
|
|
|
|
if(!(currentConnection is connection))
|
|
|
|
{
|
|
|
|
connectionQueueNew ~= currentConnection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set this as the new queue */
|
|
|
|
connectionQueue = connectionQueueNew;
|
|
|
|
|
2020-10-29 11:24:12 +02:00
|
|
|
gprintln("Removed connection from queue "~to!(string)(connection));
|
2020-09-28 12:50:40 +02:00
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
2020-09-28 11:56:59 +02:00
|
|
|
}
|
|
|
|
|
2020-09-28 19:23:04 +02:00
|
|
|
/* TODO: neew method */
|
|
|
|
public DChannel getChannel(DConnection causer, string channelName)
|
|
|
|
{
|
|
|
|
DChannel channel = null;
|
|
|
|
|
|
|
|
channelLock.lock();
|
|
|
|
|
|
|
|
|
|
|
|
foreach(DChannel currentChannel; channels)
|
|
|
|
{
|
|
|
|
if(cmp(currentChannel.getName(), channelName) == 0)
|
|
|
|
{
|
|
|
|
channel = currentChannel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(channel)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
channel = new DChannel(channelName);
|
|
|
|
|
|
|
|
this.addChannel(causer, channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
channelLock.unlock();
|
|
|
|
|
|
|
|
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
public DChannel getChannelByName(string channelName)
|
|
|
|
{
|
2020-09-25 10:21:58 +02:00
|
|
|
/* The channel */
|
|
|
|
DChannel channel = null;
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
/* Lock the channels list */
|
|
|
|
channelLock.lock();
|
|
|
|
|
|
|
|
foreach(DChannel currentChannel; channels)
|
|
|
|
{
|
|
|
|
if(cmp(currentChannel.getName(), channelName) == 0)
|
|
|
|
{
|
2020-09-25 10:21:58 +02:00
|
|
|
channel = currentChannel;
|
|
|
|
break;
|
2020-09-23 18:52:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the channels list */
|
|
|
|
channelLock.unlock();
|
|
|
|
|
2020-09-25 10:21:58 +02:00
|
|
|
return channel;
|
2020-09-23 18:52:11 +02:00
|
|
|
}
|
2020-09-24 12:32:00 +02:00
|
|
|
|
2020-09-27 18:31:34 +02:00
|
|
|
/**
|
|
|
|
* Returns the DConnection with the matching
|
|
|
|
* username, null if not found
|
|
|
|
*/
|
|
|
|
public DConnection findUser(string username)
|
|
|
|
{
|
2020-10-26 13:32:33 +02:00
|
|
|
/* The found Connection */
|
|
|
|
DConnection foundConnection;
|
|
|
|
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
2020-09-27 18:31:34 +02:00
|
|
|
|
|
|
|
/* Find the user with the matching user name */
|
2020-10-26 13:32:33 +02:00
|
|
|
foreach(DConnection connection; connectionQueue)
|
2020-09-27 18:31:34 +02:00
|
|
|
{
|
|
|
|
/* The connection must be a user (not unspec or server) */
|
|
|
|
if(connection.getConnectionType() == DConnection.ConnectionType.CLIENT)
|
|
|
|
{
|
|
|
|
/* Match the username */
|
|
|
|
if(cmp(connection.getUsername(), username) == 0)
|
|
|
|
{
|
2020-10-26 13:32:33 +02:00
|
|
|
foundConnection = connection;
|
2020-09-27 18:31:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
2020-10-26 13:32:33 +02:00
|
|
|
|
|
|
|
return foundConnection;
|
2020-09-27 18:31:34 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 14:29:46 +02:00
|
|
|
public bool channelExists(string channelName)
|
|
|
|
{
|
|
|
|
/* Whether or not it exists */
|
|
|
|
bool exists;
|
|
|
|
|
|
|
|
/* Get all channels */
|
|
|
|
DChannel[] currentChannels = getChannels();
|
|
|
|
|
|
|
|
foreach(DChannel currentChannel; currentChannels)
|
|
|
|
{
|
|
|
|
if(cmp(currentChannel.getName(), channelName) == 0)
|
|
|
|
{
|
|
|
|
exists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return exists;
|
|
|
|
}
|
|
|
|
|
2020-09-24 12:32:00 +02:00
|
|
|
public DChannel[] getChannels()
|
|
|
|
{
|
2020-09-26 15:05:52 +02:00
|
|
|
/* The current channels list */
|
|
|
|
DChannel[] currentChannels;
|
|
|
|
|
|
|
|
/* Lock the channels list */
|
|
|
|
channelLock.lock();
|
|
|
|
|
|
|
|
currentChannels = channels;
|
|
|
|
|
|
|
|
/* Unlock the channels list */
|
|
|
|
channelLock.unlock();
|
|
|
|
|
|
|
|
return currentChannels;
|
2020-09-24 12:32:00 +02:00
|
|
|
}
|
2020-10-02 16:11:27 +02:00
|
|
|
|
2020-10-20 07:57:27 +02:00
|
|
|
public string getStatusMessage(string username)
|
|
|
|
{
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* The matching connection */
|
|
|
|
DConnection matchedConnection;
|
|
|
|
|
|
|
|
/* Find the connection */
|
|
|
|
foreach(DConnection connection; connectionQueue)
|
|
|
|
{
|
|
|
|
if(cmp(connection.getUsername(), username) == 0)
|
|
|
|
{
|
|
|
|
matchedConnection = connection;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
|
|
|
|
|
|
|
return matchedConnection.getStatusMessage(username);
|
|
|
|
}
|
|
|
|
|
2020-10-20 09:54:42 +02:00
|
|
|
/**
|
|
|
|
* Checks whether the given user has the given
|
|
|
|
* property
|
|
|
|
*/
|
|
|
|
public bool isProperty(string username, string propertyName)
|
|
|
|
{
|
|
|
|
/* Whether or not the user has the given property */
|
|
|
|
bool status;
|
|
|
|
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* The matching connection */
|
|
|
|
DConnection matchedConnection;
|
|
|
|
|
|
|
|
/* Find the connection */
|
|
|
|
foreach(DConnection connection; connectionQueue)
|
|
|
|
{
|
|
|
|
if(cmp(connection.getUsername(), username) == 0)
|
|
|
|
{
|
|
|
|
matchedConnection = connection;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
|
|
|
|
|
|
|
/* Check for the user's property */
|
|
|
|
status = matchedConnection.isProperty(propertyName);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2020-10-26 13:32:33 +02:00
|
|
|
/* TODO: All these functions can really be re-duced, why am I not using getConnection() */
|
|
|
|
|
2020-10-20 09:54:42 +02:00
|
|
|
/**
|
|
|
|
* Checks whether the given user has the given
|
|
|
|
* property
|
|
|
|
*/
|
|
|
|
public string getProperty(string username, string propertyName)
|
|
|
|
{
|
|
|
|
/* The retrieved property value */
|
|
|
|
string propertyValue;
|
|
|
|
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* The matching connection */
|
|
|
|
DConnection matchedConnection;
|
|
|
|
|
|
|
|
/* Find the connection */
|
|
|
|
foreach(DConnection connection; connectionQueue)
|
|
|
|
{
|
|
|
|
if(cmp(connection.getUsername(), username) == 0)
|
|
|
|
{
|
|
|
|
matchedConnection = connection;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
|
|
|
|
|
|
|
/* Check for the user's property */
|
|
|
|
propertyValue = matchedConnection.getProperty(propertyName);
|
|
|
|
|
|
|
|
return propertyValue;
|
|
|
|
}
|
|
|
|
|
2020-10-20 10:45:17 +02:00
|
|
|
/**
|
|
|
|
* Set the property of the given user to the given value
|
|
|
|
*/
|
|
|
|
public void setProperty(string username, string propertyName, string propertyValue)
|
|
|
|
{
|
|
|
|
/* Lock the connections list */
|
|
|
|
connectionLock.lock();
|
|
|
|
|
|
|
|
/* The matching connection */
|
|
|
|
DConnection matchedConnection;
|
|
|
|
|
|
|
|
/* Find the connection */
|
|
|
|
foreach(DConnection connection; connectionQueue)
|
|
|
|
{
|
|
|
|
if(cmp(connection.getUsername(), username) == 0)
|
|
|
|
{
|
|
|
|
matchedConnection = connection;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the connections list */
|
|
|
|
connectionLock.unlock();
|
|
|
|
|
|
|
|
/* Set the property's value of the user */
|
|
|
|
matchedConnection.setProperty(propertyName, propertyValue);
|
|
|
|
}
|
2020-10-20 09:54:42 +02:00
|
|
|
|
|
|
|
|
2020-10-02 16:11:27 +02:00
|
|
|
public string getServerInfo()
|
|
|
|
{
|
|
|
|
/* The server information */
|
|
|
|
string serverInfo;
|
|
|
|
|
|
|
|
/* TODO: Fetch serverName */
|
|
|
|
/* TODO: Fetch networkName */
|
|
|
|
/* TODO: Fetch userCount */
|
|
|
|
/* TODO: Fetch channelCount */
|
|
|
|
|
|
|
|
|
|
|
|
return serverInfo;
|
|
|
|
}
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|