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