]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc.c
e80cf754395882a1d8d9225c74271d703388d2e9
[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.12 2001/12/27 19:17:26 alex Exp $
13  *
14  * irc.c: IRC-Befehle
15  *
16  * $Log: irc.c,v $
17  * Revision 1.12  2001/12/27 19:17:26  alex
18  * - neue Befehle PRIVMSG, NOTICE, PING.
19  *
20  * Revision 1.11  2001/12/27 16:55:41  alex
21  * - neu: IRC_WriteStrRelated(), Aenderungen auch in IRC_WriteStrClient().
22  *
23  * Revision 1.10  2001/12/26 22:48:53  alex
24  * - MOTD-Datei ist nun konfigurierbar und wird gelesen.
25  *
26  * Revision 1.9  2001/12/26 14:45:37  alex
27  * - "Code Cleanups".
28  *
29  * Revision 1.8  2001/12/26 03:21:46  alex
30  * - PING/PONG-Befehle implementiert,
31  * - Meldungen ueberarbeitet: enthalten nun (fast) immer den Nick.
32  *
33  * Revision 1.7  2001/12/25 23:25:18  alex
34  * - und nochmal Aenderungen am Logging ;-)
35  *
36  * Revision 1.6  2001/12/25 23:13:33  alex
37  * - Debug-Meldungen angepasst.
38  *
39  * Revision 1.5  2001/12/25 22:02:42  alex
40  * - neuer IRC-Befehl "/QUIT". Verbessertes Logging & Debug-Ausgaben.
41  *
42  * Revision 1.4  2001/12/25 19:19:30  alex
43  * - bessere Fehler-Abfragen, diverse Bugfixes.
44  * - Nicks werden nur einmal vergeben :-)
45  * - /MOTD wird unterstuetzt.
46  *
47  * Revision 1.3  2001/12/24 01:34:06  alex
48  * - USER und NICK wird nun in beliebiger Reihenfolge akzeptiert (wg. BitchX)
49  * - MOTD-Ausgabe begonnen zu implementieren.
50  *
51  * Revision 1.2  2001/12/23 21:57:16  alex
52  * - erste IRC-Befehle zu implementieren begonnen.
53  *
54  * Revision 1.1  2001/12/14 08:13:43  alex
55  * - neues Modul begonnen :-)
56  */
57
58
59 #include <portab.h>
60 #include "global.h"
61
62 #include <imp.h>
63 #include <assert.h>
64 #include <errno.h>
65 #include <stdarg.h>
66 #include <stdio.h>
67 #include <string.h>
68
69 #include "client.h"
70 #include "conf.h"
71 #include "log.h"
72 #include "messages.h"
73 #include "parse.h"
74
75 #include <exp.h>
76 #include "irc.h"
77
78
79 #define CONNECTED TRUE
80 #define DISCONNECTED FALSE
81
82
83 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client );
84
85 LOCAL BOOLEAN Hello_User( CLIENT *Client );
86 LOCAL BOOLEAN Show_MOTD( CLIENT *Client );
87
88
89 GLOBAL VOID IRC_Init( VOID )
90 {
91 } /* IRC_Init */
92
93
94 GLOBAL VOID IRC_Exit( VOID )
95 {
96 } /* IRC_Exit */
97
98
99 GLOBAL BOOLEAN IRC_WriteStrClient( CLIENT *Client, CLIENT *Prefix, CHAR *Format, ... )
100 {
101         /* Text an Clients, lokal bzw. remote, senden. */
102
103         CHAR buffer[1000];
104         BOOLEAN ok = CONNECTED;
105         CONN_ID send_to;
106         va_list ap;
107
108         assert( Client != NULL );
109         assert( Format != NULL );
110
111         va_start( ap, Format );
112         vsnprintf( buffer, 1000, Format, ap );
113         va_end( ap );
114
115         if( Client->conn_id != NONE ) send_to = Client->conn_id;
116         else send_to = Client->introducer->conn_id;
117
118         if( Prefix ) ok = Conn_WriteStr( Client->conn_id, ":%s %s", Client_GetID( Prefix ), buffer );
119         else ok = Conn_WriteStr( Client->conn_id, buffer );
120
121         return ok;
122 } /* IRC_WriteStrClient */
123
124
125 GLOBAL BOOLEAN IRC_WriteStrRelated( CLIENT *Client, CHAR *Format, ... )
126 {
127         CHAR buffer[1000];
128         BOOLEAN ok = CONNECTED;
129         va_list ap;
130
131         assert( Client != NULL );
132         assert( Format != NULL );
133
134         va_start( ap, Format );
135         vsnprintf( buffer, 1000, Format, ap );
136         va_end( ap );
137
138         /* an den Client selber */
139         ok = IRC_WriteStrClient( Client, Client, buffer );
140
141         return ok;
142 } /* IRC_WriteStrRelated */
143
144
145 GLOBAL BOOLEAN IRC_PASS( CLIENT *Client, REQUEST *Req )
146 {
147         assert( Client != NULL );
148         assert( Req != NULL );
149
150         if( Client->type == CLIENT_UNKNOWN )
151         {
152                 Log( LOG_DEBUG, "Connection %d: got PASS command ...", Client->conn_id );
153                 return IRC_WriteStrClient( Client, This_Server, ERR_UNKNOWNCOMMAND_MSG, Client_Name( Client ), Req->command );
154         }
155         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
156 } /* IRC_PASS */
157
158
159 GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req )
160 {
161         assert( Client != NULL );
162         assert( Req != NULL );
163
164         if( Client->type != CLIENT_SERVER && Client->type != CLIENT_SERVICE )
165         {
166                 /* Falsche Anzahl Parameter? */
167                 if( Req->argc != 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
168
169                 /* pruefen, ob Nick bereits vergeben */
170                 if( ! Client_CheckNick( Client, Req->argv[0] )) return CONNECTED;
171
172                 if( Client->type == CLIENT_USER )
173                 {
174                         /* Nick-Aenderung: allen mitteilen! */
175                         Log( LOG_INFO, "User \"%s!%s@%s\" (%s) changed nick: \"%s\" -> \"%s\".", Client->nick, Client->user, Client->host, Client->name, Client->nick, Req->argv[0] );
176                         IRC_WriteStrRelated( Client, "NICK :%s", Req->argv[0] );
177                 }
178                 
179                 /* Client-Nick registrieren */
180                 strcpy( Client->nick, Req->argv[0] );
181
182                 if( Client->type != CLIENT_USER )
183                 {
184                         /* Neuer Client */
185                         Log( LOG_DEBUG, "Connection %d: got NICK command ...", Client->conn_id );
186                         if( Client->type == CLIENT_GOTUSER ) return Hello_User( Client );
187                         else Client->type = CLIENT_GOTNICK;
188                 }
189                 return CONNECTED;
190         }
191         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
192 } /* IRC_NICK */
193
194
195 GLOBAL BOOLEAN IRC_USER( CLIENT *Client, REQUEST *Req )
196 {
197         assert( Client != NULL );
198         assert( Req != NULL );
199
200         if( Client->type == CLIENT_UNKNOWN || Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTPASS )
201         {
202                 /* Falsche Anzahl Parameter? */
203                 if( Req->argc != 4 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
204
205                 strncpy( Client->user, Req->argv[0], CLIENT_USER_LEN );
206                 Client->user[CLIENT_USER_LEN] = '\0';
207                 strncpy( Client->name, Req->argv[3], CLIENT_NAME_LEN );
208                 Client->name[CLIENT_NAME_LEN] = '\0';
209
210                 Log( LOG_DEBUG, "Connection %d: got USER command ...", Client->conn_id );
211                 if( Client->type == CLIENT_GOTNICK ) return Hello_User( Client );
212                 else Client->type = CLIENT_GOTUSER;
213                 return CONNECTED;
214         }
215         else if( Client->type == CLIENT_USER || Client->type == CLIENT_SERVER || Client->type == CLIENT_SERVICE )
216         {
217                 return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Name( Client ));
218         }
219         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
220 } /* IRC_USER */
221
222
223 GLOBAL BOOLEAN IRC_QUIT( CLIENT *Client, REQUEST *Req )
224 {
225         assert( Client != NULL );
226         assert( Req != NULL );
227
228         if( Client->type != CLIENT_SERVER && Client->type != CLIENT_SERVICE )
229         {
230                 /* Falsche Anzahl Parameter? */
231                 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
232
233                 Conn_Close( Client->conn_id, "Client wants to quit." );
234                 return DISCONNECTED;
235         }
236         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
237 } /* IRC_QUIT */
238
239
240 GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req )
241 {
242         CLIENT *to;
243         
244         assert( Client != NULL );
245         assert( Req != NULL );
246
247         if( ! Check_Valid_User( Client )) return CONNECTED;
248
249         /* Falsche Anzahl Parameter? */
250         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Name( Client ));
251         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
252
253         to = Client_Search( Req->argv[0] );
254         
255         if( to ) return IRC_WriteStrClient( Client, This_Server, "PONG :%s", Client_Name( Client ));
256         else return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Name( Client ), Req->argv[0] );
257 } /* IRC_PING */
258
259
260 GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req )
261 {
262         assert( Client != NULL );
263         assert( Req != NULL );
264
265         if( ! Check_Valid_User( Client )) return CONNECTED;
266
267         /* Falsche Anzahl Parameter? */
268         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Name( Client ));
269         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
270
271         /* Der Connection-Timestamp wurde schon beim Lesen aus dem Socket
272          * aktualisiert, daher muss das hier nicht mehr gemacht werden. */
273
274         Log( LOG_DEBUG, "Connection %d: received PONG.", Client->conn_id );
275         return CONNECTED;
276 } /* IRC_PONG */
277
278
279 GLOBAL BOOLEAN IRC_MOTD( CLIENT *Client, REQUEST *Req )
280 {
281         assert( Client != NULL );
282         assert( Req != NULL );
283
284         if( ! Check_Valid_User( Client )) return CONNECTED;
285
286         /* Falsche Anzahl Parameter? */
287         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
288
289         return Show_MOTD( Client );
290 } /* IRC_MOTD */
291
292
293 GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req )
294 {
295         CLIENT *to;
296         
297         assert( Client != NULL );
298         assert( Req != NULL );
299
300         if( ! Check_Valid_User( Client )) return CONNECTED;
301
302         /* Falsche Anzahl Parameter? */
303         if( Req->argc == 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NORECIPIENT_MSG, Client_Name( Client ), Req->command );
304         if( Req->argc == 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOTEXTTOSEND_MSG, Client_Name( Client ));
305         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Name( Client ), Req->command );
306
307         to = Client_Search( Req->argv[0] );
308         if( to )
309         {
310                 /* Okay, Ziel ist ein User */
311                 return IRC_WriteStrClient( to, Client, "PRIVMSG %s :%s", to->nick, Req->argv[1] );
312         }
313
314         return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Name( Client ), Req->argv[0] );
315 } /* IRC_PRIVMSG */
316
317
318 GLOBAL BOOLEAN IRC_NOTICE( CLIENT *Client, REQUEST *Req )
319 {
320         CLIENT *to;
321
322         assert( Client != NULL );
323         assert( Req != NULL );
324
325         if( ! Check_Valid_User( Client )) return CONNECTED;
326
327         /* Falsche Anzahl Parameter? */
328         if( Req->argc != 2 ) return CONNECTED;
329
330         to = Client_Search( Req->argv[0] );
331         if( to )
332         {
333                 /* Okay, Ziel ist ein User */
334                 return IRC_WriteStrClient( to, Client, "NOTICE %s :%s", to->nick, Req->argv[1] );
335         }
336
337         return CONNECTED;
338 } /* IRC_NOTICE */
339
340
341 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client )
342 {
343         assert( Client != NULL );
344
345         if( Client->type != CLIENT_USER )
346         {
347                 IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Name( Client ));
348                 return FALSE;
349         }
350         else return TRUE;
351 } /* Check_Valid_User */
352
353
354 LOCAL BOOLEAN Hello_User( CLIENT *Client )
355 {
356         assert( Client != NULL );
357         assert( Client->nick[0] );
358         
359         Log( LOG_INFO, "User \"%s!%s@%s\" (%s) registered.", Client->nick, Client->user, Client->host, Client->name );
360
361         IRC_WriteStrClient( Client, This_Server, RPL_WELCOME_MSG, Client->nick, Client_GetID( Client ));
362         IRC_WriteStrClient( Client, This_Server, RPL_YOURHOST_MSG, Client->nick, This_Server->host );
363         IRC_WriteStrClient( Client, This_Server, RPL_CREATED_MSG, Client->nick );
364         IRC_WriteStrClient( Client, This_Server, RPL_MYINFO_MSG, Client->nick, This_Server->host );
365
366         Client->type = CLIENT_USER;
367
368         return Show_MOTD( Client );
369 } /* Hello_User */
370
371
372 LOCAL BOOLEAN Show_MOTD( CLIENT *Client )
373 {
374         BOOLEAN ok;
375         CHAR line[127];
376         FILE *fd;
377         
378         assert( Client != NULL );
379         assert( Client->nick[0] );
380
381         fd = fopen( Conf_MotdFile, "r" );
382         if( ! fd )
383         {
384                 Log( LOG_WARNING, "Can't read MOTD file \"%s\": %s", Conf_MotdFile, strerror( errno ));
385                 return IRC_WriteStrClient( Client, This_Server, ERR_NOMOTD_MSG, Client->nick );
386         }
387         
388         IRC_WriteStrClient( Client, This_Server, RPL_MOTDSTART_MSG, Client->nick, This_Server->host );
389         while( TRUE )
390         {
391                 if( ! fgets( line, 126, fd )) break;
392                 if( line[strlen( line ) - 1] == '\n' ) line[strlen( line ) - 1] = '\0';
393                 IRC_WriteStrClient( Client, This_Server, RPL_MOTD_MSG, Client->nick, line );
394         }
395         ok = IRC_WriteStrClient( Client, This_Server, RPL_ENDOFMOTD_MSG, Client->nick );
396
397         fclose( fd );
398         
399         return ok;
400 } /* Show_MOTD */
401
402
403 /* -eof- */