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