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