Push project

This commit is contained in:
Xamora 2024-08-27 17:56:32 +02:00
commit c15ff7d6e7
33 changed files with 2039 additions and 0 deletions

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
# https://EditorConfig.org
root = true
[*.{cpp,hpp}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/build/
/ircserv

44
Makefile Normal file
View file

@ -0,0 +1,44 @@
MAKEFLAGS += -j
CXX := c++
LD := $(CXX)
CXXFLAGS := -g -O0 -Wall -Wextra -Werror -std=c++98 -MMD
OBJ := $(patsubst src/%.cpp,build/%.o,$(wildcard src/*.cpp))
DEP := $(patsubst %.o,%.d,$(OBJ))
NAME := ircserv
all: $(NAME)
$(NAME): $(OBJ)
@printf 'LD %s\n' "$@"
@$(LD) -o $(NAME) $(OBJ)
build/%.o: src/%.cpp
@printf 'CXX %s\n' "$@"
@mkdir -p $(@D)
@$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
@printf 'RM build\n'
@rm -rf build/
fclean:
@printf 'RM build %s\n' "$(NAME)"
@rm -rf build/ $(NAME)
re:
@make --no-print-directory fclean
@make --no-print-directory all
run: $(NAME)
clear
@./$(NAME) 6969 michel
gdb: $(NAME)
clear
@gdb --args ./$(NAME) 6969 michel
.PHONY: all clean fclean re run gdb
-include $(DEP)

3
compile_flags.txt Normal file
View file

@ -0,0 +1,3 @@
-Wall
-Wextra
-std=c++98

443
src/Channel.cpp Normal file
View file

@ -0,0 +1,443 @@
#include "Channel.hpp"
#include "errcodes.hpp"
#include "Server.hpp"
#include "log.hpp"
#include "split.hpp"
#include <cstdlib>
#include <sstream>
Channel::Channel(std::string const& name, Client& client) :
_clients(),
_op_clients(),
_name(name),
_topic(),
_fInviteOnly(false),
_fTopicRestric(false),
_keyword(),
_userLimit(0)
{
_op_clients.insert(&client);
if (addClient(client)) {
return;
}
}
Channel::Channel(Channel const& src) :
_clients(src._clients),
_op_clients(src._clients),
_name(src._name),
_topic(src._topic),
_fInviteOnly(src._fInviteOnly),
_fTopicRestric(src._fTopicRestric),
_keyword(src._keyword),
_userLimit(src._userLimit)
{}
bool
Channel::addClient(Client& client)
{
if (_clients.insert(&client).second) {
std::set<Client*>::iterator it = _clients.begin();
for (; it != _clients.end(); ++it) {
(*it)->sendMsg(client.nick, "JOIN", _name);
}
if (!_topic.empty()) {
if (client.sendCode(RPL_TOPIC, _name, _topic)) {
removeClient(client);
return false;
}
}
std::string names;
for (it = _clients.begin(); it != _clients.end(); ++it) {
if (isOp(**it)) {
names += '@';
}
names += (*it)->nick + ' ';
}
if (client.sendCode(RPL_NAMREPLY, _name, names)) {
removeClient(client);
return false;
}
return true;
}
return false;
}
bool
Channel::addOp(Client& client)
{
if (_clients.find(&client) == _clients.end()) {
/* client to op not in channel */
return false;
}
if (_op_clients.insert(&client).second) {
/* client is now op */
LOG << client.nick << " is op" << std::endl;
_op_clients.insert(&client);
return true;
}
/* cringe shit lol */
return false;
}
bool
Channel::removeClient(Client& client)
{
removeOp(client);
return (_clients.erase(&client) == 1);
}
bool
Channel::removeOp(Client &client)
{
return (_op_clients.erase(&client) == 1);
}
bool
Channel::partClient(Client& client, std::string const& reason)
{
if (_clients.find(&client) == _clients.end()) {
return false;
}
std::set<Client*>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) {
(*it)->sendMsg(client.nick, "PART " + _name, reason);
}
return removeClient(client);
}
bool
Channel::kickClient(Client& client, Client& victim, std::string const& reason)
{
if (_clients.find(&client) == _clients.end()) {
return false;
}
std::set<Client*>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) {
(*it)->sendMsg(client.nick, "KICK " + _name + ' ' + victim.nick, reason);
}
return removeClient(victim);
}
size_t
Channel::numClients() const
{
return _clients.size();
}
int
Channel::sendMessage(std::string const& msg, Client const* client) const
{
std::string const sender = client ? client->nick : "";
std::set<Client*>::iterator it = _clients.begin();
for (; it != _clients.end(); ++it) {
if (client == *it) {
continue;
}
(*it)->sendMsg(sender, "PRIVMSG " + _name, msg);
}
return 0;
}
int
Channel::sendNotice(std::string const& msg, Client const& client) const
{
std::set<Client*>::iterator it = _clients.begin();
for (; it != _clients.end(); ++it) {
if (client == **it) {
continue;
}
(*it)->sendMsg(client.nick, "NOTICE " + _name, msg);
}
return 0;
}
int
Channel::sendTopic(Client& client) const
{
if (!isOnChannel(client)) {
return 0;
}
if (_topic.empty()) {
return client.sendCode(RPL_NOTOPIC, _name, "No topic is set");
} else {
return client.sendCode(RPL_TOPIC, _name, _topic);
}
}
int
Channel::setTopic(Client& client, std::string const& newTopic)
{
if (_fTopicRestric && !isOp(client)) {
return client.sendCode(ERR_CHANOPRIVSNEEDED, _name,
"You're not channel operator");
}
_topic = newTopic;
return sendTopic(client);
}
int
Channel::setMode(Server& server, Client& client, string_vector const& args)
{
if (!isOnChannel(client)) {
return client.sendCode(ERR_NOTONCHANNEL, _name,
"You're not on that channel");
}
if (!isOp(client)) {
return client.sendCode(ERR_CHANOPRIVSNEEDED, _name,
"You're not channel operator");
}
bool sign = true;
size_t argToCheck = 2;
std::string const& modes = args[1];
string_vector update(1);
for (size_t i = 0; i < modes.length(); ++i) {
const char c = modes[i];
if (c == 'o' || ((c == 'k' || c == 'l') && sign)) {
if (args.size() <= argToCheck) {
continue;
}
}
switch (c) {
case '-':
sign = false;
if (update[0].length() &&
(update[0][update[0].length() - 1] == '-' ||
update[0][update[0].length() - 1] == '+'))
{
update[0].erase(update[0].length() - 1);
}
update[0].push_back(c);
break;
case '+':
sign = true;
if (update[0].length() &&
(update[0][update[0].length() - 1] == '-' ||
update[0][update[0].length() - 1] == '+'))
{
update[0].erase(update[0].length() - 1);
}
update[0].push_back(c);
break;
case 'i':
if (_fInviteOnly != sign) {
update[0].push_back(c);
}
_fInviteOnly = sign;
break;
case 't':
if (_fInviteOnly != sign) {
update[0].push_back(c);
}
_fTopicRestric = sign;
break;
case 'o': {
Client* user = server.findClient(args[argToCheck++]);
if (!user) {
if (client.sendCode(ERR_NOSUCHNICK, args[argToCheck - 1], "No such nick (UwU~)")) {
return -1;
}
break;
}
if (!isOnChannel(*user)) {
if (client.sendCode(ERR_USERNOTINCHANNEL, args[argToCheck - 1] + ' ' + _name,
"They aren't on that channel"))
{
return -1;
}
break;
}
if (sign) {
if (!isOp(*user)) {
update[0].push_back(c);
update.push_back(args[argToCheck - 1]);
}
addOp(*user);
} else {
if (isOp(*user)) {
update[0].push_back(c);
update.push_back(args[argToCheck - 1]);
}
removeOp(*user);
}
break;
}
case 'k':
_keyword = sign ? args[argToCheck++] : "";
update[0].push_back(c);
if (sign) {
update.push_back(args[argToCheck - 1]);
}
break;
case 'l':
if (sign) {
const int n = std::atoi(args[argToCheck++].c_str());
if (n > 0) {
update[0].push_back(c);
update.push_back(args[argToCheck - 1]);
_userLimit = n;
}
} else {
if (_userLimit) {
update[0].push_back(c);
}
_userLimit = 0;
}
break;
default: {
const char cs[] = {c, 0};
if (client.sendCode(ERR_UNKNOWNMODE, cs,
"is unknown mode char to " + _name))
{
return -1;
}
break;
}
}
}
if (update[0].length() &&
(update[0][update[0].length() - 1] == '-' ||
update[0][update[0].length() - 1] == '+'))
{
update[0].erase(update[0].length() - 1);
}
if (!update[0].length()) {
return 0;
}
for (size_t i = 1; i < update.size(); ++i) {
update[0] += ' ' + update[i];
}
std::set<Client*>::iterator it = _clients.begin();
for (; it != _clients.end(); ++it) {
(*it)->sendMsg(client.nick, "MODE " + _name + " " + update[0], "");
}
return 0;
}
int
Channel::sendMode(Client& client)
{
string_vector modes(1);
if (_fInviteOnly) {
modes[0] += 'i';
}
if (_fTopicRestric) {
modes[0] += 't';
}
if (!_keyword.empty()) {
modes[0] += 'k';
if (isOnChannel(client)) {
modes.push_back(_keyword);
}
}
if (_userLimit != 0) {
modes[0] += 'l';
std::stringstream str;
str << _userLimit;
modes.push_back(str.str());
}
std::string out = _name + " +" + modes[0];
for (size_t i = 1; i < modes.size(); ++i) {
out += ' ' + modes[i];
}
return client.sendCode(RPL_CHANNELMODEIS, out, "");
}
std::string const&
Channel::getName() const
{
return _name;
}
size_t
Channel::getUserLimit() const
{
return _userLimit;
}
bool
Channel::isInviteOnly() const
{
return _fInviteOnly;
}
std::string const&
Channel::getKeyword() const
{
return _keyword;
}
bool
Channel::areOnChannel(Client& c1, Client& c2) const
{
if (_clients.find(&c1) != _clients.end()) {
if (_clients.find(&c2) != _clients.end()) {
return true;
}
}
return false;
}
bool
Channel::isOnChannel(Client& c1) const
{
if (_clients.find(&c1) != _clients.end()) {
return true;
}
return false;
}
bool
Channel::isOp(Client &client) const
{
return (_op_clients.find(&client) != _op_clients.end());
}
bool
Channel::isChannelEmpty() const
{
if (_clients.empty() && _op_clients.empty()) {
return true;
}
return false;
}
Channel&
Channel::operator=(Channel const& rhs)
{
if (this == &rhs) {
return *this;
}
_clients = rhs._clients;
_op_clients = rhs._op_clients;
_topic = rhs._topic;
_fInviteOnly = rhs._fInviteOnly;
_fTopicRestric = rhs._fTopicRestric;
_keyword = rhs._keyword;
_userLimit = rhs._userLimit;
return *this;
}
bool
Channel::operator==(Channel const& rhs) const
{
return _name == rhs._name;
}
bool
Channel::operator==(std::string const& name) const
{
return _name == name;
}

