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