]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
Remove duplicated member, kept in classes.. Possible now we have a pointer. this...
[user/henk/code/inspircd.git] / src / channels.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core: libIRCDchannels */
15
16 #include "inspircd.h"
17 #include <cstdarg>
18 #include "wildcard.h"
19 #include "mode.h"
20
21 Channel::Channel(InspIRCd* Instance, const std::string &cname, time_t ts) : ServerInstance(Instance)
22 {
23         chan_hash::iterator findchan = ServerInstance->chanlist->find(cname);
24         if (findchan != Instance->chanlist->end())
25                 throw CoreException("Cannot create duplicate channel " + cname);
26
27         (*(ServerInstance->chanlist))[cname.c_str()] = this;
28         this->name.assign(cname, 0, ServerInstance->Config->Limits.ChanMax);
29         this->created = ts ? ts : ServerInstance->Time();
30         this->age = this->created;
31
32         maxbans = topicset = 0;
33         modes.reset();
34 }
35
36 void Channel::SetMode(char mode,bool mode_on)
37 {
38         modes[mode-65] = mode_on;
39         if (!mode_on)
40                 this->SetModeParam(mode,"",false);
41 }
42
43
44 void Channel::SetModeParam(char mode,const char* parameter,bool mode_on)
45 {
46         CustomModeList::iterator n = custom_mode_params.find(mode);
47
48         if (mode_on)
49         {
50                 if (n == custom_mode_params.end())
51                         custom_mode_params[mode] = strdup(parameter);
52         }
53         else
54         {
55                 if (n != custom_mode_params.end())
56                 {
57                         free(n->second);
58                         custom_mode_params.erase(n);
59                 }
60         }
61 }
62
63 bool Channel::IsModeSet(char mode)
64 {
65         return modes[mode-65];
66 }
67
68 std::string Channel::GetModeParameter(char mode)
69 {
70         CustomModeList::iterator n = custom_mode_params.find(mode);
71         if (n != custom_mode_params.end())
72                 return n->second;
73         return "";
74 }
75
76 long Channel::GetUserCounter()
77 {
78         return (this->internal_userlist.size());
79 }
80
81 void Channel::AddUser(User* user)
82 {
83         internal_userlist[user] = user->nick;
84 }
85
86 unsigned long Channel::DelUser(User* user)
87 {
88         CUListIter a = internal_userlist.find(user);
89
90         if (a != internal_userlist.end())
91         {
92                 internal_userlist.erase(a);
93                 /* And tidy any others... */
94                 DelOppedUser(user);
95                 DelHalfoppedUser(user);
96                 DelVoicedUser(user);
97         }
98
99         return internal_userlist.size();
100 }
101
102 bool Channel::HasUser(User* user)
103 {
104         return (internal_userlist.find(user) != internal_userlist.end());
105 }
106
107 void Channel::AddOppedUser(User* user)
108 {
109         internal_op_userlist[user] = user->nick;
110 }
111
112 void Channel::DelOppedUser(User* user)
113 {
114         CUListIter a = internal_op_userlist.find(user);
115         if (a != internal_op_userlist.end())
116         {
117                 internal_op_userlist.erase(a);
118                 return;
119         }
120 }
121
122 void Channel::AddHalfoppedUser(User* user)
123 {
124         internal_halfop_userlist[user] = user->nick;
125 }
126
127 void Channel::DelHalfoppedUser(User* user)
128 {
129         CUListIter a = internal_halfop_userlist.find(user);
130
131         if (a != internal_halfop_userlist.end())
132         {
133                 internal_halfop_userlist.erase(a);
134         }
135 }
136
137 void Channel::AddVoicedUser(User* user)
138 {
139         internal_voice_userlist[user] = user->nick;
140 }
141
142 void Channel::DelVoicedUser(User* user)
143 {
144         CUListIter a = internal_voice_userlist.find(user);
145
146         if (a != internal_voice_userlist.end())
147         {
148                 internal_voice_userlist.erase(a);
149         }
150 }
151
152 CUList* Channel::GetUsers()
153 {
154         return &internal_userlist;
155 }
156
157 CUList* Channel::GetOppedUsers()
158 {
159         return &internal_op_userlist;
160 }
161
162 CUList* Channel::GetHalfoppedUsers()
163 {
164         return &internal_halfop_userlist;
165 }
166
167 CUList* Channel::GetVoicedUsers()
168 {
169         return &internal_voice_userlist;
170 }
171
172 void Channel::SetDefaultModes()
173 {
174         ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s", ServerInstance->Config->DefaultModes);
175         irc::spacesepstream list(ServerInstance->Config->DefaultModes);
176         std::string modeseq;
177         std::string parameter;
178
179         list.GetToken(modeseq);
180
181         for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
182         {
183                 ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
184                 if (mode)
185                 {
186                         if (mode->GetNumParams(true))
187                                 list.GetToken(parameter);
188                         else
189                                 parameter.clear();
190
191                         mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
192                 }
193         }
194 }
195
196 /*
197  * add a channel to a user, creating the record for it if needed and linking
198  * it to the user record
199  */
200 Channel* Channel::JoinUser(InspIRCd* Instance, User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
201 {
202         if (!user || !cn)
203                 return NULL;
204
205         char cname[MAXBUF];
206         int MOD_RESULT = 0;
207         std::string privs;
208         Channel *Ptr;
209
210         /*
211          * We don't restrict the number of channels that remote users or users that are override-joining may be in.
212          * We restrict local users to MaxChans channels.
213          * We restrict local operators to OperMaxChans channels.
214          * This is a lot more logical than how it was formerly. -- w00t
215          */
216         if (IS_LOCAL(user) && !override)
217         {
218                 // Checking MyClass exists because we *may* get here with NULL, not 100% sure.
219                 if (user->MyClass && user->MyClass->GetMaxChans())
220                 {
221                         if (user->chans.size() >= user->MyClass->GetMaxChans())
222                         {
223                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
224                                 return NULL;
225                         }
226                 }
227                 else
228                 {
229                         if (IS_OPER(user))
230                         {
231                                 if (user->chans.size() >= Instance->Config->OperMaxChans)
232                                 {
233                                         user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
234                                         return NULL;
235                                 }
236                         }
237                         else
238                         {
239                                 if (user->chans.size() >= Instance->Config->MaxChans)
240                                 {
241                                         user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
242                                         return NULL;
243                                 }
244                         }
245                 }
246         }
247
248         strlcpy(cname, cn, Instance->Config->Limits.ChanMax);
249         Ptr = Instance->FindChan(cname);
250
251         if (!Ptr)
252         {
253                 /*
254                  * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
255                  */
256                 if (!IS_LOCAL(user))
257                 {
258                         if (!TS)
259                                 Instance->Logs->Log("CHANNEL",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
260                 }
261                 else
262                 {
263                         privs = "@";
264                 }
265
266                 if (IS_LOCAL(user) && override == false)
267                 {
268                         MOD_RESULT = 0;
269                         FOREACH_RESULT_I(Instance,I_OnUserPreJoin, OnUserPreJoin(user, NULL, cname, privs, key ? key : ""));
270                         if (MOD_RESULT == 1)
271                                 return NULL;
272                 }
273
274                 Ptr = new Channel(Instance, cname, TS);
275         }
276         else
277         {
278                 /* Already on the channel */
279                 if (Ptr->HasUser(user))
280                         return NULL;
281
282                 /*
283                  * remote users are allowed us to bypass channel modes
284                  * and bans (used by servers)
285                  */
286                 if (IS_LOCAL(user) && override == false)
287                 {
288                         MOD_RESULT = 0;
289                         FOREACH_RESULT_I(Instance,I_OnUserPreJoin, OnUserPreJoin(user, Ptr, cname, privs, key ? key : ""));
290                         if (MOD_RESULT == 1)
291                         {
292                                 return NULL;
293                         }
294                         else if (MOD_RESULT == 0)
295                         {
296                                 std::string ckey = Ptr->GetModeParameter('k');
297                                 if (!ckey.empty())
298                                 {
299                                         MOD_RESULT = 0;
300                                         FOREACH_RESULT_I(Instance, I_OnCheckKey, OnCheckKey(user, Ptr, key ? key : ""));
301                                         if (!MOD_RESULT)
302                                         {
303                                                 if ((!key) || ckey != key)
304                                                 {
305                                                         user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
306                                                         return NULL;
307                                                 }
308                                         }
309                                 }
310                                 if (Ptr->IsModeSet('i'))
311                                 {
312                                         MOD_RESULT = 0;
313                                         FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr));
314                                         if (!MOD_RESULT)
315                                         {
316                                                 if (!user->IsInvited(Ptr->name.c_str()))
317                                                 {
318                                                         user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
319                                                         return NULL;
320                                                 }
321                                         }
322                                         user->RemoveInvite(Ptr->name.c_str());
323                                 }
324
325                                 std::string limit = Ptr->GetModeParameter('l');
326                                 if (!limit.empty())
327                                 {
328                                         MOD_RESULT = 0;
329                                         FOREACH_RESULT_I(Instance, I_OnCheckLimit, OnCheckLimit(user, Ptr));
330                                         if (!MOD_RESULT)
331                                         {
332                                                 long llimit = atol(limit.c_str());
333                                                 if (Ptr->GetUserCounter() >= llimit)
334                                                 {
335                                                         user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
336                                                         return NULL;
337                                                 }
338                                         }
339                                 }
340
341                                 if (Ptr->bans.size())
342                                 {
343                                         if (Ptr->IsBanned(user))
344                                         {
345                                                 user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
346                                                 return NULL;
347                                         }
348                                 }
349                         }
350                 }
351         }
352
353         /* As spotted by jilles, dont bother to set this on remote users */
354         if (IS_LOCAL(user) && Ptr->GetUserCounter() == 0)
355                 Ptr->SetDefaultModes();
356
357         return Channel::ForceChan(Instance, Ptr, user, privs, bursting);
358 }
359
360 Channel* Channel::ForceChan(InspIRCd* Instance, Channel* Ptr, User* user, const std::string &privs, bool bursting)
361 {       
362         std::string nick = user->nick;
363         bool silent = false;
364
365         Ptr->AddUser(user);
366
367         /* Just in case they have no permissions */
368         user->chans[Ptr] = 0;
369
370         for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
371         {
372                 const char status = *x;
373                 ModeHandler* mh = Instance->Modes->FindPrefix(status);
374                 if (mh)
375                 {
376                         /* Set, and make sure that the mode handler knows this mode was now set */
377                         Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true);
378                         mh->OnModeChange(Instance->FakeClient, Instance->FakeClient, Ptr, nick, true);
379
380                         switch (mh->GetPrefix())
381                         {
382                                 /* These logic ops are SAFE IN THIS CASE because if the entry doesnt exist,
383                                  * addressing operator[] creates it. If they do exist, it points to it.
384                                  * At all other times where we dont want to create an item if it doesnt exist, we
385                                  * must stick to ::find().
386                                  */
387                                 case '@':
388                                         user->chans[Ptr] |= UCMODE_OP;
389                                 break;
390                                 case '%':
391                                         user->chans[Ptr] |= UCMODE_HOP;
392                                 break;
393                                 case '+':
394                                         user->chans[Ptr] |= UCMODE_VOICE;
395                                 break;
396                         }
397                 }
398         }
399
400         FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, bursting, silent));
401
402         if (!silent)
403                 Ptr->WriteChannel(user,"JOIN :%s",Ptr->name.c_str());
404
405         /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
406         std::string ms = Instance->Modes->ModeString(user, Ptr);
407         if ((Ptr->GetUserCounter() > 1) && (ms.length()))
408                 Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
409
410         /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
411         if (IS_LOCAL(user))
412         {
413                 if (Ptr->topicset)
414                 {
415                         user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
416                         user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
417                 }
418                 Ptr->UserList(user);
419         }
420         FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr));
421         return Ptr;
422 }
423
424 bool Channel::IsBanned(User* user)
425 {
426         char mask[MAXBUF];
427         int MOD_RESULT = 0;
428         FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this));
429
430         if (MOD_RESULT == -1)
431                 return true;
432         else if (MOD_RESULT == 0)
433         {
434                 snprintf(mask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), user->GetIPString());
435                 for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
436                 {
437                         /* This allows CIDR ban matching
438                          *
439                          *        Full masked host                      Full unmasked host                   IP with/without CIDR
440                          */
441                         if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true)))
442                         {
443                                 return true;
444                         }
445                 }
446         }
447         return false;
448 }
449
450 bool Channel::IsExtBanned(const std::string &str, char type)
451 {
452         int MOD_RESULT = 0;
453         FOREACH_RESULT(I_OnCheckStringExtBan, OnCheckStringExtBan(str, this, type));
454
455         if (MOD_RESULT == -1)
456                 return true;
457         else if (MOD_RESULT == 0)
458         {
459                 for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
460                 {
461                         if (i->data[0] != type || i->data[1] != ':')
462                                 continue;
463
464                         // Iterate past char and : to get to the mask without doing a data copy(!)
465                         std::string maskptr = i->data.substr(2);
466                         ServerInstance->Logs->Log("EXTBANS", DEBUG, "Checking %s against %s, type is %c", str.c_str(), maskptr.c_str(), type);
467
468                         if (match(str, maskptr))
469                                 return true;
470                 }
471         }
472
473         return false;
474 }
475
476 bool Channel::IsExtBanned(User *user, char type)
477 {
478         int MOD_RESULT = 0;
479         FOREACH_RESULT(I_OnCheckExtBan, OnCheckExtBan(user, this, type));
480
481         if (MOD_RESULT == -1)
482                 return true;
483         else if (MOD_RESULT == 0)
484         {
485                 char mask[MAXBUF];
486                 snprintf(mask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), user->GetIPString());
487
488                 // XXX: we should probably hook cloaked hosts in here somehow too..
489                 if (this->IsExtBanned(mask, type))
490                         return true;
491
492                 if (this->IsExtBanned(user->GetFullHost(), type))
493                         return true;
494
495                 if (this->IsExtBanned(user->GetFullRealHost(), type))
496                         return true;
497         }
498
499         return false;
500 }
501
502 /* Channel::PartUser
503  * remove a channel from a users record, and return the number of users left.
504  * Therefore, if this function returns 0 the caller should delete the Channel.
505  *
506  * XXX: bleh, string copy of reason, fixme! -- w00t
507  */
508 long Channel::PartUser(User *user, std::string &reason)
509 {
510         bool silent = false;
511
512         if (!user)
513                 return this->GetUserCounter();
514
515         UCListIter i = user->chans.find(this);
516         if (i != user->chans.end())
517         {
518                 FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason, silent));
519
520                 if (!silent)
521                         this->WriteChannel(user, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
522
523                 user->chans.erase(i);
524                 this->RemoveAllPrefixes(user);
525         }
526
527         if (!this->DelUser(user)) /* if there are no users left on the channel... */
528         {
529                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
530                 /* kill the record */
531                 if (iter != ServerInstance->chanlist->end())
532                 {
533                         int MOD_RESULT = 0;
534                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
535                         if (MOD_RESULT == 1)
536                                 return 1; // delete halted by module
537                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
538                         ServerInstance->chanlist->erase(iter);
539                 }
540                 return 0;
541         }
542
543         return this->GetUserCounter();
544 }
545
546 long Channel::ServerKickUser(User* user, const char* reason, bool triggerevents, const char* servername)
547 {
548         bool silent = false;
549
550         if (!user || !reason)
551                 return this->GetUserCounter();
552
553         if (IS_LOCAL(user))
554         {
555                 if (!this->HasUser(user))
556                 {
557                         /* Not on channel */
558                         return this->GetUserCounter();
559                 }
560         }
561
562         if (servername == NULL || *ServerInstance->Config->HideWhoisServer)
563                 servername = ServerInstance->Config->ServerName;
564
565         if (triggerevents)
566         {
567                 FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent));
568         }
569
570         UCListIter i = user->chans.find(this);
571         if (i != user->chans.end())
572         {
573                 if (!silent)
574                         this->WriteChannelWithServ(servername, "KICK %s %s :%s", this->name.c_str(), user->nick.c_str(), reason);
575
576                 user->chans.erase(i);
577                 this->RemoveAllPrefixes(user);
578         }
579
580         if (!this->DelUser(user))
581         {
582                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
583                 /* kill the record */
584                 if (iter != ServerInstance->chanlist->end())
585                 {
586                         int MOD_RESULT = 0;
587                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
588                         if (MOD_RESULT == 1)
589                                 return 1; // delete halted by module
590                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
591                         ServerInstance->chanlist->erase(iter);
592                 }
593                 return 0;
594         }
595
596         return this->GetUserCounter();
597 }
598
599 long Channel::KickUser(User *src, User *user, const char* reason)
600 {
601         bool silent = false;
602
603         if (!src || !user || !reason)
604                 return this->GetUserCounter();
605
606         if (IS_LOCAL(src))
607         {
608                 if (!this->HasUser(user))
609                 {
610                         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());
611                         return this->GetUserCounter();
612                 }
613                 if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
614                 {
615                         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());
616                         return this->GetUserCounter();
617                 }
618                 int MOD_RESULT = 0;
619
620                 if (!ServerInstance->ULine(src->server))
621                 {
622                         MOD_RESULT = 0;
623                         FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason));
624                         if (MOD_RESULT == 1)
625                                 return this->GetUserCounter();
626                 }
627                 /* Set to -1 by OnUserPreKick if explicit allow was set */
628                 if (MOD_RESULT != -1)
629                 {
630                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK));
631                         if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server)))
632                                 return this->GetUserCounter();
633
634                         if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server)))
635                         {
636                                 int them = this->GetStatus(src);
637                                 int us = this->GetStatus(user);
638                                 if ((them < STATUS_HOP) || (them < us))
639                                 {
640                                         src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",src->nick.c_str(), this->name.c_str(), them == STATUS_HOP ? "" : "half-");
641                                         return this->GetUserCounter();
642                                 }
643                         }
644                 }
645         }
646
647         FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent));
648
649         UCListIter i = user->chans.find(this);
650         if (i != user->chans.end())
651         {
652                 /* zap it from the channel list of the user */
653                 if (!silent)
654                         this->WriteChannel(src, "KICK %s %s :%s", this->name.c_str(), user->nick.c_str(), reason);
655
656                 user->chans.erase(i);
657                 this->RemoveAllPrefixes(user);
658         }
659
660         if (!this->DelUser(user))
661         /* if there are no users left on the channel */
662         {
663                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name.c_str());
664
665                 /* kill the record */
666                 if (iter != ServerInstance->chanlist->end())
667                 {
668                         int MOD_RESULT = 0;
669                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
670                         if (MOD_RESULT == 1)
671                                 return 1; // delete halted by module
672                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
673                         ServerInstance->chanlist->erase(iter);
674                 }
675                 return 0;
676         }
677
678         return this->GetUserCounter();
679 }
680
681 void Channel::WriteChannel(User* user, const char* text, ...)
682 {
683         char textbuffer[MAXBUF];
684         va_list argsPtr;
685
686         if (!user || !text)
687                 return;
688
689         va_start(argsPtr, text);
690         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
691         va_end(argsPtr);
692
693         this->WriteChannel(user, std::string(textbuffer));
694 }
695
696 void Channel::WriteChannel(User* user, const std::string &text)
697 {
698         CUList *ulist = this->GetUsers();
699         char tb[MAXBUF];
700
701         if (!user)
702                 return;
703
704         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
705         std::string out = tb;
706
707         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
708         {
709                 if (IS_LOCAL(i->first))
710                         i->first->Write(out);
711         }
712 }
713
714 void Channel::WriteChannelWithServ(const char* ServName, const char* text, ...)
715 {
716         char textbuffer[MAXBUF];
717         va_list argsPtr;
718
719         if (!text)
720                 return;
721
722         va_start(argsPtr, text);
723         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
724         va_end(argsPtr);
725
726         this->WriteChannelWithServ(ServName, std::string(textbuffer));
727 }
728
729 void Channel::WriteChannelWithServ(const char* ServName, const std::string &text)
730 {
731         CUList *ulist = this->GetUsers();
732         char tb[MAXBUF];
733
734         snprintf(tb,MAXBUF,":%s %s", ServName ? ServName : ServerInstance->Config->ServerName, text.c_str());
735         std::string out = tb;
736
737         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
738         {
739                 if (IS_LOCAL(i->first))
740                         i->first->Write(out);
741         }
742 }
743
744 /* write formatted text from a source user to all users on a channel except
745  * for the sender (for privmsg etc) */
746 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
747 {
748         char textbuffer[MAXBUF];
749         va_list argsPtr;
750
751         if (!text)
752                 return;
753
754         va_start(argsPtr, text);
755         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
756         va_end(argsPtr);
757
758         this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
759 }
760
761 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
762 {
763         char textbuffer[MAXBUF];
764         va_list argsPtr;
765
766         if (!text)
767                 return;
768
769         va_start(argsPtr, text);
770         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
771         va_end(argsPtr);
772
773         this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
774 }
775
776 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
777 {
778         CUList *ulist = this->GetUsers();
779         char tb[MAXBUF];
780
781         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
782         std::string out = tb;
783
784         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
785         {
786                 if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end()))
787                 {
788                         /* User doesnt have the status we're after */
789                         if (status && !strchr(this->GetAllPrefixChars(i->first), status))
790                                 continue;
791
792                         if (serversource)
793                                 i->first->WriteServ(text);
794                         else
795                                 i->first->Write(out);
796                 }
797         }
798 }
799
800 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
801 {
802         CUList except_list;
803         except_list[user] = user->nick;
804         this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
805 }
806
807 /*
808  * return a count of the users on a specific channel accounting for
809  * invisible users who won't increase the count. e.g. for /LIST
810  */
811 int Channel::CountInvisible()
812 {
813         int count = 0;
814         CUList *ulist= this->GetUsers();
815         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
816         {
817                 if (!(i->first->IsModeSet('i')))
818                         count++;
819         }
820
821         return count;
822 }
823
824 char* Channel::ChanModes(bool showkey)
825 {
826         static char scratch[MAXBUF];
827         static char sparam[MAXBUF];
828         char* offset = scratch;
829         std::string extparam;
830
831         *scratch = '\0';
832         *sparam = '\0';
833
834         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
835         for(int n = 0; n < 64; n++)
836         {
837                 if(this->modes[n])
838                 {
839                         *offset++ = n + 65;
840                         extparam.clear();
841                         switch (n)
842                         {
843                                 case CM_KEY:
844                                         // Unfortunately this must be special-cased, as we definitely don't want to always display key.
845                                         if (showkey)
846                                         {
847                                                 extparam = this->GetModeParameter('k');
848                                         }
849                                         else
850                                         {
851                                                 extparam = "<key>";
852                                         }
853                                         break;
854                                 case CM_NOEXTERNAL:
855                                 case CM_TOPICLOCK:
856                                 case CM_INVITEONLY:
857                                 case CM_MODERATED:
858                                 case CM_SECRET:
859                                 case CM_PRIVATE:
860                                         /* We know these have no parameters */
861                                 break;
862                                 default:
863                                         extparam = this->GetModeParameter(n + 65);
864                                 break;
865                         }
866                         if (!extparam.empty())
867                         {
868                                 charlcat(sparam,' ',MAXBUF);
869                                 strlcat(sparam,extparam.c_str(),MAXBUF);
870                         }
871                 }
872         }
873
874         /* Null terminate scratch */
875         *offset = '\0';
876         strlcat(scratch,sparam,MAXBUF);
877         return scratch;
878 }
879
880 /* compile a userlist of a channel into a string, each nick seperated by
881  * spaces and op, voice etc status shown as @ and +, and send it to 'user'
882  */
883 void Channel::UserList(User *user, CUList *ulist)
884 {
885         char list[MAXBUF];
886         size_t dlen, curlen;
887         int MOD_RESULT = 0;
888         bool call_modules = true;
889
890         if (!IS_LOCAL(user))
891                 return;
892
893         FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist));
894         if (MOD_RESULT == 1)
895                 call_modules = false;
896
897         if (MOD_RESULT != -1)
898         {
899                 if ((this->IsModeSet('s')) && (!this->HasUser(user)))
900                 {
901                         user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
902                         return;
903                 }
904         }
905
906         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
907
908         int numusers = 0;
909         char* ptr = list + dlen;
910
911         if (!ulist)
912                 ulist = this->GetUsers();
913
914         /* Improvement by Brain - this doesnt change in value, so why was it inside
915          * the loop?
916          */
917         bool has_user = this->HasUser(user);
918
919         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
920         {
921                 if ((!has_user) && (i->first->IsModeSet('i')))
922                 {
923                         /*
924                          * user is +i, and source not on the channel, does not show
925                          * nick in NAMES list
926                          */
927                         continue;
928                 }
929
930                 if (i->first->Visibility && !i->first->Visibility->VisibleTo(user))
931                         continue;
932
933                 std::string prefixlist = this->GetPrefixChar(i->first);
934                 std::string nick = i->first->nick;
935
936                 if (call_modules)
937                 {
938                         FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->first, this, prefixlist, nick));
939
940                         /* Nick was nuked, a module wants us to skip it */
941                         if (nick.empty())
942                                 continue;
943                 }
944
945                 size_t ptrlen = 0;
946
947                 if (curlen + prefixlist.length() + nick.length() + 1 > 480)
948                 {
949                         /* list overflowed into multiple numerics */
950                         user->WriteServ(std::string(list));
951
952                         /* reset our lengths */
953                         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
954                         ptr = list + dlen;
955
956                         ptrlen = 0;
957                         numusers = 0;
958                 }
959
960                 ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
961
962                 curlen += ptrlen;
963                 ptr += ptrlen;
964
965                 numusers++;
966         }
967
968         /* if whats left in the list isnt empty, send it */
969         if (numusers)
970         {
971                 user->WriteNumeric(RPL_NAMREPLY, std::string(list));
972         }
973
974         user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
975 }
976
977 long Channel::GetMaxBans()
978 {
979         /* Return the cached value if there is one */
980         if (this->maxbans)
981                 return this->maxbans;
982
983         /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
984         for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
985         {
986                 if (match(this->name,n->first))
987                 {
988                         this->maxbans = n->second;
989                         return n->second;
990                 }
991         }
992
993         /* Screw it, just return the default of 64 */
994         this->maxbans = 64;
995         return this->maxbans;
996 }
997
998 void Channel::ResetMaxBans()
999 {
1000         this->maxbans = 0;
1001 }
1002
1003 /* returns the status character for a given user on a channel, e.g. @ for op,
1004  * % for halfop etc. If the user has several modes set, the highest mode
1005  * the user has must be returned.
1006  */
1007 const char* Channel::GetPrefixChar(User *user)
1008 {
1009         static char pf[2] = {0, 0};
1010
1011         prefixlist::iterator n = prefixes.find(user);
1012         if (n != prefixes.end())
1013         {
1014                 if (n->second.size())
1015                 {
1016                         /* If the user has any prefixes, their highest prefix
1017                          * will always be at the head of the list, as the list is
1018                          * sorted in rank order highest first (see SetPrefix()
1019                          * for reasons why)
1020                          */
1021                         *pf = n->second.begin()->first;
1022                         return pf;
1023                 }
1024         }
1025
1026         *pf = 0;
1027         return pf;
1028 }
1029
1030
1031 const char* Channel::GetAllPrefixChars(User* user)
1032 {
1033         static char prefix[MAXBUF];
1034         int ctr = 0;
1035         *prefix = 0;
1036
1037         prefixlist::iterator n = prefixes.find(user);
1038         if (n != prefixes.end())
1039         {
1040                 for (std::vector<prefixtype>::iterator x = n->second.begin(); x != n->second.end(); x++)
1041                 {
1042                         prefix[ctr++] = x->first;
1043                 }
1044         }
1045
1046         prefix[ctr] = 0;
1047
1048         return prefix;
1049 }
1050
1051 unsigned int Channel::GetPrefixValue(User* user)
1052 {
1053         prefixlist::iterator n = prefixes.find(user);
1054         if (n != prefixes.end())
1055         {
1056                 if (n->second.size())
1057                         return n->second.begin()->second;
1058         }
1059         return 0;
1060 }
1061
1062 int Channel::GetStatusFlags(User *user)
1063 {
1064         UCListIter i = user->chans.find(this);
1065         if (i != user->chans.end())
1066         {
1067                 return i->second;
1068         }
1069         return 0;
1070 }
1071
1072 int Channel::GetStatus(User *user)
1073 {
1074         if (ServerInstance->ULine(user->server))
1075                 return STATUS_OP;
1076
1077         UCListIter i = user->chans.find(this);
1078         if (i != user->chans.end())
1079         {
1080                 if ((i->second & UCMODE_OP) > 0)
1081                 {
1082                         return STATUS_OP;
1083                 }
1084                 if ((i->second & UCMODE_HOP) > 0)
1085                 {
1086                         return STATUS_HOP;
1087                 }
1088                 if ((i->second & UCMODE_VOICE) > 0)
1089                 {
1090                         return STATUS_VOICE;
1091                 }
1092                 return STATUS_NORMAL;
1093         }
1094         return STATUS_NORMAL;
1095 }
1096
1097 void Channel::SetPrefix(User* user, char prefix, unsigned int prefix_value, bool adding)
1098 {
1099         prefixlist::iterator n = prefixes.find(user);
1100         prefixtype pfx = std::make_pair(prefix,prefix_value);
1101         if (adding)
1102         {
1103                 if (n != prefixes.end())
1104                 {
1105                         if (std::find(n->second.begin(), n->second.end(), pfx) == n->second.end())
1106                         {
1107                                 n->second.push_back(pfx);
1108                                 /* We must keep prefixes in rank order, largest first.
1109                                  * This is for two reasons, firstly because x-chat *ass-u-me's* this
1110                                  * state, and secondly it turns out to be a benefit to us later.
1111                                  * See above in GetPrefix().
1112                                  */
1113                                 std::sort(n->second.begin(), n->second.end(), ModeParser::PrefixComparison);
1114                         }
1115                 }
1116                 else
1117                 {
1118                         pfxcontainer one;
1119                         one.push_back(pfx);
1120                         prefixes.insert(std::make_pair<User*,pfxcontainer>(user, one));
1121                 }
1122         }
1123         else
1124         {
1125                 if (n != prefixes.end())
1126                 {
1127                         pfxcontainer::iterator x = std::find(n->second.begin(), n->second.end(), pfx);
1128                         if (x != n->second.end())
1129                                 n->second.erase(x);
1130                 }
1131         }
1132 }
1133
1134 void Channel::RemoveAllPrefixes(User* user)
1135 {
1136         prefixlist::iterator n = prefixes.find(user);
1137         if (n != prefixes.end())
1138         {
1139                 prefixes.erase(n);
1140         }
1141 }
1142