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