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