]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listmode.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / listmode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
5  *   Copyright (C) 2018 B00mX0r <b00mx0r@aureus.pw>
6  *   Copyright (C) 2017-2019 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2013-2014, 2016 Attila Molnar <attilamolnar@hush.com>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "inspircd.h"
23 #include "listmode.h"
24
25 ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string& eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy)
26         : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST)
27         , listnumeric(lnum)
28         , endoflistnumeric(eolnum)
29         , endofliststring(eolstr)
30         , tidy(autotidy)
31         , extItem(name + "_mode_list", ExtensionItem::EXT_CHANNEL, Creator)
32 {
33         list = true;
34 }
35
36 void ListModeBase::DisplayList(User* user, Channel* channel)
37 {
38         ChanData* cd = extItem.get(channel);
39         if (cd)
40         {
41                 for (ModeList::const_iterator it = cd->list.begin(); it != cd->list.end(); ++it)
42                 {
43                         user->WriteNumeric(listnumeric, channel->name, it->mask, it->setter, (unsigned long) it->time);
44                 }
45         }
46         user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
47 }
48
49 void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
50 {
51         user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
52 }
53
54 void ListModeBase::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
55 {
56         ChanData* cd = extItem.get(channel);
57         if (cd)
58         {
59                 for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
60                 {
61                         changelist.push_remove(this, it->mask);
62                 }
63         }
64 }
65
66 void ListModeBase::DoRehash()
67 {
68         ConfigTagList tags = ServerInstance->Config->ConfTags("maxlist");
69         limitlist newlimits;
70         bool seen_default = false;
71         for (ConfigIter i = tags.first; i != tags.second; i++)
72         {
73                 ConfigTag* c = i->second;
74
75                 const std::string mname = c->getString("mode");
76                 if (!mname.empty() && !stdalgo::string::equalsci(mname, name) && !(mname.length() == 1 && GetModeChar() == mname[0]))
77                         continue;
78
79                 ListLimit limit(c->getString("chan", "*", 1), c->getUInt("limit", DEFAULT_LIST_SIZE));
80
81                 if (limit.mask.empty())
82                         throw ModuleException(InspIRCd::Format("<maxlist:chan> is empty, at %s", c->getTagLocation().c_str()));
83
84                 if (limit.mask == "*" || limit.mask == "#*")
85                         seen_default = true;
86
87                 newlimits.push_back(limit);
88         }
89
90         // If no default limit has been specified then insert one.
91         if (!seen_default)
92         {
93                 ServerInstance->Logs->Log("MODE", LOG_DEBUG, "No default <maxlist> entry was found for the %s mode; defaulting to %u",
94                         name.c_str(), DEFAULT_LIST_SIZE);
95                 newlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
96         }
97
98         // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
99         if (chanlimits == newlimits)
100                 return;
101
102         chanlimits.swap(newlimits);
103
104         const chan_hash& chans = ServerInstance->GetChans();
105         for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
106         {
107                 ChanData* cd = extItem.get(i->second);
108                 if (cd)
109                         cd->maxitems = -1;
110         }
111 }
112
113 unsigned int ListModeBase::FindLimit(const std::string& channame)
114 {
115         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
116         {
117                 if (InspIRCd::Match(channame, it->mask))
118                 {
119                         // We have a pattern matching the channel
120                         return it->limit;
121                 }
122         }
123         return 0;
124 }
125
126 unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
127 {
128         if (cd->maxitems < 0)
129                 cd->maxitems = FindLimit(channame);
130         return cd->maxitems;
131 }
132
133 unsigned int ListModeBase::GetLimit(Channel* channel)
134 {
135         ChanData* cd = extItem.get(channel);
136         if (!cd) // just find the limit
137                 return FindLimit(channel->name);
138
139         return GetLimitInternal(channel->name, cd);
140 }
141
142 unsigned int ListModeBase::GetLowerLimit()
143 {
144         if (chanlimits.empty())
145                 return DEFAULT_LIST_SIZE;
146
147         unsigned int limit = UINT_MAX;
148         for (limitlist::iterator iter = chanlimits.begin(); iter != chanlimits.end(); ++iter)
149         {
150                 if (iter->limit < limit)
151                         limit = iter->limit;
152         }
153         return limit;
154 }
155
156 ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
157 {
158         // Try and grab the list
159         ChanData* cd = extItem.get(channel);
160
161         if (adding)
162         {
163                 if (tidy)
164                         ModeParser::CleanMask(parameter);
165
166                 // If there was no list
167                 if (!cd)
168                 {
169                         // Make one
170                         cd = new ChanData;
171                         extItem.set(channel, cd);
172                 }
173
174                 // Check if the item already exists in the list
175                 for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
176                 {
177                         if (parameter == it->mask)
178                         {
179                                 /* Give a subclass a chance to error about this */
180                                 TellAlreadyOnList(source, channel, parameter);
181
182                                 // it does, deny the change
183                                 return MODEACTION_DENY;
184                         }
185                 }
186
187                 if ((IS_LOCAL(source)) && (cd->list.size() >= GetLimitInternal(channel->name, cd)))
188                 {
189                         /* List is full, give subclass a chance to send a custom message */
190                         TellListTooLong(source, channel, parameter);
191                         return MODEACTION_DENY;
192                 }
193
194                 /* Ok, it *could* be allowed, now give someone subclassing us
195                  * a chance to validate the parameter.
196                  * The param is passed by reference, so they can both modify it
197                  * and tell us if we allow it or not.
198                  *
199                  * eg, the subclass could:
200                  * 1) allow
201                  * 2) 'fix' parameter and then allow
202                  * 3) deny
203                  */
204                 if (ValidateParam(source, channel, parameter))
205                 {
206                         // And now add the mask onto the list...
207                         cd->list.push_back(ListItem(parameter, source->nick, ServerInstance->Time()));
208                         return MODEACTION_ALLOW;
209                 }
210                 else
211                 {
212                         /* If they deny it they have the job of giving an error message */
213                         return MODEACTION_DENY;
214                 }
215         }
216         else
217         {
218                 // We're taking the mode off
219                 if (cd)
220                 {
221                         for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); ++it)
222                         {
223                                 if (parameter == it->mask)
224                                 {
225                                         stdalgo::vector::swaperase(cd->list, it);
226                                         return MODEACTION_ALLOW;
227                                 }
228                         }
229                 }
230
231                 /* Tried to remove something that wasn't set */
232                 TellNotSet(source, channel, parameter);
233                 return MODEACTION_DENY;
234         }
235 }
236
237 bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
238 {
239         return true;
240 }
241
242 void ListModeBase::OnParameterMissing(User*, User*, Channel*)
243 {
244         // Intentionally left blank.
245 }
246
247 void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
248 {
249         source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, mode, InspIRCd::Format("Channel %s list is full", name.c_str()));
250 }
251
252 void ListModeBase::TellAlreadyOnList(User* source, Channel* channel, std::string& parameter)
253 {
254         source->WriteNumeric(ERR_LISTMODEALREADYSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list already contains %s", name.c_str(), parameter.c_str()));
255 }
256
257 void ListModeBase::TellNotSet(User* source, Channel* channel, std::string& parameter)
258 {
259         source->WriteNumeric(ERR_LISTMODENOTSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list does not contain %s", name.c_str(), parameter.c_str()));
260 }