From 0694c5146cca75d19cd0d511c5077baba2579d17 Mon Sep 17 00:00:00 2001 From: JustinAJ Date: Mon, 4 Jan 2016 00:31:43 -0500 Subject: [PATCH] Revamped ban system internals: * Rewrote the database format using Jupiter::Database * Replaced 'active' with 'flags' in ban entries, adding support for many types of bans * Added 'prefix_length' to entries; netmask banning is now supported internally * Moved 'banner' to ban entry from varData * "bansearch" IRC command now includes type (flags) information General bug fixes and optimization --- Jupiter | 2 +- Release/Bot.lib | Bin 24762 -> 24762 bytes Release/Plugins/RenX.Core.lib | Bin 139062 -> 152658 bytes RenX.Commands/RenX_Commands.cpp | 119 ++++---- RenX.Commands/RenX_Commands.h | 4 +- RenX.Core/RenX_BanDatabase.cpp | 288 +++++++----------- RenX.Core/RenX_BanDatabase.h | 115 ++++++- RenX.Core/RenX_PlayerInfo.h | 3 +- RenX.Core/RenX_Server.cpp | 169 ++++++++-- RenX.Core/RenX_Server.h | 6 +- .../ExcessiveHeadshots.cpp | 18 +- RenX.Ladder/RenX_Ladder.cpp | 5 +- RenX.Logging/RenX_Logging.cpp | 4 +- RenX.Warn/RenX_Warn.cpp | 6 +- 14 files changed, 445 insertions(+), 294 deletions(-) diff --git a/Jupiter b/Jupiter index 5338777..5b83899 160000 --- a/Jupiter +++ b/Jupiter @@ -1 +1 @@ -Subproject commit 5338777b91ca98bfcd4aea3620a69071d42701f0 +Subproject commit 5b83899a03642d18deb6a85dabfb1afd8d76422e diff --git a/Release/Bot.lib b/Release/Bot.lib index ca176ee0f2898592be1d1a8e0c4c5d8d02d3c755..9b08312dc2c7a351fc98b81b162e709de199db45 100644 GIT binary patch delta 1905 zcmZuxJ4{ni7;Xg=g-E5)i>W{n5ELUd;bEzzEqy?Hdns)xP+MAv5GMl;L>(B6A;ySe z^2f;#VQ?~ngpI@pBZDE1BoZd$;2;jh!9+dh{`Z`F+SpF_%lCiZ|2$jU#;tAR(=$h} zbaixe*~lq_*<7>U(Z53$b4Pc#4Sy3SBE}-a;>N?xWcN2Cj|o{I<1iIb ze8y3fhd=2-e6`0a;1cXP&6E${odI1&0;kJdM&OaN27|$9D25-kHL&O{gKTe=NMq4^ zR|}(6(U{9EvR}K*^imZ_F@##}RLQxZS`0^r2M^yNg&#C)Z;E2CUg-`n(4AzoW z2iH#eS4n&hkG7Kb)iMh8VHuVLxDSoo51`NOK!~w5SQTs|g4IOujjajlgIv%iOVF0a z$eAE-`U;uFmmtEwgQHB4+Ox?m^y`xo;GZIFfx!VRb9z816Q}oSKxVqLS%jh?4y}X` z`WWIirZ7UT@U$K%!TT_-Tq~)tp03;}lMVia`D|=5hADE6g^A-jkn`H;j9}HV2v0@q z?rtQnyPXQMWhKhB6)kZQlE5*D%;;cDXG-vP&}^bN>r+Q=SkTl`R$DiwWboy)@*y&dCq zvIBI*`2lM7>TA$X1TT?WhZo1(;2z?eWlv~XCms~!ahw)R^RUBivJpJP9!AhIPze_a z;j&8jJZxhG){6EdZ-qFWog~kiJ_zk2SaY06bhHOZuAz8JKMN$qOBj&MO5Suuuf zxHo!>TMKaW*ni<29cE9|%~atNrCwu>WYj}4Dz6-axj63`&FJwpOj;onC!uVd3l;owB9A88{{g1DOgR7m delta 1905 zcmZuxJxr5P6m2aa0o)N;A8{|8;L(184PhGkuVtt2XQbCB^8(=bm$a-%dfjQ&2xUS9Q&5 zw)9zGAtP;BCynegA8Oj(Fy1SE`waR%i^YV$nVO(lqdKlx*-lu#t9gvi0BNgEL-Cwd zQ-VJoKzx0`DBu$8+w_zVKWv_&j085jzLvlf=T$0|T2%o*o15UVs}?eY4I+(2?_V#E zmPMm>hsb_o*Vj?eb|P9q{-D0DwknF{2F5ze|o49@!`*mWU%=yHp& zV+i5IP)Y4#Lr;^2e$*Ht?dH>`f`lR`YIT=Hl8(smYqzcFV}KLG4KjRo*veofS$S~1 zq<{6q=cuZiw6B>_s1M7q(!+gd@Hrj$%bu`FR5xct= zEa`542-$&9glmVC#6?H~YZ#f4aJ0yj;9Xd+qc>$G$#;?D9kX+D5KZ;NQmm2j%n?+a zj@)99&M(1c3>AMzcsnS3e+=Q(v6~`W8^sqPK#Y3G9uy42(2}eceQ2F);}~6t@;cc8 z+GG3x6?^$L7$JgJ$gRW6lMZl<@y#;Fm8|0{WqF*U1=Bptu$O2D=eUy*lni9TWkR?r z6TXa_7=g8-H^EyWPG>j4v!)M1?*!HyBT{{&=Jy=|QZru+TE(n#HQy$cVH+My z-r?2)+&T4Mc((|%C+c>x{E3pUF-tNUAQ{!wPr+i0_l#!r|rqMb`tewRAsH+V&rpu=zN48~n rr(V1#oI#OCGrj)+IPX01 diff --git a/Release/Plugins/RenX.Core.lib b/Release/Plugins/RenX.Core.lib index 46f8691434f5fcb83c438fdc4a00dc767dcdd776..fb645884eb11effcd638ceb18d06d383e33f68f3 100644 GIT binary patch literal 152658 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, Jupiter::StringS::empty); } -bool RenX_CommandsPlugin::RenX_OnBan(RenX::Server *server, const RenX::PlayerInfo *player, Jupiter::StringType &data) -{ - data = player->varData.get(this->getName(), STRING_LITERAL_AS_REFERENCE("banner")); - return !data.isEmpty(); -} - void RenX_CommandsPlugin::RenX_OnSuicide(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &) { onDie(server, player); @@ -1122,43 +1116,38 @@ void BanSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString default: case 0: // ANY return isMatch(1) || isMatch(2) || isMatch(3) || isMatch(4); - case 1: // IP + case 1: // ALL + return true; + case 2: // IP return entry->ip == params.asUnsignedInt(); - case 2: // RDNS + case 3: // RDNS return entry->rdns.equals(params); - case 3: // STEAM + case 4: // STEAM return entry->steamid == params.asUnsignedLongLong(); - case 4: // NAME + case 5: // NAME return entry->name.equalsi(params); - case 5: // BANNER + case 6: // BANNER return entry->varData.get(pluginInstance.getName()).equalsi(params); - case 6: // ACTIVE - if (params.asBool()) // Got tired of seeing a compiler warning. - return entry->active == 1; - else - return entry->active == 0; - case 7: // ALL - return true; + case 7: // ACTIVE + return params.asBool() == entry->is_active(); } }; unsigned int type; Jupiter::ReferenceString type_str = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); - if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("ip"))) + if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("all")) || type_str.equals('*')) type = 1; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("rdns"))) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("ip"))) type = 2; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("steam"))) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("rdns"))) type = 3; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("name"))) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("steam"))) type = 4; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("banner"))) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("name"))) type = 5; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("active"))) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("banner"))) type = 6; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("any"))) - type = 0; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("all")) || type_str.equals('*')) + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("active"))) type = 7; else { @@ -1167,17 +1156,43 @@ void BanSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString } Jupiter::String out(256); + Jupiter::String types(64); char timeStr[256]; for (size_t i = 0; i != entries.size(); i++) { entry = entries.get(i); if (isMatch(type)) { - Jupiter::StringS ip_str = Jupiter::Socket::ntop4(entry->ip); + Jupiter::StringS &ip_str = Jupiter::Socket::ntop4(entry->ip); const Jupiter::ReadableString &banner = entry->varData.get(pluginInstance.getName()); strftime(timeStr, sizeof(timeStr), "%b %d %Y; Time: %H:%M:%S", localtime(&(entry->timestamp))); - out.format("ID: %lu; Status: %sactive; Date: %s; IP: %.*s; Steam: %llu; Name: %.*s%s", i, entry->active ? "" : "in", timeStr, ip_str.size(), ip_str.ptr(), entry->steamid, entry->name.size(), entry->name.ptr(), banner.isEmpty() ? "" : "; Banner: "); - out.concat(banner); + + if ((entry->flags & 0x7F) == 0) + types = " NULL;"_jrs; + else + { + types.erase(); + if (entry->is_type_game()) + types += " game"_jrs; + if (entry->is_type_chat()) + types += " chat"_jrs; + if (entry->is_type_bot()) + types += " bot"_jrs; + if (entry->is_type_vote()) + types += " vote"_jrs; + if (entry->is_type_mine()) + types += " mine"_jrs; + if (entry->is_type_ladder()) + types += " ladder"_jrs; + if (entry->is_type_alert()) + types += " alert"_jrs; + types += ";"_jrs; + } + + out.format("ID: %lu (%sactive); Date: %s; IP: %.*s/%u; Steam: %llu; Types:%.*s Name: %.*s; Banner: %.*s", + i, entry->is_active() ? "" : "in", timeStr, ip_str.size(), ip_str.ptr(), entry->prefix_length, entry->steamid, types.size(), types.ptr(), + entry->name.size(), entry->name.ptr(), banner.size(), banner.ptr()); + if (entry->rdns.isNotEmpty()) { out.concat("; RDNS: "_jrs); @@ -1883,8 +1898,7 @@ void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & player = server->getPlayerByPartName(name); if (player != nullptr) { - player->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), nick); - server->banPlayer(player, reason, pluginInstance.getTBanTime()); + server->banPlayer(player, banner, reason, pluginInstance.getTBanTime()); kicks++; } } @@ -1941,8 +1955,7 @@ void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & player = server->getPlayerByPartName(name); if (player != nullptr) { - player->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), nick); - server->banPlayer(player, reason); + server->banPlayer(player, banner, reason); kicks++; } } @@ -2519,37 +2532,29 @@ void ModRequestGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *play Jupiter::IRC::Client::Channel *channel; unsigned int channelCount; unsigned int messageCount = 0; - int type; Jupiter::String &fmtName = RenX::getFormattedPlayerName(player); Jupiter::StringL msg = Jupiter::StringL::Format(IRCCOLOR "12[Moderator Request] " IRCCOLOR IRCBOLD "%.*s" IRCBOLD IRCCOLOR "07 has requested assistance in-game; please look in ", fmtName.size(), fmtName.ptr()); Jupiter::StringS msg2 = Jupiter::StringS::Format(IRCCOLOR "12[Moderator Request] " IRCCOLOR IRCBOLD "%.*s" IRCBOLD IRCCOLOR "07 has requested assistance in-game!" IRCCOLOR, fmtName.size(), fmtName.ptr()); for (unsigned int a = 0; a < serverCount; a++) { server = serverManager->getServer(a); - if (server != nullptr) + channelCount = server->getChannelCount(); + for (unsigned int b = 0; b < channelCount; b++) { - channelCount = server->getChannelCount(); - for (unsigned int b = 0; b < channelCount; b++) + channel = server->getChannel(b); + if (source->isLogChanType(channel->getType())) { - channel = server->getChannel(b); - if (channel != nullptr) + server->sendMessage(channel->getName(), msg2); + msg += channel->getName(); + for (unsigned int c = 0; c < channel->getUserCount(); c++) { - type = channel->getType(); - if (source->isLogChanType(type)) + if (channel->getUserPrefix(c) != 0 && channel->getUser(c)->getNickname().equals(server->getNickname()) == false) { - server->sendMessage(channel->getName(), msg2); - msg += channel->getName(); - for (unsigned int c = 0; c < channel->getUserCount(); c++) - { - if (channel->getUserPrefix(c) != 0 && channel->getUser(c)->getNickname().equals(server->getNickname()) == false) - { - server->sendMessage(channel->getUser(c)->getUser()->getNickname(), msg); - messageCount++; - } - } - msg -= channel->getName().size(); + server->sendMessage(channel->getUser(c)->getUser()->getNickname(), msg); + messageCount++; } } + msg -= channel->getName().size(); } } } @@ -2807,8 +2812,7 @@ void TempBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: You can not ban higher level moderators.")); else { - target->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), player->name); - source->banPlayer(target, reason, pluginInstance.getTBanTime()); + source->banPlayer(target, player->name, reason, pluginInstance.getTBanTime()); source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Player has been temporarily banned and kicked from the game.")); } } @@ -2850,8 +2854,7 @@ void KickBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: You can not ban higher level moderators.")); else { - target->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), player->name); - source->banPlayer(target, reason); + source->banPlayer(target, player->name, reason); source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Player has been banned and kicked from the game.")); } } diff --git a/RenX.Commands/RenX_Commands.h b/RenX.Commands/RenX_Commands.h index fed419a..a790a1c 100644 --- a/RenX.Commands/RenX_Commands.h +++ b/RenX.Commands/RenX_Commands.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,8 +27,6 @@ class RenX_CommandsPlugin : public RenX::Plugin { public: // RenX::Plugin - bool RenX_OnBan(RenX::Server *server, const RenX::PlayerInfo *player, Jupiter::StringType &data); - void RenX_OnSuicide(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &damageType) override; void RenX_OnKill(RenX::Server *server, const RenX::PlayerInfo *player, const RenX::PlayerInfo *victim, const Jupiter::ReadableString &damageType) override; void RenX_OnDie(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &damageType) override; diff --git a/RenX.Core/RenX_BanDatabase.cpp b/RenX.Core/RenX_BanDatabase.cpp index aec8fdc..851a209 100644 --- a/RenX.Core/RenX_BanDatabase.cpp +++ b/RenX.Core/RenX_BanDatabase.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -30,170 +30,74 @@ RenX::BanDatabase _banDatabase; RenX::BanDatabase *RenX::banDatabase = &_banDatabase; RenX::BanDatabase &RenX::defaultBanDatabase = _banDatabase; -const Jupiter::ReferenceString DEFAULT_REASON = "(No reason information provided)"; -const uint8_t write_version = 2U; - -bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) +void RenX::BanDatabase::process_data(Jupiter::DataBuffer &buffer, FILE *file, fpos_t pos) { - RenX::BanDatabase::filename = fname; - FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "rb"); - if (file != nullptr) - { - RenX::BanDatabase::version = fgetc(file); - while (!feof(file)) - if (fgetc(file) == '\n') - break; - Jupiter::String rdns(128); - Jupiter::String playerName(16); - Jupiter::String key(32); - Jupiter::String value(32); - Jupiter::String reason(128); - if (RenX::BanDatabase::version == 0) - reason = DEFAULT_REASON; - Entry *entry; - int c; - while (!feof(file)) - { - entry = new Entry(); - fgetpos(file, &entry->pos); - fread(&entry->active, 1, 1, file); - fread(&entry->timestamp, sizeof(time_t), 1, file); - fread(&entry->length, sizeof(time_t), 1, file); - fread(&entry->steamid, sizeof(uint64_t), 1, file); - fread(&entry->ip, sizeof(uint32_t), 1, file); - if (feof(file)) - { - delete entry; - break; - } - - // load rdns - if (RenX::BanDatabase::version >= 2) - { - rdns.truncate(rdns.size()); - c = fgetc(file); - while (c != '\n' && c != '\0') - { - if (c == EOF) - { - fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); - break; - } - rdns += c; - c = fgetc(file); - } - entry->rdns = rdns; - } - - // load name - playerName.truncate(playerName.size()); - c = fgetc(file); - while (c != '\n' && c != '\0') - { - if (c == EOF) - { - fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); - break; - } - playerName += c; - c = fgetc(file); - } - entry->name = playerName; - - // load reason - if (RenX::BanDatabase::version >= 1) - { - reason.truncate(reason.size()); - c = fgetc(file); - while (c != '\n' && c != '\0') - { - if (c == EOF) - { - fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); - break; - } - reason += c; - c = fgetc(file); - } - } - entry->reason = reason; - - // load variable data - while (c == '\0') - { - key.truncate(key.size()); - value.truncate(value.size()); + if (RenX::BanDatabase::read_version < 3U) + return; // incompatible database version + + RenX::BanDatabase::Entry *entry = new RenX::BanDatabase::Entry(); + entry->pos = pos; + + // Read data from buffer to entry + entry->flags = buffer.pop(); + entry->timestamp = buffer.pop(); + entry->length = buffer.pop(); + entry->steamid = buffer.pop(); + entry->ip = buffer.pop(); + entry->prefix_length = buffer.pop(); + entry->rdns = buffer.pop(); + entry->name = buffer.pop(); + entry->banner = buffer.pop(); + entry->reason = buffer.pop(); + + // Read varData from buffer to entry + for (size_t varData_entries = buffer.pop(); varData_entries != 0; --varData_entries) + entry->varData.set(buffer.pop(), buffer.pop()); +} - // load key - c = fgetc(file); - while (c != '\0' && c != EOF) - { - key += c; - c = fgetc(file); - } - if (c == EOF) - { - fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); - break; - } +void RenX::BanDatabase::process_header(FILE *file) +{ + int chr = fgetc(file); + if (chr != EOF) + RenX::BanDatabase::read_version = chr; +} - // load value - c = fgetc(file); - while (c != '\n' && c != '\0' && c != EOF) - { - value += c; - c = fgetc(file); - } - if (c == EOF) - { - fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); - break; - } +void RenX::BanDatabase::create_header(FILE *file) +{ + fputc(RenX::BanDatabase::write_version, file); +} - entry->varData.set(key, value); - } - entries.add(entry); - } - fclose(file); - if (RenX::BanDatabase::version != write_version) +void RenX::BanDatabase::process_file_finish(FILE *file) +{ + if (RenX::BanDatabase::read_version < 3) + { + if (freopen(RenX::BanDatabase::filename.c_str(), "wb", file) == nullptr) + puts("FATAL ERROR: UNABLE TO REMOVE UNSUPPORTED BAN DATABASE FILE VERSION"); + else { - file = fopen(RenX::BanDatabase::filename.c_str(), "wb"); - if (file != nullptr) - { - fputc(write_version, file); - fputc('\n', file); - size_t index = 0; - while (index != RenX::BanDatabase::entries.size()) - RenX::BanDatabase::write(RenX::BanDatabase::entries.get(++index), file); - fclose(file); - fprintf(stdout, "Updated BanDatabase file \"%s\" from version %d to %d." ENDL, RenX::BanDatabase::filename.c_str(), RenX::BanDatabase::version, write_version); - } - else - fprintf(stdout, "CRITICAL ERROR: BanDatabase file \"%s\" failed to update from version %d to %d." ENDL, RenX::BanDatabase::filename.c_str(), RenX::BanDatabase::version, write_version); - RenX::BanDatabase::version = write_version; + puts("Warning: Unsupported ban database file version. The database will be removed and rewritten."); + this->create_header(file); + RenX::BanDatabase::read_version = RenX::BanDatabase::write_version; } - return true; } - else +} + +void RenX::BanDatabase::upgrade_database() +{ + FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "wb"); + if (file != nullptr) { - RenX::BanDatabase::version = write_version; - file = fopen(RenX::BanDatabase::filename.c_str(), "ab"); - if (file != nullptr) - { - fputc(RenX::BanDatabase::version, file); - fputc('\n', file); - fclose(file); - return true; - } - return false; + this->create_header(file); + for (size_t index = 0; RenX::BanDatabase::entries.size(); ++index) + RenX::BanDatabase::write(RenX::BanDatabase::entries.get(index), file); + + fclose(file); } } void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry) { - if (RenX::BanDatabase::version != write_version) - "CRITICAL ERROR: COULD NOT WRITE BAN ENTRY TO BAN DATABASE (VERSION MISMATCH)"_jrs.print(stdout); - FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "ab"); + FILE *file = fopen(filename.c_str(), "ab"); if (file != nullptr) { RenX::BanDatabase::write(entry, file); @@ -203,40 +107,50 @@ void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry) void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry, FILE *file) { + Jupiter::DataBuffer buffer; fgetpos(file, &entry->pos); - fwrite(&entry->active, 1, 1, file); - fwrite(&entry->timestamp, sizeof(time_t), 1, file); - fwrite(&entry->length, sizeof(time_t), 1, file); - fwrite(&entry->steamid, sizeof(uint64_t), 1, file); - fwrite(&entry->ip, sizeof(uint32_t), 1, file); - fwrite(entry->rdns.ptr(), sizeof(char), entry->rdns.size(), file); - fputc('\0', file); - fwrite(entry->name.ptr(), sizeof(char), entry->name.size(), file); - fputc('\0', file); - fwrite(entry->reason.ptr(), sizeof(char), entry->reason.size(), file); - for (size_t index = 0; index != entry->varData.size(); ++index) + // push data from entry to buffer + buffer.push(entry->flags); + buffer.push(entry->timestamp); + buffer.push(entry->length); + buffer.push(entry->steamid); + buffer.push(entry->ip); + buffer.push(entry->prefix_length); + buffer.push(entry->rdns); + buffer.push(entry->name); + buffer.push(entry->banner); + buffer.push(entry->reason); + + // push varData from entry to buffer + size_t varData_entries = entry->varData.size(); + buffer.push(varData_entries); + + Jupiter::INIFile::Section::KeyValuePair *pair; + while (varData_entries != 0) { - const Jupiter::INIFile::Section::KeyValuePair *pair = entry->varData.getPair(index); - fputc('\0', file); - fwrite(pair->getKey().ptr(), sizeof(char), pair->getKey().size(), file); - fputc('\0', file); - fwrite(pair->getValue().ptr(), sizeof(char), pair->getValue().size(), file); + pair = entry->varData.getPair(--varData_entries); + buffer.push(pair->getKey()); + buffer.push(pair->getValue()); } - fputc('\n', file); + // push buffer to file + buffer.push_to(file); } -void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length) +void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length, uint8_t flags) { Entry *entry = new Entry(); - entry->active = 1; + entry->set_active(); + entry->flags |= flags; entry->timestamp = time(0); entry->length = length; entry->steamid = player->steamid; entry->ip = player->ip32; + entry->prefix_length = 32U; entry->rdns = player->rdns; entry->name = player->name; + entry->banner = banner; entry->reason = reason; // add plugin data @@ -244,7 +158,26 @@ void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player Jupiter::ArrayList &xPlugins = *RenX::getCore()->getPlugins(); for (size_t i = 0; i < xPlugins.size(); i++) if (xPlugins.get(i)->RenX_OnBan(server, player, pluginData)) - entry->varData.set(xPlugins.get(i)->getName(), pluginData); + entry->varData.set(xPlugins.get(i)->getName(), pluginData); + + entries.add(entry); + RenX::BanDatabase::write(entry); +} + +void RenX::BanDatabase::add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, time_t length, uint8_t flags) +{ + Entry *entry = new Entry(); + entry->set_active(); + entry->flags |= flags; + entry->timestamp = time(0); + entry->length = length; + entry->steamid = steamid; + entry->ip = ip; + entry->prefix_length = prefix_length; + entry->rdns = rdns; + entry->name = name; + entry->banner = banner; + entry->reason = reason; entries.add(entry); RenX::BanDatabase::write(entry); @@ -253,14 +186,14 @@ void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player bool RenX::BanDatabase::deactivate(size_t index) { RenX::BanDatabase::Entry *entry = RenX::BanDatabase::entries.get(index); - if (entry->active) + if (entry->is_active()) { - entry->active = 0; + entry->unset_active(); FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "r+b"); if (file != nullptr) { fsetpos(file, &entry->pos); - fputc(entry->active, file); + fputc(entry->flags, file); fclose(file); } return true; @@ -270,7 +203,7 @@ bool RenX::BanDatabase::deactivate(size_t index) uint8_t RenX::BanDatabase::getVersion() const { - return RenX::BanDatabase::version; + return RenX::BanDatabase::write_version; } const Jupiter::ReadableString &RenX::BanDatabase::getFileName() const @@ -285,7 +218,8 @@ const Jupiter::ArrayList &RenX::BanDatabase::getEntrie RenX::BanDatabase::BanDatabase() { - RenX::BanDatabase::load(Jupiter::IRC::Client::Config->get(STRING_LITERAL_AS_REFERENCE("RenX"), STRING_LITERAL_AS_REFERENCE("BanDB"), STRING_LITERAL_AS_REFERENCE("Bans.db"))); + RenX::BanDatabase::filename = Jupiter::IRC::Client::Config->get(STRING_LITERAL_AS_REFERENCE("RenX"), STRING_LITERAL_AS_REFERENCE("BanDB"), STRING_LITERAL_AS_REFERENCE("Bans.db")); + this->process_file(filename); } RenX::BanDatabase::~BanDatabase() diff --git a/RenX.Core/RenX_BanDatabase.h b/RenX.Core/RenX_BanDatabase.h index 49057de..a82b079 100644 --- a/RenX.Core/RenX_BanDatabase.h +++ b/RenX.Core/RenX_BanDatabase.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include #include +#include "Jupiter/Database.h" #include "Jupiter/String.h" #include "Jupiter/CString.h" #include "Jupiter/ArrayList.h" @@ -39,8 +40,39 @@ namespace RenX /** * @brief Represents the local ban database. */ - class RENX_API BanDatabase + class RENX_API BanDatabase : public Jupiter::Database { + public: // Jupiter::Database + /** + * @brief Processes a chunk of data in a database. + * + * @param buffer Buffer to process + * @param file File being processed + * @param pos position that the buffer starts at in the file + */ + void process_data(Jupiter::DataBuffer &buffer, FILE *file, fpos_t pos) override; + + /** + * @brief Processes the header for a database. + * + * @param file File being processed + */ + void process_header(FILE *file) override; + + /** + * @brief Generates a header for a database. + * + * @param file File being created + */ + void create_header(FILE *file) override; + + /** + * @brief Called when process_file() is successfully completed. + * + * @param file File being processed + */ + void process_file_finish(FILE *file) override; + public: /** * @brief Represents a Ban entry in the database. @@ -48,44 +80,94 @@ namespace RenX struct RENX_API Entry { fpos_t pos; /** Position of the entry in the database */ - unsigned char active; /** 1 if the ban is active, 0 otherwise */ + uint8_t flags /** Flags affecting this ban entry (0 = Active, 1 = Game, 2 = Chat, 3 = Command, 4 = Vote, 5 = Mine, 6 = Ladder, 7 = Alert Mods) */ = 0x00; time_t timestamp /** Time the ban was created */; time_t length /** Duration of the ban; 0 if permanent */; uint64_t steamid /** SteamID of the banned player */; uint32_t ip /** IPv4 address of the banned player */; + uint8_t prefix_length /** Prefix length for the IPv4 address block */; Jupiter::StringS rdns /** RDNS of the banned player */; Jupiter::StringS name /** Name of the banned player */; + Jupiter::StringS banner /** Name of the user who initiated the ban */; Jupiter::StringS reason /** Reason the player was banned */; Jupiter::INIFile::Section varData; /** Variable entry data */ + + static const uint8_t FLAG_ACTIVE = 0x80; + static const uint8_t FLAG_TYPE_GAME = 0x40; + static const uint8_t FLAG_TYPE_CHAT = 0x20; + static const uint8_t FLAG_TYPE_BOT = 0x10; + static const uint8_t FLAG_TYPE_VOTE = 0x08; + static const uint8_t FLAG_TYPE_MINE = 0x04; + static const uint8_t FLAG_TYPE_LADDER = 0x02; + static const uint8_t FLAG_TYPE_ALERT = 0x01; + + inline bool is_active() { return (flags & FLAG_ACTIVE) != 0; }; + inline bool is_type_game() { return (flags & FLAG_TYPE_GAME) != 0; }; + inline bool is_type_chat() { return (flags & FLAG_TYPE_CHAT) != 0; }; + inline bool is_type_bot() { return (flags & FLAG_TYPE_BOT) != 0; }; + inline bool is_type_vote() { return (flags & FLAG_TYPE_VOTE) != 0; }; + inline bool is_type_mine() { return (flags & FLAG_TYPE_MINE) != 0; }; + inline bool is_type_ladder() { return (flags & FLAG_TYPE_LADDER) != 0; }; + inline bool is_type_alert() { return (flags & FLAG_TYPE_ALERT) != 0; }; + inline bool is_type_global() { return ~(flags | 0x01) == 0; }; + + inline void set_active() { flags |= FLAG_ACTIVE; }; + inline void set_type_game() { flags |= FLAG_TYPE_GAME; }; + inline void set_type_chat() { flags |= FLAG_TYPE_CHAT; }; + inline void set_type_bot() { flags |= FLAG_TYPE_BOT; }; + inline void set_type_vote() { flags |= FLAG_TYPE_VOTE; }; + inline void set_type_mine() { flags |= FLAG_TYPE_MINE; }; + inline void set_type_ladder() { flags |= FLAG_TYPE_LADDER; }; + inline void set_type_alert() { flags |= FLAG_TYPE_ALERT; }; + inline void set_type_global() { flags = 0xFF; }; + + inline void unset_active() { flags &= ~FLAG_ACTIVE; }; + inline void unset_type_game() { flags &= ~FLAG_TYPE_GAME; }; + inline void unset_type_chat() { flags &= ~FLAG_TYPE_CHAT; }; + inline void unset_type_bot() { flags &= ~FLAG_TYPE_BOT; }; + inline void unset_type_vote() { flags &= ~FLAG_TYPE_VOTE; }; + inline void unset_type_mine() { flags &= ~FLAG_TYPE_MINE; }; + inline void unset_type_ladder() { flags &= ~FLAG_TYPE_LADDER; }; + inline void unset_type_alert() { flags &= ~FLAG_TYPE_ALERT; }; + inline void unset_type_global() { flags = 0x00; }; }; /** - * @brief Loads a file into the ban system. - * Note: This will generate a database file if none is found. + * @brief Adds a ban entry for a player and immediately writes it to the database. * - * @param fname String containing the name of the file to load - * @return True on success, false otherwise. + * @param server Server the player is playing in + * @param player Data of the player to be banned + * @param length Duration of the ban */ - bool load(const Jupiter::ReadableString &fname); + void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length, uint8_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); /** - * @brief Adds a ban entry for a player and immediately writes it to the database. + * @brief Adds a ban entry for a set of player information and immediately writes it to the database. * - * @param server Server the player is playing in - * @param player Data of the player to be banned + * @param name Name of the player to ban + * @param ip IPv4 address of the player to ban + * @param steamid SteamID of the player to ban + * @param rdns RDNS of the player to ban + * @param banner Person implementing the ban + * @param reason Reason the player is getting banned * @param length Duration of the ban */ - void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length); + void add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, time_t length, uint8_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); /** - * @brief Writes a ban file to the database. + * @brief Upgrades the ban database to the current write_version. + */ + void upgrade_database(); + + /** + * @brief Writes a ban entry to the database. * * @param entry Entry to write to the database. */ void write(Entry *entry); /** - * @brief Writes a ban file to the database. + * @brief Writes a ban entry to the database. * * @param entry Entry to write to the database. * @param file FILE stream to write to. @@ -125,7 +207,10 @@ namespace RenX ~BanDatabase(); private: - uint8_t version; + /** Database version */ + const uint8_t write_version = 3U; + uint8_t read_version = write_version; + Jupiter::CStringS filename; Jupiter::ArrayList entries; }; diff --git a/RenX.Core/RenX_PlayerInfo.h b/RenX.Core/RenX_PlayerInfo.h index 5b4bbf1..d01f511 100644 --- a/RenX.Core/RenX_PlayerInfo.h +++ b/RenX.Core/RenX_PlayerInfo.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -53,6 +53,7 @@ namespace RenX Jupiter::StringS vehicle; uint64_t steamid = 0; uint32_t ip32 = 0; + uint8_t ban_flags = 0; TeamType team = TeamType::Other; int id = 0; bool isBot = false; diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp index 50bbede..c11833d 100644 --- a/RenX.Core/RenX_Server.cpp +++ b/RenX.Core/RenX_Server.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,6 +17,7 @@ */ #include +#include #include "Jupiter/INIFile.h" #include "Jupiter/String.h" #include "ServerManager.h" @@ -378,7 +379,7 @@ void RenX::Server::forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter RenX::Server::forceKickPlayer(player->id, reason); } -void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &reason) +void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason) { if (RenX::Server::rconBan) RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", id, reason.size(), reason.ptr())); @@ -386,22 +387,26 @@ void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &reason) { RenX::PlayerInfo *player = RenX::Server::getPlayer(id); if (player != nullptr) - RenX::Server::banPlayer(player, reason); + RenX::Server::banPlayer(player, banner, reason); } } -void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length) +void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length) { if (RenX::Server::localBan) - RenX::banDatabase->add(this, player, reason, length); + RenX::banDatabase->add(this, player, banner, reason, length); if (length == 0) { if (RenX::Server::rconBan) RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, reason.size(), reason.ptr())); + else if (banner.isNotEmpty()) + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server by %.*s for: %.*s", banner.size(), banner.ptr(), reason.size(), reason.ptr())); else RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server for: %.*s", reason.size(), reason.ptr())); } + else if (banner.isNotEmpty()) + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server by %.*s for the next %d days, %d:%d:%d for: %.*s", banner.size(), banner.ptr(), length / 86400, length % 3600, (length % 3600) / 60, length % 60, reason.size(), reason.ptr())); else RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server for the next %d days, %d:%d:%d for: %.*s", length/86400, length%3600, (length%3600)/60, length%60, reason.size(), reason.ptr())); } @@ -988,7 +993,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) auto onChat = [this](RenX::PlayerInfo *player, const Jupiter::ReadableString &message) { const Jupiter::ReadableString &prefix = this->getCommandPrefix(); - if (message.find(prefix) == 0 && message.size() != prefix.size()) + if ((player->ban_flags & RenX::BanDatabase::Entry::FLAG_TYPE_BOT) == 0 && message.find(prefix) == 0 && message.size() != prefix.size()) { Jupiter::ReferenceString command; Jupiter::ReferenceString parameters; @@ -1028,34 +1033,152 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) isBot = false; id = idToken.asInt(10); }; - auto banCheck = [this](const RenX::PlayerInfo *player) + auto banCheck = [this](RenX::PlayerInfo *player) { const Jupiter::ArrayList &entries = RenX::banDatabase->getEntries(); RenX::BanDatabase::Entry *entry = nullptr; + uint32_t netmask; + + RenX::BanDatabase::Entry *last_to_expire[7]; + for (size_t index = 0; index != sizeof(last_to_expire); ++index) + last_to_expire[index] = nullptr; + + auto handle_type = [entry](RenX::BanDatabase::Entry *&last_to_expire) + { + if (last_to_expire == nullptr) + last_to_expire = entry; + else if (last_to_expire->length == 0) + { + // favor older bans if they're also permanent + if (entry->length == 0 && entry->timestamp < last_to_expire->timestamp) + last_to_expire = entry; + } + else if (entry->length == 0 || entry->timestamp + entry->length > last_to_expire->timestamp + last_to_expire->length) + last_to_expire = entry; + }; + for (size_t i = 0; i != entries.size(); i++) { entry = entries.get(i); - if (entry->active) + if (entry->is_active()) { if (entry->length != 0 && entry->timestamp + entry->length < time(0)) banDatabase->deactivate(i); - else if ((this->localSteamBan && entry->steamid != 0 && entry->steamid == player->steamid) - || (this->localIPBan && entry->ip != 0 && entry->ip == player->ip32) - || (this->localRDNSBan && entry->rdns.isNotEmpty() && entry->rdns.equals(player->rdns)) - || (this->localNameBan && entry->name.isNotEmpty() && entry->name.equalsi(player->name))) + else { - char timeStr[256]; - if (entry->length == 0) - { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(&(entry->timestamp))); - this->forceKickPlayer(player, Jupiter::StringS::Format("You are were permanently banned from the server on %s for: %.*s", timeStr, entry->reason.size(), entry->reason.ptr())); - } + if (entry->prefix_length >= 32) + netmask = 0xFFFFFFFF; else + netmask = Jupiter_prefix_length_to_netmask(entry->prefix_length); + + if ((this->localSteamBan && entry->steamid != 0 && entry->steamid == player->steamid) + || (this->localIPBan && entry->ip != 0 && (entry->ip & netmask) == (player->ip32 & netmask)) + || (this->localRDNSBan && entry->rdns.isNotEmpty() && entry->rdns.equals(player->rdns)) + || (this->localNameBan && entry->name.isNotEmpty() && entry->name.equalsi(player->name))) + { + player->ban_flags |= entry->flags; + if (entry->is_type_game()) + handle_type(last_to_expire[0]); + if (entry->is_type_chat()) + handle_type(last_to_expire[1]); + if (entry->is_type_bot()) + handle_type(last_to_expire[2]); + if (entry->is_type_vote()) + handle_type(last_to_expire[3]); + if (entry->is_type_mine()) + handle_type(last_to_expire[4]); + if (entry->is_type_ladder()) + handle_type(last_to_expire[5]); + if (entry->is_type_alert()) + handle_type(last_to_expire[6]); + } + } + } + } + + char timeStr[256]; + if (last_to_expire[0] != nullptr) // Game ban + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[0]->timestamp + last_to_expire[0]->length))); + if (last_to_expire[0]->length == 0) + this->forceKickPlayer(player, Jupiter::StringS::Format("You were permanently banned from the server on %s for: %.*s", timeStr, last_to_expire[0]->reason.size(), last_to_expire[0]->reason.ptr())); + else + this->forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server until %s for: %.*s", timeStr, last_to_expire[0]->reason.size(), last_to_expire[0]->reason.ptr())); + + player->ban_flags |= RenX::BanDatabase::Entry::FLAG_TYPE_BOT; // implies FLAG_TYPE_BOT + } + else + { + if (last_to_expire[1] != nullptr) // Chat ban + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[1]->timestamp + last_to_expire[1]->length))); + this->mute(player); + if (last_to_expire[1]->length == 0) + this->sendMessage(player, Jupiter::StringS::Format("You were permanently muted on this server on %s for: %.*s", timeStr, last_to_expire[1]->reason.size(), last_to_expire[1]->reason.ptr())); + else + this->sendMessage(player, Jupiter::StringS::Format("You are muted on this server until %s for: %.*s", timeStr, last_to_expire[1]->reason.size(), last_to_expire[1]->reason.ptr())); + + player->ban_flags |= RenX::BanDatabase::Entry::FLAG_TYPE_BOT; // implies FLAG_TYPE_BOT + } + else if (last_to_expire[2] != nullptr) // Bot ban + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[2]->timestamp + last_to_expire[2]->length))); + if (last_to_expire[2]->length == 0) + this->sendMessage(player, Jupiter::StringS::Format("You were permanently bot-muted on this server on %s for: %.*s", timeStr, last_to_expire[2]->reason.size(), last_to_expire[2]->reason.ptr())); + else + this->sendMessage(player, Jupiter::StringS::Format("You are bot-muted on this server until %s for: %.*s", timeStr, last_to_expire[2]->reason.size(), last_to_expire[2]->reason.ptr())); + } + if (last_to_expire[3] != nullptr) // Vote ban + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[3]->timestamp + last_to_expire[3]->length))); + if (last_to_expire[3]->length == 0) + this->sendMessage(player, Jupiter::StringS::Format("You were permanently vote-muted on this server on %s for: %.*s", timeStr, last_to_expire[3]->reason.size(), last_to_expire[3]->reason.ptr())); + else + this->sendMessage(player, Jupiter::StringS::Format("You are vote-muted on this server until %s for: %.*s", timeStr, last_to_expire[3]->reason.size(), last_to_expire[3]->reason.ptr())); + } + if (last_to_expire[4] != nullptr) // Mine ban + { + this->mineBan(player); + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[4]->timestamp + last_to_expire[4]->length))); + if (last_to_expire[4]->length == 0) + this->sendMessage(player, Jupiter::StringS::Format("You were permanently mine-banned on this server on %s for: %.*s", timeStr, last_to_expire[4]->reason.size(), last_to_expire[4]->reason.ptr())); + else + this->sendMessage(player, Jupiter::StringS::Format("You are mine-banned on this server until %s for: %.*s", timeStr, last_to_expire[4]->reason.size(), last_to_expire[4]->reason.ptr())); + } + if (last_to_expire[5] != nullptr) // Ladder ban + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[5]->timestamp + last_to_expire[5]->length))); + if (last_to_expire[5]->length == 0) + this->sendMessage(player, Jupiter::StringS::Format("You were permanently ladder-banned on this server on %s for: %.*s", timeStr, last_to_expire[5]->reason.size(), last_to_expire[5]->reason.ptr())); + else + this->sendMessage(player, Jupiter::StringS::Format("You are ladder-banned on this server until %s for: %.*s", timeStr, last_to_expire[5]->reason.size(), last_to_expire[5]->reason.ptr())); + } + if (last_to_expire[6] != nullptr) // Alert + { + unsigned int serverCount = serverManager->size(); + IRC_Bot *server; + Jupiter::IRC::Client::Channel *channel; + unsigned int channelCount; + Jupiter::String &fmtName = RenX::getFormattedPlayerName(player); + Jupiter::StringL msg = Jupiter::StringL::Format(IRCCOLOR "04[Alert] " IRCCOLOR IRCBOLD "%.*s" IRCBOLD IRCCOLOR " is marked for monitoring by %.*s for: \"%.*s\". Please keep an eye on them in ", fmtName.size(), fmtName.ptr(), last_to_expire[6]->banner.size(), last_to_expire[6]->banner.ptr(), last_to_expire[6]->reason.size(), last_to_expire[6]->reason.ptr()); + Jupiter::StringS msg2 = Jupiter::StringS::Format(IRCCOLOR "04[Alert] " IRCCOLOR IRCBOLD "%.*s" IRCBOLD IRCCOLOR " is marked for monitoring by %.*s for: \"%.*s\"." IRCCOLOR, fmtName.size(), fmtName.ptr(), last_to_expire[6]->banner.size(), last_to_expire[6]->banner.ptr(), last_to_expire[6]->reason.size(), last_to_expire[6]->reason.ptr()); + for (unsigned int a = 0; a < serverCount; a++) + { + server = serverManager->getServer(a); + channelCount = server->getChannelCount(); + for (unsigned int b = 0; b < channelCount; b++) { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(entry->timestamp + entry->length))); - this->forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server until %s for: %.*s", timeStr, entry->reason.size(), entry->reason.ptr())); + channel = server->getChannel(b); + if (this->isAdminLogChanType(channel->getType())) + { + server->sendMessage(channel->getName(), msg2); + msg += channel->getName(); + for (unsigned int c = 0; c < channel->getUserCount(); c++) + if (channel->getUserPrefix(c) != 0 && channel->getUser(c)->getNickname().equals(server->getNickname()) == false) + server->sendMessage(channel->getUser(c)->getUser()->getNickname(), msg); + msg -= channel->getName().size(); + } } - return; } } } @@ -2329,6 +2452,10 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) playerToken = tokens.getToken(5); RenX::PlayerInfo *player = parseGetPlayerOrAdd(playerToken); + + if ((player->ban_flags & RenX::BanDatabase::Entry::FLAG_TYPE_VOTE) != 0) + RenX::Server::sendData(Jupiter::StringS::Format("ccancelvote %.*s\n", teamToken.size(), teamToken.ptr())); + for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnVoteCall(this, team, voteType, player, parameters); onAction(); diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h index 8a842c7..f96ebb2 100644 --- a/RenX.Core/RenX_Server.h +++ b/RenX.Core/RenX_Server.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -339,7 +339,7 @@ namespace RenX * * @param id Player ID of the player to ban. */ - void banPlayer(int id, const Jupiter::ReadableString &reason); + void banPlayer(int id, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason); /** * @brief Bans a player from the server. @@ -347,7 +347,7 @@ namespace RenX * @param player Data of the player to ban. * @param length Duration of the ban (0 for permanent). */ - void banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length = 0); + void banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length = 0); /** * @brief Removes a player's data based on their ID number. diff --git a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp index 372504a..293e389 100644 --- a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp +++ b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,6 +24,8 @@ #include "RenX_PlayerInfo.h" #include "RenX_Functions.h" +using namespace Jupiter::literals; + RenX_ExcessiveHeadshotsPlugin::RenX_ExcessiveHeadshotsPlugin() { RenX_ExcessiveHeadshotsPlugin::OnRehash(); @@ -31,18 +33,18 @@ RenX_ExcessiveHeadshotsPlugin::RenX_ExcessiveHeadshotsPlugin() int RenX_ExcessiveHeadshotsPlugin::OnRehash() { - RenX_ExcessiveHeadshotsPlugin::ratio = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("HeadshotKillRatio"), 0.5); - RenX_ExcessiveHeadshotsPlugin::minKills = Jupiter::IRC::Client::Config->getInt(RenX_ExcessiveHeadshotsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("Kills"), 10); - RenX_ExcessiveHeadshotsPlugin::minKD = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("KillDeathRatio"), 5.0); - RenX_ExcessiveHeadshotsPlugin::minKPS = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("KillsPerSecond"), 0.5); - RenX_ExcessiveHeadshotsPlugin::minFlags = Jupiter::IRC::Client::Config->getInt(RenX_ExcessiveHeadshotsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("Flags"), 4); + RenX_ExcessiveHeadshotsPlugin::ratio = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), "HeadshotKillRatio"_jrs, 0.5); + RenX_ExcessiveHeadshotsPlugin::minKills = Jupiter::IRC::Client::Config->getInt(RenX_ExcessiveHeadshotsPlugin::getName(), "Kills"_jrs, 10); + RenX_ExcessiveHeadshotsPlugin::minKD = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), "KillDeathRatio"_jrs, 5.0); + RenX_ExcessiveHeadshotsPlugin::minKPS = Jupiter::IRC::Client::Config->getDouble(RenX_ExcessiveHeadshotsPlugin::getName(), "KillsPerSecond"_jrs, 0.5); + RenX_ExcessiveHeadshotsPlugin::minFlags = Jupiter::IRC::Client::Config->getInt(RenX_ExcessiveHeadshotsPlugin::getName(), "Flags"_jrs, 4); return 0; } void RenX_ExcessiveHeadshotsPlugin::RenX_OnKill(RenX::Server *server, const RenX::PlayerInfo *player, const RenX::PlayerInfo *victim, const Jupiter::ReadableString &damageType) { if (player->kills < 3) return; - if (damageType.equals("Rx_DmgType_Headshot")) + if (damageType.equals("Rx_DmgType_Headshot"_jrs)) { unsigned int flags = 0; std::chrono::milliseconds game_time = server->getGameTime(player); @@ -56,7 +58,7 @@ void RenX_ExcessiveHeadshotsPlugin::RenX_OnKill(RenX::Server *server, const RenX if (flags >= RenX_ExcessiveHeadshotsPlugin::minFlags) { - server->banPlayer(player, STRING_LITERAL_AS_REFERENCE("Aimbot detected")); + server->banPlayer(player, "Jupiter Bot"_jrs, "Aimbot detected"_jrs); server->sendPubChan(IRCCOLOR "13[Aimbot]" IRCCOLOR " %.*s was banned from the server! Kills: %u - Deaths: %u - Headshots: %u", player->name.size(), player->name.ptr(), player->kills, player->deaths, player->headshots); const Jupiter::ReadableString &steamid = server->formatSteamID(player); server->sendAdmChan(IRCCOLOR "13[Aimbot]" IRCCOLOR " %.*s was banned from the server! Kills: %u - Deaths: %u - Headshots: %u - IP: " IRCBOLD "%.*s" IRCBOLD " - Steam ID: " IRCBOLD "%.*s" IRCBOLD, player->name.size(), player->name.ptr(), player->kills, player->deaths, player->headshots, player->ip.size(), player->ip.ptr(), steamid.size(), steamid.ptr()); diff --git a/RenX.Ladder/RenX_Ladder.cpp b/RenX.Ladder/RenX_Ladder.cpp index 831366f..c13434c 100644 --- a/RenX.Ladder/RenX_Ladder.cpp +++ b/RenX.Ladder/RenX_Ladder.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2015 Jessica James. + * Copyright (C) 2015-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include "RenX_Ladder.h" #include "RenX_Server.h" #include "RenX_PlayerInfo.h" +#include "RenX_BanDatabase.h" using namespace Jupiter::literals; @@ -49,7 +50,7 @@ void RenX_LadderPlugin::updateLadder(RenX::Server *server, const RenX::TeamType for (Jupiter::DLList::Node *node = server->players.getNode(0); node != nullptr; node = node->next) { player = node->data; - if (player->steamid != 0) + if (player->steamid != 0 && (player->ban_flags & RenX::BanDatabase::Entry::FLAG_TYPE_LADDER) == 0) { entry = RenX_LadderPlugin::database.getPlayerEntry(player->steamid); if (entry == nullptr) diff --git a/RenX.Logging/RenX_Logging.cpp b/RenX.Logging/RenX_Logging.cpp index 54435fe..93cd5be 100644 --- a/RenX.Logging/RenX_Logging.cpp +++ b/RenX.Logging/RenX_Logging.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -110,7 +110,7 @@ void RenX_LoggingPlugin::init() Jupiter::StringS::Format(IRCCOLOR "12[Join] " IRCBOLD "%.*s" IRCBOLD " joined the game fighting for the %.*s from " IRCBOLD "%.*s" IRCBOLD " using Steam ID " IRCBOLD "%.*s" IRCBOLD ". Their hostname is: " IRCBOLD "%.*s", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->teamLongTag.size(), RenX::tags->teamLongTag.ptr(), RenX::tags->ipTag.size(), RenX::tags->ipTag.ptr(), RenX::tags->steamTag.size(), RenX::tags->steamTag.ptr(), RenX::tags->rdnsTag.size(), RenX::tags->rdnsTag.ptr())); RenX_LoggingPlugin::joinNoSteamAdminFmt = Jupiter::IRC::Client::Config->get(this->getName(), STRING_LITERAL_AS_REFERENCE("JoinNoSteamAdminFormat"), - Jupiter::StringS::Format(IRCCOLOR "12[Join] " IRCBOLD "%.*s" IRCBOLD " joined the game fighting for the %.*s from " IRCBOLD "%.*s" IRCBOLD ", but is not using Steam. Their hostname is: " IRCBOLD "%.*s", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->teamLongTag.size(), RenX::tags->teamLongTag.ptr(), RenX::tags->ipTag.size(), RenX::tags->ipTag.ptr(), RenX::tags->rdnsTag.size(), RenX::tags->rdnsTag.ptr())); + Jupiter::StringS::Format(IRCCOLOR "12[Join] " IRCBOLD "%.*s" IRCBOLD " joined the game fighting for the %.*s from " IRCBOLD "%.*s" IRCBOLD ", but is " IRCBOLD "not" IRCBOLD " using Steam. Their hostname is: " IRCBOLD "%.*s", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->teamLongTag.size(), RenX::tags->teamLongTag.ptr(), RenX::tags->ipTag.size(), RenX::tags->ipTag.ptr(), RenX::tags->rdnsTag.size(), RenX::tags->rdnsTag.ptr())); RenX_LoggingPlugin::partFmt = Jupiter::IRC::Client::Config->get(this->getName(), STRING_LITERAL_AS_REFERENCE("PartFormat"), Jupiter::StringS::Format(IRCCOLOR "12[Part] " IRCBOLD "%.*s" IRCBOLD " left the %.*s.", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->teamLongTag.size(), RenX::tags->teamLongTag.ptr())); diff --git a/RenX.Warn/RenX_Warn.cpp b/RenX.Warn/RenX_Warn.cpp index 093eb3e..c7f53ee 100644 --- a/RenX.Warn/RenX_Warn.cpp +++ b/RenX.Warn/RenX_Warn.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2014-2015 Jessica James. + * Copyright (C) 2014-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -77,7 +77,7 @@ void WarnIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &cha source->sendNotice(nick, Jupiter::StringS::Format("%.*s has been kicked from the server for exceeding the warning limit (%d warnings).", player->name.size(), player->name.ptr(), warns)); break; default: - server->banPlayer(player, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); + server->banPlayer(player, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); source->sendNotice(nick, Jupiter::StringS::Format("%.*s has been banned from the server for exceeding the warning limit (%d warnings).", player->name.size(), player->name.ptr(), warns)); break; } @@ -187,7 +187,7 @@ void WarnGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, co source->sendMessage(player, Jupiter::StringS::Format("%.*s has been kicked from the server for exceeding the warning limit (%d warnings).", target->name.size(), target->name.ptr(), warns)); break; default: - source->banPlayer(target, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); + source->banPlayer(target, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); source->sendMessage(player, Jupiter::StringS::Format("%.*s has been banned from the server for exceeding the warning limit (%d warnings).", target->name.size(), target->name.ptr(), warns)); break; }