Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add module GeoLite Citys. #281

Merged
merged 34 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1d5e9af
add new module for profiles link in /whois response.
revrsefr Jul 3, 2024
9441690
remove ulines thing
revrsefr Jul 3, 2024
c36c6e3
Merge branch 'inspircd:master' into master
revrsefr Jul 5, 2024
1907dfe
Create m_ipinfo_io.cpp
revrsefr Jul 5, 2024
a48d250
Update m_ipinfo_io.cpp
revrsefr Jul 5, 2024
db925cc
Update m_ipinfo_io.cpp
revrsefr Jul 5, 2024
651d665
Update m_profileLink.cpp
revrsefr Jul 5, 2024
c74beb1
Update m_ipinfo_io.cpp
revrsefr Jul 5, 2024
b24f15d
Update m_ipinfo_io.cpp
revrsefr Jul 7, 2024
d2e18fc
Update m_ipinfo_io.cpp
revrsefr Jul 7, 2024
92e60bc
Create m_randomidxlines.cpp
revrsefr Jul 9, 2024
18f3d66
Merge branch 'inspircd:master' into master
revrsefr Jul 9, 2024
686662f
Update m_randomidxlines.cpp
revrsefr Jul 9, 2024
f5e1bdf
Update m_randomidxlines.cpp
revrsefr Jul 9, 2024
bb23989
Update m_randomidxlines.cpp
revrsefr Jul 9, 2024
36c0957
Merge branch 'inspircd:master' into master
revrsefr Jul 10, 2024
2ad4274
Update m_censor.cpp
revrsefr Jul 13, 2024
a73f28a
Merge pull request #1 from revrsedev/patch-2
revrsefr Jul 13, 2024
dc10c91
Update m_censor.cpp
revrsefr Jul 13, 2024
c64e5cc
Update m_censor.cpp
revrsefr Jul 13, 2024
c01f4b9
Update m_censor.cpp
revrsefr Jul 13, 2024
e1a7035
Merge branch 'inspircd:master' into master
revrsefr Sep 24, 2024
4b7344f
Merge branch 'inspircd:master' into master
revrsefr Nov 24, 2024
3be7b08
Merge branch 'inspircd:master' into master
revrsefr Nov 30, 2024
9a5d0e8
Merge branch 'inspircd:master' into master
revrsefr Dec 26, 2024
5600f9b
Merge branch 'inspircd:master' into master
revrsefr Jan 6, 2025
17cad1a
Create m_whoisgeolite.cpp
revrsefr Jan 7, 2025
4bf4798
Delete m_whoisgeolite.cpp
revrsefr Jan 7, 2025
cc0a8d6
Create m_whoisgeolite.cpp
revrsefr Jan 7, 2025
f34b784
Update m_censor.cpp
revrsefr Jan 7, 2025
acc6b26
Update m_whoisgeolite.cpp
revrsefr Jan 7, 2025
f7680f8
Update m_whoisgeolite.cpp
revrsefr Jan 7, 2025
23d7e92
Update m_whoisgeolite.cpp
revrsefr Jan 7, 2025
478f716
Update m_whoisgeolite.cpp
revrsefr Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 195 additions & 38 deletions 4/m_censor.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2018 linuxdaemon <[email protected]>
* Copyright (C) 2013, 2017-2018, 2020-2021 Sadie Powell <[email protected]>
* Copyright (C) 2012-2013 Attila Molnar <[email protected]>
* Copyright (C) 2012, 2019 Robby <[email protected]>
* Copyright (C) 2009-2010 Daniel De Graaf <[email protected]>
* Copyright (C) 2008 Thomas Stagner <[email protected]>
* Copyright (C) 2007 Dennis Friis <[email protected]>
* Copyright (C) 2005, 2008 Robin Burchell <[email protected]>
* Copyright (C) 2004, 2006, 2010 Craig Edwards <[email protected]>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Expand All @@ -29,9 +13,19 @@
/// $ModDesc: Allows the server administrator to define inappropriate phrases that are not allowed to be used in private or channel messages.


///$CompilerFlags: find_compiler_flags("icu-uc")
///$LinkerFlags: find_linker_flags("icu-uc")
///$CompilerFlags: find_compiler_flags("icu-i18n")
/// $LinkerFlags: find_linker_flags("icu-i18n")

#include "inspircd.h"
#include "modules/exemption.h"
#include "numerichelper.h"
#include "utility/string.h"
#include <codecvt>
#include <locale>
#include <unicode/regex.h>
#include <unicode/unistr.h>

typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> CensorMap;

