2020-09-23 09:37:18 +02:00
|
|
|
/**
|
|
|
|
* dconnection
|
|
|
|
*
|
|
|
|
* Client/server connection handler spawned
|
|
|
|
* by socket connection dequeue loop.
|
|
|
|
*
|
|
|
|
* Handles all interactions between
|
|
|
|
* the server and the specific client/server.
|
|
|
|
*/
|
|
|
|
|
|
|
|
module dnetd.dconnection;
|
|
|
|
|
|
|
|
import core.thread : Thread;
|
|
|
|
import std.socket : Socket;
|
2020-09-23 10:33:10 +02:00
|
|
|
import bmessage;
|
|
|
|
import tristanable.encoding : DataMessage;
|
2020-09-23 13:14:37 +02:00
|
|
|
import core.sync.mutex : Mutex;
|
2020-09-23 18:52:11 +02:00
|
|
|
import dnetd.dserver : DServer;
|
|
|
|
import std.string : split;
|
|
|
|
import dnetd.dchannel : DChannel;
|
2020-09-25 09:25:37 +02:00
|
|
|
import std.conv : to;
|
2020-09-25 19:30:45 +02:00
|
|
|
import std.stdio : writeln;
|
2020-09-29 14:29:46 +02:00
|
|
|
import std.algorithm : reverse;
|
2020-09-23 09:37:18 +02:00
|
|
|
|
|
|
|
public class DConnection : Thread
|
|
|
|
{
|
2020-09-24 13:54:01 +02:00
|
|
|
/* The connection type */
|
|
|
|
public enum ConnectionType
|
|
|
|
{
|
2020-09-26 12:24:04 +02:00
|
|
|
CLIENT, SERVER, UNSPEC
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Command types */
|
|
|
|
public enum Command
|
|
|
|
{
|
|
|
|
JOIN,
|
|
|
|
PART,
|
|
|
|
AUTH,
|
|
|
|
LINK,
|
|
|
|
REGISTER,
|
|
|
|
LIST,
|
|
|
|
MSG,
|
2020-09-29 14:29:46 +02:00
|
|
|
MEMBER_COUNT,
|
2020-09-29 23:28:58 +02:00
|
|
|
MEMBER_LIST,
|
2020-10-02 16:11:27 +02:00
|
|
|
SERVER_INFO,
|
|
|
|
MOTD,
|
2020-10-14 18:48:23 +02:00
|
|
|
MEMBER_INFO,
|
|
|
|
STATUS,
|
2020-10-15 11:26:57 +02:00
|
|
|
CHAN_PROP,
|
|
|
|
SET_PROP,
|
2020-09-26 12:24:04 +02:00
|
|
|
UNKNOWN
|
2020-09-24 13:54:01 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 10:33:10 +02:00
|
|
|
/**
|
|
|
|
* Connection information
|
|
|
|
*/
|
2020-09-23 18:52:11 +02:00
|
|
|
private DServer server;
|
2020-09-23 09:37:18 +02:00
|
|
|
private Socket socket;
|
2020-09-23 10:33:10 +02:00
|
|
|
private bool hasAuthed;
|
2020-09-24 13:54:01 +02:00
|
|
|
private ConnectionType connType;
|
|
|
|
private string username;
|
2020-10-14 23:19:40 +02:00
|
|
|
private string currentStatus;
|
2020-10-20 08:17:48 +02:00
|
|
|
private string[string] properties; /* TODO: New, replace old status mechanism */
|
2020-09-23 10:33:10 +02:00
|
|
|
|
2020-09-23 13:14:37 +02:00
|
|
|
/* Write lock for socket */
|
|
|
|
/* TODO: Forgot how bmessage works, might need, might not, if multipel calls
|
|
|
|
* then yes, if single then no as it is based off (well glibc's write)
|
|
|
|
* thread safe code
|
|
|
|
*/
|
|
|
|
private Mutex writeLock;
|
2020-10-14 23:19:40 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Mutex to provide safe access to the status message
|
|
|
|
*/
|
|
|
|
private Mutex statusMessageLock;
|
2020-09-23 13:14:37 +02:00
|
|
|
|
2020-09-23 10:33:10 +02:00
|
|
|
/* Reserved tag for push notifications */
|
|
|
|
private long notificationTag = 0;
|
2020-09-23 09:37:18 +02:00
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
this(DServer server, Socket socket)
|
2020-09-23 09:37:18 +02:00
|
|
|
{
|
|
|
|
/* Set the function to be called on thread start */
|
|
|
|
super(&worker);
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
/* Set the associated server */
|
|
|
|
this.server = server;
|
|
|
|
|
2020-09-23 09:37:18 +02:00
|
|
|
/* Set the socket */
|
|
|
|
this.socket = socket;
|
|
|
|
|
2020-09-26 12:24:04 +02:00
|
|
|
/* Set the default state */
|
|
|
|
connType = ConnectionType.UNSPEC;
|
|
|
|
|
2020-09-23 13:14:37 +02:00
|
|
|
/* Initialize locks */
|
|
|
|
initLocks();
|
2020-09-23 10:33:10 +02:00
|
|
|
|
2020-10-20 07:57:21 +02:00
|
|
|
/* Initialize status */
|
|
|
|
currentStatus = "available,Hey there I'm using DNET!";
|
|
|
|
|
2020-09-23 09:37:18 +02:00
|
|
|
/* Start the connection handler */
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
2020-09-23 10:33:10 +02:00
|
|
|
/**
|
2020-09-23 13:14:37 +02:00
|
|
|
* Initializes mutexes
|
2020-09-23 10:33:10 +02:00
|
|
|
*/
|
2020-09-23 13:14:37 +02:00
|
|
|
private void initLocks()
|
2020-09-23 10:33:10 +02:00
|
|
|
{
|
2020-10-14 23:19:40 +02:00
|
|
|
/* Initialize the socket write lock */
|
2020-09-23 13:14:37 +02:00
|
|
|
writeLock = new Mutex();
|
2020-10-14 23:19:40 +02:00
|
|
|
|
|
|
|
/* Initialize the status message lock */
|
|
|
|
statusMessageLock = new Mutex();
|
2020-09-23 10:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 09:37:18 +02:00
|
|
|
/**
|
|
|
|
* Byte dequeue loop
|
|
|
|
*/
|
|
|
|
private void worker()
|
|
|
|
{
|
2020-09-23 10:33:10 +02:00
|
|
|
/* Received bytes (for bformat) */
|
|
|
|
byte[] receivedBytes;
|
|
|
|
|
|
|
|
/* Received message */
|
|
|
|
DataMessage receivedMessage;
|
|
|
|
|
2020-09-23 09:37:18 +02:00
|
|
|
while(true)
|
2020-09-23 10:33:10 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Block to receive a bformat message
|
|
|
|
*
|
|
|
|
* (Does decoding for bformat too)
|
|
|
|
*/
|
|
|
|
bool status = receiveMessage(socket, receivedBytes);
|
|
|
|
|
|
|
|
/* TODO: Check status */
|
2020-09-24 19:11:20 +02:00
|
|
|
if(status)
|
|
|
|
{
|
|
|
|
/* Decode the tristanable message (tagged message) */
|
|
|
|
receivedMessage = DataMessage.decode(receivedBytes);
|
2020-09-23 10:33:10 +02:00
|
|
|
|
2020-09-24 19:11:20 +02:00
|
|
|
/* Process the message */
|
2020-09-28 10:12:13 +02:00
|
|
|
process(receivedMessage);
|
2020-09-23 13:14:37 +02:00
|
|
|
|
2020-09-24 19:11:20 +02:00
|
|
|
/* TODO: Tristanable needs reserved-tag support (client-side concern) */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* TODO: Error handling */
|
2020-09-28 11:42:14 +02:00
|
|
|
writeln("Error with receive: "~to!(string)(this));
|
|
|
|
break;
|
2020-09-24 19:11:20 +02:00
|
|
|
}
|
2020-09-23 10:33:10 +02:00
|
|
|
}
|
2020-09-28 11:42:14 +02:00
|
|
|
|
|
|
|
/* Clean up */
|
|
|
|
cleanUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void cleanUp()
|
|
|
|
{
|
|
|
|
writeln(to!(string)(this)~" Cleaning up connection...");
|
|
|
|
|
|
|
|
/* Remove this user from all channels he is in */
|
|
|
|
DChannel[] channels = server.getChannels();
|
|
|
|
|
|
|
|
/* Loop through each channel */
|
|
|
|
foreach(DChannel currentChannel; channels)
|
|
|
|
{
|
|
|
|
/* Check if you are a member of it */
|
|
|
|
if(currentChannel.isMember(this))
|
|
|
|
{
|
|
|
|
/* Leave the channel */
|
|
|
|
currentChannel.leave(this);
|
|
|
|
writeln(to!(string)(this)~" Leaving '"~currentChannel.getName()~"'...");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove this user from the connection queue */
|
2020-09-28 12:50:40 +02:00
|
|
|
server.removeConnection(this);
|
2020-09-28 13:47:27 +02:00
|
|
|
|
|
|
|
writeln(to!(string)(this)~" Connection cleaned up");
|
2020-09-23 10:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 13:14:37 +02:00
|
|
|
/* TODO: add mutex for writing with message and funciton for doing so */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write to socket
|
2020-09-23 18:52:11 +02:00
|
|
|
*
|
|
|
|
* Encodes the byte array as a tristanable tagged
|
|
|
|
* message and then encodes that as a bformat
|
|
|
|
* message
|
|
|
|
*
|
|
|
|
* Locks the writeLock mutex, sends it over the
|
|
|
|
* socket to the client/server, and unlocks the
|
|
|
|
* mutex
|
2020-09-23 13:14:37 +02:00
|
|
|
*/
|
2020-09-24 13:28:09 +02:00
|
|
|
public bool writeSocket(long tag, byte[] data)
|
2020-09-23 13:14:37 +02:00
|
|
|
{
|
2020-09-23 18:52:11 +02:00
|
|
|
/* Send status */
|
|
|
|
bool status;
|
2020-09-23 13:14:37 +02:00
|
|
|
|
|
|
|
/* Create the tagged message */
|
|
|
|
DataMessage message = new DataMessage(tag, data);
|
|
|
|
|
2020-09-28 19:23:04 +02:00
|
|
|
writeln("writeSocket: mutex lock");
|
2020-09-23 13:14:37 +02:00
|
|
|
/* Lock the write mutex */
|
|
|
|
writeLock.lock();
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
/* Send the message */
|
2020-09-28 19:23:04 +02:00
|
|
|
writeln("writeSocket: Data: "~to!(string)(data)~" Tag: "~to!(string)(tag));
|
2020-09-28 13:47:27 +02:00
|
|
|
status = sendMessage(socket, message.encode());
|
2020-09-23 13:14:37 +02:00
|
|
|
|
|
|
|
/* Unlock the write mutex */
|
|
|
|
writeLock.unlock();
|
2020-09-23 18:52:11 +02:00
|
|
|
|
2020-09-28 19:23:04 +02:00
|
|
|
writeln("writeSocket: mutex unlock");
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
return status;
|
2020-09-23 13:14:37 +02:00
|
|
|
}
|
2020-09-25 19:30:45 +02:00
|
|
|
|
2020-10-05 18:01:47 +02:00
|
|
|
private Command getCommand(ubyte commandByte)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
Command command = Command.UNKNOWN;
|
|
|
|
|
2020-10-05 18:01:47 +02:00
|
|
|
if(commandByte == 0)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.AUTH;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 1)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.LINK;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 2)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.REGISTER;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 3)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.JOIN;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 4)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.PART;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 5)
|
2020-09-27 18:31:34 +02:00
|
|
|
{
|
|
|
|
command = Command.MSG;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 6)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.LIST;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 7)
|
2020-09-25 19:30:45 +02:00
|
|
|
{
|
|
|
|
command = Command.MSG;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 8)
|
2020-09-29 14:29:46 +02:00
|
|
|
{
|
|
|
|
command = Command.MEMBER_COUNT;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 9)
|
2020-09-30 10:15:32 +02:00
|
|
|
{
|
|
|
|
command = Command.MEMBER_LIST;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 10)
|
2020-10-02 16:11:27 +02:00
|
|
|
{
|
|
|
|
command = Command.SERVER_INFO;
|
|
|
|
}
|
2020-10-05 18:01:47 +02:00
|
|
|
else if(commandByte == 11)
|
2020-10-02 16:11:27 +02:00
|
|
|
{
|
|
|
|
command = Command.MOTD;
|
|
|
|
}
|
2020-10-14 23:19:40 +02:00
|
|
|
else if(commandByte == 12)
|
|
|
|
{
|
|
|
|
command = Command.MEMBER_INFO;
|
|
|
|
}
|
|
|
|
else if(commandByte == 13)
|
|
|
|
{
|
|
|
|
command = Command.STATUS;
|
|
|
|
}
|
2020-10-15 11:27:25 +02:00
|
|
|
else if(commandByte == 14)
|
|
|
|
{
|
|
|
|
command = Command.CHAN_PROP;
|
|
|
|
}
|
|
|
|
else if(commandByte == 15)
|
|
|
|
{
|
|
|
|
command = Command.SET_PROP;
|
|
|
|
}
|
|
|
|
|
2020-10-02 16:11:27 +02:00
|
|
|
|
2020-09-25 19:30:45 +02:00
|
|
|
return command;
|
|
|
|
}
|
2020-09-23 13:14:37 +02:00
|
|
|
|
2020-09-23 10:33:10 +02:00
|
|
|
/**
|
|
|
|
* Process the received message
|
|
|
|
*/
|
|
|
|
private void process(DataMessage message)
|
|
|
|
{
|
2020-09-23 11:20:09 +02:00
|
|
|
/* Get the tag */
|
2020-09-24 13:54:01 +02:00
|
|
|
/**
|
|
|
|
* TODO: Client side will always do 1, because we don't have
|
|
|
|
* multi-thread job processing, only need this to differentiate
|
|
|
|
* between commands and async notifications
|
|
|
|
*/
|
2020-09-23 11:20:09 +02:00
|
|
|
long tag = message.tag;
|
2020-09-28 19:23:04 +02:00
|
|
|
writeln("tag:", tag);
|
2020-09-23 11:20:09 +02:00
|
|
|
|
2020-09-26 12:24:04 +02:00
|
|
|
/* The reply */
|
|
|
|
byte[] reply;
|
2020-09-25 19:30:45 +02:00
|
|
|
|
2020-09-26 12:24:04 +02:00
|
|
|
/* Get the command */
|
|
|
|
byte commandByte = message.data[0];
|
2020-09-25 19:30:45 +02:00
|
|
|
Command command = getCommand(commandByte);
|
|
|
|
writeln(to!(string)(this)~" ~> "~to!(string)(command));
|
|
|
|
|
2020-09-23 10:52:33 +02:00
|
|
|
/* If `auth` command (requires: unauthed) */
|
2020-09-25 19:30:45 +02:00
|
|
|
if(command == Command.AUTH && !hasAuthed)
|
2020-09-23 10:33:10 +02:00
|
|
|
{
|
2020-09-23 10:52:33 +02:00
|
|
|
/* Get the length of the username */
|
2020-10-05 16:41:40 +02:00
|
|
|
ubyte usernameLength = message.data[1];
|
2020-09-23 10:52:33 +02:00
|
|
|
|
|
|
|
/* Get the username and password */
|
2020-10-05 17:59:40 +02:00
|
|
|
string username = cast(string)message.data[2..2+cast(uint)usernameLength];
|
|
|
|
string password = cast(string)message.data[2+cast(uint)usernameLength..message.data.length];
|
2020-09-23 13:14:37 +02:00
|
|
|
|
|
|
|
/* Authenticate */
|
|
|
|
bool status = authenticate(username, password);
|
|
|
|
|
2020-09-24 13:54:01 +02:00
|
|
|
/* TODO: What to do on bad authetication? */
|
|
|
|
|
|
|
|
/* Set the username */
|
|
|
|
this.username = username;
|
|
|
|
|
|
|
|
/* Set the type of this connection to `client` */
|
|
|
|
connType = ConnectionType.CLIENT;
|
2020-09-25 01:08:28 +02:00
|
|
|
hasAuthed = true;
|
2020-09-24 13:54:01 +02:00
|
|
|
|
2020-09-23 13:14:37 +02:00
|
|
|
/* Encode the reply */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [status];
|
2020-09-23 10:33:10 +02:00
|
|
|
}
|
2020-09-23 10:52:33 +02:00
|
|
|
/* If `link` command (requires: unauthed) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.LINK && !hasAuthed)
|
2020-09-23 10:33:10 +02:00
|
|
|
{
|
2020-09-23 21:53:25 +02:00
|
|
|
/* TODO: Implement me later */
|
2020-09-24 13:54:01 +02:00
|
|
|
|
2020-10-05 15:59:08 +02:00
|
|
|
/* Check if this connection is a DLink'd one */
|
|
|
|
//server.getMeyer().get
|
|
|
|
|
2020-09-24 13:54:01 +02:00
|
|
|
|
|
|
|
/* Set the type of this connection to `server` */
|
|
|
|
connType = ConnectionType.SERVER;
|
2020-09-26 12:24:04 +02:00
|
|
|
hasAuthed = true;
|
2020-09-23 18:52:11 +02:00
|
|
|
}
|
2020-09-24 19:11:20 +02:00
|
|
|
/* If `register` command (requires: unauthed, client) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.REGISTER && !hasAuthed && connType == ConnectionType.CLIENT)
|
2020-09-23 18:52:11 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2020-09-24 14:36:32 +02:00
|
|
|
/* If `join` command (requires: authed, client) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.JOIN && hasAuthed && connType == ConnectionType.CLIENT)
|
2020-09-23 18:52:11 +02:00
|
|
|
{
|
|
|
|
/* Get the channel names */
|
|
|
|
string channelList = cast(string)message.data[1..message.data.length];
|
|
|
|
string[] channels = split(channelList, ",");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loop through each channel, check if it
|
|
|
|
* exists, if so join it, else create it
|
|
|
|
* and then join it
|
|
|
|
*/
|
2020-09-25 19:30:45 +02:00
|
|
|
bool isPresentInfo = false;
|
2020-09-23 18:52:11 +02:00
|
|
|
foreach(string channelName; channels)
|
|
|
|
{
|
2020-09-28 19:46:03 +02:00
|
|
|
/**
|
|
|
|
* Finds the channel, if it exists then it returns it,
|
|
|
|
* if it does not exist then it will create it and then
|
|
|
|
* return it
|
|
|
|
*/
|
2020-09-28 19:23:04 +02:00
|
|
|
DChannel channel = server.getChannel(this, channelName);
|
|
|
|
|
2020-09-23 18:52:11 +02:00
|
|
|
/* Join the channel */
|
2020-09-25 19:30:45 +02:00
|
|
|
isPresentInfo = channel.join(this);
|
2020-09-23 18:52:11 +02:00
|
|
|
}
|
2020-09-23 21:40:22 +02:00
|
|
|
|
|
|
|
/* TODO: Do reply */
|
|
|
|
/* Encode the reply */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [isPresentInfo];
|
2020-09-23 10:33:10 +02:00
|
|
|
}
|
2020-09-24 14:36:32 +02:00
|
|
|
/* If `part` command (requires: authed, client) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.PART && hasAuthed && connType == ConnectionType.CLIENT)
|
2020-09-23 23:57:45 +02:00
|
|
|
{
|
|
|
|
/* Get the channel names */
|
|
|
|
string channelList = cast(string)message.data[1..message.data.length];
|
|
|
|
string[] channels = split(channelList, ",");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loop through each channel, check if it
|
|
|
|
* exists, if so leave it
|
|
|
|
*/
|
|
|
|
foreach(string channelName; channels)
|
|
|
|
{
|
|
|
|
/* Attempt to find the channel */
|
|
|
|
DChannel channel = server.getChannelByName(channelName);
|
|
|
|
|
|
|
|
/* Leave a channel the channel only if it exists */
|
|
|
|
if(!(channel is null))
|
|
|
|
{
|
|
|
|
channel.leave(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Do reply */
|
|
|
|
/* Encode the reply */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [true];
|
2020-09-23 23:57:45 +02:00
|
|
|
}
|
2020-09-24 14:36:32 +02:00
|
|
|
/* If `list` command (requires: authed, client) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.LIST && hasAuthed && connType == ConnectionType.CLIENT)
|
2020-09-24 12:32:00 +02:00
|
|
|
{
|
|
|
|
/* Get all channels */
|
|
|
|
DChannel[] channels = server.getChannels();
|
|
|
|
|
|
|
|
/* Generate a list of channel names (CSV) */
|
|
|
|
string channelList;
|
|
|
|
for(ulong i = 0; i < channels.length; i++)
|
|
|
|
{
|
|
|
|
if(i == channels.length-1)
|
|
|
|
{
|
|
|
|
channelList ~= channels[i].getName();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
channelList ~= channels[i].getName()~",";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Reply */
|
|
|
|
/* Encode the reply */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [true];
|
2020-09-24 12:32:00 +02:00
|
|
|
reply ~= channelList;
|
|
|
|
}
|
2020-09-24 19:11:20 +02:00
|
|
|
/* If `msg` command (requires: authed, client) */
|
2020-09-25 19:30:45 +02:00
|
|
|
else if(command == Command.MSG && hasAuthed && connType == ConnectionType.CLIENT)
|
2020-09-24 13:28:09 +02:00
|
|
|
{
|
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
/* Get the type of message */
|
2020-10-05 16:41:40 +02:00
|
|
|
ubyte messageType = message.data[1];
|
2020-09-28 10:12:13 +02:00
|
|
|
|
|
|
|
/* Get the location length */
|
2020-10-05 16:41:40 +02:00
|
|
|
ubyte locationLength = message.data[2];
|
2020-09-24 13:28:09 +02:00
|
|
|
|
|
|
|
/* Get the channel/person name */
|
2020-10-05 17:59:40 +02:00
|
|
|
string destination = cast(string)message.data[3..3+cast(uint)locationLength];
|
2020-09-24 13:28:09 +02:00
|
|
|
|
2020-09-28 10:12:13 +02:00
|
|
|
/* Get the message */
|
2020-10-05 17:59:40 +02:00
|
|
|
string msg = cast(string)message.data[3+cast(uint)locationLength..message.data.length];
|
2020-09-24 13:28:09 +02:00
|
|
|
|
2020-09-27 19:39:48 +02:00
|
|
|
/* Send status */
|
|
|
|
bool sendStatus;
|
|
|
|
|
2020-09-24 13:28:09 +02:00
|
|
|
/* If we are sending to a user */
|
2020-10-05 19:38:01 +02:00
|
|
|
if(messageType == 0)
|
2020-09-24 13:28:09 +02:00
|
|
|
{
|
2020-09-27 18:31:34 +02:00
|
|
|
/* Send the message to the user */
|
2020-09-27 19:39:48 +02:00
|
|
|
sendStatus = sendUserMessage(destination, msg);
|
2020-09-24 13:28:09 +02:00
|
|
|
}
|
|
|
|
/* If we are sending to a channel */
|
2020-10-05 19:38:01 +02:00
|
|
|
else if(messageType == 1)
|
2020-09-24 13:28:09 +02:00
|
|
|
{
|
2020-09-24 15:01:23 +02:00
|
|
|
/* The channel wanting to send to */
|
|
|
|
DChannel channel = server.getChannelByName(destination);
|
|
|
|
|
2020-09-24 13:28:09 +02:00
|
|
|
/* If the channel exists */
|
2020-09-24 15:01:23 +02:00
|
|
|
if(channel)
|
2020-09-24 13:28:09 +02:00
|
|
|
{
|
|
|
|
/* TODO Implemet me */
|
2020-09-27 19:50:39 +02:00
|
|
|
sendStatus = channel.sendMessage(this, msg);
|
2020-09-24 13:28:09 +02:00
|
|
|
}
|
|
|
|
/* If the channel does not exist */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Unknown destination type */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Handling here, should we make the user wait? */
|
|
|
|
|
|
|
|
/* Encode the reply */
|
2020-09-25 19:41:30 +02:00
|
|
|
/* TODO: */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [status];
|
2020-09-24 13:28:09 +02:00
|
|
|
}
|
2020-09-29 14:29:46 +02:00
|
|
|
/* If `membercount` command (requires: authed, client) */
|
|
|
|
else if(command == Command.MEMBER_COUNT && hasAuthed && connType == ConnectionType.CLIENT)
|
|
|
|
{
|
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
/* Get the channel name */
|
|
|
|
string channelName = cast(string)message.data[1..message.data.length];
|
|
|
|
|
|
|
|
/* The memebr count */
|
|
|
|
long memberCount;
|
|
|
|
|
|
|
|
/* Get the member count */
|
|
|
|
status = getMemberCount(channelName, memberCount);
|
|
|
|
|
|
|
|
/* Encode the status */
|
|
|
|
reply = [status];
|
|
|
|
|
|
|
|
/* If there was no error fetching the member count */
|
|
|
|
if(status)
|
|
|
|
{
|
|
|
|
/* Data bytes */
|
|
|
|
byte[] numberBytes;
|
|
|
|
numberBytes.length = 8;
|
|
|
|
|
|
|
|
/* Encode the length (Big Endian) from Little Endian */
|
|
|
|
numberBytes[0] = *((cast(byte*)&memberCount)+7);
|
|
|
|
numberBytes[1] = *((cast(byte*)&memberCount)+6);
|
|
|
|
numberBytes[2] = *((cast(byte*)&memberCount)+5);
|
|
|
|
numberBytes[3] = *((cast(byte*)&memberCount)+4);
|
|
|
|
numberBytes[4] = *((cast(byte*)&memberCount)+3);
|
|
|
|
numberBytes[5] = *((cast(byte*)&memberCount)+2);
|
|
|
|
numberBytes[6] = *((cast(byte*)&memberCount)+1);
|
|
|
|
numberBytes[7] = *((cast(byte*)&memberCount)+0);
|
|
|
|
|
|
|
|
/* Append the length */
|
|
|
|
reply ~= numberBytes;
|
|
|
|
}
|
2020-09-29 23:28:58 +02:00
|
|
|
}
|
|
|
|
/* If `memberlist` command (requires: authed, client) */
|
|
|
|
else if(command == Command.MEMBER_LIST && hasAuthed && connType == ConnectionType.CLIENT)
|
|
|
|
{
|
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
/* Get the channel name */
|
|
|
|
string channelName = cast(string)message.data[1..message.data.length];
|
|
|
|
|
|
|
|
/* Get the channel */
|
|
|
|
DChannel channel = server.getChannelByName(channelName);
|
|
|
|
|
|
|
|
/* Encode the status */
|
|
|
|
reply ~= [channel !is null];
|
|
|
|
|
|
|
|
/* If the channel exists */
|
|
|
|
if(channel)
|
|
|
|
{
|
|
|
|
/* Get the list of members in the channel */
|
|
|
|
DConnection[] members = channel.getMembers();
|
|
|
|
|
|
|
|
/* Construct a CSV string of the members */
|
|
|
|
string memberString;
|
|
|
|
|
|
|
|
for(ulong i = 0; i < members.length; i++)
|
|
|
|
{
|
|
|
|
if(i == members.length-1)
|
|
|
|
{
|
|
|
|
memberString ~= members[i].getUsername();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memberString ~= members[i].getUsername()~",";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Encode the string into the reply */
|
|
|
|
reply ~= cast(byte[])memberString;
|
|
|
|
}
|
|
|
|
/* If the channel does not exist */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-10-02 16:11:27 +02:00
|
|
|
}
|
|
|
|
/* If `serverinfo` command (requires: authed, !unspec) */
|
|
|
|
else if(command == Command.SERVER_INFO && hasAuthed && connType != ConnectionType.UNSPEC)
|
|
|
|
{
|
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
/* Get the server info */
|
|
|
|
string serverInfo = server.getServerInfo();
|
|
|
|
|
|
|
|
/* Encode the reply */
|
|
|
|
reply ~= [status];
|
|
|
|
reply ~= serverInfo;
|
|
|
|
}
|
|
|
|
/* If `motd` command (requires: _nothing_) */
|
|
|
|
else if(command == Command.MOTD)
|
|
|
|
{
|
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
/* Get the message of the day */
|
|
|
|
string motd = server.getConfig().getGeneral().getMotd();
|
|
|
|
|
|
|
|
/* Encode the reply */
|
|
|
|
reply ~= [status];
|
|
|
|
reply ~= motd;
|
2020-09-29 14:29:46 +02:00
|
|
|
}
|
2020-10-14 23:19:40 +02:00
|
|
|
/* If `memberinfo` command (requires: authed, client) */
|
|
|
|
else if(command == Command.MEMBER_INFO && hasAuthed && connType == ConnectionType.CLIENT)
|
|
|
|
{
|
2020-10-15 14:07:01 +02:00
|
|
|
/* Status */
|
|
|
|
bool status = true;
|
|
|
|
|
2020-10-14 23:19:40 +02:00
|
|
|
/* TODO: Implement me */
|
2020-10-20 07:57:21 +02:00
|
|
|
string user = cast(string)message.data[1..message.data.length];
|
2020-10-15 14:07:01 +02:00
|
|
|
|
|
|
|
/* TODO: fetch longontime, serveron, status */
|
|
|
|
string logontime;
|
|
|
|
string serveron;
|
|
|
|
|
|
|
|
/* Encode the reply */
|
|
|
|
reply ~= [status];
|
|
|
|
reply ~= [cast(byte)logontime.length];
|
|
|
|
reply ~= logontime;
|
|
|
|
reply ~= [cast(byte)serveron.length];
|
|
|
|
reply ~= serveron;
|
2020-10-20 07:57:21 +02:00
|
|
|
reply ~= server.getStatusMessage(user);
|
2020-10-14 23:19:40 +02:00
|
|
|
}
|
|
|
|
/* If `status` command (requires: authed, client) */
|
|
|
|
else if(command == Command.STATUS && hasAuthed && connType == ConnectionType.CLIENT)
|
|
|
|
{
|
|
|
|
/* Get the new status line */
|
|
|
|
string statusMessage = cast(string)message.data[1..message.data.length];
|
|
|
|
|
|
|
|
/* Set the new status message */
|
|
|
|
setStatusMessage(statusMessage);
|
|
|
|
|
|
|
|
/* Encode the reply */
|
|
|
|
reply ~= [true];
|
|
|
|
}
|
2020-10-15 11:28:12 +02:00
|
|
|
/* TODO: `CHAN_PROP`, `SET_PROP` */
|
2020-09-25 19:33:37 +02:00
|
|
|
/* If no matching built-in command was found */
|
2020-09-23 10:33:10 +02:00
|
|
|
else
|
2020-09-23 09:37:18 +02:00
|
|
|
{
|
2020-09-23 11:20:09 +02:00
|
|
|
/* TODO: Check plugins */
|
|
|
|
bool isPlugin = false;
|
|
|
|
|
2020-09-25 19:33:37 +02:00
|
|
|
/* A matching plugin was found */
|
2020-09-23 11:20:09 +02:00
|
|
|
if(isPlugin)
|
|
|
|
{
|
2020-09-25 19:33:37 +02:00
|
|
|
/* TODO: Implement me */
|
2020-09-23 11:20:09 +02:00
|
|
|
}
|
2020-09-25 19:33:37 +02:00
|
|
|
/* The command was invalid */
|
2020-09-23 11:20:09 +02:00
|
|
|
else
|
|
|
|
{
|
2020-09-25 19:33:37 +02:00
|
|
|
/* Write error message */
|
2020-09-26 12:24:04 +02:00
|
|
|
reply = [2];
|
2020-09-23 11:20:09 +02:00
|
|
|
}
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|
2020-09-26 12:24:04 +02:00
|
|
|
|
|
|
|
/* Write the response */
|
|
|
|
writeSocket(tag, reply);
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|
2020-09-23 10:33:10 +02:00
|
|
|
|
|
|
|
/**
|
2020-09-23 13:14:37 +02:00
|
|
|
* Authenticate
|
2020-09-23 10:33:10 +02:00
|
|
|
*
|
2020-09-23 13:14:37 +02:00
|
|
|
* Login as a user with the given credentials
|
2020-09-23 10:33:10 +02:00
|
|
|
*/
|
2020-09-23 13:14:37 +02:00
|
|
|
private bool authenticate(string username, string password)
|
|
|
|
{
|
|
|
|
/* TODO: Implement me */
|
|
|
|
return true;
|
|
|
|
}
|
2020-09-24 13:54:01 +02:00
|
|
|
|
2020-09-29 14:29:46 +02:00
|
|
|
/**
|
|
|
|
* Get member count
|
|
|
|
*
|
|
|
|
* Gets the member count of a given channel
|
|
|
|
*/
|
|
|
|
private bool getMemberCount(string channelName, ref long count)
|
|
|
|
{
|
|
|
|
/* Status of operation */
|
|
|
|
bool status;
|
|
|
|
|
|
|
|
/* The channel */
|
|
|
|
DChannel channel = server.getChannelByName(channelName);
|
|
|
|
|
|
|
|
/* Check if the channel exists */
|
|
|
|
if(channel)
|
|
|
|
{
|
|
|
|
/* Get the channel count */
|
|
|
|
count = channel.getMemberCount();
|
|
|
|
|
|
|
|
|
|
|
|
status = true;
|
|
|
|
}
|
|
|
|
/* If the channel does not exist */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2020-09-27 18:31:34 +02:00
|
|
|
/**
|
|
|
|
* Send user a message
|
|
|
|
*
|
|
|
|
* Sends the provided user the specified message
|
|
|
|
*/
|
|
|
|
private bool sendUserMessage(string username, string message)
|
|
|
|
{
|
|
|
|
/* Find the user to send to */
|
|
|
|
DConnection user = server.findUser(username);
|
|
|
|
|
2020-09-28 22:00:31 +02:00
|
|
|
writeln("sendUserMessage(): ", user);
|
|
|
|
|
2020-09-27 18:31:34 +02:00
|
|
|
/* If the user was found */
|
|
|
|
if(user)
|
|
|
|
{
|
2020-09-28 22:00:31 +02:00
|
|
|
/* The protocol data to send */
|
|
|
|
byte[] protocolData;
|
|
|
|
|
|
|
|
/* Set the sub-type (ntype=0) */
|
|
|
|
protocolData ~= [0];
|
|
|
|
|
|
|
|
/* Encode the sender's length */
|
|
|
|
protocolData ~= [cast(byte)username.length];
|
|
|
|
|
|
|
|
/* Encode the username */
|
|
|
|
protocolData ~= cast(byte[])username;
|
|
|
|
|
|
|
|
/* Encode the message */
|
|
|
|
protocolData ~= cast(byte[])message;
|
|
|
|
|
2020-09-27 18:31:34 +02:00
|
|
|
/* Send the messge */
|
2020-09-29 14:29:46 +02:00
|
|
|
bool sendStatus = user.writeSocket(0, protocolData);
|
|
|
|
|
|
|
|
return sendStatus;
|
2020-09-27 18:31:34 +02:00
|
|
|
}
|
|
|
|
/* If the user was not found */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-24 13:54:01 +02:00
|
|
|
public string getUsername()
|
|
|
|
{
|
|
|
|
return username;
|
|
|
|
}
|
2020-09-25 09:25:37 +02:00
|
|
|
|
2020-10-14 23:19:40 +02:00
|
|
|
/**
|
|
|
|
* Set the status message
|
|
|
|
*/
|
|
|
|
private void setStatusMessage(string statusMessage)
|
|
|
|
{
|
|
|
|
/* Lock the status message mutex */
|
|
|
|
statusMessageLock.lock();
|
|
|
|
|
|
|
|
/* Set the status message */
|
|
|
|
currentStatus = statusMessage;
|
|
|
|
|
|
|
|
/* Unlock the statue message mutex */
|
|
|
|
statusMessageLock.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current status message
|
|
|
|
*/
|
2020-10-20 07:57:21 +02:00
|
|
|
public string getStatusMessage(string username)
|
2020-10-14 23:19:40 +02:00
|
|
|
{
|
2020-10-20 07:57:21 +02:00
|
|
|
/* The status message */
|
|
|
|
string statusMessage;
|
|
|
|
|
|
|
|
writeln("hfsjkhfjsdkhfdskj");
|
2020-10-14 23:19:40 +02:00
|
|
|
|
|
|
|
/* Lock the status message mutex */
|
|
|
|
statusMessageLock.lock();
|
|
|
|
|
2020-10-20 07:57:21 +02:00
|
|
|
/* Get the status message */
|
|
|
|
statusMessage = currentStatus;
|
2020-10-14 23:19:40 +02:00
|
|
|
|
|
|
|
/* Unlock the statue message mutex */
|
|
|
|
statusMessageLock.unlock();
|
|
|
|
|
2020-10-20 07:57:21 +02:00
|
|
|
return statusMessage;
|
2020-10-14 23:19:40 +02:00
|
|
|
}
|
|
|
|
|
2020-10-20 08:17:48 +02:00
|
|
|
/**
|
|
|
|
* Sets a property for this user
|
|
|
|
*/
|
|
|
|
public void setProperty(string propertyName, string propertyValue)
|
|
|
|
{
|
|
|
|
properties[propertyName] = propertyValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a property
|
|
|
|
*/
|
|
|
|
public string getProperty(string propertyName)
|
|
|
|
{
|
|
|
|
/* TODO: Error handling */
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2020-10-20 08:22:13 +02:00
|
|
|
/**
|
|
|
|
* Returns a list of proerties
|
|
|
|
*/
|
|
|
|
public string[] getProperties()
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes a given proerty
|
|
|
|
*/
|
|
|
|
public void deleteProperty(string propertyName)
|
|
|
|
{
|
|
|
|
/* TODO: Implement me */
|
|
|
|
}
|
|
|
|
|
2020-09-27 18:31:34 +02:00
|
|
|
public ConnectionType getConnectionType()
|
|
|
|
{
|
|
|
|
return connType;
|
|
|
|
}
|
|
|
|
|
2020-09-25 09:25:37 +02:00
|
|
|
public override string toString()
|
|
|
|
{
|
2020-09-28 11:42:14 +02:00
|
|
|
string toStr = "["~to!(string)(connType)~" (";
|
|
|
|
toStr ~= socket.remoteAddress.toString();
|
|
|
|
|
|
|
|
|
|
|
|
toStr ~= ")]: ";
|
|
|
|
|
2020-09-25 09:25:37 +02:00
|
|
|
|
|
|
|
if(connType == ConnectionType.CLIENT)
|
|
|
|
{
|
|
|
|
toStr = toStr ~ getUsername();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* TODO Implement me */
|
|
|
|
}
|
|
|
|
|
|
|
|
return toStr;
|
|
|
|
}
|
2020-09-23 09:37:18 +02:00
|
|
|
}
|