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