]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-channel.c
1b8685d03f5b1886902261d4cc9da9b23638131b
[ngircd-alex.git] / src / ngircd / irc-channel.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2010 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
12
13 #include "portab.h"
14
15 /**
16  * @file
17  * IRC channel commands
18  */
19
20 #include "imp.h"
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "defines.h"
27 #include "conn.h"
28 #include "channel.h"
29 #include "conn-func.h"
30 #include "lists.h"
31 #include "log.h"
32 #include "match.h"
33 #include "messages.h"
34 #include "parse.h"
35 #include "irc-info.h"
36 #include "irc-write.h"
37 #include "conf.h"
38
39 #include "exp.h"
40 #include "irc-channel.h"
41
42
43 /*
44  * RFC 2812, (3.2.1 Join message Command):
45  *  Note that this message
46  *  accepts a special argument ("0"), which is a special request to leave all
47  *  channels the user is currently a member of. The server will process this
48  *  message as if the user had sent a PART command (See Section 3.2.2) for
49  *  each channel he is a member of.
50  */
51 static bool
52 part_from_all_channels(CLIENT* client, CLIENT *target)
53 {
54         CL2CHAN *cl2chan;
55         CHANNEL *chan;
56
57         while ((cl2chan = Channel_FirstChannelOf(target))) {
58                 chan = Channel_GetChannel(cl2chan);
59                 assert( chan != NULL );
60                 Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
61         }
62         return CONNECTED;
63 }
64
65
66 /**
67  * Check weather a local client is allowed to join an already existing
68  * channel or not.
69  * @param Client Client that sent the JOIN command
70  * @param chan Channel to check
71  * @param channame Name of the channel
72  * @param key Provided channel key (or NULL if none has been provided)
73  * @return true if client is allowed to join channel, false otherwise
74  */
75 static bool
76 join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
77              const char *key)
78 {
79         bool is_invited, is_banned;
80         const char *channel_modes;
81
82         /* Allow IRC operators to overwrite channel limits */
83         if (strchr(Client_Modes(Client), 'o'))
84                 return true;
85
86         is_banned = Lists_Check(Channel_GetListBans(chan), Client);
87         is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
88
89         if (is_banned && !is_invited) {
90                 /* Client is banned from channel (and not on invite list) */
91                 IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
92                                    Client_ID(Client), channame);
93                 return false;
94         }
95
96         channel_modes = Channel_Modes(chan);
97         if (strchr(channel_modes, 'i') && !is_invited) {
98                 /* Channel is "invite-only" and client is not on invite list */
99                 IRC_WriteStrClient(Client, ERR_INVITEONLYCHAN_MSG,
100                                    Client_ID(Client), channame);
101                 return false;
102         }
103
104         if (!Channel_CheckKey(chan, Client, key ? key : "")) {
105                 /* Channel is protected by a channel key and the client
106                  * didn't specify the correct one */
107                 IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG,
108                                    Client_ID(Client), channame);
109                 return false;
110         }
111
112         if (strchr(channel_modes, 'l') &&
113             (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
114                 /* There are more clints joined to this channel than allowed */
115                 IRC_WriteStrClient(Client, ERR_CHANNELISFULL_MSG,
116                                    Client_ID(Client), channame);
117                 return false;
118         }
119
120         if (strchr(channel_modes, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
121                 /* Only "secure" clients are allowed, but clients doesn't
122                  * use SSL encryption */
123                 IRC_WriteStrClient(Client, ERR_SECURECHANNEL_MSG,
124                                    Client_ID(Client), channame);
125                 return false;
126         }
127
128         if (strchr(channel_modes, 'O') && !Client_OperByMe(Client)) {
129                 /* Only IRC operators are allowed! */
130                 IRC_WriteStrClient(Client, ERR_OPONLYCHANNEL_MSG,
131                                    Client_ID(Client), channame);
132                 return false;
133         }
134
135         return true;
136 }
137
138
139 static void
140 join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
141 {
142         if (flags) {
143                 while (*flags) {
144                         Channel_UserModeAdd(chan, target, *flags);
145                         flags++;
146                 }
147         }
148
149         /* If channel persistent and client is ircop: make client chanop */
150         if (strchr(Channel_Modes(chan), 'P') && strchr(Client_Modes(target), 'o'))
151                 Channel_UserModeAdd(chan, target, 'o');
152 }
153
154
155 static void
156 cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
157 {
158         CONN_ID conn;
159         char str[COMMAND_LEN], *ptr = NULL;
160
161         strlcpy(str, (char *)Data, sizeof(str));
162         conn = Client_Conn(To);
163
164         if (Conn_Options(conn) & CONN_RFC1459) {
165                 /* RFC 1459 compatibility mode, appended modes are NOT
166                  * supported, so strip them off! */
167                 ptr = strchr(str, 0x7);
168                 if (ptr)
169                         *ptr++ = '\0';
170         }
171
172         IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
173         if (ptr && *ptr)
174                 IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
175                                          Client_ID(Prefix));
176 } /* cb_join_forward */
177
178
179 static void
180 join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
181                                         const char *channame)
182 {
183         char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN];
184
185         strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1);
186         if (modes[1])
187                 modes[0] = 0x7;
188         else
189                 modes[0] = '\0';
190
191         /* forward to other servers (if it is not a local channel) */
192         if (!Channel_IsLocal(chan)) {
193                 snprintf(str, sizeof(str), "%s%s", channame, modes);
194                 IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0',
195                                                  cb_join_forward, str);
196         }
197
198         /* tell users in this channel about the new client */
199         IRC_WriteStrChannelPrefix(Client, chan, target, false,
200                                   "JOIN :%s",  channame);
201
202         /* syncronize channel modes */
203         if (modes[1]) {
204                 IRC_WriteStrChannelPrefix(Client, chan, target, false,
205                                           "MODE %s +%s %s", channame,
206                                           &modes[1], Client_ID(target));
207         }
208 } /* join_forward */
209
210
211 static bool
212 join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
213                                         const char *channame)
214 {
215         const char *topic;
216
217         if (Client_Type(Client) != CLIENT_USER)
218                 return true;
219         /* acknowledge join */
220         if (!IRC_WriteStrClientPrefix(Client, target, "JOIN :%s", channame))
221                 return false;
222
223         /* Send topic to client, if any */
224         topic = Channel_Topic(chan);
225         assert(topic != NULL);
226         if (*topic) {
227                 if (!IRC_WriteStrClient(Client, RPL_TOPIC_MSG,
228                         Client_ID(Client), channame, topic))
229                                 return false;
230 #ifndef STRICT_RFC
231                 if (!IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG,
232                         Client_ID(Client), channame,
233                         Channel_TopicWho(chan),
234                         Channel_TopicTime(chan)))
235                                 return false;
236 #endif
237         }
238         /* send list of channel members to client */
239         if (!IRC_Send_NAMES(Client, chan))
240                 return false;
241         return IRC_WriteStrClient(Client, RPL_ENDOFNAMES_MSG, Client_ID(Client), Channel_Name(chan));
242 }
243
244
245 GLOBAL bool
246 IRC_JOIN( CLIENT *Client, REQUEST *Req )
247 {
248         char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL;
249         CLIENT *target;
250         CHANNEL *chan;
251
252         assert( Client != NULL );
253         assert( Req != NULL );
254
255         /* Bad number of arguments? */
256         if (Req->argc < 1 || Req->argc > 2)
257                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
258                                           Client_ID(Client), Req->command);
259
260         /* Who is the sender? */
261         if (Client_Type(Client) == CLIENT_SERVER)
262                 target = Client_Search(Req->prefix);
263         else
264                 target = Client;
265
266         if (!target)
267                 return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix);
268
269         /* Is argument "0"? */
270         if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
271                 return part_from_all_channels(Client, target);
272
273         /* Are channel keys given? */
274         if (Req->argc > 1)
275                 key = strtok_r(Req->argv[1], ",", &lastkey);
276
277         channame = Req->argv[0];
278         channame = strtok_r(channame, ",", &lastchan);
279
280         /* Make sure that "channame" is not the empty string ("JOIN :") */
281         if (! channame)
282                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
283                                           Client_ID(Client), Req->command);
284
285         while (channame) {
286                 flags = NULL;
287
288                 /* Did the server include channel-user-modes? */
289                 if (Client_Type(Client) == CLIENT_SERVER) {
290                         flags = strchr(channame, 0x7);
291                         if (flags) {
292                                 *flags = '\0';
293                                 flags++;
294                         }
295                 }
296
297                 chan = Channel_Search(channame);
298                 if (!chan && Conf_PredefChannelsOnly) {
299                          /* channel must be created, but server does not allow this */
300                         IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame);
301                         break;
302                 }
303
304                 /* Local client? */
305                 if (Client_Type(Client) == CLIENT_USER) {
306                         /* Test if the user has reached the channel limit */
307                         if ((Conf_MaxJoins > 0) &&
308                             (Channel_CountForUser(Client) >= Conf_MaxJoins))
309                                 return IRC_WriteStrClient(Client,
310                                                 ERR_TOOMANYCHANNELS_MSG,
311                                                 Client_ID(Client), channame);
312                         if (chan) {
313                                 /* Already existing channel: check if the
314                                  * client is allowed to join */
315                                 if (!join_allowed(Client, chan, channame, key))
316                                         break;
317                         } else {
318                                 /* New channel: first user will become channel
319                                  * operator unless this is a modeless channel */
320                                 if (*channame != '+')
321                                         flags = "o";
322                         }
323
324                         /* Local client: update idle time */
325                         Conn_UpdateIdle(Client_Conn(Client));
326                 } else {
327                         /* Remote server: we don't need to know whether the
328                          * client is invited or not, but we have to make sure
329                          * that the "one shot" entries (generated by INVITE
330                          * commands) in this list become deleted when a user
331                          * joins a channel this way. */
332                         if (chan)
333                                 (void)Lists_Check(Channel_GetListInvites(chan),
334                                                   target);
335                 }
336
337                 /* Join channel (and create channel if it doesn't exist) */
338                 if (!Channel_Join(target, channame))
339                         break;
340
341                 if (!chan) { /* channel is new; it has been created above */
342                         chan = Channel_Search(channame);
343                         assert(chan != NULL);
344                         if (Channel_IsModeless(chan)) {
345                                 Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
346                                 Channel_ModeAdd(chan, 'n'); /* no external msgs */
347                         }
348                 }
349                 assert(chan != NULL);
350
351                 join_set_channelmodes(chan, target, flags);
352
353                 join_forward(Client, target, chan, channame);
354
355                 if (!join_send_topic(Client, target, chan, channame))
356                         break; /* write error */
357
358                 /* next channel? */
359                 channame = strtok_r(NULL, ",", &lastchan);
360                 if (channame && key)
361                         key = strtok_r(NULL, ",", &lastkey);
362         }
363         return CONNECTED;
364 } /* IRC_JOIN */
365
366
367 /**
368  * Handler for the IRC "PART" command.
369  */
370 GLOBAL bool
371 IRC_PART(CLIENT * Client, REQUEST * Req)
372 {
373         CLIENT *target;
374         char *chan;
375
376         assert(Client != NULL);
377         assert(Req != NULL);
378
379         if (Req->argc < 1 || Req->argc > 2)
380                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
381                                           Client_ID(Client), Req->command);
382
383         /* Get the sender */
384         if (Client_Type(Client) == CLIENT_SERVER)
385                 target = Client_Search(Req->prefix);
386         else
387                 target = Client;
388         if (!target)
389                 return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
390                                           Client_ID(Client), Req->prefix);
391
392         /* Loop over all the given channel names */
393         chan = strtok(Req->argv[0], ",");
394
395         /* Make sure that "chan" is not the empty string ("PART :") */
396         if (! chan)
397                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
398                                           Client_ID(Client), Req->command);
399
400         while (chan) {
401                 Channel_Part(target, Client, chan,
402                              Req->argc > 1 ? Req->argv[1] : Client_ID(target));
403                 chan = strtok(NULL, ",");
404         }
405
406         /* Update idle time, if local client */
407         if (Client_Conn(Client) > NONE)
408                 Conn_UpdateIdle(Client_Conn(Client));
409
410         return CONNECTED;
411 } /* IRC_PART */
412
413
414 GLOBAL bool
415 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
416 {
417         CHANNEL *chan;
418         CLIENT *from;
419         char *topic;
420         bool onchannel, topicok, use_servermode, r;
421
422         assert( Client != NULL );
423         assert( Req != NULL );
424
425         if (Req->argc < 1 || Req->argc > 2)
426                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
427                                           Client_ID(Client), Req->command);
428
429         if (Client_Type(Client) == CLIENT_SERVER)
430                 from = Client_Search(Req->prefix);
431         else
432                 from = Client;
433
434         if (!from)
435                 return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
436                                           Client_ID(Client), Req->prefix);
437
438         chan = Channel_Search(Req->argv[0]);
439         if (!chan)
440                 return IRC_WriteStrClient(from, ERR_NOSUCHCHANNEL_MSG,
441                                           Client_ID(from), Req->argv[0]);
442
443         Channel_CheckAdminRights(chan, Client, from,
444                                  &onchannel, &topicok, &use_servermode);
445
446         if (!onchannel && !topicok)
447                 return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG,
448                                           Client_ID(from), Req->argv[0]);
449
450         if (Req->argc == 1) {
451                 /* Request actual topic */
452                 topic = Channel_Topic(chan);
453                 if (*topic) {
454                         r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
455                                                Client_ID(Client),
456                                                Channel_Name(chan), topic);
457 #ifndef STRICT_RFC
458                         r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
459                                                Client_ID(Client),
460                                                Channel_Name(chan),
461                                                Channel_TopicWho(chan),
462                                                Channel_TopicTime(chan));
463 #endif
464                         return r;
465                 }
466                 else
467                         return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
468                                                   Client_ID(from),
469                                                   Channel_Name(chan));
470         }
471
472         if (strchr(Channel_Modes(chan), 't')) {
473                 /* Topic Lock. Is the user a channel or IRC operator? */
474                 if (!topicok)
475                         return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
476                                                   Client_ID(from),
477                                                   Channel_Name(chan));
478         }
479
480         /* Set new topic */
481         Channel_SetTopic(chan, from, Req->argv[1]);
482         LogDebug("%s \"%s\" set topic on \"%s\": %s",
483                  Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
484                  Req->argv[1][0] ? Req->argv[1] : "<none>");
485
486         if (use_servermode)
487                 from = Client_ThisServer();
488
489         /* Update channel and forward new topic to other servers */
490         if (!Channel_IsLocal(chan))
491                 IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
492                                           Req->argv[0], Req->argv[1]);
493         IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s",
494                                   Req->argv[0], Req->argv[1]);
495
496         if (Client_Type(Client) == CLIENT_USER)
497                 return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
498                                                 Req->argv[0], Req->argv[1]);
499         else
500                 return CONNECTED;
501 } /* IRC_TOPIC */
502
503
504 /**
505  * Handler for the IRC "LIST" command.
506  * This implementation handles the local case as well as the forwarding of the
507  * LIST command to other servers in the IRC network.
508  */
509 GLOBAL bool
510 IRC_LIST( CLIENT *Client, REQUEST *Req )
511 {
512         char *pattern;
513         CHANNEL *chan;
514         CLIENT *from, *target;
515
516         assert( Client != NULL );
517         assert( Req != NULL );
518
519         /* Bad number of prameters? */
520         if( Req->argc > 2 )
521                 return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
522                         Client_ID( Client ), Req->command );
523
524         if( Req->argc > 0 )
525                 pattern = strtok( Req->argv[0], "," );
526         else
527                 pattern = "*";
528
529         /* Get sender from prefix, if any */
530         if( Client_Type( Client ) == CLIENT_SERVER )
531                 from = Client_Search( Req->prefix );
532         else
533                 from = Client;
534
535         if( ! from )
536                 return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG,
537                                 Client_ID( Client ), Req->prefix );
538
539         if( Req->argc == 2 )
540         {
541                 /* Forward to other server? */
542                 target = Client_Search( Req->argv[1] );
543                 if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER ))
544                         return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG,
545                                         Client_ID( Client ), Req->argv[1] );
546
547                 if( target != Client_ThisServer( ))
548                 {
549                         /* Target is indeed an other server, forward it! */
550                         return IRC_WriteStrClientPrefix( target, from,
551                                         "LIST %s :%s", Client_ID( from ),
552                                         Req->argv[1] );
553                 }
554         }
555
556         while( pattern )
557         {
558                 /* Loop through all the channels */
559                 chan = Channel_First( );
560                 while( chan )
561                 {
562                         /* Check search pattern */
563                         if( Match( pattern, Channel_Name( chan )))
564                         {
565                                 /* Gotcha! */
566                                 if( ! strchr( Channel_Modes( chan ), 's' ) ||
567                                     Channel_IsMemberOf( chan, from ))
568                                 {
569                                         if( ! IRC_WriteStrClient( from,
570                                             RPL_LIST_MSG, Client_ID( from ),
571                                             Channel_Name( chan ),
572                                             Channel_MemberCount( chan ),
573                                             Channel_Topic( chan )))
574                                                 return DISCONNECTED;
575                                 }
576                         }
577                         chan = Channel_Next( chan );
578                 }
579
580                 /* Get next name ... */
581                 if( Req->argc > 0 )
582                         pattern = strtok( NULL, "," );
583                 else
584                         pattern = NULL;
585         }
586
587         return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from ));
588 } /* IRC_LIST */
589
590
591 GLOBAL bool
592 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
593 {
594         char modes_add[COMMAND_LEN], l[16], *ptr;
595         CLIENT *from;
596         CHANNEL *chan;
597         int arg_topic;
598
599         assert( Client != NULL );
600         assert( Req != NULL );
601
602         /* Bad number of parameters? */
603         if(( Req->argc < 2 ) || ( Req->argc == 4 ) || ( Req->argc > 5 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
604
605         /* Compatibility kludge */
606         if( Req->argc == 5 ) arg_topic = 4;
607         else if( Req->argc == 3 ) arg_topic = 2;
608         else arg_topic = -1;
609
610         /* Search origin */
611         from = Client_Search( Req->prefix );
612         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
613
614         /* Search or create channel */
615         chan = Channel_Search( Req->argv[0] );
616         if( ! chan ) chan = Channel_Create( Req->argv[0] );
617         if( ! chan ) return CONNECTED;
618
619         if( Req->argv[1][0] == '+' )
620         {
621                 ptr = Channel_Modes( chan );
622                 if( ! *ptr )
623                 {
624                         /* OK, this channel doesn't have modes jet, set the received ones: */
625                         Channel_SetModes( chan, &Req->argv[1][1] );
626
627                         if( Req->argc == 5 )
628                         {
629                                 if( strchr( Channel_Modes( chan ), 'k' )) Channel_SetKey( chan, Req->argv[2] );
630                                 if( strchr( Channel_Modes( chan ), 'l' )) Channel_SetMaxUsers( chan, atol( Req->argv[3] ));
631                         }
632                         else
633                         {
634                                 /* Delete modes which we never want to inherit */
635                                 Channel_ModeDel( chan, 'l' );
636                                 Channel_ModeDel( chan, 'k' );
637                         }
638
639                         strcpy( modes_add, "" );
640                         ptr = Channel_Modes( chan );
641                         while( *ptr )
642                         {
643                                 if( *ptr == 'l' )
644                                 {
645                                         snprintf( l, sizeof( l ), " %lu", Channel_MaxUsers( chan ));
646                                         strlcat( modes_add, l, sizeof( modes_add ));
647                                 }
648                                 if( *ptr == 'k' )
649                                 {
650                                         strlcat( modes_add, " ", sizeof( modes_add ));
651                                         strlcat( modes_add, Channel_Key( chan ), sizeof( modes_add ));
652                                 }
653                                 ptr++;
654                         }
655
656                         /* Inform members of this channel */
657                         IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add );
658                 }
659         }
660         else Log( LOG_WARNING, "CHANINFO: invalid MODE format ignored!" );
661
662         if( arg_topic > 0 )
663         {
664                 /* We got a topic */
665                 ptr = Channel_Topic( chan );
666                 if(( ! *ptr ) && ( Req->argv[arg_topic][0] ))
667                 {
668                         /* OK, there is no topic jet */
669                         Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
670                         IRC_WriteStrChannelPrefix(Client, chan, from, false,
671                              "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
672                 }
673         }
674
675         /* Forward CHANINFO to other serevrs */
676         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] );
677         else if( Req->argc == 3 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
678         else IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s", Req->argv[0], Req->argv[1] );
679
680         return CONNECTED;
681 } /* IRC_CHANINFO */
682
683
684 /* -eof- */