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