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