Expand All @@ -42,22 +36,166 @@ class ModuleCensor : public Module
CensorMap censors;
SimpleUserMode cu;
SimpleChannelMode cc;
std::unique_ptr<icu::RegexPattern> emoji_pattern;
std::unique_ptr<icu::RegexPattern> whitelist_pattern;
std::unique_ptr<icu::RegexPattern> kiwiirc_pattern;
std::string emoji_regex_str;
std::string whitelist_regex_str;
std::string kiwiirc_regex_str;

bool IsMixedUTF8(const std::string& text)
{
if (text.empty())
return false;

enum ScriptType { SCRIPT_UNKNOWN, SCRIPT_LATIN, SCRIPT_NONLATIN };
ScriptType detected = SCRIPT_UNKNOWN;

for (const auto& c : text)
{
if (static_cast<unsigned char>(c) < 128)
continue; // ASCII characters are ignored

if (std::isalpha(static_cast<unsigned char>(c)))
{
ScriptType current = std::islower(static_cast<unsigned char>(c)) || std::isupper(static_cast<unsigned char>(c)) ? SCRIPT_LATIN : SCRIPT_NONLATIN;
if (detected == SCRIPT_UNKNOWN)
{
detected = current;
}
else if (detected != current)
{
return true; // Mixed scripts detected
}
}
}

return false;
}

// Helper function to convert UTF-8 string to UTF-32
std::u32string to_utf32(const std::string& utf8)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.from_bytes(utf8);
}

// Helper function to convert UTF-32 character to UTF-8
std::string to_utf8(char32_t utf32_char)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.to_bytes(utf32_char);
}

bool IsEmojiOnly(const std::string& text)
{
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString ustr(text.c_str(), "UTF-8");
std::unique_ptr<icu::RegexMatcher> emoji_matcher(emoji_pattern->matcher(ustr, status));
if (U_FAILURE(status))
{
ServerInstance->Logs.Normal(MODNAME, "Failed to create regex matcher for emojis: %s", u_errorName(status));
return false;
}

// Check if the entire text is matched by the emoji pattern
return emoji_matcher->matches(status);
}

bool IsKiwiIRCOnly(const std::string& text)
{
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString ustr(text.c_str(), "UTF-8");
std::unique_ptr<icu::RegexMatcher> kiwiirc_matcher(kiwiirc_pattern->matcher(ustr, status));
if (U_FAILURE(status))
{
ServerInstance->Logs.Normal(MODNAME, "Failed to create regex matcher for KiwiIRC: %s", u_errorName(status));
return false;
}

// Check if the entire text is matched by the KiwiIRC pattern
return kiwiirc_matcher->matches(status);
}

bool IsAllowed(const std::string& text)
{
// Allow ASCII characters and common symbols by default
if (std::all_of(text.begin(), text.end(), [](unsigned char c) { return c >= 32 && c <= 126; }))
{
return true;
}

UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString ustr(text.c_str(), "UTF-8");
std::unique_ptr<icu::RegexMatcher> whitelist_matcher(whitelist_pattern->matcher(ustr, status));
if (U_FAILURE(status))
{
ServerInstance->Logs.Normal(MODNAME, "Failed to create regex matcher for whitelist: %s", u_errorName(status));
return false;
}

return whitelist_matcher->matches(status) || IsEmojiOnly(text) || IsKiwiIRCOnly(text);
}

public:
ModuleCensor()
: Module(VF_NONE, "Allows the server administrator to define inappropriate phrases that are not allowed to be used in private or channel messages.")
: Module(VF_NONE, "Allows the server administrator to define inappropriate phrases that are not allowed to be used in private or channel messages and blocks messages with mixed UTF-8 scripts, only allowing certain Unicode smileys.")
, exemptionprov(this)
revrsefr marked this conversation as resolved.
Show resolved Hide resolved
, cu(this, "u_censor", 'G')
, cc(this, "censor", 'G')
{
}

