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