]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Add a replacement for the nationalchars module.
authorSadie Powell <sadie@witchery.services>
Tue, 7 Jan 2020 16:55:25 +0000 (16:55 +0000)
committerSadie Powell <sadie@witchery.services>
Tue, 7 Jan 2020 18:55:35 +0000 (18:55 +0000)
docs/conf/codepages/ascii.conf.example [new file with mode: 0644]
docs/conf/codepages/latin1.conf.example [new file with mode: 0644]
docs/conf/codepages/rfc1459.conf.example [new file with mode: 0644]
docs/conf/codepages/strict-rfc1459.conf.example [new file with mode: 0644]
docs/conf/modules.conf.example
make/template/main.mk
src/modules/m_codepage.cpp [new file with mode: 0644]

diff --git a/docs/conf/codepages/ascii.conf.example b/docs/conf/codepages/ascii.conf.example
new file mode 100644 (file)
index 0000000..7c5ecdd
--- /dev/null
@@ -0,0 +1,37 @@
+# This file contains ASCII codepage rules for use with the codepage module.
+
+<codepage name="ascii">
+
+<cpchars index="45">                        # -
+<cpchars begin="48"  end="57">              # 01234567899
+<cpchars begin="65"  end="90"  front="yes"> # ABCDEFGHIJKLMNOPQRSTUVWXYZ
+<cpchars begin="91"  end="96"  front="yes"> # [\]^_`
+<cpchars begin="97"  end="122" front="yes"> # abcdefghijklmnopqrstuvwxyz
+<cpchars begin="123" end="125" front="yes"> # {|}
+
+<cpcase lower="97"  upper="65"> # a => A
+<cpcase lower="98"  upper="66"> # b => B
+<cpcase lower="99"  upper="67"> # c => C
+<cpcase lower="100" upper="68"> # d => D
+<cpcase lower="101" upper="69"> # e => E
+<cpcase lower="102" upper="70"> # f => F
+<cpcase lower="103" upper="71"> # g => G
+<cpcase lower="104" upper="72"> # h => H
+<cpcase lower="105" upper="73"> # i => I
+<cpcase lower="106" upper="74"> # j => J
+<cpcase lower="107" upper="75"> # k => K
+<cpcase lower="108" upper="76"> # l => L
+<cpcase lower="109" upper="77"> # m => M
+<cpcase lower="110" upper="78"> # n => N
+<cpcase lower="111" upper="79"> # o => O
+<cpcase lower="112" upper="80"> # p => P
+<cpcase lower="113" upper="81"> # q => Q
+<cpcase lower="114" upper="82"> # r => R
+<cpcase lower="115" upper="83"> # s => S
+<cpcase lower="116" upper="84"> # t => T
+<cpcase lower="117" upper="85"> # u => U
+<cpcase lower="118" upper="86"> # v => V
+<cpcase lower="119" upper="87"> # w => W
+<cpcase lower="120" upper="88"> # x => X
+<cpcase lower="121" upper="89"> # y => Y
+<cpcase lower="122" upper="90"> # z => Z
diff --git a/docs/conf/codepages/latin1.conf.example b/docs/conf/codepages/latin1.conf.example
new file mode 100644 (file)
index 0000000..3beb002
--- /dev/null
@@ -0,0 +1,42 @@
+# This file contains ISO 8859-1 codepage rules for use with the codepage module.
+
+<codepage name="latin1">
+
+<cpchars begin="192" end="214" front="yes"> # ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ
+<cpchars begin="216" end="246" front="yes"> # ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö
+<cpchars begin="248" end="255" front="yes"> # øùúûüýþÿ
+
+<cpcase lower="83"  upper="223"> # ß => S
+<cpcase lower="192" upper="224"> # à => À
+<cpcase lower="193" upper="225"> # á => Á
+<cpcase lower="194" upper="226"> # â => Â
+<cpcase lower="195" upper="227"> # ã => Ã
+<cpcase lower="196" upper="228"> # ä => Ä
+<cpcase lower="197" upper="229"> # å => Å
+<cpcase lower="198" upper="230"> # æ => Æ
+<cpcase lower="199" upper="231"> # ç => Ç
+<cpcase lower="200" upper="232"> # è => È
+<cpcase lower="201" upper="233"> # é => É
+<cpcase lower="202" upper="234"> # ê => Ê
+<cpcase lower="203" upper="235"> # ë => Ë
+<cpcase lower="204" upper="236"> # ì => Ì
+<cpcase lower="205" upper="237"> # í => Í
+<cpcase lower="206" upper="238"> # î => Î
+<cpcase lower="207" upper="239"> # ï => Ï
+<cpcase lower="208" upper="240"> # ð => Ð
+<cpcase lower="209" upper="241"> # ñ => Ñ
+<cpcase lower="210" upper="242"> # ò => Ò
+<cpcase lower="211" upper="243"> # ó => Ó
+<cpcase lower="212" upper="244"> # ô => Ô
+<cpcase lower="213" upper="245"> # õ => Õ
+<cpcase lower="214" upper="246"> # ö => Ö
+<cpcase lower="216" upper="248"> # ø => Ø
+<cpcase lower="217" upper="249"> # ù => Ù
+<cpcase lower="218" upper="250"> # ú => Ú
+<cpcase lower="219" upper="251"> # û => Û
+<cpcase lower="220" upper="252"> # ü => Ü
+<cpcase lower="221" upper="253"> # ý => Ý
+<cpcase lower="222" upper="254"> # þ => Þ
+
+# Include the ASCII rules to avoid duplication.
+<include file="examples/codepages/ascii.conf.example">
diff --git a/docs/conf/codepages/rfc1459.conf.example b/docs/conf/codepages/rfc1459.conf.example
new file mode 100644 (file)
index 0000000..32f4530
--- /dev/null
@@ -0,0 +1,41 @@
+# This file contains RFC 1459 codepage rules for use with the codepage module.
+
+<codepage name="rfc1459">
+
+<cpchars index="45">                        # -
+<cpchars begin="48"  end="57">              # 01234567899
+<cpchars begin="65"  end="90"  front="yes"> # ABCDEFGHIJKLMNOPQRSTUVWXYZ
+<cpchars begin="91"  end="96"  front="yes"> # [\]^_`
+<cpchars begin="97"  end="122" front="yes"> # abcdefghijklmnopqrstuvwxyz
+<cpchars begin="123" end="125" front="yes"> # {|}
+
+<cpcase lower="97"  upper="65"> # a => A
+<cpcase lower="98"  upper="66"> # b => B
+<cpcase lower="99"  upper="67"> # c => C
+<cpcase lower="100" upper="68"> # d => D
+<cpcase lower="101" upper="69"> # e => E
+<cpcase lower="102" upper="70"> # f => F
+<cpcase lower="103" upper="71"> # g => G
+<cpcase lower="104" upper="72"> # h => H
+<cpcase lower="105" upper="73"> # i => I
+<cpcase lower="106" upper="74"> # j => J
+<cpcase lower="107" upper="75"> # k => K
+<cpcase lower="108" upper="76"> # l => L
+<cpcase lower="109" upper="77"> # m => M
+<cpcase lower="110" upper="78"> # n => N
+<cpcase lower="111" upper="79"> # o => O
+<cpcase lower="112" upper="80"> # p => P
+<cpcase lower="113" upper="81"> # q => Q
+<cpcase lower="114" upper="82"> # r => R
+<cpcase lower="115" upper="83"> # s => S
+<cpcase lower="116" upper="84"> # t => T
+<cpcase lower="117" upper="85"> # u => U
+<cpcase lower="118" upper="86"> # v => V
+<cpcase lower="119" upper="87"> # w => W
+<cpcase lower="120" upper="88"> # x => X
+<cpcase lower="121" upper="89"> # y => Y
+<cpcase lower="122" upper="90"> # z => Z
+<cpcase lower="123" upper="91"> # { => [
+<cpcase lower="124" upper="92"> # | => \
+<cpcase lower="125" upper="93"> # } => ]
+<cpcase lower="126" upper="94"> # ~ => ^
diff --git a/docs/conf/codepages/strict-rfc1459.conf.example b/docs/conf/codepages/strict-rfc1459.conf.example
new file mode 100644 (file)
index 0000000..b14e477
--- /dev/null
@@ -0,0 +1,40 @@
+# This file contains strict RFC 1459 codepage rules for use with the codepage module.
+
+<codepage name="rfc1459">
+
+<cpchars index="45">                        # -
+<cpchars begin="48"  end="57">              # 01234567899
+<cpchars begin="65"  end="90"  front="yes"> # ABCDEFGHIJKLMNOPQRSTUVWXYZ
+<cpchars begin="91"  end="96"  front="yes"> # [\]^_`
+<cpchars begin="97"  end="122" front="yes"> # abcdefghijklmnopqrstuvwxyz
+<cpchars begin="123" end="125" front="yes"> # {|}
+
+<cpcase lower="97"  upper="65"> # a => A
+<cpcase lower="98"  upper="66"> # b => B
+<cpcase lower="99"  upper="67"> # c => C
+<cpcase lower="100" upper="68"> # d => D
+<cpcase lower="101" upper="69"> # e => E
+<cpcase lower="102" upper="70"> # f => F
+<cpcase lower="103" upper="71"> # g => G
+<cpcase lower="104" upper="72"> # h => H
+<cpcase lower="105" upper="73"> # i => I
+<cpcase lower="106" upper="74"> # j => J
+<cpcase lower="107" upper="75"> # k => K
+<cpcase lower="108" upper="76"> # l => L
+<cpcase lower="109" upper="77"> # m => M
+<cpcase lower="110" upper="78"> # n => N
+<cpcase lower="111" upper="79"> # o => O
+<cpcase lower="112" upper="80"> # p => P
+<cpcase lower="113" upper="81"> # q => Q
+<cpcase lower="114" upper="82"> # r => R
+<cpcase lower="115" upper="83"> # s => S
+<cpcase lower="116" upper="84"> # t => T
+<cpcase lower="117" upper="85"> # u => U
+<cpcase lower="118" upper="86"> # v => V
+<cpcase lower="119" upper="87"> # w => W
+<cpcase lower="120" upper="88"> # x => X
+<cpcase lower="121" upper="89"> # y => Y
+<cpcase lower="122" upper="90"> # z => Z
+<cpcase lower="123" upper="91"> # { => [
+<cpcase lower="124" upper="92"> # | => \
+<cpcase lower="125" upper="93"> # } => ]
index 215f661370d4b06bd189fcf6c8a97353ddfa6c93..29adff840d341de1c38077dba4974689b2cbbd3c 100644 (file)
 # To use, CLONES must be in one of your oper class blocks.
 #<module name="clones">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Codepage module: Allows using a custom 8-bit codepage for nicknames
