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