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