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