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