]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listmode.cpp
Call the OnUserInit hook from earlier in UserManager::AddUser.
[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("listbase_mode_" + name + "_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         for (ConfigIter i = tags.first; i != tags.second; i++)
68         {
69                 ConfigTag* c = i->second;
70
71                 const std::string mname = c->getString("mode");
72                 if (!mname.empty() && !stdalgo::string::equalsci(mname, name) && !(mname.length() == 1 && GetModeChar() == mname[0]))
73                         continue;
74
75                 ListLimit limit(c->getString("chan", "*"), c->getUInt("limit", 0));
76
77                 if (limit.mask.empty())
78                         throw ModuleException(InspIRCd::Format("<maxlist:chan> is empty, at %s", c->getTagLocation().c_str()));
79
80                 if (limit.limit <= 0)
81                         throw ModuleException(InspIRCd::Format("<maxlist:limit> must be non-zero, at %s", c->getTagLocation().c_str()));
82
83                 newlimits.push_back(limit);
84         }
85
86         // Add the default entry. This is inserted last so if the user specifies a
87         // wildcard record in the config it will take precedence over this entry.
88         newlimits.push_back(ListLimit("*", DEFAULT_LIST_SIZE));
89
90         // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
91         if (chanlimits == newlimits)
92                 return;
93
94         chanlimits.swap(newlimits);
95
96         const chan_hash& chans = ServerInstance->GetChans();
97         for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
98         {
99                 ChanData* cd = extItem.get(i->second);
100                 if (cd)
101                         cd->maxitems = -1;
102         }
103 }
104
105 unsigned int ListModeBase::FindLimit(const std::string& channame)
106 {
107         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
108         {
109                 if (InspIRCd::Match(channame, it->mask))
110                 {
111                         // We have a pattern matching the channel
112                         return it->limit;
113                 }
114         }
115         return DEFAULT_LIST_SIZE;
116 }
117
118 unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
119 {
120         if (cd->maxitems < 0)
121                 cd->maxitems = FindLimit(channame);
122         return cd->maxitems;
123 }
124
125 unsigned int ListModeBase::GetLimit(Channel* channel)
126 {
127         ChanData* cd = extItem.get(channel);
128         if (!cd) // just find the limit
129                 return FindLimit(channel->name);
130
131         return GetLimitInternal(channel->name, cd);
132 }
133
134 unsigned int ListModeBase::GetLowerLimit()
135 {
136         unsigned int limit = UINT_MAX;
137         for (limitlist::iterator iter = chanlimits.begin(); iter != chanlimits.end(); ++iter)
138         {
139                 if (iter->limit < limit)
140                         limit = iter->limit;
141         }
142         return limit == UINT_MAX ? DEFAULT_LIST_SIZE : limit;
143 }
144
145 ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
146 {
147         // Try and grab the list
148         ChanData* cd = extItem.get(channel);
149
150         if (adding)
151         {
152                 if (tidy)
153                         ModeParser::CleanMask(parameter);
154
155                 if (parameter.length() > 250)
156                         return MODEACTION_DENY;
157
158                 // If there was no list
159                 if (!cd)
160                 {
161                         // Make one
162                         cd = new ChanData;
163                         extItem.set(channel, cd);
164                 }
165
166                 // Check if the item already exists in the list
167                 for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
168                 {
169                         if (parameter == it->mask)
170                         {
171                                 /* Give a subclass a chance to error about this */
172                                 TellAlreadyOnList(source, channel, parameter);
173
174                                 // it does, deny the change
175                                 return MODEACTION_DENY;
176                         }
177                 }
178
179                 if ((IS_LOCAL(source)) && (cd->list.size() >= GetLimitInternal(channel->name, cd)))
180                 {
181                         /* List is full, give subclass a chance to send a custom message */
182                         TellListTooLong(source, channel, parameter);
183                         return MODEACTION_DENY;
184                 }
185
186                 /* Ok, it *could* be allowed, now give someone subclassing us
187                  * a chance to validate the parameter.
188                  * The param is passed by reference, so they can both modify it
189                  * and tell us if we allow it or not.
190                  *
191                  * eg, the subclass could:
192                  * 1) allow
193                  * 2) 'fix' parameter and then allow
194                  * 3) deny
195                  */
196                 if (ValidateParam(source, channel, parameter))
197                 {
198                         // And now add the mask onto the list...
199                         cd->list.push_back(ListItem(parameter, source->nick, ServerInstance->Time()));
200                         return MODEACTION_ALLOW;
201                 }
202                 else
203                 {
204                         /* If they deny it they have the job of giving an error message */
205                         return MODEACTION_DENY;
206                 }
207         }
208         else
209         {
210                 // We're taking the mode off
211                 if (cd)
212                 {
213                         for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); ++it)
214                         {
215                                 if (parameter == it->mask)
216                                 {
217                                         stdalgo::vector::swaperase(cd->list, it);
218                                         return MODEACTION_ALLOW;
219                                 }
220                         }
221                 }
222
223                 /* Tried to remove something that wasn't set */
224                 TellNotSet(source, channel, parameter);
225                 return MODEACTION_DENY;
226         }
227 }
228
229 bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
230 {
231         return true;
232 }
233
234 void ListModeBase::OnParameterMissing(User*, User*, Channel*)
235 {
236         // Intentionally left blank.
237 }
238
239 void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
240 {
241         source->WriteNumeric(ERR_BANLISTFULL, channel->name, parameter, mode, InspIRCd::Format("Channel %s list is full", name.c_str()));
242 }
243
244 void ListModeBase::TellAlreadyOnList(User* source, Channel* channel, std::string& parameter)
245 {
246         source->WriteNumeric(ERR_LISTMODEALREADYSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list already contains %s", name.c_str(), parameter.c_str()));
247 }
248
249 void ListModeBase::TellNotSet(User* source, Channel* channel, std::string& parameter)
250 {
251         source->WriteNumeric(ERR_LISTMODENOTSET, channel->name, parameter, mode, InspIRCd::Format("Channel %s list does not contain %s", name.c_str(), parameter.c_str()));
252 }