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