]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Move AllowedPrivs and similar oper information from LocalUser to OperInfo
[user/henk/code/inspircd.git] / src / channels.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core */
15
16 #include "inspircd.h"
17 #include <cstdarg>
18 #include "mode.h"
19
20 Channel::Channel(const std::string &cname, time_t ts)
21 {
22         chan_hash::iterator findchan = ServerInstance->chanlist->find(cname);
23         if (findchan != ServerInstance->chanlist->end())
24                 throw CoreException("Cannot create duplicate channel " + cname);
25
26         (*(ServerInstance->chanlist))[cname.c_str()] = this;
27         this->name.assign(cname, 0, ServerInstance->Config->Limits.ChanMax);
28         this->age = ts ? ts : ServerInstance->Time();
29
30         maxbans = topicset = 0;
31         modes.reset();
32 }
33
34 void Channel::SetMode(char mode,bool mode_on)
35 {
36         modes[mode-65] = mode_on;
37 }
38
39 void Channel::SetModeParam(char mode, std::string parameter)
40 {
41         CustomModeList::iterator n = custom_mode_params.find(mode);
42         // always erase, even if changing, so that the map gets the new value
43         if (n != custom_mode_params.end())
44                 custom_mode_params.erase(n);
45         if (parameter.empty())
46         {
47                 modes[mode-65] = false;
48         }
49         else
50         {
51                 custom_mode_params[mode] = parameter;
52                 modes[mode-65] = true;
53         }
54 }
55
56 std::string Channel::GetModeParameter(char mode)
57 {
58         CustomModeList::iterator n = custom_mode_params.find(mode);
59         if (n != custom_mode_params.end())
60                 return n->second;
61         return "";
62 }
63
64 int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
65 {
66         if (u)
67         {
68                 if(!forceset)
69                 {
70                         ModResult res;
71                         /* 0: check status, 1: don't, -1: disallow change silently */
72
73                         FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
74
75                         if (res == MOD_RES_DENY)
76                                 return CMD_FAILURE;
77                         if (res != MOD_RES_ALLOW)
78                         {
79                                 if (!this->HasUser(u))
80                                 {
81                                         u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
82                                         return CMD_FAILURE;
83                                 }
84                                 if ((this->IsModeSet('t')) && (this->GetPrefixValue(u) < HALFOP_VALUE))
85                                 {
86                                         u->WriteNumeric(482, "%s %s :You must be at least a half-operator to change the topic on this channel", u->nick.c_str(), this->name.c_str());
87                                         return CMD_FAILURE;
88                                 }
89                         }
90                 }
91         }
92
93         this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
94         if (u)
95         {
96                 this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
97                 this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
98         }
99         else
100         {
101                 this->setby.assign(ServerInstance->Config->ServerName);
102                 this->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
103         }
104
105         this->topicset = ServerInstance->Time();
106
107         // XXX: this check for 'u' is probably pre-fake-user, and it fucking sucks anyway. we need to change this.
108         if (u)
109         {
110                 FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
111         }
112
113         return CMD_SUCCESS;
114 }
115
116 long Channel::GetUserCounter()
117 {
118         return userlist.size();
119 }
120
121 Membership* Channel::AddUser(User* user)
122 {
123         Membership* memb = new Membership(user, this);
124         userlist[user] = memb;
125         return memb;
126 }
127
128 void Channel::DelUser(User* user)
129 {
130         UserMembIter a = userlist.find(user);
131
132         if (a != userlist.end())
133         {
134                 a->second->cull();
135                 delete a->second;
136                 userlist.erase(a);
137         }
138
139         if (userlist.empty())
140         {
141                 ModResult res;
142                 FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
143                 if (res == MOD_RES_DENY)
144                         return;
145                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
146                 /* kill the record */
147                 if (iter != ServerInstance->chanlist->end())
148                 {
149                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
150                         ServerInstance->chanlist->erase(iter);
151                 }
152                 ServerInstance->GlobalCulls.AddItem(this);
153         }
154 }
155
156 bool Channel::HasUser(User* user)
157 {
158         return (userlist.find(user) != userlist.end());
159 }
160
161 Membership* Channel::GetUser(User* user)
162 {
163         UserMembIter i = userlist.find(user);
164         if (i == userlist.end())
165                 return NULL;
166         return i->second;
167 }
168
169 const UserMembList* Channel::GetUsers()
170 {
171         return &userlist;
172 }
173
174 void Channel::SetDefaultModes()
175 {
176         ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
177                 ServerInstance->Config->DefaultModes.c_str());
178         irc::spacesepstream list(ServerInstance->Config->DefaultModes);
179         std::string modeseq;
180         std::string parameter;
181
182         list.GetToken(modeseq);
183
184         for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
185         {
186                 ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
187                 if (mode)
188                 {
189                         if (mode->GetNumParams(true))
190                                 list.GetToken(parameter);
191                         else
192                                 parameter.clear();
193
194                         mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
195                 }
196         }
197 }
198
199 /*
200  * add a channel to a user, creating the record for it if needed and linking
201  * it to the user record
202  */
203 Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
204 {
205         // Fix: unregistered users could be joined using /SAJOIN
206         if (!user || !cn || user->registered != REG_ALL)
207                 return NULL;
208
209         char cname[MAXBUF];
210         std::string privs;
211         Channel *Ptr;
212
213         /*
214          * We don't restrict the number of channels that remote users or users that are override-joining may be in.
215          * We restrict local users to MaxChans channels.
216          * We restrict local operators to OperMaxChans channels.
217          * This is a lot more logical than how it was formerly. -- w00t
218          */
219         if (IS_LOCAL(user) && !override)
220         {
221                 if (user->HasPrivPermission("channels/high-join-limit"))
222                 {
223                         if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
224                         {
225                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
226                                 return NULL;
227                         }
228                 }
229                 else
230                 {
231                         unsigned int maxchans = user->GetClass()->maxchans;
232                         if (!maxchans)
233                                 maxchans = ServerInstance->Config->MaxChans;
234                         if (user->chans.size() >= maxchans)
235                         {
236                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
237                                 return NULL;
238                         }
239                 }
240         }
241
242         strlcpy(cname, cn, ServerInstance->Config->Limits.ChanMax);
243         Ptr = ServerInstance->FindChan(cname);
244         bool created_by_local = false;
245
246         if (!Ptr)
247         {
248                 /*
249                  * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
250                  */
251                 if (!IS_LOCAL(user))
252                 {
253                         if (!TS)
254                                 ServerInstance->Logs->Log("CHANNEL",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
255                 }
256                 else
257                 {
258                         privs = "o";
259                         created_by_local = true;
260                 }
261
262                 if (IS_LOCAL(user) && override == false)
263                 {
264                         ModResult MOD_RESULT;
265                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key ? key : ""));
266                         if (MOD_RESULT == MOD_RES_DENY)
267                                 return NULL;
268                 }
269
270                 Ptr = new Channel(cname, TS);
271         }
272         else
273         {
274                 /* Already on the channel */
275                 if (Ptr->HasUser(user))
276                         return NULL;
277
278                 /*
279                  * remote users are allowed us to bypass channel modes
280                  * and bans (used by servers)
281                  */
282                 if (IS_LOCAL(user) && override == false)
283                 {
284                         ModResult MOD_RESULT;
285                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname, privs, key ? key : ""));
286                         if (MOD_RESULT == MOD_RES_DENY)
287                         {
288                                 return NULL;
289                         }
290                         else if (MOD_RESULT == MOD_RES_PASSTHRU)
291                         {
292                                 std::string ckey = Ptr->GetModeParameter('k');
293                                 bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
294                                 bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
295
296                                 if (!ckey.empty())
297                                 {
298                                         FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
299                                         if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
300                                         {
301                                                 // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
302                                                 user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
303                                                 return NULL;
304                                         }
305                                 }
306
307                                 if (Ptr->IsModeSet('i'))
308                                 {
309                                         FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
310                                         if (!MOD_RESULT.check(invited))
311                                         {
312                                                 user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
313                                                 return NULL;
314                                         }
315                                 }
316
317                                 std::string limit = Ptr->GetModeParameter('l');
318                                 if (!limit.empty())
319                                 {
320                                         FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
321                                         if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
322                                         {
323                                                 user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
324                                                 return NULL;
325                                         }
326                                 }
327
328                                 if (Ptr->IsBanned(user) && !can_bypass)
329                                 {
330                                         user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
331                                         return NULL;
332                                 }
333
334                                 /*
335                                  * If the user has invites for this channel, remove them now
336                                  * after a successful join so they don't build up.
337                                  */
338                                 if (invited)
339                                 {
340                                         IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
341                                 }
342                         }
343                 }
344         }
345
346         if (created_by_local)
347         {
348                 /* As spotted by jilles, dont bother to set this on remote users */
349                 Ptr->SetDefaultModes();
350         }
351
352         return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
353 }
354
355 Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
356 {
357         std::string nick = user->nick;
358
359         Membership* memb = Ptr->AddUser(user);
360         user->chans.insert(Ptr);
361
362         for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
363         {
364                 const char status = *x;
365                 ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
366                 if (mh)
367                 {
368                         /* Set, and make sure that the mode handler knows this mode was now set */
369                         Ptr->SetPrefix(user, mh->GetModeChar(), true);
370                         mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
371                 }
372         }
373
374         CUList except_list;
375         FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
376
377         Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
378
379         /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
380         std::string ms = ServerInstance->Modes->ModeString(user, Ptr);
381         if ((Ptr->GetUserCounter() > 1) && (ms.length()))
382                 Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
383
384         /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
385         if (IS_LOCAL(user))
386         {
387                 if (Ptr->topicset)
388                 {
389                         user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
390                         user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
391                 }
392                 Ptr->UserList(user);
393         }
394         FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
395         return Ptr;
396 }
397
398 bool Channel::IsBanned(User* user)
399 {
400         ModResult result;
401         FIRST_MOD_RESULT(OnCheckChannelBan, result, (user, this));
402
403         if (result != MOD_RES_PASSTHRU)
404                 return (result == MOD_RES_DENY);
405
406         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
407         {
408                 if (CheckBan(user, i->data))
409                         return true;
410         }
411         return false;
412 }
413
414 bool Channel::CheckBan(User* user, const std::string& mask)
415 {
416         ModResult result;
417         FIRST_MOD_RESULT(OnCheckBan, result, (user, this, mask));
418         if (result != MOD_RES_PASSTHRU)
419                 return (result == MOD_RES_DENY);
420
421         // extbans were handled above, if this is one it obviously didn't match
422         if (mask[1] == ':')
423                 return false;
424
425         std::string::size_type at = mask.find('@');
426         if (at == std::string::npos)
427                 return false;
428
429         char tomatch[MAXBUF];
430         snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
431         std::string prefix = mask.substr(0, at);
432         if (InspIRCd::Match(tomatch, prefix, NULL))
433         {
434                 std::string suffix = mask.substr(at + 1);
435                 if (InspIRCd::Match(user->host, suffix, NULL) ||
436                         InspIRCd::Match(user->dhost, suffix, NULL) ||
437                         InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
438                         return true;
439         }
440         return false;
441 }
442
443 ModResult Channel::GetExtBanStatus(User *user, char type)
444 {
445         ModResult rv;
446         FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
447         if (rv != MOD_RES_PASSTHRU)
448                 return rv;
449         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
450         {
451                 if (i->data[0] == type && i->data[1] == ':')
452                 {
453                         std::string val = i->data.substr(2);
454                         if (CheckBan(user, val))
455                                 return MOD_RES_DENY;
456                 }
457         }
458         return MOD_RES_PASSTHRU;
459 }
460
461 /* Channel::PartUser
462  * remove a channel from a users record, and return the number of users left.
463  * Therefore, if this function returns 0 the caller should delete the Channel.
464  */
465 void Channel::PartUser(User *user, std::string &reason)
466 {
467         if (!user)
468                 return;
469
470         Membership* memb = GetUser(user);
471
472         if (memb)
473         {
474                 CUList except_list;
475                 FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
476
477                 WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
478
479                 user->chans.erase(this);
480                 this->RemoveAllPrefixes(user);
481         }
482
483         this->DelUser(user);
484 }
485
486 void Channel::ServerKickUser(User* user, const char* reason, const std::string& servername)
487 {
488         if (servername.empty() || !ServerInstance->Config->HideWhoisServer.empty())
489                 ServerInstance->FakeClient->server = ServerInstance->Config->ServerName;
490         else
491                 ServerInstance->FakeClient->server = servername;
492
493         KickUser(ServerInstance->FakeClient, user, reason);
494 }
495
496 void Channel::KickUser(User *src, User *user, const char* reason)
497 {
498         if (!src || !user || !reason)
499                 return;
500
501         Membership* memb = GetUser(user);
502         if (IS_LOCAL(src))
503         {
504                 if (!memb)
505                 {
506                         src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
507                         return;
508                 }
509                 if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
510                 {
511                         src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
512                         return;
513                 }
514
515                 ModResult res;
516                 if (ServerInstance->ULine(src->server))
517                         res = MOD_RES_ALLOW;
518                 else
519                         FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
520
521                 if (res == MOD_RES_DENY)
522                         return;
523
524                 if (res == MOD_RES_PASSTHRU)
525                 {
526                         int them = this->GetPrefixValue(src);
527                         int us = this->GetPrefixValue(user);
528                         if ((them < HALFOP_VALUE) || (them < us))
529                         {
530                                 src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",src->nick.c_str(), this->name.c_str(), them >= HALFOP_VALUE ? "" : "half-");
531                                 return;
532                         }
533                 }
534         }
535
536         if (memb)
537         {
538                 CUList except_list;
539                 FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
540
541                 WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
542
543                 user->chans.erase(this);
544                 this->RemoveAllPrefixes(user);
545         }
546
547         this->DelUser(user);
548 }
549
550 void Channel::WriteChannel(User* user, const char* text, ...)
551 {
552         char textbuffer[MAXBUF];
553         va_list argsPtr;
554
555         if (!user || !text)
556                 return;
557
558         va_start(argsPtr, text);
559         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
560         va_end(argsPtr);
561
562         this->WriteChannel(user, std::string(textbuffer));
563 }
564
565 void Channel::WriteChannel(User* user, const std::string &text)
566 {
567         char tb[MAXBUF];
568
569         if (!user)
570                 return;
571
572         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
573         std::string out = tb;
574
575         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
576         {
577                 if (IS_LOCAL(i->first))
578                         i->first->Write(out);
579         }
580 }
581
582 void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
583 {
584         char textbuffer[MAXBUF];
585         va_list argsPtr;
586
587         if (!text)
588                 return;
589
590         va_start(argsPtr, text);
591         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
592         va_end(argsPtr);
593
594         this->WriteChannelWithServ(ServName, std::string(textbuffer));
595 }
596
597 void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
598 {
599         char tb[MAXBUF];
600
601         snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
602         std::string out = tb;
603
604         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
605         {
606                 if (IS_LOCAL(i->first))
607                         i->first->Write(out);
608         }
609 }
610
611 /* write formatted text from a source user to all users on a channel except
612  * for the sender (for privmsg etc) */
613 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
614 {
615         char textbuffer[MAXBUF];
616         va_list argsPtr;
617
618         if (!text)
619                 return;
620
621         va_start(argsPtr, text);
622         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
623         va_end(argsPtr);
624
625         this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
626 }
627
628 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
629 {
630         char textbuffer[MAXBUF];
631         va_list argsPtr;
632
633         if (!text)
634                 return;
635
636         int offset = snprintf(textbuffer,MAXBUF,":%s ", user->GetFullHost().c_str());
637
638         va_start(argsPtr, text);
639         vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
640         va_end(argsPtr);
641
642         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
643 }
644
645 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
646 {
647         char tb[MAXBUF];
648
649         snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
650         std::string out = tb;
651
652         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
653 }
654
655 void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
656 {
657         char statmode = 0;
658         if (status)
659         {
660                 ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
661                 if (mh)
662                         statmode = mh->GetModeChar();
663         }
664         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
665         {
666                 if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end()))
667                 {
668                         /* User doesnt have the status we're after */
669                         if (statmode && !i->second->hasMode(statmode))
670                                 continue;
671
672                         i->first->Write(out);
673                 }
674         }
675 }
676
677 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
678 {
679         CUList except_list;
680         except_list.insert(user);
681         this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
682 }
683
684 /*
685  * return a count of the users on a specific channel accounting for
686  * invisible users who won't increase the count. e.g. for /LIST
687  */
688 int Channel::CountInvisible()
689 {
690         int count = 0;
691         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
692         {
693                 if (!(i->first->IsModeSet('i')))
694                         count++;
695         }
696
697         return count;
698 }
699
700 char* Channel::ChanModes(bool showkey)
701 {
702         static char scratch[MAXBUF];
703         static char sparam[MAXBUF];
704         char* offset = scratch;
705         std::string extparam;
706
707         *scratch = '\0';
708         *sparam = '\0';
709
710         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
711         for(int n = 0; n < 64; n++)
712         {
713                 if(this->modes[n])
714                 {
715                         *offset++ = n + 65;
716                         extparam.clear();
717                         switch (n)
718                         {
719                                 case CM_KEY:
720                                         // Unfortunately this must be special-cased, as we definitely don't want to always display key.
721                                         if (showkey)
722                                         {
723                                                 extparam = this->GetModeParameter('k');
724                                         }
725                                         else
726                                         {
727                                                 extparam = "<key>";
728                                         }
729                                         break;
730                                 case CM_NOEXTERNAL:
731                                 case CM_TOPICLOCK:
732                                 case CM_INVITEONLY:
733                                 case CM_MODERATED:
734                                 case CM_SECRET:
735                                 case CM_PRIVATE:
736                                         /* We know these have no parameters */
737                                 break;
738                                 default:
739                                         extparam = this->GetModeParameter(n + 65);
740                                 break;
741                         }
742                         if (!extparam.empty())
743                         {
744                                 charlcat(sparam,' ',MAXBUF);
745                                 strlcat(sparam,extparam.c_str(),MAXBUF);
746                         }
747                 }
748         }
749
750         /* Null terminate scratch */
751         *offset = '\0';
752         strlcat(scratch,sparam,MAXBUF);
753         return scratch;
754 }
755
756 /* compile a userlist of a channel into a string, each nick seperated by
757  * spaces and op, voice etc status shown as @ and +, and send it to 'user'
758  */
759 void Channel::UserList(User *user)
760 {
761         char list[MAXBUF];
762         size_t dlen, curlen;
763         ModResult call_modules;
764
765         if (!IS_LOCAL(user))
766                 return;
767
768         FIRST_MOD_RESULT(OnUserList, call_modules, (user, this));
769
770         if (call_modules != MOD_RES_ALLOW)
771         {
772                 if ((this->IsModeSet('s')) && (!this->HasUser(user)))
773                 {
774                         user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
775                         return;
776                 }
777         }
778
779         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
780
781         int numusers = 0;
782         char* ptr = list + dlen;
783
784         /* Improvement by Brain - this doesnt change in value, so why was it inside
785          * the loop?
786          */
787         bool has_user = this->HasUser(user);
788
789         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
790         {
791                 if ((!has_user) && (i->first->IsModeSet('i')))
792                 {
793                         /*
794                          * user is +i, and source not on the channel, does not show
795                          * nick in NAMES list
796                          */
797                         continue;
798                 }
799
800                 std::string prefixlist = this->GetPrefixChar(i->first);
801                 std::string nick = i->first->nick;
802
803                 if (call_modules != MOD_RES_DENY)
804                 {
805                         FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
806
807                         /* Nick was nuked, a module wants us to skip it */
808                         if (nick.empty())
809                                 continue;
810                 }
811
812                 size_t ptrlen = 0;
813
814                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
815                 {
816                         /* list overflowed into multiple numerics */
817                         user->WriteNumeric(RPL_NAMREPLY, std::string(list));
818
819                         /* reset our lengths */
820                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
821                         ptr = list + dlen;
822
823                         ptrlen = 0;
824                         numusers = 0;
825                 }
826
827                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
828
829                 curlen += ptrlen;
830                 ptr += ptrlen;
831
832                 numusers++;
833         }
834
835         /* if whats left in the list isnt empty, send it */
836         if (numusers)
837         {
838                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
839         }
840
841         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
842 }
843
844 long Channel::GetMaxBans()
845 {
846         /* Return the cached value if there is one */
847         if (this->maxbans)
848                 return this->maxbans;
849
850         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
851         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
852         {
853                 if (InspIRCd::Match(this->name, n->first, NULL))
854                 {
855                         this->maxbans = n->second;
856                         return n->second;
857                 }
858         }
859
860         /* Screw it, just return the default of 64 */
861         this->maxbans = 64;
862         return this->maxbans;
863 }
864
865 void Channel::ResetMaxBans()
866 {
867         this->maxbans = 0;
868 }
869
870 /* returns the status character for a given user on a channel, e.g. @ for op,
871  * % for halfop etc. If the user has several modes set, the highest mode
872  * the user has must be returned.
873  */
874 const char* Channel::GetPrefixChar(User *user)
875 {
876         static char pf[2] = {0, 0};
877         *pf = 0;
878         unsigned int bestrank = 0;
879
880         UserMembIter m = userlist.find(user);
881         if (m != userlist.end())
882         {
883                 for(unsigned int i=0; i < m->second->modes.length(); i++)
884                 {
885                         char mchar = m->second->modes[i];
886                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
887                         if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
888                         {
889                                 bestrank = mh->GetPrefixRank();
890                                 pf[0] = mh->GetPrefix();
891                         }
892                 }
893         }
894         return pf;
895 }
896
897 unsigned int Membership::getRank()
898 {
899         char mchar = modes.c_str()[0];
900         unsigned int rv = 0;
901         if (mchar)
902         {
903                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
904                 if (mh)
905                         rv = mh->GetPrefixRank();
906         }
907         return rv;
908 }
909
910 const char* Channel::GetAllPrefixChars(User* user)
911 {
912         static char prefix[64];
913         int ctr = 0;
914
915         UserMembIter m = userlist.find(user);
916         if (m != userlist.end())
917         {
918                 for(unsigned int i=0; i < m->second->modes.length(); i++)
919                 {
920                         char mchar = m->second->modes[i];
921                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
922                         if (mh && mh->GetPrefix())
923                                 prefix[ctr++] = mh->GetPrefix();
924                 }
925         }
926         prefix[ctr] = 0;
927
928         return prefix;
929 }
930
931 unsigned int Channel::GetPrefixValue(User* user)
932 {
933         UserMembIter m = userlist.find(user);
934         if (m == userlist.end())
935                 return 0;
936         return m->second->getRank();
937 }
938
939 void Channel::SetPrefix(User* user, char prefix, bool adding)
940 {
941         ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
942         if (!delta_mh)
943                 return;
944         UserMembIter m = userlist.find(user);
945         if (m == userlist.end())
946                 return;
947         for(unsigned int i=0; i < m->second->modes.length(); i++)
948         {
949                 char mchar = m->second->modes[i];
950                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
951                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
952                 {
953                         m->second->modes =
954                                 m->second->modes.substr(0,i) +
955                                 (adding ? std::string(1, prefix) : "") +
956                                 m->second->modes.substr(mchar == prefix ? i+1 : i);
957                         return;
958                 }
959         }
960         if (adding)
961                 m->second->modes += std::string(1, prefix);
962 }
963
964 void Channel::RemoveAllPrefixes(User* user)
965 {
966         UserMembIter m = userlist.find(user);
967         if (m != userlist.end())
968         {
969                 m->second->modes.clear();
970         }
971 }