95b84f3eaab9aeb9e285224956241e74b8aa1fab
[ngircd-alex.git] / src / ngircd / irc-mode.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
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  * IRC commands for mode changes (MODE, AWAY, ...)
12  */
13
14
15 #include "portab.h"
16
17 static char UNUSED id[] = "$Id: irc-mode.c,v 1.17 2002/12/12 12:24:18 alex Exp $";
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <string.h>
22
23 #include "conn.h"
24 #include "client.h"
25 #include "channel.h"
26 #include "defines.h"
27 #include "irc-write.h"
28 #include "lists.h"
29 #include "log.h"
30 #include "parse.h"
31 #include "messages.h"
32 #include "resolve.h"
33 #include "conf.h"
34
35 #include "exp.h"
36 #include "irc-mode.h"
37
38
39 LOCAL BOOLEAN Add_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
40 LOCAL BOOLEAN Add_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
41
42 LOCAL BOOLEAN Del_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
43 LOCAL BOOLEAN Del_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
44
45 LOCAL BOOLEAN Send_ListChange PARAMS(( CHAR *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Mask ));
46
47
48 GLOBAL BOOLEAN
49 IRC_MODE( CLIENT *Client, REQUEST *Req )
50 {
51         CHAR *mode_ptr, the_modes[CLIENT_MODE_LEN], x[2];
52         CLIENT *cl, *chan_cl, *prefix;
53         BOOLEAN set, ok, modeok;
54         CHANNEL *chan;
55         
56         assert( Client != NULL );
57         assert( Req != NULL );
58
59         cl = chan_cl = prefix = NULL;
60         chan = NULL;
61
62         /* Keine Parameter? */
63         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
64
65         /* Ziel suchen: Client bzw. Channel */
66         if( Client_IsValidNick( Req->argv[0] )) cl = Client_Search( Req->argv[0] );
67         if( Channel_IsValidName( Req->argv[0] )) chan = Channel_Search( Req->argv[0] );
68
69         /* Kein Ziel gefunden? */
70         if(( ! cl ) && ( ! chan )) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] );
71
72         assert(( cl && chan ) != TRUE );
73
74         /* Falsche Anzahl Parameter? */
75         if(( cl ) && ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
76         if(( chan ) && ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
77
78         /* Prefix fuer Antworten etc. ermitteln */
79         if( Client_Type( Client ) == CLIENT_SERVER )
80         {
81                 prefix = Client_Search( Req->prefix );
82                 if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
83         }
84         else prefix = Client;
85         
86         if(( chan ) && (( Req->argc == 2 ) || ( Req->argc == 3 )))
87         {
88                 /* pruefen, ob "Listen-Operation": Invite, Ban */
89                 if(( Req->argv[1][0] == '-'  ) || ( Req->argv[1][0] == '+' )) mode_ptr = &Req->argv[1][1];
90                 else mode_ptr = &Req->argv[1][0];
91
92                 if( Req->argc == 2 )
93                 {
94                         /* Liste anzeigen */
95                         if( *mode_ptr == 'I' ) return Lists_ShowInvites( prefix, chan );
96                         if( *mode_ptr == 'b' ) return Lists_ShowBans( prefix, chan );
97                 }
98                 else
99                 {
100                         /* Listen veraendern */
101
102                         if( Client_Type( Client ) == CLIENT_USER )
103                         {
104                                 /* Ist der User Channel-Operator? */
105                                 modeok = FALSE;
106                                 if( strchr( Channel_UserModes( chan, Client ), 'o' )) modeok = TRUE;
107                                 if( Conf_OperCanMode )
108                                 {
109                                         /* auch IRC-Operatoren duerfen MODE verwenden */
110                                         if( Client_OperByMe( Client )) modeok = TRUE;
111                                 }
112
113                                 if( ! modeok )
114                                 {
115                                         Log( LOG_DEBUG, "Can't change modes: \"%s\" is not operator on %s!", Client_ID( Client ), Channel_Name( chan ));
116                                         return IRC_WriteStrClient( Client, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Client ), Channel_Name( chan ));
117                                 }
118                         }
119                         
120                         if( Req->argv[1][0] == '+' )
121                         {
122                                 /* Listen-Eintrag hinzufuegen */
123                                 if( *mode_ptr == 'I' ) return Add_Invite( prefix, Client, chan, Req->argv[2] );
124                                 if( *mode_ptr == 'b' ) return Add_Ban( prefix, Client, chan, Req->argv[2] );
125                         }
126                         else if( Req->argv[1][0] == '-' )
127                         {
128                                 /* Listen-Eintrag loeschen */
129                                 if( *mode_ptr == 'I' ) return Del_Invite( prefix, Client, chan, Req->argv[2] );
130                                 if( *mode_ptr == 'b' ) return Del_Ban( prefix, Client, chan, Req->argv[2] );
131                         }
132                 }
133         }
134
135         /* Client ermitteln, wenn bei Channel-Modes mit 3 Parametern */
136         if(( chan ) && (Req->argc == 3 ))
137         {
138                 chan_cl = Client_Search( Req->argv[2] );
139                 if( ! chan_cl ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] );
140         }
141         
142         /* Wenn Anfragender ein User ist: Zugriff erlaubt? */
143         if( Client_Type( Client ) == CLIENT_USER )
144         {
145                 if( cl )
146                 {
147                         /* MODE ist nur fuer sich selber zulaessig! */
148                         if( cl != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client ));
149                 }
150                 if( chan )
151                 {
152                         /* Darf der User die Channel-Modes ermitteln? */
153                 }
154         }
155
156         /* Werden die Modes "nur" erfragt? */
157         if(( cl ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_UMODEIS_MSG, Client_ID( Client ), Client_Modes( cl ));
158         if(( chan ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_CHANNELMODEIS_MSG, Client_ID( Client ), Channel_Name( chan ), Channel_Modes( chan ));
159
160         mode_ptr = Req->argv[1];
161
162         /* Sollen Modes gesetzt oder geloescht werden? */
163         if( cl )
164         {
165                 if( *mode_ptr == '+' ) set = TRUE;
166                 else if( *mode_ptr == '-' ) set = FALSE;
167                 else return IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client ));
168                 mode_ptr++;
169         }
170         else
171         {
172                 if( *mode_ptr == '-' ) set = FALSE;
173                 else set = TRUE;
174                 if(( *mode_ptr == '-' ) || ( *mode_ptr == '+' )) mode_ptr++;
175         }
176
177         /* Reply-String mit Aenderungen vorbereiten */
178         if( set ) strcpy( the_modes, "+" );
179         else strcpy( the_modes, "-" );
180
181         ok = TRUE;
182         x[1] = '\0';
183         while( *mode_ptr )
184         {
185                 x[0] = '\0';
186                 if( Client_Type( Client ) == CLIENT_SERVER )
187                 {
188                         /* Befehl kommt von einem Server, daher
189                          * trauen wir ihm "unbesehen" ... */
190                         x[0] = *mode_ptr;
191                 }
192                 else
193                 {
194                         /* Modes validieren */
195                         if( cl )
196                         {
197                                 /* User-Modes */
198                                 switch( *mode_ptr )
199                                 {
200                                         case 'i':
201                                                 /* invisible */
202                                                 x[0] = 'i';
203                                                 break;
204                                         case 'o':
205                                                 /* operator (kann nur geloescht werden) */
206                                                 if( ! set )
207                                                 {
208                                                         Client_SetOperByMe( Client, FALSE );
209                                                         x[0] = 'o';
210                                                 }
211                                                 else ok = IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG, Client_ID( Client ));
212                                                 break;
213                                         case 'r':
214                                                 /* restricted (kann nur gesetzt werden) */
215                                                 if( set ) x[0] = 'r';
216                                                 else ok = IRC_WriteStrClient( Client, ERR_RESTRICTED_MSG, Client_ID( Client ));
217                                                 break;
218                                         case 's':
219                                                 /* server messages */
220                                                 x[0] = 's';
221                                                 break;
222                                         default:
223                                                 Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ));
224                                                 ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
225                                                 x[0] = '\0';
226                                 }
227                         }
228                         if( chan )
229                         {
230                                 /* Ist der User ein Channel Operator? */
231                                 modeok = FALSE;
232                                 if( strchr( Channel_UserModes( chan, Client ), 'o' )) modeok = TRUE;
233                                 if( Conf_OperCanMode )
234                                 {
235                                         /* auch IRC-Operatoren duerfen MODE verwenden */
236                                         if( Client_OperByMe( Client )) modeok = TRUE;
237                                 }
238
239                                 if( ! modeok )
240                                 {
241                                         Log( LOG_DEBUG, "Can't change modes: \"%s\" is not operator on %s!", Client_ID( Client ), Channel_Name( chan ));
242                                         ok = IRC_WriteStrClient( Client, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Client ), Channel_Name( chan ));
243                                         break;
244                                 }
245                                 
246                                 /* Channel-Modes oder Channel-User-Modes */
247                                 if( chan_cl )
248                                 {
249                                         /* Channel-User-Modes */
250                                         switch( *mode_ptr )
251                                         {
252                                                 case 'o':
253                                                         /* Channel Operator */
254                                                         x[0] = 'o';
255                                                         break;
256                                                 case 'v':
257                                                         /* Voice */
258                                                         x[0] = 'v';
259                                                         break;
260                                                 default:
261                                                         Log( LOG_DEBUG, "Unknown channel-user-mode \"%c%c\" from \"%s\" on \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Client_ID( chan_cl ), Channel_Name( chan ));
262                                                         ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
263                                                         x[0] = '\0';
264                                         }
265                                 }
266                                 else
267                                 {
268                                         /* Channel-Modes */
269                                         switch( *mode_ptr )
270                                         {
271                                                 case 'i':
272                                                         /* Invite-Only */
273                                                         x[0] = 'i';
274                                                         break;
275                                                 case 'm':
276                                                         /* Moderated */
277                                                         x[0] = 'm';
278                                                         break;
279                                                 case 'n':
280                                                         /* kein Schreiben in den Channel von aussen */
281                                                         x[0] = 'n';
282                                                         break;
283                                                 case 't':
284                                                         /* Topic Lock */
285                                                         x[0] = 't';
286                                                         break;
287                                                 case 'P':
288                                                         /* Persistent. Dieses Flag darf nur von IRC-Operatoren gesetzt werden! */
289                                                         if( set && ( ! Client_OperByMe( Client )))
290                                                         {
291                                                                 ok = IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG, Client_ID( Client ));
292                                                         }
293                                                         else x[0] = 'P';
294                                                         break;
295                                                 default:
296                                                         Log( LOG_DEBUG, "Unknown channel-mode \"%c%c\" from \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Channel_Name( chan ));
297                                                         ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
298                                                         x[0] = '\0';
299                                         }
300                                 }
301                         }
302                 }
303                 if( ! ok ) break;
304                 
305                 mode_ptr++;
306                 if( ! x[0] ) continue;
307
308                 /* Okay, gueltigen Mode gefunden */
309                 if( cl )
310                 {
311                         /* Es geht um User-Modes */
312                         if( set )
313                         {
314                                 /* Mode setzen. Wenn der Client ihn noch nicht hatte: merken */
315                                 if( Client_ModeAdd( cl, x[0] )) strcat( the_modes, x );
316                                 
317                         }
318                         else
319                         {
320                                 /* Modes geloescht. Wenn der Client ihn hatte: merken */
321                                 if( Client_ModeDel( cl, x[0] )) strcat( the_modes, x );
322                         }
323
324                         /* "nachbearbeiten" */
325                         if( x[0] == 'a' )
326                         {
327                                 /* away */
328                                 if( set ) Client_SetAway( cl, DEFAULT_AWAY_MSG );
329                                 else Client_SetAway( cl, NULL );
330                         }
331                 }
332                 if( chan )
333                 {
334                         /* Es geht um Channel-Modes oder Channel-User-Modes */
335                         if( chan_cl )
336                         {
337                                 /* Channel-User-Modes */
338                                 if( set )
339                                 {
340                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
341                                         if( Channel_UserModeAdd( chan, chan_cl, x[0] )) strcat( the_modes, x );
342                                 }
343                                 else
344                                 {
345                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
346                                         if( Channel_UserModeDel( chan, chan_cl, x[0] )) strcat( the_modes, x );
347                                 }
348                         }
349                         else
350                         {
351                                 /* Channel-Mode */
352                                 if( set )
353                                 {
354                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
355                                         if( Channel_ModeAdd( chan, x[0] )) strcat( the_modes, x );
356                                 }
357                                 else
358                                 {
359                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
360                                         if( Channel_ModeDel( chan, x[0] )) strcat( the_modes, x );
361                                 }
362                         }
363                 }
364         }
365
366         /* Wurden Modes geaendert? */
367         if( the_modes[1] )
368         {
369                 if( cl )
370                 {
371                         /* Client-Mode */
372                         if( Client_Type( Client ) == CLIENT_SERVER )
373                         {
374                                 /* Modes an andere Server forwarden */
375                                 IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes );
376                         }
377                         else
378                         {
379                                 /* Bestaetigung an Client schicken & andere Server informieren */
380                                 ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Client_ID( cl ), the_modes );
381                                 IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes );
382                         }
383                         Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( cl ), Client_Modes( cl ));
384                 }
385                 if( chan )
386                 {
387                         /* Channel-Modes oder Channel-User-Mode */
388                         if( chan_cl )
389                         {
390                                 /* Channel-User-Mode */
391                                 if( Client_Type( Client ) == CLIENT_SERVER )
392                                 {
393                                         /* Modes an andere Server und Channel-User forwarden */
394                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
395                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
396                                 }
397                                 else
398                                 {
399                                         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
400                                         ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
401                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
402                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
403                                 }
404                                 Log( LOG_DEBUG, "User \"%s\" on %s: Mode change, now \"%s\".", Client_Mask( chan_cl), Channel_Name( chan ), Channel_UserModes( chan, chan_cl ));
405                         }
406                         else
407                         {
408                                 /* Channel-Mode */
409                                 if( Client_Type( Client ) == CLIENT_SERVER )
410                                 {
411                                         /* Modes an andere Server und Channel-User forwarden */
412                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes );
413                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes );
414                                 }
415                                 else
416                                 {
417                                         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
418                                         ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Channel_Name( chan ), the_modes );
419                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes );
420                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes );
421                                 }
422                                 Log( LOG_DEBUG, "Channel \"%s\": Mode change, now \"%s\".", Channel_Name( chan ), Channel_Modes( chan ));
423                         }
424                 }
425         }
426
427         return ok;
428 } /* IRC_MODE */
429
430
431 GLOBAL BOOLEAN
432 IRC_AWAY( CLIENT *Client, REQUEST *Req )
433 {
434         assert( Client != NULL );
435         assert( Req != NULL );
436
437         /* Falsche Anzahl Parameter? */
438         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
439
440         if(( Req->argc == 1 ) && (Req->argv[0][0] ))
441         {
442                 /* AWAY setzen */
443                 Client_SetAway( Client, Req->argv[0] );
444                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
445                 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
446         }
447         else
448         {
449                 /* AWAY loeschen */
450                 Client_SetAway( Client, NULL );
451                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
452                 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
453         }
454 } /* IRC_AWAY */
455
456
457 LOCAL BOOLEAN
458 Add_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
459 {
460         CHAR *mask;
461
462         assert( Client != NULL );
463         assert( Channel != NULL );
464         assert( Pattern != NULL );
465
466         mask = Lists_MakeMask( Pattern );
467
468         if( ! Lists_AddInvited( Prefix, mask, Channel, FALSE )) return CONNECTED;
469         return Send_ListChange( "+I", Prefix, Client, Channel, mask );
470 } /* Add_Invite */
471
472
473 LOCAL BOOLEAN
474 Add_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
475 {
476         CHAR *mask;
477
478         assert( Client != NULL );
479         assert( Channel != NULL );
480         assert( Pattern != NULL );
481
482         mask = Lists_MakeMask( Pattern );
483
484         if( ! Lists_AddBanned( Prefix, mask, Channel )) return CONNECTED;
485         return Send_ListChange( "+b", Prefix, Client, Channel, mask );
486 } /* Add_Ban */
487
488
489 LOCAL BOOLEAN
490 Del_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
491 {
492         CHAR *mask;
493
494         assert( Client != NULL );
495         assert( Channel != NULL );
496         assert( Pattern != NULL );
497
498         mask = Lists_MakeMask( Pattern );
499         Lists_DelInvited( mask, Channel );
500         return Send_ListChange( "-I", Prefix, Client, Channel, mask );
501 } /* Del_Invite */
502
503
504 LOCAL BOOLEAN
505 Del_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
506 {
507         CHAR *mask;
508
509         assert( Client != NULL );
510         assert( Channel != NULL );
511         assert( Pattern != NULL );
512
513         mask = Lists_MakeMask( Pattern );
514         Lists_DelBanned( mask, Channel );
515         return Send_ListChange( "-b", Prefix, Client, Channel, mask );
516 } /* Del_Ban */
517
518
519 LOCAL BOOLEAN
520 Send_ListChange( CHAR *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Mask )
521 {
522         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
523
524         BOOLEAN ok;
525
526         if( Client_Type( Client ) == CLIENT_USER )
527         {
528                 /* Bestaetigung an Client */
529                 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
530         }
531         else ok = TRUE;
532
533         /* an andere Server */
534         IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
535
536         /* und lokale User im Channel */
537         IRC_WriteStrChannelPrefix( Client, Channel, Prefix, FALSE, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
538         
539         return ok;
540 } /* Send_ListChange */
541
542
543 /* -eof- */