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