]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Remove unused variables, avoid copies where possible, check empty() instead of size...
[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         if (u)
120         {
121                 this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
122                 this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
123         }
124         else
125         {
126                 this->setby.assign(ServerInstance->Config->ServerName);
127                 this->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
128         }
129
130         this->topicset = ServerInstance->Time();
131
132         FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
133
134         return CMD_SUCCESS;
135 }
136
137 long Channel::GetUserCounter()
138 {
139         return userlist.size();
140 }
141
142 Membership* Channel::AddUser(User* user)
143 {
144         Membership* memb = new Membership(user, this);
145         userlist[user] = memb;
146         return memb;
147 }
148
149 void Channel::DelUser(User* user)
150 {
151         UserMembIter a = userlist.find(user);
152
153         if (a != userlist.end())
154         {
155                 a->second->cull();
156                 delete a->second;
157                 userlist.erase(a);
158         }
159
160         if (userlist.empty())
161         {
162                 ModResult res;
163                 FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
164                 if (res == MOD_RES_DENY)
165                         return;
166                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
167                 /* kill the record */
168                 if (iter != ServerInstance->chanlist->end())
169                 {
170                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
171                         ServerInstance->chanlist->erase(iter);
172                 }
173
174                 ClearInvites();
175                 ServerInstance->GlobalCulls.AddItem(this);
176         }
177 }
178
179 bool Channel::HasUser(User* user)
180 {
181         return (userlist.find(user) != userlist.end());
182 }
183
184 Membership* Channel::GetUser(User* user)
185 {
186         UserMembIter i = userlist.find(user);
187         if (i == userlist.end())
188                 return NULL;
189         return i->second;
190 }
191
192 const UserMembList* Channel::GetUsers()
193 {
194         return &userlist;
195 }
196
197 void Channel::SetDefaultModes()
198 {
199         ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
200                 ServerInstance->Config->DefaultModes.c_str());
201         irc::spacesepstream list(ServerInstance->Config->DefaultModes);
202         std::string modeseq;
203         std::string parameter;
204
205         list.GetToken(modeseq);
206
207         for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
208         {
209                 ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
210                 if (mode)
211                 {
212                         if (mode->GetNumParams(true))
213                                 list.GetToken(parameter);
214                         else
215                                 parameter.clear();
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         char list[MAXBUF];
770         size_t dlen, curlen;
771
772         if (!IS_LOCAL(user))
773                 return;
774
775         if (this->IsModeSet('s') && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
776         {
777                 user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
778                 return;
779         }
780
781         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
782
783         int numusers = 0;
784         char* ptr = list + dlen;
785
786         /* Improvement by Brain - this doesnt change in value, so why was it inside
787          * the loop?
788          */
789         bool has_user = this->HasUser(user);
790
791         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
792         {
793                 if (i->first->quitting)
794                         continue;
795                 if ((!has_user) && (i->first->IsModeSet('i')))
796                 {
797                         /*
798                          * user is +i, and source not on the channel, does not show
799                          * nick in NAMES list
800                          */
801                         continue;
802                 }
803
804                 std::string prefixlist = this->GetPrefixChar(i->first);
805                 std::string nick = i->first->nick;
806
807                 FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
808
809                 /* Nick was nuked, a module wants us to skip it */
810                 if (nick.empty())
811                         continue;
812
813                 size_t ptrlen = 0;
814
815                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
816                 {
817                         /* list overflowed into multiple numerics */
818                         user->WriteNumeric(RPL_NAMREPLY, std::string(list));
819
820                         /* reset our lengths */
821                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
822                         ptr = list + dlen;
823
824                         numusers = 0;
825                 }
826
827                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
828
829                 curlen += ptrlen;
830                 ptr += ptrlen;
831
832                 numusers++;
833         }
834
835         /* if whats left in the list isnt empty, send it */
836         if (numusers)
837         {
838                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
839         }
840
841         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
842 }
843
844 long Channel::GetMaxBans()
845 {
846         /* Return the cached value if there is one */
847         if (this->maxbans)
848                 return this->maxbans;
849
850         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
851         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
852         {
853                 if (InspIRCd::Match(this->name, n->first, NULL))
854                 {
855                         this->maxbans = n->second;
856                         return n->second;
857                 }
858         }
859
860         /* Screw it, just return the default of 64 */
861         this->maxbans = 64;
862         return this->maxbans;
863 }
864
865 void Channel::ResetMaxBans()
866 {
867         this->maxbans = 0;
868 }
869
870 /* returns the status character for a given user on a channel, e.g. @ for op,
871  * % for halfop etc. If the user has several modes set, the highest mode
872  * the user has must be returned.
873  */
874 const char* Channel::GetPrefixChar(User *user)
875 {
876         static char pf[2] = {0, 0};
877         *pf = 0;
878         unsigned int bestrank = 0;
879
880         UserMembIter m = userlist.find(user);
881         if (m != userlist.end())
882         {
883                 for(unsigned int i=0; i < m->second->modes.length(); i++)
884                 {
885                         char mchar = m->second->modes[i];
886                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
887                         if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
888                         {
889                                 bestrank = mh->GetPrefixRank();
890                                 pf[0] = mh->GetPrefix();
891                         }
892                 }
893         }
894         return pf;
895 }
896
897 unsigned int Membership::getRank()
898 {
899         char mchar = modes.c_str()[0];
900         unsigned int rv = 0;
901         if (mchar)
902         {
903                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
904                 if (mh)
905                         rv = mh->GetPrefixRank();
906         }
907         return rv;
908 }
909
910 const char* Channel::GetAllPrefixChars(User* user)
911 {
912         static char prefix[64];
913         int ctr = 0;
914
915         UserMembIter m = userlist.find(user);
916         if (m != userlist.end())
917         {
918                 for(unsigned int i=0; i < m->second->modes.length(); i++)
919                 {
920                         char mchar = m->second->modes[i];
921                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
922                         if (mh && mh->GetPrefix())
923                                 prefix[ctr++] = mh->GetPrefix();
924                 }
925         }
926         prefix[ctr] = 0;
927
928         return prefix;
929 }
930
931 unsigned int Channel::GetPrefixValue(User* user)
932 {
933         UserMembIter m = userlist.find(user);
934         if (m == userlist.end())
935                 return 0;
936         return m->second->getRank();
937 }
938
939 bool Channel::SetPrefix(User* user, char prefix, bool adding)
940 {
941         ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
942         if (!delta_mh)
943                 return false;
944         UserMembIter m = userlist.find(user);
945         if (m == userlist.end())
946                 return false;
947         for(unsigned int i=0; i < m->second->modes.length(); i++)
948         {
949                 char mchar = m->second->modes[i];
950                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
951                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
952                 {
953                         m->second->modes =
954                                 m->second->modes.substr(0,i) +
955                                 (adding ? std::string(1, prefix) : "") +
956                                 m->second->modes.substr(mchar == prefix ? i+1 : i);
957                         return adding != (mchar == prefix);
958                 }
959         }
960         if (adding)
961                 m->second->modes += std::string(1, prefix);
962         return adding;
963 }
964
965 void Channel::RemoveAllPrefixes(User* user)
966 {
967         UserMembIter m = userlist.find(user);
968         if (m != userlist.end())
969         {
970                 m->second->modes.clear();
971         }
972 }
973
974 void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
975 {
976         if ((timeout != 0) && (ServerInstance->Time() >= timeout))
977                 // Expired, don't bother
978                 return;
979
980         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
981
982         Invitation* inv = Invitation::Find(c, u, false);
983         if (inv)
984         {
985                  if ((inv->expiry == 0) || (inv->expiry > timeout))
986                         return;
987                 inv->expiry = timeout;
988                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
989         }
990         else
991         {
992                 inv = new Invitation(c, u, timeout);
993                 c->invites.push_back(inv);
994                 u->invites.push_back(inv);
995                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
996         }
997 }
998
999 Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
1000 {
1001         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);
1002         if (!u || u->invites.empty())
1003                 return NULL;
1004
1005         InviteList locallist;
1006         locallist.swap(u->invites);
1007
1008         Invitation* result = NULL;
1009         for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
1010         {
1011                 Invitation* inv = *i;
1012                 if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
1013                 {
1014                         /* Expired invite, remove it. */
1015                         std::string expiration = ServerInstance->TimeString(inv->expiry);
1016                         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
1017                         i = locallist.erase(i);
1018                         inv->cull();
1019                         delete inv;
1020                 }
1021                 else
1022                 {
1023                         /* Is it what we're searching for? */
1024                         if (inv->chan == c)
1025                         {
1026                                 result = inv;
1027                                 break;
1028                         }
1029                         ++i;
1030                 }
1031         }
1032
1033         locallist.swap(u->invites);
1034         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
1035         return result;
1036 }
1037
1038 Invitation::~Invitation()
1039 {
1040         // Remove this entry from both lists
1041         InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
1042         if (it != chan->invites.end())
1043                 chan->invites.erase(it);
1044         it = std::find(user->invites.begin(), user->invites.end(), this);
1045         if (it != user->invites.end())
1046                 user->invites.erase(it);
1047 }
1048
1049 void InviteBase::ClearInvites()
1050 {
1051         ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
1052         InviteList locallist;
1053         locallist.swap(invites);
1054         for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
1055         {
1056                 (*i)->cull();
1057                 delete *i;
1058         }
1059 }