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