57
src/Channel.hpp Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include "Client.hpp"
#include "Server.hpp"
#include "split.hpp"
#include <set>
#include <string>
class Client;
class Server;
class Channel {
private:
std::set<Client*> _clients;
std::set<Client*> _op_clients;
std::string const _name;
std::string _topic;
//Channel modes
bool _fInviteOnly;
bool _fTopicRestric;
std::string _keyword;
size_t _userLimit;
public:
Channel(std::string const& name, Client& client);
Channel(Channel const& src);
bool addClient(Client& client);
bool addOp(Client& client);
bool removeClient(Client& client);
bool removeOp(Client& client);
bool partClient(Client& client, std::string const& reason = "");
bool kickClient(Client& client, Client& victim, std::string const& reason);
size_t numClients() const;
int sendMessage(std::string const& msg, Client const* client = NULL) const;
int sendNotice(std::string const& msg, Client const& client) const;
int sendTopic(Client& client) const;
int setTopic(Client& client, std::string const& newTopic);
int setMode(Server& server, Client& client, string_vector const& args);
int sendMode(Client& client);
std::string const& getName() const;
size_t getUserLimit() const;
bool isInviteOnly() const;
std::string const& getKeyword() const;
bool areOnChannel(Client& c1, Client& c2) const;
bool isOnChannel(Client& c1) const;
bool isOp(Client& client) const;
bool isChannelEmpty() const;
Channel& operator=(Channel const& rhs);
bool operator==(Channel const& rhs) const;
bool operator==(std::string const& name) const;
};

