]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Kill m_spy, which was replaced by channels/auspex in 1.2 but kept alive for nostalgia
[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                         FIRST_MOD_RESULT(OnChannelRestrictionApply, res, (u,this,"topiclock"));
96                         bool defok = IsModeSet('t') ? GetPrefixValue(u) >= HALFOP_VALUE : HasUser(u);
97                         if (!res.check(defok))
98                         {
99                                 if (!this->HasUser(u))
100                                         u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
101                                 else
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, true, 0, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
396
397         /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
398         if (IS_LOCAL(user))
399         {
400                 if (Ptr->topicset)
401                 {
402                         user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
403                         user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
404                 }
405                 Ptr->UserList(user);
406         }
407         FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
408         return Ptr;
409 }
410
411 bool Channel::IsBanned(User* user)
412 {
413         ModResult result;
414         FIRST_MOD_RESULT(OnCheckChannelBan, result, (user, this));
415
416         if (result != MOD_RES_PASSTHRU)
417                 return (result == MOD_RES_DENY);
418
419         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
420         {
421                 if (CheckBan(user, i->data))
422                         return true;
423         }
424         return false;
425 }
426
427 bool Channel::CheckBan(User* user, const std::string& mask)
428 {
429         ModResult result;
430         FIRST_MOD_RESULT(OnCheckBan, result, (user, this, mask));
431         if (result != MOD_RES_PASSTHRU)
432                 return (result == MOD_RES_DENY);
433
434         // extbans were handled above, if this is one it obviously didn't match
435         if (mask[1] == ':')
436                 return false;
437
438         std::string::size_type at = mask.find('@');
439         if (at == std::string::npos)
440                 return false;
441
442         char tomatch[MAXBUF];
443         snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
444         std::string prefix = mask.substr(0, at);
445         if (InspIRCd::Match(tomatch, prefix, NULL))
446         {
447                 std::string suffix = mask.substr(at + 1);
448                 if (InspIRCd::Match(user->host, suffix, NULL) ||
449                         InspIRCd::Match(user->dhost, suffix, NULL) ||
450                         InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
451                         return true;
452         }
453         return false;
454 }
455
456 ModResult Channel::GetExtBanStatus(User *user, char type)
457 {
458         ModResult rv;
459         FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
460         if (rv != MOD_RES_PASSTHRU)
461                 return rv;
462         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
463         {
464                 if (i->data[0] == type && i->data[1] == ':')
465                 {
466                         std::string val = i->data.substr(2);
467                         if (CheckBan(user, val))
468                                 return MOD_RES_DENY;
469                 }
470         }
471         return MOD_RES_PASSTHRU;
472 }
473
474 /* Channel::PartUser
475  * remove a channel from a users record, and return the number of users left.
476  * Therefore, if this function returns 0 the caller should delete the Channel.
477  */
478 void Channel::PartUser(User *user, std::string &reason)
479 {
480         if (!user)
481                 return;
482
483         Membership* memb = GetUser(user);
484
485         if (memb)
486         {
487                 CUList except_list;
488                 FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
489
490                 WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
491
492                 user->chans.erase(this);
493                 this->RemoveAllPrefixes(user);
494         }
495
496         this->DelUser(user);
497 }
498
499 void Channel::KickUser(User *src, User *user, const char* reason)
500 {
501         if (!src || !user || !reason)
502                 return;
503
504         Membership* memb = GetUser(user);
505         if (IS_LOCAL(src))
506         {
507                 if (!memb)
508                 {
509                         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());
510                         return;
511                 }
512                 if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
513                 {
514                         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());
515                         return;
516                 }
517
518                 ModResult res;
519                 if (ServerInstance->ULine(src->server))
520                         res = MOD_RES_ALLOW;
521                 else
522                         FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
523
524                 if (res == MOD_RES_DENY)
525                         return;
526
527                 if (res == MOD_RES_PASSTHRU)
528                 {
529                         int them = this->GetPrefixValue(src);
530                         int us = this->GetPrefixValue(user);
531                         if ((them < HALFOP_VALUE) || (them < us))
532                         {
533                                 src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",src->nick.c_str(), this->name.c_str(), them >= HALFOP_VALUE ? "" : "half-");
534                                 return;
535                         }
536                 }
537         }
538
539         if (memb)
540         {
541                 CUList except_list;
542                 FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
543
544                 WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
545
546                 user->chans.erase(this);
547                 this->RemoveAllPrefixes(user);
548         }
549
550         this->DelUser(user);
551 }
552
553 void Channel::WriteChannel(User* user, const char* text, ...)
554 {
555         char textbuffer[MAXBUF];
556         va_list argsPtr;
557
558         if (!user || !text)
559                 return;
560
561         va_start(argsPtr, text);
562         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
563         va_end(argsPtr);
564
565         this->WriteChannel(user, std::string(textbuffer));
566 }
567
568 void Channel::WriteChannel(User* user, const std::string &text)
569 {
570         char tb[MAXBUF];
571
572         if (!user)
573                 return;
574
575         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
576         std::string out = tb;
577
578         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
579         {
580                 if (IS_LOCAL(i->first))
581                         i->first->Write(out);
582         }
583 }
584
585 void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
586 {
587         char textbuffer[MAXBUF];
588         va_list argsPtr;
589
590         if (!text)
591                 return;
592
593         va_start(argsPtr, text);
594         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
595         va_end(argsPtr);
596
597         this->WriteChannelWithServ(ServName, std::string(textbuffer));
598 }
599
600 void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
601 {
602         char tb[MAXBUF];
603
604         snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
605         std::string out = tb;
606
607         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
608         {
609                 if (IS_LOCAL(i->first))
610                         i->first->Write(out);
611         }
612 }
613
614 /* write formatted text from a source user to all users on a channel except
615  * for the sender (for privmsg etc) */
616 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
617 {
618         char textbuffer[MAXBUF];
619         va_list argsPtr;
620
621         if (!text)
622                 return;
623
624         va_start(argsPtr, text);
625         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
626         va_end(argsPtr);
627
628         this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
629 }
630
631 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
632 {
633         char textbuffer[MAXBUF];
634         va_list argsPtr;
635
636         if (!text)
637                 return;
638
639         int offset = snprintf(textbuffer,MAXBUF,":%s ", user->GetFullHost().c_str());
640
641         va_start(argsPtr, text);
642         vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
643         va_end(argsPtr);
644
645         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
646 }
647
648 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
649 {
650         char tb[MAXBUF];
651
652         snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
653         std::string out = tb;
654
655         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
656 }
657
658 void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
659 {
660         unsigned int minrank = 0;
661         if (status)
662         {
663                 ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
664                 if (mh)
665                         minrank = mh->GetPrefixRank();
666         }
667         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
668         {
669                 if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
670                 {
671                         /* User doesn't have the status we're after */
672                         if (minrank && i->second->getRank() < minrank)
673                                 continue;
674
675                         i->first->Write(out);
676                 }
677         }
678 }
679
680 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
681 {
682         CUList except_list;
683         except_list.insert(user);
684         this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
685 }
686
687 /*
688  * return a count of the users on a specific channel accounting for
689  * invisible users who won't increase the count. e.g. for /LIST
690  */
691 int Channel::CountInvisible()
692 {
693         int count = 0;
694         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
695         {
696                 if (!(i->first->IsModeSet('i')))
697                         count++;
698         }
699
700         return count;
701 }
702
703 char* Channel::ChanModes(bool showkey)
704 {
705         static char scratch[MAXBUF];
706         static char sparam[MAXBUF];
707         char* offset = scratch;
708         std::string extparam;
709
710         *scratch = '\0';
711         *sparam = '\0';
712
713         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
714         for(int n = 0; n < 64; n++)
715         {
716                 if(this->modes[n])
717                 {
718                         *offset++ = n + 65;
719                         extparam.clear();
720                         if (n == 'k' - 65 && !showkey)
721                         {
722                                 extparam = "<key>";
723                         }
724                         else
725                         {
726                                 extparam = this->GetModeParameter(n + 65);
727                         }
728                         if (!extparam.empty())
729                         {
730                                 charlcat(sparam,' ',MAXBUF);
731                                 strlcat(sparam,extparam.c_str(),MAXBUF);
732                         }
733                 }
734         }
735
736         /* Null terminate scratch */
737         *offset = '\0';
738         strlcat(scratch,sparam,MAXBUF);
739         return scratch;
740 }
741
742 /* compile a userlist of a channel into a string, each nick seperated by
743  * spaces and op, voice etc status shown as @ and +, and send it to 'user'
744  */
745 void Channel::UserList(User *user)
746 {
747         char list[MAXBUF];
748         size_t dlen, curlen;
749
750         if (!IS_LOCAL(user))
751                 return;
752
753         if (this->IsModeSet('s') && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
754         {
755                 user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
756                 return;
757         }
758
759         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
760
761         int numusers = 0;
762         char* ptr = list + dlen;
763
764         /* Improvement by Brain - this doesnt change in value, so why was it inside
765          * the loop?
766          */
767         bool has_user = this->HasUser(user);
768
769         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
770         {
771                 if ((!has_user) && (i->first->IsModeSet('i')))
772                 {
773                         /*
774                          * user is +i, and source not on the channel, does not show
775                          * nick in NAMES list
776                          */
777                         continue;
778                 }
779
780                 std::string prefixlist = this->GetPrefixChar(i->first);
781                 std::string nick = i->first->nick;
782
783                 FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
784
785                 /* Nick was nuked, a module wants us to skip it */
786                 if (nick.empty())
787                         continue;
788
789                 size_t ptrlen = 0;
790
791                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
792                 {
793                         /* list overflowed into multiple numerics */
794                         user->WriteNumeric(RPL_NAMREPLY, std::string(list));
795
796                         /* reset our lengths */
797                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
798                         ptr = list + dlen;
799
800                         ptrlen = 0;
801                         numusers = 0;
802                 }
803
804                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
805
806                 curlen += ptrlen;
807                 ptr += ptrlen;
808
809                 numusers++;
810         }
811
812         /* if whats left in the list isnt empty, send it */
813         if (numusers)
814         {
815                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
816         }
817
818         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
819 }
820
821 long Channel::GetMaxBans()
822 {
823         /* Return the cached value if there is one */
824         if (this->maxbans)
825                 return this->maxbans;
826
827         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
828         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
829         {
830                 if (InspIRCd::Match(this->name, n->first, NULL))
831                 {
832                         this->maxbans = n->second;
833                         return n->second;
834                 }
835         }
836
837         /* Screw it, just return the default of 64 */
838         this->maxbans = 64;
839         return this->maxbans;
840 }
841
842 void Channel::ResetMaxBans()
843 {
844         this->maxbans = 0;
845 }
846
847 /* returns the status character for a given user on a channel, e.g. @ for op,
848  * % for halfop etc. If the user has several modes set, the highest mode
849  * the user has must be returned.
850  */
851 const char* Channel::GetPrefixChar(User *user)
852 {
853         static char pf[2] = {0, 0};
854         *pf = 0;
855         unsigned int bestrank = 0;
856
857         UserMembIter m = userlist.find(user);
858         if (m != userlist.end())
859         {
860                 for(unsigned int i=0; i < m->second->modes.length(); i++)
861                 {
862                         char mchar = m->second->modes[i];
863                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
864                         if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
865                         {
866                                 bestrank = mh->GetPrefixRank();
867                                 pf[0] = mh->GetPrefix();
868                         }
869                 }
870         }
871         return pf;
872 }
873
874 unsigned int Membership::getRank()
875 {
876         char mchar = modes.c_str()[0];
877         unsigned int rv = 0;
878         if (mchar)
879         {
880                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
881                 if (mh)
882                         rv = mh->GetPrefixRank();
883         }
884         return rv;
885 }
886
887 const char* Channel::GetAllPrefixChars(User* user)
888 {
889         static char prefix[64];
890         int ctr = 0;
891
892         UserMembIter m = userlist.find(user);
893         if (m != userlist.end())
894         {
895                 for(unsigned int i=0; i < m->second->modes.length(); i++)
896                 {
897                         char mchar = m->second->modes[i];
898                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
899                         if (mh && mh->GetPrefix())
900                                 prefix[ctr++] = mh->GetPrefix();
901                 }
902         }
903         prefix[ctr] = 0;
904
905         return prefix;
906 }
907
908 unsigned int Channel::GetPrefixValue(User* user)
909 {
910         UserMembIter m = userlist.find(user);
911         if (m == userlist.end())
912                 return 0;
913         return m->second->getRank();
914 }
915
916 bool Channel::SetPrefix(User* user, char prefix, bool adding)
917 {
918         ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
919         if (!delta_mh)
920                 return false;
921         UserMembIter m = userlist.find(user);
922         if (m == userlist.end())
923                 return false;
924         for(unsigned int i=0; i < m->second->modes.length(); i++)
925         {
926                 char mchar = m->second->modes[i];
927                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
928                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
929                 {
930                         m->second->modes =
931                                 m->second->modes.substr(0,i) +
932                                 (adding ? std::string(1, prefix) : "") +
933                                 m->second->modes.substr(mchar == prefix ? i+1 : i);
934                         return adding != (mchar == prefix);
935                 }
936         }
937         if (adding)
938                 m->second->modes += std::string(1, prefix);
939         return adding;
940 }
941
942 void Channel::RemoveAllPrefixes(User* user)
943 {
944         UserMembIter m = userlist.find(user);
945         if (m != userlist.end())
946         {
947                 m->second->modes.clear();
948         }
949 }