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