]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/channel.c
- neue Funktion Channel_MemberCount() implementiert.
[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.21 2002/04/23 19:51:31 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 "client.h"
29 #include "hash.h"
30 #include "irc-write.h"
31 #include "log.h"
32 #include "messages.h"
33
34 #include "exp.h"
35 #include "channel.h"
36
37
38 LOCAL CHANNEL *My_Channels;
39 LOCAL CL2CHAN *My_Cl2Chan;
40
41
42 LOCAL CHANNEL *New_Chan( CHAR *Name );
43 LOCAL CL2CHAN *Get_Cl2Chan( CHANNEL *Chan, CLIENT *Client );
44 LOCAL CL2CHAN *Add_Client( CHANNEL *Chan, CLIENT *Client );
45 LOCAL BOOLEAN Remove_Client( CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, CHAR *Reason, BOOLEAN ServerPART );
46 LOCAL CL2CHAN *Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan );
47 LOCAL CL2CHAN *Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Chan );
48 LOCAL BOOLEAN Delete_Channel( CHANNEL *Chan );
49
50
51 GLOBAL VOID Channel_Init( VOID )
52 {
53         My_Channels = NULL;
54         My_Cl2Chan = NULL;
55 } /* Channel_Init */
56
57
58 GLOBAL VOID Channel_Exit( VOID )
59 {
60         CHANNEL *c, *c_next;
61         CL2CHAN *cl2chan, *cl2chan_next;
62         
63         /* Channel-Strukturen freigeben */
64         c = My_Channels;
65         while( c )
66         {
67                 c_next = c->next;
68                 free( c );
69                 c = c_next;
70         }
71
72         /* Channel-Zuordnungstabelle freigeben */
73         cl2chan = My_Cl2Chan;
74         while( c )
75         {
76                 cl2chan_next = cl2chan->next;
77                 free( cl2chan );
78                 cl2chan = cl2chan_next;
79         }
80 } /* Channel_Exit */
81
82
83 GLOBAL BOOLEAN Channel_Join( CLIENT *Client, CHAR *Name )
84 {
85         CHANNEL *chan;
86         
87         assert( Client != NULL );
88         assert( Name != NULL );
89
90         /* Valider Channel-Name? */
91         if( ! Channel_IsValidName( Name ))
92         {
93                 IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name );
94                 return FALSE;
95         }
96
97         /* Channel suchen */
98         chan = Channel_Search( Name );
99         if( chan )
100         {
101                 /* Ist der Client bereits Mitglied? */
102                 if( Get_Cl2Chan( chan, Client )) return FALSE;
103         }
104         else
105         {
106                 /* Gibt es noch nicht? Dann neu anlegen: */
107                 chan = New_Chan( Name );
108                 if( ! chan ) return FALSE;
109
110                 /* Verketten */
111                 chan->next = My_Channels;
112                 My_Channels = chan;
113         }
114
115         /* User dem Channel hinzufuegen */
116         if( ! Add_Client( chan, Client )) return FALSE;
117         else return TRUE;
118 } /* Channel_Join */
119
120
121 GLOBAL BOOLEAN Channel_Part( CLIENT *Client, CLIENT *Origin, CHAR *Name, CHAR *Reason )
122 {
123         CHANNEL *chan;
124
125         assert( Client != NULL );
126         assert( Name != NULL );
127
128         /* Channel suchen */
129         chan = Channel_Search( Name );
130         if(( ! chan ) || ( ! Get_Cl2Chan( chan, Client )))
131         {
132                 IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name );
133                 return FALSE;
134         }
135
136         /* User aus Channel entfernen */
137         if( ! Remove_Client( chan, Client, Origin, Reason, TRUE )) return FALSE;
138         else return TRUE;
139 } /* Channel_Part */
140
141
142 GLOBAL VOID Channel_RemoveClient( CLIENT *Client, CHAR *Reason )
143 {
144         CHANNEL *c, *next_c;
145
146         assert( Client != NULL );
147
148         c = My_Channels;
149         while( c )
150         {
151                 next_c = c->next;
152                 Remove_Client( c, Client, Client_ThisServer( ), Reason, FALSE );
153                 c = next_c;
154         }
155 } /* Channel_RemoveClient */
156
157
158 GLOBAL INT Channel_Count( VOID )
159 {
160         CHANNEL *c;
161         INT count;
162         
163         count = 0;
164         c = My_Channels;
165         while( c )
166         {
167                 count++;
168                 c = c->next;
169         }
170         return count;
171 } /* Channel_Count */
172
173
174 GLOBAL INT Channel_MemberCount( CHANNEL *Chan )
175 {
176         CL2CHAN *cl2chan;
177         INT count;
178
179         assert( Chan != NULL );
180
181         count = 0;
182         cl2chan = My_Cl2Chan;
183         while( cl2chan )
184         {
185                 if( cl2chan->channel == Chan ) count++;
186                 cl2chan = cl2chan->next;
187         }
188         return count;
189 } /* Channel_MemberCount */
190
191
192 GLOBAL CHAR *Channel_Name( CHANNEL *Chan )
193 {
194         assert( Chan != NULL );
195         return Chan->name;
196 } /* Channel_Name */
197
198
199 GLOBAL CHAR *Channel_Modes( CHANNEL *Chan )
200 {
201         assert( Chan != NULL );
202         return Chan->modes;
203 } /* Channel_Modes */
204
205
206 GLOBAL CHANNEL *Channel_First( VOID )
207 {
208         return My_Channels;
209 } /* Channel_First */
210
211
212 GLOBAL CHANNEL *Channel_Next( CHANNEL *Chan )
213 {
214         assert( Chan != NULL );
215         return Chan->next;
216 } /* Channel_Next */
217
218
219 GLOBAL CHANNEL *Channel_Search( CHAR *Name )
220 {
221         /* Channel-Struktur suchen */
222         
223         CHANNEL *c;
224         UINT32 search_hash;
225
226         assert( Name != NULL );
227
228         search_hash = Hash( Name );
229         c = My_Channels;
230         while( c )
231         {
232                 if( search_hash == c->hash )
233                 {
234                         /* lt. Hash-Wert: Treffer! */
235                         if( strcasecmp( Name, c->name ) == 0 ) return c;
236                 }
237                 c = c->next;
238         }
239         return NULL;
240 } /* Channel_Search */
241
242
243 GLOBAL CL2CHAN *Channel_FirstMember( CHANNEL *Chan )
244 {
245         assert( Chan != NULL );
246         return Get_First_Cl2Chan( NULL, Chan );
247 } /* Channel_FirstMember */
248
249
250 GLOBAL CL2CHAN *Channel_NextMember( CHANNEL *Chan, CL2CHAN *Cl2Chan )
251 {
252         assert( Chan != NULL );
253         assert( Cl2Chan != NULL );
254         return Get_Next_Cl2Chan( Cl2Chan->next, NULL, Chan );
255 } /* Channel_NextMember */
256
257
258 GLOBAL CL2CHAN *Channel_FirstChannelOf( CLIENT *Client )
259 {
260         assert( Client != NULL );
261         return Get_First_Cl2Chan( Client, NULL );
262 } /* Channel_FirstChannelOf */
263
264
265 GLOBAL CL2CHAN *Channel_NextChannelOf( CLIENT *Client, CL2CHAN *Cl2Chan )
266 {
267         assert( Client != NULL );
268         assert( Cl2Chan != NULL );
269         return Get_Next_Cl2Chan( Cl2Chan->next, Client, NULL );
270 } /* Channel_NextChannelOf */
271
272
273 GLOBAL CLIENT *Channel_GetClient( CL2CHAN *Cl2Chan )
274 {
275         assert( Cl2Chan != NULL );
276         return Cl2Chan->client;
277 } /* Channel_GetClient */
278
279
280 GLOBAL CHANNEL *Channel_GetChannel( CL2CHAN *Cl2Chan )
281 {
282         assert( Cl2Chan != NULL );
283         return Cl2Chan->channel;
284 } /* Channel_GetChannel */
285
286
287 GLOBAL BOOLEAN Channel_IsValidName( CHAR *Name )
288 {
289         /* Pr\9ffen, ob Name als Channelname gueltig */
290
291         CHAR *ptr, badchars[] = " ,:\x07";
292         
293         assert( Name != NULL );
294
295         if(( Name[0] != '#' ) || ( strlen( Name ) >= CHANNEL_NAME_LEN )) return FALSE;
296
297         ptr = Name;
298         while( *ptr )
299         {
300                 if( strchr( badchars, *ptr )) return FALSE;
301                 ptr++;
302         }
303         
304         return TRUE;
305 } /* Channel_IsValidName */
306
307
308 GLOBAL BOOLEAN Channel_ModeAdd( CHANNEL *Chan, CHAR Mode )
309 {
310         /* Mode soll gesetzt werden. TRUE wird geliefert, wenn der
311          * Mode neu gesetzt wurde, FALSE, wenn der Channel den Mode
312          * bereits hatte. */
313
314         CHAR x[2];
315
316         assert( Chan != NULL );
317
318         x[0] = Mode; x[1] = '\0';
319         if( ! strchr( Chan->modes, x[0] ))
320         {
321                 /* Client hat den Mode noch nicht -> setzen */
322                 strcat( Chan->modes, x );
323                 return TRUE;
324         }
325         else return FALSE;
326 } /* Channel_ModeAdd */
327
328
329 GLOBAL BOOLEAN Channel_ModeDel( CHANNEL *Chan, CHAR Mode )
330 {
331         /* Mode soll geloescht werden. TRUE wird geliefert, wenn der
332          * Mode entfernt wurde, FALSE, wenn der Channel den Mode
333          * ueberhaupt nicht hatte. */
334
335         CHAR x[2], *p;
336
337         assert( Chan != NULL );
338
339         x[0] = Mode; x[1] = '\0';
340
341         p = strchr( Chan->modes, x[0] );
342         if( ! p ) return FALSE;
343
344         /* Client hat den Mode -> loeschen */
345         while( *p )
346         {
347                 *p = *(p + 1);
348                 p++;
349         }
350         return TRUE;
351 } /* Channel_ModeDel */
352
353
354 GLOBAL BOOLEAN Channel_UserModeAdd( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
355 {
356         /* Channel-User-Mode soll gesetzt werden. TRUE wird geliefert,
357          * wenn der Mode neu gesetzt wurde, FALSE, wenn der User den
358          * Channel-Mode bereits hatte. */
359
360         CL2CHAN *cl2chan;
361         CHAR x[2];
362
363         assert( Chan != NULL );
364         assert( Client != NULL );
365
366         cl2chan = Get_Cl2Chan( Chan, Client );
367         assert( cl2chan != NULL );
368         
369         x[0] = Mode; x[1] = '\0';
370         if( ! strchr( cl2chan->modes, x[0] ))
371         {
372                 /* Client hat den Mode noch nicht -> setzen */
373                 strcat( cl2chan->modes, x );
374                 return TRUE;
375         }
376         else return FALSE;
377 } /* Channel_UserModeAdd */
378
379
380 GLOBAL BOOLEAN Channel_UserModeDel( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
381 {
382         /* Channel-User-Mode soll geloescht werden. TRUE wird geliefert,
383          * wenn der Mode entfernt wurde, FALSE, wenn der User den Channel-Mode
384          * ueberhaupt nicht hatte. */
385
386         CL2CHAN *cl2chan;
387         CHAR x[2], *p;
388
389         assert( Chan != NULL );
390         assert( Client != NULL );
391
392         cl2chan = Get_Cl2Chan( Chan, Client );
393         assert( cl2chan != NULL );
394
395         x[0] = Mode; x[1] = '\0';
396
397         p = strchr( cl2chan->modes, x[0] );
398         if( ! p ) return FALSE;
399
400         /* Client hat den Mode -> loeschen */
401         while( *p )
402         {
403                 *p = *(p + 1);
404                 p++;
405         }
406         return TRUE;
407 } /* Channel_UserModeDel */
408
409
410 GLOBAL CHAR *Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
411 {
412         /* Channel-Modes eines Users liefern */
413         
414         CL2CHAN *cl2chan;
415
416         assert( Chan != NULL );
417         assert( Client != NULL );
418
419         cl2chan = Get_Cl2Chan( Chan, Client );
420         assert( cl2chan != NULL );
421
422         return cl2chan->modes;
423 } /* Channel_UserModes */
424
425
426 GLOBAL BOOLEAN Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
427 {
428         /* Pruefen, ob Client Mitglied in Channel ist */
429
430         assert( Chan != NULL );
431         assert( Client != NULL );
432
433         if( Get_Cl2Chan( Chan, Client )) return TRUE;
434         else return FALSE;
435 } /* Channel_IsMemberOf */
436
437
438 GLOBAL CHAR *Channel_Topic( CHANNEL *Chan )
439 {
440         assert( Chan != NULL );
441         return Chan->topic;
442 } /* Channel_Topic */
443
444
445 GLOBAL VOID Channel_SetTopic( CHANNEL *Chan, CHAR *Topic )
446 {
447         assert( Chan != NULL );
448         assert( Topic != NULL );
449         
450         strncpy( Chan->topic, Topic, CHANNEL_TOPIC_LEN - 1 );
451         Chan->topic[CHANNEL_TOPIC_LEN - 1] = '\0';
452 } /* Channel_SetTopic */
453
454
455 GLOBAL BOOLEAN Channel_Write( CHANNEL *Chan, CLIENT *From, CLIENT *Client, CHAR *Text )
456 {
457         BOOLEAN is_member, has_voice, is_op, ok;
458
459         /* Okay, Ziel ist ein Channel */
460         is_member = has_voice = is_op = FALSE;
461         if( Channel_IsMemberOf( Chan, From ))
462         {
463                 is_member = TRUE;
464                 if( strchr( Channel_UserModes( Chan, From ), 'v' )) has_voice = TRUE;
465                 if( strchr( Channel_UserModes( Chan, From ), 'o' )) is_op = TRUE;
466         }
467
468         /* pruefen, ob Client in Channel schreiben darf */
469         ok = TRUE;
470         if( strchr( Channel_Modes( Chan ), 'n' ) && ( ! is_member )) ok = FALSE;
471         if( strchr( Channel_Modes( Chan ), 'm' ) && ( ! is_op ) && ( ! has_voice )) ok = FALSE;
472
473         if( ! ok ) return IRC_WriteStrClient( From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID( From ), Channel_Name( Chan ));
474
475         /* Text senden */
476         if( Client_Conn( From ) > NONE ) Conn_UpdateIdle( Client_Conn( From ));
477         return IRC_WriteStrChannelPrefix( Client, Chan, From, TRUE, "PRIVMSG %s :%s", Channel_Name( Chan ), Text );
478 } /* Channel_Write */
479
480
481
482 LOCAL CHANNEL *New_Chan( CHAR *Name )
483 {
484         /* Neue Channel-Struktur anlegen */
485
486         CHANNEL *c;
487
488         assert( Name != NULL );
489         
490         c = malloc( sizeof( CHANNEL ));
491         if( ! c )
492         {
493                 Log( LOG_EMERG, "Can't allocate memory!" );
494                 return NULL;
495         }
496         c->next = NULL;
497         strncpy( c->name, Name, CHANNEL_NAME_LEN - 1 );
498         c->name[CHANNEL_NAME_LEN - 1] = '\0';
499         strcpy( c->modes, "" );
500         strcpy( c->topic, "" );
501         c->hash = Hash( c->name );
502
503         Log( LOG_DEBUG, "Created new channel structure for \"%s\".", Name );
504         
505         return c;
506 } /* New_Chan */
507
508
509 LOCAL CL2CHAN *Get_Cl2Chan( CHANNEL *Chan, CLIENT *Client )
510 {
511         CL2CHAN *cl2chan;
512
513         assert( Chan != NULL );
514         assert( Client != NULL );
515
516         cl2chan = My_Cl2Chan;
517         while( cl2chan )
518         {
519                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) return cl2chan;
520                 cl2chan = cl2chan->next;
521         }
522         return NULL;
523 } /* Get_Cl2Chan */
524
525
526 LOCAL CL2CHAN *Add_Client( CHANNEL *Chan, CLIENT *Client )
527 {
528         CL2CHAN *cl2chan;
529
530         assert( Chan != NULL );
531         assert( Client != NULL );
532
533         /* neue CL2CHAN-Struktur anlegen */
534         cl2chan = malloc( sizeof( CL2CHAN ));
535         if( ! cl2chan )
536         {
537                 Log( LOG_EMERG, "Can't allocate memory!" );
538                 return NULL;
539         }
540         cl2chan->channel = Chan;
541         cl2chan->client = Client;
542         strcpy( cl2chan->modes, "" );
543
544         /* Verketten */
545         cl2chan->next = My_Cl2Chan;
546         My_Cl2Chan = cl2chan;
547
548         Log( LOG_DEBUG, "User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name );
549
550         return cl2chan;
551 } /* Add_Client */
552
553
554 LOCAL BOOLEAN Remove_Client( CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, CHAR *Reason, BOOLEAN ServerPART )
555 {
556         CL2CHAN *cl2chan, *last_cl2chan;
557         CHANNEL *c;
558         
559         assert( Chan != NULL );
560         assert( Client != NULL );
561         assert( Origin != NULL );
562         assert( Reason != NULL );
563
564         last_cl2chan = NULL;
565         cl2chan = My_Cl2Chan;
566         while( cl2chan )
567         {
568                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
569                 last_cl2chan = cl2chan;
570                 cl2chan = cl2chan->next;
571         }
572         if( ! cl2chan ) return FALSE;
573
574         c = cl2chan->channel;
575         assert( c != NULL );
576
577         /* Aus Verkettung loesen und freigeben */
578         if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
579         else My_Cl2Chan = cl2chan->next;
580         free( cl2chan );
581
582         if( ServerPART ) IRC_WriteStrServersPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
583         IRC_WriteStrChannelPrefix( Origin, c, Client, FALSE, "PART %s :%s", c->name, Reason );
584         if(( Client_Conn( Origin ) > NONE ) && ( Client_Type( Origin ) == CLIENT_USER )) IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
585
586         Log( LOG_DEBUG, "User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason );
587
588         /* Wenn Channel nun leer: loeschen */
589         if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
590                 
591         return TRUE;
592 } /* Remove_Client */
593
594
595 LOCAL CL2CHAN *Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
596 {
597         return Get_Next_Cl2Chan( My_Cl2Chan, Client, Chan );
598 } /* Get_First_Cl2Chan */
599
600
601 LOCAL CL2CHAN *Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel )
602 {
603         CL2CHAN *cl2chan;
604
605         assert( Client != NULL || Channel != NULL );
606         
607         cl2chan = Start;
608         while( cl2chan )
609         {
610                 if(( Client ) && ( cl2chan->client == Client )) return cl2chan;
611                 if(( Channel ) && ( cl2chan->channel == Channel )) return cl2chan;
612                 cl2chan = cl2chan->next;
613         }
614         return NULL;
615 } /* Get_Next_Cl2Chan */
616
617
618 LOCAL BOOLEAN Delete_Channel( CHANNEL *Chan )
619 {
620         /* Channel-Struktur loeschen */
621         
622         CHANNEL *chan, *last_chan;
623
624         last_chan = NULL;
625         chan = My_Channels;
626         while( chan )
627         {
628                 if( chan == Chan ) break;
629                 last_chan = chan;
630                 chan = chan->next;
631         }
632         if( ! chan ) return FALSE;
633
634         Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name );
635
636         /* Neu verketten und freigeben */
637         if( last_chan ) last_chan->next = chan->next;
638         else My_Channels = chan->next;
639         free( chan );
640                 
641         return TRUE;
642 } /* Delete_Channel */
643
644
645 /* -eof- */