209
src/Client.cpp Normal file
View file

@ -0,0 +1,209 @@
#include "Client.hpp"
#include "Server.hpp"
#include "error.hpp"
#include "log.hpp"
#include <unistd.h>
#include <cassert>
Client::Client(Server& server):
_server(server),
_addrsize(sizeof(_addr)),
_addr(),
_fd(-1),
_buffer(),
_pass(false),
_registered(false),
_deleted(false),
_welcomed(false),
_channelsInvitedTo(),
nick(""),
username(""),
realname("")
{}
Client::~Client()
{
if (_deleted) {
return;
}
_deleted = true;
if (_server.isLive()) {
_server.removeClient(*this);
}
if (_fd >= 0) {
close(_fd);
LOG << "client disconnected" << std::endl;
_fd = -1;
}
}
int
Client::init(int servfd)
{
_fd = accept(servfd, &_addr, &_addrsize);
if (_fd < 0) {
error("accept");
return -1;
}
return 0;
}
int
Client::getFd() const
{
return _fd;
}
int
Client::recv()
{
char buf[4096] = {};
const int rv = ::recv(getFd(), buf, sizeof(buf) - 1, 0);
if (rv < 0) {
error("recv");
_server.removeClient(*this);
return -1;
}
if (rv == 0) {
/* disconnection */
_server.quitClient(*this);
return 1;
}
_buffer += std::string(buf);
return 0;
}
bool
Client::queuedMsg() const
{
return (_buffer.find("\r\n") != std::string::npos);
}
std::string
Client::pollMsg()
{
const std::size_t div = _buffer.find("\r\n");
assert(div != std::string::npos);
const std::string msg = _buffer.substr(0, div);
_buffer = _buffer.substr(div + 2);
return msg;
}
int
Client::sendCode(const std::string& code, const std::string& msg)
{
const std::string out = code + " :" + msg + "\r\n";
if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
LOG << "send failed cringe kekw" << std::endl;
_server.removeClient(*this);
return -1;
}
return 0;
}
int
Client::sendCode(const std::string& code, const std::string& msg0, const std::string& msg1)
{
const std::string out = code + " " + msg0 + " :" + msg1 + "\r\n";
if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
LOG << "send failed cringe kekw" << std::endl;
_server.removeClient(*this);
return -1;
}
return 0;
}
void
Client::fatalSendCode(const std::string &code, const std::string &msg)
{
if (!sendCode(code, msg)) {
_server.removeClient(*this);
}
}
int
Client::sendMsg(std::string const& sender, std::string const& cmd,
std::string const& trailing)
{
std::string const out = ':' + sender + ' ' + cmd + " :" + trailing + "\r\n";
if (::send(_fd, out.c_str(), out.length(), 0) != (ssize_t)out.length()) {
LOG << "send failed cringe kekw" << std::endl;
_server.removeClient(*this);
return -1;
}
return 0;
}
int
Client::sendRaw(std::string const& msg)
{
if (::send(_fd, msg.c_str(), msg.length(), 0) != (ssize_t)msg.length()) {
LOG << "send failed cringe kekw" << std::endl;
_server.removeClient(*this);
return -1;
}
return 0;
}
bool
Client::getPass() const
{
return _pass;
}
void
Client::setPass()
{
_pass = true;
}
bool
Client::getRegistered() const
{
return _registered;
}
void
Client::setRegistered()
{
_registered = true;
}
bool
Client::getWelcomed() const
{
return _welcomed;
}
void
Client::setWelcomed()
{
_welcomed = true;
}
void
Client::addInvite(Channel const& channel)
{
_channelsInvitedTo.insert(channel.getName());
}
bool
Client::isInvited(Channel const& channel)
{
return _channelsInvitedTo.erase(channel.getName());
}
bool
Client::operator==(Client const& rhs) const
{
return this == &rhs;
}
bool
Client::operator==(std::string const& nick) const
{
return this->nick == nick;
}

58
src/Client.hpp Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include <netinet/in.h>
#include <string>
#include <set>
#include "Channel.hpp"
class Channel;
class Server;
class Client {
private:
Server& _server;
unsigned int _addrsize;
struct sockaddr _addr;
int _fd;
std::string _buffer;
bool _pass;
bool _registered;
bool _deleted;
bool _welcomed;
std::set<std::string> _channelsInvitedTo;
public:
std::string nick;
std::string username;
std::string realname;
Client(Server& _server);
~Client();
int init(int servfd);
int getFd() const;
int recv();
bool queuedMsg() const;
std::string pollMsg();
int sendCode(const std::string& code, const std::string& msg);
int sendCode(const std::string& code, const std::string& msg0, const std::string& msg1);
void fatalSendCode(const std::string& code, const std::string& msg);
int sendMsg(std::string const& sender, std::string const& cmd,
std::string const& trailing);
int sendRaw(std::string const& msg);
bool getPass() const;
void setPass();
bool getRegistered() const;
void setRegistered();
bool getWelcomed() const;
void setWelcomed();
void addInvite(Channel const& channel);
bool isInvited(Channel const& channel);
bool operator==(Client const& rhs) const;
bool operator==(std::string const& nick) const;
};

80
src/Message.cpp Normal file
View file

