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