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