]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Stop recreating hashmaps every hour, move garbage collection code related to local...
[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, std::string cname, bool override, const std::string& key, bool bursting, time_t TS)
229 {
230         // Fix: unregistered users could be joined using /SAJOIN
231         if (!user || user->registered != REG_ALL)
232                 return NULL;
233
234         std::string privs;
235         Channel *Ptr;
236
237         /*
238          * We don't restrict the number of channels that remote users or users that are override-joining may be in.
239          * We restrict local users to MaxChans channels.
240          * We restrict local operators to OperMaxChans channels.
241          * This is a lot more logical than how it was formerly. -- w00t
242          */
243         if (IS_LOCAL(user) && !override)
244         {
245                 if (user->HasPrivPermission("channels/high-join-limit"))
246                 {
247                         if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
248                         {
249                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cname.c_str());
250                                 return NULL;
251                         }
252                 }
253                 else
254                 {
255                         unsigned int maxchans = user->GetClass()->maxchans;
256                         if (!maxchans)
257                                 maxchans = ServerInstance->Config->MaxChans;
258                         if (user->chans.size() >= maxchans)
259                         {
260                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cname.c_str());
261                                 return NULL;
262                         }
263                 }
264         }
265
266         if (cname.length() > ServerInstance->Config->Limits.ChanMax)
267                 cname.resize(ServerInstance->Config->Limits.ChanMax);
268
269         Ptr = ServerInstance->FindChan(cname);
270         bool created_by_local = false;
271
272         if (!Ptr)
273         {
274                 /*
275                  * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
276                  */
277                 if (!IS_LOCAL(user))
278                 {
279                         if (!TS)
280                                 ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cname.c_str());
281                 }
282                 else
283                 {
284                         privs = "o";
285                         created_by_local = true;
286                 }
287
288                 if (IS_LOCAL(user) && override == false)
289                 {
290                         ModResult MOD_RESULT;
291                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
292                         if (MOD_RESULT == MOD_RES_DENY)
293                                 return NULL;
294                 }
295
296                 Ptr = new Channel(cname, TS);
297         }
298         else
299         {
300                 /* Already on the channel */
301                 if (Ptr->HasUser(user))
302                         return NULL;
303
304                 /*
305                  * remote users are allowed us to bypass channel modes
306                  * and bans (used by servers)
307                  */
308                 if (IS_LOCAL(user) && override == false)
309                 {
310                         ModResult MOD_RESULT;
311                         FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname, privs, key));
312                         if (MOD_RESULT == MOD_RES_DENY)
313                         {
314                                 return NULL;
315                         }
316                         else if (MOD_RESULT == MOD_RES_PASSTHRU)
317                         {
318                                 std::string ckey = Ptr->GetModeParameter('k');
319                                 bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
320                                 bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
321
322                                 if (!ckey.empty())
323                                 {
324                                         FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key));
325                                         if (!MOD_RESULT.check((ckey == key) || can_bypass))
326                                         {
327                                                 // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
328                                                 user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
329                                                 return NULL;
330                                         }
331                                 }
332
333                                 if (Ptr->IsModeSet('i'))
334                                 {
335                                         FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
336                                         if (!MOD_RESULT.check(invited))
337                                         {
338                                                 user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
339                                                 return NULL;
340                                         }
341                                 }
342
343                                 std::string limit = Ptr->GetModeParameter('l');
344                                 if (!limit.empty())
345                                 {
346                                         FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
347                                         if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
348                                         {
349                                                 user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
350                                                 return NULL;
351                                         }
352                                 }
353
354                                 if (Ptr->IsBanned(user) && !can_bypass)
355                                 {
356                                         user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
357                                         return NULL;
358                                 }
359
360                                 /*
361                                  * If the user has invites for this channel, remove them now
362                                  * after a successful join so they don't build up.
363                                  */
364                                 if (invited)
365                                 {
366                                         IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
367                                 }
368                         }
369                 }
370         }
371
372         if (created_by_local)
373         {
374                 /* As spotted by jilles, dont bother to set this on remote users */
375                 Ptr->SetDefaultModes();
376         }
377
378         return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
379 }
380
381 Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
382 {
383         std::string nick = user->nick;
384
385         Membership* memb = Ptr->AddUser(user);
386         user->chans.insert(Ptr);
387
388         for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
389         {
390                 const char status = *x;
391                 ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
392                 if (mh)
393                 {
394                         /* Set, and make sure that the mode handler knows this mode was now set */
395                         Ptr->SetPrefix(user, mh->GetModeChar(), true);
396                         mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
397                 }
398         }
399
400         CUList except_list;
401         FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
402
403         Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
404
405         /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
406         if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
407         {
408                 std::string ms = memb->modes;
409                 for(unsigned int i=0; i < memb->modes.length(); i++)
410                         ms.append(" ").append(user->nick);
411
412                 except_list.insert(user);
413                 Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
414         }
415
416         if (IS_LOCAL(user))
417         {
418                 if (Ptr->topicset)
419                 {
420                         user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
421                         user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
422                 }
423                 Ptr->UserList(user);
424         }
425         FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
426         return Ptr;
427 }
428
429 bool Channel::IsBanned(User* user)
430 {
431         ModResult result;
432         FIRST_MOD_RESULT(OnCheckChannelBan, result, (user, this));
433
434         if (result != MOD_RES_PASSTHRU)
435                 return (result == MOD_RES_DENY);
436
437         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
438         {
439                 if (CheckBan(user, i->data))
440                         return true;
441         }
442         return false;
443 }
444
445 bool Channel::CheckBan(User* user, const std::string& mask)
446 {
447         ModResult result;
448         FIRST_MOD_RESULT(OnCheckBan, result, (user, this, mask));
449         if (result != MOD_RES_PASSTHRU)
450                 return (result == MOD_RES_DENY);
451
452         // extbans were handled above, if this is one it obviously didn't match
453         if ((mask.length() <= 2) || (mask[1] == ':'))
454                 return false;
455
456         std::string::size_type at = mask.find('@');
457         if (at == std::string::npos)
458                 return false;
459
460         char tomatch[MAXBUF];
461         snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
462         std::string prefix = mask.substr(0, at);
463         if (InspIRCd::Match(tomatch, prefix, NULL))
464         {
465                 std::string suffix = mask.substr(at + 1);
466                 if (InspIRCd::Match(user->host, suffix, NULL) ||
467                         InspIRCd::Match(user->dhost, suffix, NULL) ||
468                         InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL))
469                         return true;
470         }
471         return false;
472 }
473
474 ModResult Channel::GetExtBanStatus(User *user, char type)
475 {
476         ModResult rv;
477         FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
478         if (rv != MOD_RES_PASSTHRU)
479                 return rv;
480         for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
481         {
482                 if (i->data[0] == type && i->data[1] == ':')
483                 {
484                         std::string val = i->data.substr(2);
485                         if (CheckBan(user, val))
486                                 return MOD_RES_DENY;
487                 }
488         }
489         return MOD_RES_PASSTHRU;
490 }
491
492 /* Channel::PartUser
493  * remove a channel from a users record, and return the number of users left.
494  * Therefore, if this function returns 0 the caller should delete the Channel.
495  */
496 void Channel::PartUser(User *user, std::string &reason)
497 {
498         if (!user)
499                 return;
500
501         Membership* memb = GetUser(user);
502
503         if (memb)
504         {
505                 CUList except_list;
506                 FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
507
508                 WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
509
510                 user->chans.erase(this);
511                 this->RemoveAllPrefixes(user);
512         }
513
514         this->DelUser(user);
515 }
516
517 void Channel::KickUser(User *src, User *user, const char* reason)
518 {
519         if (!src || !user || !reason)
520                 return;
521
522         Membership* memb = GetUser(user);
523         if (IS_LOCAL(src))
524         {
525                 if (!memb)
526                 {
527                         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());
528                         return;
529                 }
530                 if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
531                 {
532                         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());
533                         return;
534                 }
535
536                 ModResult res;
537                 if (ServerInstance->ULine(src->server))
538                         res = MOD_RES_ALLOW;
539                 else
540                         FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
541
542                 if (res == MOD_RES_DENY)
543                         return;
544
545                 if (res == MOD_RES_PASSTHRU)
546                 {
547                         unsigned int them = this->GetPrefixValue(src);
548                         unsigned int req = HALFOP_VALUE;
549                         for (std::string::size_type i = 0; i < memb->modes.length(); i++)
550                         {
551                                 ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
552                                 if (mh && mh->GetLevelRequired() > req)
553                                         req = mh->GetLevelRequired();
554                         }
555
556                         if (them < req)
557                         {
558                                 src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
559                                         src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
560                                 return;
561                         }
562                 }
563         }
564
565         if (memb)
566         {
567                 CUList except_list;
568                 FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
569
570                 WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
571
572                 user->chans.erase(this);
573                 this->RemoveAllPrefixes(user);
574         }
575
576         this->DelUser(user);
577 }
578
579 void Channel::WriteChannel(User* user, const char* text, ...)
580 {
581         char textbuffer[MAXBUF];
582         va_list argsPtr;
583
584         if (!user || !text)
585                 return;
586
587         va_start(argsPtr, text);
588         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
589         va_end(argsPtr);
590
591         this->WriteChannel(user, std::string(textbuffer));
592 }
593
594 void Channel::WriteChannel(User* user, const std::string &text)
595 {
596         char tb[MAXBUF];
597
598         if (!user)
599                 return;
600
601         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
602         std::string out = tb;
603
604         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
605         {
606                 if (IS_LOCAL(i->first))
607                         i->first->Write(out);
608         }
609 }
610
611 void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
612 {
613         char textbuffer[MAXBUF];
614         va_list argsPtr;
615
616         if (!text)
617                 return;
618
619         va_start(argsPtr, text);
620         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
621         va_end(argsPtr);
622
623         this->WriteChannelWithServ(ServName, std::string(textbuffer));
624 }
625
626 void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
627 {
628         char tb[MAXBUF];
629
630         snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
631         std::string out = tb;
632
633         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
634         {
635                 if (IS_LOCAL(i->first))
636                         i->first->Write(out);
637         }
638 }
639
640 /* write formatted text from a source user to all users on a channel except
641  * for the sender (for privmsg etc) */
642 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
643 {
644         char textbuffer[MAXBUF];
645         va_list argsPtr;
646
647         if (!text)
648                 return;
649
650         va_start(argsPtr, text);
651         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
652         va_end(argsPtr);
653
654         this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
655 }
656
657 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
658 {
659         char textbuffer[MAXBUF];
660         va_list argsPtr;
661
662         if (!text)
663                 return;
664
665         int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
666
667         va_start(argsPtr, text);
668         vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
669         va_end(argsPtr);
670
671         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
672 }
673
674 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
675 {
676         char tb[MAXBUF];
677
678         snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
679         std::string out = tb;
680
681         this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
682 }
683
684 void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
685 {
686         unsigned int minrank = 0;
687         if (status)
688         {
689                 ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
690                 if (mh)
691                         minrank = mh->GetPrefixRank();
692         }
693         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
694         {
695                 if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
696                 {
697                         /* User doesn't have the status we're after */
698                         if (minrank && i->second->getRank() < minrank)
699                                 continue;
700
701                         i->first->Write(out);
702                 }
703         }
704 }
705
706 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
707 {
708         CUList except_list;
709         except_list.insert(user);
710         this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
711 }
712
713 /*
714  * return a count of the users on a specific channel accounting for
715  * invisible users who won't increase the count. e.g. for /LIST
716  */
717 int Channel::CountInvisible()
718 {
719         int count = 0;
720         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
721         {
722                 if (!i->first->quitting && !i->first->IsModeSet('i'))
723                         count++;
724         }
725
726         return count;
727 }
728
729 char* Channel::ChanModes(bool showkey)
730 {
731         static char scratch[MAXBUF];
732         static char sparam[MAXBUF];
733         char* offset = scratch;
734         std::string extparam;
735
736         *scratch = '\0';
737         *sparam = '\0';
738
739         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
740         for(int n = 0; n < 64; n++)
741         {
742                 if(this->modes[n])
743                 {
744                         *offset++ = n + 65;
745                         extparam.clear();
746                         if (n == 'k' - 65 && !showkey)
747                         {
748                                 extparam = "<key>";
749                         }
750                         else
751                         {
752                                 extparam = this->GetModeParameter(n + 65);
753                         }
754                         if (!extparam.empty())
755                         {
756                                 charlcat(sparam,' ',MAXBUF);
757                                 strlcat(sparam,extparam.c_str(),MAXBUF);
758                         }
759                 }
760         }
761
762         /* Null terminate scratch */
763         *offset = '\0';
764         strlcat(scratch,sparam,MAXBUF);
765         return scratch;
766 }
767
768 /* compile a userlist of a channel into a string, each nick seperated by
769  * spaces and op, voice etc status shown as @ and +, and send it to 'user'
770  */
771 void Channel::UserList(User *user)
772 {
773         char list[MAXBUF];
774         size_t dlen, curlen;
775
776         if (!IS_LOCAL(user))
777                 return;
778
779         if (this->IsModeSet('s') && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
780         {
781                 user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
782                 return;
783         }
784
785         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
786
787         int numusers = 0;
788         char* ptr = list + dlen;
789
790         /* Improvement by Brain - this doesnt change in value, so why was it inside
791          * the loop?
792          */
793         bool has_user = this->HasUser(user);
794
795         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
796         {
797                 if (i->first->quitting)
798                         continue;
799                 if ((!has_user) && (i->first->IsModeSet('i')))
800                 {
801                         /*
802                          * user is +i, and source not on the channel, does not show
803                          * nick in NAMES list
804                          */
805                         continue;
806                 }
807
808                 std::string prefixlist = this->GetPrefixChar(i->first);
809                 std::string nick = i->first->nick;
810
811                 FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
812
813                 /* Nick was nuked, a module wants us to skip it */
814                 if (nick.empty())
815                         continue;
816
817                 size_t ptrlen = 0;
818
819                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
820                 {
821                         /* list overflowed into multiple numerics */
822                         user->WriteNumeric(RPL_NAMREPLY, std::string(list));
823
824                         /* reset our lengths */
825                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
826                         ptr = list + dlen;
827
828                         numusers = 0;
829                 }
830
831                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
832
833                 curlen += ptrlen;
834                 ptr += ptrlen;
835
836                 numusers++;
837         }
838
839         /* if whats left in the list isnt empty, send it */
840         if (numusers)
841         {
842                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
843         }
844
845         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
846 }
847
848 long Channel::GetMaxBans()
849 {
850         /* Return the cached value if there is one */
851         if (this->maxbans)
852                 return this->maxbans;
853
854         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
855         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
856         {
857                 if (InspIRCd::Match(this->name, n->first, NULL))
858                 {
859                         this->maxbans = n->second;
860                         return n->second;
861                 }
862         }
863
864         /* Screw it, just return the default of 64 */
865         this->maxbans = 64;
866         return this->maxbans;
867 }
868
869 void Channel::ResetMaxBans()
870 {
871         this->maxbans = 0;
872 }
873
874 /* returns the status character for a given user on a channel, e.g. @ for op,
875  * % for halfop etc. If the user has several modes set, the highest mode
876  * the user has must be returned.
877  */
878 const char* Channel::GetPrefixChar(User *user)
879 {
880         static char pf[2] = {0, 0};
881         *pf = 0;
882         unsigned int bestrank = 0;
883
884         UserMembIter m = userlist.find(user);
885         if (m != userlist.end())
886         {
887                 for(unsigned int i=0; i < m->second->modes.length(); i++)
888                 {
889                         char mchar = m->second->modes[i];
890                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
891                         if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
892                         {
893                                 bestrank = mh->GetPrefixRank();
894                                 pf[0] = mh->GetPrefix();
895                         }
896                 }
897         }
898         return pf;
899 }
900
901 unsigned int Membership::getRank()
902 {
903         char mchar = modes.c_str()[0];
904         unsigned int rv = 0;
905         if (mchar)
906         {
907                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
908                 if (mh)
909                         rv = mh->GetPrefixRank();
910         }
911         return rv;
912 }
913
914 const char* Channel::GetAllPrefixChars(User* user)
915 {
916         static char prefix[64];
917         int ctr = 0;
918
919         UserMembIter m = userlist.find(user);
920         if (m != userlist.end())
921         {
922                 for(unsigned int i=0; i < m->second->modes.length(); i++)
923                 {
924                         char mchar = m->second->modes[i];
925                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
926                         if (mh && mh->GetPrefix())
927                                 prefix[ctr++] = mh->GetPrefix();
928                 }
929         }
930         prefix[ctr] = 0;
931
932         return prefix;
933 }
934
935 unsigned int Channel::GetPrefixValue(User* user)
936 {
937         UserMembIter m = userlist.find(user);
938         if (m == userlist.end())
939                 return 0;
940         return m->second->getRank();
941 }
942
943 bool Channel::SetPrefix(User* user, char prefix, bool adding)
944 {
945         ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
946         if (!delta_mh)
947                 return false;
948         UserMembIter m = userlist.find(user);
949         if (m == userlist.end())
950                 return false;
951         for(unsigned int i=0; i < m->second->modes.length(); i++)
952         {
953                 char mchar = m->second->modes[i];
954                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
955                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
956                 {
957                         m->second->modes =
958                                 m->second->modes.substr(0,i) +
959                                 (adding ? std::string(1, prefix) : "") +
960                                 m->second->modes.substr(mchar == prefix ? i+1 : i);
961                         return adding != (mchar == prefix);
962                 }
963         }
964         if (adding)
965                 m->second->modes += std::string(1, prefix);
966         return adding;
967 }
968
969 void Channel::RemoveAllPrefixes(User* user)
970 {
971         UserMembIter m = userlist.find(user);
972         if (m != userlist.end())
973         {
974                 m->second->modes.clear();
975         }
976 }
977
978 void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
979 {
980         if ((timeout != 0) && (ServerInstance->Time() >= timeout))
981                 // Expired, don't bother
982                 return;
983
984         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
985
986         Invitation* inv = Invitation::Find(c, u, false);
987         if (inv)
988         {
989                  if ((inv->expiry == 0) || (inv->expiry > timeout))
990                         return;
991                 inv->expiry = timeout;
992                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
993         }
994         else
995         {
996                 inv = new Invitation(c, u, timeout);
997                 c->invites.push_back(inv);
998                 u->invites.push_back(inv);
999                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
1000         }
1001 }
1002
1003 Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
1004 {
1005         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);
1006         if (!u || u->invites.empty())
1007                 return NULL;
1008
1009         InviteList locallist;
1010         locallist.swap(u->invites);
1011
1012         Invitation* result = NULL;
1013         for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
1014         {
1015                 Invitation* inv = *i;
1016                 if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
1017                 {
1018                         /* Expired invite, remove it. */
1019                         std::string expiration = ServerInstance->TimeString(inv->expiry);
1020                         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
1021                         i = locallist.erase(i);
1022                         inv->cull();
1023                         delete inv;
1024                 }
1025                 else
1026                 {
1027                         /* Is it what we're searching for? */
1028                         if (inv->chan == c)
1029                         {
1030                                 result = inv;
1031                                 break;
1032                         }
1033                         ++i;
1034                 }
1035         }
1036
1037         locallist.swap(u->invites);
1038         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
1039         return result;
1040 }
1041
1042 Invitation::~Invitation()
1043 {
1044         // Remove this entry from both lists
1045         InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
1046         if (it != chan->invites.end())
1047                 chan->invites.erase(it);
1048         it = std::find(user->invites.begin(), user->invites.end(), this);
1049         if (it != user->invites.end())
1050                 user->invites.erase(it);
1051 }
1052
1053 void InviteBase::ClearInvites()
1054 {
1055         ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
1056         InviteList locallist;
1057         locallist.swap(invites);
1058         for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
1059         {
1060                 (*i)->cull();
1061                 delete *i;
1062         }
1063 }