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