34 #define ERR_SERVER LOG_STREAM(err, log_server)
35 #define LOG_SERVER LOG_STREAM(info, log_server)
36 #define DBG_SERVER LOG_STREAM(debug, log_server)
40 return o <<
"IP: " <<
n.get_ip() <<
41 (
n.get_nick().empty() ?
"" :
" nick: " +
n.get_nick()) <<
42 " reason: '" <<
n.get_reason() <<
"'"
43 " start_time: " <<
n.get_human_start_time() <<
44 " end_time: " <<
n.get_human_end_time() <<
45 " issuer: " <<
n.get_who_banned();
62 return a->get_int_ip() <
b->get_int_ip();
74 , who_banned_(who_banned_default_)
84 const utils::optional<std::chrono::system_clock::time_point>& end_time,
85 const std::string& reason,
86 const std::string& who_banned,
87 const std::string& group,
88 const std::string& nick)
93 , start_time_(std::
chrono::system_clock::now())
95 , who_banned_(who_banned)
111 , who_banned_(who_banned_default_)
125 std::vector<std::string> split_ip =
utils::split(ip,
'.');
126 if (split_ip.size() > 4)
throw banned::error(
"Malformed ip address: " + ip);
128 unsigned int shift = 4*8;
130 const unsigned int complete_part_mask = 0xFF;
131 auto part = split_ip.begin();
132 bool wildcard =
false;
136 if(part == split_ip.end()) {
148 unsigned int part_ip = lexical_cast_default<unsigned int>(*part, complete_part_mask + 1);
149 if(part_ip > complete_part_mask) {
153 ret.first |= (part_ip << shift);
154 ret.second |= (complete_part_mask << shift);
173 nick_ = cfg[
"nick"].str();
191 group_ = cfg[
"group"].str();
243 auto remaining = *
end_time_ - std::chrono::system_clock::now();
249 static constexpr std::chrono::system_clock::time_point epoch;
250 return end_time_.value_or(epoch) >
b.get_end_time().value_or(epoch);
266 return (
ip_ &
mask_ & pair.second) == (pair.first & pair.second &
mask_);
283 auto new_ban = std::make_shared<banned>(
b);
284 assert(
bans_.insert(new_ban).second);
286 if (new_ban->get_end_time())
295 for(
const config&
b : cfg_del->child_range(
"ban")) {
297 auto new_ban = std::make_shared<banned>(
b);
300 ERR_SERVER <<
e.message <<
" while reading deleted bans";
316 for(
const auto&
b :
bans_) {
333 const std::string& duration, std::chrono::system_clock::time_point start_time)
const
335 if(duration.substr(0, 4) ==
"TIME") {
336 auto as_time_t = std::chrono::system_clock::to_time_t(start_time);
337 std::tm* loc = std::localtime(&as_time_t);
339 std::size_t number = 0;
340 for(
auto i = duration.begin() + 4;
i != duration.end(); ++
i) {
346 loc->tm_year = number;
349 loc->tm_mon = number;
352 loc->tm_mday = number;
355 loc->tm_hour = number;
358 loc->tm_min = number;
361 loc->tm_sec = number;
364 LOG_SERVER <<
"Invalid time modifier given: '" << *
i <<
"'.";
370 return {
true, std::chrono::system_clock::from_time_t(std::mktime(loc)) };
373 std::string dur_lower;
377 ERR_SERVER <<
"While parsing ban command duration string, caught an invalid utf8 exception: " <<
e.what();
378 return {
false, utils::nullopt };
381 if(dur_lower ==
"permanent" || duration ==
"0") {
382 return {
true, utils::nullopt };
384 return {
true, start_time + time_itor->second };
386 std::string::const_iterator
i = duration.begin();
388 for (std::string::const_iterator d_end = duration.end();
i != d_end; ++
i) {
391 if (number == -1) number = 0;
394 if (number == -1) number = 1;
399 if (++
i != d_end && tolower(*
i) ==
'e'
400 && ++
i != d_end && tolower(*
i) ==
'a'
401 && ++
i != d_end && tolower(*
i) ==
'r'
402 && ++
i != d_end && tolower(*
i) ==
's') {
407 if (++
i != d_end && tolower(*
i) ==
'i') {
408 if (++
i != d_end && tolower(*
i) ==
'n'
409 && ++
i != d_end && tolower(*
i) ==
'u'
410 && ++
i != d_end && tolower(*
i) ==
't'
411 && ++
i != d_end && tolower(*
i) ==
'e'
412 && ++
i != d_end && tolower(*
i) ==
's') {
414 start_time += std::chrono::minutes{number};
418 if (++
i != d_end && tolower(*
i) ==
'o'
419 && ++
i != d_end && tolower(*
i) ==
'n'
420 && ++
i != d_end && tolower(*
i) ==
't'
421 && ++
i != d_end && tolower(*
i) ==
'h'
422 && ++
i != d_end && tolower(*
i) ==
's') {
428 if (++
i != d_end && tolower(*
i) ==
'a'
429 && ++
i != d_end && tolower(*
i) ==
'y'
430 && ++
i != d_end && tolower(*
i) ==
's') {
436 if (++
i != d_end && tolower(*
i) ==
'o'
437 && ++
i != d_end && tolower(*
i) ==
'u'
438 && ++
i != d_end && tolower(*
i) ==
'r'
439 && ++
i != d_end && tolower(*
i) ==
's') {
441 start_time += std::chrono::hours{number};
444 if (++
i != d_end && tolower(*
i) ==
'o') {
445 if (++
i != d_end && tolower(*
i) ==
'n'
446 && ++
i != d_end && tolower(*
i) ==
't'
447 && ++
i != d_end && tolower(*
i) ==
'h'
448 && ++
i != d_end && tolower(*
i) ==
's') {
454 if (++
i != d_end && tolower(*
i) ==
'i'
455 && ++
i != d_end && tolower(*
i) ==
'n'
456 && ++
i != d_end && tolower(*
i) ==
'u'
457 && ++
i != d_end && tolower(*
i) ==
't'
458 && ++
i != d_end && tolower(*
i) ==
'e'
459 && ++
i != d_end && tolower(*
i) ==
's') {
461 start_time += std::chrono::minutes{number};
465 if (++
i != d_end && tolower(*
i) ==
'e'
466 && ++
i != d_end && tolower(*
i) ==
'c'
467 && ++
i != d_end && tolower(*
i) ==
'o'
468 && ++
i != d_end && tolower(*
i) ==
'n'
469 && ++
i != d_end && tolower(*
i) ==
'd'
470 && ++
i != d_end && tolower(*
i) ==
's') {
472 start_time += std::chrono::seconds{number};
475 return {
false, utils::nullopt };
483 start_time += std::chrono::minutes{number};
486 return {
true, start_time };
491 const utils::optional<std::chrono::system_clock::time_point>& end_time,
492 const std::string& reason,
493 const std::string& who_banned,
494 const std::string& group,
495 const std::string& nick)
497 std::ostringstream ret;
500 if((
ban =
bans_.find(std::make_shared<banned>(ip))) !=
bans_.end()) {
502 ret <<
"Overwriting ban: " << (**ban) <<
"\n";
506 ERR_SERVER <<
e.message <<
" while creating dummy ban for finding existing ban";
511 auto new_ban = std::make_shared<banned>(ip, end_time, reason, who_banned, group, nick);
512 bans_.insert(new_ban);
531 ban =
bans_.find(std::make_shared<banned>(ip));
539 os <<
"There is no ban on '" << ip <<
"'.";
544 os <<
"Ban on '" << **
ban <<
"' removed.";
549 if(immediate_write) {
557 std::insert_iterator<ban_set> temp_inserter(temp, temp.begin());
558 std::remove_copy_if(
bans_.begin(),
bans_.end(), temp_inserter, [&group](
const banned_ptr&
p) { return p->match_group(group); });
560 os <<
"Removed " << (
bans_.size() - temp.size()) <<
" bans";
570 const auto& end_time =
ban->get_end_time();
572 if(!end_time || *end_time > time_now) {
575 <<
"Ban on " <<
ban->get_ip() <<
" not removed."
583 <<
"Removing ban on " <<
ban->get_ip() <<
"."
586 std::ostringstream os;
598 out <<
"No removed bans found.";
606 out <<
"parse error: " <<
e.message;
610 out <<
"DELETED BANS LIST";
612 if((*i)->match_ipmask(pair)) {
613 out <<
"\n" << (**i);
622 out <<
"No bans set.";
630 out <<
"parse error: " <<
e.message;
635 std::set<std::string> groups;
637 for(
const auto&
b :
bans_) {
638 if(
b->get_group().empty()) {
639 if(
b->match_ipmask(pair)) {
643 groups.insert(
b->get_group());
648 if(!groups.empty() && mask ==
"*") {
649 out <<
"\nban groups: ";
651 out << *groups.begin();
652 std::ostream& (*fn)(std::ostream&,
const std::string&) = &std::operator<<;
653 std::for_each(++groups.begin(), groups.end(),
654 std::bind(fn, std::bind(fn, std::ref(out), std::string(
", ")), std::placeholders::_1));
670 const std::string& nick = (*ban)->get_nick();
671 return (*ban)->get_reason() + (nick.empty() ?
"" :
" (" + nick +
")") +
" (Remaining ban duration: " + (*ban)->get_human_time_span() +
")";
676 ban_help_ =
"ban <mask> <time> <reason>\n"
677 "The time format is: %d[%s[%d[%s[...]]]] where %s is a time"
678 " modifier: s or S (seconds), m (minutes), h or H (hours), d"
679 " or D (days), M (months) or y or Y (years) and %d is a number.\n"
680 "Permanent bans can be set with 'permanent' or '0' as the time"
684 ban_help_ +=
"You can also use " + itor->first;
688 ban_help_ += std::string(
", ") + itor->first;
691 ban_help_ +=
" for standard ban times. (not combinable)\n";
693 ban_help_ +=
"ban 127.0.0.1 2h20m flooded lobby\n"
694 "kban suokko 5D flooded again\n"
695 "kban suokko Y One year ban for constant flooding";
703 auto [success, end_time] =
parse_time(bt[
"time"], {});
706 auto duration = end_time.value_or(decltype(end_time)::value_type{}).time_since_epoch();
707 ban_times_.emplace(bt[
"name"], std::chrono::duration_cast<std::chrono::seconds>(duration));
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
bool has_attribute(config_key_type key) const
child_itors child_range(config_key_type key)
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
Thrown by operations encountering invalid UTF-8 data.
std::string ban(const std::string &ip, const utils::optional< std::chrono::system_clock::time_point > &end_time, const std::string &reason, const std::string &who_banned, const std::string &group, const std::string &nick="")
void list_bans(std::ostringstream &out, const std::string &mask="*")
void unban(std::ostringstream &os, const std::string &ip, bool immediate_write=true)
std::pair< bool, utils::optional< std::chrono::system_clock::time_point > > parse_time(const std::string &duration, std::chrono::system_clock::time_point start_time) const
Parses the given duration and adds it to *time except if the duration is '0' or 'permanent' in which ...
void check_ban_times(const std::chrono::system_clock::time_point &time_now)
default_ban_times ban_times_
void unban_group(std::ostringstream &os, const std::string &group)
bool is_digit(const char &c) const
std::size_t to_digit(const char &c) const
ban_time_queue time_queue_
std::string is_ip_banned(const std::string &ip)
deleted_ban_list deleted_bans_
void list_deleted_bans(std::ostringstream &out, const std::string &mask="*") const
void load_config(const config &)
utils::optional< std::chrono::system_clock::time_point > start_time_
bool match_ipmask(const ip_mask &ip) const
std::string get_human_start_time() const
unsigned int get_mask_ip(unsigned int) const
unsigned int mask() const
void read(const config &)
static const std::string who_banned_default_
std::string get_nick() const
bool operator>(const banned &b) const
void write(config &) const
banned(const std::string &ip, const utils::optional< std::chrono::system_clock::time_point > &end_time, const std::string &reason, const std::string &who_banned=who_banned_default_, const std::string &group="", const std::string &nick="")
bool match_ip(const ip_mask &ip) const
std::string get_human_end_time() const
std::string get_human_time_span() const
std::string get_ip() const
utils::optional< std::chrono::system_clock::time_point > end_time_
Definitions for the interface to Wesnoth Markup Language (WML).
Declarations for File-IO.
New lexcical_cast header.
Standard logging facilities (interface).
std::chrono::duration< int, std::ratio< 2629746 > > months
auto serialize_timestamp(const std::chrono::system_clock::time_point &time)
auto parse_timestamp(long long val)
auto format_local_timestamp(const std::chrono::system_clock::time_point &time, std::string_view format="%F %T")
std::chrono::duration< int, std::ratio< 31556952 > > years
std::chrono::duration< int, std::ratio< 86400 > > days
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
static bool file_exists(const bfs::path &fpath)
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
std::unique_ptr< std::ostream > scoped_ostream
std::string format_timespan(const std::chrono::seconds &span)
TODO: we also have utils::format_timespan, which does something very similar...
std::string lowercase(const std::string &s)
Returns a lowercased version of the string.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
std::vector< std::string > split(const config_attribute_value &val)
std::ostream & operator<<(std::ostream &o, const banned &n)
std::set< banned_ptr, banned_compare_subnet > ban_set
static lg::log_domain log_server("server")
std::shared_ptr< banned > banned_ptr
ip_mask parse_ip(const std::string &ip)
std::pair< unsigned int, unsigned int > ip_mask
std::string::const_iterator iterator
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially a gzip_error.
bool operator()(const banned_ptr &a, const banned_ptr &b) const
static compare_fn active_
bool(banned_compare_subnet::* compare_fn)(const banned_ptr &a, const banned_ptr &b) const
bool less(const banned_ptr &a, const banned_ptr &b) const
bool operator()(const banned_ptr &a, const banned_ptr &b) const
static map_location::direction n