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