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