528751e48d0f013291ace866a66f071323f78967
[ngircd-alex.git] / src / ngircd / irc-channel.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  *
11  * IRC channel commands
12  */
13
14
15 #include "portab.h"
16
17 static char UNUSED id[] = "$Id: irc-channel.c,v 1.20 2002/12/14 13:23:11 alex Exp $";
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <string.h>
22
23 #include "defines.h"
24 #include "conn.h"
25 #include "client.h"
26 #include "channel.h"
27 #include "lists.h"
28 #include "log.h"
29 #include "match.h"
30 #include "messages.h"
31 #include "parse.h"
32 #include "irc-info.h"
33 #include "irc-write.h"
34 #include "resolve.h"
35 #include "conf.h"
36
37 #include "exp.h"
38 #include "irc-channel.h"
39
40
41 GLOBAL BOOLEAN
42 IRC_JOIN( CLIENT *Client, REQUEST *Req )
43 {
44         CHAR *channame, *flags, *topic, modes[8];
45         BOOLEAN is_new_chan, is_invited, is_banned;
46         CLIENT *target;
47         CHANNEL *chan;
48
49         assert( Client != NULL );
50         assert( Req != NULL );
51
52         /* Falsche Anzahl Parameter? */
53         if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
54
55         /* Wer ist der Absender? */
56         if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
57         else target = Client;
58         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
59
60         /* Channel-Namen durchgehen */
61         chan = NULL;
62         channame = strtok( Req->argv[0], "," );
63         while( channame )
64         {
65                 chan = flags = NULL;
66
67                 /* wird der Channel neu angelegt? */
68                 if( Channel_Search( channame )) is_new_chan = FALSE;
69                 else is_new_chan = TRUE;
70
71                 /* Hat ein Server Channel-User-Modes uebergeben? */
72                 if( Client_Type( Client ) == CLIENT_SERVER )
73                 {
74                         /* Channel-Flags extrahieren */
75                         flags = strchr( channame, 0x7 );
76                         if( flags )
77                         {
78                                 *flags = '\0';
79                                 flags++;
80                         }
81                 }
82
83                 /* Lokaler Client? */
84                 if( Client_Type( Client ) == CLIENT_USER )
85                 {
86                         /* Test if the user has reached his maximum channel count */
87                         if( Client_Type( Client ) == CLIENT_USER )
88                         {
89                                 if(( Conf_MaxJoins > 0 ) && ( Channel_CountForUser( Client ) >= Conf_MaxJoins ))
90                                 {
91                                         IRC_WriteStrClient( Client, ERR_TOOMANYCHANNELS_MSG, Client_ID( Client ), channame );
92                                         return CONNECTED;
93                                 }
94                         }
95
96                         /* Existiert der Channel bereits, oder wird er im Moment neu erzeugt? */
97                         if( is_new_chan )
98                         {
99                                 /* Erster User im Channel: Operator-Flag setzen */
100                                 flags = "o";
101                         }
102                         else
103                         {
104                                 /* Existierenden Channel suchen */
105                                 chan = Channel_Search( channame );
106                                 assert( chan != NULL );
107
108                                 is_banned = Lists_CheckBanned( target, chan );
109                                 is_invited = Lists_CheckInvited( target, chan );
110
111                                 /* Testen, ob Client gebanned ist */
112                                 if(( is_banned == TRUE ) &&  ( is_invited == FALSE ))
113                                 {
114                                         /* Client ist gebanned (und nicht invited): */
115                                         IRC_WriteStrClient( Client, ERR_BANNEDFROMCHAN_MSG, Client_ID( Client ), channame );
116
117                                         /* naechsten Namen ermitteln */
118                                         channame = strtok( NULL, "," );
119                                         continue;
120                                 }
121
122                                 /* Ist der Channel "invite-only"? */
123                                 if(( strchr( Channel_Modes( chan ), 'i' ) != NULL ) && ( is_invited == FALSE ))
124                                 {
125                                         /* Channel ist "invite-only" und Client wurde nicht invited: */
126                                         IRC_WriteStrClient( Client, ERR_INVITEONLYCHAN_MSG, Client_ID( Client ), channame );
127
128                                         /* naechsten Namen ermitteln */
129                                         channame = strtok( NULL, "," );
130                                         continue;
131                                 }
132                         }
133                 }
134
135                 /* Channel joinen (und ggf. anlegen) */
136                 if( ! Channel_Join( target, channame ))
137                 {
138                         /* naechsten Namen ermitteln */
139                         channame = strtok( NULL, "," );
140                         continue;
141                 }
142                 if( ! chan ) chan = Channel_Search( channame );
143                 assert( chan != NULL );
144
145                 /* Modes setzen (wenn vorhanden) */
146                 while( flags && *flags )
147                 {
148                         Channel_UserModeAdd( chan, target, *flags );
149                         flags++;
150                 }
151
152                 /* Wenn persistenter Channel und IRC-Operator: zum Channel-OP machen */
153                 if(( strchr( Channel_Modes( chan ), 'P' )) && ( strchr( Client_Modes( target ), 'o' ))) Channel_UserModeAdd( chan, target, 'o' );
154
155                 /* Muessen Modes an andere Server gemeldet werden? */
156                 strcpy( &modes[1], Channel_UserModes( chan, target ));
157                 if( modes[1] ) modes[0] = 0x7;
158                 else modes[0] = '\0';
159
160                 /* An andere Server weiterleiten */
161                 IRC_WriteStrServersPrefix( Client, target, "JOIN :%s%s", channame, modes );
162
163                 /* im Channel bekannt machen */
164                 IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "JOIN :%s", channame );
165                 if( modes[1] )
166                 {
167                         /* Modes im Channel bekannt machen */
168                         IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "MODE %s +%s %s", channame, &modes[1], Client_ID( target ));
169                 }
170
171                 if( Client_Type( Client ) == CLIENT_USER )
172                 {
173                         /* an Client bestaetigen */
174                         IRC_WriteStrClientPrefix( Client, target, "JOIN :%s", channame );
175
176                         /* Topic an Client schicken */
177                         topic = Channel_Topic( chan );
178                         if( *topic ) IRC_WriteStrClient( Client, RPL_TOPIC_MSG, Client_ID( Client ), channame, topic );
179
180                         /* Mitglieder an Client Melden */
181                         IRC_Send_NAMES( Client, chan );
182                         IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( chan ));
183                 }
184
185                 /* naechsten Namen ermitteln */
186                 channame = strtok( NULL, "," );
187         }
188         return CONNECTED;
189 } /* IRC_JOIN */
190
191
192 GLOBAL BOOLEAN
193 IRC_PART( CLIENT *Client, REQUEST *Req )
194 {
195         CLIENT *target;
196         CHAR *chan;
197
198         assert( Client != NULL );
199         assert( Req != NULL );
200
201         /* Falsche Anzahl Parameter? */
202         if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
203
204         /* Wer ist der Absender? */
205         if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
206         else target = Client;
207         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
208
209         /* Channel-Namen durchgehen */
210         chan = strtok( Req->argv[0], "," );
211         while( chan )
212         {
213                 if( ! Channel_Part( target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID( target )))
214                 {
215                         /* naechsten Namen ermitteln */
216                         chan = strtok( NULL, "," );
217                         continue;
218                 }
219
220                 /* naechsten Namen ermitteln */
221                 chan = strtok( NULL, "," );
222         }
223         return CONNECTED;
224 } /* IRC_PART */
225
226
227 GLOBAL BOOLEAN
228 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
229 {
230         CHANNEL *chan;
231         CLIENT *from;
232         CHAR *topic;
233
234         assert( Client != NULL );
235         assert( Req != NULL );
236
237         /* Falsche Anzahl Parameter? */
238         if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
239
240         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
241         else from = Client;
242         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
243
244         /* Welcher Channel? */
245         chan = Channel_Search( Req->argv[0] );
246         if( ! chan ) return IRC_WriteStrClient( from, ERR_NOSUCHCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
247
248         /* Ist der User Mitglied in dem Channel? */
249         if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
250
251         if( Req->argc == 1 )
252         {
253                 /* Topic erfragen */
254                 topic = Channel_Topic( chan );
255                 if( *topic ) return IRC_WriteStrClient( from, RPL_TOPIC_MSG, Client_ID( from ), Channel_Name( chan ), topic );
256                 else return IRC_WriteStrClient( from, RPL_NOTOPIC_MSG, Client_ID( from ), Channel_Name( chan ));
257         }
258
259         if( strchr( Channel_Modes( chan ), 't' ))
260         {
261                 /* Topic Lock. Ist der User ein Channel Operator? */
262                 if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan ));
263         }
264
265         /* Topic setzen */
266         Channel_SetTopic( chan, Req->argv[1] );
267         Log( LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s", Client_Mask( from ), Channel_Name( chan ), Req->argv[1][0] ? Req->argv[1] : "<none>" );
268
269         /* im Channel bekannt machen und an Server weiterleiten */
270         IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
271         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
272
273         if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
274         else return CONNECTED;
275 } /* IRC_TOPIC */
276
277
278 GLOBAL BOOLEAN
279 IRC_LIST( CLIENT *Client, REQUEST *Req )
280 {
281         CHAR *pattern;
282         CHANNEL *chan;
283         CLIENT *from, *target;
284
285         assert( Client != NULL );
286         assert( Req != NULL );
287
288         /* Falsche Anzahl Parameter? */
289         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
290
291         if( Req->argc > 0 ) pattern = strtok( Req->argv[0], "," );
292         else pattern = "*";
293
294         /* From aus Prefix ermitteln */
295         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
296         else from = Client;
297         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix );
298
299         if( Req->argc == 2 )
300         {
301                 /* an anderen Server forwarden */
302                 target = Client_Search( Req->argv[1] );
303                 if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[1] );
304
305                 if( target != Client_ThisServer( ))
306                 {
307                         /* Ok, anderer Server ist das Ziel: forwarden */
308                         return IRC_WriteStrClientPrefix( target, from, "LIST %s :%s", from, Req->argv[1] );
309                 }
310         }
311         
312         while( pattern )
313         {
314                 /* alle Channel durchgehen */
315                 chan = Channel_First( );
316                 while( chan )
317                 {
318                         /* Passt die Suchmaske auf diesen Channel? */
319                         if( Match( pattern, Channel_Name( chan )))
320                         {
321                                 /* Treffer! */
322                                 if( ! IRC_WriteStrClient( from, RPL_LIST_MSG, from, Channel_Name( chan ), Channel_MemberCount( chan ), Channel_Topic( chan ))) return DISCONNECTED;
323                         }
324                         chan = Channel_Next( chan );
325                 }
326                 
327                 /* naechsten Namen ermitteln */
328                 if( Req->argc > 0 ) pattern = strtok( NULL, "," );
329                 else pattern = NULL;
330         }
331         
332         return IRC_WriteStrClient( from, RPL_LISTEND_MSG, from );
333 } /* IRC_LIST */
334
335
336 GLOBAL BOOLEAN
337 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
338 {
339         CLIENT *from;
340         CHANNEL *chan;
341         CHAR *ptr;
342
343         assert( Client != NULL );
344         assert( Req != NULL );
345
346         /* Falsche Anzahl Parameter? */
347         if(( Req->argc < 1 ) || ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
348
349         /* From-Server suchen */
350         from = Client_Search( Req->prefix );
351         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
352
353         /* Channel suchen bzw. erzeugen */
354         chan = Channel_Search( Req->argv[0] );
355         if( ! chan ) chan = Channel_Create( Req->argv[0] );
356         if( ! chan ) return CONNECTED;
357
358         if( Req->argv[1][0] == '+' )
359         {
360                 ptr = Channel_Modes( chan );
361                 if( ! *ptr )
362                 {
363                         /* OK, es sind noch keine Modes gesetzt */
364                         Channel_SetModes( chan, &Req->argv[1][1] );
365                         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "MODE %s +%s", Req->argv[0], &Req->argv[1][1] );
366                 }
367         }
368         else Log( LOG_WARNING, "CHANNELINFO: invalid MODE format ignored!" );
369
370         if( Req->argc == 3 )
371         {
372                 /* Es wurde auch ein Topic mit uebermittelt */
373                 ptr = Channel_Topic( chan );
374                 if( ! *ptr )
375                 {
376                         /* OK, es ist bisher kein Topic gesetzt */
377                         Channel_SetTopic( chan, Req->argv[2] );
378                         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "TOPIC %s :%s", Req->argv[0], Req->argv[2] );
379                 }
380         }
381
382         /* an andere Server forwarden */
383         IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
384         return CONNECTED;
385 } /* IRC_CHANINFO */
386
387
388 /* -eof- */