+# and case mapping.
+#
+# You should include one of the following files to set your codepage:
+#<include file="examples/codepages/ascii.conf.example">
+#<include file="examples/codepages/latin1.conf.example">
+#<include file="examples/codepages/rfc1459.conf.example">
+#<include file="examples/codepages/strict-rfc1459.conf.example">
+#
+# You can also define a custom codepage. For details on how to do this
+# please refer to the docs site:
+# https://docs.inspircd.org/3/modules/codepage
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # Common channels module: Adds user mode +c, which, when set, requires
 # that users must share a common channel with you to PRIVMSG or NOTICE
 # National characters module:
 # 1) Allows using national characters in nicknames.
 # 2) Allows using custom (national) casemapping over the network.
+#
+# This module is incredibly poorly written and documented. You should
+# probably use the codepage module instead for 8-bit codepages.
 #<module name="nationalchars">
 #
 # file - Location of the file which contains casemapping rules. If this
index 1e6ba5cd053fc8e055ee7f360f03fd7d946500b1..f71852aa5d0a5f30649f760f69e344be64b3beda 100644 (file)
@@ -210,6 +210,7 @@ install: target
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(BINPATH)
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(CONPATH)
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(DATPATH)
+       @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(EXAPATH)/codepages
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(EXAPATH)/providers
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(EXAPATH)/services
        @-$(INSTALL) -d -g @GID@ -o @UID@ -m $(INSTMODE_DIR) $(EXAPATH)/sql
