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