- Copyright-Texte angepasst.
[ngircd-alex.git] / src / ngircd / irc.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.c,v 1.19 2002/01/02 02:51:39 alex Exp $
13  *
14  * irc.c: IRC-Befehle
15  *
16  * $Log: irc.c,v $
17  * Revision 1.19  2002/01/02 02:51:39  alex
18  * - Copyright-Texte angepasst.
19  * - neuer Befehl "ERROR".
20  *
21  * Revision 1.17  2001/12/31 15:33:13  alex
22  * - neuer Befehl NAMES, kleinere Bugfixes.
23  * - Bug bei PING behoben: war zu restriktiv implementiert :-)
24  *
25  * Revision 1.16  2001/12/31 02:18:51  alex
26  * - viele neue Befehle (WHOIS, ISON, OPER, DIE, RESTART),
27  * - neuen Header "defines.h" mit (fast) allen Konstanten.
28  * - Code Cleanups und viele "kleine" Aenderungen & Bugfixes.
29  *
30  * Revision 1.15  2001/12/30 19:26:11  alex
31  * - Unterstuetzung fuer die Konfigurationsdatei eingebaut.
32  *
33  * Revision 1.14  2001/12/30 11:42:00  alex
34  * - der Server meldet nun eine ordentliche "Start-Zeit".
35  *
36  * Revision 1.13  2001/12/29 03:10:06  alex
37  * - Neue Funktion IRC_MODE() implementiert, div. Aenderungen.
38  * - neue configure-Optione "--enable-strict-rfc".
39  *
40  * Revision 1.12  2001/12/27 19:17:26  alex
41  * - neue Befehle PRIVMSG, NOTICE, PING.
42  *
43  * Revision 1.11  2001/12/27 16:55:41  alex
44  * - neu: IRC_WriteStrRelated(), Aenderungen auch in IRC_WriteStrClient().
45  *
46  * Revision 1.10  2001/12/26 22:48:53  alex
47  * - MOTD-Datei ist nun konfigurierbar und wird gelesen.
48  *
49  * Revision 1.9  2001/12/26 14:45:37  alex
50  * - "Code Cleanups".
51  *
52  * Revision 1.8  2001/12/26 03:21:46  alex
53  * - PING/PONG-Befehle implementiert,
54  * - Meldungen ueberarbeitet: enthalten nun (fast) immer den Nick.
55  *
56  * Revision 1.7  2001/12/25 23:25:18  alex
57  * - und nochmal Aenderungen am Logging ;-)
58  *
59  * Revision 1.6  2001/12/25 23:13:33  alex
60  * - Debug-Meldungen angepasst.
61  *
62  * Revision 1.5  2001/12/25 22:02:42  alex
63  * - neuer IRC-Befehl "/QUIT". Verbessertes Logging & Debug-Ausgaben.
64  *
65  * Revision 1.4  2001/12/25 19:19:30  alex
66  * - bessere Fehler-Abfragen, diverse Bugfixes.
67  * - Nicks werden nur einmal vergeben :-)
68  * - /MOTD wird unterstuetzt.
69  *
70  * Revision 1.3  2001/12/24 01:34:06  alex
71  * - USER und NICK wird nun in beliebiger Reihenfolge akzeptiert (wg. BitchX)
72  * - MOTD-Ausgabe begonnen zu implementieren.
73  *
74  * Revision 1.2  2001/12/23 21:57:16  alex
75  * - erste IRC-Befehle zu implementieren begonnen.
76  *
77  * Revision 1.1  2001/12/14 08:13:43  alex
78  * - neues Modul begonnen :-)
79  */
80
81
82 #include <portab.h>
83 #include "global.h"
84
85 #include <imp.h>
86 #include <assert.h>
87 #include <errno.h>
88 #include <stdarg.h>
89 #include <stdio.h>
90 #include <string.h>
91
92 #include "ngircd.h"
93 #include "client.h"
94 #include "conf.h"
95 #include "conn.h"
96 #include "log.h"
97 #include "messages.h"
98 #include "parse.h"
99 #include "tool.h"
100
101 #include <exp.h>
102 #include "irc.h"
103
104
105 #define CONNECTED TRUE
106 #define DISCONNECTED FALSE
107
108
109 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client );
110
111 LOCAL BOOLEAN Hello_User( CLIENT *Client );
112 LOCAL BOOLEAN Show_MOTD( CLIENT *Client );
113
114
115 GLOBAL VOID IRC_Init( VOID )
116 {
117 } /* IRC_Init */
118
119
120 GLOBAL VOID IRC_Exit( VOID )
121 {
122 } /* IRC_Exit */
123
124
125 GLOBAL BOOLEAN IRC_WriteStrClient( CLIENT *Client, CLIENT *Prefix, CHAR *Format, ... )
126 {
127         /* Text an Clients, lokal bzw. remote, senden. */
128
129         CHAR buffer[1000];
130         BOOLEAN ok = CONNECTED;
131         CONN_ID send_to;
132         va_list ap;
133
134         assert( Client != NULL );
135         assert( Format != NULL );
136
137         va_start( ap, Format );
138         vsnprintf( buffer, 1000, Format, ap );
139         va_end( ap );
140
141         if( Client->conn_id != NONE ) send_to = Client->conn_id;
142         else send_to = Client->introducer->conn_id;
143
144         if( Prefix ) ok = Conn_WriteStr( Client->conn_id, ":%s %s", Client_GetID( Prefix ), buffer );
145         else ok = Conn_WriteStr( Client->conn_id, buffer );
146
147         return ok;
148 } /* IRC_WriteStrClient */
149
150
151 GLOBAL BOOLEAN IRC_WriteStrRelated( CLIENT *Client, CHAR *Format, ... )
152 {
153         CHAR buffer[1000];
154         BOOLEAN ok = CONNECTED;
155         va_list ap;
156
157         assert( Client != NULL );
158         assert( Format != NULL );
159
160         va_start( ap, Format );
161         vsnprintf( buffer, 1000, Format, ap );
162         va_end( ap );
163
164         /* an den Client selber */
165         ok = IRC_WriteStrClient( Client, Client, buffer );
166
167         return ok;
168 } /* IRC_WriteStrRelated */
169
170
171 GLOBAL BOOLEAN IRC_PASS( CLIENT *Client, REQUEST *Req )
172 {
173         assert( Client != NULL );
174         assert( Req != NULL );
175
176         if( Client->type == CLIENT_UNKNOWN )
177         {
178                 Log( LOG_DEBUG, "Connection %d: got PASS command ...", Client->conn_id );
179                 return IRC_WriteStrClient( Client, This_Server, ERR_UNKNOWNCOMMAND_MSG, Client_Nick( Client ), Req->command );
180         }
181         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Nick( Client ));
182 } /* IRC_PASS */
183
184
185 GLOBAL BOOLEAN IRC_NICK( CLIENT *Client, REQUEST *Req )
186 {
187         assert( Client != NULL );
188         assert( Req != NULL );
189
190         /* Zumindest BitchX sendet NICK-USER in der falschen Reihenfolge. */
191 #ifndef STRICT_RFC
192         if( Client->type == CLIENT_UNKNOWN || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTUSER || Client->type == CLIENT_USER )
193 #else
194         if( Client->type == CLIENT_UNKNOWN || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_GOTNICK || Client->type == CLIENT_USER  )
195 #endif
196         {
197                 /* Falsche Anzahl Parameter? */
198                 if( Req->argc != 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
199
200                 /* Ist der Client "restricted"? */
201                 if( strchr( Client->modes, 'r' )) return IRC_WriteStrClient( Client, This_Server, ERR_RESTRICTED_MSG, Client_Nick( Client ));
202
203                 /* Wenn der Client zu seinem eigenen Nick wechseln will, so machen
204                  * wir nichts. So macht es das Original und mind. Snak hat probleme,
205                  * wenn wir es nicht so machen. Ob es so okay ist? Hm ... */
206 #ifndef STRICT_RFC
207                 if( strcasecmp( Client->nick, Req->argv[0] ) == 0 ) return CONNECTED;
208 #endif
209                 
210                 /* pruefen, ob Nick bereits vergeben */
211                 if( ! Client_CheckNick( Client, Req->argv[0] )) return CONNECTED;
212
213                 if( Client->type == CLIENT_USER )
214                 {
215                         /* Nick-Aenderung: allen mitteilen! */
216                         Log( LOG_INFO, "User \"%s!%s@%s\" changed nick: \"%s\" -> \"%s\".", Client->nick, Client->user, Client->host, Client->nick, Req->argv[0] );
217                         IRC_WriteStrRelated( Client, "NICK :%s", Req->argv[0] );
218                 }
219                 
220                 /* Client-Nick registrieren */
221                 strcpy( Client->nick, Req->argv[0] );
222
223                 if( Client->type != CLIENT_USER )
224                 {
225                         /* Neuer Client */
226                         Log( LOG_DEBUG, "Connection %d: got NICK command ...", Client->conn_id );
227                         if( Client->type == CLIENT_GOTUSER ) return Hello_User( Client );
228                         else Client->type = CLIENT_GOTNICK;
229                 }
230                 return CONNECTED;
231         }
232         else return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Nick( Client ));
233 } /* IRC_NICK */
234
235
236 GLOBAL BOOLEAN IRC_USER( CLIENT *Client, REQUEST *Req )
237 {
238         assert( Client != NULL );
239         assert( Req != NULL );
240
241 #ifndef STRICT_RFC
242         if( Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTPASS || Client->type == CLIENT_UNKNOWN )
243 #else
244         if( Client->type == CLIENT_GOTNICK || Client->type == CLIENT_GOTPASS )
245 #endif
246         {
247                 /* Falsche Anzahl Parameter? */
248                 if( Req->argc != 4 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
249
250                 strncpy( Client->user, Req->argv[0], CLIENT_USER_LEN );
251                 Client->user[CLIENT_USER_LEN - 1] = '\0';
252                 strncpy( Client->name, Req->argv[3], CLIENT_NAME_LEN );
253                 Client->name[CLIENT_NAME_LEN - 1] = '\0';
254
255                 Log( LOG_DEBUG, "Connection %d: got USER command ...", Client->conn_id );
256                 if( Client->type == CLIENT_GOTNICK ) return Hello_User( Client );
257                 else Client->type = CLIENT_GOTUSER;
258                 return CONNECTED;
259         }
260         else if( Client->type == CLIENT_USER || Client->type == CLIENT_SERVER || Client->type == CLIENT_SERVICE )
261         {
262                 return IRC_WriteStrClient( Client, This_Server, ERR_ALREADYREGISTRED_MSG, Client_Nick( Client ));
263         }
264         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Nick( Client ));
265 } /* IRC_USER */
266
267
268 GLOBAL BOOLEAN IRC_QUIT( CLIENT *Client, REQUEST *Req )
269 {
270         assert( Client != NULL );
271         assert( Req != NULL );
272
273         if( Client->type != CLIENT_SERVER && Client->type != CLIENT_SERVICE )
274         {
275                 /* Falsche Anzahl Parameter? */
276                 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
277
278                 Conn_Close( Client->conn_id, "Client wants to quit." );
279                 return DISCONNECTED;
280         }
281         else return IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Nick( Client ));
282 } /* IRC_QUIT */
283
284
285 GLOBAL BOOLEAN IRC_PING( CLIENT *Client, REQUEST *Req )
286 {
287         assert( Client != NULL );
288         assert( Req != NULL );
289
290         if( ! Check_Valid_User( Client )) return CONNECTED;
291
292         /* Falsche Anzahl Parameter? */
293         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Nick( Client ));
294         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
295
296         return IRC_WriteStrClient( Client, This_Server, "PONG %s :%s", Client_Nick( This_Server), Client_Nick( Client ));
297 } /* IRC_PING */
298
299
300 GLOBAL BOOLEAN IRC_PONG( CLIENT *Client, REQUEST *Req )
301 {
302         assert( Client != NULL );
303         assert( Req != NULL );
304
305         if( ! Check_Valid_User( Client )) return CONNECTED;
306
307         /* Falsche Anzahl Parameter? */
308         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOORIGIN_MSG, Client_Nick( Client ));
309         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
310
311         /* Der Connection-Timestamp wurde schon beim Lesen aus dem Socket
312          * aktualisiert, daher muss das hier nicht mehr gemacht werden. */
313
314         Log( LOG_DEBUG, "Connection %d: received PONG.", Client->conn_id );
315         return CONNECTED;
316 } /* IRC_PONG */
317
318
319 GLOBAL BOOLEAN IRC_MOTD( CLIENT *Client, REQUEST *Req )
320 {
321         assert( Client != NULL );
322         assert( Req != NULL );
323
324         if( ! Check_Valid_User( Client )) return CONNECTED;
325
326         /* Falsche Anzahl Parameter? */
327         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
328
329         return Show_MOTD( Client );
330 } /* IRC_MOTD */
331
332
333 GLOBAL BOOLEAN IRC_PRIVMSG( CLIENT *Client, REQUEST *Req )
334 {
335         CLIENT *to;
336         
337         assert( Client != NULL );
338         assert( Req != NULL );
339
340         if( ! Check_Valid_User( Client )) return CONNECTED;
341
342         /* Falsche Anzahl Parameter? */
343         if( Req->argc == 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NORECIPIENT_MSG, Client_Nick( Client ), Req->command );
344         if( Req->argc == 1 ) return IRC_WriteStrClient( Client, This_Server, ERR_NOTEXTTOSEND_MSG, Client_Nick( Client ));
345         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
346
347         to = Client_Search( Req->argv[0] );
348         if( to )
349         {
350                 /* Okay, Ziel ist ein User */
351                 if( Client->conn_id >= 0 ) Conn_UpdateIdle( Client->conn_id );
352                 return IRC_WriteStrClient( to, Client, "PRIVMSG %s :%s", to->nick, Req->argv[1] );
353         }
354
355         return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Nick( Client ), Req->argv[0] );
356 } /* IRC_PRIVMSG */
357
358
359 GLOBAL BOOLEAN IRC_NOTICE( CLIENT *Client, REQUEST *Req )
360 {
361         CLIENT *to;
362
363         assert( Client != NULL );
364         assert( Req != NULL );
365
366         if( ! Check_Valid_User( Client )) return CONNECTED;
367
368         /* Falsche Anzahl Parameter? */
369         if( Req->argc != 2 ) return CONNECTED;
370
371         to = Client_Search( Req->argv[0] );
372         if( to )
373         {
374                 /* Okay, Ziel ist ein User */
375                 return IRC_WriteStrClient( to, Client, "NOTICE %s :%s", to->nick, Req->argv[1] );
376         }
377
378         return CONNECTED;
379 } /* IRC_NOTICE */
380
381
382 GLOBAL BOOLEAN IRC_MODE( CLIENT *Client, REQUEST *Req )
383 {
384         CHAR x[2], new_modes[CLIENT_MODE_LEN], *ptr, *p;
385         BOOLEAN set, ok;
386         
387         assert( Client != NULL );
388         assert( Req != NULL );
389
390         if( ! Check_Valid_User( Client )) return CONNECTED;
391
392         /* Falsche Anzahl Parameter? */
393         if(( Req->argc > 2 ) || ( Req->argc < 1 )) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
394
395         /* MODE ist nur fuer sich selber zulaessig */
396         if( Client_Search( Req->argv[0] ) != Client ) return IRC_WriteStrClient( Client, This_Server, ERR_USERSDONTMATCH_MSG, Client_Nick( Client ));
397
398         /* Werden die Modes erfragt? */
399         if( Req->argc == 1 ) return IRC_WriteStrClient( Client, This_Server, RPL_UMODEIS_MSG, Client_Nick( Client ), Client->modes );
400
401         ptr = Req->argv[1];
402
403         /* Sollen Modes gesetzt oder geloescht werden? */
404         if( *ptr == '+' ) set = TRUE;
405         else if( *ptr == '-' ) set = FALSE;
406         else return IRC_WriteStrClient( Client, This_Server, ERR_UMODEUNKNOWNFLAG_MSG, Client_Nick( Client ));
407
408         /* Reply-String mit Aenderungen vorbereiten */
409         if( set ) strcpy( new_modes, "+" );
410         else strcpy( new_modes, "-" );
411
412         ptr++;
413         ok = TRUE;
414         x[1] = '\0';
415         while( *ptr )
416         {
417                 x[0] = '\0';
418                 switch( *ptr )
419                 {
420                         case 'i':
421                                 /* invisible */
422                                 x[0] = 'i';
423                                 break;
424                         case 'r':
425                                 /* restricted (kann nur gesetzt werden) */
426                                 if( set ) x[0] = 'r';
427                                 else ok = IRC_WriteStrClient( Client, This_Server, ERR_RESTRICTED_MSG, Client_Nick( Client ));
428                                 break;
429                         case 'o':
430                                 /* operator (kann nur geloescht werden) */
431                                 if( ! set )
432                                 {
433                                         Client->oper_by_me = FALSE;
434                                         x[0] = 'o';
435                                 }
436                                 else ok = IRC_WriteStrClient( Client, This_Server, ERR_UMODEUNKNOWNFLAG_MSG, Client_Nick( Client ));
437                                 break;
438                         default:
439                                 ok = IRC_WriteStrClient( Client, This_Server, ERR_UMODEUNKNOWNFLAG_MSG, Client_Nick( Client ));
440                                 x[0] = '\0';
441                 }
442                 if( ! ok ) break;
443
444                 ptr++;
445                 if( ! x[0] ) continue;
446
447                 /* Okay, gueltigen Mode gefunden */
448                 if( set )
449                 {
450                         /* Modes sollen gesetzt werden */
451                         if( ! strchr( Client->modes, x[0] ))
452                         {
453                                 /* Client hat den Mode noch nicht -> setzen */
454                                 strcat( Client->modes, x );
455                                 strcat( new_modes, x );
456                         }
457                 }
458                 else
459                 {
460                         /* Modes sollen geloescht werden */
461                         p = strchr( Client->modes, x[0] );
462                         if( p )
463                         {
464                                 /* Client hat den Mode -> loeschen */
465                                 while( *p )
466                                 {
467                                         *p = *(p + 1);
468                                         p++;
469                                 }
470                                 strcat( new_modes, x );
471                         }
472                 }
473         }
474         
475         /* Geanderte Modes? */
476         if( new_modes[1] && ok )
477         {
478                 ok = IRC_WriteStrRelated( Client, "MODE %s :%s", Client->nick, new_modes );
479                 Log( LOG_DEBUG, "User \"%s!%s@%s\": Mode change, now \"%s\".", Client->nick, Client->user, Client->host, Client->modes );
480         }
481         return ok;
482 } /* IRC_MODE */
483
484
485 GLOBAL BOOLEAN IRC_OPER( CLIENT *Client, REQUEST *Req )
486 {
487         INT i;
488         
489         assert( Client != NULL );
490         assert( Req != NULL );
491
492         if( ! Check_Valid_User( Client )) return CONNECTED;
493         
494         /* Falsche Anzahl Parameter? */
495         if( Req->argc != 2 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
496
497         /* Operator suchen */
498         for( i = 0; i < Conf_Oper_Count; i++)
499         {
500                 if( Conf_Oper[i].name[0] && Conf_Oper[i].pwd[0] && ( strcmp( Conf_Oper[i].name, Req->argv[0] ) == 0 )) break;
501         }
502         if( i >= Conf_Oper_Count ) return IRC_WriteStrClient( Client, This_Server, ERR_PASSWDMISMATCH_MSG, Client_Nick( Client ));
503
504         /* Stimmt der Name und das Passwort? */
505         if(( strcmp( Conf_Oper[i].name, Req->argv[0] ) != 0 ) || ( strcmp( Conf_Oper[i].pwd, Req->argv[1] ) != 0 )) return IRC_WriteStrClient( Client, This_Server, ERR_PASSWDMISMATCH_MSG, Client_Nick( Client ));
506         
507         if( ! strchr( Client->modes, 'o' ))
508         {
509                 /* noch kein o-Mode gesetzt */
510                 strcat( Client->modes, "o" );
511                 if( ! IRC_WriteStrRelated( Client, "MODE %s :+o", Client->nick )) return DISCONNECTED;
512         }
513
514         if( ! Client->oper_by_me ) Log( LOG_NOTICE, "User \"%s!%s@%s\" is now an IRC Operator.", Client->nick, Client->user, Client->host );
515
516         Client->oper_by_me = TRUE;
517         return IRC_WriteStrClient( Client, This_Server, RPL_YOUREOPER_MSG, Client_Nick( Client ));
518 } /* IRC_OPER */
519
520
521 GLOBAL BOOLEAN IRC_DIE( CLIENT *Client, REQUEST *Req )
522 {
523         assert( Client != NULL );
524         assert( Req != NULL );
525
526         if( ! Check_Valid_User( Client )) return CONNECTED;
527
528         /* Falsche Anzahl Parameter? */
529         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
530
531         if(( ! strchr( Client->modes, 'o' )) || ( ! Client->oper_by_me )) return IRC_WriteStrClient( Client, This_Server, ERR_NOPRIVILEGES_MSG, Client_Nick( Client ));
532
533         Log( LOG_NOTICE, "Got DIE command from \"%s!%s@%s\", going down!", Client->nick, Client->user, Client->host );
534         NGIRCd_Quit = TRUE;
535         return CONNECTED;
536 } /* IRC_DIE */
537
538
539 GLOBAL BOOLEAN IRC_RESTART( CLIENT *Client, REQUEST *Req )
540 {
541         assert( Client != NULL );
542         assert( Req != NULL );
543
544         if( ! Check_Valid_User( Client )) return CONNECTED;
545
546         /* Falsche Anzahl Parameter? */
547         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
548
549         if(( ! strchr( Client->modes, 'o' )) || ( ! Client->oper_by_me )) return IRC_WriteStrClient( Client, This_Server, ERR_NOPRIVILEGES_MSG, Client_Nick( Client ));
550
551         Log( LOG_NOTICE, "Got RESTART command from \"%s!%s@%s\", going down!", Client->nick, Client->user, Client->host );
552         NGIRCd_Restart = TRUE;
553         return CONNECTED;
554 } /* IRC_RESTART */
555
556
557 GLOBAL BOOLEAN IRC_NAMES( CLIENT *Client, REQUEST *Req )
558 {
559         CHAR rpl[COMMAND_LEN];
560         CLIENT *c;
561         
562         assert( Client != NULL );
563         assert( Req != NULL );
564
565         if( ! Check_Valid_User( Client )) return CONNECTED;
566
567         /* Falsche Anzahl Parameter? */
568         if( Req->argc != 0 ) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
569
570         /* Noch alle User ausgeben, die in keinem Channel sind */
571         rpl[0] = '\0';
572         c = Client_First( );
573         while( c )
574         {
575                 if( c->type == CLIENT_USER )
576                 {
577                         /* Okay, das ist ein User */
578                         strcat( rpl, Client_Nick( c ));
579                         strcat( rpl, " " );
580                 }
581
582                 /* Antwort zu lang? Splitten. */
583                 if( strlen( rpl ) > 480 )
584                 {
585                         if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0';
586                         if( ! IRC_WriteStrClient( Client, This_Server, RPL_NAMREPLY_MSG, Client_Nick( Client ), "*", "*", rpl )) return DISCONNECTED;
587                         rpl[0] = '\0';
588                 }
589                 
590                 c = Client_Next( c );
591         }
592         if( rpl[0] )
593         {
594                 /* es wurden User gefunden */
595                 if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0';
596                 if( ! IRC_WriteStrClient( Client, This_Server, RPL_NAMREPLY_MSG, Client_Nick( Client ), "*", "*", rpl )) return DISCONNECTED;
597         }
598         return IRC_WriteStrClient( Client, This_Server, RPL_ENDOFNAMES_MSG, Client_Nick( Client ), "*" );
599 } /* IRC_NAMES */
600
601
602 GLOBAL BOOLEAN IRC_ISON( CLIENT *Client, REQUEST *Req )
603 {
604         CHAR rpl[COMMAND_LEN];
605         CLIENT *c;
606         CHAR *ptr;
607         INT i;
608         
609         assert( Client != NULL );
610         assert( Req != NULL );
611
612         if( ! Check_Valid_User( Client )) return CONNECTED;
613
614         /* Falsche Anzahl Parameter? */
615         if(( Req->argc < 1 )) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
616
617         strcpy( rpl, RPL_ISON_MSG );
618         for( i = 0; i < Req->argc; i++ )
619         {
620                 ptr = strtok( Req->argv[i], " " );
621                 while( ptr )
622                 {
623                         ngt_TrimStr( ptr );
624                         c = Client_GetFromNick( ptr );
625                         if( c && ( c->type == CLIENT_USER ))
626                         {
627                                 /* Dieser Nick ist "online" */
628                                 strcat( rpl, ptr );
629                                 strcat( rpl, " " );
630                         }
631                         ptr = strtok( NULL, " " );
632                 }
633         }
634         if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0';
635
636         return IRC_WriteStrClient( Client, This_Server, rpl, Client->nick );
637 } /* IRC_ISON */
638
639
640 GLOBAL BOOLEAN IRC_WHOIS( CLIENT *Client, REQUEST *Req )
641 {
642         CLIENT *c;
643         
644         assert( Client != NULL );
645         assert( Req != NULL );
646
647         if( ! Check_Valid_User( Client )) return CONNECTED;
648
649         /* Falsche Anzahl Parameter? */
650         if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
651
652         /* Client suchen */
653         c = Client_GetFromNick( Req->argv[0] );
654         if(( ! c ) || ( c->type != CLIENT_USER )) return IRC_WriteStrClient( Client, This_Server, ERR_NOSUCHNICK_MSG, Client_Nick( Client ), Req->argv[0] );
655         
656         /* Nick, User und Name */
657         if( ! IRC_WriteStrClient( Client, This_Server, RPL_WHOISUSER_MSG, Client_Nick( Client ), c->nick, c->user, c->host, c->name )) return DISCONNECTED;
658
659         /* Server */
660         if( ! IRC_WriteStrClient( Client, This_Server, RPL_WHOISSERVER_MSG, Client_Nick( Client ), c->nick, c->introducer->nick, c->introducer->info )) return DISCONNECTED;
661
662         /* IRC-Operator? */
663         if( strchr( c->modes, 'o' ))
664         {
665                 if( ! IRC_WriteStrClient( Client, This_Server, RPL_WHOISOPERATOR_MSG, Client_Nick( Client ), c->nick )) return DISCONNECTED;
666         }
667
668         /* Idle (nur lokale Clients) */
669         if( c->conn_id >= 0 )
670         {
671                 if( ! IRC_WriteStrClient( Client, This_Server, RPL_WHOISIDLE_MSG, Client_Nick( Client ), c->nick, Conn_GetIdle( c->conn_id ))) return DISCONNECTED;
672         }
673
674         /* End of Whois */
675         return IRC_WriteStrClient( Client, This_Server, RPL_ENDOFWHOIS_MSG, Client_Nick( Client ), c->nick );
676 } /* IRC_WHOIS */
677
678
679 GLOBAL BOOLEAN IRC_USERHOST( CLIENT *Client, REQUEST *Req )
680 {
681         CHAR rpl[COMMAND_LEN];
682         CLIENT *c;
683         INT max, i;
684
685         assert( Client != NULL );
686         assert( Req != NULL );
687
688         if( ! Check_Valid_User( Client )) return CONNECTED;
689
690         /* Falsche Anzahl Parameter? */
691         if(( Req->argc < 1 )) return IRC_WriteStrClient( Client, This_Server, ERR_NEEDMOREPARAMS_MSG, Client_Nick( Client ), Req->command );
692
693         if( Req->argc > 5 ) max = 5;
694         else max = Req->argc;
695         
696         strcpy( rpl, RPL_USERHOST_MSG );
697         for( i = 0; i < max; i++ )
698         {
699                 c = Client_GetFromNick( Req->argv[i] );
700                 if( c && ( c->type == CLIENT_USER ))
701                 {
702                         /* Dieser Nick ist "online" */
703                         strcat( rpl, c->nick );
704                         if( strchr( c->modes, 'o' )) strcat( rpl, "*" );
705                         strcat( rpl, "=" );
706                         if( strchr( c->modes, 'a' )) strcat( rpl, "-" );
707                         else strcat( rpl, "+" );
708                         strcat( rpl, c->user );
709                         strcat( rpl, "@" );
710                         strcat( rpl, c->host );
711                         strcat( rpl, " " );
712                 }
713         }
714         if( rpl[strlen( rpl ) - 1] == ' ' ) rpl[strlen( rpl ) - 1] = '\0';
715
716         return IRC_WriteStrClient( Client, This_Server, rpl, Client->nick );
717 } /* IRC_USERHOST */
718
719
720 GLOBAL BOOLEAN IRC_ERROR( CLIENT *Client, REQUEST *Req )
721 {
722         assert( Client != NULL );
723         assert( Req != NULL );
724
725         if( Req->argc < 1 ) Log( LOG_NOTICE, "Got ERROR from \"%s!%s@%s\"!", Client_Nick( Client ), Client->user, Client->host );
726         else Log( LOG_NOTICE, "Got ERROR from \"%s!%s@%s\": %s!", Client_Nick( Client ), Client->user, Client->host, Req->argv[0] );
727
728         return CONNECTED;
729 } /* IRC_ERROR */
730
731
732 LOCAL BOOLEAN Check_Valid_User( CLIENT *Client )
733 {
734         assert( Client != NULL );
735
736         if( Client->type != CLIENT_USER )
737         {
738                 IRC_WriteStrClient( Client, This_Server, ERR_NOTREGISTERED_MSG, Client_Nick( Client ));
739                 return FALSE;
740         }
741         else return TRUE;
742 } /* Check_Valid_User */
743
744
745 LOCAL BOOLEAN Hello_User( CLIENT *Client )
746 {
747         assert( Client != NULL );
748         assert( Client->nick[0] );
749         
750         Log( LOG_NOTICE, "User \"%s!%s@%s\" (%s) registered (connection %d).", Client->nick, Client->user, Client->host, Client->name, Client->conn_id );
751
752         IRC_WriteStrClient( Client, This_Server, RPL_WELCOME_MSG, Client->nick, Client_GetID( Client ));
753         IRC_WriteStrClient( Client, This_Server, RPL_YOURHOST_MSG, Client->nick, This_Server->nick );
754         IRC_WriteStrClient( Client, This_Server, RPL_CREATED_MSG, Client->nick, NGIRCd_StartStr );
755         IRC_WriteStrClient( Client, This_Server, RPL_MYINFO_MSG, Client->nick, This_Server->nick );
756
757         Client->type = CLIENT_USER;
758
759         return Show_MOTD( Client );
760 } /* Hello_User */
761
762
763 LOCAL BOOLEAN Show_MOTD( CLIENT *Client )
764 {
765         BOOLEAN ok;
766         CHAR line[127];
767         FILE *fd;
768         
769         assert( Client != NULL );
770         assert( Client->nick[0] );
771
772         fd = fopen( Conf_MotdFile, "r" );
773         if( ! fd )
774         {
775                 Log( LOG_WARNING, "Can't read MOTD file \"%s\": %s", Conf_MotdFile, strerror( errno ));
776                 return IRC_WriteStrClient( Client, This_Server, ERR_NOMOTD_MSG, Client->nick );
777         }
778         
779         IRC_WriteStrClient( Client, This_Server, RPL_MOTDSTART_MSG, Client->nick, This_Server->nick );
780         while( TRUE )
781         {
782                 if( ! fgets( line, 126, fd )) break;
783                 if( line[strlen( line ) - 1] == '\n' ) line[strlen( line ) - 1] = '\0';
784                 IRC_WriteStrClient( Client, This_Server, RPL_MOTD_MSG, Client->nick, line );
785         }
786         ok = IRC_WriteStrClient( Client, This_Server, RPL_ENDOFMOTD_MSG, Client->nick );
787
788         fclose( fd );
789         
790         return ok;
791 } /* Show_MOTD */
792
793
794 /* -eof- */