irc-channel.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2008 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. #include "portab.h"
  14. #include "imp.h"
  15. #include <assert.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include "defines.h"
  20. #include "conn.h"
  21. #include "client.h"
  22. #include "channel.h"
  23. #include "conn-func.h"
  24. #include "lists.h"
  25. #include "log.h"
  26. #include "match.h"
  27. #include "messages.h"
  28. #include "parse.h"
  29. #include "irc-info.h"
  30. #include "irc-write.h"
  31. #include "resolve.h"
  32. #include "conf.h"
  33. #include "exp.h"
  34. #include "irc-channel.h"
  35. /*
  36. * RFC 2812, (3.2.1 Join message Command):
  37. * Note that this message
  38. * accepts a special argument ("0"), which is a special request to leave all
  39. * channels the user is currently a member of. The server will process this
  40. * message as if the user had sent a PART command (See Section 3.2.2) for
  41. * each channel he is a member of.
  42. */
  43. static bool
  44. part_from_all_channels(CLIENT* client, CLIENT *target)
  45. {
  46. CL2CHAN *cl2chan;
  47. CHANNEL *chan;
  48. while ((cl2chan = Channel_FirstChannelOf(target))) {
  49. chan = Channel_GetChannel(cl2chan);
  50. assert( chan != NULL );
  51. Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
  52. }
  53. return CONNECTED;
  54. }
  55. static bool
  56. join_allowed(CLIENT *Client, CLIENT *target, CHANNEL *chan,
  57. const char *channame, const char *key)
  58. {
  59. bool is_invited, is_banned;
  60. const char *channel_modes;
  61. /* Allow IRC operators to overwrite channel limits */
  62. if (strchr(Client_Modes(Client), 'o'))
  63. return true;
  64. is_banned = Lists_Check(Channel_GetListBans(chan), target);
  65. is_invited = Lists_Check(Channel_GetListInvites(chan), target);
  66. if (is_banned && !is_invited) {
  67. IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame);
  68. return false;
  69. }
  70. channel_modes = Channel_Modes(chan);
  71. if ((strchr(channel_modes, 'i')) && !is_invited) {
  72. /* Channel is "invite-only" (and Client wasn't invited) */
  73. IRC_WriteStrClient(Client, ERR_INVITEONLYCHAN_MSG, Client_ID(Client), channame);
  74. return false;
  75. }
  76. /* Is the channel protected by a key? */
  77. if (!Channel_CheckKey(chan, target, key ? key : "")) {
  78. IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG,
  79. Client_ID(Client), channame);
  80. return false;
  81. }
  82. /* Are there already too many members? */
  83. if ((strchr(channel_modes, 'l')) && (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
  84. IRC_WriteStrClient(Client, ERR_CHANNELISFULL_MSG, Client_ID(Client), channame);
  85. return false;
  86. }
  87. return true;
  88. }
  89. static void
  90. join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
  91. {
  92. if (flags) {
  93. while (*flags) {
  94. Channel_UserModeAdd(chan, target, *flags);
  95. flags++;
  96. }
  97. }
  98. /* If channel persistent and client is ircop: make client chanop */
  99. if (strchr(Channel_Modes(chan), 'P') && strchr(Client_Modes(target), 'o'))
  100. Channel_UserModeAdd(chan, target, 'o');
  101. }
  102. static void
  103. cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
  104. {
  105. CONN_ID conn;
  106. char str[COMMAND_LEN], *ptr = NULL;
  107. strlcpy(str, (char *)Data, sizeof(str));
  108. conn = Client_Conn(To);
  109. if (Conn_Options(conn) & CONN_RFC1459) {
  110. /* RFC 1459 compatibility mode, appended modes are NOT
  111. * supported, so strip them off! */
  112. ptr = strchr(str, 0x7);
  113. if (ptr)
  114. *ptr++ = '\0';
  115. }
  116. IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
  117. if (ptr && *ptr)
  118. IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
  119. Client_ID(Prefix));
  120. } /* cb_join_forward */
  121. static void
  122. join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
  123. const char *channame)
  124. {
  125. char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN];
  126. strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1);
  127. if (modes[1])
  128. modes[0] = 0x7;
  129. else
  130. modes[0] = '\0';
  131. /* forward to other servers (if it is not a local channel) */
  132. if (!Channel_IsLocal(chan)) {
  133. snprintf(str, sizeof(str), "%s%s", channame, modes);
  134. IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0',
  135. cb_join_forward, str);
  136. }
  137. /* tell users in this channel about the new client */
  138. IRC_WriteStrChannelPrefix(Client, chan, target, false,
  139. "JOIN :%s", channame);
  140. /* syncronize channel modes */
  141. if (modes[1]) {
  142. IRC_WriteStrChannelPrefix(Client, chan, target, false,
  143. "MODE %s +%s %s", channame,
  144. &modes[1], Client_ID(target));
  145. }
  146. } /* join_forward */
  147. static bool
  148. join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
  149. const char *channame)
  150. {
  151. const char *topic;
  152. if (Client_Type(Client) != CLIENT_USER)
  153. return true;
  154. /* acknowledge join */
  155. if (!IRC_WriteStrClientPrefix(Client, target, "JOIN :%s", channame))
  156. return false;
  157. /* Send topic to client, if any */
  158. topic = Channel_Topic(chan);
  159. assert(topic != NULL);
  160. if (*topic) {
  161. if (!IRC_WriteStrClient(Client, RPL_TOPIC_MSG,
  162. Client_ID(Client), channame, topic))
  163. return false;
  164. #ifndef STRICT_RFC
  165. if (!IRC_WriteStrClient(Client, RPL_TOPICSETBY_MSG,
  166. Client_ID(Client), channame,
  167. Channel_TopicWho(chan),
  168. Channel_TopicTime(chan)))
  169. return false;
  170. #endif
  171. }
  172. /* send list of channel members to client */
  173. if (!IRC_Send_NAMES(Client, chan))
  174. return false;
  175. return IRC_WriteStrClient(Client, RPL_ENDOFNAMES_MSG, Client_ID(Client), Channel_Name(chan));
  176. }
  177. GLOBAL bool
  178. IRC_JOIN( CLIENT *Client, REQUEST *Req )
  179. {
  180. char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL;
  181. CLIENT *target;
  182. CHANNEL *chan;
  183. assert( Client != NULL );
  184. assert( Req != NULL );
  185. /* Bad number of arguments? */
  186. if (Req->argc < 1 || Req->argc > 2)
  187. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  188. Client_ID(Client), Req->command);
  189. /* Who is the sender? */
  190. if (Client_Type(Client) == CLIENT_SERVER)
  191. target = Client_Search(Req->prefix);
  192. else
  193. target = Client;
  194. if (!target)
  195. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix);
  196. /* Is argument "0"? */
  197. if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
  198. return part_from_all_channels(Client, target);
  199. /* Are channel keys given? */
  200. if (Req->argc > 1)
  201. key = strtok_r(Req->argv[1], ",", &lastkey);
  202. channame = Req->argv[0];
  203. channame = strtok_r(channame, ",", &lastchan);
  204. /* Make sure that "channame" is not the empty string ("JOIN :") */
  205. if (! channame)
  206. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  207. Client_ID(Client), Req->command);
  208. while (channame) {
  209. flags = NULL;
  210. /* Did the server include channel-user-modes? */
  211. if (Client_Type(Client) == CLIENT_SERVER) {
  212. flags = strchr(channame, 0x7);
  213. if (flags) {
  214. *flags = '\0';
  215. flags++;
  216. }
  217. }
  218. chan = Channel_Search(channame);
  219. if (!chan && Conf_PredefChannelsOnly) {
  220. /* channel must be created, but server does not allow this */
  221. IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame);
  222. break;
  223. }
  224. /* Local client? */
  225. if (Client_Type(Client) == CLIENT_USER) {
  226. /* Test if the user has reached his maximum channel count */
  227. if ((Conf_MaxJoins > 0) && (Channel_CountForUser(Client) >= Conf_MaxJoins))
  228. return IRC_WriteStrClient(Client, ERR_TOOMANYCHANNELS_MSG,
  229. Client_ID(Client), channame);
  230. if (!chan) {
  231. /*
  232. * New Channel: first user will be channel operator
  233. * unless this is a modeless channel.
  234. */
  235. if (*channame != '+')
  236. flags = "o";
  237. } else
  238. if (!join_allowed(Client, target, chan, channame, key))
  239. break;
  240. /* Local client: update idle time */
  241. Conn_UpdateIdle(Client_Conn(Client));
  242. } else {
  243. /* Remote server: we don't need to know whether the
  244. * client is invited or not, but we have to make sure
  245. * that the "one shot" entries (generated by INVITE
  246. * commands) in this list become deleted when a user
  247. * joins a channel this way. */
  248. if (chan) (void)Lists_Check(Channel_GetListInvites(chan), target);
  249. }
  250. /* Join channel (and create channel if it doesn't exist) */
  251. if (!Channel_Join(target, channame))
  252. break;
  253. if (!chan) { /* channel is new; it has been created above */
  254. chan = Channel_Search(channame);
  255. assert(chan != NULL);
  256. if (Channel_IsModeless(chan)) {
  257. Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */
  258. Channel_ModeAdd(chan, 'n'); /* no external msgs */
  259. }
  260. }
  261. assert(chan != NULL);
  262. join_set_channelmodes(chan, target, flags);
  263. join_forward(Client, target, chan, channame);
  264. if (!join_send_topic(Client, target, chan, channame))
  265. break; /* write error */
  266. /* next channel? */
  267. channame = strtok_r(NULL, ",", &lastchan);
  268. if (channame && key)
  269. key = strtok_r(NULL, ",", &lastkey);
  270. }
  271. return CONNECTED;
  272. } /* IRC_JOIN */
  273. /**
  274. * Handler for the IRC "PART" command.
  275. */
  276. GLOBAL bool
  277. IRC_PART(CLIENT * Client, REQUEST * Req)
  278. {
  279. CLIENT *target;
  280. char *chan;
  281. assert(Client != NULL);
  282. assert(Req != NULL);
  283. if (Req->argc < 1 || Req->argc > 2)
  284. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  285. Client_ID(Client), Req->command);
  286. /* Get the sender */
  287. if (Client_Type(Client) == CLIENT_SERVER)
  288. target = Client_Search(Req->prefix);
  289. else
  290. target = Client;
  291. if (!target)
  292. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  293. Client_ID(Client), Req->prefix);
  294. /* Loop over all the given channel names */
  295. chan = strtok(Req->argv[0], ",");
  296. /* Make sure that "chan" is not the empty string ("PART :") */
  297. if (! chan)
  298. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  299. Client_ID(Client), Req->command);
  300. while (chan) {
  301. Channel_Part(target, Client, chan,
  302. Req->argc > 1 ? Req->argv[1] : Client_ID(target));
  303. chan = strtok(NULL, ",");
  304. }
  305. /* Update idle time, if local client */
  306. if (Client_Conn(Client) > NONE)
  307. Conn_UpdateIdle(Client_Conn(Client));
  308. return CONNECTED;
  309. } /* IRC_PART */
  310. GLOBAL bool
  311. IRC_TOPIC( CLIENT *Client, REQUEST *Req )
  312. {
  313. CHANNEL *chan;
  314. CLIENT *from;
  315. char *topic;
  316. bool r;
  317. assert( Client != NULL );
  318. assert( Req != NULL );
  319. if ((Req->argc < 1) || (Req->argc > 2))
  320. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command);
  321. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  322. else from = Client;
  323. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  324. /* Welcher Channel? */
  325. chan = Channel_Search( Req->argv[0] );
  326. if( ! chan ) return IRC_WriteStrClient( from, ERR_NOSUCHCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
  327. /* Ist der User Mitglied in dem Channel? */
  328. if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( from ), Req->argv[0] );
  329. if( Req->argc == 1 )
  330. {
  331. /* Request actual topic */
  332. topic = Channel_Topic(chan);
  333. if (*topic) {
  334. r = IRC_WriteStrClient(from, RPL_TOPIC_MSG,
  335. Client_ID(Client), Channel_Name(chan), topic);
  336. #ifndef STRICT_RFC
  337. r = IRC_WriteStrClient(from, RPL_TOPICSETBY_MSG,
  338. Client_ID(Client), Channel_Name(chan),
  339. Channel_TopicWho(chan),
  340. Channel_TopicTime(chan));
  341. #endif
  342. return r;
  343. }
  344. else
  345. return IRC_WriteStrClient(from, RPL_NOTOPIC_MSG,
  346. Client_ID(from), Channel_Name(chan));
  347. }
  348. if( strchr( Channel_Modes( chan ), 't' ))
  349. {
  350. /* Topic Lock. Ist der User ein Channel Operator? */
  351. if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan ));
  352. }
  353. /* Set new topic */
  354. Channel_SetTopic(chan, from, Req->argv[1]);
  355. LogDebug("%s \"%s\" set topic on \"%s\": %s",
  356. Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
  357. Req->argv[1][0] ? Req->argv[1] : "<none>");
  358. /* Update channel and forward new topic to other servers */
  359. if (!Channel_IsLocal(chan))
  360. IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s",
  361. Req->argv[0], Req->argv[1]);
  362. IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s",
  363. Req->argv[0], Req->argv[1]);
  364. if (Client_Type(Client) == CLIENT_USER)
  365. return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s",
  366. Req->argv[0], Req->argv[1]);
  367. else
  368. return CONNECTED;
  369. } /* IRC_TOPIC */
  370. /**
  371. * Handler for the IRC "LIST" command.
  372. * This implementation handles the local case as well as the forwarding of the
  373. * LIST command to other servers in the IRC network.
  374. */
  375. GLOBAL bool
  376. IRC_LIST( CLIENT *Client, REQUEST *Req )
  377. {
  378. char *pattern;
  379. CHANNEL *chan;
  380. CLIENT *from, *target;
  381. assert( Client != NULL );
  382. assert( Req != NULL );
  383. /* Bad number of prameters? */
  384. if( Req->argc > 2 )
  385. return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
  386. Client_ID( Client ), Req->command );
  387. if( Req->argc > 0 )
  388. pattern = strtok( Req->argv[0], "," );
  389. else
  390. pattern = "*";
  391. /* Get sender from prefix, if any */
  392. if( Client_Type( Client ) == CLIENT_SERVER )
  393. from = Client_Search( Req->prefix );
  394. else
  395. from = Client;
  396. if( ! from )
  397. return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG,
  398. Client_ID( Client ), Req->prefix );
  399. if( Req->argc == 2 )
  400. {
  401. /* Forward to other server? */
  402. target = Client_Search( Req->argv[1] );
  403. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER ))
  404. return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG,
  405. Client_ID( Client ), Req->argv[1] );
  406. if( target != Client_ThisServer( ))
  407. {
  408. /* Target is indeed an other server, forward it! */
  409. return IRC_WriteStrClientPrefix( target, from,
  410. "LIST %s :%s", Client_ID( from ),
  411. Req->argv[1] );
  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. /* Get next name ... */
  438. if( Req->argc > 0 )
  439. pattern = strtok( NULL, "," );
  440. else
  441. pattern = NULL;
  442. }
  443. return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from ));
  444. } /* IRC_LIST */
  445. GLOBAL bool
  446. IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
  447. {
  448. char modes_add[COMMAND_LEN], l[16], *ptr;
  449. CLIENT *from;
  450. CHANNEL *chan;
  451. int arg_topic;
  452. assert( Client != NULL );
  453. assert( Req != NULL );
  454. /* Bad number of parameters? */
  455. if(( Req->argc < 2 ) || ( Req->argc == 4 ) || ( Req->argc > 5 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  456. /* Compatibility kludge */
  457. if( Req->argc == 5 ) arg_topic = 4;
  458. else if( Req->argc == 3 ) arg_topic = 2;
  459. else arg_topic = -1;
  460. /* Search origin */
  461. from = Client_Search( Req->prefix );
  462. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  463. /* Search or create channel */
  464. chan = Channel_Search( Req->argv[0] );
  465. if( ! chan ) chan = Channel_Create( Req->argv[0] );
  466. if( ! chan ) return CONNECTED;
  467. if( Req->argv[1][0] == '+' )
  468. {
  469. ptr = Channel_Modes( chan );
  470. if( ! *ptr )
  471. {
  472. /* OK, this channel doesn't have modes jet, set the received ones: */
  473. Channel_SetModes( chan, &Req->argv[1][1] );
  474. if( Req->argc == 5 )
  475. {
  476. if( strchr( Channel_Modes( chan ), 'k' )) Channel_SetKey( chan, Req->argv[2] );
  477. if( strchr( Channel_Modes( chan ), 'l' )) Channel_SetMaxUsers( chan, atol( Req->argv[3] ));
  478. }
  479. else
  480. {
  481. /* Delete modes which we never want to inherit */
  482. Channel_ModeDel( chan, 'l' );
  483. Channel_ModeDel( chan, 'k' );
  484. }
  485. strcpy( modes_add, "" );
  486. ptr = Channel_Modes( chan );
  487. while( *ptr )
  488. {
  489. if( *ptr == 'l' )
  490. {
  491. snprintf( l, sizeof( l ), " %lu", Channel_MaxUsers( chan ));
  492. strlcat( modes_add, l, sizeof( modes_add ));
  493. }
  494. if( *ptr == 'k' )
  495. {
  496. strlcat( modes_add, " ", sizeof( modes_add ));
  497. strlcat( modes_add, Channel_Key( chan ), sizeof( modes_add ));
  498. }
  499. ptr++;
  500. }
  501. /* Inform members of this channel */
  502. IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add );
  503. }
  504. }
  505. else Log( LOG_WARNING, "CHANINFO: invalid MODE format ignored!" );
  506. if( arg_topic > 0 )
  507. {
  508. /* We got a topic */
  509. ptr = Channel_Topic( chan );
  510. if(( ! *ptr ) && ( Req->argv[arg_topic][0] ))
  511. {
  512. /* OK, there is no topic jet */
  513. Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
  514. IRC_WriteStrChannelPrefix(Client, chan, from, false,
  515. "TOPIC %s :%s", Req->argv[0], Channel_Topic(chan));
  516. }
  517. }
  518. /* Forward CHANINFO to other serevrs */
  519. 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] );
  520. else if( Req->argc == 3 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
  521. else IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s", Req->argv[0], Req->argv[1] );
  522. return CONNECTED;
  523. } /* IRC_CHANINFO */
  524. /* -eof- */