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