]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Sync helpop chmodes s and p with docs
[user/henk/code/inspircd.git] / src / channels.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
5  *   Copyright (C) 2013-2014, 2016-2020 Sadie Powell <sadie@witchery.services>
6  *   Copyright (C) 2013 Adam <Adam@anope.org>
7  *   Copyright (C) 2012-2016, 2018 Attila Molnar <attilamolnar@hush.com>
8  *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
9  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
10  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
11  *   Copyright (C) 2006-2009 Robin Burchell <robin+git@viroteck.net>
12  *   Copyright (C) 2006-2008, 2010 Craig Edwards <brain@inspircd.org>
13  *
14  * This file is part of InspIRCd.  InspIRCd is free software: you can
15  * redistribute it and/or modify it under the terms of the GNU General Public
16  * License as published by the Free Software Foundation, version 2.
17  *
18  * This program is distributed in the hope that it will be useful, but WITHOUT
19  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  */
26
27
28 #include "inspircd.h"
29 #include "listmode.h"
30
31 namespace
32 {
33         ChanModeReference ban(NULL, "ban");
34 }
35
36 Channel::Channel(const std::string &cname, time_t ts)
37         : name(cname), age(ts), topicset(0)
38 {
39         if (!ServerInstance->chanlist.insert(std::make_pair(cname, this)).second)
40                 throw CoreException("Cannot create duplicate channel " + cname);
41 }
42
43 void Channel::SetMode(ModeHandler* mh, bool on)
44 {
45         if (mh && mh->GetId() != ModeParser::MODEID_MAX)
46                 modes[mh->GetId()] = on;
47 }
48
49 void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const std::string* setter)
50 {
51         // Send a TOPIC message to the channel only if the new topic text differs
52         if (this->topic != ntopic)
53         {
54                 this->topic = ntopic;
55                 ClientProtocol::Messages::Topic topicmsg(u, this, this->topic);
56                 Write(ServerInstance->GetRFCEvents().topic, topicmsg);
57         }
58
59         // Always update setter and set time
60         if (!setter)
61                 setter = ServerInstance->Config->FullHostInTopic ? &u->GetFullHost() : &u->nick;
62         this->setby.assign(*setter, 0, ServerInstance->Config->Limits.GetMaxMask());
63         this->topicset = topicts;
64
65         FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
66 }
67
68 Membership* Channel::AddUser(User* user)
69 {
70         std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
71         if (!ret.second)
72                 return NULL;
73
74         Membership* memb = new(ret.first->second) Membership(user, this);
75         return memb;
76 }
77
78 void Channel::DelUser(User* user)
79 {
80         MemberMap::iterator it = userlist.find(user);
81         if (it != userlist.end())
82                 DelUser(it);
83 }
84
85 void Channel::CheckDestroy()
86 {
87         if (!userlist.empty())
88                 return;
89
90         ModResult res;
91         FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
92         if (res == MOD_RES_DENY)
93                 return;
94
95         // If the channel isn't in chanlist then it is already in the cull list, don't add it again
96         chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
97         if ((iter == ServerInstance->chanlist.end()) || (iter->second != this))
98                 return;
99
100         FOREACH_MOD(OnChannelDelete, (this));
101         ServerInstance->chanlist.erase(iter);
102         ServerInstance->GlobalCulls.AddItem(this);
103 }
104
105 void Channel::DelUser(const MemberMap::iterator& membiter)
106 {
107         Membership* memb = membiter->second;
108         memb->cull();
109         memb->~Membership();
110         userlist.erase(membiter);
111
112         // If this channel became empty then it should be removed
113         CheckDestroy();
114 }
115
116 Membership* Channel::GetUser(User* user)
117 {
118         MemberMap::iterator i = userlist.find(user);
119         if (i == userlist.end())
120                 return NULL;
121         return i->second;
122 }
123
124 void Channel::SetDefaultModes()
125 {
126         ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
127                 ServerInstance->Config->DefaultModes.c_str());
128         irc::spacesepstream list(ServerInstance->Config->DefaultModes);
129         std::string modeseq;
130         std::string parameter;
131
132         list.GetToken(modeseq);
133
134         for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
135         {
136                 ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
137                 if (mode)
138                 {
139                         if (mode->IsPrefixMode())
140                                 continue;
141
142                         if (mode->NeedsParam(true))
143                         {
144                                 list.GetToken(parameter);
145                                 // If the parameter begins with a ':' then it's invalid
146                                 if (parameter.c_str()[0] == ':')
147                                         continue;
148                         }
149                         else
150                                 parameter.clear();
151
152                         if ((mode->NeedsParam(true)) && (parameter.empty()))
153                                 continue;
154
155                         mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
156                 }
157         }
158 }
159
160 /*
161  * add a channel to a user, creating the record for it if needed and linking
162  * it to the user record
163  */
164 Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key)
165 {
166         if (user->registered != REG_ALL)
167         {
168                 ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname);
169                 return NULL;
170         }
171
172         /*
173          * We don't restrict the number of channels that remote users or users that are override-joining may be in.
174          * We restrict local users to <connect:maxchans> channels.
175          * We restrict local operators to <oper:maxchans> channels.
176          * This is a lot more logical than how it was formerly. -- w00t
177          */
178         if (!override)
179         {
180                 unsigned int maxchans = user->GetClass()->maxchans;
181                 if (!maxchans)
182                         maxchans = ServerInstance->Config->MaxChans;
183                 if (user->IsOper())
184                 {
185                         unsigned int opermaxchans = ConvToNum<unsigned int>(user->oper->getConfig("maxchans"));
186                         // If not set, use 2.0's <channels:opers>, if that's not set either, use limit from CC
187                         if (!opermaxchans && user->HasPrivPermission("channels/high-join-limit"))
188                                 opermaxchans = ServerInstance->Config->OperMaxChans;
189                         if (opermaxchans)
190                                 maxchans = opermaxchans;
191                 }
192                 if (user->chans.size() >= maxchans)
193                 {
194                         user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels");
195                         return NULL;
196                 }
197         }
198
199         // Crop channel name if it's too long
200         if (cname.length() > ServerInstance->Config->Limits.ChanMax)
201                 cname.resize(ServerInstance->Config->Limits.ChanMax);
202
203         Channel* chan = ServerInstance->FindChan(cname);
204         bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
205         std::string privs; // Prefix mode(letter)s to give to the joining user
206
207         if (!chan)
208         {
209                 privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
210
211                 if (override == false)
212                 {
213                         // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
214                         ModResult MOD_RESULT;
215                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
216                         if (MOD_RESULT == MOD_RES_DENY)
217                                 return NULL; // A module wasn't happy with the join, abort
218                 }
219
220                 chan = new Channel(cname, ServerInstance->Time());
221                 // Set the default modes on the channel (<options:defaultmodes>)
222                 chan->SetDefaultModes();
223         }
224         else
225         {
226                 /* Already on the channel */
227                 if (chan->HasUser(user))
228                         return NULL;
229
230                 if (override == false)
231                 {
232                         ModResult MOD_RESULT;
233                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
234
235                         // A module explicitly denied the join and (hopefully) generated a message
236                         // describing the situation, so we may stop here without sending anything
237                         if (MOD_RESULT == MOD_RES_DENY)
238                                 return NULL;
239
240                         // If no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
241                         // most of the time) then proceed to check channel bans.
242                         //
243                         // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
244                         // then this entire section is skipped
245                         if (MOD_RESULT == MOD_RES_PASSTHRU)
246                         {
247                                 if (chan->IsBanned(user))
248                                 {
249                                         user->WriteNumeric(ERR_BANNEDFROMCHAN, chan->name, "Cannot join channel (you're banned)");
250                                         return NULL;
251                                 }
252                         }
253                 }
254         }
255
256         // We figured that this join is allowed and also created the
257         // channel if it didn't exist before, now do the actual join
258         chan->ForceJoin(user, &privs, false, created_by_local);
259         return chan;
260 }
261
262 Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
263 {
264         if (IS_SERVER(user))
265         {
266                 ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
267                 return NULL;
268         }
269
270         Membership* memb = this->AddUser(user);
271         if (!memb)
272                 return NULL; // Already on the channel
273
274         user->chans.push_front(memb);
275
276         if (privs)
277         {
278                 // If the user was granted prefix modes (in the OnUserPreJoin hook, or they're a
279                 // remote user and their own server set the modes), then set them internally now
280                 for (std::string::const_iterator i = privs->begin(); i != privs->end(); ++i)
281                 {
282                         PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
283                         if (mh)
284                         {
285                                 std::string nick = user->nick;
286                                 // Set the mode on the user
287                                 mh->OnModeChange(ServerInstance->FakeClient, NULL, this, nick, true);
288                         }
289                 }
290         }
291
292         // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
293         CUList except_list;
294         FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
295
296         ClientProtocol::Events::Join joinevent(memb);
297         this->Write(joinevent, 0, except_list);
298
299         FOREACH_MOD(OnPostJoin, (memb));
300         return memb;
301 }
302
303 bool Channel::IsBanned(User* user)
304 {
305         ModResult result;
306         FIRST_MOD_RESULT(OnCheckChannelBan, result, (user, this));
307
308         if (result != MOD_RES_PASSTHRU)
309                 return (result == MOD_RES_DENY);
310
311         ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
312         if (!banlm)
313                 return false;
314
315         const ListModeBase::ModeList* bans = banlm->GetList(this);
316         if (bans)
317         {
318                 for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); it++)
319                 {
320                         if (CheckBan(user, it->mask))
321                                 return true;
322                 }
323         }
324         return false;
325 }
326
327 bool Channel::CheckBan(User* user, const std::string& mask)
328 {
329         ModResult result;
330         FIRST_MOD_RESULT(OnCheckBan, result, (user, this, mask));
331         if (result != MOD_RES_PASSTHRU)
332                 return (result == MOD_RES_DENY);
333
334         // extbans were handled above, if this is one it obviously didn't match
335         if ((mask.length() <= 2) || (mask[1] == ':'))
336                 return false;
337
338         std::string::size_type at = mask.find('@');
339         if (at == std::string::npos)
340                 return false;
341
342         const std::string nickIdent = user->nick + "!" + user->ident;
343         std::string prefix(mask, 0, at);
344         if (InspIRCd::Match(nickIdent, prefix, NULL))
345         {
346                 std::string suffix(mask, at + 1);
347                 if (InspIRCd::Match(user->GetRealHost(), suffix, NULL) ||
348                         InspIRCd::Match(user->GetDisplayedHost(), suffix, NULL) ||
349                         InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
350                         return true;
351         }
352         return false;
353 }
354
355 ModResult Channel::GetExtBanStatus(User *user, char type)
356 {
357         ModResult rv;
358         FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
359         if (rv != MOD_RES_PASSTHRU)
360                 return rv;
361
362         ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
363         if (!banlm)
364                 return MOD_RES_PASSTHRU;
365
366         const ListModeBase::ModeList* bans = banlm->GetList(this);
367         if (bans)
368         {
369                 for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); ++it)
370                 {
371                         if (it->mask.length() <= 2 || it->mask[0] != type || it->mask[1] != ':')
372                                 continue;
373
374                         if (CheckBan(user, it->mask.substr(2)))
375                                 return MOD_RES_DENY;
376                 }
377         }
378         return MOD_RES_PASSTHRU;
379 }
380
381 /* Channel::PartUser
382  * Remove a channel from a users record, remove the reference to the Membership object
383  * from the channel and destroy it.
384  */
385 bool Channel::PartUser(User* user, std::string& reason)
386 {
387         MemberMap::iterator membiter = userlist.find(user);
388
389         if (membiter == userlist.end())
390                 return false;
391
392         Membership* memb = membiter->second;
393         CUList except_list;
394         FOREACH_MOD(OnUserPart, (memb, reason, except_list));
395
396         ClientProtocol::Messages::Part partmsg(memb, reason);
397         Write(ServerInstance->GetRFCEvents().part, partmsg, 0, except_list);
398
399         // Remove this channel from the user's chanlist
400         user->chans.erase(memb);
401         // Remove the Membership from this channel's userlist and destroy it
402         this->DelUser(membiter);
403
404         return true;
405 }
406
407 void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason)
408 {
409         Membership* memb = victimiter->second;
410         CUList except_list;
411         FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
412
413         ClientProtocol::Messages::Kick kickmsg(src, memb, reason);
414         Write(ServerInstance->GetRFCEvents().kick, kickmsg, 0, except_list);
415
416         memb->user->chans.erase(memb);
417         this->DelUser(victimiter);
418 }
419
420 void Channel::Write(ClientProtocol::Event& protoev, char status, const CUList& except_list)
421 {
422         unsigned int minrank = 0;
423         if (status)
424         {
425                 PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
426                 if (mh)
427                         minrank = mh->GetPrefixRank();
428         }
429         for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
430         {
431                 LocalUser* user = IS_LOCAL(i->first);
432                 if ((user) && (!except_list.count(user)))
433                 {
434                         /* User doesn't have the status we're after */
435                         if (minrank && i->second->getRank() < minrank)
436                                 continue;
437
438                         user->Send(protoev);
439                 }
440         }
441 }
442
443 const char* Channel::ChanModes(bool showsecret)
444 {
445         static std::string scratch;
446         std::string sparam;
447
448         scratch.clear();
449
450         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
451         for(int n = 0; n < 64; n++)
452         {
453                 ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_CHANNEL);
454                 if (mh && IsModeSet(mh))
455                 {
456                         scratch.push_back(n + 65);
457
458                         ParamModeBase* pm = mh->IsParameterMode();
459                         if (!pm)
460                                 continue;
461
462                         if (pm->IsParameterSecret() && !showsecret)
463                         {
464                                 sparam += " <" + pm->name + ">";
465                         }
466                         else
467                         {
468                                 sparam += ' ';
469                                 pm->GetParameter(this, sparam);
470                         }
471                 }
472         }
473
474         scratch += sparam;
475         return scratch.c_str();
476 }
477
478 void Channel::WriteNotice(const std::string& text, char status)
479 {
480         ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this, text, MSG_NOTICE, status);
481         Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
482 }
483
484 void Channel::WriteRemoteNotice(const std::string& text, char status)
485 {
486         WriteNotice(text, status);
487         ServerInstance->PI->SendMessage(this, status, text, MSG_NOTICE);
488 }
489
490 /* returns the status character for a given user on a channel, e.g. @ for op,
491  * % for halfop etc. If the user has several modes set, the highest mode
492  * the user has must be returned.
493  */
494 char Membership::GetPrefixChar() const
495 {
496         char pf = 0;
497         unsigned int bestrank = 0;
498
499         for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
500         {
501                 PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
502                 if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
503                 {
504                         bestrank = mh->GetPrefixRank();
505                         pf = mh->GetPrefix();
506                 }
507         }
508         return pf;
509 }
510
511 unsigned int Membership::getRank()
512 {
513         char mchar = modes.c_str()[0];
514         unsigned int rv = 0;
515         if (mchar)
516         {
517                 PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
518                 if (mh)
519                         rv = mh->GetPrefixRank();
520         }
521         return rv;
522 }
523
524 std::string Membership::GetAllPrefixChars() const
525 {
526         std::string ret;
527         for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
528         {
529                 PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
530                 if (mh && mh->GetPrefix())
531                         ret.push_back(mh->GetPrefix());
532         }
533
534         return ret;
535 }
536
537 unsigned int Channel::GetPrefixValue(User* user)
538 {
539         MemberMap::iterator m = userlist.find(user);
540         if (m == userlist.end())
541                 return 0;
542         return m->second->getRank();
543 }
544
545 bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
546 {
547         char prefix = delta_mh->GetModeChar();
548         for (unsigned int i = 0; i < modes.length(); i++)
549         {
550                 char mchar = modes[i];
551                 PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
552                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
553                 {
554                         modes = modes.substr(0,i) +
555                                 (adding ? std::string(1, prefix) : "") +
556                                 modes.substr(mchar == prefix ? i+1 : i);
557                         return adding != (mchar == prefix);
558                 }
559         }
560         if (adding)
561                 modes.push_back(prefix);
562         return adding;
563 }
564
565
566 void Membership::WriteNotice(const std::string& text) const
567 {
568         LocalUser* const localuser = IS_LOCAL(user);
569         if (!localuser)
570                 return;
571
572         ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this->chan, text, MSG_NOTICE);
573         localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
574 }