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