]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-mode.c
Return ERR_UNKNOWNMODE(472) for unknown channel modes
[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  * 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                         if (Client_Type(Client) != CLIENT_SERVER) {
246                                 Log(LOG_DEBUG,
247                                     "Unknown mode \"%c%c\" from \"%s\"!?",
248                                     set ? '+' : '-', *mode_ptr,
249                                     Client_ID(Origin));
250                                 ok = IRC_WriteStrClient(Origin,
251                                                         ERR_UMODEUNKNOWNFLAG2_MSG,
252                                                         Client_ID(Origin),
253                                                         set ? '+' : '-',
254                                                         *mode_ptr);
255                                 x[0] = '\0';
256                         } else {
257                                 Log(LOG_DEBUG,
258                                     "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
259                                     set ? '+' : '-', *mode_ptr,
260                                     Client_ID(Origin), Client_ID(Target));
261                                 x[0] = *mode_ptr;
262                         }
263                 }
264
265                 if (!ok)
266                         break;
267
268                 /* Is there a valid mode change? */
269                 if (!x[0])
270                         continue;
271
272                 if (set) {
273                         if (Client_ModeAdd(Target, x[0]))
274                                 strlcat(the_modes, x, sizeof(the_modes));
275                 } else {
276                         if (Client_ModeDel(Target, x[0]))
277                                 strlcat(the_modes, x, sizeof(the_modes));
278                 }
279         }
280
281         /* Are there changed modes? */
282         if (the_modes[1]) {
283                 /* Remove needless action modifier characters */
284                 len = strlen(the_modes) - 1;
285                 if (the_modes[len] == '+' || the_modes[len] == '-')
286                         the_modes[len] = '\0';
287
288                 if (Client_Type(Client) == CLIENT_SERVER) {
289                         /* Forward modes to other servers */
290                         if (Client_Conn(Target) != NONE) {
291                                 /* Remote server (service?) changed modes
292                                  * for one of our clients. Inform it! */
293                                 IRC_WriteStrClientPrefix(Target, Origin,
294                                                          "MODE %s :%s",
295                                                          Client_ID(Target),
296                                                          the_modes);
297                         }
298                         IRC_WriteStrServersPrefix(Client, Origin,
299                                                   "MODE %s :%s",
300                                                   Client_ID(Target),
301                                                   the_modes);
302                 } else {
303                         /* Send reply to client and inform other servers */
304                         ok = IRC_WriteStrClientPrefix(Client, Origin,
305                                                       "MODE %s :%s",
306                                                       Client_ID(Target),
307                                                       the_modes);
308                         IRC_WriteStrServersPrefix(Client, Origin,
309                                                   "MODE %s :%s",
310                                                   Client_ID(Target),
311                                                   the_modes);
312                 }
313                 LogDebug("%s \"%s\": Mode change, now \"%s\".",
314                          Client_TypeText(Target), Client_Mask(Target),
315                          Client_Modes(Target));
316         }
317
318         IRC_SetPenalty(Client, 1);
319         return ok;
320 } /* Client_Mode */
321
322
323 static bool
324 Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
325 {
326         char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], argadd[CLIENT_PASS_LEN];
327         const char *mode_ptr;
328
329         /* Member or not? -- That's the question! */
330         if (!Channel_IsMemberOf(Channel, Origin))
331                 return IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
332                         Client_ID(Origin), Channel_Name(Channel), Channel_Modes(Channel));
333
334         /* The sender is a member: generate extended reply */
335         strlcpy(the_modes, Channel_Modes(Channel), sizeof(the_modes));
336         mode_ptr = the_modes;
337         the_args[0] = '\0';
338
339         while(*mode_ptr) {
340                 switch(*mode_ptr) {
341                 case 'l':
342                         snprintf(argadd, sizeof(argadd), " %lu", Channel_MaxUsers(Channel));
343                         strlcat(the_args, argadd, sizeof(the_args));
344                         break;
345                 case 'k':
346                         strlcat(the_args, " ", sizeof(the_args));
347                         strlcat(the_args, Channel_Key(Channel), sizeof(the_args));
348                         break;
349                 }
350                 mode_ptr++;
351         }
352         if (the_args[0])
353                 strlcat(the_modes, the_args, sizeof(the_modes));
354
355         if (!IRC_WriteStrClient(Origin, RPL_CHANNELMODEIS_MSG,
356                                 Client_ID(Origin), Channel_Name(Channel),
357                                 the_modes))
358                 return DISCONNECTED;
359 #ifndef STRICT_RFC
360         if (!IRC_WriteStrClient(Origin, RPL_CREATIONTIME_MSG,
361                                   Client_ID(Origin), Channel_Name(Channel),
362                                   Channel_CreationTime(Channel)))
363                 return DISCONNECTED;
364 #endif
365         return CONNECTED;
366 }
367
368
369 /**
370  * Handle channel mode and channel-user mode changes
371  */
372 static bool
373 Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
374 {
375         char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
376             argadd[CLIENT_PASS_LEN], *mode_ptr;
377         bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
378         int mode_arg, arg_arg, mode_arg_count = 0;
379         CLIENT *client;
380         long l;
381         size_t len;
382
383         if (Channel_IsModeless(Channel))
384                 return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
385                                 Client_ID(Client), Channel_Name(Channel));
386
387         /* Mode request: let's answer it :-) */
388         if (Req->argc <= 1)
389                 return Channel_Mode_Answer_Request(Origin, Channel);
390
391         Channel_CheckAdminRights(Channel, Client, Origin,
392                                  &onchannel, &modeok, &use_servermode);
393
394         if (!onchannel && !modeok)
395                 return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
396                                           Client_ID(Origin),
397                                           Channel_Name(Channel));
398
399         mode_arg = 1;
400         mode_ptr = Req->argv[mode_arg];
401         if (Req->argc > mode_arg + 1)
402                 arg_arg = mode_arg + 1;
403         else
404                 arg_arg = -1;
405
406         /* Initial state: set or unset modes? */
407         skiponce = false;
408         switch (*mode_ptr) {
409         case '-':
410                 set = false;
411                 break;
412         case '+':
413                 set = true;
414                 break;
415         default:
416                 set = true;
417                 skiponce = true;
418         }
419
420         /* Prepare reply string */
421         strcpy(the_modes, set ? "+" : "-");
422         the_args[0] = '\0';
423
424         x[1] = '\0';
425         connected = CONNECTED;
426         while (mode_ptr) {
427                 if (!skiponce)
428                         mode_ptr++;
429                 if (!*mode_ptr) {
430                         /* Try next argument if there's any */
431                         if (arg_arg < 0)
432                                 break;
433                         if (arg_arg > mode_arg)
434                                 mode_arg = arg_arg;
435                         else
436                                 mode_arg++;
437
438                         if (mode_arg >= Req->argc)
439                                 break;
440                         mode_ptr = Req->argv[mode_arg];
441
442                         if (Req->argc > mode_arg + 1)
443                                 arg_arg = mode_arg + 1;
444                         else
445                                 arg_arg = -1;
446                 }
447                 skiponce = false;
448
449                 switch (*mode_ptr) {
450                 case '+':
451                 case '-':
452                         if (((*mode_ptr == '+') && !set)
453                             || ((*mode_ptr == '-') && set)) {
454                                 /* Action modifier ("+"/"-") must be changed ... */
455                                 len = strlen(the_modes) - 1;
456                                 if (the_modes[len] == '+' || the_modes[len] == '-') {
457                                         /* Adjust last action modifier in result */
458                                         the_modes[len] = *mode_ptr;
459                                 } else {
460                                         /* Append modifier character to result string */
461                                         x[0] = *mode_ptr;
462                                         strlcat(the_modes, x, sizeof(the_modes));
463                                 }
464                                 set = *mode_ptr == '+';
465                         }
466                         continue;
467                 }
468
469                 /* Are there arguments left? */
470                 if (arg_arg >= Req->argc)
471                         arg_arg = -1;
472
473                 /* Validate modes */
474                 x[0] = '\0';
475                 argadd[0] = '\0';
476                 client = NULL;
477                 switch (*mode_ptr) {
478                 /* --- Channel modes --- */
479                 case 'i': /* Invite only */
480                 case 'm': /* Moderated */
481                 case 'n': /* Only members can write */
482                 case 'R': /* Registered users only */
483                 case 's': /* Secret channel */
484                 case 't': /* Topic locked */
485                 case 'z': /* Secure connections only */
486                         if (modeok)
487                                 x[0] = *mode_ptr;
488                         else
489                                 connected = IRC_WriteStrClient(Origin,
490                                         ERR_CHANOPRIVSNEEDED_MSG,
491                                         Client_ID(Origin), Channel_Name(Channel));
492                         break;
493                 case 'k': /* Channel key */
494                         if (mode_arg_count++ >= MAX_HNDL_MODES_ARG)
495                                 goto chan_exit;
496                         if (!set) {
497                                 if (modeok)
498                                         x[0] = *mode_ptr;
499                                 else
500                                         connected = IRC_WriteStrClient(Origin,
501                                                 ERR_CHANOPRIVSNEEDED_MSG,
502                                                 Client_ID(Origin),
503                                                 Channel_Name(Channel));
504                                 break;
505                         }
506                         if (arg_arg > mode_arg) {
507                                 if (modeok) {
508                                         Channel_ModeDel(Channel, 'k');
509                                         Channel_SetKey(Channel,
510                                                        Req->argv[arg_arg]);
511                                         strlcpy(argadd, Channel_Key(Channel),
512                                                 sizeof(argadd));
513                                         x[0] = *mode_ptr;
514                                 } else {
515                                         connected = IRC_WriteStrClient(Origin,
516                                                 ERR_CHANOPRIVSNEEDED_MSG,
517                                                 Client_ID(Origin),
518                                                 Channel_Name(Channel));
519                                 }
520                                 Req->argv[arg_arg][0] = '\0';
521                                 arg_arg++;
522                         } else {
523                                 connected = IRC_WriteStrClient(Origin,
524                                         ERR_NEEDMOREPARAMS_MSG,
525                                         Client_ID(Origin), Req->command);
526                                 goto chan_exit;
527                         }
528                         break;
529                 case 'l': /* Member limit */
530                         if (mode_arg_count++ >= MAX_HNDL_MODES_ARG)
531                                 goto chan_exit;
532                         if (!set) {
533                                 if (modeok)
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                                 break;
541                         }
542                         if (arg_arg > mode_arg) {
543                                 if (modeok) {
544                                         l = atol(Req->argv[arg_arg]);
545                                         if (l > 0 && l < 0xFFFF) {
546                                                 Channel_ModeDel(Channel, 'l');
547                                                 Channel_SetMaxUsers(Channel, l);
548                                                 snprintf(argadd, sizeof(argadd),
549                                                          "%ld", l);
550                                                 x[0] = *mode_ptr;
551                                         }
552                                 } else {
553                                         connected = IRC_WriteStrClient(Origin,
554                                                 ERR_CHANOPRIVSNEEDED_MSG,
555                                                 Client_ID(Origin),
556                                                 Channel_Name(Channel));
557                                 }
558                                 Req->argv[arg_arg][0] = '\0';
559                                 arg_arg++;
560                         } else {
561                                 connected = IRC_WriteStrClient(Origin,
562                                         ERR_NEEDMOREPARAMS_MSG,
563                                         Client_ID(Origin), Req->command);
564                                 goto chan_exit;
565                         }
566                         break;
567                 case 'O': /* IRC operators only */
568                         if (modeok) {
569                                 /* Only IRC operators are allowed to
570                                  * set the 'O' channel mode! */
571                                 if (set && !(Client_OperByMe(Client)
572                                     || Client_Type(Client) == CLIENT_SERVER))
573                                         connected = IRC_WriteStrClient(Origin,
574                                                 ERR_NOPRIVILEGES_MSG,
575                                                 Client_ID(Origin));
576                                 else
577                                         x[0] = 'O';
578                         } else
579                                 connected = IRC_WriteStrClient(Origin,
580                                                 ERR_CHANOPRIVSNEEDED_MSG,
581                                                 Client_ID(Origin),
582                                                 Channel_Name(Channel));
583                                 break;
584                 case 'P': /* Persistent channel */
585                         if (modeok) {
586                                 /* Only IRC operators are allowed to
587                                  * set the 'P' channel mode! */
588                                 if (set && !(Client_OperByMe(Client)
589                                     || Client_Type(Client) == CLIENT_SERVER))
590                                         connected = IRC_WriteStrClient(Origin,
591                                                 ERR_NOPRIVILEGES_MSG,
592                                                 Client_ID(Origin));
593                                 else
594                                         x[0] = 'P';
595                         } else
596                                 connected = IRC_WriteStrClient(Origin,
597                                         ERR_CHANOPRIVSNEEDED_MSG,
598                                         Client_ID(Origin),
599                                         Channel_Name(Channel));
600                         break;
601                 /* --- Channel user modes --- */
602                 case 'a':
603                 case 'h':
604                 case 'q':
605                         if (Client_Type(Client) != CLIENT_SERVER) {
606                                 connected = IRC_WriteStrClient(Origin,
607                                         ERR_CHANOPRIVSNEEDED_MSG,
608                                         Client_ID(Origin),
609                                         Channel_Name(Channel));
610                                 goto chan_exit;
611                         }
612                 case 'o': /* Channel operator */
613                 case 'v': /* Voice */
614                         if (arg_arg > mode_arg) {
615                                 if (modeok) {
616                                         client = Client_Search(Req->argv[arg_arg]);
617                                         if (client)
618                                                 x[0] = *mode_ptr;
619                                         else
620                                                 connected = IRC_WriteStrClient(Client,
621                                                         ERR_NOSUCHNICK_MSG,
622                                                         Client_ID(Client),
623                                                         Req->argv[arg_arg]);
624                                 } else {
625                                         connected = IRC_WriteStrClient(Origin,
626                                                 ERR_CHANOPRIVSNEEDED_MSG,
627                                                 Client_ID(Origin),
628                                                 Channel_Name(Channel));
629                                 }
630                                 Req->argv[arg_arg][0] = '\0';
631                                 arg_arg++;
632                         } else {
633                                 connected = IRC_WriteStrClient(Origin,
634                                         ERR_NEEDMOREPARAMS_MSG,
635                                         Client_ID(Origin), Req->command);
636                                 goto chan_exit;
637                         }
638                         break;
639                 /* --- Channel lists --- */
640                 case 'I': /* Invite lists */
641                 case 'b': /* Ban lists */
642                         if (mode_arg_count++ >= MAX_HNDL_MODES_ARG)
643                                 goto chan_exit;
644                         if (arg_arg > mode_arg) {
645                                 /* modify list */
646                                 if (modeok) {
647                                         connected = set
648                                            ? Add_Ban_Invite(*mode_ptr, Origin,
649                                                 Client, Channel,
650                                                 Req->argv[arg_arg])
651                                            : Del_Ban_Invite(*mode_ptr, Origin,
652                                                 Client, Channel,
653                                                 Req->argv[arg_arg]);
654                                 } else {
655                                         connected = IRC_WriteStrClient(Origin,
656                                                 ERR_CHANOPRIVSNEEDED_MSG,
657                                                 Client_ID(Origin),
658                                                 Channel_Name(Channel));
659                                 }
660                                 Req->argv[arg_arg][0] = '\0';
661                                 arg_arg++;
662                         } else {
663                                 if (*mode_ptr == 'I')
664                                         Channel_ShowInvites(Origin, Channel);
665                                 else
666                                         Channel_ShowBans(Origin, Channel);
667                         }
668                         break;
669                 default:
670                         if (Client_Type(Client) != CLIENT_SERVER) {
671                                 Log(LOG_DEBUG,
672                                     "Unknown mode \"%c%c\" from \"%s\" on %s!?",
673                                     set ? '+' : '-', *mode_ptr,
674                                     Client_ID(Origin), Channel_Name(Channel));
675                                 connected = IRC_WriteStrClient(Origin,
676                                         ERR_UNKNOWNMODE_MSG,
677                                         Client_ID(Origin), *mode_ptr,
678                                         Channel_Name(Channel));
679                                 x[0] = '\0';
680                         } else {
681                                 Log(LOG_DEBUG,
682                                     "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
683                                     set ? '+' : '-', *mode_ptr,
684                                     Client_ID(Origin), Channel_Name(Channel));
685                                 x[0] = *mode_ptr;
686                         }
687                 }
688
689                 if (!connected)
690                         break;
691
692                 /* Is there a valid mode change? */
693                 if (!x[0])
694                         continue;
695
696                 /* Validate target client */
697                 if (client && (!Channel_IsMemberOf(Channel, client))) {
698                         if (!IRC_WriteStrClient
699                             (Origin, ERR_USERNOTINCHANNEL_MSG,
700                              Client_ID(Origin), Client_ID(client),
701                              Channel_Name(Channel)))
702                                 break;
703
704                         continue;
705                 }
706
707                 if (client) {
708                         /* Channel-User-Mode */
709                         retval = set
710                                ? Channel_UserModeAdd(Channel, client, x[0])
711                                : Channel_UserModeDel(Channel, client, x[0]);
712                         if (retval) {
713                                 strlcat(the_args, " ", sizeof(the_args));
714                                 strlcat(the_args, Client_ID(client),
715                                         sizeof(the_args));
716                                 strlcat(the_modes, x, sizeof(the_modes));
717                                 LogDebug
718                                     ("User \"%s\": Mode change on %s, now \"%s\"",
719                                      Client_Mask(client), Channel_Name(Channel),
720                                      Channel_UserModes(Channel, client));
721                         }
722                 } else {
723                         /* Channel-Mode */
724                         retval = set
725                                ? Channel_ModeAdd(Channel, x[0])
726                                : Channel_ModeDel(Channel, x[0]);
727                         if (retval) {
728                                 strlcat(the_modes, x, sizeof(the_modes));
729                                 LogDebug("Channel %s: Mode change, now \"%s\".",
730                                          Channel_Name(Channel),
731                                          Channel_Modes(Channel));
732                         }
733                 }
734
735                 /* Are there additional arguments to add? */
736                 if (argadd[0]) {
737                         strlcat(the_args, " ", sizeof(the_args));
738                         strlcat(the_args, argadd, sizeof(the_args));
739                 }
740         }
741
742       chan_exit:
743         /* Are there changed modes? */
744         if (the_modes[1]) {
745                 /* Clean up mode string */
746                 len = strlen(the_modes) - 1;
747                 if ((the_modes[len] == '+') || (the_modes[len] == '-'))
748                         the_modes[len] = '\0';
749
750                 if (Client_Type(Client) == CLIENT_SERVER) {
751                         /* MODE requests for local channels from other servers
752                          * are definitely invalid! */
753                         if (Channel_IsLocal(Channel)) {
754                                 Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored.");
755                                 return CONNECTED;
756                         }
757
758                         /* Forward mode changes to channel users and all the
759                          * other remote servers: */
760                         IRC_WriteStrServersPrefix(Client, Origin,
761                                 "MODE %s %s%s", Channel_Name(Channel),
762                                 the_modes, the_args);
763                         IRC_WriteStrChannelPrefix(Client, Channel, Origin,
764                                 false, "MODE %s %s%s", Channel_Name(Channel),
765                                 the_modes, the_args);
766                 } else {
767                         if (use_servermode)
768                                 Origin = Client_ThisServer();
769                         /* Send reply to client and inform other servers and channel users */
770                         connected = IRC_WriteStrClientPrefix(Client, Origin,
771                                         "MODE %s %s%s", Channel_Name(Channel),
772                                         the_modes, the_args);
773                         /* Only forward requests for non-local channels */
774                         if (!Channel_IsLocal(Channel))
775                                 IRC_WriteStrServersPrefix(Client, Origin,
776                                         "MODE %s %s%s", Channel_Name(Channel),
777                                         the_modes, the_args);
778                         IRC_WriteStrChannelPrefix(Client, Channel, Origin,
779                                 false, "MODE %s %s%s", Channel_Name(Channel),
780                                 the_modes, the_args);
781                 }
782         }
783
784         IRC_SetPenalty(Client, 1);
785         return connected;
786 } /* Channel_Mode */
787
788
789 GLOBAL bool
790 IRC_AWAY( CLIENT *Client, REQUEST *Req )
791 {
792         assert( Client != NULL );
793         assert( Req != NULL );
794
795         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
796
797         if(( Req->argc == 1 ) && (Req->argv[0][0] ))
798         {
799                 Client_SetAway( Client, Req->argv[0] );
800                 Client_ModeAdd( Client, 'a' );
801                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
802                 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
803         }
804         else
805         {
806                 Client_ModeDel( Client, 'a' );
807                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
808                 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
809         }
810 } /* IRC_AWAY */
811
812
813 static bool
814 Add_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
815 {
816         const char *mask;
817         bool already;
818         bool ret;
819
820         assert( Client != NULL );
821         assert( Channel != NULL );
822         assert( Pattern != NULL );
823         assert(what == 'I' || what == 'b');
824
825         mask = Lists_MakeMask(Pattern);
826
827         already = Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask);
828         if (!already) {
829                 if (what == 'I')
830                         ret = Channel_AddInvite(Channel, mask, false);
831                 else
832                         ret = Channel_AddBan(Channel, mask);
833                 if (!ret)
834                         return CONNECTED;
835         }
836         if (already && (Client_Type(Prefix) == CLIENT_SERVER))
837                 return CONNECTED;
838
839         if (what == 'I')
840                 return Send_ListChange("+I", Prefix, Client, Channel, mask);
841         return Send_ListChange("+b", Prefix, Client, Channel, mask);
842 }
843
844
845 static bool
846 Del_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
847 {
848         const char *mask;
849         struct list_head *list;
850
851         assert( Client != NULL );
852         assert( Channel != NULL );
853         assert( Pattern != NULL );
854         assert(what == 'I' || what == 'b');
855
856         mask = Lists_MakeMask( Pattern );
857
858         if (what == 'I')
859                 list = Channel_GetListInvites(Channel);
860         else
861                 list = Channel_GetListBans(Channel);
862
863         Lists_Del(list, mask);
864         if (what == 'I')
865                 return Send_ListChange( "-I", Prefix, Client, Channel, mask );
866         return Send_ListChange( "-b", Prefix, Client, Channel, mask );
867 }
868
869
870 static bool
871 Send_ListChange(const char *Mode, CLIENT *Prefix, CLIENT *Client,
872                 CHANNEL *Channel, const char *Mask)
873 {
874         bool ok;
875
876         if( Client_Type( Client ) == CLIENT_USER )
877         {
878                 /* send confirmation to client */
879                 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
880         }
881         else ok = true;
882
883         /* to other servers */
884         IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
885
886         /* and local users in channel */
887         IRC_WriteStrChannelPrefix( Client, Channel, Prefix, false, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
888         
889         return ok;
890 } /* Send_ListChange */
891
892
893 /* -eof- */