@ -0,0 +1,80 @@
#include "Message.hpp"
#include "Client.hpp"
#include "log.hpp"
#include "split.hpp"
#include "errcodes.hpp"
#include <iostream>
Message::Message(Client& client, const std::string& raw) : _client(client), _raw(raw), _cmd(), _args()
{
if (_raw == "") {
return;
}
string_vector major_split = split(_raw, " :");
while (major_split.size() > 2) {
size_t size = major_split.size();
major_split[size - 2] += " :" + major_split[size - 1];
major_split.pop_back();
}
_args = split(major_split[0], " ", true);
if (major_split.size() == 2) {
_args.push_back(major_split[1]);
}
if (!_args.empty()) {
_cmd = _args[0];
_args.erase(_args.begin());
}
}
const std::string&
Message::getRaw() const
{
return _raw;
}
const std::string&
Message::getCmd() const
{
return _cmd;
}
int
Message::expectArgs(unsigned int count) const
{
if (getArgs().size() < count) {
LOG << _cmd << " expects " << count << " parameters" << std::endl;
if (_client.sendCode(ERR_NEEDMOREPARAMS, _cmd, "Not enough parameters")) {
return -1;
}
return 1;
}
return 0;
}
const string_vector&
Message::getArgs() const
{
return _args;
}
Client&
Message::getClient() const
{
return _client;
}
std::ostream&
operator<<(std::ostream &os, const Message& msg)
{
os << "CMD: '" << msg.getCmd() << '\'' << std::endl;
os << "ARGS: ";
string_vector const& args = msg.getArgs();
for (size_t i = 0; i < args.size(); ++i) {
os << '\'' << args[i] << "'\t";
}
return os;
}

23
src/Message.hpp Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include "split.hpp"
class Client;
class Message {
private:
Client& _client;
const std::string _raw;
std::string _cmd;
string_vector _args;
public:
Message(Client& _client, const std::string& raw);
const std::string& getRaw() const;
const std::string& getCmd() const;
int expectArgs(unsigned int count) const;
const string_vector& getArgs() const;
Client& getClient() const;
};
std::ostream& operator<<(std::ostream &os, const Message& msg);

48
src/Server.INVITE.cpp Normal file
View file

