]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/channel.c
- New_Chan() berechnet Hash-Werte ueber den Namen.
[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.20 2002/03/25 16:54:26 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 CHAR *Channel_Name( CHANNEL *Chan )
175 {
176         assert( Chan != NULL );
177         return Chan->name;
178 } /* Channel_Name */
179
180
181 GLOBAL CHAR *Channel_Modes( CHANNEL *Chan )
182 {
183         assert( Chan != NULL );
184         return Chan->modes;
185 } /* Channel_Modes */
186
187
188 GLOBAL CHANNEL *Channel_First( VOID )
189 {
190         return My_Channels;
191 } /* Channel_First */
192
193
194 GLOBAL CHANNEL *Channel_Next( CHANNEL *Chan )
195 {
196         assert( Chan != NULL );
197         return Chan->next;
198 } /* Channel_Next */
199
200
201 GLOBAL CHANNEL *Channel_Search( CHAR *Name )
202 {
203         /* Channel-Struktur suchen */
204         
205         CHANNEL *c;
206         UINT32 search_hash;
207
208         assert( Name != NULL );
209
210         search_hash = Hash( Name );
211         c = My_Channels;
212         while( c )
213         {
214                 if( search_hash == c->hash )
215                 {
216                         /* lt. Hash-Wert: Treffer! */
217                         if( strcasecmp( Name, c->name ) == 0 ) return c;
218                 }
219                 c = c->next;
220         }
221         return NULL;
222 } /* Channel_Search */
223
224
225 GLOBAL CL2CHAN *Channel_FirstMember( CHANNEL *Chan )
226 {
227         assert( Chan != NULL );
228         return Get_First_Cl2Chan( NULL, Chan );
229 } /* Channel_FirstMember */
230
231
232 GLOBAL CL2CHAN *Channel_NextMember( CHANNEL *Chan, CL2CHAN *Cl2Chan )
233 {
234         assert( Chan != NULL );
235         assert( Cl2Chan != NULL );
236         return Get_Next_Cl2Chan( Cl2Chan->next, NULL, Chan );
237 } /* Channel_NextMember */
238
239
240 GLOBAL CL2CHAN *Channel_FirstChannelOf( CLIENT *Client )
241 {
242         assert( Client != NULL );
243         return Get_First_Cl2Chan( Client, NULL );
244 } /* Channel_FirstChannelOf */
245
246
247 GLOBAL CL2CHAN *Channel_NextChannelOf( CLIENT *Client, CL2CHAN *Cl2Chan )
248 {
249         assert( Client != NULL );
250         assert( Cl2Chan != NULL );
251         return Get_Next_Cl2Chan( Cl2Chan->next, Client, NULL );
252 } /* Channel_NextChannelOf */
253
254
255 GLOBAL CLIENT *Channel_GetClient( CL2CHAN *Cl2Chan )
256 {
257         assert( Cl2Chan != NULL );
258         return Cl2Chan->client;
259 } /* Channel_GetClient */
260
261
262 GLOBAL CHANNEL *Channel_GetChannel( CL2CHAN *Cl2Chan )
263 {
264         assert( Cl2Chan != NULL );
265         return Cl2Chan->channel;
266 } /* Channel_GetChannel */
267
268
269 GLOBAL BOOLEAN Channel_IsValidName( CHAR *Name )
270 {
271         /* Pr\9ffen, ob Name als Channelname gueltig */
272
273         CHAR *ptr, badchars[] = " ,:\x07";
274         
275         assert( Name != NULL );
276
277         if(( Name[0] != '#' ) || ( strlen( Name ) >= CHANNEL_NAME_LEN )) return FALSE;
278
279         ptr = Name;
280         while( *ptr )
281         {
282                 if( strchr( badchars, *ptr )) return FALSE;
283                 ptr++;
284         }
285         
286         return TRUE;
287 } /* Channel_IsValidName */
288
289
290 GLOBAL BOOLEAN Channel_ModeAdd( CHANNEL *Chan, CHAR Mode )
291 {
292         /* Mode soll gesetzt werden. TRUE wird geliefert, wenn der
293          * Mode neu gesetzt wurde, FALSE, wenn der Channel den Mode
294          * bereits hatte. */
295
296         CHAR x[2];
297
298         assert( Chan != NULL );
299
300         x[0] = Mode; x[1] = '\0';
301         if( ! strchr( Chan->modes, x[0] ))
302         {
303                 /* Client hat den Mode noch nicht -> setzen */
304                 strcat( Chan->modes, x );
305                 return TRUE;
306         }
307         else return FALSE;
308 } /* Channel_ModeAdd */
309
310
311 GLOBAL BOOLEAN Channel_ModeDel( CHANNEL *Chan, CHAR Mode )
312 {
313         /* Mode soll geloescht werden. TRUE wird geliefert, wenn der
314          * Mode entfernt wurde, FALSE, wenn der Channel den Mode
315          * ueberhaupt nicht hatte. */
316
317         CHAR x[2], *p;
318
319         assert( Chan != NULL );
320
321         x[0] = Mode; x[1] = '\0';
322
323         p = strchr( Chan->modes, x[0] );
324         if( ! p ) return FALSE;
325
326         /* Client hat den Mode -> loeschen */
327         while( *p )
328         {
329                 *p = *(p + 1);
330                 p++;
331         }
332         return TRUE;
333 } /* Channel_ModeDel */
334
335
336 GLOBAL BOOLEAN Channel_UserModeAdd( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
337 {
338         /* Channel-User-Mode soll gesetzt werden. TRUE wird geliefert,
339          * wenn der Mode neu gesetzt wurde, FALSE, wenn der User den
340          * Channel-Mode bereits hatte. */
341
342         CL2CHAN *cl2chan;
343         CHAR x[2];
344
345         assert( Chan != NULL );
346         assert( Client != NULL );
347
348         cl2chan = Get_Cl2Chan( Chan, Client );
349         assert( cl2chan != NULL );
350         
351         x[0] = Mode; x[1] = '\0';
352         if( ! strchr( cl2chan->modes, x[0] ))
353         {
354                 /* Client hat den Mode noch nicht -> setzen */
355                 strcat( cl2chan->modes, x );
356                 return TRUE;
357         }
358         else return FALSE;
359 } /* Channel_UserModeAdd */
360
361
362 GLOBAL BOOLEAN Channel_UserModeDel( CHANNEL *Chan, CLIENT *Client, CHAR Mode )
363 {
364         /* Channel-User-Mode soll geloescht werden. TRUE wird geliefert,
365          * wenn der Mode entfernt wurde, FALSE, wenn der User den Channel-Mode
366          * ueberhaupt nicht hatte. */
367
368         CL2CHAN *cl2chan;
369         CHAR x[2], *p;
370
371         assert( Chan != NULL );
372         assert( Client != NULL );
373
374         cl2chan = Get_Cl2Chan( Chan, Client );
375         assert( cl2chan != NULL );
376
377         x[0] = Mode; x[1] = '\0';
378
379         p = strchr( cl2chan->modes, x[0] );
380         if( ! p ) return FALSE;
381
382         /* Client hat den Mode -> loeschen */
383         while( *p )
384         {
385                 *p = *(p + 1);
386                 p++;
387         }
388         return TRUE;
389 } /* Channel_UserModeDel */
390
391
392 GLOBAL CHAR *Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
393 {
394         /* Channel-Modes eines Users liefern */
395         
396         CL2CHAN *cl2chan;
397
398         assert( Chan != NULL );
399         assert( Client != NULL );
400
401         cl2chan = Get_Cl2Chan( Chan, Client );
402         assert( cl2chan != NULL );
403
404         return cl2chan->modes;
405 } /* Channel_UserModes */
406
407
408 GLOBAL BOOLEAN Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
409 {
410         /* Pruefen, ob Client Mitglied in Channel ist */
411
412         assert( Chan != NULL );
413         assert( Client != NULL );
414
415         if( Get_Cl2Chan( Chan, Client )) return TRUE;
416         else return FALSE;
417 } /* Channel_IsMemberOf */
418
419
420 GLOBAL CHAR *Channel_Topic( CHANNEL *Chan )
421 {
422         assert( Chan != NULL );
423         return Chan->topic;
424 } /* Channel_Topic */
425
426
427 GLOBAL VOID Channel_SetTopic( CHANNEL *Chan, CHAR *Topic )
428 {
429         assert( Chan != NULL );
430         assert( Topic != NULL );
431         
432         strncpy( Chan->topic, Topic, CHANNEL_TOPIC_LEN - 1 );
433         Chan->topic[CHANNEL_TOPIC_LEN - 1] = '\0';
434 } /* Channel_SetTopic */
435
436
437 GLOBAL BOOLEAN Channel_Write( CHANNEL *Chan, CLIENT *From, CLIENT *Client, CHAR *Text )
438 {
439         BOOLEAN is_member, has_voice, is_op, ok;
440
441         /* Okay, Ziel ist ein Channel */
442         is_member = has_voice = is_op = FALSE;
443         if( Channel_IsMemberOf( Chan, From ))
444         {
445                 is_member = TRUE;
446                 if( strchr( Channel_UserModes( Chan, From ), 'v' )) has_voice = TRUE;
447                 if( strchr( Channel_UserModes( Chan, From ), 'o' )) is_op = TRUE;
448         }
449
450         /* pruefen, ob Client in Channel schreiben darf */
451         ok = TRUE;
452         if( strchr( Channel_Modes( Chan ), 'n' ) && ( ! is_member )) ok = FALSE;
453         if( strchr( Channel_Modes( Chan ), 'm' ) && ( ! is_op ) && ( ! has_voice )) ok = FALSE;
454
455         if( ! ok ) return IRC_WriteStrClient( From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID( From ), Channel_Name( Chan ));
456
457         /* Text senden */
458         if( Client_Conn( From ) > NONE ) Conn_UpdateIdle( Client_Conn( From ));
459         return IRC_WriteStrChannelPrefix( Client, Chan, From, TRUE, "PRIVMSG %s :%s", Channel_Name( Chan ), Text );
460 } /* Channel_Write */
461
462
463
464 LOCAL CHANNEL *New_Chan( CHAR *Name )
465 {
466         /* Neue Channel-Struktur anlegen */
467
468         CHANNEL *c;
469
470         assert( Name != NULL );
471         
472         c = malloc( sizeof( CHANNEL ));
473         if( ! c )
474         {
475                 Log( LOG_EMERG, "Can't allocate memory!" );
476                 return NULL;
477         }
478         c->next = NULL;
479         strncpy( c->name, Name, CHANNEL_NAME_LEN - 1 );
480         c->name[CHANNEL_NAME_LEN - 1] = '\0';
481         strcpy( c->modes, "" );
482         strcpy( c->topic, "" );
483         c->hash = Hash( c->name );
484
485         Log( LOG_DEBUG, "Created new channel structure for \"%s\".", Name );
486         
487         return c;
488 } /* New_Chan */
489
490
491 LOCAL CL2CHAN *Get_Cl2Chan( CHANNEL *Chan, CLIENT *Client )
492 {
493         CL2CHAN *cl2chan;
494
495         assert( Chan != NULL );
496         assert( Client != NULL );
497
498         cl2chan = My_Cl2Chan;
499         while( cl2chan )
500         {
501                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) return cl2chan;
502                 cl2chan = cl2chan->next;
503         }
504         return NULL;
505 } /* Get_Cl2Chan */
506
507
508 LOCAL CL2CHAN *Add_Client( CHANNEL *Chan, CLIENT *Client )
509 {
510         CL2CHAN *cl2chan;
511
512         assert( Chan != NULL );
513         assert( Client != NULL );
514
515         /* neue CL2CHAN-Struktur anlegen */
516         cl2chan = malloc( sizeof( CL2CHAN ));
517         if( ! cl2chan )
518         {
519                 Log( LOG_EMERG, "Can't allocate memory!" );
520                 return NULL;
521         }
522         cl2chan->channel = Chan;
523         cl2chan->client = Client;
524         strcpy( cl2chan->modes, "" );
525
526         /* Verketten */
527         cl2chan->next = My_Cl2Chan;
528         My_Cl2Chan = cl2chan;
529
530         Log( LOG_DEBUG, "User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name );
531
532         return cl2chan;
533 } /* Add_Client */
534
535
536 LOCAL BOOLEAN Remove_Client( CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, CHAR *Reason, BOOLEAN ServerPART )
537 {
538         CL2CHAN *cl2chan, *last_cl2chan;
539         CHANNEL *c;
540         
541         assert( Chan != NULL );
542         assert( Client != NULL );
543         assert( Origin != NULL );
544         assert( Reason != NULL );
545
546         last_cl2chan = NULL;
547         cl2chan = My_Cl2Chan;
548         while( cl2chan )
549         {
550                 if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
551                 last_cl2chan = cl2chan;
552                 cl2chan = cl2chan->next;
553         }
554         if( ! cl2chan ) return FALSE;
555
556         c = cl2chan->channel;
557         assert( c != NULL );
558
559         /* Aus Verkettung loesen und freigeben */
560         if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
561         else My_Cl2Chan = cl2chan->next;
562         free( cl2chan );
563
564         if( ServerPART ) IRC_WriteStrServersPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
565         IRC_WriteStrChannelPrefix( Origin, c, Client, FALSE, "PART %s :%s", c->name, Reason );
566         if(( Client_Conn( Origin ) > NONE ) && ( Client_Type( Origin ) == CLIENT_USER )) IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason );
567
568         Log( LOG_DEBUG, "User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason );
569
570         /* Wenn Channel nun leer: loeschen */
571         if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
572                 
573         return TRUE;
574 } /* Remove_Client */
575
576
577 LOCAL CL2CHAN *Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
578 {
579         return Get_Next_Cl2Chan( My_Cl2Chan, Client, Chan );
580 } /* Get_First_Cl2Chan */
581
582
583 LOCAL CL2CHAN *Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel )
584 {
585         CL2CHAN *cl2chan;
586
587         assert( Client != NULL || Channel != NULL );
588         
589         cl2chan = Start;
590         while( cl2chan )
591         {
592                 if(( Client ) && ( cl2chan->client == Client )) return cl2chan;
593                 if(( Channel ) && ( cl2chan->channel == Channel )) return cl2chan;
594                 cl2chan = cl2chan->next;
595         }
596         return NULL;
597 } /* Get_Next_Cl2Chan */
598
599
600 LOCAL BOOLEAN Delete_Channel( CHANNEL *Chan )
601 {
602         /* Channel-Struktur loeschen */
603         
604         CHANNEL *chan, *last_chan;
605
606         last_chan = NULL;
607         chan = My_Channels;
608         while( chan )
609         {
610                 if( chan == Chan ) break;
611                 last_chan = chan;
612                 chan = chan->next;
613         }
614         if( ! chan ) return FALSE;
615
616         Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name );
617
618         /* Neu verketten und freigeben */
619         if( last_chan ) last_chan->next = chan->next;
620         else My_Channels = chan->next;
621         free( chan );
622                 
623         return TRUE;
624 } /* Delete_Channel */
625
626
627 /* -eof- */