Updated Makefile, restructured code

This commit is contained in:
2022-05-24 10:39:49 +01:00
parent 658aebf932
commit 398aa7d46f
17 changed files with 14 additions and 37 deletions
+28
View File
@@ -0,0 +1,28 @@
#include "channel.h"
Channel::Channel(IRCServer* server, std::string channel_name): Nameable(server) {
this->channel_name = channel_name;
server->register_addressable_name(channel_name, this);
}
Channel::~Channel() {
remove_addressable_name(channel_name);
}
void Channel::send_direct(std::vector<IRCCommand> messages, std::string exclude) {
send_relay(messages, exclude);
}
std::string Channel::get_topic() {
return topic;
}
void Channel::set_topic(std::string topic) {
this->topic = topic;
}
bool Channel::has_topic() {
return (this->topic!="");
}
NameableType Channel::what_are_you() {
return NT_Channel;
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef CHANNEL_H
#define CHANNEL_H
#include <string>
#include "nameable.h"
#include "ircserver.h"
class Channel: public Nameable {
private:
std::string channel_name;
std::string topic;
public:
Channel(IRCServer* server, std::string channel_name);
~Channel();
void send_direct(std::vector<IRCCommand> messages, std::string exclude="");
std::string get_topic();
bool has_topic();
void set_topic(std::string topic);
NameableType what_are_you() override;
};
#endif
+44
View File
@@ -0,0 +1,44 @@
#include "connection.h"
#include <iostream>
Connection::Connection(int fd) {
this->fd = fd;
std::thread(&Connection::conn_hdlr, this).detach();
}
Connection::~Connection() {
close(this->fd);
}
void Connection::conn_hdlr() {
buffer = new char[512];
while (alive) {
if (read(this->fd, buffer, 511) == 0) {
destroy();
return;
}
std::string message(buffer);
int index;
if ((index = message.find_first_of("\r\n")) == std::string::npos)
continue;
else
message = message.substr(0, index);
process_cmd(message);
}
}
void Connection::process_cmd(std::string command) {}
void Connection::destroy() {}
void Connection::kill() {
this->alive = false;
}
void Connection::send(std::string to_write) {
if (to_write == "")
return;
to_write = to_write + "\r\n";
const char* buffer = to_write.c_str();
int len = strlen(buffer);
write(this->fd, buffer, len);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include <string>
#include <unistd.h>
#include <thread>
#include <string.h>
class Connection {
private:
int fd;
int alive = true;
char* buffer;
public:
Connection(int fd);
~Connection();
void conn_hdlr();
virtual void process_cmd(std::string command);
virtual void destroy();
void kill();
void send(std::string to_write);
};
#endif
+102
View File
@@ -0,0 +1,102 @@
#include "irccommand.h"
IRCCommand::IRCCommand() {}
IRCCommand::IRCCommand(std::string raw_command) {
if (raw_command[0] == '@') {
int index = raw_command.find_first_of(" ");
std::string tag_section = raw_command.substr(1, index);
raw_command = raw_command.substr(index+1);
// Todo: Further process tags down
}
if (raw_command[0] != 0)
raw_command.substr(1);
else
return;
if (raw_command[0] == ':') {
int index = raw_command.find_first_of(" ");
source = raw_command.substr(1, index);
raw_command = raw_command.substr(index+1);
}
if (raw_command[0] != 0)
raw_command.substr(1);
else
return;
int index = raw_command.find_first_of(" ");
command = raw_command.substr(0, index);
if (index == std::string::npos)
return;
raw_command = raw_command.substr(index+1);
if (raw_command[0] != 0)
raw_command.substr(1);
else
return;
index = raw_command.find_first_of(" ");
while (index != std::string::npos && raw_command[0] != ':') {
this->parameters.push_back(raw_command.substr(0, index));
raw_command = raw_command.substr(index + 1);
index = raw_command.find_first_of(" ");
}
if (raw_command[0] == ':')
this->parameters.push_back(raw_command.substr(1));
else
this->parameters.push_back(raw_command);
}
IRCCommand::IRCCommand(std::string source, std::string command) {
this->source = source;
this->command = command;
}
IRCCommand::~IRCCommand() {
this->parameters.clear();
this->parameters.shrink_to_fit();
}
bool IRCCommand::has_source() {
return (source!="");
}
std::string IRCCommand::get_source() {
return source;
}
bool IRCCommand::has_command() {
return (command!="");
}
std::string IRCCommand::get_command() {
return command;
}
bool IRCCommand::has_parameter() {
return parameters.size() > 0;
}
std::string IRCCommand::get_parameter(int i) {
return parameters.at(i);
}
int IRCCommand::get_parameter_count() {
return parameters.size();
}
std::string IRCCommand::to_string() {
std::string myself;
if (has_source())
myself += ":" + get_source() + " ";
myself += get_command();
for (auto i=parameters.begin(); i != parameters.end(); i++)
myself += ((i==parameters.end()-1)&&(i->find(" ")!=std::string::npos)?" :":" ") + *i;
return myself;
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef IRCCOMMAND_H
#define IRCCOMMAND_H
#include <unordered_map>
#include <string>
#include <vector>
class IRCCommand {
private:
std::unordered_map<std::string, std::string> tags;
std::string source, command = "";
std::vector<std::string> parameters;
public:
IRCCommand();
IRCCommand(std::string raw_command);
IRCCommand(std::string source, std::string command);
template<typename... T>
IRCCommand(std::string source, std::string command, T... params) {
this->source = source;
this->command = command;
this->parameters = {params...};
}
~IRCCommand();
bool has_source();
std::string get_source();
bool has_command();
std::string get_command();
bool has_parameter();
std::string get_parameter(int i);
int get_parameter_count();
std::string to_string();
};
#endif
+30
View File
@@ -0,0 +1,30 @@
#include "ircserver.h"
IRCServer::IRCServer(std::string hostname) {
this->hostname = hostname;
this->nameable_map = {};
}
bool IRCServer::is_addressable_name_free(std::string name) {
return nameable_map.find(name) == nameable_map.end();
}
bool IRCServer::has_addressable_name(std::string name) {
return !is_addressable_name_free(name);
}
void IRCServer::remove_addressable_name(std::string name) {
nameable_map.erase(name);
}
void IRCServer::register_addressable_name(std::string name, Nameable* bind) {
nameable_map[name] = bind;
}
Nameable* IRCServer::resolve_addressable_name(std::string name) {
return nameable_map[name];
}
std::string IRCServer::get_hostname() {
return hostname;
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef IRCSERVER_H
#define IRCSERVER_H
#include <string>
#include <unordered_map>
class Nameable;
class IRCServer {
private:
std::unordered_map<std::string, Nameable*> nameable_map;
std::string hostname;
public:
IRCServer(std::string);
void register_addressable_name(std::string name, Nameable* bind);
bool is_addressable_name_free(std::string name);
bool has_addressable_name(std::string name);
void remove_addressable_name(std::string name);
Nameable* resolve_addressable_name(std::string name);
std::string get_hostname();
};
#endif
+42
View File
@@ -0,0 +1,42 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include "ircserver.h"
#include "userconnection.h"
int main() {
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == 0) {
std::cout << "Socket Create Failed!\n" << std::flush;
return 1;
}
int opt = 1;
if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
std::cout << "Setting Socket Options Failed!\n" << std::flush;
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(6667);
unsigned int addrsize = sizeof(addr);
if (bind(socketfd, (struct sockaddr*)&addr, addrsize) < 0) {
std::cout << "Socket Bind Failed!\n" << std::flush;
return 1;
}
if (listen(socketfd, 5) < 0) {
std::cout << "Socket Listen Failed!\n" << std::flush;
return 1;
}
IRCServer* server = new IRCServer("xnoircd");
while (true) {
int sockfd = accept(socketfd, (struct sockaddr*)&addr, &addrsize);
new UserConnection(sockfd, server);
}
}
+72
View File
@@ -0,0 +1,72 @@
#include "nameable.h"
Nameable::Nameable(IRCServer* server) {
this->server = server;
}
Nameable::~Nameable() {
this->associates.clear();
this->associates.shrink_to_fit();
}
std::string Nameable::get_addressable_name() {
return this->addressable_name;
}
bool Nameable::set_addressable_name(std::string name) {
if (this->server->is_addressable_name_free(name)) {
this->addressable_name = name;
server->register_addressable_name(name, this);
return true;
} else {
return false;
}
}
bool Nameable::has_addressable_name() {
return (this->addressable_name!="");
}
void Nameable::remove_addressable_name(std::string name) {
server->remove_addressable_name(name);
}
bool Nameable::rename(std::string name) {
if (server->is_addressable_name_free(name)) {
server->remove_addressable_name(name);
server->register_addressable_name(name, this);
this->addressable_name = name;
return true;
} else {
return false;
}
}
std::vector<Nameable*> Nameable::get_associates() {
return this->associates;
}
void Nameable::add_associate(Nameable* associate) {
std::cout << "Adding: " << associate->get_addressable_name() << "\n";
this->associates.push_back(associate);
}
bool Nameable::has_associate(Nameable* associate) {
return !(std::find(associates.begin(), associates.end(), associate) == associates.end());
}
void Nameable::remove_associate(Nameable* associate) {
auto index = std::find(this->associates.begin(), this->associates.end(), associate);
if (index != this->associates.end()) {
this->associates.erase(index);
}
}
std::string Nameable::associates_as_string() {
std::string associate_list;
for (Nameable* associate : associates) {
associate_list += associate->get_addressable_name() + " ";
}
return associate_list;
}
void Nameable::send_direct(std::vector<IRCCommand> messages, std::string exclude) {}
void Nameable::send_relay(std::vector<IRCCommand> messages, std::string exclude) {
for (Nameable* associate : this->associates) {
if (associate->get_addressable_name() != exclude)
associate->send_direct(messages);
}
}
NameableType Nameable::what_are_you() {return NT_None;}
+46
View File
@@ -0,0 +1,46 @@
#ifndef NAMEABLE_H
#define NAMEABLE_H
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include "connection.h"
#include "ircserver.h"
#include "irccommand.h"
enum NameableType {
NT_None = 1,
NT_User = 2,
NT_Channel = 3
};
class Nameable {
private:
std::string addressable_name = "";
std::vector<Nameable*> associates;
public:
IRCServer* server;
Nameable(IRCServer* server);
~Nameable();
std::string get_addressable_name();
bool set_addressable_name(std::string name);
bool has_addressable_name();
void remove_addressable_name(std::string name);
bool rename(std::string to);
std::vector<Nameable*> get_associates();
void add_associate(Nameable* associate);
bool has_associate(Nameable* associate);
void remove_associate(Nameable* associate);
std::string associates_as_string();
virtual void send_direct(std::vector<IRCCommand> messages, std::string exclude="");
void send_relay(std::vector<IRCCommand> messages, std::string exclude="");
virtual NameableType what_are_you();
};
#endif
+281
View File
@@ -0,0 +1,281 @@
#include "user.h"
#include <iostream>
std::vector<std::string> split_string(std::string str, std::string delim) {
std::vector<std::string> to_return;
int delim_len = delim.length();
int index = str.find_first_of(delim);
while (index != std::string::npos) {
to_return.push_back(str.substr(0, index));
str = str.substr(index+delim_len);
index = str.find_first_of(delim);
}
to_return.push_back(str);
return to_return;
}
std::unordered_map<std::string, std::tuple<int, std::function<std::vector<IRCCommand>(User*, IRCCommand)>, bool>> User::commands = {
{"NICK", {1, &User::nick_cmd, false}},
{"USER", {4, &User::user_cmd, false}},
{"PING", {1, &User::ping_cmd, false}},
{"WHO", {1, &User::who_cmd, true}},
{"WHOIS", {1, &User::whois_cmd, true}},
{"JOIN", {1, &User::join_cmd, true}},
{"PART", {1, &User::part_cmd, true}},
{"QUIT", {0, &User::quit_cmd, true}},
{"PRIVMSG", {2, &User::privmsg_cmd, true}},
{"TOPIC", {2, &User::topic_cmd, true}},
{"NAMES", {1, &User::names_cmd, true}},
{"MOTD", {0, &User::motd_cmd, true}}
};
User::User(IRCServer* server, UserConnection* conn): Nameable(server) {
this->connection = conn;
}
User::~User() {
this->server->remove_addressable_name(this->get_nick());
this->send_relay({IRCCommand(this->get_nick(), "QUIT", "Quit: ")});
for (Nameable* associate : this->get_associates())
associate->remove_associate(this);
delete connection;
}
std::vector<IRCCommand> User::nick_cmd(IRCCommand cmd) {
return {set_nick(cmd.get_parameter(0))};
}
std::vector<IRCCommand> User::user_cmd(IRCCommand cmd) {
this->username = cmd.get_parameter(0);
this->realname = cmd.get_parameter(3);
if (this->has_nick()) {
this->state = Registered;
}
std::vector<IRCCommand> to_return = {
IRCCommand(server->get_hostname(), "001", this->get_nick(), "Welcome to the Polyglottal Network, " + this->get_nick()),
IRCCommand(server->get_hostname(), "002", this->get_nick(), "Your host is " + server->get_hostname()),
IRCCommand(server->get_hostname(), "003", this->get_nick(), "This server was never created."),
IRCCommand(server->get_hostname(), "004", this->get_nick(), server->get_hostname(), "0", "ABCabc", "ABCabc")
};
auto motd = motd_cmd(IRCCommand());
to_return.insert(to_return.end(), motd.begin(), motd.end());
return to_return;
}
std::vector<IRCCommand> User::ping_cmd(IRCCommand cmd) {
return {IRCCommand(server->get_hostname(), "PONG", server->get_hostname(), cmd.get_parameter(0))};
}
std::vector<IRCCommand> User::join_cmd(IRCCommand cmd) {
std::string channels_parameter = cmd.get_parameter(0);
std::vector<std::string> named_channels = split_string(channels_parameter, ",");
std::cout << named_channels.size() << "\n";
std::vector<IRCCommand> to_return;
for (std::string named_channel : named_channels) {
Nameable* channel;
if (server->has_addressable_name(named_channel)) {
channel = server->resolve_addressable_name(named_channel);
} else {
channel = new Channel(server, named_channel);
}
if (channel->what_are_you() != NT_Channel)
continue;
Channel* chan = (Channel*)channel;
this->add_associate(chan);
chan->add_associate(this);
chan->send_direct({IRCCommand(this->get_nick(), "JOIN", named_channel)});
std::vector<Nameable*> channel_members = chan->get_associates();
to_return.push_back(IRCCommand(server->get_hostname(), "353", this->get_nick(), "=", named_channel, chan->associates_as_string()));
if (chan->has_topic())
to_return.push_back(IRCCommand(server->get_hostname(), "332", this->get_nick(), named_channel, chan->get_topic()));
}
return to_return;
}
std::vector<IRCCommand> User::part_cmd(IRCCommand cmd) {
std::string channels_parameter = cmd.get_parameter(0);
std::vector<std::string> named_channels = split_string(channels_parameter, ",");
for (std::string named_channel : named_channels) {
if (server->has_addressable_name(named_channel)) {
Nameable* channel = server->resolve_addressable_name(named_channel);
this->remove_associate(channel);
channel->remove_associate(this);
channel->send_direct({IRCCommand(this->get_nick(), "PART", named_channel)});
}
}
return {};
}
std::vector<IRCCommand> User::privmsg_cmd(IRCCommand cmd) {
std::string named_target = cmd.get_parameter(0);
if (server->has_addressable_name(named_target)) {
Nameable* target = server->resolve_addressable_name(named_target);
target->send_direct({IRCCommand(this->get_nick(), "PRIVMSG", named_target, cmd.get_parameter(1))}, this->get_nick());
}
return {};
}
std::vector<IRCCommand> User::quit_cmd(IRCCommand cmd) {
destroy();
return {};
}
std::vector<IRCCommand> User::who_cmd(IRCCommand cmd) {
if (!server->has_addressable_name(cmd.get_parameter(0)))
return {};
Nameable* target = server->resolve_addressable_name(cmd.get_parameter(0));
if (target->what_are_you() != NT_User)
return {};
User* user = (User*)target;
return {
IRCCommand(server->get_hostname(), "352", this->get_nick(), "*", user->get_username(), " ", server->get_hostname(), "H", "0 " + user->get_realname()),
IRCCommand(server->get_hostname(), "315", this->get_nick(), user->get_nick(), "End of WHO listing.")
};
}
std::vector<IRCCommand> User::whois_cmd(IRCCommand cmd) {
if (!server->has_addressable_name(cmd.get_parameter(0)))
return {};
Nameable* target = server->resolve_addressable_name(cmd.get_parameter(0));
if (target->what_are_you() != NT_User)
return {};
User* user = (User*)target;
return {};
}
std::vector<IRCCommand> User::topic_cmd(IRCCommand cmd) {
std::string named_channel = cmd.get_parameter(0);
if (!server->has_addressable_name(named_channel))
return {};
Nameable* target = server->resolve_addressable_name(named_channel);
if (target->what_are_you() != NT_Channel)
return {};
std::string topic = cmd.get_parameter(1);
((Channel*)target)->set_topic(topic);;
return {IRCCommand(server->get_hostname(), this->get_nick(), named_channel, topic)};
}
std::vector<IRCCommand> User::names_cmd(IRCCommand cmd) {
std::string named_channel = cmd.get_parameter(0);
if (!server->has_addressable_name(named_channel))
return {};
Nameable* resolved = server->resolve_addressable_name(named_channel);
if (resolved->what_are_you() != NT_Channel)
return {};
Channel* chan = (Channel*)resolved;
return {
IRCCommand(server->get_hostname(), "353", this->get_nick(), named_channel, chan->associates_as_string()),
IRCCommand(server->get_hostname(), "366", this->get_nick(), named_channel, "End of NAMES.")
};
}
std::vector<IRCCommand> User::motd_cmd(IRCCommand cmd) {
return {IRCCommand(server->get_hostname(), "422", this->get_nick(), "MOTD is not defined.")};
}
std::vector<IRCCommand> User::process_cmd(IRCCommand cmd) {
auto command = commands.find(cmd.get_command());
if (command == commands.end())
return {IRCCommand(server->get_hostname(), "421", get_nick(), cmd.get_command(), "Unknown Command.")};
if (cmd.get_parameter_count() < std::get<0>(command->second))
return {IRCCommand(server->get_hostname(), "461", get_nick(), cmd.get_command(), "Too few parameters.")};
if (this->state == Initial && std::get<2>(command->second))
return {};
return std::get<1>(command->second)(this, cmd);
}
IRCCommand User::set_nick(std::string nick) {
bool success;
std::string old_nick = this->get_nick();
if (this->has_nick())
success = this->rename(nick);
else
success = this->set_addressable_name(nick);
if (success) {
if (old_nick != "") {
return IRCCommand(old_nick, "NICK", this->get_nick());
}
return {};
} else {
return IRCCommand(server->get_hostname(), "433", "*", nick, "Nickname is already in use!");
}
}
void User::set_username(std::string username) {
this->username = username;
}
void User::set_realname(std::string realname) {
this->realname = realname;
}
bool User::has_nick() {
return has_addressable_name();
}
bool User::has_username(){
return (username!="");
}
bool User::has_realname(){
return (realname!="");
}
std::string User::get_nick(){
return get_addressable_name();
}
std::string User::get_username(){
return username;
}
std::string User::get_realname(){
return realname;
}
void User::send_direct(std::vector<IRCCommand> messages, std::string exclude) {
for (IRCCommand m : messages) {
std::cout << m.to_string() << "\n";
this->connection->send(m.to_string());
}
}
NameableType User::what_are_you() {
return NT_User;
}
void User::destroy() {
Connection* conn = connection;
delete this;
conn->kill();
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef USER_H
#define USER_H
#include <string>
#include <unordered_map>
#include <tuple>
#include <functional>
#include "irccommand.h"
#include "ircserver.h"
#include "userconnection.h"
#include "nameable.h"
#include "channel.h"
enum UCState {
Initial,
Registered
};
class UserConnection;
class User: public Nameable {
private:
std::string username, realname = "";
Connection* connection;
UCState state;
std::vector<IRCCommand> nick_cmd(IRCCommand cmd);
std::vector<IRCCommand> user_cmd(IRCCommand cmd);
std::vector<IRCCommand> ping_cmd(IRCCommand cmd);
std::vector<IRCCommand> who_cmd(IRCCommand cmd);
std::vector<IRCCommand> whois_cmd(IRCCommand cmd);
std::vector<IRCCommand> join_cmd(IRCCommand cmd);
std::vector<IRCCommand> part_cmd(IRCCommand cmd);
std::vector<IRCCommand> quit_cmd(IRCCommand cmd);
std::vector<IRCCommand> privmsg_cmd(IRCCommand cmd);
std::vector<IRCCommand> topic_cmd(IRCCommand cmd);
std::vector<IRCCommand> names_cmd(IRCCommand cmd);
std::vector<IRCCommand> motd_cmd(IRCCommand cmd);
static std::unordered_map<std::string, std::tuple<int, std::function<std::vector<IRCCommand>(User*, IRCCommand)>, bool>> commands;
//std::string construct_string(int count, std::string source, std::string command, std::string parameters...);
template<typename... T>
std::string construct_string(std::string source, std::string command, T... parameters);
public:
User(IRCServer* server, UserConnection* conn);
~User();
std::vector<IRCCommand> process_cmd(IRCCommand cmd);
IRCCommand set_nick(std::string nick);
void set_username(std::string username);
void set_realname(std::string realname);
bool has_nick();
bool has_username();
bool has_realname();
std::string get_nick();
std::string get_username();
std::string get_realname();
void send_direct(std::vector<IRCCommand> messages, std::string exclude="");
NameableType what_are_you() override;
void destroy();
};
#endif
+25
View File
@@ -0,0 +1,25 @@
#include "userconnection.h"
#include <iostream>
UserConnection::UserConnection(int fd, IRCServer* server): Connection(fd) {
this->server = server;
this->user = new User(server, this);
}
UserConnection::~UserConnection() {}
void UserConnection::process_cmd(std::string cmd_in) {
if (cmd_in == "")
return;
IRCCommand cmd = IRCCommand(cmd_in);
std::cout << cmd.to_string() << std::endl;
for (IRCCommand c : user->process_cmd(cmd))
send(c.to_string());
}
void UserConnection::destroy() {
this->user->destroy();
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef USERCONNECTION_H
#define USERCONNECTION_H
#include <string>
#include "connection.h"
#include "ircserver.h"
#include "user.h"
class User;
class UserConnection: public Connection {
private:
User* user;
IRCServer* server;
public:
UserConnection(int fd, IRCServer* server);
~UserConnection();
void process_cmd(std::string cmd_in);
void destroy();
};
#endif