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