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