]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/channels.cpp
ServerConfig extern moved into class InspIRCd
[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 <string>
20 #include <map>
21 #include <sstream>
22 #include <vector>
23 #include <deque>
24 #include <stdarg.h>
25 #include "configreader.h"
26 #include "inspircd.h"
27 #include "hash_map.h"
28 #include "users.h"
29 #include "ctables.h"
30 #include "globals.h"
31 #include "modules.h"
32 #include "dynamic.h"
33 #include "commands.h"
34 #include "wildcard.h"
35 #include "message.h"
36 #include "mode.h"
37 #include "xline.h"
38 #include "inspstring.h"
39 #include "helperfuncs.h"
40 #include "typedefs.h"
41
42 extern InspIRCd* ServerInstance;
43
44 extern int MODCOUNT;
45 extern std::vector<Module*> modules;
46 extern std::vector<ircd_module*> factory;
47 extern time_t TIME;
48 extern chan_hash chanlist;
49
50 chanrec::chanrec()
51 {
52         *name = *topic = *setby = *key = 0;
53         created = topicset = limit = 0;
54         internal_userlist.clear();
55         memset(&modes,0,64);
56 }
57
58 void chanrec::SetMode(char mode,bool mode_on)
59 {
60         modes[mode-65] = mode_on;
61         if (!mode_on)
62                 this->SetModeParam(mode,"",false);
63 }
64
65
66 void chanrec::SetModeParam(char mode,const char* parameter,bool mode_on)
67 {
68         log(DEBUG,"SetModeParam called");
69         
70         CustomModeList::iterator n = custom_mode_params.find(mode);     
71
72         if (mode_on)
73         {
74                 if (n == custom_mode_params.end())
75                 {
76                         custom_mode_params[mode] = strdup(parameter);
77                         log(DEBUG,"Custom mode parameter %c %s added",mode,parameter);
78                 }
79                 else
80                 {
81                         log(DEBUG, "Tried to set custom mode parameter for %c '%s' when it was already '%s'", mode, parameter, n->second);
82                 }
83         }
84         else
85         {
86                 if (n != custom_mode_params.end())
87                 {
88                         free(n->second);
89                         custom_mode_params.erase(n);
90                 }
91         }
92 }
93
94 bool chanrec::IsModeSet(char mode)
95 {
96         return modes[mode-65];
97 }
98
99 std::string chanrec::GetModeParameter(char mode)
100 {
101         if (mode == 'k')
102         {
103                 return this->key;
104         }
105         else if (mode == 'l')
106         {
107                 return ConvToStr(this->limit);
108         }
109         else
110         {
111                 CustomModeList::iterator n = custom_mode_params.find(mode);
112                 if (n != custom_mode_params.end())
113                 {
114                         return n->second;
115                 }
116                 return "";
117         }
118 }
119
120 long chanrec::GetUserCounter()
121 {
122         return (this->internal_userlist.size());
123 }
124
125 void chanrec::AddUser(userrec* user)
126 {
127         internal_userlist[user] = user;
128 }
129
130 unsigned long chanrec::DelUser(userrec* user)
131 {
132         CUListIter a = internal_userlist.find(user);
133         
134         if (a != internal_userlist.end())
135         {
136                 internal_userlist.erase(a);
137                 /* And tidy any others... */
138                 DelOppedUser(user);
139                 DelHalfoppedUser(user);
140                 DelVoicedUser(user);
141         }
142         
143         return internal_userlist.size();
144 }
145
146 bool chanrec::HasUser(userrec* user)
147 {
148         return (internal_userlist.find(user) != internal_userlist.end());
149 }
150
151 void chanrec::AddOppedUser(userrec* user)
152 {
153         internal_op_userlist[user] = user;
154 }
155
156 void chanrec::DelOppedUser(userrec* user)
157 {
158         CUListIter a = internal_op_userlist.find(user);
159         if (a != internal_op_userlist.end())
160         {
161                 internal_op_userlist.erase(a);
162                 return;
163         }
164 }
165
166 void chanrec::AddHalfoppedUser(userrec* user)
167 {
168         internal_halfop_userlist[user] = user;
169 }
170
171 void chanrec::DelHalfoppedUser(userrec* user)
172 {
173         CUListIter a = internal_halfop_userlist.find(user);
174
175         if (a != internal_halfop_userlist.end())
176         {   
177                 internal_halfop_userlist.erase(a);
178         }
179 }
180
181 void chanrec::AddVoicedUser(userrec* user)
182 {
183         internal_voice_userlist[user] = user;
184 }
185
186 void chanrec::DelVoicedUser(userrec* user)
187 {
188         CUListIter a = internal_voice_userlist.find(user);
189         
190         if (a != internal_voice_userlist.end())
191         {
192                 internal_voice_userlist.erase(a);
193         }
194 }
195
196 CUList* chanrec::GetUsers()
197 {
198         return &internal_userlist;
199 }
200
201 CUList* chanrec::GetOppedUsers()
202 {
203         return &internal_op_userlist;
204 }
205
206 CUList* chanrec::GetHalfoppedUsers()
207 {
208         return &internal_halfop_userlist;
209 }
210
211 CUList* chanrec::GetVoicedUsers()
212 {
213         return &internal_voice_userlist;
214 }
215
216 /* 
217  * add a channel to a user, creating the record for it if needed and linking
218  * it to the user record 
219  */
220 chanrec* chanrec::JoinUser(userrec *user, const char* cn, bool override, const char* key)
221 {
222         if (!user || !cn)
223                 return NULL;
224
225         int created = 0;
226         char cname[MAXBUF];
227         int MOD_RESULT = 0;
228         strlcpy(cname,cn,CHANMAX);
229
230         chanrec* Ptr = FindChan(cname);
231
232         if (!Ptr)
233         {
234                 if (user->fd > -1)
235                 {
236                         MOD_RESULT = 0;
237                         FOREACH_RESULT(I_OnUserPreJoin,OnUserPreJoin(user,NULL,cname));
238                         if (MOD_RESULT == 1)
239                                 return NULL;
240                 }
241
242                 /* create a new one */
243                 chanlist[cname] = new chanrec();
244                 strlcpy(chanlist[cname]->name, cname,CHANMAX);
245                 chanlist[cname]->modes[CM_TOPICLOCK] = chanlist[cname]->modes[CM_NOEXTERNAL] = 1;
246                 //chanlist[cname]->binarymodes = CM_TOPICLOCK | CM_NOEXTERNAL;
247                 chanlist[cname]->created = TIME;
248                 *chanlist[cname]->topic = 0;
249                 strlcpy(chanlist[cname]->setby, user->nick,NICKMAX-1);
250                 chanlist[cname]->topicset = 0;
251                 Ptr = chanlist[cname];
252                 log(DEBUG,"chanrec::JoinUser(): created: %s",cname);
253                 /*
254                  * set created to 2 to indicate user
255                  * is the first in the channel
256                  * and should be given ops
257                  */
258                 created = 2;
259         }
260         else
261         {
262                 /* Already on the channel */
263                 if (Ptr->HasUser(user))
264                         return NULL;
265
266                 /*
267                  * remote users are allowed us to bypass channel modes
268                  * and bans (used by servers)
269                  */
270                 if (IS_LOCAL(user)) /* was a check on fd > -1 */
271                 {
272                         MOD_RESULT = 0;
273                         FOREACH_RESULT(I_OnUserPreJoin,OnUserPreJoin(user,Ptr,cname));
274                         if (MOD_RESULT == 1)
275                         {
276                                 return NULL;
277                         }
278                         else if (MOD_RESULT == 0)
279                         {
280                                 if (*Ptr->key)
281                                 {
282                                         MOD_RESULT = 0;
283                                         FOREACH_RESULT(I_OnCheckKey,OnCheckKey(user, Ptr, key ? key : ""));
284                                         if (!MOD_RESULT)
285                                         {
286                                                 if (!key)
287                                                 {
288                                                         log(DEBUG,"chanrec::JoinUser(): no key given in JOIN");
289                                                         user->WriteServ("475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
290                                                         return NULL;
291                                                 }
292                                                 else
293                                                 {
294                                                         if (strcmp(key,Ptr->key))
295                                                         {
296                                                                 log(DEBUG,"chanrec::JoinUser(): bad key given in JOIN");
297                                                                 user->WriteServ("475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
298                                                                 return NULL;
299                                                         }
300                                                 }
301                                         }
302                                 }
303                                 if (Ptr->modes[CM_INVITEONLY])
304                                 {
305                                         MOD_RESULT = 0;
306                                         irc::string xname(Ptr->name);
307                                         FOREACH_RESULT(I_OnCheckInvite,OnCheckInvite(user, Ptr));
308                                         if (!MOD_RESULT)
309                                         {
310                                                 if (user->IsInvited(xname))
311                                                 {
312                                                         /* user was invited to channel */
313                                                         /* there may be an optional channel NOTICE here */
314                                                 }
315                                                 else
316                                                 {
317                                                         user->WriteServ("473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
318                                                         return NULL;
319                                                 }
320                                         }
321                                         user->RemoveInvite(xname);
322                                 }
323                                 if (Ptr->limit)
324                                 {
325                                         MOD_RESULT = 0;
326                                         FOREACH_RESULT(I_OnCheckLimit,OnCheckLimit(user, Ptr));
327                                         if (!MOD_RESULT)
328                                         {
329                                                 if (usercount(Ptr) >= Ptr->limit)
330                                                 {
331                                                         user->WriteServ("471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
332                                                         return NULL;
333                                                 }
334                                         }
335                                 }
336                                 if (Ptr->bans.size())
337                                 {
338                                         MOD_RESULT = 0;
339                                         FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, Ptr));
340                                         char mask[MAXBUF];
341                                         sprintf(mask,"%s!%s@%s",user->nick, user->ident, user->GetIPString());
342                                         if (!MOD_RESULT)
343                                         {
344                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
345                                                 {
346                                                         /* This allows CIDR ban matching
347                                                          * 
348                                                          *        Full masked host                      Full unmasked host                   IP with/without CIDR
349                                                          */
350                                                         if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true)))
351                                                         {
352                                                                 user->WriteServ("474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
353                                                                 return NULL;
354                                                         }
355                                                 }
356                                         }
357                                 }
358                         }
359                 }
360                 else
361                 {
362                         log(DEBUG,"chanrec::JoinUser(): Overridden checks");
363                 }
364                 created = 1;
365         }
366
367         for (UserChanList::const_iterator index = user->chans.begin(); index != user->chans.end(); index++)
368         {
369                 if ((*index)->channel == NULL)
370                 {
371                         return chanrec::ForceChan(Ptr, *index, user, created);
372                 }
373         }
374
375         /*
376          * XXX: If the user is an oper here, we can just extend their user->chans vector by one
377          * and put the channel in here. Same for remote users which are not bound by
378          * the channel limits. Otherwise, nope, youre boned.
379          */
380         if (!IS_LOCAL(user)) /* was a check on fd < 0 */
381         {
382                 ucrec* a = new ucrec();
383                 chanrec* c = chanrec::ForceChan(Ptr,a,user,created);
384                 user->chans.push_back(a);
385                 return c;
386         }
387         else if (*user->oper)
388         {
389                 /* Oper allows extension up to the OPERMAXCHANS value */
390                 if (user->chans.size() < OPERMAXCHANS)
391                 {
392                         ucrec* a = new ucrec();
393                         chanrec* c = chanrec::ForceChan(Ptr,a,user,created);
394                         user->chans.push_back(a);
395                         return c;
396                 }
397         }
398
399         user->WriteServ("405 %s %s :You are on too many channels",user->nick, cname);
400
401         if (created == 2)
402         {
403                 log(DEBUG,"BLAMMO, Whacking channel.");
404                 /* Things went seriously pear shaped, so take this away. bwahaha. */
405                 chan_hash::iterator n = chanlist.find(cname);
406                 if (n != chanlist.end())
407                 {
408                         Ptr->DelUser(user);
409                         DELETE(Ptr);
410                         chanlist.erase(n);
411                         for (unsigned int index =0; index < user->chans.size(); index++)
412                         {
413                                 if (user->chans[index]->channel == Ptr)
414                                 {
415                                         user->chans[index]->channel = NULL;
416                                         user->chans[index]->uc_modes = 0;       
417                                 }
418                         }
419                 }
420         }
421         else
422         {
423                 for (unsigned int index =0; index < user->chans.size(); index++)
424                 {
425                         if (user->chans[index]->channel == Ptr)
426                         {
427                                 user->chans[index]->channel = NULL;
428                                 user->chans[index]->uc_modes = 0;
429                         }
430                 }
431         }
432         return NULL;
433 }
434
435 chanrec* chanrec::ForceChan(chanrec* Ptr,ucrec *a,userrec* user, int created)
436 {
437         if (created == 2)
438         {
439                 /* first user in is given ops */
440                 a->uc_modes = UCMODE_OP;
441                 Ptr->AddOppedUser(user);
442         }
443         else
444         {
445                 a->uc_modes = 0;
446         }
447
448         a->channel = Ptr;
449         Ptr->AddUser(user);
450         Ptr->WriteChannel(user,"JOIN :%s",Ptr->name);
451
452         /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
453         if (IS_LOCAL(user))
454         {
455                 log(DEBUG,"Sent JOIN to client");
456                 if (Ptr->topicset)
457                 {
458                         user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
459                         user->WriteServ("333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset);
460                 }
461                 userlist(user,Ptr);
462                 user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
463         }
464         FOREACH_MOD(I_OnUserJoin,OnUserJoin(user,Ptr));
465         return Ptr;
466 }
467
468 /* chanrec::PartUser
469  * remove a channel from a users record, and remove the record from the hash
470  * if the channel has become empty
471  */
472 long chanrec::PartUser(userrec *user, const char* reason)
473 {
474         if (!user)
475                 return this->GetUserCounter();
476
477         for (unsigned int i =0; i < user->chans.size(); i++)
478         {
479                 /* zap it from the channel list of the user */
480                 if (user->chans[i]->channel == this)
481                 {
482                         if (reason)
483                         {
484                                 FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason));
485                                 this->WriteChannel(user, "PART %s :%s", this->name, reason);
486                         }
487                         else
488                         {
489                                 FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, ""));
490                                 this->WriteChannel(user, "PART :%s", this->name);
491                         }
492                         user->chans[i]->uc_modes = 0;
493                         user->chans[i]->channel = NULL;
494                         break;
495                 }
496         }
497
498         if (!this->DelUser(user)) /* if there are no users left on the channel... */
499         {
500                 chan_hash::iterator iter = chanlist.find(this->name);
501                 /* kill the record */
502                 if (iter != chanlist.end())
503                 {
504                         log(DEBUG,"del_channel: destroyed: %s", this->name);
505                         FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
506                         chanlist.erase(iter);
507                 }
508                 return 0;
509         }
510
511         return this->GetUserCounter();
512 }
513
514 long chanrec::ServerKickUser(userrec* user, const char* reason, bool triggerevents)
515 {
516         if (!user || !reason)
517                 return this->GetUserCounter();
518
519         if (IS_LOCAL(user))
520         {
521                 if (!this->HasUser(user))
522                 {
523                         /* Not on channel */
524                         return this->GetUserCounter();
525                 }
526         }
527
528         if (triggerevents)
529         {
530                 FOREACH_MOD(I_OnUserKick,OnUserKick(NULL,user,this,reason));
531         }
532
533         for (unsigned int i =0; i < user->chans.size(); i++)
534         {
535                 if (user->chans[i]->channel == this)
536                 {
537                         this->WriteChannelWithServ(ServerInstance->Config->ServerName, "KICK %s %s :%s", this->name, user->nick, reason);
538                         user->chans[i]->uc_modes = 0;
539                         user->chans[i]->channel = NULL;
540                         break;
541                 }
542         }
543
544         if (!this->DelUser(user))
545         {
546                 chan_hash::iterator iter = chanlist.find(this->name);
547                 /* kill the record */
548                 if (iter != chanlist.end())
549                 {
550                         FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
551                         chanlist.erase(iter);
552                 }
553                 return 0;
554         }
555
556         return this->GetUserCounter();
557 }
558
559 long chanrec::KickUser(userrec *src, userrec *user, const char* reason)
560 {
561         if (!src || !user || !reason)
562                 return this->GetUserCounter();
563
564         if (IS_LOCAL(src))
565         {
566                 if (!this->HasUser(user))
567                 {
568                         src->WriteServ("441 %s %s %s :They are not on that channel",src->nick, user->nick, this->name);
569                         return this->GetUserCounter();
570                 }
571                 if ((is_uline(user->server)) && (!is_uline(src->server)))
572                 {
573                         src->WriteServ("482 %s %s :Only a u-line may kick a u-line from a channel.",src->nick, this->name);
574                         return this->GetUserCounter();
575                 }
576                 int MOD_RESULT = 0;
577
578                 if (!is_uline(src->server))
579                 {
580                         MOD_RESULT = 0;
581                         FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason));
582                         if (MOD_RESULT == 1)
583                                 return this->GetUserCounter();
584                 }
585                 /* Set to -1 by OnUserPreKick if explicit allow was set */
586                 if (MOD_RESULT != -1)
587                 {
588                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK));
589                         if ((MOD_RESULT == ACR_DENY) && (!is_uline(src->server)))
590                                 return this->GetUserCounter();
591         
592                         if ((MOD_RESULT == ACR_DEFAULT) || (!is_uline(src->server)))
593                         {
594                                 int them = cstatus(src, this);
595                                 int us = cstatus(user, this);
596                                 if ((them < STATUS_HOP) || (them < us))
597                                 {
598                                         if (them == STATUS_HOP)
599                                         {
600                                                 src->WriteServ("482 %s %s :You must be a channel operator",src->nick, this->name);
601                                         }
602                                         else
603                                         {
604                                                 src->WriteServ("482 %s %s :You must be at least a half-operator",src->nick, this->name);
605                                         }
606                                         return this->GetUserCounter();
607                                 }
608                         }
609                 }
610         }
611
612         FOREACH_MOD(I_OnUserKick,OnUserKick(src,user,this,reason));
613                         
614         for (UserChanList::const_iterator i = user->chans.begin(); i != user->chans.end(); i++)
615         {
616                 /* zap it from the channel list of the user */
617                 if ((*i)->channel == this)
618                 {
619                         this->WriteChannel(src, "KICK %s %s :%s", this->name, user->nick, reason);
620                         (*i)->uc_modes = 0;
621                         (*i)->channel = NULL;
622                         break;
623                 }
624         }
625
626         if (!this->DelUser(user))
627         /* if there are no users left on the channel */
628         {
629                 chan_hash::iterator iter = chanlist.find(this->name);
630
631                 /* kill the record */
632                 if (iter != chanlist.end())
633                 {
634                         FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
635                         chanlist.erase(iter);
636                 }
637                 return 0;
638         }
639
640         return this->GetUserCounter();
641 }
642
643 void chanrec::WriteChannel(userrec* user, char* text, ...)
644 {
645         char textbuffer[MAXBUF];
646         va_list argsPtr;
647
648         if (!user || !text)
649                 return;
650
651         va_start(argsPtr, text);
652         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
653         va_end(argsPtr);
654
655         this->WriteChannel(user, std::string(textbuffer));
656 }
657
658 void chanrec::WriteChannel(userrec* user, const std::string &text)
659 {
660         CUList *ulist = this->GetUsers();
661
662         if (!user)
663                 return;
664
665         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
666         {
667                 if (i->second->fd != FD_MAGIC_NUMBER)
668                         user->WriteTo(i->second,text);
669         }
670 }
671
672 void chanrec::WriteChannelWithServ(const char* ServName, const char* text, ...)
673 {
674         char textbuffer[MAXBUF];
675         va_list argsPtr;
676
677         if (!text)
678                 return;
679
680         va_start(argsPtr, text);
681         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
682         va_end(argsPtr);
683
684         this->WriteChannelWithServ(ServName, std::string(textbuffer));
685 }
686
687 void chanrec::WriteChannelWithServ(const char* ServName, const std::string &text)
688 {
689         CUList *ulist = this->GetUsers();
690
691         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
692         {
693                 if (IS_LOCAL(i->second))
694                         i->second->WriteServ(text);
695         }
696 }
697
698 /* write formatted text from a source user to all users on a channel except
699  * for the sender (for privmsg etc) */
700 void chanrec::WriteAllExceptSender(userrec* user, char status, char* text, ...)
701 {
702         char textbuffer[MAXBUF];
703         va_list argsPtr;
704
705         if (!user || !text)
706                 return;
707
708         va_start(argsPtr, text);
709         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
710         va_end(argsPtr);
711
712         this->WriteAllExceptSender(user, status, std::string(textbuffer));
713 }
714
715 void chanrec::WriteAllExceptSender(userrec* user, char status, const std::string& text)
716 {
717         CUList *ulist;
718
719         if (!user)
720                 return;
721
722         switch (status)
723         {
724                 case '@':
725                         ulist = this->GetOppedUsers();
726                         break;
727                 case '%':
728                         ulist = this->GetHalfoppedUsers();
729                         break;
730                 case '+':
731                         ulist = this->GetVoicedUsers();
732                         break;
733                 default:
734                         ulist = this->GetUsers();
735                         break;
736         }
737
738         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
739         {
740                 if ((IS_LOCAL(i->second)) && (user != i->second))
741                         i->second->WriteFrom(user,text);
742         }
743 }
744