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