@@ -230,6 +231,7 @@ endif
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) docs/conf/*.example $(EXAPATH)
+       -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) docs/conf/codepages/*.example $(EXAPATH)/codepages
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) docs/conf/providers/*.example $(EXAPATH)/providers
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) docs/conf/services/*.example $(EXAPATH)/services
        -$(INSTALL) -g @GID@ -o @UID@ -m $(INSTMODE_TXT) docs/sql/*.sql $(EXAPATH)/sql
diff --git a/src/modules/m_codepage.cpp b/src/modules/m_codepage.cpp
new file mode 100644 (file)
index 0000000..1c3ac02
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2014 Googolplexed <googol@googolplexed.net>
+ *
+ * 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
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+typedef std::bitset<UCHAR_MAX + 1> AllowedChars;
+
+namespace
+{
+       // The characters which are allowed in nicknames.
+       AllowedChars allowedchars;
+
+       // The characters which are allowed at the front of a nickname.
+       AllowedChars allowedfrontchars;
+
+       // The mapping of lower case characters to upper case characters.
+       unsigned char casemap[UCHAR_MAX];
+
+       bool IsValidNick(const std::string& nick)
+       {
+               if (nick.empty() || nick.length() > ServerInstance->Config->Limits.NickMax)
+                       return false;
+
+               for (std::string::const_iterator iter = nick.begin(); iter != nick.end(); ++iter)
+               {
+                       unsigned char chr = static_cast<unsigned char>(*iter);
+
+                       // Check that the character is allowed at the front of the nick.
+                       if (iter == nick.begin() && !allowedfrontchars[chr])
+                               return false;
+
+                       // Check that the character is allowed in the nick.
+                       if (!allowedchars[chr])
+                               return false;
+               }
+
+               return true;
+       }
+}
+
+class ModuleCodepage
+       : public Module
+{
+ private:
+       // The character map which was set before this module was loaded.
+       const unsigned char* origcasemap;
+
+       // The IsNick handler which was set before this module was loaded.
+       TR1NS::function<bool(const std::string&)> origisnick;
+
+       template <typename T>
+       void RehashHashmap(T& hashmap)
+       {
+               T newhash(hashmap.bucket_count());
+               for (typename T::const_iterator i = hashmap.begin(); i != hashmap.end(); ++i)
+                       newhash.insert(std::make_pair(i->first, i->second));
+               hashmap.swap(newhash);
+       }
+
+       void CheckInvalidNick()
+       {
+               const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+               for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter)
+               {
+                       LocalUser* user = *iter;
+                       if (user->nick != user->uuid && !ServerInstance->IsNick(user->nick))
+                               user->ChangeNick(user->uuid);
+               }
+       }
+
+       void CheckRehash(unsigned char* prevmap)
+       {
+               if (!memcmp(prevmap, national_case_insensitive_map, sizeof(origcasemap)))
+                       return;
+
+               RehashHashmap(ServerInstance->Users.clientlist);
+               RehashHashmap(ServerInstance->Users.uuidlist);
+               RehashHashmap(ServerInstance->chanlist);
+       }
+
+ public:
+       ModuleCodepage()
+               : origcasemap(national_case_insensitive_map)
+               , origisnick(ServerInstance->IsNick)
+       {
+       }
+
+       ~ModuleCodepage()
+       {
+               ServerInstance->IsNick = origisnick;
+               CheckInvalidNick();
+
+               national_case_insensitive_map = origcasemap;
+               CheckRehash(casemap);
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               const std::string name = ServerInstance->Config->ConfValue("codepage")->getString("name");
+               if (name.empty())
+                       throw ModuleException("<codepage:name> is a required field!");
+
+               AllowedChars newallowedchars;
+               AllowedChars newallowedfrontchars;
+               ConfigTagList cpchars = ServerInstance->Config->ConfTags("cpchars");
+               for (ConfigIter i = cpchars.first; i != cpchars.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+
+                       unsigned char begin = tag->getUInt("begin", tag->getUInt("index", 0), 1, UCHAR_MAX);
+                       if (!begin)
+                               throw ModuleException("<cpchars> tag without index or begin specified at " + tag->getTagLocation());
+       
+                       unsigned char end = tag->getUInt("end", begin, 1, UCHAR_MAX);
+                       if (begin > end)
+                               throw ModuleException("<cpchars:begin> must be lower than <cpchars:end> at " + tag->getTagLocation());
+
+                       bool front = tag->getBool("front", false);
+                       for (unsigned short pos = begin; pos <= end; ++pos)
+                       {
+                               if (pos == '\n' || pos == '\r' || pos == ' ')
+                               {
+                                       throw ModuleException(InspIRCd::Format("<cpchars> tag contains a forbidden character: %u at %s",
+                                               pos, tag->getTagLocation().c_str()));
+                               }
+
+                               if (front && (pos == ':' || isdigit(pos)))
+                               {
+                                       throw ModuleException(InspIRCd::Format("<cpchars> tag contains a forbidden front character: %u at %s",
+                                               pos, tag->getTagLocation().c_str()));
+                               }
+
+                               newallowedchars.set(pos);
+                               newallowedfrontchars.set(pos, front);
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Marked %u (%c) as allowed (front: %s)",
+                                       pos, pos, front ? "yes" : "no");
+                       }
+               }
+
+               unsigned char newcasemap[UCHAR_MAX];
+               for (size_t i = 0; i < UCHAR_MAX; ++i)
+                       newcasemap[i] = i;
+               ConfigTagList cpcase = ServerInstance->Config->ConfTags("cpcase");
+               for (ConfigIter i = cpcase.first; i != cpcase.second; ++i)
+               {
+                       ConfigTag* tag = i->second;
+
+                       unsigned char lower = tag->getUInt("lower", 0, 1, UCHAR_MAX);
+                       if (!lower)
+                               throw ModuleException("<cpcase:lower> is required at " + tag->getTagLocation());
+
+                       unsigned char upper = tag->getUInt("upper", 0, 1, UCHAR_MAX);
+                       if (!upper)
+                               throw ModuleException("<cpcase:upper> is required at " + tag->getTagLocation());
+
+                       newcasemap[upper] = lower;
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Marked %u (%c) as the lower case version of %u (%c)",
+                               lower, lower, upper, upper);
+               }
+
+               std::swap(allowedchars, newallowedchars);
+               std::swap(allowedfrontchars, newallowedfrontchars);
+               std::swap(casemap, newcasemap);
+
+               ServerInstance->IsNick = &IsValidNick;
+               CheckInvalidNick();
+
+               ServerInstance->Config->CaseMapping = name;
+               national_case_insensitive_map = casemap;
+               CheckRehash(newcasemap);
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               std::stringstream linkdata;
+
+               linkdata << "front=";
+               for (size_t i = 0; i < allowedfrontchars.size(); ++i)
+                       if (allowedfrontchars[i])
+                               linkdata << static_cast<unsigned char>(i);
+
+               linkdata << "&middle=";
+               for (size_t i = 0; i < allowedchars.size(); ++i)
+                       if (allowedchars[i])
+                               linkdata << static_cast<unsigned char>(i);
+
+               linkdata << "&map=";
+               for (size_t i = 0; i < sizeof(casemap); ++i)
+                       if (casemap[i] != i)
+                               linkdata << static_cast<unsigned char>(i) << casemap[i] << ',';
+
+               return Version("Provides support for custom 8-bit codepages", VF_COMMON | VF_VENDOR, linkdata.str());
+       }
+};
+MODULE_INIT(ModuleCodepage)