@ -0,0 +1,48 @@
#include "Server.hpp"
#include "errcodes.hpp"
#include <algorithm>
int
Server::_cmdINVITE(Message const& msg)
{
if (int rv = msg.expectArgs(2)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
std::list<Client>::iterator guest = std::find(_clients.begin(),
_clients.end(),
msg.getArgs()[0]);
std::vector<Channel>::iterator channel = std::find(_channels.begin(),
_channels.end(),
msg.getArgs()[1]);
if (guest == _clients.end()) {
return client.sendCode(ERR_NOSUCHNICK, msg.getArgs()[0], "No such nick");
}
if (channel == _channels.end()) {
goto send_invite_msg;
}
if (!channel->isOnChannel(client)) {
return client.sendCode(ERR_NOTONCHANNEL, msg.getArgs()[1],
"You're not on that channel");
}
if (channel->isOnChannel(*guest)) {
return client.sendCode(ERR_USERONCHANNEL,
msg.getArgs()[0] + ' ' + msg.getArgs()[1],
"is already on channel");
}
if (channel->isInviteOnly() && !channel->isOp(client)) {
return client.sendCode(ERR_CHANOPRIVSNEEDED, msg.getArgs()[1],
"You're not channel operator");
}
guest->addInvite(*channel);
send_invite_msg:
guest->sendMsg(client.nick,
"INVITE " + msg.getArgs()[0] + ' ' + msg.getArgs()[1],
"");
return client.sendCode(RPL_INVITING,
msg.getArgs()[1] + ' ' + msg.getArgs()[0],
"");
}

65
src/Server.JOIN.cpp Normal file
View file

@ -0,0 +1,65 @@
#include "Server.hpp"
#include "errcodes.hpp"
#include <algorithm>
int
Server::_cmdJOIN(Message const& msg)
{
static std::string const charCheck(" \a\r\n");
if (int rv = msg.expectArgs(1)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
string_vector const& chansToJoin = split(msg.getArgs()[0], ",", true);
string_vector keywords(chansToJoin.size());
if (msg.getArgs().size() > 1) {
keywords = split(msg.getArgs()[1], ",", false);
}
for (size_t i = 0; i < chansToJoin.size(); ++i) {
if (chansToJoin[i][0] != '&' && chansToJoin[i][0] != '#') {
if (client.sendCode(ERR_NOSUCHCHANNEL, chansToJoin[i], "No such channel")) {
return 1;
}
continue;
}
for (size_t j = 1; j < chansToJoin[i].length(); ++j) {
if (charCheck.find(chansToJoin[i][j]) != charCheck.npos) {
if (client.sendCode(ERR_NOSUCHCHANNEL, chansToJoin[i], "No such channel")) {
return 1;
}
goto loop_skip;
}
}
{
std::vector<Channel>::iterator it = std::find(_channels.begin(),
_channels.end(), chansToJoin[i]);
if (it == _channels.end()) {
it = _channels.insert(_channels.end(), Channel(chansToJoin[i], client));
if (it->isChannelEmpty()) {
_channels.erase(it);
}
} else if (it->getUserLimit() && it->getUserLimit() >= it->numClients()) {
if (client.sendCode(ERR_CHANNELISFULL, chansToJoin[i], "Cannot join channel (+l)")) {
return 1;
}
goto loop_skip;
} else if (it->isInviteOnly() && !client.isInvited(*it)) {
if (client.sendCode(ERR_INVITEONLYCHAN, chansToJoin[i], "Cannot join channel (+i)")) {
return 1;
}
} else if (!it->getKeyword().empty() && keywords[i] != it->getKeyword()) {
if (client.sendCode(ERR_BADCHANNELKEY, chansToJoin[i], "Cannot join channel (+k)")) {
return 1;
}
} else {
it->addClient(client);
}
}
loop_skip:;
}
return 0;
}

67
src/Server.KICK.cpp Normal file
View file

@ -0,0 +1,67 @@
#include "Server.hpp"
#include "split.hpp"
#include "errcodes.hpp"
#include <algorithm>
#include <string>
int
Server::_cmdKICK(Message const& msg)
{
if (int rv = msg.expectArgs(2)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
std::string reason;
if (msg.getArgs().size() > 2) {
reason = msg.getArgs()[2];
} else {
reason = client.nick;
}
string_vector const& chans = split(msg.getArgs()[0], ",");
string_vector const& users = split(msg.getArgs()[1], ",");
if (users.size() != chans.size()) {
client.sendCode(ERR_NEEDMOREPARAMS, "KICK", "Not enough parameters");
return 0;
}
for (size_t i = 0; i < users.size(); ++i) {
std::list<Client>::iterator itClient;
std::vector<Channel>::iterator itChannel;
itClient = std::find(_clients.begin(), _clients.end(), users[i]);
if (itClient == _clients.end()) {
client.sendCode(ERR_NOSUCHNICK, users[i], "No such nick/channel");
continue;
}
itChannel = std::find(_channels.begin(), _channels.end(), chans[i]);
if (itChannel == _channels.end()) {
client.sendCode(ERR_NOSUCHCHANNEL, chans[i], "No such channel");
continue;
}
if (!itChannel->isOnChannel(client)) {
client.sendCode(ERR_NOTONCHANNEL, chans[i], "You're not on that channel");
continue;
}
if (!itChannel->isOnChannel(*itClient)) {
client.sendCode(ERR_USERNOTINCHANNEL, users[i] + " " + chans[i],
"They aren't on that channel");
continue;
}
if (!itChannel->isOp(client)) {
client.sendCode(ERR_CHANOPRIVSNEEDED, chans[i], "You're not channel operator");
continue;
}
itChannel->kickClient(client, *itClient, reason);
if (itChannel->isChannelEmpty()) {
_channels.erase(itChannel);
std::list<Client>::iterator itC;
for (itC = _clients.begin(); itC != _clients.end(); ++itC) {
itC->isInvited(*itChannel);
}
}
}
return 0;
}

27
src/Server.MODE.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "Server.hpp"
#include "errcodes.hpp"
#include "split.hpp"
#include <algorithm>
int
Server::_cmdMODE(Message const& msg)
{
if (int rv = msg.expectArgs(1)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
string_vector const& args = msg.getArgs();
std::vector<Channel>::iterator itChannel;
itChannel = std::find(_channels.begin(), _channels.end(), args[0]);
if (itChannel == _channels.end()) {
return client.sendCode(ERR_NOSUCHCHANNEL, msg.getArgs()[0],
"No such channel");
}
if (msg.getArgs().size() > 1) {
return itChannel->setMode(*this, client, args);
} else {
return itChannel->sendMode(client);
}
}

96
src/Server.MOTD.cpp Normal file
View file

@ -0,0 +1,96 @@
#include "Server.hpp"
#include "errcodes.hpp"
int
Server::_cmdMOTD(const Message& msg)
{
static const char *const motd[] = {
"- We're no strangers to love",
"- You know the rules and so do I",
"- A full commitment's what I'm thinking of",
"- You wouldn't get this from any other guy",
"- ",
"- I just wanna tell you how I'm feeling",
"- Gotta make you understand",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you",
"- ",
"- We've known each other for so long",
"- Your heart's been aching, but",
"- You're too shy to say it",
"- Inside, we both know what's been going on",
"- We know the game and we're gonna play it",
"- ",
"- And if you ask me how I'm feeling",
"- Don't tell me you're too blind to see",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you",
"- ",
"- (Ooh, give you up)",
"- (Ooh, give you up)",
"- Never gonna give, never gonna give",
"- (Give you up)",
"- Never gonna give, never gonna give",
"- (Give you up)",
"- ",
"- We've known each other for so long",
"- Your heart's been aching, but",
"- You're too shy to say it",
"- Inside, we both know what's been going on",
"- We know the game and we're gonna play it",
"- ",
"- I just wanna tell you how I'm feeling",
"- Gotta make you understand",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you",
"- ",
"- Never gonna give you up",
"- Never gonna let you down",
"- Never gonna run around and desert you",
"- Never gonna make you cry",
"- Never gonna say goodbye",
"- Never gonna tell a lie and hurt you"
};
if (msg.getClient().sendCode(RPL_MOTDSTART, "- Message of the day -")) {
return -1;
}
for (std::size_t i = 0; i < sizeof(motd) / sizeof(*motd); i++) {
if (msg.getClient().sendCode(RPL_MOTD, motd[i])) {
return -1;
}
}
if (msg.getClient().sendCode(RPL_ENDOFMOTD, "End of MOTD command")) {
return -1;
}
return 0;
}

42
src/Server.NICK.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "Server.hpp"
#include "errcodes.hpp"
int
Server::_cmdNICK(Message const& msg)
{
if (int rv = msg.expectArgs(1)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
static std::string const special("[]\\`_^{|}");
std::string const& nick = msg.getArgs()[0];
std::list<Client>::iterator it = _clients.begin();
for (; it != _clients.end(); ++it) {
if (client == *it) {
continue;
}
if (nick == it->nick) {
return client.sendCode(ERR_NICKNAMEINUSE, nick, "Nickname is already in use");
}
}
if (nick.length() > 9 || nick.length() < 1 ||
(!std::isalpha(nick[0]) && special.find(nick[0]) == special.npos))
{
return client.sendCode(ERR_ERRONEUSNICKNAME, nick, "Erroneous nickname");
}
for (size_t i = 1; i < nick.length(); ++i) {
if (!std::isdigit(nick[i]) && !std::isalpha(nick[i]) &&
special.find(nick[i]) == special.npos && nick[i] != '-')
{
return client.sendCode(ERR_ERRONEUSNICKNAME, nick, "Erroneous nickname");
}
}
client.nick = nick;
if (client.getRegistered() && !client.getWelcomed()) {
return _welcomeClient(client);
}
return 0;
}

30
src/Server.NOTICE.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Server.hpp"
#include "Message.hpp"
int
Server::_cmdNOTICE(Message const& msg)
{
if (msg.getArgs().size() < 2) {
return 0;
}
Client& client = msg.getClient();
std::list<Client>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) {
if (it->nick == msg.getArgs()[0]) {
if (it->sendMsg(client.nick, "NOTICE " + it->nick, msg.getArgs()[1])
&& it->nick == client.nick) {
return 1;
}
return 0;
}
}
for (size_t i = 0; i < _channels.size(); ++i) {
if (_channels[i].getName() == msg.getArgs()[0]) {
_channels[i].sendNotice(msg.getArgs()[1], client);
return 0;
}
}
return 0;
}

43
src/Server.PART.cpp Normal file
View file

@ -0,0 +1,43 @@
#include "Server.hpp"
#include "split.hpp"
#include "errcodes.hpp"
#include <algorithm>
int
Server::_cmdPART(Message const& msg)
{
if (int rv = msg.expectArgs(1)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
std::string const& reason = (msg.getArgs().size() > 1) ? msg.getArgs()[1] : "";
string_vector const& channels = split(msg.getArgs()[0], ",", true);
for (size_t i = 0; i < channels.size(); ++i) {
std::vector<Channel>::iterator it;
it = std::find(_channels.begin(), _channels.end(), channels[i]);
if (it == _channels.end()) {
if (client.sendCode(ERR_NOSUCHCHANNEL, channels[i], "No such channel")) {
return 1;
}
continue;
}
if (!it->isOnChannel(client)) {
if (client.sendCode(ERR_NOTONCHANNEL, channels[i], "You're not on that channel")) {
return 1;
}
continue;
}
it->partClient(client, reason);
if (it->isChannelEmpty()) {
_channels.erase(it);
std::list<Client>::iterator itC;
for (itC = _clients.begin(); itC != _clients.end(); ++itC) {
itC->isInvited(*it);
}
}
}
return 0;
}

15
src/Server.PING.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "Server.hpp"
int
Server::_cmdPING(Message const& msg)
{
Client& client = msg.getClient();
string_vector const& args = msg.getArgs();
std::string out = "PONG";
for (size_t i = 0; i < args.size(); ++i) {
out += ' ' + args[i];
}
out += "\r\n";
return client.sendRaw(out);
}

34
src/Server.PRIVMSG.cpp Normal file
View file

@ -0,0 +1,34 @@
#include "Server.hpp"
#include "split.hpp"
#include "errcodes.hpp"
int
Server::_cmdPRIVMSG(Message const& msg)
{
string_vector const& args = msg.getArgs();
Client& client = msg.getClient();
if (args.empty()) {
return client.sendCode(ERR_NORECIPIENT, ":No recipient given (PRIVMSG)");
}
if (args.size() < 2) {
return client.sendCode(ERR_NOTEXTTOSEND, ":No text to send");
}
string_vector const& recipients = split(args[0], ",", true);
for (size_t i = 0; i < recipients.size(); ++i) {
std::list<Client>::iterator itClient;
for (itClient = _clients.begin(); itClient != _clients.end(); ++itClient) {
if (itClient->nick == recipients[i]) {
itClient->sendMsg(client.nick, "PRIVMSG " + itClient->nick, args[1]);
continue;
}
}
for (size_t j = 0; j < _channels.size(); ++j) {
if (_channels[j].getName() == recipients[i]) {
_channels[j].sendMessage(args[1], &client);
continue;
}
}
}
return 0;
}

37
src/Server.QUIT.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "Server.hpp"
#include "errcodes.hpp"
int
Server::_cmdQUIT(Message const& msg)
{
Client& client = msg.getClient();
std::string quit;
if (msg.getArgs().empty()) {
quit = client.username;
} else {
quit = msg.getArgs()[0];
}
std::list<Client>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) {
for (size_t i = 0; i < _channels.size(); ++i) {
if (_channels[i].areOnChannel(client, *it)) {
it->sendMsg(client.nick, "QUIT", quit);
goto next_client;
}
}
next_client:;
}
for (size_t i = 0; i < _channels.size(); ++i) {
_channels[i].removeClient(client);
if (_channels[i].isChannelEmpty()) {
_channels.erase(_channels.begin() + i);
std::list<Client>::iterator itC;
for (itC = _clients.begin(); itC != _clients.end(); ++itC) {
itC->isInvited(_channels[i]);
}
}
}
removeClient(client);
return 1;
}

30
src/Server.TOPIC.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Server.hpp"
#include "split.hpp"
#include "errcodes.hpp"
#include <algorithm>
int
Server::_cmdTOPIC(Message const& msg)
{
if (int rv = msg.expectArgs(1)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
string_vector const& args = msg.getArgs();
std::vector<Channel>::iterator itChannel;
itChannel = std::find(_channels.begin(), _channels.end(), args[0]);
if (itChannel == _channels.end()) {
return client.sendCode(ERR_NOSUCHCHANNEL, args[0], "No such channel");
}
if (!itChannel->isOnChannel(client)) {
return client.sendCode(ERR_NOTONCHANNEL, args[0],
"You're not on that channel");
}
if (args.size() < 2) {
return itChannel->sendTopic(client);
} else {
return itChannel->setTopic(client, args[1]);
}
}

24
src/Server.USER.cpp Normal file
View file

@ -0,0 +1,24 @@
#include "Server.hpp"
#include "errcodes.hpp"
int
Server::_cmdUSER(Message const& msg)
{
if (int rv = msg.expectArgs(4)) {
return rv == -1 ? rv : 0;
}
Client& client = msg.getClient();
if (client.getRegistered()) {
return client.sendCode(ERR_ALREADYREGISTRED);
}
client.username = msg.getArgs()[0];
client.realname = msg.getArgs()[3];
client.setRegistered();
if (!client.nick.empty()) {
return _welcomeClient(client);
}
return 0;
}

302
src/Server.cpp Normal file
View file

@ -0,0 +1,302 @@
#include "Server.hpp"
#include "Client.hpp"
#include "Message.hpp"
#include "error.hpp"
#include "errcodes.hpp"
#include "log.hpp"
#include <cstddef>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <algorithm>
#include <unistd.h>
#include <string>
#include <cstring>
Server::Server(int port, const std::string& keyword) :
_port(port),
_keyword(keyword),
_efd(-1),
_socket(-1),
_dead(false),
_clients(),
_channels()
{}
Server::~Server()
{
_dead = true;
if (_socket >= 0) {
close(_socket);
_socket = -1;
}
if (_efd >= 0) {
close(_efd);
_socket = -1;
}
}
int
Server::init()
{
_efd = epoll_create('U'+'w'+'U');
if (_efd < 0) {
error("epoll_create");
return -1;
}
_socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket < 0) {
error("socket");
return -1;
}
int option = 1;
if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) {
error("setsockopt");
return -1;
}
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(_port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
error("bind");
return -1;
}
if (listen(_socket, 128) < 0) {
error("listen");
return -1;
}
return _addFd(_socket);
}
int
Server::_addFd(int fd) const
{
epoll_event event = {};
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(_efd, EPOLL_CTL_ADD, fd, &event)) {
error("epoll_ctl");
return -1;
}
return 0;
}
int
Server::wait()
{
epoll_event event = {};
const int ev = epoll_wait(_efd, &event, 1, -1);
if (ev < 0) {
error("epoll_wait");
return -1;
}
const int fd = event.data.fd;
if (fd == _socket) {
if (_accept(fd)) {
return -1;
}
return 0;
}
return _receiveMsg(fd);
}
int
Server::_accept(int fd)
{
_clients.push_back(Client(*this));
Client& client = _clients.back();
if (client.init(fd)) {
_clients.pop_back();
return -1;
}
return _addFd(client.getFd());
}
int
Server::_receiveMsg(int fd)
{
std::list<Client>::iterator client;
for (client = _clients.begin(); client != _clients.end(); ++client) {
if (client->getFd() == fd) {
break;
}
}
if (client == _clients.end()) {
LOG << "no client has this fd" << std::endl;
return -1;
}
if (client->recv()) {
return 0;
}
while (_processMsg(*client) == 0) {}
return 0;
}
int
Server::_processMsg(Client &client)
{
if (!client.queuedMsg()) {
return 1;
}
const Message msg(client, client.pollMsg());
LOG << msg << std::endl;
if (msg.getCmd().empty()) {
return 0;
}
const std::string& cmd = msg.getCmd();
if (!client.getPass()) {
if (cmd != "PASS" || msg.getArgs().empty() || msg.getArgs()[0] != _keyword) {
client.fatalSendCode(ERR_PASSWDMISMATCH);
return 1;
}
client.setPass();
return 0;
}
if (cmd == "PASS") {
return client.sendCode(ERR_ALREADYREGISTRED);
}
if (cmd == "NICK") {
return _cmdNICK(msg);
}
if (cmd == "USER") {
return _cmdUSER(msg);
}
if (!client.getRegistered() || client.nick.empty()) {
return client.sendCode(ERR_NOTREGISTERED);
}
if (cmd == "QUIT") {
return _cmdQUIT(msg);
}
if (cmd == "PART") {
return _cmdPART(msg);
}
if (cmd == "JOIN") {
return _cmdJOIN(msg);
}
if (cmd == "PRIVMSG") {
return _cmdPRIVMSG(msg);
}
if (cmd == "NOTICE") {
return _cmdNOTICE(msg);
}
if (cmd == "KICK") {
return _cmdKICK(msg);
}
if (cmd == "TOPIC") {
return _cmdTOPIC(msg);
}
if (cmd == "PING") {
return _cmdPING(msg);
}
if (cmd == "MODE") {
return _cmdMODE(msg);
}
if (cmd == "INVITE") {
return _cmdINVITE(msg);
}
if (cmd == "MOTD") {
return _cmdMOTD(msg);
}
client.sendCode(ERR_UNKNOWNCOMMAND, cmd, "Unknown command");
return 1;
}
void
Server::removeClient(Client &client)
{
_clients.remove(client);
}
void
Server::quitClient(Client& client)
{
std::list<Client>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) {
if (client == *it) {
continue;
}
for (size_t i = 0; i < _channels.size(); ++i) {
if (_channels[i].areOnChannel(client, *it)) {
it->sendMsg(client.nick, "QUIT", "Client closed connection");
goto next_client;
}
}
next_client:;
}
for (size_t i = 0; i < _channels.size(); ++i) {
_channels[i].removeClient(client);
if (_channels[i].isChannelEmpty()) {
_channels.erase(_channels.begin() + i);
std::list<Client>::iterator itC;
for (itC = _clients.begin(); itC != _clients.end(); ++itC) {
itC->isInvited(_channels[i]);
}
}
}
removeClient(client);
}
Client*
Server::findClient(const std::string& nick)
{
std::list<Client>::iterator it = std::find(_clients.begin(), _clients.end(), nick);
if (it != _clients.end()) {
return &*it;
}
return NULL;
}
int
Server::_welcomeClient(Client& client)
{
if (client.getWelcomed()) {
return 0;
}
if (client.sendCode(RPL_WELCOME, client.nick, "Welcome to our IRC server bozo")) {
return -1;
}
client.setWelcomed();
return 0;
}
bool
Server::isLive() const
{
return !_dead;
}

57
src/Server.hpp Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include "Client.hpp"
#include "Message.hpp"
#include "Channel.hpp"
#include <string>
#include <list>
#include <vector>
class Client;
class Channel;
class Server {
private:
const int _port;
const std::string _keyword;
int _efd;
int _socket;
bool _dead;
std::list<Client> _clients;
std::vector<Channel> _channels;
int _addFd(int fd) const;
int _accept(int fd);
int _receiveMsg(int fd);
int _processMsg(Client &client);
int _cmdNICK(Message const& msg);
int _cmdUSER(Message const& msg);
int _cmdQUIT(Message const& msg);
int _cmdPART(Message const& msg);
int _cmdJOIN(Message const& msg);
int _cmdPRIVMSG(Message const& msg);
int _cmdNOTICE(Message const& msg);
int _cmdKICK(Message const& msg);
int _cmdMOTD(Message const& msg);
int _cmdTOPIC(Message const& msg);
int _cmdPING(Message const& msg);
int _cmdMODE(Message const& msg);
int _cmdINVITE(Message const& msg);
int _welcomeClient(Client& client);
public:
Server(int port, const std::string& password);
~Server();
int init();
int wait();
void removeClient(Client& client);
void quitClient(Client& client);
Client *findClient(const std::string& nick);
bool isLive() const;
};

96
src/errcodes.hpp Normal file
View file

@ -0,0 +1,96 @@
#pragma once
#define ERR_NOSUCHNICK "401"
#define ERR_NOSUCHSERVER "402"
#define ERR_NOSUCHCHANNEL "403"
#define ERR_CANNOTSENDTOCHAN "404"
#define ERR_TOOMANYCHANNELS "405"
#define ERR_WASNOSUCHNICK "406"
#define ERR_TOOMANYTARGETS "407"
#define ERR_NOSUCHSERVICE "408"
#define ERR_NOORIGIN "409"
#define ERR_NORECIPIENT "411"
#define ERR_NOTEXTTOSEND "412"
#define ERR_NOTOPLEVEL "413"
#define ERR_WILDTOPLEVEL "414"
#define ERR_BADMASK "415"
#define ERR_UNKNOWNCOMMAND "421"
#define ERR_NOMOTD "422"
#define ERR_NOADMININFO "423"
#define ERR_FILEERROR "424"
#define ERR_NONICKNAMEGIVEN "431"
#define ERR_ERRONEUSNICKNAME "432"
#define ERR_NICKNAMEINUSE "433"
#define ERR_NICKCOLLISION "436"
#define ERR_UNAVAILRESOURCE "437"
#define ERR_USERNOTINCHANNEL "441"
#define ERR_NOTONCHANNEL "442"
#define ERR_USERONCHANNEL "443"
#define ERR_NOLOGIN "444"
#define ERR_SUMMONDISABLED "445"
#define ERR_USERSDISABLED "446"
#define ERR_NOTREGISTERED "451", "You have not registered"
#define ERR_NEEDMOREPARAMS "461"
#define ERR_ALREADYREGISTRED "462", "Unauthorized command (already registered)"
#define ERR_NOPERMFORHOST "463"
#define ERR_PASSWDMISMATCH "464", "Password incorrect"
#define ERR_YOUREBANNEDCREEP "465"
#define ERR_YOUWILLBEBANNED "466"
#define ERR_KEYSET "467"
#define ERR_CHANNELISFULL "471"
#define ERR_UNKNOWNMODE "472"
#define ERR_INVITEONLYCHAN "473"
#define ERR_BANNEDFROMCHAN "474"
#define ERR_BADCHANNELKEY "475"
#define ERR_BADCHANMASK "476"
#define ERR_NOCHANMODES "477"
#define ERR_BANLISTFULL "478"
#define ERR_ILLEGALCHANNAME "479"
#define ERR_NOPRIVILEGES "481"
#define ERR_CHANOPRIVSNEEDED "482"
#define ERR_CANTKILLSERVER "483"
#define ERR_RESTRICTED "484"
#define ERR_UNIQOPPRIVSNEEDED "485"
#define ERR_NOOPERHOST "491"
#define ERR_UMODEUNKNOWNFLAG "501"
#define ERR_USERSDONTMATCH "502"
#define RPL_WELCOME "001"
#define RPL_YOURHOST "002"
#define RPL_CREATED "003"
#define RPL_MYINFO "004"
#define RPL_BOUNCE "005"
#define RPL_USERHOST "302"
#define RPL_ISON "303"
#define RPL_AWAY "301"
#define RPL_UNAWAY "305"
#define RPL_NOWAWAY "306"
#define RPL_WHOISUSER "311"
#define RPL_WHOISSERVER "312"
#define RPL_WHOISOPERATOR "313"
#define RPL_WHOISIDLE "317"
#define RPL_ENDOFWHOIS "318"
#define RPL_WHOISCHANNELS "319"
#define RPL_WHOWASUSER "314"
#define RPL_ENDOFWHOWAS "369"
#define RPL_LISTSTART "321"
#define RPL_LIST "322"
#define RPL_LISTEND "323"
#define RPL_UNIQOPIS "325"
#define RPL_CHANNELMODEIS "324"
#define RPL_NOTOPIC "331"
#define RPL_TOPIC "332"
#define RPL_INVITING "341"
#define RPL_SUMMONING "342"
#define RPL_INVITELIST "346"
#define RPL_ENDOFINVITELIST "347"
#define RPL_EXCEPTLIST "348"
#define RPL_ENDOFEXCEPTLIST "349"
#define RPL_VERSION "351"
#define RPL_WHOREPLY "352"
#define RPL_ENDOFWHO "315"
#define RPL_NAMREPLY "353"
#define RPL_LINKS "364"
#define RPL_ENDOFNAMES "366"
#define RPL_MOTD "372"
#define RPL_MOTDSTART "375"
#define RPL_ENDOFMOTD "376"
#define RPL_UMODEIS "221"

9
src/error.cpp Normal file
View file

@ -0,0 +1,9 @@
#include "error.hpp"
#include "log.hpp"
#include <cstring>
#include <cerrno>
void error(const char* msg)
{
LOG << msg << ": " << std::strerror(errno) << std::endl;
}

3
src/error.hpp Normal file
View file

@ -0,0 +1,3 @@
#pragma once
void error(const char* msg);

4
src/log.hpp Normal file
View file

@ -0,0 +1,4 @@
#pragma once
#include <iostream>
#define LOG std::cerr

43
src/main.cpp Normal file
View file

@ -0,0 +1,43 @@
#include <iostream>
#include <cerrno>
#include <cstdlib>
#include <signal.h>
#include "log.hpp"
#include "Server.hpp"
static void sigint_handler(int sig);
static int main_loop = true;
int
main(int argc, char **argv)
{
if (argc != 3) {
LOG << "usage: " << argv[0] << " <port> <password>" << std::endl;
return 1;
}
char *pEnd;
long port = std::strtol(argv[1], &pEnd, 10);
if (errno == ERANGE || *pEnd || port < 0 || port > 0xffff) {
LOG << "incorrect port" << std::endl;
return 1;
}
Server serv(port, argv[2]);
if (serv.init()) {
LOG << "omegasadge :(" << std::endl;
return 1;
}
signal(SIGINT, sigint_handler);
while (main_loop && !serv.wait()) {}
return 0;
}
static void
sigint_handler(int)
{
main_loop = false;
}

30
src/split.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "split.hpp"
string_vector
split(std::string str, const std::string &sep, bool ignore_empty)
{
std::vector<std::string> vec;
bool end_sep = (sep.length() <= str.length() &&
str.substr(str.length() - sep.length()) == sep);
for (;;) {
const size_t find = str.find(sep);
if (find == std::string::npos) {
break;
}
std::string const bit = str.substr(0, find);
if (!ignore_empty || !bit.empty()) {
vec.push_back(bit);
}
str.erase(0, find + sep.length());
}
if (!str.empty()) {
vec.push_back(str);
}
if (!ignore_empty && end_sep) {
vec.push_back("");
}
return vec;
}

7
src/split.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <string>
#include <vector>
typedef std::vector<std::string> string_vector;
string_vector split(std::string str, const std::string& sep, bool ignore_empty=false);

BIN
subject.pdf Normal file

Binary file not shown.