1+ /*
2+ * InspIRCd -- Internet Relay Chat Daemon
3+ *
4+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
5+ *
6+ * This file is part of InspIRCd. InspIRCd is free software: you can
7+ * redistribute it and/or modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation, version 2.
9+ *
10+ * This program is distributed in the hope that it will be useful, but WITHOUT
11+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13+ * details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+ // / $ModAuthor: Attila Molnar updated by reverse for v4
20+ // / $ModAuthorMail: attilamolnar@hush.com
21+ // / $ModConfig: <extbanredirect char="d">
22+ // / $ModDepends: core 4
23+ // / $ModDesc: Provide extended ban <extbanchar>:<chan>:<mask> to redirect users to another channel
24+
25+
26+ #include " inspircd.h"
27+ #include " listmode.h"
28+ #include " modules/extban.h"
29+
30+ enum
31+ {
32+ // From UnrealIRCd
33+ ERR_LINKCHANNEL = 470 ,
34+ // From m_banredirect
35+ ERR_REDIRECT = 690
36+ };
37+
38+ class BanWatcher : public ModeWatcher
39+ {
40+ public:
41+ ExtBan::Base& extban;
42+
43+ BanWatcher (Module* parent, ExtBan::Base& xb)
44+ : ModeWatcher(parent, " ban" , MODETYPE_CHANNEL )
45+ , extban(xb)
46+ {
47+ }
48+
49+ bool IsExtBanRedirect (const std::string& mask, std::string& value, bool & inverted)
50+ {
51+ std::string name;
52+ if (!ExtBan::Parse (mask, name, value, inverted))
53+ return false ;
54+
55+ if (inverted)
56+ return false ;
57+
58+ if (name.size () == 1 )
59+ return name[0 ] == extban.GetLetter ();
60+ return irc::equals (name, extban.GetName ());
61+ }
62+
63+ bool BeforeMode (User* source, User*, Channel* channel, Modes::Change& change) override
64+ {
65+ if (!IS_LOCAL (source) || !channel || !change.adding )
66+ return true ;
67+
68+ std::string value;
69+ bool inverted;
70+ if (!IsExtBanRedirect (change.param , value, inverted))
71+ return true ;
72+
73+ const std::string::size_type p = value.find (' :' );
74+ if (p == std::string::npos)
75+ {
76+ source->WriteNumeric (ERR_REDIRECT , INSP_FORMAT (" Extban redirect \" {}\" is invalid. Format: <extban>:<chan>:<mask>" , change.param ));
77+ return false ;
78+ }
79+
80+ const std::string targetname (value, 0 , p);
81+ if (!ServerInstance->Channels .IsChannel (targetname))
82+ {
83+ source->WriteNumeric (ERR_NOSUCHCHANNEL , channel->name , INSP_FORMAT (" Invalid channel name in redirection ({})" , targetname));
84+ return false ;
85+ }
86+
87+ Channel* const targetchan = ServerInstance->Channels .Find (targetname);
88+ if (!targetchan)
89+ {
90+ source->WriteNumeric (ERR_NOSUCHCHANNEL , channel->name , INSP_FORMAT (" Target channel {} must exist to be set as a redirect." , targetname));
91+ return false ;
92+ }
93+
94+ if (targetchan == channel)
95+ {
96+ source->WriteNumeric (ERR_NOSUCHCHANNEL , channel->name , " You cannot set a ban redirection to the channel the ban is on" );
97+ return false ;
98+ }
99+
100+ if (targetchan->GetPrefixValue (source) < OP_VALUE )
101+ {
102+ source->WriteNumeric (ERR_CHANOPRIVSNEEDED , channel->name , INSP_FORMAT (" You must be opped on {} to set it as a redirect." , targetname));
103+ return false ;
104+ }
105+
106+ return true ;
107+ }
108+ };
109+
110+ class ExtBanRedirect final
111+ : public ExtBan::MatchingBase
112+ {
113+ static bool CheckSimpleBan (User* user, const std::string& mask)
114+ {
115+ const auto at = mask.find (' @' );
116+ if (at == std::string::npos)
117+ return false ;
118+
119+ const std::string prefix (mask, 0 , at);
120+ if (!InspIRCd::Match (user->nick + " !" + user->GetDisplayedUser (), prefix) &&
121+ !InspIRCd::Match (user->nick + " !" + user->GetRealUser (), prefix))
122+ {
123+ return false ;
124+ }
125+
126+ const std::string suffix (mask, at + 1 );
127+ return InspIRCd::Match (user->GetRealHost (), suffix) ||
128+ InspIRCd::Match (user->GetDisplayedHost (), suffix) ||
129+ InspIRCd::MatchCIDR (user->GetAddress (), suffix);
130+ }
131+
132+ public:
133+ ExtBanRedirect (Module* Creator)
134+ : MatchingBase(Creator, " redirect" , ' d' )
135+ {
136+ }
137+
138+ void Canonicalize (std::string& text) override
139+ {
140+ const auto p = text.find (' :' );
141+ if (p == std::string::npos)
142+ return ;
143+
144+ std::string chan (text, 0 , p);
145+ std::string mask (text, p + 1 );
146+ ModeParser::CleanMask (mask);
147+ text.assign (chan).append (" :" ).append (mask);
148+ }
149+
150+ bool IsMatch (User* user, Channel* channel, const std::string& text) override
151+ {
152+ const auto p = text.find (' :' );
153+ if (p == std::string::npos)
154+ return false ;
155+
156+ const std::string mask (text, p + 1 );
157+ return CheckSimpleBan (user, mask);
158+ }
159+ };
160+
161+ class ModuleExtBanRedirect : public Module
162+ {
163+ ChanModeReference limitmode;
164+ ChanModeReference limitredirect;
165+ ExtBanRedirect extban;
166+ BanWatcher banwatcher;
167+ bool active;
168+
169+ public:
170+ ModuleExtBanRedirect ()
171+ : Module(VF_VENDOR , " Provide extended ban <extbanchar>:<chan>:<mask> to redirect users to another channel" )
172+ , limitmode(this , " limit" )
173+ , limitredirect(this , " redirect" )
174+ , extban(this )
175+ , banwatcher(this , extban)
176+ , active(false )
177+ {
178+ }
179+
180+ void ReadConfig (ConfigStatus&) override
181+ {
182+ // The extban letter is configured via <extbans redirect="...">.
183+ // This tag is kept for compatibility with older configs.
184+ const auto & tag = ServerInstance->Config ->ConfValue (" extbanredirect" );
185+ const auto confletter = tag->getString (" char" , " " , 1 , 1 );
186+ if (!confletter.empty () && confletter[0 ] != extban.GetLetter ())
187+ ServerInstance->Logs .Debug (MODNAME , " Ignoring <extbanredirect:char>; use <extbans redirect=\" {}\" > instead." , confletter[0 ]);
188+ }
189+
190+ ModResult OnCheckBan (User* user, Channel* chan, const std::string& mask) override
191+ {
192+ LocalUser* localuser = IS_LOCAL (user);
193+
194+ if (active || !localuser)
195+ return MOD_RES_PASSTHRU ;
196+
197+ std::string value;
198+ bool inverted;
199+ if (!banwatcher.IsExtBanRedirect (mask, value, inverted))
200+ return MOD_RES_PASSTHRU ;
201+
202+ if (!extban.IsMatch (localuser, chan, value))
203+ return MOD_RES_PASSTHRU ;
204+
205+ std::string::size_type p = value.find (' :' );
206+ if (p == std::string::npos)
207+ return MOD_RES_PASSTHRU ;
208+
209+ const std::string targetname = value.substr (0 , p);
210+ Channel* const target = ServerInstance->Channels .Find (targetname);
211+ if (target && target->IsModeSet (limitmode))
212+ {
213+ if (target->IsModeSet (limitredirect) && target->GetUsers ().size () >= ConvToNum<size_t >(target->GetModeParameter (limitmode)))
214+ {
215+ // The core will send "You're banned"
216+ return MOD_RES_DENY ;
217+ }
218+ }
219+
220+ // Ok to redirect
221+ // The core will send "You're banned"
222+ localuser->WriteNumeric (ERR_LINKCHANNEL , chan->name , targetname, " You are banned from this channel, so you are automatically being transferred to the redirected channel." );
223+ active = true ;
224+ Channel::JoinUser (localuser, targetname);
225+ active = false ;
226+
227+ return MOD_RES_DENY ;
228+ }
229+ };
230+
231+ MODULE_INIT (ModuleExtBanRedirect)
0 commit comments