]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-channel.c
New configuration option "PredefChannelsOnly": if set, make
[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.38 2006/11/05 13:03:48 fw Exp $";
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "defines.h"
26 #include "conn.h"
27 #include "client.h"
28 #include "channel.h"
29 #include "lists.h"
30 #include "log.h"
31 #include "match.h"
32 #include "messages.h"
33 #include "parse.h"
34 #include "irc-info.h"
35 #include "irc-write.h"
36 #include "resolve.h"
37 #include "conf.h"
38
39 #include "exp.h"
40 #include "irc-channel.h"
41
42
43 GLOBAL bool
44 IRC_JOIN( CLIENT *Client, REQUEST *Req )
45 {
46         char *channame, *channame_ptr, *key, *key_ptr, *flags, *topic, modes[8];
47         bool is_new_chan, is_invited, is_banned;
48         CLIENT *target;
49         CHANNEL *chan;
50         
51         assert( Client != NULL );
52         assert( Req != NULL );
53
54         /* Bad number of arguments? */
55         if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
56
57         /* Who is the sender? */
58         if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
59         else target = Client;
60         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
61
62         /* Are channel keys given? */
63         if (Req->argc > 1) {
64                 key = Req->argv[1];
65                 key_ptr = strchr(key, ',');
66                 if (key_ptr) *key_ptr = '\0';
67         }
68         else
69                 key = key_ptr = NULL;
70
71         channame = Req->argv[0];
72         channame_ptr = strchr(channame, ',');
73         if (channame_ptr) *channame_ptr = '\0';
74
75         /* Channel-Namen durchgehen */
76         while (channame)
77         {
78                 chan = NULL; flags = NULL;
79
80                 /* wird der Channel neu angelegt? */
81                 if( Channel_Search( channame )) {
82                         is_new_chan = false;
83                 } else {
84                         if (Conf_PredefChannelsOnly) { /* this server does not allow creation of channels */
85                                 IRC_WriteStrClient( Client, ERR_BANNEDFROMCHAN_MSG, Client_ID( Client ), channame );
86                                 /* Try next name, if any */
87                                 channame = strchr(channame, ',');
88                                 continue;
89                         }
90                         is_new_chan = true;
91                 }
92
93                 /* Hat ein Server Channel-User-Modes uebergeben? */
94                 if( Client_Type( Client ) == CLIENT_SERVER )
95                 {
96                         /* Channel-Flags extrahieren */
97                         flags = strchr( channame, 0x7 );
98                         if( flags )
99                         {
100                                 *flags = '\0';
101                                 flags++;
102                         }
103                 }
104
105                 /* Local client? */
106                 if( Client_Type( Client ) == CLIENT_USER )
107                 {
108                         /* Test if the user has reached his maximum channel count */
109                         if(( Conf_MaxJoins > 0 ) && ( Channel_CountForUser( Client ) >= Conf_MaxJoins ))
110                                 return IRC_WriteStrClient( Client, ERR_TOOMANYCHANNELS_MSG,
111                                                         Client_ID( Client ), channame );
112
113                         /* Existiert der Channel bereits, oder wird er im Moment neu erzeugt? */
114                         if( is_new_chan )
115                         {
116                                 /* Erster User im Channel: Operator-Flag setzen */
117                                 flags = "o";
118                         }
119                         else
120                         {
121                                 /* Existierenden Channel suchen */
122                                 chan = Channel_Search( channame );
123                                 assert( chan != NULL );
124
125                                 is_banned = Lists_CheckBanned( target, chan );
126                                 is_invited = Lists_CheckInvited( target, chan );
127
128                                 /* Testen, ob Client gebanned ist */
129                                 if(( is_banned == true) &&  ( is_invited == false ))
130                                 {
131                                         /* Client ist gebanned (und nicht invited): */
132                                         IRC_WriteStrClient( Client, ERR_BANNEDFROMCHAN_MSG, Client_ID( Client ), channame );
133
134                                         /* Try next name, if any */
135                                         channame = strchr(channame, ',');
136                                         continue;
137                                 }
138
139                                 /* Ist der Channel "invite-only"? */
140                                 if(( strchr( Channel_Modes( chan ), 'i' )) && ( is_invited == false ))
141                                 {
142                                         /* Channel ist "invite-only" und Client wurde nicht invited: */
143                                         IRC_WriteStrClient( Client, ERR_INVITEONLYCHAN_MSG, Client_ID( Client ), channame );
144
145                                         /* Try next name, if any */
146                                         channame = strchr(channame, ',');
147                                         continue;
148                                 }
149
150                                 /* Is the channel protected by a key? */
151                                 if(( strchr( Channel_Modes( chan ), 'k' )) && ( strcmp( Channel_Key( chan ), key ? key : "" ) != 0 ))
152                                 {
153                                         /* Bad channel key! */
154                                         IRC_WriteStrClient( Client, ERR_BADCHANNELKEY_MSG, Client_ID( Client ), channame );
155
156                                         /* Try next name, if any */
157                                         channame = strchr(channame, ',');
158                                         continue;
159                                 }
160
161                                 /* Are there already too many members? */
162                                 if(( strchr( Channel_Modes( chan ), 'l' )) && ( Channel_MaxUsers( chan ) <= Channel_MemberCount( chan )))
163                                 {
164                                         /* Bad channel key! */
165                                         IRC_WriteStrClient( Client, ERR_CHANNELISFULL_MSG, Client_ID( Client ), channame );
166
167                                         /* Try next name, if any */
168                                         channame = strchr(channame, ',');
169                                         continue;
170                                 }
171                         }
172                 }
173                 else
174                 {
175                         /* Remote server: we don't need to know whether the
176                          * client is invited or not, but we have to make sure
177                          * that the "one shot" entries (generated by INVITE
178                          * commands) in this list become deleted when a user
179                          * joins a channel this way. */
180                         chan = Channel_Search( channame );
181                         if( chan != NULL ) (void)Lists_CheckInvited( target, chan );
182                 }
183
184                 /* Channel joinen (und ggf. anlegen) */
185                 if( ! Channel_Join( target, channame ))
186                 {
187                         /* naechsten Namen ermitteln */
188                         channame = strchr(channame, ',');
189                         continue;
190                 }
191                 if( ! chan ) chan = Channel_Search( channame );
192                 assert( chan != NULL );
193
194                 /* Modes setzen (wenn vorhanden) */
195                 while( flags && *flags )
196                 {
197                         Channel_UserModeAdd( chan, target, *flags );
198                         flags++;
199                 }
200
201                 /* Wenn persistenter Channel und IRC-Operator: zum Channel-OP machen */
202                 if(( strchr( Channel_Modes( chan ), 'P' )) && ( strchr( Client_Modes( target ), 'o' ))) Channel_UserModeAdd( chan, target, 'o' );
203
204                 /* Muessen Modes an andere Server gemeldet werden? */
205                 strlcpy( &modes[1], Channel_UserModes( chan, target ), sizeof( modes ) - 1 );
206                 if( modes[1] ) modes[0] = 0x7;
207                 else modes[0] = '\0';
208
209                 /* An andere Server weiterleiten */
210                 IRC_WriteStrServersPrefix( Client, target, "JOIN :%s%s", channame, modes );
211
212                 /* im Channel bekannt machen */
213                 IRC_WriteStrChannelPrefix( Client, chan, target, false, "JOIN :%s", channame );
214                 if( modes[1] )
215                 {
216                         /* Modes im Channel bekannt machen */
217                         IRC_WriteStrChannelPrefix( Client, chan, target, false, "MODE %s +%s %s", channame, &modes[1], Client_ID( target ));
218                 }
219
220                 if( Client_Type( Client ) == CLIENT_USER )
221                 {
222                         /* an Client bestaetigen */
223                         IRC_WriteStrClientPrefix( Client, target, "JOIN :%s", channame );
224
225                         /* Send topic to client, if any */
226                         topic = Channel_Topic(chan);
227                         if (*topic) {
228                                 IRC_WriteStrClient(Client, RPL_TOPIC_MSG,
229                                         Client_ID(Client), channame, topic);
230 #ifndef STRICT_RFC
231                                 IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG,
232                                         Client_ID(Client), channame,
233                                         Channel_TopicWho(chan),
234                                         Channel_TopicTime(chan));
235 #endif
236                         }
237
238                         /* Mitglieder an Client Melden */
239                         IRC_Send_NAMES( Client, chan );
240                         IRC_WriteStrClient( Client, RPL_ENDOFNAMES_MSG, Client_ID( Client ), Channel_Name( chan ));
241                 }
242
243                 /* next channel? */
244                 channame = channame_ptr;
245                 if (channame) {
246                         channame++;
247                         channame_ptr = strchr(channame, ',');
248                         if (channame_ptr) *channame_ptr = '\0';
249
250                         if (key_ptr) {
251                                 key = ++key_ptr;
252                                 key_ptr = strchr(key, ',');
253                                 if (key_ptr) *key_ptr = '\0';
254                         }
255                 }
256         }
257         return CONNECTED;
258 } /* IRC_JOIN */
259
260
261 GLOBAL bool
262 IRC_PART( CLIENT *Client, REQUEST *Req )
263 {
264         CLIENT *target;
265         char *chan;
266
267         assert( Client != NULL );
268         assert( Req != NULL );
269
270         /* Falsche Anzahl Parameter? */
271         if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
272
273         /* Wer ist der Absender? */
274         if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
275         else target = Client;
276         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
277
278         /* Channel-Namen durchgehen */
279         chan = strtok( Req->argv[0], "," );
280         while( chan )
281         {
282                 if( ! Channel_Part( target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID( target )))
283                 {
284                         /* naechsten Namen ermitteln */
285                         chan = strtok( NULL, "," );
286                         continue;
287                 }
288
289                 /* naechsten Namen ermitteln */
290                 chan = strtok( NULL, "," );
291         }
292         return CONNECTED;
293 } /* IRC_PART */
294
295
296 GLOBAL bool
297 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
298 {
299         CHANNEL *chan;
300         CLIENT *from;
301         char *topic;
302         bool r;
303
304         assert( Client != NULL );
305         assert( Req != NULL );
306
307         /* Falsche Anzahl Parameter? */
308         if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
309
310         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
311         else from = Client;
312         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
313
314         /* Welcher Channel? */
315         chan = Channel_Search( Req->argv[0] );
316         if( ! chan ) return IRC_WriteStrClient( from, ERR_NOSUCHCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
317
318         /* Ist der User Mitglied in dem Channel? */
319         if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
320
321         if( Req->argc == 1 )
322         {
323                 /* Request actual topic */
324                 topic = Channel_Topic(chan);
325                 if (*topic) {
326                         r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
327                                 Client_ID(Client), Channel_Name(chan), topic);
328 #ifndef STRICT_RFC
329                         r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
330                                 Client_ID(Client), Channel_Name(chan),
331                                 Channel_TopicWho(chan),
332                                 Channel_TopicTime(chan));
333 #endif
334                         return r;
335                 }
336                 else
337                          return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
338                                         Client_ID(from), Channel_Name(chan));
339         }
340
341         if( strchr( Channel_Modes( chan ), 't' ))
342         {
343                 /* Topic Lock. Ist der User ein Channel Operator? */
344                 if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan ));
345         }
346
347         /* Set new topic */
348         Channel_SetTopic(chan, from, Req->argv[1]);
349         Log(LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s",
350                 Client_Mask(from), Channel_Name(chan),
351                 Req->argv[1][0] ? Req->argv[1] : "<none>");
352
353         /* im Channel bekannt machen und an Server weiterleiten */
354         IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
355         IRC_WriteStrChannelPrefix( Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
356
357         if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] );
358         else return CONNECTED;
359 } /* IRC_TOPIC */
360
361
362 /**
363  * Handler for the IRC "LIST" command.
364  * This implementation handles the local case as well as the forwarding of the
365  * LIST command to other servers in the IRC network.
366  */
367 GLOBAL bool
368 IRC_LIST( CLIENT *Client, REQUEST *Req )
369 {
370         char *pattern;
371         CHANNEL *chan;
372         CLIENT *from, *target;
373
374         assert( Client != NULL );
375         assert( Req != NULL );
376
377         /* Bad number of prameters? */
378         if( Req->argc > 2 )
379                 return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
380                         Client_ID( Client ), Req->command );
381
382         if( Req->argc > 0 )
383                 pattern = strtok( Req->argv[0], "," );
384         else
385                 pattern = "*";
386
387         /* Get sender from prefix, if any */
388         if( Client_Type( Client ) == CLIENT_SERVER )
389                 from = Client_Search( Req->prefix );
390         else
391                 from = Client;
392
393         if( ! from )
394                 return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG,
395                                 Client_ID( Client ), Req->prefix );
396
397         if( Req->argc == 2 )
398         {
399                 /* Forward to other server? */
400                 target = Client_Search( Req->argv[1] );
401                 if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER ))
402                         return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG,
403                                         Client_ID( Client ), Req->argv[1] );
404
405                 if( target != Client_ThisServer( ))
406                 {
407                         /* Target is indeed an other server, forward it! */
408                         return IRC_WriteStrClientPrefix( target, from,
409                                         "LIST %s :%s", Client_ID( from ),
410                                         Req->argv[1] );
411                 }
412         }
413         
414         while( pattern )
415         {
416                 /* Loop through all the channels */
417                 chan = Channel_First( );
418                 while( chan )
419                 {
420                         /* Check search pattern */
421                         if( Match( pattern, Channel_Name( chan )))
422                         {
423                                 /* Gotcha! */
424                                 if( ! strchr( Channel_Modes( chan ), 's' ) ||
425                                     Channel_IsMemberOf( chan, from ))
426                                 {
427                                         if( ! IRC_WriteStrClient( from,
428                                             RPL_LIST_MSG, Client_ID( from ),
429                                             Channel_Name( chan ),
430                                             Channel_MemberCount( chan ),
431                                             Channel_Topic( chan )))
432                                                 return DISCONNECTED;
433                                 }
434                         }
435                         chan = Channel_Next( chan );
436                 }
437                 
438                 /* Get next name ... */
439                 if( Req->argc > 0 )
440                         pattern = strtok( NULL, "," );
441                 else
442                         pattern = NULL;
443         }
444         
445         return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from ));
446 } /* IRC_LIST */
447
448
449 GLOBAL bool
450 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
451 {
452         char modes_add[COMMAND_LEN], l[16], *ptr;
453         CLIENT *from;
454         CHANNEL *chan;
455         int arg_topic;
456
457         assert( Client != NULL );
458         assert( Req != NULL );
459
460         /* Bad number of parameters? */
461         if(( Req->argc < 2 ) || ( Req->argc == 4 ) || ( Req->argc > 5 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
462
463         /* Compatibility kludge */
464         if( Req->argc == 5 ) arg_topic = 4;
465         else if( Req->argc == 3 ) arg_topic = 2;
466         else arg_topic = -1;
467
468         /* Search origin */
469         from = Client_Search( Req->prefix );
470         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
471
472         /* Search or create channel */
473         chan = Channel_Search( Req->argv[0] );
474         if( ! chan ) chan = Channel_Create( Req->argv[0] );
475         if( ! chan ) return CONNECTED;
476
477         if( Req->argv[1][0] == '+' )
478         {
479                 ptr = Channel_Modes( chan );
480                 if( ! *ptr )
481                 {
482                         /* OK, this channel doesn't have modes jet, set the received ones: */
483                         Channel_SetModes( chan, &Req->argv[1][1] );
484
485                         if( Req->argc == 5 )
486                         {
487                                 if( strchr( Channel_Modes( chan ), 'k' )) Channel_SetKey( chan, Req->argv[2] );
488                                 if( strchr( Channel_Modes( chan ), 'l' )) Channel_SetMaxUsers( chan, atol( Req->argv[3] ));
489                         }
490                         else
491                         {
492                                 /* Delete modes which we never want to inherit */
493                                 Channel_ModeDel( chan, 'l' );
494                                 Channel_ModeDel( chan, 'k' );
495                         }
496
497                         strcpy( modes_add, "" );
498                         ptr = Channel_Modes( chan );
499                         while( *ptr )
500                         {
501                                 if( *ptr == 'l' )
502                                 {
503                                         snprintf( l, sizeof( l ), " %lu", Channel_MaxUsers( chan ));
504                                         strlcat( modes_add, l, sizeof( modes_add ));
505                                 }
506                                 if( *ptr == 'k' )
507                                 {
508                                         strlcat( modes_add, " ", sizeof( modes_add ));
509                                         strlcat( modes_add, Channel_Key( chan ), sizeof( modes_add ));
510                                 }
511                                 ptr++;
512                         }
513                         
514                         /* Inform members of this channel */
515                         IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add );
516                 }
517         }
518         else Log( LOG_WARNING, "CHANINFO: invalid MODE format ignored!" );
519
520         if( arg_topic > 0 )
521         {
522                 /* We got a topic */
523                 ptr = Channel_Topic( chan );
524                 if(( ! *ptr ) && ( Req->argv[arg_topic][0] ))
525                 {
526                         /* OK, there is no topic jet */
527                         Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
528                         IRC_WriteStrChannelPrefix(Client, chan, from, false,
529                              "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
530                 }
531         }
532
533         /* Forward CHANINFO to other serevrs */
534         if( Req->argc == 5 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2], Req->argv[3], Req->argv[4] );
535         else if( Req->argc == 3 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
536         else IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s", Req->argv[0], Req->argv[1] );
537
538         return CONNECTED;
539 } /* IRC_CHANINFO */
540
541
542 /* -eof- */