]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-mode.c
Channel lists: Fix duplicate check and error messages
[ngircd-alex.git] / src / ngircd / irc-mode.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2012 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-write.h"
29 #include "lists.h"
30 #include "log.h"
31 #include "parse.h"
32 #include "messages.h"
33 #include "conf.h"
34
35 #include "exp.h"
36 #include "irc-mode.h"
37
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_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
45         CHANNEL *Channel, const char *Pattern));
46 static bool Del_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
47         CHANNEL *Channel, const char *Pattern));
48
49 static bool Send_ListChange PARAMS((const char *Mode, CLIENT *Prefix,
50         CLIENT *Client, CHANNEL *Channel, const char *Mask));
51
52
53 /**
54  * Handler for the IRC "MODE" command.
55  *
56  * See RFC 2812 section 3.1.5 ("user mode message") and section 3.2.3
57  * ("channel mode message"), and RFC 2811 section 4 ("channel modes").
58  *
59  * @param Client        The client from which this command has been received.
60  * @param Req           Request structure with prefix and all parameters.
61  * @returns             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         /* No parameters? */
73         if (Req->argc < 1)
74                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
75                                           Client_ID(Client), Req->command);
76
77         /* Origin for answers */
78         if (Client_Type(Client) == CLIENT_SERVER) {
79                 origin = Client_Search(Req->prefix);
80                 if (!origin)
81                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
82                                                   Client_ID(Client),
83                                                   Req->prefix);
84         } else
85                 origin = Client;
86
87         /* Channel or user mode? */
88         cl = NULL; chan = NULL;
89         if (Client_IsValidNick(Req->argv[0]))
90                 cl = Client_Search(Req->argv[0]);
91         if (Channel_IsValidName(Req->argv[0]))
92                 chan = Channel_Search(Req->argv[0]);
93
94         if (cl)
95                 return Client_Mode(Client, Req, origin, cl);
96         if (chan)
97                 return Channel_Mode(Client, Req, origin, chan);
98
99         /* No target found! */
100         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
101                         Client_ID(Client), Req->argv[0]);
102 } /* IRC_MODE */
103
104
105 /**
106  * Check if the "mode limit" for a client has been reached.
107  *
108  * This limit doesn't apply for servers or services!
109  *
110  * @param Client The client to check.
111  * @param Count The number of modes already handled.
112  * @return true if the limit has been reached.
113  */
114 static bool
115 Mode_Limit_Reached(CLIENT *Client, int Count)
116 {
117         if (Client_Type(Client) == CLIENT_SERVER
118             || Client_Type(Client) == CLIENT_SERVICE)
119                 return false;
120         if (Count < MAX_HNDL_MODES_ARG)
121                 return false;
122         return true;
123 }
124
125
126 /**
127  * Handle client mode requests
128  *
129  * @param Client        The client from which this command has been received.
130  * @param Req           Request structure with prefix and all parameters.
131  * @param Origin        The originator of the MODE command (prefix).
132  * @param Target        The target (client) of this MODE command.
133  * @returns             CONNECTED or DISCONNECTED.
134  */
135 static bool
136 Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
137 {
138         char the_modes[COMMAND_LEN], x[2], *mode_ptr;
139         bool ok, set;
140         int mode_arg;
141         size_t len;
142
143         /* Is the client allowed to request or change the modes? */
144         if (Client_Type(Client) == CLIENT_USER) {
145                 /* Users are only allowed to manipulate their own modes! */
146                 if (Target != Client)
147                         return IRC_WriteStrClient(Client,
148                                                   ERR_USERSDONTMATCH_MSG,
149                                                   Client_ID(Client));
150         }
151
152         /* Mode request: let's answer it :-) */
153         if (Req->argc == 1)
154                 return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
155                                           Client_ID(Origin),
156                                           Client_Modes(Target));
157
158         mode_arg = 1;
159         mode_ptr = Req->argv[mode_arg];
160
161         /* Initial state: set or unset modes? */
162         if (*mode_ptr == '+') {
163                 set = true;
164                 strcpy(the_modes, "+");
165         } else if (*mode_ptr == '-') {
166                 set = false;
167                 strcpy(the_modes, "-");
168         } else
169                 return IRC_WriteStrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
170                                           Client_ID(Origin));
171
172         x[1] = '\0';
173         ok = CONNECTED;
174         while (mode_ptr) {
175                 mode_ptr++;
176                 if (!*mode_ptr) {
177                         /* Try next argument if there's any */
178                         mode_arg++;
179                         if (mode_arg < Req->argc)
180                                 mode_ptr = Req->argv[mode_arg];
181                         else
182                                 break;
183                 }
184
185                 switch(*mode_ptr) {
186                   case '+':
187                   case '-':
188                         if ((*mode_ptr == '+' && !set)
189                             || (*mode_ptr == '-' && set)) {
190                                 /* Action modifier ("+"/"-") must be changed */
191                                 len = strlen(the_modes) - 1;
192                                 if (the_modes[len] == '+'
193                                     || the_modes[len] == '-') {
194                                         /* Last character in the "result
195                                          * string" was an "action", so just
196                                          * overwrite it with the new action */
197                                         the_modes[len] = *mode_ptr;
198                                 } else {
199                                         /* Append new modifier character to
200                                          * the resulting mode string */
201                                         x[0] = *mode_ptr;
202                                         strlcat(the_modes, x,
203                                                 sizeof(the_modes));
204                                 }
205                                 if (*mode_ptr == '+')
206                                         set = true;
207                                 else
208                                         set = false;
209                         }
210                         continue;
211                 }
212
213                 /* Validate modes */
214                 x[0] = '\0';
215                 switch (*mode_ptr) {
216                 case 'i': /* Invisible */
217                 case 's': /* Server messages */
218                 case 'w': /* Wallops messages */
219                         x[0] = *mode_ptr;
220                         break;
221                 case 'a': /* Away */
222                         if (Client_Type(Client) == CLIENT_SERVER) {
223                                 x[0] = 'a';
224                                 Client_SetAway(Origin, DEFAULT_AWAY_MSG);
225                         } else
226                                 ok = IRC_WriteStrClient(Origin,
227                                                         ERR_NOPRIVILEGES_MSG,
228                                                         Client_ID(Origin));
229                         break;
230                 case 'c': /* Receive connect notices
231                            * (only settable by IRC operators!) */
232                         if (!set || Client_Type(Client) == CLIENT_SERVER
233                             || Client_OperByMe(Origin))
234                                 x[0] = 'c';
235                         else
236                                 ok = IRC_WriteStrClient(Origin,
237                                                         ERR_NOPRIVILEGES_MSG,
238                                                         Client_ID(Origin));
239                         break;
240                 case 'o': /* IRC operator (only unsettable!) */
241                         if (!set || Client_Type(Client) == CLIENT_SERVER) {
242                                 Client_SetOperByMe(Target, false);
243                                 x[0] = 'o';
244                         } else
245                                 ok = IRC_WriteStrClient(Origin,
246                                                         ERR_NOPRIVILEGES_MSG,
247                                                         Client_ID(Origin));
248                         break;
249                 case 'r': /* Restricted (only settable) */
250                         if (set || Client_Type(Client) == CLIENT_SERVER)
251                                 x[0] = 'r';
252                         else
253                                 ok = IRC_WriteStrClient(Origin,
254                                                         ERR_RESTRICTED_MSG,
255                                                         Client_ID(Origin));
256                         break;
257                 case 'x': /* Cloak hostname */
258                         if (Client_HasMode(Client, 'r'))
259                                 ok = IRC_WriteStrClient(Origin,
260                                                         ERR_RESTRICTED_MSG,
261                                                         Client_ID(Origin));
262                         else
263                                 x[0] = 'x';
264                         break;
265                 default:
266                         if (Client_Type(Client) != CLIENT_SERVER) {
267                                 Log(LOG_DEBUG,
268                                     "Unknown mode \"%c%c\" from \"%s\"!?",
269                                     set ? '+' : '-', *mode_ptr,
270                                     Client_ID(Origin));
271                                 ok = IRC_WriteStrClient(Origin,
272                                                         ERR_UMODEUNKNOWNFLAG2_MSG,
273                                                         Client_ID(Origin),
274                                                         set ? '+' : '-',
275                                                         *mode_ptr);
276                                 x[0] = '\0';
277                         } else {
278                                 Log(LOG_DEBUG,
279                                     "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
280                                     set ? '+' : '-', *mode_ptr,
281                                     Client_ID(Origin), Client_ID(Target));
282                                 x[0] = *mode_ptr;
283                         }
284                 }
285
286                 if (!ok)
287                         break;
288
289                 /* Is there a valid mode change? */
290                 if (!x[0])
291                         continue;
292
293                 if (set) {
294                         if (Client_ModeAdd(Target, x[0]))
295                                 strlcat(the_modes, x, sizeof(the_modes));
296                 } else {
297                         if (Client_ModeDel(Target, x[0]))
298                                 strlcat(the_modes, x, sizeof(the_modes));
299                 }
300         }
301
302         /* Are there changed modes? */
303         if (the_modes[1]) {
304                 /* Remove needless action modifier characters */
305                 len = strlen(the_modes) - 1;
306                 if (the_modes[len] == '+' || the_modes[len] == '-')
307                         the_modes[len] = '\0';
308
309                 if (Client_Type(Client) == CLIENT_SERVER) {
310                         /* Forward modes to other servers */
311                         if (Client_Conn(Target) != NONE) {
312                                 /* Remote server (service?) changed modes
313                                  * for one of our clients. Inform it! */
314                                 IRC_WriteStrClientPrefix(Target, Origin,
315                                                          "MODE %s :%s",
316                                                          Client_ID(Target),
317                                                          the_modes);
318                         }
319                         IRC_WriteStrServersPrefix(Client, Origin,
320                                                   "MODE %s :%s",
321                                                   Client_ID(Target),
322                                                   the_modes);
323                 } else {
324                         /* Send reply to client and inform other servers */
325                         ok = IRC_WriteStrClientPrefix(Client, Origin,
326                                                       "MODE %s :%s",
327                                                       Client_ID(Target),
328                                                       the_modes);
329                         IRC_WriteStrServersPrefix(Client, Origin,
330                                                   "MODE %s :%s",
331                                                   Client_ID(Target),
332                                                   the_modes);
333                 }
334                 LogDebug("%s \"%s\": Mode change, now \"%s\".",
335                          Client_TypeText(Target), Client_Mask(Target),
336                          Client_Modes(Target));
337         }
338
339         IRC_SetPenalty(Client, 1);
340         return ok;
341 } /* Client_Mode */
342
343
344 static bool
345 Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
346 {
347         char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
348         const char *mode_ptr;
349
350         /* Member or not? -- That's the question! */
351         if (!Channel_IsMemberOf(Channel, Origin))
352                 return IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
353                         Client_ID(Origin), Channel_Name(Channel), Channel_Modes(Channel));
354
355         /* The sender is a member: generate extended reply */
356         strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
357         mode_ptr = the_modes;
358         the_args[0] = '\0';
359
360         while(*mode_ptr) {
361                 switch(*mode_ptr) {
362                 case 'l':
363                         snprintf(argadd, sizeof(argadd), " %lu", Channel_MaxUsers(Channel));
364                         strlcat(the_args, argadd, sizeof(the_args));
365                         break;
366                 case 'k':
367                         strlcat(the_args, " ", sizeof(the_args));
368                         strlcat(the_args, Channel_Key(Channel), sizeof(the_args));
369                         break;
370                 }
371                 mode_ptr++;
372         }
373         if (the_args[0])
374                 strlcat(the_modes, the_args, sizeof(the_modes));
375
376         if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
377                                 Client_ID(Origin), Channel_Name(Channel),
378                                 the_modes))
379                 return DISCONNECTED;
380 #ifndef STRICT_RFC
381         if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
382                                   Client_ID(Origin), Channel_Name(Channel),
383                                   Channel_CreationTime(Channel)))
384                 return DISCONNECTED;
385 #endif
386         return CONNECTED;
387 }
388
389
390 /**
391  * Handle channel mode and channel-user mode changes
392  */
393 static bool
394 Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
395 {
396         char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
397             argadd[CLIENT_PASS_LEN], *mode_ptr;
398         bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
399         int mode_arg, arg_arg, mode_arg_count = 0;
400         CLIENT *client;
401         long l;
402         size_t len;
403
404         if (Channel_IsModeless(Channel))
405                 return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
406                                 Client_ID(Client), Channel_Name(Channel));
407
408         /* Mode request: let's answer it :-) */
409         if (Req->argc <= 1)
410                 return Channel_Mode_Answer_Request(Origin, Channel);
411
412         Channel_CheckAdminRights(Channel, Client, Origin,
413                                  &onchannel, &modeok, &use_servermode);
414
415         if (!onchannel && !modeok)
416                 return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
417                                           Client_ID(Origin),
418                                           Channel_Name(Channel));
419
420         mode_arg = 1;
421         mode_ptr = Req->argv[mode_arg];
422         if (Req->argc > mode_arg + 1)
423                 arg_arg = mode_arg + 1;
424         else
425                 arg_arg = -1;
426
427         /* Initial state: set or unset modes? */
428         skiponce = false;
429         switch (*mode_ptr) {
430         case '-':
431                 set = false;
432                 break;
433         case '+':
434                 set = true;
435                 break;
436         default:
437                 set = true;
438                 skiponce = true;
439         }
440
441         /* Prepare reply string */
442         strcpy(the_modes, set ? "+" : "-");
443         the_args[0] = '\0';
444
445         x[1] = '\0';
446         connected = CONNECTED;
447         while (mode_ptr) {
448                 if (!skiponce)
449                         mode_ptr++;
450                 if (!*mode_ptr) {
451                         /* Try next argument if there's any */
452                         if (arg_arg < 0)
453                                 break;
454                         if (arg_arg > mode_arg)
455                                 mode_arg = arg_arg;
456                         else
457                                 mode_arg++;
458
459                         if (mode_arg >= Req->argc)
460                                 break;
461                         mode_ptr = Req->argv[mode_arg];
462
463                         if (Req->argc > mode_arg + 1)
464                                 arg_arg = mode_arg + 1;
465                         else
466                                 arg_arg = -1;
467                 }
468                 skiponce = false;
469
470                 switch (*mode_ptr) {
471                 case '+':
472                 case '-':
473                         if (((*mode_ptr == '+') && !set)
474                             || ((*mode_ptr == '-') && set)) {
475                                 /* Action modifier ("+"/"-") must be changed ... */
476                                 len = strlen(the_modes) - 1;
477                                 if (the_modes[len] == '+' || the_modes[len] == '-') {
478                                         /* Adjust last action modifier in result */
479                                         the_modes[len] = *mode_ptr;
480                                 } else {
481                                         /* Append modifier character to result string */
482                                         x[0] = *mode_ptr;
483                                         strlcat(the_modes, x, sizeof(the_modes));
484                                 }
485                                 set = *mode_ptr == '+';
486                         }
487                         continue;
488                 }
489
490                 /* Are there arguments left? */
491                 if (arg_arg >= Req->argc)
492                         arg_arg = -1;
493
494                 /* Validate modes */
495                 x[0] = '\0';
496                 argadd[0] = '\0';
497                 client = NULL;
498                 switch (*mode_ptr) {
499                 /* --- Channel modes --- */
500                 case 'i': /* Invite only */
501                 case 'm': /* Moderated */
502                 case 'n': /* Only members can write */
503                 case 'R': /* Registered users only */
504                 case 's': /* Secret channel */
505                 case 't': /* Topic locked */
506                 case 'z': /* Secure connections only */
507                         if (modeok)
508                                 x[0] = *mode_ptr;
509                         else
510                                 connected = IRC_WriteStrClient(Origin,
511                                         ERR_CHANOPRIVSNEEDED_MSG,
512                                         Client_ID(Origin), Channel_Name(Channel));
513                         break;
514                 case 'k': /* Channel key */
515                         if (Mode_Limit_Reached(Client, mode_arg_count++))
516                                 goto chan_exit;
517                         if (!set) {
518                                 if (modeok)
519                                         x[0] = *mode_ptr;
520                                 else
521                                         connected = IRC_WriteStrClient(Origin,
522                                                 ERR_CHANOPRIVSNEEDED_MSG,
523                                                 Client_ID(Origin),
524                                                 Channel_Name(Channel));
525                                 break;
526                         }
527                         if (arg_arg > mode_arg) {
528                                 if (modeok) {
529                                         Channel_ModeDel(Channel, 'k');
530                                         Channel_SetKey(Channel,
531                                                        Req->argv[arg_arg]);
532                                         strlcpy(argadd, Channel_Key(Channel),
533                                                 sizeof(argadd));
534                                         x[0] = *mode_ptr;
535                                 } else {
536                                         connected = IRC_WriteStrClient(Origin,
537                                                 ERR_CHANOPRIVSNEEDED_MSG,
538                                                 Client_ID(Origin),
539                                                 Channel_Name(Channel));
540                                 }
541                                 Req->argv[arg_arg][0] = '\0';
542                                 arg_arg++;
543                         } else {
544                                 connected = IRC_WriteStrClient(Origin,
545                                         ERR_NEEDMOREPARAMS_MSG,
546                                         Client_ID(Origin), Req->command);
547                                 goto chan_exit;
548                         }
549                         break;
550                 case 'l': /* Member limit */
551                         if (Mode_Limit_Reached(Client, mode_arg_count++))
552                                 goto chan_exit;
553                         if (!set) {
554                                 if (modeok)
555                                         x[0] = *mode_ptr;
556                                 else
557                                         connected = IRC_WriteStrClient(Origin,
558                                                 ERR_CHANOPRIVSNEEDED_MSG,
559                                                 Client_ID(Origin),
560                                                 Channel_Name(Channel));
561                                 break;
562                         }
563                         if (arg_arg > mode_arg) {
564                                 if (modeok) {
565                                         l = atol(Req->argv[arg_arg]);
566                                         if (l > 0 && l < 0xFFFF) {
567                                                 Channel_ModeDel(Channel, 'l');
568                                                 Channel_SetMaxUsers(Channel, l);
569                                                 snprintf(argadd, sizeof(argadd),
570                                                          "%ld", l);
571                                                 x[0] = *mode_ptr;
572                                         }
573                                 } else {
574                                         connected = IRC_WriteStrClient(Origin,
575                                                 ERR_CHANOPRIVSNEEDED_MSG,
576                                                 Client_ID(Origin),
577                                                 Channel_Name(Channel));
578                                 }
579                                 Req->argv[arg_arg][0] = '\0';
580                                 arg_arg++;
581                         } else {
582                                 connected = IRC_WriteStrClient(Origin,
583                                         ERR_NEEDMOREPARAMS_MSG,
584                                         Client_ID(Origin), Req->command);
585                                 goto chan_exit;
586                         }
587                         break;
588                 case 'O': /* IRC operators only */
589                         if (modeok) {
590                                 /* Only IRC operators are allowed to
591                                  * set the 'O' channel mode! */
592                                 if (set && !(Client_OperByMe(Client)
593                                     || Client_Type(Client) == CLIENT_SERVER))
594                                         connected = IRC_WriteStrClient(Origin,
595                                                 ERR_NOPRIVILEGES_MSG,
596                                                 Client_ID(Origin));
597                                 else
598                                         x[0] = 'O';
599                         } else
600                                 connected = IRC_WriteStrClient(Origin,
601                                                 ERR_CHANOPRIVSNEEDED_MSG,
602                                                 Client_ID(Origin),
603                                                 Channel_Name(Channel));
604                                 break;
605                 case 'P': /* Persistent channel */
606                         if (modeok) {
607                                 /* Only IRC operators are allowed to
608                                  * set the 'P' channel mode! */
609                                 if (set && !(Client_OperByMe(Client)
610                                     || Client_Type(Client) == CLIENT_SERVER))
611                                         connected = IRC_WriteStrClient(Origin,
612                                                 ERR_NOPRIVILEGES_MSG,
613                                                 Client_ID(Origin));
614                                 else
615                                         x[0] = 'P';
616                         } else
617                                 connected = IRC_WriteStrClient(Origin,
618                                         ERR_CHANOPRIVSNEEDED_MSG,
619                                         Client_ID(Origin),
620                                         Channel_Name(Channel));
621                         break;
622                 /* --- Channel user modes --- */
623                 case 'a':
624                 case 'h':
625                 case 'q':
626                         if (Client_Type(Client) != CLIENT_SERVER) {
627                                 connected = IRC_WriteStrClient(Origin,
628                                         ERR_CHANOPRIVSNEEDED_MSG,
629                                         Client_ID(Origin),
630                                         Channel_Name(Channel));
631                                 goto chan_exit;
632                         }
633                 case 'o': /* Channel operator */
634                 case 'v': /* Voice */
635                         if (arg_arg > mode_arg) {
636                                 if (modeok) {
637                                         client = Client_Search(Req->argv[arg_arg]);
638                                         if (client)
639                                                 x[0] = *mode_ptr;
640                                         else
641                                                 connected = IRC_WriteStrClient(Client,
642                                                         ERR_NOSUCHNICK_MSG,
643                                                         Client_ID(Client),
644                                                         Req->argv[arg_arg]);
645                                 } else {
646                                         connected = IRC_WriteStrClient(Origin,
647                                                 ERR_CHANOPRIVSNEEDED_MSG,
648                                                 Client_ID(Origin),
649                                                 Channel_Name(Channel));
650                                 }
651                                 Req->argv[arg_arg][0] = '\0';
652                                 arg_arg++;
653                         } else {
654                                 connected = IRC_WriteStrClient(Origin,
655                                         ERR_NEEDMOREPARAMS_MSG,
656                                         Client_ID(Origin), Req->command);
657                                 goto chan_exit;
658                         }
659                         break;
660                 /* --- Channel lists --- */
661                 case 'I': /* Invite lists */
662                 case 'b': /* Ban lists */
663                         if (Mode_Limit_Reached(Client, mode_arg_count++))
664                                 goto chan_exit;
665                         if (arg_arg > mode_arg) {
666                                 /* modify list */
667                                 if (modeok) {
668                                         connected = set
669                                            ? Add_Ban_Invite(*mode_ptr, Origin,
670                                                 Client, Channel,
671                                                 Req->argv[arg_arg])
672                                            : Del_Ban_Invite(*mode_ptr, Origin,
673                                                 Client, Channel,
674                                                 Req->argv[arg_arg]);
675                                 } else {
676                                         connected = IRC_WriteStrClient(Origin,
677                                                 ERR_CHANOPRIVSNEEDED_MSG,
678                                                 Client_ID(Origin),
679                                                 Channel_Name(Channel));
680                                 }
681                                 Req->argv[arg_arg][0] = '\0';
682                                 arg_arg++;
683                         } else {
684                                 if (*mode_ptr == 'I')
685                                         Channel_ShowInvites(Origin, Channel);
686                                 else
687                                         Channel_ShowBans(Origin, Channel);
688                         }
689                         break;
690                 default:
691                         if (Client_Type(Client) != CLIENT_SERVER) {
692                                 Log(LOG_DEBUG,
693                                     "Unknown mode \"%c%c\" from \"%s\" on %s!?",
694                                     set ? '+' : '-', *mode_ptr,
695                                     Client_ID(Origin), Channel_Name(Channel));
696                                 connected = IRC_WriteStrClient(Origin,
697                                         ERR_UNKNOWNMODE_MSG,
698                                         Client_ID(Origin), *mode_ptr,
699                                         Channel_Name(Channel));
700                                 x[0] = '\0';
701                         } else {
702                                 Log(LOG_DEBUG,
703                                     "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
704                                     set ? '+' : '-', *mode_ptr,
705                                     Client_ID(Origin), Channel_Name(Channel));
706                                 x[0] = *mode_ptr;
707                         }
708                 }
709
710                 if (!connected)
711                         break;
712
713                 /* Is there a valid mode change? */
714                 if (!x[0])
715                         continue;
716
717                 /* Validate target client */
718                 if (client && (!Channel_IsMemberOf(Channel, client))) {
719                         if (!IRC_WriteStrClient
720                             (Origin, ERR_USERNOTINCHANNEL_MSG,
721                              Client_ID(Origin), Client_ID(client),
722                              Channel_Name(Channel)))
723                                 break;
724
725                         continue;
726                 }
727
728                 if (client) {
729                         /* Channel-User-Mode */
730                         retval = set
731                                ? Channel_UserModeAdd(Channel, client, x[0])
732                                : Channel_UserModeDel(Channel, client, x[0]);
733                         if (retval) {
734                                 strlcat(the_args, " ", sizeof(the_args));
735                                 strlcat(the_args, Client_ID(client),
736                                         sizeof(the_args));
737                                 strlcat(the_modes, x, sizeof(the_modes));
738                                 LogDebug
739                                     ("User \"%s\": Mode change on %s, now \"%s\"",
740                                      Client_Mask(client), Channel_Name(Channel),
741                                      Channel_UserModes(Channel, client));
742                         }
743                 } else {
744                         /* Channel-Mode */
745                         retval = set
746                                ? Channel_ModeAdd(Channel, x[0])
747                                : Channel_ModeDel(Channel, x[0]);
748                         if (retval) {
749                                 strlcat(the_modes, x, sizeof(the_modes));
750                                 LogDebug("Channel %s: Mode change, now \"%s\".",
751                                          Channel_Name(Channel),
752                                          Channel_Modes(Channel));
753                         }
754                 }
755
756                 /* Are there additional arguments to add? */
757                 if (argadd[0]) {
758                         strlcat(the_args, " ", sizeof(the_args));
759                         strlcat(the_args, argadd, sizeof(the_args));
760                 }
761         }
762
763       chan_exit:
764         /* Are there changed modes? */
765         if (the_modes[1]) {
766                 /* Clean up mode string */
767                 len = strlen(the_modes) - 1;
768                 if ((the_modes[len] == '+') || (the_modes[len] == '-'))
769                         the_modes[len] = '\0';
770
771                 if (Client_Type(Client) == CLIENT_SERVER) {
772                         /* MODE requests for local channels from other servers
773                          * are definitely invalid! */
774                         if (Channel_IsLocal(Channel)) {
775                                 Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
776                                 return CONNECTED;
777                         }
778
779                         /* Forward mode changes to channel users and all the
780                          * other remote servers: */
781                         IRC_WriteStrServersPrefix(Client, Origin,
782                                 "MODE %s %s%s", Channel_Name(Channel),
783                                 the_modes, the_args);
784                         IRC_WriteStrChannelPrefix(Client, Channel, Origin,
785                                 false, "MODE %s %s%s", Channel_Name(Channel),
786                                 the_modes, the_args);
787                 } else {
788                         if (use_servermode)
789                                 Origin = Client_ThisServer();
790                         /* Send reply to client and inform other servers and channel users */
791                         connected = IRC_WriteStrClientPrefix(Client, Origin,
792                                         "MODE %s %s%s", Channel_Name(Channel),
793                                         the_modes, the_args);
794                         /* Only forward requests for non-local channels */
795                         if (!Channel_IsLocal(Channel))
796                                 IRC_WriteStrServersPrefix(Client, Origin,
797                                         "MODE %s %s%s", Channel_Name(Channel),
798                                         the_modes, the_args);
799                         IRC_WriteStrChannelPrefix(Client, Channel, Origin,
800                                 false, "MODE %s %s%s", Channel_Name(Channel),
801                                 the_modes, the_args);
802                 }
803         }
804
805         IRC_SetPenalty(Client, 1);
806         return connected;
807 } /* Channel_Mode */
808
809
810 GLOBAL bool
811 IRC_AWAY( CLIENT *Client, REQUEST *Req )
812 {
813         assert( Client != NULL );
814         assert( Req != NULL );
815
816         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
817
818         if(( Req->argc == 1 ) && (Req->argv[0][0] ))
819         {
820                 Client_SetAway( Client, Req->argv[0] );
821                 Client_ModeAdd( Client, 'a' );
822                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
823                 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
824         }
825         else
826         {
827                 Client_ModeDel( Client, 'a' );
828                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
829                 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
830         }
831 } /* IRC_AWAY */
832
833
834 /**
835  * Add entries to channel ban and invite lists.
836  *
837  * @param what Can be 'I' for invite or 'b' for ban list.
838  * @param Prefix The originator of the command.
839  * @param Client The sender of the command.
840  * @param Channel The channel of which the list should be modified.
841  * @param Pattern The pattern to add to the list.
842  * @return CONNECTED or DISCONNECTED.
843  */
844 static bool
845 Add_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
846                const char *Pattern)
847 {
848         const char *mask;
849
850         assert(Client != NULL);
851         assert(Channel != NULL);
852         assert(Pattern != NULL);
853         assert(what == 'I' || what == 'b');
854
855         mask = Lists_MakeMask(Pattern);
856
857         if (what == 'I') {
858                 if (Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask))
859                         return CONNECTED;
860                 if (!Channel_AddInvite(Channel, mask, false))
861                         return CONNECTED;
862                 return Send_ListChange("+I", Prefix, Client, Channel, mask);
863         } else {
864                 if (Lists_CheckDupeMask(Channel_GetListBans(Channel), mask))
865                         return CONNECTED;
866                 if (!Channel_AddBan(Channel, mask))
867                         return CONNECTED;
868                 return Send_ListChange("+b", Prefix, Client, Channel, mask);
869         }
870 }
871
872
873 /**
874  * Delete entries from channel ban and invite lists.
875  *
876  * @param what Can be 'I' for invite or 'b' for ban list.
877  * @param Prefix The originator of the command.
878  * @param Client The sender of the command.
879  * @param Channel The channel of which the list should be modified.
880  * @param Pattern The pattern to add to the list.
881  * @return CONNECTED or DISCONNECTED.
882  */
883 static bool
884 Del_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
885                const char *Pattern)
886 {
887         const char *mask;
888
889         assert(Client != NULL);
890         assert(Channel != NULL);
891         assert(Pattern != NULL);
892         assert(what == 'I' || what == 'b');
893
894         mask = Lists_MakeMask(Pattern);
895
896         if (what == 'I') {
897                 if (!Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask))
898                         return CONNECTED;
899                 Lists_Del(Channel_GetListInvites(Channel), mask);
900                 return Send_ListChange( "-I", Prefix, Client, Channel, mask );
901         } else {
902                 if (!Lists_CheckDupeMask(Channel_GetListBans(Channel), mask))
903                         return CONNECTED;
904                 Lists_Del(Channel_GetListBans(Channel), mask);
905                 return Send_ListChange( "-b", Prefix, Client, Channel, mask );
906         }
907
908 }
909
910
911 static bool
912 Send_ListChange(const char *Mode, CLIENT *Prefix, CLIENT *Client,
913                 CHANNEL *Channel, const char *Mask)
914 {
915         bool ok;
916
917         if( Client_Type( Client ) == CLIENT_USER )
918         {
919                 /* send confirmation to client */
920                 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
921         }
922         else ok = true;
923
924         /* to other servers */
925         IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
926
927         /* and local users in channel */
928         IRC_WriteStrChannelPrefix( Client, Channel, Prefix, false, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
929         
930         return ok;
931 } /* Send_ListChange */
932
933
934 /* -eof- */