]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/channel.c
- QUIT wurde mir falschem Prefix verschickt.
[ngircd-alex.git] / src / ngircd / channel.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: channel.c,v 1.26 2002/06/01 15:55:17 alex Exp $
13  *
14  * channel.c: Management der Channels
15  */
16
17
18 #define __channel_c__
19
20
21 #include "portab.h"
22
23 #include "imp.h"
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "conn.h"
29 #include "client.h"
30
31 #include "exp.h"
32 #include "channel.h"
33
34 #include "imp.h"
35 #include "irc-write.h"
36 #include "resolve.h"
37 #include "conf.h"
38 #include "hash.h"
39 #include "log.h"
40 #include "messages.h"
41
42 #include "exp.h"
43
44
45 #define REMOVE_PART 0
46 #define REMOVE_QUIT 1
47 #define REMOVE_KICK 2
48
49
50 LOCAL CHANNEL *My_Channels;
51 LOCAL CL2CHAN *My_Cl2Chan;
52
53
54 LOCAL CHANNEL *New_Chan PARAMS(( CHAR *Name ));
55 LOCAL CL2CHAN *Get_Cl2Chan PARAMS(( CHANNEL *Chan, CLIENT *Client ));
56 LOCAL CL2CHAN *Add_Client PARAMS(( CHANNEL *Chan, CLIENT *Client ));
57 LOCAL BOOLEAN Remove_Client PARAMS(( INT Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, CHAR *Reason, BOOLEAN InformServer ));
58 LOCAL CL2CHAN *Get_First_Cl2Chan PARAMS(( CLIENT *Client, CHANNEL *Chan ));
59 LOCAL CL2CHAN *Get_Next_Cl2Chan PARAMS(( CL2CHAN *Start, CLIENT *Client, CHANNEL *Chan ));
60 LOCAL BOOLEAN Delete_Channel PARAMS(( CHANNEL *Chan ));
61
62
63 GLOBAL VOID
64 Channel_Init( VOID )
65 {
66         CHANNEL *chan;
67         CHAR *c;
68         INT i;
69         
70         My_Channels = NULL;
71         My_Cl2Chan = NULL;
72
73         /* Vordefinierte persistente Channels erzeugen */
74         for( i = 0; i < Conf_Channel_Count; i++ )
75         {
76                 /* Ist ein Name konfiguriert? */
77                 if( ! Conf_Channel[i].name ) continue;
78
79                 /* Gueltiger Channel-Name? */
80                 if( ! Channel_IsValidName( Conf_Channel[i].name ))
81                 {
82                         Log( LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"!", Conf_Channel[i].name );
83                         continue;
84                 }
85                 
86                 /* Channel anlegen */
87                 chan = New_Chan( Conf_Channel[i].name );
88                 if( chan )
89                 {
90                         /* Verketten */
91                         chan->next = My_Channels;
92                         My_Channels = chan;
93                         Channel_ModeAdd( chan, 'P' );
94                         Channel_SetTopic( chan, Conf_Channel[i].topic );
95                         c = Conf_Channel[i].modes;
96                         while( *c ) Channel_ModeAdd( chan, *c++ );
97                         Log( LOG_INFO, "Created pre-defined channel \"%s\".", Conf_Channel[i].name );
98                 }
99                 else Log( LOG_ERR, "Can't create pre-defined channel \"%s\"!", Conf_Channel[i].name );
100         }
101 } /* Channel_Init */
102
103
104 GLOBAL VOID
105 Channel_Exit( VOID )
106 {
107         CHANNEL *c, *c_next;
108         CL2CHAN *cl2chan, *cl2chan_next;
109         
110         /* Channel-Strukturen freigeben */
111         c = My_Channels;
112         while( c )
113         {
114                 c_next = c->next;
115                 free( c );
116                 c = c_next;
117         }
118
119         /* Channel-Zuordnungstabelle freigeben */
120         cl2chan = My_Cl2Chan;
121         while( c )
122         {
123                 cl2chan_next = cl2chan->next;
124                 free( cl2chan );
125                 cl2chan = cl2chan_next;
126         }
127 } /* Channel_Exit */
128
129
130 GLOBAL BOOLEAN
131 Channel_Join( CLIENT *Client, CHAR *Name )
132 {
133         CHANNEL *chan;
134         
135         assert( Client != NULL );
136         assert( Name != NULL );
137
138         /* Valider Channel-Name? */
139         if( ! Channel_IsValidName( Name ))
140         {
141                 IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name );
142                 return FALSE;
143         }
144
145         /* Channel suchen */
146         chan = Channel_Search( Name );
147         if( chan )
148         {
149                 /* Ist der Client bereits Mitglied? */
150                 if( Get_Cl2Chan( chan, Client )) return FALSE;
151         }
152         else
153         {
154                 /* Gibt es noch nicht? Dann neu anlegen: */
155                 chan = New_Chan( Name );
156                 if( ! chan ) return FALSE;
157
158                 /* Verketten */
159                 chan->next = My_Channels;
160                 My_Channels = chan;
161         }
162
163         /* User dem Channel hinzufuegen */
164         if( ! Add_Client( chan, Client )) return FALSE;
165         else return TRUE;
166 } /* Channel_Join */
167
168
169 GLOBAL BOOLEAN
170 Channel_Part( CLIENT *Client, CLIENT *Origin, CHAR *Name, CHAR *Reason )
171 {
172         CHANNEL *chan;
173
174         assert( Client != NULL );
175         assert( Name != NULL );
176         assert( Reason != NULL );
177
178         /* Channel suchen */
179         chan = Channel_Search( Name );
180         if(( ! chan ) || ( ! Get_Cl2Chan( chan, Client )))
181         {
182                 IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name );
183                 return FALSE;
184         }
185
186         /* User aus Channel entfernen */
187         if( ! Remove_Client( REMOVE_PART, chan, Client, Origin, Reason, TRUE )) return FALSE;
188         else return TRUE;
189 } /* Channel_Part */
190
191
192 GLOBAL VOID
193 Channel_Kick( CLIENT *Client, CLIENT *Origin, CHAR *Name, CHAR *Reason )
194 {
195         CHANNEL *chan;
196
197         assert( Client != NULL );
198         assert( Origin != NULL );
199         assert( Name != NULL );
200         assert( Reason != NULL );
201
202         /* Channel suchen */
203         chan = Channel_Search( Name );
204         if( ! chan )
205         {
206                 IRC_WriteStrClient( Origin, ERR_NOSUCHCHANNEL_MSG, Client_ID( Origin ), Name );
207                 return;
208         }
209
210         /* Ist der User Mitglied in dem Channel? */
211         if( ! Channel_IsMemberOf( chan, Origin ))
212         {
213                 IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, Client_ID( Origin ), Name );
214                 return;
215         }
216
217         /* Ist der User Channel-Operator? */
218         if( ! strchr( Channel_UserModes( chan, Origin ), 'o' ))
219         {
220                 IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Name);
221                 return;
222         }
223
224         /* Ist der Ziel-User Mitglied im Channel? */
225         if( ! Channel_IsMemberOf( chan, Client ))
226         {
227                 IRC_WriteStrClient( Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID( Origin ), Client_ID( Client ), Name );
228                 return;
229         }
230
231         Remove_Client( REMOVE_KICK, chan, Client, Origin, Reason, TRUE );
232 } /* Channel_Kick */
233
234
235 GLOBAL VOID
236 Channel_Quit( CLIENT *Client, CHAR *Reason )
237 {
238         CHANNEL *c, *next_c;
239
240         assert( Client != NULL );
241         assert( Reason != NULL );
242
243         c = My_Channels;
244         while( c )
245         {
246                 next_c = c->next;
247                 Remove_Client( REMOVE_QUIT, c, Client, Client, Reason, FALSE );
248                 c = next_c;
249         }
250 } /* Channel_Quit */
251
252
253 GLOBAL INT
254 Channel_Count( VOID )
255 {
256         CHANNEL *c;
257         INT count;
258         
259         count = 0;
260         c = My_Channels;
261         while( c )
262         {
263                 count++;
264                 c = c->next;
265         }
266         return count;
267 } /* Channel_Count */
268
269
270 GLOBAL INT
271 Channel_MemberCount( CHANNEL *Chan )
272 {
273         CL2CHAN *cl2chan;
274         INT count;
275
276         assert( Chan != NULL );
277
278         count = 0;
279         cl2chan = My_Cl2Chan;
280         while( cl2chan )
281         {
282                 if( cl2chan->channel == Chan ) count++;
283                 cl2chan = cl2chan->next;
284         }
285         return count;
286 } /* Channel_MemberCount */
287
288
289 GLOBAL CHAR *
290 Channel_Name( CHANNEL *Chan )
291 {
292         assert( Chan != NULL );
293         return Chan->name;
294 } /* Channel_Name */
295
296
297 GLOBAL CHAR *
298 Channel_Modes( CHANNEL *Chan )
299 {
300         assert( Chan != NULL );
301         return Chan->modes;
302 } /* Channel_Modes */
303
304
305 GLOBAL CHANNEL *
306 Channel_First( VOID )
307 {
308         return My_Channels;
309 } /* Channel_First */
310
311
312 GLOBAL CHANNEL *
313 Channel_Next( CHANNEL *Chan )
314 {
315         assert( Chan != NULL );
316         return Chan->next;
317 } /* Channel_Next */
318
319
320 GLOBAL CHANNEL *
321 Channel_Search( CHAR *Name )
322 {
323         /* Channel-Struktur suchen */
324         
325         CHANNEL *c;
326         UINT32 search_hash;
327
328         assert( Name != NULL );
329
330         search_hash = Hash( Name );
331         c = My_Channels;
332         while( c )
333         {
334                 if( search_hash == c->hash )
335                 {
336                         /* lt. Hash-Wert: Treffer! */
337                         if( strcasecmp( Name, c->name ) == 0 ) return c;
338                 }
339                 c = c->next;
340         }
341         return NULL;
342 } /* Channel_Search */
343
344
345 GLOBAL CL2CHAN *
346 Channel_FirstMember( CHANNEL *Chan )
347 {
348         assert( Chan != NULL );
349         return Get_First_Cl2Chan( NULL, Chan );
350 } /* Channel_FirstMember */
351
352
353 GLOBAL CL2CHAN *
354 Channel_NextMember( CHANNEL *Chan, CL2CHAN *Cl2Chan )
355 {
356         assert( Chan != NULL );
357         assert( Cl2Chan != NULL );
358         return Get_Next_Cl2Chan( Cl2Chan->next, NULL, Chan );
359 } /* Channel_NextMember */
360
361
362 GLOBAL CL2CHAN *
363 Channel_FirstChannelOf( CLIENT *Client )
364 {
365         assert( Client != NULL );
366         return Get_First_Cl2Chan( Client, NULL );
367 } /* Channel_FirstChannelOf */
368
369
370 GLOBAL CL2CHAN *
371 Channel_NextChannelOf( CLIENT *Client, CL2CHAN *Cl2Chan )
372 {
373         assert( Client != NULL );
374         assert( Cl2Chan != NULL );
375         return Get_Next_Cl2Chan( Cl2Chan->next, Client, NULL );
376 } /* Channel_NextChannelOf */
377
378
379 GLOBAL CLIENT *
380 Channel_GetClient( CL2CHAN *Cl2Chan )
381 {
382         assert( Cl2Chan != NULL );
383         return Cl2Chan->client;
384 } /* Channel_GetClient */
385
386
387 GLOBAL CHANNEL *
388 Channel_GetChannel( CL2CHAN *Cl2Chan )
389 {
390         assert( Cl2Chan != NULL );
391         return Cl2Chan->channel;
392 } /* Channel_GetChannel */
393
394
395 GLOBAL BOOLEAN
396 Channel_IsValidName( CHAR *Name )
397 {
398         /* Pruefen, ob Name als Channelname gueltig */
399
400         CHAR *ptr, badchars[10];
401         
402         assert( Name != NULL );
403
404         if(( Name[0] != '#' ) || ( strlen( Name ) >= CHANNEL_NAME_LEN )) return FALSE;
405
406         ptr = Name;
407         strcpy( badchars, " ,:\x07" );
408         while( *ptr )
409         {
410                 if( strchr( badchars, *ptr )) return FALSE;
411                 ptr++;
412         }
413         
414         return TRUE;
415 } /* Channel_IsValidName */
416
417
418 GLOBAL BOOLEAN
419 Channel_ModeAdd( CHANNEL *Chan, CHAR Mode )
420 {
421         /* Mode soll gesetzt werden. TRUE wird geliefert, wenn der
422          * Mode neu gesetzt wurde, FALSE, wenn der Channel den Mode
423          * bereits hatte. */
424
425         CHAR x[2];
426
427         assert( Chan != NULL );
428
429         x[0] = Mode; x[1] = '\0';
430         if( ! strchr( Chan->modes, x[0] ))
431         {
432                 /* Client hat den Mode noch nicht -> setzen */
433                 strcat( Chan->modes, x );
434                 return TRUE;
435         }
436         else return FALSE;
437 } /* Channel_ModeAdd */
438
439
440 GLOBAL BOOLEAN
441 Channel_ModeDel( CHANNEL *Chan, CHAR Mode )
442 {
443         /* Mode soll geloescht werden. TRUE wird geliefert, wenn der
444          * Mode entfernt wurde, FALSE, wenn der Channel den Mode
445          * ueberhaupt nicht hatte. */
446
447         CHAR x[2], *p;
448
449         assert( Chan != NULL );
450
451         x[0] = Mode; x[1] = '\0';
452
453         p = strchr( Chan->modes, x[0] );
454         if( ! p ) return FALSE;
455
456         /* Client hat den Mode -> loeschen */
457         while( *p )
458         {
459                 *p = *(p + 1);
460                 p++;
461         }
462         return TRUE;
463 } /* Channel_ModeDel */
464
465
466 GLOBAL BOOLEAN
467 Channel_UserModeAdd( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
468 {
469         /* Channel-User-Mode soll gesetzt werden. TRUE wird geliefert,
470          * wenn der Mode neu gesetzt wurde, FALSE, wenn der User den
471          * Channel-Mode bereits hatte. */
472
473         CL2CHAN *cl2chan;
474         CHAR x[2];
475
476         assert( Chan != NULL );
477         assert( Client != NULL );
478
479         cl2chan = Get_Cl2Chan( Chan, Client );
480         assert( cl2chan != NULL );
481         
482         x[0] = Mode; x[1] = '\0';
483         if( ! strchr( cl2chan->modes, x[0] ))
484         {
485                 /* Client hat den Mode noch nicht -> setzen */
486                 strcat( cl2chan->modes, x );
487                 return TRUE;
488         }
489         else return FALSE;
490 } /* Channel_UserModeAdd */
491
492
493 GLOBAL BOOLEAN
494 Channel_UserModeDel( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
495 {
496         /* Channel-User-Mode soll geloescht werden. TRUE wird geliefert,
497          * wenn der Mode entfernt wurde, FALSE, wenn der User den Channel-Mode
498          * ueberhaupt nicht hatte. */
499
500         CL2CHAN *cl2chan;
501         CHAR x[2], *p;
502
503         assert( Chan != NULL );
504         assert( Client != NULL );
505
506         cl2chan = Get_Cl2Chan( Chan, Client );
507         assert( cl2chan != NULL );
508
509         x[0] = Mode; x[1] = '\0';
510
511         p = strchr( cl2chan->modes, x[0] );
512         if( ! p ) return FALSE;
513
514         /* Client hat den Mode -> loeschen */
515         while( *p )
516         {
517                 *p = *(p + 1);
518                 p++;
519         }
520         return TRUE;
521 } /* Channel_UserModeDel */
522
523
524 GLOBAL CHAR *
525 Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
526 {
527         /* Channel-Modes eines Users liefern */
528         
529         CL2CHAN *cl2chan;
530
531         assert( Chan != NULL );
532         assert( Client != NULL );
533
534         cl2chan = Get_Cl2Chan( Chan, Client );
535         assert( cl2chan != NULL );
536
537         return cl2chan->modes;
538 } /* Channel_UserModes */
539
540
541 GLOBAL BOOLEAN
542 Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
543 {
544         /* Pruefen, ob Client Mitglied in Channel ist */
545
546         assert( Chan != NULL );
547         assert( Client != NULL );
548
549         if( Get_Cl2Chan( Chan, Client )) return TRUE;
550         else return FALSE;
551 } /* Channel_IsMemberOf */
552
553
554 GLOBAL CHAR *
555 Channel_Topic( CHANNEL *Chan )
556 {
557         assert( Chan != NULL );
558         return Chan->topic;
559 } /* Channel_Topic */
560
561
562 GLOBAL VOID
563 Channel_SetTopic( CHANNEL *Chan, CHAR *Topic )
564 {
565         assert( Chan != NULL );
566         assert( Topic != NULL );
567         
568         strncpy( Chan->topic, Topic, CHANNEL_TOPIC_LEN - 1 );
569         Chan->topic[CHANNEL_TOPIC_LEN - 1] = '\0';
570 } /* Channel_SetTopic */
571
572
573 GLOBAL BOOLEAN
574 Channel_Write( CHANNEL *Chan, CLIENT *From, CLIENT *Client, CHAR *Text )
575 {
576         BOOLEAN is_member, has_voice, is_op, ok;
577
578         /* Okay, Ziel ist ein Channel */
579         is_member = has_voice = is_op = FALSE;
580         if( Channel_IsMemberOf( Chan, From ))
581         {
582                 is_member = TRUE;
583                 if( strchr( Channel_UserModes( Chan, From ), 'v' )) has_voice = TRUE;
584                 if( strchr( Channel_UserModes( Chan, From ), 'o' )) is_op = TRUE;
585         }
586
587         /* pruefen, ob Client in Channel schreiben darf */
588         ok = TRUE;
589         if( strchr( Channel_Modes( Chan ), 'n' ) && ( ! is_member )) ok = FALSE;
590         if( strchr( Channel_Modes( Chan ), 'm' ) && ( ! is_op ) && ( ! has_voice )) ok = FALSE;
591
592         if( ! ok ) return IRC_WriteStrClient( From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID( From ), Channel_Name( Chan ));
593
594         /* Text senden */
595         if( Client_Conn( From ) > NONE ) Conn_UpdateIdle( Client_Conn( From ));
596         return IRC_WriteStrChannelPrefix( Client, Chan, From, TRUE, "PRIVMSG %s :%s", Channel_Name( Chan ), Text );
597 } /* Channel_Write */
598
599
600
601 LOCAL CHANNEL *
602 New_Chan( CHAR *Name )
603 {
604         /* Neue Channel-Struktur anlegen */
605
606         CHANNEL *c;
607
608         assert( Name != NULL );
609         
610         c = malloc( sizeof( CHANNEL ));
611         if( ! c )
612         {
613                 Log( LOG_EMERG, "Can't allocate memory!" );
614                 return NULL;
615         }
616         c->next = NULL;
617         strncpy( c->name, Name, CHANNEL_NAME_LEN - 1 );
618         c->name[CHANNEL_NAME_LEN - 1] = '\0';
619         strcpy( c->modes, "" );
620         strcpy( c->topic, "" );
621         c->hash = Hash( c->name );
622
623         Log( LOG_DEBUG, "Created new channel structure for \"%s\".", Name );
624         
625         return c;
626 } /* New_Chan */
627
628
629 LOCAL CL2CHAN *
630 Get_Cl2Chan( CHANNEL *Chan, CLIENT *Client )
631 {
632         CL2CHAN *cl2chan;
633
634         assert( Chan != NULL );
635         assert( Client != NULL );
636
637         cl2chan = My_Cl2Chan;
638         while( cl2chan )
639         {
640                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) return cl2chan;
641                 cl2chan = cl2chan->next;
642         }
643         return NULL;
644 } /* Get_Cl2Chan */
645
646
647 LOCAL CL2CHAN *
648 Add_Client( CHANNEL *Chan, CLIENT *Client )
649 {
650         CL2CHAN *cl2chan;
651
652         assert( Chan != NULL );
653         assert( Client != NULL );
654
655         /* neue CL2CHAN-Struktur anlegen */
656         cl2chan = malloc( sizeof( CL2CHAN ));
657         if( ! cl2chan )
658         {
659                 Log( LOG_EMERG, "Can't allocate memory!" );
660                 return NULL;
661         }
662         cl2chan->channel = Chan;
663         cl2chan->client = Client;
664         strcpy( cl2chan->modes, "" );
665
666         /* Verketten */
667         cl2chan->next = My_Cl2Chan;
668         My_Cl2Chan = cl2chan;
669
670         Log( LOG_DEBUG, "User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name );
671
672         return cl2chan;
673 } /* Add_Client */
674
675
676 LOCAL BOOLEAN
677 Remove_Client( INT Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, CHAR *Reason, BOOLEAN InformServer )
678 {
679         CL2CHAN *cl2chan, *last_cl2chan;
680         CHANNEL *c;
681         
682         assert( Chan != NULL );
683         assert( Client != NULL );
684         assert( Origin != NULL );
685         assert( Reason != NULL );
686
687         last_cl2chan = NULL;
688         cl2chan = My_Cl2Chan;
689         while( cl2chan )
690         {
691                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
692                 last_cl2chan = cl2chan;
693                 cl2chan = cl2chan->next;
694         }
695         if( ! cl2chan ) return FALSE;
696
697         c = cl2chan->channel;
698         assert( c != NULL );
699
700         /* Aus Verkettung loesen und freigeben */
701         if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
702         else My_Cl2Chan = cl2chan->next;
703         free( cl2chan );
704
705         switch( Type )
706         {
707                 case REMOVE_QUIT:
708                         /* QUIT: andere Server wurden bereits informiert, vgl. Client_Destroy();
709                          * hier also "nur" noch alle User in betroffenen Channeln infomieren */
710                         assert( InformServer == FALSE );
711                         IRC_WriteStrChannelPrefix( Origin, c, Origin, FALSE, "QUIT :%s", Reason );
712                         Log( LOG_DEBUG, "User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason );
713                         break;
714                 case REMOVE_KICK:
715                         /* User wurde geKICKed: ggf. andere Server sowie alle betroffenen User
716                          * im entsprechenden Channel informieren */
717                         if( InformServer ) IRC_WriteStrServersPrefix( Client_NextHop( Origin ), Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason );
718                         IRC_WriteStrChannelPrefix( Client, c, Origin, FALSE, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason );
719                         if(( Client_Conn( Client ) > NONE ) && ( Client_Type( Client ) == CLIENT_USER )) IRC_WriteStrClientPrefix( Client, Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason );
720                         Log( LOG_DEBUG, "User \"%s\" has been kicked of \"%s\" by \"%s\": %s.", Client_Mask( Client ), c->name, Client_ID( Origin ), Reason );
721                         break;
722                 default:
723                         /* PART */
724                         if( InformServer ) IRC_WriteStrServersPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
725                         IRC_WriteStrChannelPrefix( Origin, c, Client, FALSE, "PART %s :%s", c->name, Reason );
726                         if(( Client_Conn( Origin ) > NONE ) && ( Client_Type( Origin ) == CLIENT_USER )) IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
727                         Log( LOG_DEBUG, "User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason );
728         }
729
730         /* Wenn Channel nun leer und nicht pre-defined: loeschen */
731         if( ! strchr( Channel_Modes( Chan ), 'P' ))
732         {
733                 if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
734         }
735                 
736         return TRUE;
737 } /* Remove_Client */
738
739
740 LOCAL CL2CHAN *
741 Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
742 {
743         return Get_Next_Cl2Chan( My_Cl2Chan, Client, Chan );
744 } /* Get_First_Cl2Chan */
745
746
747 LOCAL CL2CHAN *
748 Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel )
749 {
750         CL2CHAN *cl2chan;
751
752         assert( Client != NULL || Channel != NULL );
753         
754         cl2chan = Start;
755         while( cl2chan )
756         {
757                 if(( Client ) && ( cl2chan->client == Client )) return cl2chan;
758                 if(( Channel ) && ( cl2chan->channel == Channel )) return cl2chan;
759                 cl2chan = cl2chan->next;
760         }
761         return NULL;
762 } /* Get_Next_Cl2Chan */
763
764
765 LOCAL BOOLEAN
766 Delete_Channel( CHANNEL *Chan )
767 {
768         /* Channel-Struktur loeschen */
769         
770         CHANNEL *chan, *last_chan;
771
772         last_chan = NULL;
773         chan = My_Channels;
774         while( chan )
775         {
776                 if( chan == Chan ) break;
777                 last_chan = chan;
778                 chan = chan->next;
779         }
780         if( ! chan ) return FALSE;
781
782         Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name );
783
784         /* Neu verketten und freigeben */
785         if( last_chan ) last_chan->next = chan->next;
786         else My_Channels = chan->next;
787         free( chan );
788                 
789         return TRUE;
790 } /* Delete_Channel */
791
792
793 /* -eof- */