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