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