Annotation of ircnowd/src/ngircd/irc-mode.c, Revision 1.1.1.1
1.1 tomglok 1: /*
2: * ngIRCd -- The Next Generation IRC Daemon
3: * Copyright (c)2001-2014 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 commands for mode changes (like MODE, AWAY, etc.)
17: */
18:
19: #include <assert.h>
20: #include <stdio.h>
21: #include <stdlib.h>
22: #include <string.h>
23:
24: #include "conn.h"
25: #include "channel.h"
26: #include "irc-macros.h"
27: #include "irc-write.h"
28: #include "lists.h"
29: #include "log.h"
30: #include "parse.h"
31: #include "messages.h"
32: #include "conf.h"
33:
34: #include "irc-mode.h"
35:
36: static bool Client_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
37: CLIENT *Target));
38: static bool Channel_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
39: CHANNEL *Channel));
40:
41: static bool Add_To_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
42: CHANNEL *Channel, const char *Pattern));
43: static bool Del_From_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
44: CHANNEL *Channel, const char *Pattern));
45:
46: static bool Send_ListChange PARAMS((const bool IsAdd, const char ModeChar,
47: CLIENT *Prefix, CLIENT *Client,
48: CHANNEL *Channel, const char *Mask));
49:
50: /**
51: * Handler for the IRC "MODE" command.
52: *
53: * This function detects whether user or channel modes should be modified
54: * and calls the appropriate sub-functions.
55: *
56: * @param Client The client from which this command has been received.
57: * @param Req Request structure with prefix and all parameters.
58: * @return CONNECTED or DISCONNECTED.
59: */
60: GLOBAL bool
61: IRC_MODE( CLIENT *Client, REQUEST *Req )
62: {
63: CLIENT *cl, *origin;
64: CHANNEL *chan;
65:
66: assert(Client != NULL);
67: assert(Req != NULL);
68:
69: _IRC_GET_SENDER_OR_RETURN_(origin, Req, Client)
70:
71: /* Test for "fake" MODE commands injected by this local instance,
72: * for example when handling the "DefaultUserModes" settings.
73: * This doesn't harm real commands, because prefixes of regular
74: * clients are checked in Validate_Prefix() and can't be faked. */
75: if (Req->prefix && Client_Search(Req->prefix) == Client_ThisServer())
76: Client = Client_Search(Req->prefix);
77:
78: /* Channel or user mode? */
79: cl = NULL; chan = NULL;
80: if (Client_IsValidNick(Req->argv[0]))
81: cl = Client_Search(Req->argv[0]);
82: if (Channel_IsValidName(Req->argv[0]))
83: chan = Channel_Search(Req->argv[0]);
84:
85: if (cl)
86: return Client_Mode(Client, Req, origin, cl);
87: if (chan)
88: return Channel_Mode(Client, Req, origin, chan);
89:
90: /* No target found! */
91: return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
92: Client_ID(Client), Req->argv[0]);
93: } /* IRC_MODE */
94:
95: /**
96: * Check if the "mode limit" for a client has been reached.
97: *
98: * This limit doesn't apply for servers or services!
99: *
100: * @param Client The client to check.
101: * @param Count The number of modes already handled.
102: * @return true if the limit has been reached.
103: */
104: static bool
105: Mode_Limit_Reached(CLIENT *Client, int Count)
106: {
107: if (Client_Type(Client) == CLIENT_SERVER
108: || Client_Type(Client) == CLIENT_SERVICE)
109: return false;
110: if (Count < MAX_HNDL_MODES_ARG)
111: return false;
112: return true;
113: }
114:
115: /**
116: * Handle client mode requests
117: *
118: * @param Client The client from which this command has been received.
119: * @param Req Request structure with prefix and all parameters.
120: * @param Origin The originator of the MODE command (prefix).
121: * @param Target The target (client) of this MODE command.
122: * @return CONNECTED or DISCONNECTED.
123: */
124: static bool
125: Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
126: {
127: char the_modes[COMMAND_LEN], x[2], *mode_ptr;
128: bool ok, set;
129: bool send_RPL_HOSTHIDDEN_MSG = false;
130: int mode_arg;
131: size_t len;
132:
133: /* Is the client allowed to request or change the modes? */
134: if (Client_Type(Client) == CLIENT_USER) {
135: /* Users are only allowed to manipulate their own modes! */
136: if (Target != Client)
137: return IRC_WriteErrClient(Client,
138: ERR_USERSDONTMATCH_MSG,
139: Client_ID(Client));
140: }
141:
142: /* Mode request: let's answer it :-) */
143: if (Req->argc == 1)
144: return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
145: Client_ID(Target),
146: Client_Modes(Target));
147:
148: mode_arg = 1;
149: mode_ptr = Req->argv[mode_arg];
150:
151: /* Initial state: set or unset modes? */
152: if (*mode_ptr == '+') {
153: set = true;
154: strcpy(the_modes, "+");
155: } else if (*mode_ptr == '-') {
156: set = false;
157: strcpy(the_modes, "-");
158: } else
159: return IRC_WriteErrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
160: Client_ID(Origin));
161:
162: x[1] = '\0';
163: ok = CONNECTED;
164: while (mode_ptr) {
165: mode_ptr++;
166: if (!*mode_ptr) {
167: /* Try next argument if there's any */
168: mode_arg++;
169: if (mode_arg < Req->argc)
170: mode_ptr = Req->argv[mode_arg];
171: else
172: break;
173: }
174:
175: switch(*mode_ptr) {
176: case '+':
177: case '-':
178: if ((*mode_ptr == '+' && !set)
179: || (*mode_ptr == '-' && set)) {
180: /* Action modifier ("+"/"-") must be changed */
181: len = strlen(the_modes) - 1;
182: if (the_modes[len] == '+'
183: || the_modes[len] == '-') {
184: /* Last character in the "result
185: * string" was an "action", so just
186: * overwrite it with the new action */
187: the_modes[len] = *mode_ptr;
188: } else {
189: /* Append new modifier character to
190: * the resulting mode string */
191: x[0] = *mode_ptr;
192: strlcat(the_modes, x,
193: sizeof(the_modes));
194: }
195: if (*mode_ptr == '+')
196: set = true;
197: else
198: set = false;
199: }
200: continue;
201: }
202:
203: /* Validate modes */
204: x[0] = '\0';
205: switch (*mode_ptr) {
206: case 'b': /* Block private msgs */
207: case 'C': /* Only messages from clients sharing a channel */
208: case 'i': /* Invisible */
209: case 'I': /* Hide channel list from WHOIS */
210: case 's': /* Server messages */
211: case 'w': /* Wallops messages */
212: x[0] = *mode_ptr;
213: break;
214: case 'a': /* Away */
215: if (Client_Type(Client) == CLIENT_SERVER) {
216: x[0] = 'a';
217: Client_SetAway(Origin, DEFAULT_AWAY_MSG);
218: } else
219: ok = IRC_WriteErrClient(Origin,
220: ERR_NOPRIVILEGES_MSG,
221: Client_ID(Origin));
222: break;
223: case 'B': /* Bot */
224: if (Client_HasMode(Client, 'r'))
225: ok = IRC_WriteErrClient(Origin,
226: ERR_RESTRICTED_MSG,
227: Client_ID(Origin));
228: else
229: x[0] = 'B';
230: break;
231: case 'c': /* Receive connect notices */
232: case 'q': /* KICK-protected user */
233: case 'F': /* disable flood protection */
234: /* (only settable by IRC operators!) */
235: if (!set || Client_Type(Client) == CLIENT_SERVER
236: || Client_HasMode(Origin, 'o'))
237: x[0] = *mode_ptr;
238: else
239: ok = IRC_WriteErrClient(Origin,
240: ERR_NOPRIVILEGES_MSG,
241: Client_ID(Origin));
242: break;
243: case 'o': /* IRC operator (only unsettable!) */
244: if (!set || Client_Type(Client) == CLIENT_SERVER) {
245: x[0] = 'o';
246: } else
247: ok = IRC_WriteErrClient(Origin,
248: ERR_NOPRIVILEGES_MSG,
249: Client_ID(Origin));
250: break;
251: case 'r': /* Restricted (only settable) */
252: if (set || Client_Type(Client) == CLIENT_SERVER)
253: x[0] = 'r';
254: else
255: ok = IRC_WriteErrClient(Origin,
256: ERR_RESTRICTED_MSG,
257: Client_ID(Origin));
258: break;
259: case 'R': /* Registered (not [un]settable by clients) */
260: if (Client_Type(Client) == CLIENT_SERVER)
261: x[0] = 'R';
262: else
263: ok = IRC_WriteErrClient(Origin,
264: ERR_NICKREGISTER_MSG,
265: Client_ID(Origin));
266: break;
267: case 'x': /* Cloak hostname */
268: if (Client_HasMode(Client, 'r'))
269: ok = IRC_WriteErrClient(Origin,
270: ERR_RESTRICTED_MSG,
271: Client_ID(Origin));
272: else if (!set || Conf_CloakHostModeX[0]
273: || Client_Type(Client) == CLIENT_SERVER
274: || Client_HasMode(Origin, 'o')) {
275: x[0] = 'x';
276: send_RPL_HOSTHIDDEN_MSG = true;
277: } else
278: ok = IRC_WriteErrClient(Origin,
279: ERR_NOPRIVILEGES_MSG,
280: Client_ID(Origin));
281: break;
282: default:
283: if (Client_Type(Client) != CLIENT_SERVER) {
284: Log(LOG_DEBUG,
285: "Unknown mode \"%c%c\" from \"%s\"!?",
286: set ? '+' : '-', *mode_ptr,
287: Client_ID(Origin));
288: ok = IRC_WriteErrClient(Origin,
289: ERR_UMODEUNKNOWNFLAG2_MSG,
290: Client_ID(Origin),
291: set ? '+' : '-',
292: *mode_ptr);
293: x[0] = '\0';
294: } else {
295: Log(LOG_DEBUG,
296: "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
297: set ? '+' : '-', *mode_ptr,
298: Client_ID(Origin), Client_ID(Target));
299: x[0] = *mode_ptr;
300: }
301: }
302:
303: if (!ok)
304: break;
305:
306: /* Is there a valid mode change? */
307: if (!x[0])
308: continue;
309:
310: if (set) {
311: if (Client_ModeAdd(Target, x[0]))
312: strlcat(the_modes, x, sizeof(the_modes));
313: } else {
314: if (Client_ModeDel(Target, x[0]))
315: strlcat(the_modes, x, sizeof(the_modes));
316: }
317: }
318:
319: /* Are there changed modes? */
320: if (the_modes[1]) {
321: /* Remove needless action modifier characters */
322: len = strlen(the_modes) - 1;
323: if (the_modes[len] == '+' || the_modes[len] == '-')
324: the_modes[len] = '\0';
325:
326: if (Client_Type(Client) == CLIENT_SERVER) {
327: /* Forward modes to other servers */
328: if (Client_Conn(Target) != NONE) {
329: /* Remote server (service?) changed modes
330: * for one of our clients. Inform it! */
331: IRC_WriteStrClientPrefix(Target, Origin,
332: "MODE %s :%s",
333: Client_ID(Target),
334: the_modes);
335: }
336: IRC_WriteStrServersPrefix(Client, Origin,
337: "MODE %s :%s",
338: Client_ID(Target),
339: the_modes);
340: } else {
341: /* Send reply to client and inform other servers */
342: ok = IRC_WriteStrClientPrefix(Client, Origin,
343: "MODE %s :%s",
344: Client_ID(Target),
345: the_modes);
346: IRC_WriteStrServersPrefix(Client, Origin,
347: "MODE %s :%s",
348: Client_ID(Target),
349: the_modes);
350: }
351:
352: if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
353: /* A new (cloaked) hostname must be announced */
354: IRC_WriteStrClientPrefix(Target, Origin,
355: RPL_HOSTHIDDEN_MSG,
356: Client_ID(Target),
357: Client_HostnameDisplayed(Target));
358:
359: }
360:
361: LogDebug("%s \"%s\": Mode change, now \"%s\".",
362: Client_TypeText(Target), Client_Mask(Target),
363: Client_Modes(Target));
364: }
365:
366: return ok;
367: } /* Client_Mode */
368:
369: /*
370: * Reply to a channel mode request.
371: *
372: * @param Origin The originator of the MODE command (prefix).
373: * @param Channel The channel of which the modes should be sent.
374: * @return CONNECTED or DISCONNECTED.
375: */
376: static bool
377: Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
378: {
379: char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
380: const char *mode_ptr;
381:
382: if (!Channel_IsMemberOf(Channel, Origin)) {
383: /* Not a member: "simple" mode reply */
384: if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
385: Client_ID(Origin), Channel_Name(Channel),
386: Channel_Modes(Channel)))
387: return DISCONNECTED;
388: } else {
389: /* The sender is a member: generate extended reply */
390: strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
391: mode_ptr = the_modes;
392: the_args[0] = '\0';
393:
394: while(*mode_ptr) {
395: switch(*mode_ptr) {
396: case 'l':
397: snprintf(argadd, sizeof(argadd), " %lu",
398: Channel_MaxUsers(Channel));
399: strlcat(the_args, argadd, sizeof(the_args));
400: break;
401: case 'k':
402: strlcat(the_args, " ", sizeof(the_args));
403: strlcat(the_args, Channel_Key(Channel),
404: sizeof(the_args));
405: break;
406: }
407: mode_ptr++;
408: }
409: if (the_args[0])
410: strlcat(the_modes, the_args, sizeof(the_modes));
411:
412: if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
413: Client_ID(Origin), Channel_Name(Channel),
414: the_modes))
415: return DISCONNECTED;
416: }
417:
418: #ifndef STRICT_RFC
419: /* Channel creation time */
420: if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
421: Client_ID(Origin), Channel_Name(Channel),
422: Channel_CreationTime(Channel)))
423: return DISCONNECTED;
424: #endif
425: return CONNECTED;
426: }
427:
428: /**
429: * Handle channel mode and channel-user mode changes
430: *
431: * @param Client The client from which this command has been received.
432: * @param Req Request structure with prefix and all parameters.
433: * @param Origin The originator of the MODE command (prefix).
434: * @param Channel The target channel of this MODE command.
435: * @return CONNECTED or DISCONNECTED.
436: */
437: static bool
438: Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
439: {
440: char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
441: argadd[CLIENT_PASS_LEN], *mode_ptr;
442: bool connected, set, skiponce, retval, use_servermode,
443: is_halfop, is_op, is_admin, is_owner, is_machine, is_oper;
444: int mode_arg, arg_arg, mode_arg_count = 0;
445: CLIENT *client;
446: long l;
447: size_t len;
448:
449: is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false;
450:
451: if (Channel_IsModeless(Channel))
452: return IRC_WriteErrClient(Client, ERR_NOCHANMODES_MSG,
453: Client_ID(Client), Channel_Name(Channel));
454:
455: /* Mode request: let's answer it :-) */
456: if (Req->argc <= 1)
457: return Channel_Mode_Answer_Request(Origin, Channel);
458:
459: /* Check if origin is oper and opers can use mode */
460: use_servermode = Conf_OperServerMode;
461: if(Client_HasMode(Client, 'o') && Conf_OperCanMode) {
462: is_oper = true;
463: }
464:
465: /* Check if client is a server/service */
466: if(Client_Type(Client) == CLIENT_SERVER ||
467: Client_Type(Client) == CLIENT_SERVICE) {
468: is_machine = true;
469: }
470:
471: /* Check if client is member of channel or an oper or an server/service */
472: if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine)
473: return IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG,
474: Client_ID(Origin),
475: Channel_Name(Channel));
476:
477: mode_arg = 1;
478: mode_ptr = Req->argv[mode_arg];
479: if (Req->argc > mode_arg + 1)
480: arg_arg = mode_arg + 1;
481: else
482: arg_arg = -1;
483:
484: /* Initial state: set or unset modes? */
485: skiponce = false;
486: switch (*mode_ptr) {
487: case '-':
488: set = false;
489: break;
490: case '+':
491: set = true;
492: break;
493: default:
494: set = true;
495: skiponce = true;
496: }
497:
498: /* Prepare reply string */
499: strcpy(the_modes, set ? "+" : "-");
500: the_args[0] = '\0';
501:
502: x[1] = '\0';
503: connected = CONNECTED;
504: while (mode_ptr) {
505: if (!skiponce)
506: mode_ptr++;
507: if (!*mode_ptr) {
508: /* Try next argument if there's any */
509: if (arg_arg < 0)
510: break;
511: if (arg_arg > mode_arg)
512: mode_arg = arg_arg;
513: else
514: mode_arg++;
515:
516: if (mode_arg >= Req->argc)
517: break;
518: mode_ptr = Req->argv[mode_arg];
519:
520: if (Req->argc > mode_arg + 1)
521: arg_arg = mode_arg + 1;
522: else
523: arg_arg = -1;
524: }
525: skiponce = false;
526:
527: switch (*mode_ptr) {
528: case '+':
529: case '-':
530: if (((*mode_ptr == '+') && !set)
531: || ((*mode_ptr == '-') && set)) {
532: /* Action modifier ("+"/"-") must be changed ... */
533: len = strlen(the_modes) - 1;
534: if (the_modes[len] == '+' || the_modes[len] == '-') {
535: /* Adjust last action modifier in result */
536: the_modes[len] = *mode_ptr;
537: } else {
538: /* Append modifier character to result string */
539: x[0] = *mode_ptr;
540: strlcat(the_modes, x, sizeof(the_modes));
541: }
542: set = *mode_ptr == '+';
543: }
544: continue;
545: }
546:
547: /* Are there arguments left? */
548: if (arg_arg >= Req->argc)
549: arg_arg = -1;
550:
551: if(!is_machine && !is_oper) {
552: if (Channel_UserHasMode(Channel, Client, 'q'))
553: is_owner = true;
554: if (Channel_UserHasMode(Channel, Client, 'a'))
555: is_admin = true;
556: if (Channel_UserHasMode(Channel, Client, 'o'))
557: is_op = true;
558: if (Channel_UserHasMode(Channel, Client, 'h'))
559: is_halfop = true;
560: }
561:
562: /* Validate modes */
563: x[0] = '\0';
564: argadd[0] = '\0';
565: client = NULL;
566: switch (*mode_ptr) {
567: /* --- Channel modes --- */
568: case 'R': /* Registered users only */
569: case 's': /* Secret channel */
570: case 'z': /* Secure connections only */
571: if(!is_oper && !is_machine && !is_owner &&
572: !is_admin && !is_op) {
573: connected = IRC_WriteErrClient(Origin,
574: ERR_CHANOPRIVSNEEDED_MSG,
575: Client_ID(Origin), Channel_Name(Channel));
576: goto chan_exit;
577: }
578: case 'i': /* Invite only */
579: case 'V': /* Invite disallow */
580: case 'M': /* Only identified nicks can write */
581: case 'm': /* Moderated */
582: case 'n': /* Only members can write */
583: case 'N': /* Can't change nick while on this channel */
584: case 'Q': /* No kicks */
585: case 't': /* Topic locked */
586: if(is_oper || is_machine || is_owner ||
587: is_admin || is_op || is_halfop)
588: x[0] = *mode_ptr;
589: else
590: connected = IRC_WriteErrClient(Origin,
591: ERR_CHANOPRIVSNEEDED_MSG,
592: Client_ID(Origin), Channel_Name(Channel));
593: break;
594: case 'k': /* Channel key */
595: if (Mode_Limit_Reached(Client, mode_arg_count++))
596: goto chan_exit;
597: if (!set) {
598: if (is_oper || is_machine || is_owner ||
599: is_admin || is_op || is_halfop) {
600: x[0] = *mode_ptr;
601: if (Channel_HasMode(Channel, 'k'))
602: strlcpy(argadd, "*", sizeof(argadd));
603: if (arg_arg > mode_arg)
604: arg_arg++;
605: } else
606: connected = IRC_WriteErrClient(Origin,
607: ERR_CHANOPRIVSNEEDED_MSG,
608: Client_ID(Origin),
609: Channel_Name(Channel));
610: break;
611: }
612: if (arg_arg > mode_arg) {
613: if (is_oper || is_machine || is_owner ||
614: is_admin || is_op || is_halfop) {
615: Channel_ModeDel(Channel, 'k');
616: Channel_SetKey(Channel,
617: Req->argv[arg_arg]);
618: strlcpy(argadd, Channel_Key(Channel),
619: sizeof(argadd));
620: x[0] = *mode_ptr;
621: } else {
622: connected = IRC_WriteErrClient(Origin,
623: ERR_CHANOPRIVSNEEDED_MSG,
624: Client_ID(Origin),
625: Channel_Name(Channel));
626: }
627: Req->argv[arg_arg][0] = '\0';
628: arg_arg++;
629: } else {
630: #ifdef STRICT_RFC
631: /* Only send error message in "strict" mode,
632: * this is how ircd2.11 and others behave ... */
633: connected = IRC_WriteErrClient(Origin,
634: ERR_NEEDMOREPARAMS_MSG,
635: Client_ID(Origin), Req->command);
636: #endif
637: goto chan_exit;
638: }
639: break;
640: case 'l': /* Member limit */
641: if (Mode_Limit_Reached(Client, mode_arg_count++))
642: goto chan_exit;
643: if (!set) {
644: if (is_oper || is_machine || is_owner ||
645: is_admin || is_op || is_halfop)
646: x[0] = *mode_ptr;
647: else
648: connected = IRC_WriteErrClient(Origin,
649: ERR_CHANOPRIVSNEEDED_MSG,
650: Client_ID(Origin),
651: Channel_Name(Channel));
652: break;
653: }
654: if (arg_arg > mode_arg) {
655: if (is_oper || is_machine || is_owner ||
656: is_admin || is_op || is_halfop) {
657: l = atol(Req->argv[arg_arg]);
658: if (l > 0 && l < 0xFFFF) {
659: Channel_ModeDel(Channel, 'l');
660: Channel_SetMaxUsers(Channel, l);
661: snprintf(argadd, sizeof(argadd),
662: "%ld", l);
663: x[0] = *mode_ptr;
664: }
665: } else {
666: connected = IRC_WriteErrClient(Origin,
667: ERR_CHANOPRIVSNEEDED_MSG,
668: Client_ID(Origin),
669: Channel_Name(Channel));
670: }
671: Req->argv[arg_arg][0] = '\0';
672: arg_arg++;
673: } else {
674: #ifdef STRICT_RFC
675: /* Only send error message in "strict" mode,
676: * this is how ircd2.11 and others behave ... */
677: connected = IRC_WriteErrClient(Origin,
678: ERR_NEEDMOREPARAMS_MSG,
679: Client_ID(Origin), Req->command);
680: #endif
681: goto chan_exit;
682: }
683: break;
684: case 'O': /* IRC operators only */
685: if (set) {
686: /* Only IRC operators are allowed to
687: * set the 'O' channel mode! */
688: if(is_oper || is_machine)
689: x[0] = 'O';
690: else
691: connected = IRC_WriteErrClient(Origin,
692: ERR_NOPRIVILEGES_MSG,
693: Client_ID(Origin));
694: } else if(is_oper || is_machine || is_owner ||
695: is_admin || is_op)
696: x[0] = 'O';
697: else
698: connected = IRC_WriteErrClient(Origin,
699: ERR_CHANOPRIVSNEEDED_MSG,
700: Client_ID(Origin),
701: Channel_Name(Channel));
702: break;
703: case 'P': /* Persistent channel */
704: if (set) {
705: /* Only IRC operators are allowed to
706: * set the 'P' channel mode! */
707: if(is_oper || is_machine)
708: x[0] = 'P';
709: else
710: connected = IRC_WriteErrClient(Origin,
711: ERR_NOPRIVILEGES_MSG,
712: Client_ID(Origin));
713: } else if(is_oper || is_machine || is_owner ||
714: is_admin || is_op)
715: x[0] = 'P';
716: else
717: connected = IRC_WriteErrClient(Origin,
718: ERR_CHANOPRIVSNEEDED_MSG,
719: Client_ID(Origin),
720: Channel_Name(Channel));
721: break;
722: /* --- Channel user modes --- */
723: case 'q': /* Owner */
724: case 'a': /* Channel admin */
725: if(!is_oper && !is_machine && !is_owner && !is_admin) {
726: connected = IRC_WriteErrClient(Origin,
727: ERR_CHANOPPRIVTOOLOW_MSG,
728: Client_ID(Origin),
729: Channel_Name(Channel));
730: goto chan_exit;
731: }
732: case 'o': /* Channel operator */
733: if(!is_oper && !is_machine && !is_owner &&
734: !is_admin && !is_op) {
735: connected = IRC_WriteErrClient(Origin,
736: ERR_CHANOPRIVSNEEDED_MSG,
737: Client_ID(Origin),
738: Channel_Name(Channel));
739: goto chan_exit;
740: }
741: case 'h': /* Half Op */
742: if(!is_oper && !is_machine && !is_owner &&
743: !is_admin && !is_op) {
744: connected = IRC_WriteErrClient(Origin,
745: ERR_CHANOPRIVSNEEDED_MSG,
746: Client_ID(Origin),
747: Channel_Name(Channel));
748: goto chan_exit;
749: }
750: case 'v': /* Voice */
751: if (arg_arg > mode_arg) {
752: if (is_oper || is_machine || is_owner ||
753: is_admin || is_op || is_halfop) {
754: client = Client_Search(Req->argv[arg_arg]);
755: if (client)
756: x[0] = *mode_ptr;
757: else
758: connected = IRC_WriteErrClient(Origin,
759: ERR_NOSUCHNICK_MSG,
760: Client_ID(Origin),
761: Req->argv[arg_arg]);
762: } else {
763: connected = IRC_WriteErrClient(Origin,
764: ERR_CHANOPRIVSNEEDED_MSG,
765: Client_ID(Origin),
766: Channel_Name(Channel));
767: }
768: Req->argv[arg_arg][0] = '\0';
769: arg_arg++;
770: } else {
771: #ifdef STRICT_RFC
772: /* Report an error to the client, when a user
773: * mode should be changed but no nickname is
774: * given. But don't do it when not in "strict"
775: * mode, because most other servers don't do
776: * it as well and some clients send "wired"
777: * MODE commands like "MODE #chan -ooo nick". */
778: connected = IRC_WriteErrClient(Origin,
779: ERR_NEEDMOREPARAMS_MSG,
780: Client_ID(Origin), Req->command);
781: #endif
782: goto chan_exit;
783: }
784: break;
785: /* --- Channel lists --- */
786: case 'I': /* Invite lists */
787: case 'b': /* Ban lists */
788: case 'e': /* Channel exception lists */
789: if (Mode_Limit_Reached(Client, mode_arg_count++))
790: goto chan_exit;
791: if (arg_arg > mode_arg) {
792: /* modify list */
793: if (is_oper || is_machine || is_owner ||
794: is_admin || is_op || is_halfop) {
795: connected = set
796: ? Add_To_List(*mode_ptr, Origin,
797: Client, Channel,
798: Req->argv[arg_arg])
799: : Del_From_List(*mode_ptr, Origin,
800: Client, Channel,
801: Req->argv[arg_arg]);
802: } else {
803: connected = IRC_WriteErrClient(Origin,
804: ERR_CHANOPRIVSNEEDED_MSG,
805: Client_ID(Origin),
806: Channel_Name(Channel));
807: }
808: Req->argv[arg_arg][0] = '\0';
809: arg_arg++;
810: } else {
811: switch (*mode_ptr) {
812: case 'I':
813: Channel_ShowInvites(Origin, Channel);
814: break;
815: case 'b':
816: Channel_ShowBans(Origin, Channel);
817: break;
818: case 'e':
819: Channel_ShowExcepts(Origin, Channel);
820: break;
821: }
822: }
823: break;
824: default:
825: if (Client_Type(Client) != CLIENT_SERVER) {
826: Log(LOG_DEBUG,
827: "Unknown mode \"%c%c\" from \"%s\" on %s!?",
828: set ? '+' : '-', *mode_ptr,
829: Client_ID(Origin), Channel_Name(Channel));
830: connected = IRC_WriteErrClient(Origin,
831: ERR_UNKNOWNMODE_MSG,
832: Client_ID(Origin), *mode_ptr,
833: Channel_Name(Channel));
834: x[0] = '\0';
835: } else {
836: Log(LOG_DEBUG,
837: "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
838: set ? '+' : '-', *mode_ptr,
839: Client_ID(Origin), Channel_Name(Channel));
840: x[0] = *mode_ptr;
841: }
842: }
843:
844: if (!connected)
845: break;
846:
847: /* Is there a valid mode change? */
848: if (!x[0])
849: continue;
850:
851: /* Validate target client */
852: if (client && (!Channel_IsMemberOf(Channel, client))) {
853: if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
854: Client_ID(Origin),
855: Client_ID(client),
856: Channel_Name(Channel)))
857: break;
858: continue;
859: }
860:
861: if (client) {
862: /* Channel-User-Mode */
863: retval = set
864: ? Channel_UserModeAdd(Channel, client, x[0])
865: : Channel_UserModeDel(Channel, client, x[0]);
866: if (retval) {
867: strlcat(the_args, " ", sizeof(the_args));
868: strlcat(the_args, Client_ID(client),
869: sizeof(the_args));
870: strlcat(the_modes, x, sizeof(the_modes));
871: LogDebug
872: ("User \"%s\": Mode change on %s, now \"%s\"",
873: Client_Mask(client), Channel_Name(Channel),
874: Channel_UserModes(Channel, client));
875: }
876: } else {
877: /* Channel-Mode */
878: retval = set
879: ? Channel_ModeAdd(Channel, x[0])
880: : Channel_ModeDel(Channel, x[0]);
881: if (retval) {
882: strlcat(the_modes, x, sizeof(the_modes));
883: LogDebug("Channel %s: Mode change, now \"%s\".",
884: Channel_Name(Channel),
885: Channel_Modes(Channel));
886: }
887: }
888:
889: /* Are there additional arguments to add? */
890: if (argadd[0]) {
891: strlcat(the_args, " ", sizeof(the_args));
892: strlcat(the_args, argadd, sizeof(the_args));
893: }
894: }
895:
896: chan_exit:
897: /* Are there changed modes? */
898: if (the_modes[1]) {
899: /* Clean up mode string */
900: len = strlen(the_modes) - 1;
901: if ((the_modes[len] == '+') || (the_modes[len] == '-'))
902: the_modes[len] = '\0';
903:
904: if (Client_Type(Client) == CLIENT_SERVER) {
905: /* MODE requests for local channels from other servers
906: * are definitely invalid! */
907: if (Channel_IsLocal(Channel)) {
908: Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
909: return CONNECTED;
910: }
911:
912: /* Forward mode changes to channel users and all the
913: * other remote servers: */
914: IRC_WriteStrServersPrefix(Client, Origin,
915: "MODE %s %s%s", Channel_Name(Channel),
916: the_modes, the_args);
917: IRC_WriteStrChannelPrefix(Client, Channel, Origin,
918: false, "MODE %s %s%s", Channel_Name(Channel),
919: the_modes, the_args);
920: } else {
921: if (use_servermode)
922: Origin = Client_ThisServer();
923: /* Send reply to client and inform other servers and channel users */
924: connected = IRC_WriteStrClientPrefix(Client, Origin,
925: "MODE %s %s%s", Channel_Name(Channel),
926: the_modes, the_args);
927: /* Only forward requests for non-local channels */
928: if (!Channel_IsLocal(Channel))
929: IRC_WriteStrServersPrefix(Client, Origin,
930: "MODE %s %s%s", Channel_Name(Channel),
931: the_modes, the_args);
932: IRC_WriteStrChannelPrefix(Client, Channel, Origin,
933: false, "MODE %s %s%s", Channel_Name(Channel),
934: the_modes, the_args);
935: }
936: }
937:
938: return connected;
939: } /* Channel_Mode */
940:
941: /**
942: * Handler for the IRC "AWAY" command.
943: *
944: * @param Client The client from which this command has been received.
945: * @param Req Request structure with prefix and all parameters.
946: * @return CONNECTED or DISCONNECTED.
947: */
948: GLOBAL bool
949: IRC_AWAY( CLIENT *Client, REQUEST *Req )
950: {
951: assert (Client != NULL);
952: assert (Req != NULL);
953:
954: if (Req->argc == 1 && Req->argv[0][0]) {
955: Client_SetAway(Client, Req->argv[0]);
956: Client_ModeAdd(Client, 'a');
957: IRC_WriteStrServersPrefix(Client, Client, "MODE %s :+a",
958: Client_ID( Client));
959: return IRC_WriteStrClient(Client, RPL_NOWAWAY_MSG,
960: Client_ID( Client));
961: } else {
962: Client_ModeDel(Client, 'a');
963: IRC_WriteStrServersPrefix(Client, Client, "MODE %s :-a",
964: Client_ID( Client));
965: return IRC_WriteStrClient(Client, RPL_UNAWAY_MSG,
966: Client_ID( Client));
967: }
968: } /* IRC_AWAY */
969:
970: /**
971: * Add entries to channel invite, ban and exception lists.
972: *
973: * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
974: * @param Prefix The originator of the command.
975: * @param Client The sender of the command.
976: * @param Channel The channel of which the list should be modified.
977: * @param Pattern The pattern to add to the list.
978: * @return CONNECTED or DISCONNECTED.
979: */
980: static bool
981: Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
982: const char *Pattern)
983: {
984: char mask[MASK_LEN];
985: struct list_head *list = NULL;
986: long int current_count;
987:
988: assert(Client != NULL);
989: assert(Channel != NULL);
990: assert(Pattern != NULL);
991: assert(what == 'I' || what == 'b' || what == 'e');
992:
993: Lists_MakeMask(Pattern, mask, sizeof(mask));
994: current_count = Lists_Count(Channel_GetListInvites(Channel))
995: + Lists_Count(Channel_GetListExcepts(Channel))
996: + Lists_Count(Channel_GetListBans(Channel));
997:
998: switch(what) {
999: case 'I':
1000: list = Channel_GetListInvites(Channel);
1001: break;
1002: case 'b':
1003: list = Channel_GetListBans(Channel);
1004: break;
1005: case 'e':
1006: list = Channel_GetListExcepts(Channel);
1007: break;
1008: }
1009:
1010: if (Lists_CheckDupeMask(list, mask))
1011: return CONNECTED;
1012: if (Client_Type(Client) == CLIENT_USER &&
1013: current_count >= MAX_HNDL_CHANNEL_LISTS)
1014: return IRC_WriteErrClient(Client, ERR_LISTFULL_MSG,
1015: Client_ID(Client),
1016: Channel_Name(Channel), mask,
1017: MAX_HNDL_CHANNEL_LISTS);
1018:
1019: switch (what) {
1020: case 'I':
1021: if (!Channel_AddInvite(Channel, mask, false, Client_ID(Client)))
1022: return CONNECTED;
1023: break;
1024: case 'b':
1025: if (!Channel_AddBan(Channel, mask, Client_ID(Client)))
1026: return CONNECTED;
1027: break;
1028: case 'e':
1029: if (!Channel_AddExcept(Channel, mask, Client_ID(Client)))
1030: return CONNECTED;
1031: break;
1032: }
1033: return Send_ListChange(true, what, Prefix, Client, Channel, mask);
1034: }
1035:
1036: /**
1037: * Delete entries from channel invite, ban and exception lists.
1038: *
1039: * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
1040: * @param Prefix The originator of the command.
1041: * @param Client The sender of the command.
1042: * @param Channel The channel of which the list should be modified.
1043: * @param Pattern The pattern to add to the list.
1044: * @return CONNECTED or DISCONNECTED.
1045: */
1046: static bool
1047: Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
1048: const char *Pattern)
1049: {
1050: char mask[MASK_LEN];
1051: struct list_head *list = NULL;
1052:
1053: assert(Client != NULL);
1054: assert(Channel != NULL);
1055: assert(Pattern != NULL);
1056: assert(what == 'I' || what == 'b' || what == 'e');
1057:
1058: Lists_MakeMask(Pattern, mask, sizeof(mask));
1059:
1060: switch (what) {
1061: case 'I':
1062: list = Channel_GetListInvites(Channel);
1063: break;
1064: case 'b':
1065: list = Channel_GetListBans(Channel);
1066: break;
1067: case 'e':
1068: list = Channel_GetListExcepts(Channel);
1069: break;
1070: }
1071:
1072: if (!Lists_CheckDupeMask(list, mask))
1073: return CONNECTED;
1074: Lists_Del(list, mask);
1075:
1076: return Send_ListChange(false, what, Prefix, Client, Channel, mask);
1077: }
1078:
1079: /**
1080: * Send information about changed channel invite/ban/exception lists to clients.
1081: *
1082: * @param IsAdd true if the list item has been added, false otherwise.
1083: * @param ModeChar The mode to use (e. g. 'b' or 'I')
1084: * @param Prefix The originator of the mode list change.
1085: * @param Client The sender of the command.
1086: * @param Channel The channel of which the list has been modified.
1087: * @param Mask The mask which has been added or removed.
1088: * @return CONNECTED or DISCONNECTED.
1089: */
1090: static bool
1091: Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix,
1092: CLIENT *Client, CHANNEL *Channel, const char *Mask)
1093: {
1094: bool ok = true;
1095:
1096: /* Send confirmation to the client */
1097: if (Client_Type(Client) == CLIENT_USER)
1098: ok = IRC_WriteStrClientPrefix(Client, Prefix, "MODE %s %c%c %s",
1099: Channel_Name(Channel),
1100: IsAdd ? '+' : '-',
1101: ModeChar, Mask);
1102:
1103: /* to other servers */
1104: IRC_WriteStrServersPrefix(Client, Prefix, "MODE %s %c%c %s",
1105: Channel_Name(Channel), IsAdd ? '+' : '-',
1106: ModeChar, Mask);
1107:
1108: /* and local users in channel */
1109: IRC_WriteStrChannelPrefix(Client, Channel, Prefix, false,
1110: "MODE %s %c%c %s", Channel_Name(Channel),
1111: IsAdd ? '+' : '-', ModeChar, Mask );
1112:
1113: return ok;
1114: } /* Send_ListChange */
1115:
1116: /* -eof- */
CVSweb