// format of a config entry is <badword text="shit" replace="poo">
void ReadConfig(ConfigStatus& status) override
{
CensorMap newcensors;
for (const auto& [_, badword_tag] : ServerInstance->Config->ConfTags("badword"))
{
const std::string text = badword_tag->getString("text");
if (text.empty())
throw ModuleException(this, "<badword:text> is empty! at " + badword_tag->source.str());

const std::string replace = badword_tag->getString("replace");
newcensors[text] = replace;
}
censors.swap(newcensors);

const auto& tag = ServerInstance->Config->ConfValue("censorplus");
emoji_regex_str = tag->getString("emojiregex", "^[\\p{Emoji}]+$");
whitelist_regex_str = tag->getString("whitelistregex", "^[\\p{Latin}\\p{Common} ]+$");
kiwiirc_regex_str = tag->getString("kiwiircregex", "[:;][-~]?[)DdpP]|O[:;]3");

UErrorCode icu_status = U_ZERO_ERROR;
emoji_pattern = std::unique_ptr<icu::RegexPattern>(icu::RegexPattern::compile(icu::UnicodeString::fromUTF8(emoji_regex_str), 0, icu_status));
if (U_FAILURE(icu_status))
{
throw ModuleException(this, INSP_FORMAT("Failed to compile emoji regex pattern: {}", u_errorName(icu_status)));
}

icu_status = U_ZERO_ERROR;
whitelist_pattern = std::unique_ptr<icu::RegexPattern>(icu::RegexPattern::compile(icu::UnicodeString::fromUTF8(whitelist_regex_str), 0, icu_status));
if (U_FAILURE(icu_status))
{
throw ModuleException(this, INSP_FORMAT("Failed to compile whitelist regex pattern: {}", u_errorName(icu_status)));
}

icu_status = U_ZERO_ERROR;
kiwiirc_pattern = std::unique_ptr<icu::RegexPattern>(icu::RegexPattern::compile(icu::UnicodeString::fromUTF8(kiwiirc_regex_str), 0, icu_status));
if (U_FAILURE(icu_status))
{
throw ModuleException(this, INSP_FORMAT("Failed to compile KiwiIRC regex pattern: {}", u_errorName(icu_status)));
}
}

ModResult OnUserPreMessage(User* user, MessageTarget& target, MessageDetails& details) override
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;

// Allow IRC operators to bypass the restrictions
if (user->IsOper())
return MOD_RES_PASSTHRU;

switch (target.type)
{
case MessageTarget::TYPE_USER:
Expand All @@ -84,14 +222,52 @@ class ModuleCensor : public Module
return MOD_RES_PASSTHRU;
}

if (IsMixedUTF8(details.text) || !IsAllowed(details.text))
{
const std::string msg = "Your message contained disallowed characters and was blocked. IRC operators have been notified (Spamfilter purpose).";

// Announce to opers
std::string oper_announcement;
if (target.type == MessageTarget::TYPE_CHANNEL)
{
auto* targchan = target.Get<Channel>();
oper_announcement = INSP_FORMAT("MixedCharacterUTF8 notice: User {} in channel {} sent a message containing disallowed characters: '{}', which was blocked.", user->nick, targchan->name, details.text);
ServerInstance->SNO.WriteGlobalSno('a', oper_announcement);
user->WriteNumeric(Numerics::CannotSendTo(targchan, msg));
}
else
{
auto* targuser = target.Get<User>();
oper_announcement = INSP_FORMAT("MixedCharacterUTF8 notice: User {} sent a private message to {} containing disallowed characters: '{}', which was blocked.", user->nick, targuser->nick, details.text);
ServerInstance->SNO.WriteGlobalSno('a', oper_announcement);
user->WriteNumeric(Numerics::CannotSendTo(targuser, msg));
}
return MOD_RES_DENY;
}

for (const auto& [find, replace] : censors)
{
size_t censorpos;
while ((censorpos = irc::find(details.text, find)) != std::string::npos)
{
if (replace.empty())
{
const std::string msg = INSP_FORMAT("Your message to this channel contained a banned phrase ({}) and was blocked.", find);
const std::string msg = INSP_FORMAT("Your message to this channel contained a banned phrase ({}) and was blocked. IRC operators have been notified (Spamfilter purpose).", find);

// Announce to opers
std::string oper_announcement;
if (target.type == MessageTarget::TYPE_CHANNEL)
{
auto* targchan = target.Get<Channel>();
oper_announcement = INSP_FORMAT("BannedPhrase notice: User {} in channel {} sent a message containing banned phrase ({}): '{}', which was blocked.", user->nick, targchan->name, find, details.text);
}
else
{
auto* targuser = target.Get<User>();
oper_announcement = INSP_FORMAT("BannedPhrase notice: User {} sent a private message to {} containing banned phrase ({}): '{}', which was blocked.", user->nick, targuser->nick, find, details.text);
}
ServerInstance->SNO.WriteGlobalSno('a', oper_announcement);

if (target.type == MessageTarget::TYPE_CHANNEL)
user->WriteNumeric(Numerics::CannotSendTo(target.Get<Channel>(), msg));
else
Expand All @@ -104,25 +280,6 @@ class ModuleCensor : public Module
}
return MOD_RES_PASSTHRU;
}

void ReadConfig(ConfigStatus& status) override
{
/*
* reload our config file on rehash - we must destroy and re-allocate the classes
* to call the constructor again and re-read our data.
*/
CensorMap newcensors;
for (const auto& [_, tag] : ServerInstance->Config->ConfTags("badword"))
{
const std::string text = tag->getString("text");
if (text.empty())
throw ModuleException(this, "<badword:text> is empty! at " + tag->source.str());

const std::string replace = tag->getString("replace");
newcensors[text] = replace;
}
censors.swap(newcensors);
}
};

MODULE_INIT(ModuleCensor)
Loading
Loading