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