52e245190d40ec8eca2addb0edb816575589056a
[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.19 2002/12/13 17:53:32 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                         /* Existiert der Channel bereits, oder wird er im Moment neu erzeugt? */
87                         if( is_new_chan )
88                         {
89                                 /* Erster User im Channel: Operator-Flag setzen */
90                                 flags = "o";
91                         }
92                         else
93                         {
94                                 /* Existierenden Channel suchen */
95                                 chan = Channel_Search( channame );
96                                 assert( chan != NULL );
97
98                                 /* Test if the user has reached his maximum channel count */
99                                 if( Client_Type( Client ) == CLIENT_USER )
100                                 {
101                                         if(( Conf_MaxJoins > 0 ) && ( Channel_CountForUser( chan, Client ) > Conf_MaxJoins ))
102                                         {
103                                                 IRC_WriteStrClient( Client, ERR_TOOMANYCHANNELS_MSG, Client_ID( Client ), channame );
104                                                 return CONNECTED;
105                                         }
106                                 }
107
108
109                                 is_banned = Lists_CheckBanned( target, chan );
110                                 is_invited = Lists_CheckInvited( target, chan );
111
112                                 /* Testen, ob Client gebanned ist */
113                                 if(( is_banned == TRUE ) &&  ( is_invited == FALSE ))
114                                 {
115                                         /* Client ist gebanned (und nicht invited): */
116                                         IRC_WriteStrClient( Client, ERR_BANNEDFROMCHAN_MSG, Client_ID( Client ), channame );
117
118                                         /* naechsten Namen ermitteln */
119                                         channame = strtok( NULL, "," );
120                                         continue;
121                                 }
122
123                                 /* Ist der Channel "invite-only"? */
124                                 if(( strchr( Channel_Modes( chan ), 'i' ) != NULL ) && ( is_invited == FALSE ))
125                                 {
126                                         /* Channel ist "invite-only" und Client wurde nicht invited: */
127                                         IRC_WriteStrClient( Client, ERR_INVITEONLYCHAN_MSG, Client_ID( Client ), channame );
128
129                                         /* naechsten Namen ermitteln */
130                                         channame = strtok( NULL, "," );
131                                         continue;
132                                 }
133                         }
134                 }
135
136                 /* Channel joinen (und ggf. anlegen) */
137                 if( ! Channel_Join( target, channame ))
138                 {
139                         /* naechsten Namen ermitteln */
140                         channame = strtok( NULL, "," );
141                         continue;
142                 }
143                 if( ! chan ) chan = Channel_Search( channame );
144                 assert( chan != NULL );
145
146                 /* Modes setzen (wenn vorhanden) */
147                 while( flags && *flags )
148                 {
149                         Channel_UserModeAdd( chan, target, *flags );
150                         flags++;
151                 }
152
153                 /* Wenn persistenter Channel und IRC-Operator: zum Channel-OP machen */
154                 if(( strchr( Channel_Modes( chan ), 'P' )) && ( strchr( Client_Modes( target ), 'o' ))) Channel_UserModeAdd( chan, target, 'o' );
155
156                 /* Muessen Modes an andere Server gemeldet werden? */
157                 strcpy( &modes[1], Channel_UserModes( chan, target ));
158                 if( modes[1] ) modes[0] = 0x7;
159                 else modes[0] = '\0';
160
161                 /* An andere Server weiterleiten */
162                 IRC_WriteStrServersPrefix( Client, target, "JOIN :%s%s", channame, modes );
163
164                 /* im Channel bekannt machen */
165                 IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "JOIN :%s", channame );
166                 if( modes[1] )
167                 {
168                         /* Modes im Channel bekannt machen */
169                         IRC_WriteStrChannelPrefix( Client, chan, target, FALSE, "MODE %s +%s %s", channame, &modes[1], Client_ID( target ));
170                 }
171
172                 if( Client_Type( Client ) == CLIENT_USER )
173                 {
174                         /* an Client bestaetigen */
175                         IRC_WriteStrClientPrefix( Client, target, "JOIN :%s", channame );
176
177                         /* Topic an Client schicken */
178                         topic = Channel_Topic( chan );
179                         if( *topic ) IRC_WriteStrClient( Client, RPL_TOPIC_MSG, Client_ID( Client ), channame, topic );
180
181                         /* Mitglieder an Client Melden */
182                         IRC_Send_NAMES( Client, chan );
183                         IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( chan ));
184                 }
185
186                 /* naechsten Namen ermitteln */
187                 channame = strtok( NULL, "," );
188         }
189         return CONNECTED;
190 } /* IRC_JOIN */
191
192
193 GLOBAL BOOLEAN
194 IRC_PART( CLIENT *Client, REQUEST *Req )
195 {
196         CLIENT *target;
197         CHAR *chan;
198
199         assert( Client != NULL );
200         assert( Req != NULL );
201
202         /* Falsche Anzahl Parameter? */
203         if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
204
205         /* Wer ist der Absender? */
206         if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
207         else target = Client;
208         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
209
210         /* Channel-Namen durchgehen */
211         chan = strtok( Req->argv[0], "," );
212         while( chan )
213         {
214                 if( ! Channel_Part( target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID( target )))
215                 {
216                         /* naechsten Namen ermitteln */
217                         chan = strtok( NULL, "," );
218                         continue;
219                 }
220
221                 /* naechsten Namen ermitteln */
222                 chan = strtok( NULL, "," );
223         }
224         return CONNECTED;
225 } /* IRC_PART */
226
227
228 GLOBAL BOOLEAN
229 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
230 {
231         CHANNEL *chan;
232         CLIENT *from;
233         CHAR *topic;
234
235         assert( Client != NULL );
236         assert( Req != NULL );
237
238         /* Falsche Anzahl Parameter? */
239         if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
240
241         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
242         else from = Client;
243         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
244
245         /* Welcher Channel? */
246         chan = Channel_Search( Req->argv[0] );
247         if( ! chan ) return IRC_WriteStrClient( from, ERR_NOSUCHCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
248
249         /* Ist der User Mitglied in dem Channel? */
250         if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
251
252         if( Req->argc == 1 )
253         {
254                 /* Topic erfragen */
255                 topic = Channel_Topic( chan );
256                 if( *topic ) return IRC_WriteStrClient( from, RPL_TOPIC_MSG, Client_ID( from ), Channel_Name( chan ), topic );
257                 else return IRC_WriteStrClient( from, RPL_NOTOPIC_MSG, Client_ID( from ), Channel_Name( chan ));
258         }
259
260         if( strchr( Channel_Modes( chan ), 't' ))
261         {
262                 /* Topic Lock. Ist der User ein Channel Operator? */
263                 if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan ));
264         }
265
266         /* Topic setzen */
267         Channel_SetTopic( chan, Req->argv[1] );
268         Log( LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s", Client_Mask( from ), Channel_Name( chan ), Req->argv[1][0] ? Req->argv[1] : "<none>" );
269
270         /* im Channel bekannt machen und an Server weiterleiten */
271         IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
272         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
273
274         if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
275         else return CONNECTED;
276 } /* IRC_TOPIC */
277
278
279 GLOBAL BOOLEAN
280 IRC_LIST( CLIENT *Client, REQUEST *Req )
281 {
282         CHAR *pattern;
283         CHANNEL *chan;
284         CLIENT *from, *target;
285
286         assert( Client != NULL );
287         assert( Req != NULL );
288
289         /* Falsche Anzahl Parameter? */
290         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
291
292         if( Req->argc > 0 ) pattern = strtok( Req->argv[0], "," );
293         else pattern = "*";
294
295         /* From aus Prefix ermitteln */
296         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
297         else from = Client;
298         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix );
299
300         if( Req->argc == 2 )
301         {
302                 /* an anderen Server forwarden */
303                 target = Client_Search( Req->argv[1] );
304                 if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[1] );
305
306                 if( target != Client_ThisServer( ))
307                 {
308                         /* Ok, anderer Server ist das Ziel: forwarden */
309                         return IRC_WriteStrClientPrefix( target, from, "LIST %s :%s", from, Req->argv[1] );
310                 }
311         }
312         
313         while( pattern )
314         {
315                 /* alle Channel durchgehen */
316                 chan = Channel_First( );
317                 while( chan )
318                 {
319                         /* Passt die Suchmaske auf diesen Channel? */
320                         if( Match( pattern, Channel_Name( chan )))
321                         {
322                                 /* Treffer! */
323                                 if( ! IRC_WriteStrClient( from, RPL_LIST_MSG, from, Channel_Name( chan ), Channel_MemberCount( chan ), Channel_Topic( chan ))) return DISCONNECTED;
324                         }
325                         chan = Channel_Next( chan );
326                 }
327                 
328                 /* naechsten Namen ermitteln */
329                 if( Req->argc > 0 ) pattern = strtok( NULL, "," );
330                 else pattern = NULL;
331         }
332         
333         return IRC_WriteStrClient( from, RPL_LISTEND_MSG, from );
334 } /* IRC_LIST */
335
336
337 GLOBAL BOOLEAN
338 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
339 {
340         CLIENT *from;
341         CHANNEL *chan;
342         CHAR *ptr;
343
344         assert( Client != NULL );
345         assert( Req != NULL );
346
347         /* Falsche Anzahl Parameter? */
348         if(( Req->argc < 1 ) || ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
349
350         /* From-Server suchen */
351         from = Client_Search( Req->prefix );
352         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
353
354         /* Channel suchen bzw. erzeugen */
355         chan = Channel_Search( Req->argv[0] );
356         if( ! chan ) chan = Channel_Create( Req->argv[0] );
357         if( ! chan ) return CONNECTED;
358
359         if( Req->argv[1][0] == '+' )
360         {
361                 ptr = Channel_Modes( chan );
362                 if( ! *ptr )
363                 {
364                         /* OK, es sind noch keine Modes gesetzt */
365                         Channel_SetModes( chan, &Req->argv[1][1] );
366                         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "MODE %s +%s", Req->argv[0], &Req->argv[1][1] );
367                 }
368         }
369         else Log( LOG_WARNING, "CHANNELINFO: invalid MODE format ignored!" );
370
371         if( Req->argc == 3 )
372         {
373                 /* Es wurde auch ein Topic mit uebermittelt */
374                 ptr = Channel_Topic( chan );
375                 if( ! *ptr )
376                 {
377                         /* OK, es ist bisher kein Topic gesetzt */
378                         Channel_SetTopic( chan, Req->argv[2] );
379                         IRC_WriteStrChannelPrefix( Client, chan, from, FALSE, "TOPIC %s :%s", Req->argv[0], Req->argv[2] );
380                 }
381         }
382
383         /* an andere Server forwarden */
384         IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
385         return CONNECTED;
386 } /* IRC_CHANINFO */
387
388
389 /* -eof- */