]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listmode.cpp
Allow modules to prevent a failed connection from being closed.
[user/henk/code/inspircd.git] / src / listmode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
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 #include "inspircd.h"
20 #include "listmode.h"
21
22 ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string& eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy)
23         : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST)
24         , listnumeric(lnum)
25         , endoflistnumeric(eolnum)
26         , endofliststring(eolstr)
27         , tidy(autotidy)
28         , extItem(name + "_mode_list", ExtensionItem::EXT_CHANNEL, Creator)
29 {
30         list = true;
31 }
32
33 void ListModeBase::DisplayList(User* user, Channel* channel)
34 {
35         ChanData* cd = extItem.get(channel);
36         if (cd)
37         {
38                 for (ModeList::const_iterator it = cd->list.begin(); it != cd->list.end(); ++it)
39                 {
40                         user->WriteNumeric(listnumeric, channel->name, it->mask, it->setter, (unsigned long) it->time);
41                 }
42         }
43         user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
44 }
45
46 void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
47 {
48         user->WriteNumeric(endoflistnumeric, channel->name, endofliststring);
49 }
50
51 void ListModeBase::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
52 {
53         ChanData* cd = extItem.get(channel);
54         if (cd)
55         {
56                 for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
57                 {
58                         changelist.push_remove(this, it->mask);
59                 }
60         }
61 }
62
63 void ListModeBase::DoRehash()
64 {
65         ConfigTagList tags = ServerInstance->Config->ConfTags("maxlist");
66         limitlist newlimits;
67         bool seen_default = false;
68         for (ConfigIter i = tags.first; i != tags.second; i++)
69         {
70                 ConfigTag* c = i->second;
71
72                 const std::string mname = c->getString("mode");
73                 if (!mname.empty() && !stdalgo::string::equalsci(mname, name) && !(mname.length() == 1 && GetModeChar() == mname[0]))
74                         continue;
75
76                 ListLimit limit(c->getString("chan", "*", 1), c->getUInt("limit", DEFAULT_LIST_SIZE));
77
78                 if (limit.mask.empty())
79                         throw ModuleException(InspIRCd::Format("<maxlist:chan> is empty, at %s", c->getTagLocation().c_str()));
80
81                 if (limit.mask == "*" || limit.mask == "#*")
82                         seen_default = true;
83
84                 newlimits.push_back(limit);
85         }
86
87         // If no default limit has been specified then insert one.
88         if (!seen_default)
89         {
90                 ServerInstance->Logs->Log("MODE", LOG_DEBUG, "No default <maxlist> entry was found for the %s mode; defaulting to %u",
91                         name.c_str(), DEFAULT_LIST_SIZE);
92                 newlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
93         }
94
95         // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
96         if (chanlimits == newlimits)
97                 return;
98
99         chanlimits.swap(newlimits);
100
101         const chan_hash& chans = ServerInstance->GetChans();
102         for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
103         {
104                 ChanData* cd = extItem.get(i->second);
105                 if (cd)
106                         cd->maxitems = -1;
107         }
108 }
109
110 unsigned int ListModeBase::FindLimit(const std::string& channame)
111 {
112         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
113         {
114                 if (InspIRCd::Match(channame, it->mask))
115                 {
116                         // We have a pattern matching the channel
117                         return it->limit;
118                 }
119         }
120         return 0;
121 }
122
123 unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
124 {
125         if (cd->maxitems < 0)
126                 cd->maxitems = FindLimit(channame);
127         return cd->maxitems;
128 }
129
130 unsigned int ListModeBase::GetLimit(Channel* channel)
131 {
132         ChanData* cd = extItem.get(channel);
133         if (!cd) // just find the limit
134                 return FindLimit(channel->name);
135
136         return GetLimitInternal(channel->name, cd);
137 }
138
139 unsigned int ListModeBase::GetLowerLimit()
140 {
141         if (chanlimits.empty())
142                 return DEFAULT_LIST_SIZE;
143
144         unsigned int limit = UINT_MAX;
145         for (limitlist::iterator iter = chanlimits.begin(); iter != chanlimits.end(); ++iter)
146         {
147                 if (iter->limit < limit)
148                         limit = iter->limit;
149         }
150         return limit;
151 }
152
153 ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
154 {
155         // Try and grab the list
156         ChanData* cd = extItem.get(channel);
157
158         if (adding)
159         {
160                 if (tidy)
161                         ModeParser::CleanMask(parameter);
162
163                 if (parameter.length() > 250)
164                         return MODEACTION_DENY;
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 }