]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
b5132c8b3de7b008a51ec0f5f6d36b4710429728
[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         bool has_privs = user->HasPrivPermission("channels/auspex");
776
777         if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
778         {
779                 user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
780                 return;
781         }
782
783         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
784
785         int numusers = 0;
786         char* ptr = list + dlen;
787
788         /* Improvement by Brain - this doesnt change in value, so why was it inside
789          * the loop?
790          */
791         bool has_user = this->HasUser(user);
792
793         for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
794         {
795                 if (i->first->quitting)
796                         continue;
797                 if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
798                 {
799                         /*
800                          * user is +i, and source not on the channel, does not show
801                          * nick in NAMES list
802                          */
803                         continue;
804                 }
805
806                 std::string prefixlist = this->GetPrefixChar(i->first);
807                 std::string nick = i->first->nick;
808
809                 FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
810
811                 /* Nick was nuked, a module wants us to skip it */
812                 if (nick.empty())
813                         continue;
814
815                 size_t ptrlen = 0;
816
817                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
818                 {
819                         /* list overflowed into multiple numerics */
820                         user->WriteNumeric(RPL_NAMREPLY, std::string(list));
821
822                         /* reset our lengths */
823                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
824                         ptr = list + dlen;
825
826                         numusers = 0;
827                 }
828
829                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
830
831                 curlen += ptrlen;
832                 ptr += ptrlen;
833
834                 numusers++;
835         }
836
837         /* if whats left in the list isnt empty, send it */
838         if (numusers)
839         {
840                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
841         }
842
843         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
844 }
845
846 long Channel::GetMaxBans()
847 {
848         /* Return the cached value if there is one */
849         if (this->maxbans)
850                 return this->maxbans;
851
852         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
853         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
854         {
855                 if (InspIRCd::Match(this->name, n->first, NULL))
856                 {
857                         this->maxbans = n->second;
858                         return n->second;
859                 }
860         }
861
862         /* Screw it, just return the default of 64 */
863         this->maxbans = 64;
864         return this->maxbans;
865 }
866
867 void Channel::ResetMaxBans()
868 {
869         this->maxbans = 0;
870 }
871
872 /* returns the status character for a given user on a channel, e.g. @ for op,
873  * % for halfop etc. If the user has several modes set, the highest mode
874  * the user has must be returned.
875  */
876 const char* Channel::GetPrefixChar(User *user)
877 {
878         static char pf[2] = {0, 0};
879         *pf = 0;
880         unsigned int bestrank = 0;
881
882         UserMembIter m = userlist.find(user);
883         if (m != userlist.end())
884         {
885                 for(unsigned int i=0; i < m->second->modes.length(); i++)
886                 {
887                         char mchar = m->second->modes[i];
888                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
889                         if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
890                         {
891                                 bestrank = mh->GetPrefixRank();
892                                 pf[0] = mh->GetPrefix();
893                         }
894                 }
895         }
896         return pf;
897 }
898
899 unsigned int Membership::getRank()
900 {
901         char mchar = modes.c_str()[0];
902         unsigned int rv = 0;
903         if (mchar)
904         {
905                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
906                 if (mh)
907                         rv = mh->GetPrefixRank();
908         }
909         return rv;
910 }
911
912 const char* Channel::GetAllPrefixChars(User* user)
913 {
914         static char prefix[64];
915         int ctr = 0;
916
917         UserMembIter m = userlist.find(user);
918         if (m != userlist.end())
919         {
920                 for(unsigned int i=0; i < m->second->modes.length(); i++)
921                 {
922                         char mchar = m->second->modes[i];
923                         ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
924                         if (mh && mh->GetPrefix())
925                                 prefix[ctr++] = mh->GetPrefix();
926                 }
927         }
928         prefix[ctr] = 0;
929
930         return prefix;
931 }
932
933 unsigned int Channel::GetPrefixValue(User* user)
934 {
935         UserMembIter m = userlist.find(user);
936         if (m == userlist.end())
937                 return 0;
938         return m->second->getRank();
939 }
940
941 bool Channel::SetPrefix(User* user, char prefix, bool adding)
942 {
943         ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
944         if (!delta_mh)
945                 return false;
946         UserMembIter m = userlist.find(user);
947         if (m == userlist.end())
948                 return false;
949         for(unsigned int i=0; i < m->second->modes.length(); i++)
950         {
951                 char mchar = m->second->modes[i];
952                 ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
953                 if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
954                 {
955                         m->second->modes =
956                                 m->second->modes.substr(0,i) +
957                                 (adding ? std::string(1, prefix) : "") +
958                                 m->second->modes.substr(mchar == prefix ? i+1 : i);
959                         return adding != (mchar == prefix);
960                 }
961         }
962         if (adding)
963                 m->second->modes += std::string(1, prefix);
964         return adding;
965 }
966
967 void Channel::RemoveAllPrefixes(User* user)
968 {
969         UserMembIter m = userlist.find(user);
970         if (m != userlist.end())
971         {
972                 m->second->modes.clear();
973         }
974 }
975
976 void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
977 {
978         if ((timeout != 0) && (ServerInstance->Time() >= timeout))
979                 // Expired, don't bother
980                 return;
981
982         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
983
984         Invitation* inv = Invitation::Find(c, u, false);
985         if (inv)
986         {
987                  if ((inv->expiry == 0) || (inv->expiry > timeout))
988                         return;
989                 inv->expiry = timeout;
990                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
991         }
992         else
993         {
994                 inv = new Invitation(c, u, timeout);
995                 c->invites.push_back(inv);
996                 u->invites.push_back(inv);
997                 ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
998         }
999 }
1000
1001 Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
1002 {
1003         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);
1004         if (!u || u->invites.empty())
1005                 return NULL;
1006
1007         InviteList locallist;
1008         locallist.swap(u->invites);
1009
1010         Invitation* result = NULL;
1011         for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
1012         {
1013                 Invitation* inv = *i;
1014                 if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
1015                 {
1016                         /* Expired invite, remove it. */
1017                         std::string expiration = ServerInstance->TimeString(inv->expiry);
1018                         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
1019                         i = locallist.erase(i);
1020                         inv->cull();
1021                         delete inv;
1022                 }
1023                 else
1024                 {
1025                         /* Is it what we're searching for? */
1026                         if (inv->chan == c)
1027                         {
1028                                 result = inv;
1029                                 break;
1030                         }
1031                         ++i;
1032                 }
1033         }
1034
1035         locallist.swap(u->invites);
1036         ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
1037         return result;
1038 }
1039
1040 Invitation::~Invitation()
1041 {
1042         // Remove this entry from both lists
1043         InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
1044         if (it != chan->invites.end())
1045                 chan->invites.erase(it);
1046         it = std::find(user->invites.begin(), user->invites.end(), this);
1047         if (it != user->invites.end())
1048                 user->invites.erase(it);
1049 }
1050
1051 void InviteBase::ClearInvites()
1052 {
1053         ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
1054         InviteList locallist;
1055         locallist.swap(invites);
1056         for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
1057         {
1058                 (*i)->cull();
1059                 delete *i;
1060         }
1061 }