Push project
This commit is contained in:
commit
c15ff7d6e7
11
.editorconfig
Normal file
11
.editorconfig
Normal 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
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/build/
|
||||
/ircserv
|
44
Makefile
Normal file
44
Makefile
Normal 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
3
compile_flags.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
-Wall
|
||||
-Wextra
|
||||
-std=c++98
|
443
src/Channel.cpp
Normal file
443
src/Channel.cpp
Normal 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
57
src/Channel.hpp
Normal 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
209
src/Client.cpp
Normal 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
58
src/Client.hpp
Normal 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
80
src/Message.cpp
Normal 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
23
src/Message.hpp
Normal 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
48
src/Server.INVITE.cpp
Normal 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
65
src/Server.JOIN.cpp
Normal 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
67
src/Server.KICK.cpp
Normal 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
27
src/Server.MODE.cpp
Normal 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
96
src/Server.MOTD.cpp
Normal 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
42
src/Server.NICK.cpp
Normal 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
30
src/Server.NOTICE.cpp
Normal 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
43
src/Server.PART.cpp
Normal 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
15
src/Server.PING.cpp
Normal 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
34
src/Server.PRIVMSG.cpp
Normal 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
37
src/Server.QUIT.cpp
Normal 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
30
src/Server.TOPIC.cpp
Normal 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
24
src/Server.USER.cpp
Normal 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
302
src/Server.cpp
Normal 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
57
src/Server.hpp
Normal 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
96
src/errcodes.hpp
Normal 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
9
src/error.cpp
Normal 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
3
src/error.hpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void error(const char* msg);
|
4
src/log.hpp
Normal file
4
src/log.hpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
#define LOG std::cerr
|
43
src/main.cpp
Normal file
43
src/main.cpp
Normal 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
30
src/split.cpp
Normal 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
7
src/split.hpp
Normal 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
BIN
subject.pdf
Normal file
Binary file not shown.
Loading…
Reference in a new issue