]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc.c
- Neue Funktion IRC_MODE() implementiert, div. Aenderungen.
[ngircd-alex.git] / src / ngircd / irc.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001 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 comBase beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: irc.c,v 1.13 2001/12/29 03:10:06 alex Exp $
13  *
14  * irc.c: IRC-Befehle
15  *
16  * $Log: irc.c,v $
17  * Revision 1.13  2001/12/29 03:10:06  alex
18  * - Neue Funktion IRC_MODE() implementiert, div. Aenderungen.
19  * - neue configure-Optione "--enable-strict-rfc".
20  *
21  * Revision 1.12  2001/12/27 19:17:26  alex
22  * - neue Befehle PRIVMSG, NOTICE, PING.
23  *
24  * Revision 1.11  2001/12/27 16:55:41  alex
25  * - neu: IRC_WriteStrRelated(), Aenderungen auch in IRC_WriteStrClient().
26  *
27  * Revision 1.10  2001/12/26 22:48:53  alex
28  * - MOTD-Datei ist nun konfigurierbar und wird gelesen.
29  *
30  * Revision 1.9  2001/12/26 14:45:37  alex
31  * - "Code Cleanups".
32  *
33  * Revision 1.8  2001/12/26 03:21:46  alex
34  * - PING/PONG-Befehle implementiert,
35  * - Meldungen ueberarbeitet: enthalten nun (fast) immer den Nick.
36  *
37  * Revision 1.7  2001/12/25 23:25:18  alex
38  * - und nochmal Aenderungen am Logging ;-)
39  *
40  * Revision 1.6  2001/12/25 23:13:33  alex
41  * - Debug-Meldungen angepasst.
42  *
43  * Revision 1.5  2001/12/25 22:02:42  alex
44  * - neuer IRC-Befehl "/QUIT". Verbessertes Logging & Debug-Ausgaben.
45  *
46  * Revision 1.4  2001/12/25 19:19:30  alex
47  * - bessere Fehler-Abfragen, diverse Bugfixes.
48  * - Nicks werden nur einmal vergeben :-)
49  * - /MOTD wird unterstuetzt.
50  *
51  * Revision 1.3  2001/12/24 01:34:06  alex
52  * - USER und NICK wird nun in beliebiger Reihenfolge akzeptiert (wg. BitchX)
53  * - MOTD-Ausgabe begonnen zu implementieren.
54  *
55  * Revision 1.2  2001/12/23 21:57:16  alex
56  * - erste IRC-Befehle zu implementieren begonnen.
57  *
58  * Revision 1.1  2001/12/14 08:13:43  alex
59  * - neues Modul begonnen :-)
60  */
61
62
63 #include <portab.h>
64 #include "global.h"
65
66 #include <imp.h>
67 #include <assert.h>
68 #include <errno.h>
69 #include <stdarg.h>
70 #include <stdio.h>
71 #include <string.h>
72
73 #include "client.h"
74 #include "conf.h"
75 #include "log.h"
76 #include "messages.h"
77 #include "parse.h"
78
79 #include <exp.h>
80 #include "irc.h"
81
82
83 #define CONNECTED TRUE
84 #define DISCONNECTED FALSE
85
86
87 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client );
88
89 LOCAL BOOLEAN Hello_User( CLIENT *Client );
90 LOCAL BOOLEAN Show_MOTD( CLIENT *Client );
91
92
93 GLOBAL VOID IRC_Init( VOID )
94 {
95 } /* IRC_Init */
96
97
98 GLOBAL VOID IRC_Exit( VOID )
99 {
100 } /* IRC_Exit */
101
102
103 GLOBAL BOOLEAN IRC_WriteStrClient( CLIENT *Client, CLIENT *Prefix, CHAR *Format, ... )
104 {
105         /* Text an Clients, lokal bzw. remote, senden. */
106
107         CHAR buffer[1000];
108         BOOLEAN ok = CONNECTED;
109         CONN_ID send_to;
110         va_list ap;
111
112         assert( Client != NULL );
113         assert( Format != NULL );
114
115         va_start( ap, Format );
116         vsnprintf( buffer, 1000, Format, ap );
117         va_end( ap );
118
119         if( Client->conn_id != NONE ) send_to = Client->conn_id;
120         else send_to = Client->introducer->conn_id;
121
122         if( Prefix ) ok = Conn_WriteStr( Client->conn_id, ":%s %s", Client_GetID( Prefix ), buffer );
123         else ok = Conn_WriteStr( Client->conn_id, buffer );
124
125         return ok;
126 } /* IRC_WriteStrClient */
127
128
129 GLOBAL BOOLEAN IRC_WriteStrRelated( CLIENT *Client, CHAR *Format, ... )
130 {
131         CHAR buffer[1000];
132         BOOLEAN ok = CONNECTED;
133         va_list ap;
134
135         assert( Client != NULL );
136         assert( Format != NULL );
137
138         va_start( ap, Format );
139         vsnprintf( buffer, 1000, Format, ap );
140         va_end( ap );
141
142         /* an den Client selber */
143         ok = IRC_WriteStrClient( Client, Client, buffer );
144
145         return ok;
146 } /* IRC_WriteStrRelated */
147
148
149 GLOBAL BOOLEAN IRC_PASS( CLIENT *Client, REQUEST *Req )
150 {
151         assert( Client != NULL );
152         assert( Req != NULL );
153
154         if( Client->type == CLIENT_UNKNOWN )
155         {
156                 Log( LOG_DEBUG, "Connection %d: got PASS command ...", Client->conn_id );
157                 return IRC_WriteStrClient( Client, This_Server, ERR_UNKNOWNCOMMAND_MSG, Client_Name( Client ), Req->command );
158         }
159         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
160 } /* IRC_PASS */
161
162
163 GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req )
164 {
165         assert( Client != NULL );
166         assert( Req != NULL );
167
168         /* Zumindest BitchX sendet NICK-USER in der falschen Reihenfolge. */
169 #ifndef STRICT_RFC
170         if( Client->type == CLIENT_UNKNOWN || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTUSER || Client->type == CLIENT_USER )
171 #else
172         if( Client->type == CLIENT_UNKNOWN || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_GOTNICK || Client->type == CLIENT_USER  )
173 #endif
174         {
175                 /* Falsche Anzahl Parameter? */
176                 if( Req->argc != 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
177
178                 /* Ist der Client "restricted"? */
179                 if( strchr( Client->modes, 'r' )) return IRC_WriteStrClient( Client, This_Server, ERR_RESTRICTED_MSG, Client_Name( Client ));
180
181                 /* Wenn der Client zu seinem eigenen Nick wechseln will, so machen
182                  * wir nichts. So macht es das Original und mind. Snak hat probleme,
183                  * wenn wir es nicht so machen. Ob es so okay ist? Hm ... */
184 #ifndef STRICT_RFC
185                 if( strcasecmp( Client->nick, Req->argv[0] ) == 0 ) return CONNECTED;
186 #endif
187                 
188                 /* pruefen, ob Nick bereits vergeben */
189                 if( ! Client_CheckNick( Client, Req->argv[0] )) return CONNECTED;
190
191                 if( Client->type == CLIENT_USER )
192                 {
193                         /* Nick-Aenderung: allen mitteilen! */
194                         Log( LOG_INFO, "User \"%s!%s@%s\" changed nick: \"%s\" -> \"%s\".", Client->nick, Client->user, Client->host, Client->nick, Req->argv[0] );
195                         IRC_WriteStrRelated( Client, "NICK :%s", Req->argv[0] );
196                 }
197                 
198                 /* Client-Nick registrieren */
199                 strcpy( Client->nick, Req->argv[0] );
200
201                 if( Client->type != CLIENT_USER )
202                 {
203                         /* Neuer Client */
204                         Log( LOG_DEBUG, "Connection %d: got NICK command ...", Client->conn_id );
205                         if( Client->type == CLIENT_GOTUSER ) return Hello_User( Client );
206                         else Client->type = CLIENT_GOTNICK;
207                 }
208                 return CONNECTED;
209         }
210         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
211 } /* IRC_NICK */
212
213
214 GLOBAL BOOLEAN IRC_USER( CLIENT *Client, REQUEST *Req )
215 {
216         assert( Client != NULL );
217         assert( Req != NULL );
218
219 #ifndef STRICT_RFC
220         if( Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_UNKNOWN )
221 #else
222         if( Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTPASS )
223 #endif
224         {
225                 /* Falsche Anzahl Parameter? */
226                 if( Req->argc != 4 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
227
228                 strncpy( Client->user, Req->argv[0], CLIENT_USER_LEN );
229                 Client->user[CLIENT_USER_LEN] = '\0';
230                 strncpy( Client->name, Req->argv[3], CLIENT_NAME_LEN );
231                 Client->name[CLIENT_NAME_LEN] = '\0';
232
233                 Log( LOG_DEBUG, "Connection %d: got USER command ...", Client->conn_id );
234                 if( Client->type == CLIENT_GOTNICK ) return Hello_User( Client );
235                 else Client->type = CLIENT_GOTUSER;
236                 return CONNECTED;
237         }
238         else if( Client->type == CLIENT_USER || Client->type == CLIENT_SERVER || Client->type == CLIENT_SERVICE )
239         {
240                 return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
241         }
242         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
243 } /* IRC_USER */
244
245
246 GLOBAL BOOLEAN IRC_QUIT( CLIENT *Client, REQUEST *Req )
247 {
248         assert( Client != NULL );
249         assert( Req != NULL );
250
251         if( Client->type != CLIENT_SERVER && Client->type != CLIENT_SERVICE )
252         {
253                 /* Falsche Anzahl Parameter? */
254                 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
255
256                 Conn_Close( Client->conn_id, "Client wants to quit." );
257                 return DISCONNECTED;
258         }
259         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
260 } /* IRC_QUIT */
261
262
263 GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req )
264 {
265         CLIENT *to;
266         
267         assert( Client != NULL );
268         assert( Req != NULL );
269
270         if( ! Check_Valid_User( Client )) return CONNECTED;
271
272         /* Falsche Anzahl Parameter? */
273         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Name( Client ));
274         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
275
276         to = Client_Search( Req->argv[0] );
277         
278         if( to ) return IRC_WriteStrClient( Client, This_Server, "PONG :%s", Client_Name( Client ));
279         else return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Name( Client ), Req->argv[0] );
280 } /* IRC_PING */
281
282
283 GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req )
284 {
285         assert( Client != NULL );
286         assert( Req != NULL );
287
288         if( ! Check_Valid_User( Client )) return CONNECTED;
289
290         /* Falsche Anzahl Parameter? */
291         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Name( Client ));
292         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
293
294         /* Der Connection-Timestamp wurde schon beim Lesen aus dem Socket
295          * aktualisiert, daher muss das hier nicht mehr gemacht werden. */
296
297         Log( LOG_DEBUG, "Connection %d: received PONG.", Client->conn_id );
298         return CONNECTED;
299 } /* IRC_PONG */
300
301
302 GLOBAL BOOLEAN IRC_MOTD( CLIENT *Client, REQUEST *Req )
303 {
304         assert( Client != NULL );
305         assert( Req != NULL );
306
307         if( ! Check_Valid_User( Client )) return CONNECTED;
308
309         /* Falsche Anzahl Parameter? */
310         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
311
312         return Show_MOTD( Client );
313 } /* IRC_MOTD */
314
315
316 GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req )
317 {
318         CLIENT *to;
319         
320         assert( Client != NULL );
321         assert( Req != NULL );
322
323         if( ! Check_Valid_User( Client )) return CONNECTED;
324
325         /* Falsche Anzahl Parameter? */
326         if( Req->argc == 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NORECIPIENT_MSG, Client_Name( Client ), Req->command );
327         if( Req->argc == 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOTEXTTOSEND_MSG, Client_Name( Client ));
328         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
329
330         to = Client_Search( Req->argv[0] );
331         if( to )
332         {
333                 /* Okay, Ziel ist ein User */
334                 return IRC_WriteStrClient( to, Client, "PRIVMSG %s :%s", to->nick, Req->argv[1] );
335         }
336
337         return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Name( Client ), Req->argv[0] );
338 } /* IRC_PRIVMSG */
339
340
341 GLOBAL BOOLEAN IRC_NOTICE( CLIENT *Client, REQUEST *Req )
342 {
343         CLIENT *to;
344
345         assert( Client != NULL );
346         assert( Req != NULL );
347
348         if( ! Check_Valid_User( Client )) return CONNECTED;
349
350         /* Falsche Anzahl Parameter? */
351         if( Req->argc != 2 ) return CONNECTED;
352
353         to = Client_Search( Req->argv[0] );
354         if( to )
355         {
356                 /* Okay, Ziel ist ein User */
357                 return IRC_WriteStrClient( to, Client, "NOTICE %s :%s", to->nick, Req->argv[1] );
358         }
359
360         return CONNECTED;
361 } /* IRC_NOTICE */
362
363
364 GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req )
365 {
366         CHAR x[2], new_modes[CLIENT_MODE_LEN], *ptr, *p;
367         BOOLEAN set, ok;
368         
369         assert( Client != NULL );
370         assert( Req != NULL );
371
372         if( ! Check_Valid_User( Client )) return CONNECTED;
373
374         /* Falsche Anzahl Parameter? */
375         if(( Req->argc > 2 ) || ( Req->argc < 1 )) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
376
377         /* MODE ist nur fuer sich selber zulaessig */
378         if( Client_Search( Req->argv[0] ) != Client ) return IRC_WriteStrClient( Client, This_Server, ERR_USERSDONTMATCH_MSG, Client_Name( Client ));
379
380         /* Werden die Modes erfragt? */
381         if( Req->argc == 1 ) return IRC_WriteStrClient( Client, This_Server, RPL_UMODEIS_MSG, Client_Name( Client ), Client->modes );
382
383         ptr = Req->argv[1];
384
385         /* Sollen Modes gesetzt oder geloescht werden? */
386         if( *ptr == '+' ) set = TRUE;
387         else if( *ptr == '-' ) set = FALSE;
388         else return IRC_WriteStrClient( Client, This_Server, ERR_UMODEUNKNOWNFLAG_MSG, Client_Name( Client ));
389
390         /* Reply-String mit Aenderungen vorbereiten */
391         if( set ) strcpy( new_modes, "+" );
392         else strcpy( new_modes, "-" );
393
394         ptr++;
395         ok = TRUE;
396         x[1] = '\0';
397         while( *ptr )
398         {
399                 x[0] = '\0';
400                 switch( *ptr )
401                 {
402                         case 'i':
403                                 /* invisible */
404                                 x[0] = 'i';
405                                 break;
406                         case 'r':
407                                 /* restricted (kann nur gesetzt werden) */
408                                 if( set ) x[0] = 'r';
409                                 else ok = IRC_WriteStrClient( Client, This_Server, ERR_RESTRICTED_MSG, Client_Name( Client ));
410                                 break;
411                         default:
412                                 ok = IRC_WriteStrClient( Client, This_Server, ERR_UMODEUNKNOWNFLAG_MSG, Client_Name( Client ));
413                                 x[0] = '\0';
414                 }
415                 if( ! ok ) break;
416
417                 ptr++;
418                 if( ! x[0] ) continue;
419
420                 /* Okay, gueltigen Mode gefunden */
421                 if( set )
422                 {
423                         /* Modes sollen gesetzt werden */
424                         if( ! strchr( Client->modes, x[0] ))
425                         {
426                                 /* Client hat den Mode noch nicht -> setzen */
427                                 strcat( Client->modes, x );
428                                 strcat( new_modes, x );
429                         }
430                 }
431                 else
432                 {
433                         /* Modes sollen geloescht werden */
434                         p = strchr( Client->modes, x[0] );
435                         if( p )
436                         {
437                                 /* Client hat den Mode -> loeschen */
438                                 while( *p )
439                                 {
440                                         *p = *(p + 1);
441                                         p++;
442                                 }
443                                 strcat( new_modes, x );
444                         }
445                 }
446         }
447         
448         /* Geanderte Modes? */
449         if( new_modes[1] && ok )
450         {
451                 ok = IRC_WriteStrRelated( Client, "MODE %s :%s", Client->nick, new_modes );
452                 Log( LOG_DEBUG, "User \"%s!%s@%s\": Mode change, now \"%s\".", Client->nick, Client->user, Client->host, Client->modes );
453         }
454         return ok;
455 } /* IRC_MODE */
456
457
458 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client )
459 {
460         assert( Client != NULL );
461
462         if( Client->type != CLIENT_USER )
463         {
464                 IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
465                 return FALSE;
466         }
467         else return TRUE;
468 } /* Check_Valid_User */
469
470
471 LOCAL BOOLEAN Hello_User( CLIENT *Client )
472 {
473         assert( Client != NULL );
474         assert( Client->nick[0] );
475         
476         Log( LOG_NOTICE, "User \"%s!%s@%s\" (%s) registered (connection %d).", Client->nick, Client->user, Client->host, Client->name, Client->conn_id );
477
478         IRC_WriteStrClient( Client, This_Server, RPL_WELCOME_MSG, Client->nick, Client_GetID( Client ));
479         IRC_WriteStrClient( Client, This_Server, RPL_YOURHOST_MSG, Client->nick, This_Server->host );
480         IRC_WriteStrClient( Client, This_Server, RPL_CREATED_MSG, Client->nick );
481         IRC_WriteStrClient( Client, This_Server, RPL_MYINFO_MSG, Client->nick, This_Server->host );
482
483         Client->type = CLIENT_USER;
484
485         return Show_MOTD( Client );
486 } /* Hello_User */
487
488
489 LOCAL BOOLEAN Show_MOTD( CLIENT *Client )
490 {
491         BOOLEAN ok;
492         CHAR line[127];
493         FILE *fd;
494         
495         assert( Client != NULL );
496         assert( Client->nick[0] );
497
498         fd = fopen( Conf_MotdFile, "r" );
499         if( ! fd )
500         {
501                 Log( LOG_WARNING, "Can't read MOTD file \"%s\": %s", Conf_MotdFile, strerror( errno ));
502                 return IRC_WriteStrClient( Client, This_Server, ERR_NOMOTD_MSG, Client->nick );
503         }
504         
505         IRC_WriteStrClient( Client, This_Server, RPL_MOTDSTART_MSG, Client->nick, This_Server->host );
506         while( TRUE )
507         {
508                 if( ! fgets( line, 126, fd )) break;
509                 if( line[strlen( line ) - 1] == '\n' ) line[strlen( line ) - 1] = '\0';
510                 IRC_WriteStrClient( Client, This_Server, RPL_MOTD_MSG, Client->nick, line );
511         }
512         ok = IRC_WriteStrClient( Client, This_Server, RPL_ENDOFMOTD_MSG, Client->nick );
513
514         fclose( fd );
515         
516         return ok;
517 } /* Show_MOTD */
518
519
520 /* -eof- */