]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conf.c
- If Conf_MaxConnections is "ulimited" (<1) it is limited to FD_SETSIZE.
[ngircd-alex.git] / src / ngircd / conf.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 Alexander Barton (alex@barton.de)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  *
11  * Configuration management (reading, parsing & validation)
12  */
13
14
15 #include "portab.h"
16
17 static char UNUSED id[] = "$Id: conf.c,v 1.46 2002/12/18 02:52:51 alex Exp $";
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <pwd.h>
28 #include <grp.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #ifdef HAVE_CTYPE_H
33 # include <ctype.h>
34 #endif
35
36 #include "ngircd.h"
37 #include "conn.h"
38 #include "client.h"
39 #include "defines.h"
40 #include "log.h"
41 #include "resolve.h"
42 #include "tool.h"
43
44 #include "exp.h"
45 #include "conf.h"
46
47
48 LOCAL BOOLEAN Use_Log = TRUE;
49
50
51 LOCAL VOID Set_Defaults PARAMS(( VOID ));
52 LOCAL VOID Read_Config PARAMS(( VOID ));
53 LOCAL VOID Validate_Config PARAMS(( BOOLEAN TestOnly ));
54
55 LOCAL VOID Handle_GLOBAL PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
56 LOCAL VOID Handle_OPERATOR PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
57 LOCAL VOID Handle_SERVER PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
58 LOCAL VOID Handle_CHANNEL PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
59
60 LOCAL VOID Config_Error PARAMS(( CONST INT Level, CONST CHAR *Format, ... ));
61
62
63 GLOBAL VOID
64 Conf_Init( VOID )
65 {
66         Set_Defaults( );
67         Read_Config( );
68         Validate_Config( FALSE );
69 } /* Config_Init */
70
71
72 GLOBAL INT
73 Conf_Test( VOID )
74 {
75         /* Read configuration, validate and output it. */
76
77         struct passwd *pwd;
78         struct group *grp;
79         INT i;
80
81         Use_Log = FALSE;
82         Set_Defaults( );
83
84         Read_Config( );
85         Validate_Config( TRUE );
86
87         /* If stdin is a valid tty wait for a key: */
88         if( isatty( fileno( stdout )))
89         {
90                 puts( "OK, press enter to see a dump of your service configuration ..." );
91                 getchar( );
92         }
93         else puts( "Ok, dump of your server configuration follows:\n" );
94
95         puts( "[GLOBAL]" );
96         printf( "  ServerName = %s\n", Conf_ServerName );
97         printf( "  ServerInfo = %s\n", Conf_ServerInfo );
98         printf( "  ServerPwd = %s\n", Conf_ServerPwd );
99         printf( "  AdminInfo1 = %s\n", Conf_ServerAdmin1 );
100         printf( "  AdminInfo2 = %s\n", Conf_ServerAdmin2 );
101         printf( "  AdminEMail = %s\n", Conf_ServerAdminMail );
102         printf( "  MotdFile = %s\n", Conf_MotdFile );
103         printf( "  Ports = " );
104         for( i = 0; i < Conf_ListenPorts_Count; i++ )
105         {
106                 if( i != 0 ) printf( ", " );
107                 printf( "%u", Conf_ListenPorts[i] );
108         }
109         puts( "" );
110         pwd = getpwuid( Conf_UID );
111         if( pwd ) printf( "  ServerUID = %s\n", pwd->pw_name );
112         else printf( "  ServerUID = %ld\n", (LONG)Conf_UID );
113         grp = getgrgid( Conf_GID );
114         if( grp ) printf( "  ServerGID = %s\n", grp->gr_name );
115         else printf( "  ServerGID = %ld\n", (LONG)Conf_GID );
116         printf( "  PingTimeout = %d\n", Conf_PingTimeout );
117         printf( "  PongTimeout = %d\n", Conf_PongTimeout );
118         printf( "  ConnectRetry = %d\n", Conf_ConnectRetry );
119         printf( "  OperCanUseMode = %s\n", Conf_OperCanMode == TRUE ? "yes" : "no" );
120         if( Conf_MaxConnections > 0 ) printf( "  MaxConnections = %ld\n", Conf_MaxConnections );
121         else printf( "  MaxConnections = -1\n" );
122         if( Conf_MaxJoins > 0 ) printf( "  MaxJoins = %d\n", Conf_MaxJoins );
123         else printf( "  MaxJoins = -1\n" );
124         puts( "" );
125
126         for( i = 0; i < Conf_Oper_Count; i++ )
127         {
128                 if( ! Conf_Oper[i].name[0] ) continue;
129                 
130                 /* Valid "Operator" section */
131                 puts( "[OPERATOR]" );
132                 printf( "  Name = %s\n", Conf_Oper[i].name );
133                 printf( "  Password = %s\n", Conf_Oper[i].pwd );
134                 puts( "" );
135         }
136
137         for( i = 0; i < Conf_Server_Count; i++ )
138         {
139                 if( ! Conf_Server[i].name[0] ) continue;
140                 
141                 /* Valid "Server" section */
142                 puts( "[SERVER]" );
143                 printf( "  Name = %s\n", Conf_Server[i].name );
144                 printf( "  Host = %s\n", Conf_Server[i].host );
145                 printf( "  Port = %d\n", Conf_Server[i].port );
146                 printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
147                 printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
148                 printf( "  Group = %d\n", Conf_Server[i].group );
149                 puts( "" );
150         }
151
152         for( i = 0; i < Conf_Channel_Count; i++ )
153         {
154                 if( ! Conf_Channel[i].name[0] ) continue;
155                 
156                 /* Valid "Channel" section */
157                 puts( "[CHANNEL]" );
158                 printf( "  Name = %s\n", Conf_Channel[i].name );
159                 printf( "  Modes = %s\n", Conf_Channel[i].modes );
160                 printf( "  Topic = %s\n", Conf_Channel[i].topic );
161                 puts( "" );
162         }
163         
164         return 0;
165 } /* Conf_Test */
166
167
168 LOCAL VOID
169 Set_Defaults( VOID )
170 {
171         /* Initialize configuration variables with default values. */
172
173         strcpy( Conf_ServerName, "" );
174         sprintf( Conf_ServerInfo, "%s %s", PACKAGE, VERSION );
175         strcpy( Conf_ServerPwd, "" );
176
177         strcpy( Conf_ServerAdmin1, "" );
178         strcpy( Conf_ServerAdmin2, "" );
179         strcpy( Conf_ServerAdminMail, "" );
180
181         strcpy( Conf_MotdFile, MOTD_FILE );
182
183         Conf_ListenPorts_Count = 0;
184
185         Conf_UID = Conf_GID = 0;
186         
187         Conf_PingTimeout = 120;
188         Conf_PongTimeout = 20;
189
190         Conf_ConnectRetry = 60;
191
192         Conf_Oper_Count = 0;
193         Conf_Server_Count = 0;
194         Conf_Channel_Count = 0;
195
196         Conf_OperCanMode = FALSE;
197         
198         Conf_MaxConnections = -1;
199         Conf_MaxJoins = 10;
200 } /* Set_Defaults */
201
202
203 LOCAL VOID
204 Read_Config( VOID )
205 {
206         /* Read configuration file. */
207
208         CHAR section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
209         INT line;
210         FILE *fd;
211
212         fd = fopen( NGIRCd_ConfFile, "r" );
213         if( ! fd )
214         {
215                 /* No configuration file found! */
216                 Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s", NGIRCd_ConfFile, strerror( errno ));
217                 Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
218                 exit( 1 );
219         }
220
221         Config_Error( LOG_INFO, "Reading configuration from \"%s\" ...", NGIRCd_ConfFile );
222
223         line = 0;
224         strcpy( section, "" );
225         while( TRUE )
226         {
227                 if( ! fgets( str, LINE_LEN, fd )) break;
228                 ngt_TrimStr( str );
229                 line++;
230
231                 /* Skip comments and empty lines */
232                 if( str[0] == ';' || str[0] == '#' || str[0] == '\0' ) continue;
233
234                 /* Is this the beginning of a new section? */
235                 if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' ))
236                 {
237                         strcpy( section, str );
238                         if( strcasecmp( section, "[GLOBAL]" ) == 0 ) continue;
239                         if( strcasecmp( section, "[OPERATOR]" ) == 0 )
240                         {
241                                 if( Conf_Oper_Count + 1 > MAX_OPERATORS ) Config_Error( LOG_ERR, "Too many operators configured." );
242                                 else
243                                 {
244                                         /* Initialize new operator structure */
245                                         strcpy( Conf_Oper[Conf_Oper_Count].name, "" );
246                                         strcpy( Conf_Oper[Conf_Oper_Count].pwd, "" );
247                                         Conf_Oper_Count++;
248                                 }
249                                 continue;
250                         }
251                         if( strcasecmp( section, "[SERVER]" ) == 0 )
252                         {
253                                 if( Conf_Server_Count + 1 > MAX_SERVERS ) Config_Error( LOG_ERR, "Too many servers configured." );
254                                 else
255                                 {
256                                         /* Initialize new server structure */
257                                         strcpy( Conf_Server[Conf_Server_Count].host, "" );
258                                         strcpy( Conf_Server[Conf_Server_Count].ip, "" );
259                                         strcpy( Conf_Server[Conf_Server_Count].name, "" );
260                                         strcpy( Conf_Server[Conf_Server_Count].pwd_in, "" );
261                                         strcpy( Conf_Server[Conf_Server_Count].pwd_out, "" );
262                                         Conf_Server[Conf_Server_Count].port = 0;
263                                         Conf_Server[Conf_Server_Count].group = -1;
264                                         Conf_Server[Conf_Server_Count].lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
265                                         Conf_Server[Conf_Server_Count].res_stat = NULL;
266                                         Conf_Server_Count++;
267                                 }
268                                 continue;
269                         }
270                         if( strcasecmp( section, "[CHANNEL]" ) == 0 )
271                         {
272                                 if( Conf_Channel_Count + 1 > MAX_DEFCHANNELS ) Config_Error( LOG_ERR, "Too many pre-defined channels configured." );
273                                 else
274                                 {
275                                         /* Initialize new channel structure */
276                                         strcpy( Conf_Channel[Conf_Channel_Count].name, "" );
277                                         strcpy( Conf_Channel[Conf_Channel_Count].modes, "" );
278                                         strcpy( Conf_Channel[Conf_Channel_Count].topic, "" );
279                                         Conf_Channel_Count++;
280                                 }
281                                 continue;
282                         }
283                         Config_Error( LOG_ERR, "%s, line %d: Unknown section \"%s\"!", NGIRCd_ConfFile, line, section );
284                         section[0] = 0x1;
285                 }
286                 if( section[0] == 0x1 ) continue;
287
288                 /* Split line into variable name and parameters */
289                 ptr = strchr( str, '=' );
290                 if( ! ptr )
291                 {
292                         Config_Error( LOG_ERR, "%s, line %d: Syntax error!", NGIRCd_ConfFile, line );
293                         continue;
294                 }
295                 *ptr = '\0';
296                 var = str; ngt_TrimStr( var );
297                 arg = ptr + 1; ngt_TrimStr( arg );
298
299                 if( strcasecmp( section, "[GLOBAL]" ) == 0 ) Handle_GLOBAL( line, var, arg );
300                 else if( strcasecmp( section, "[OPERATOR]" ) == 0 ) Handle_OPERATOR( line, var, arg );
301                 else if( strcasecmp( section, "[SERVER]" ) == 0 ) Handle_SERVER( line, var, arg );
302                 else if( strcasecmp( section, "[CHANNEL]" ) == 0 ) Handle_CHANNEL( line, var, arg );
303                 else Config_Error( LOG_ERR, "%s, line %d: Variable \"%s\" outside section!", NGIRCd_ConfFile, line, var );
304         }
305         
306         fclose( fd );
307         
308         /* If there are no ports configured use the default: 6667 */
309         if( Conf_ListenPorts_Count < 1 )
310         {
311                 Conf_ListenPorts_Count = 1;
312                 Conf_ListenPorts[0] = 6667;
313         }
314 } /* Read_Config */
315
316
317 LOCAL VOID
318 Handle_GLOBAL( INT Line, CHAR *Var, CHAR *Arg )
319 {
320         struct passwd *pwd;
321         struct group *grp;
322         CHAR *ptr;
323         LONG port;
324         
325         assert( Line > 0 );
326         assert( Var != NULL );
327         assert( Arg != NULL );
328         
329         if( strcasecmp( Var, "Name" ) == 0 )
330         {
331                 /* Server name */
332                 strncpy( Conf_ServerName, Arg, CLIENT_ID_LEN - 1 );
333                 Conf_ServerName[CLIENT_ID_LEN - 1] = '\0';
334                 if( strlen( Arg ) > CLIENT_ID_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
335                 return;
336         }
337         if( strcasecmp( Var, "Info" ) == 0 )
338         {
339                 /* Info text of server */
340                 strncpy( Conf_ServerInfo, Arg, CLIENT_INFO_LEN - 1 );
341                 Conf_ServerInfo[CLIENT_INFO_LEN - 1] = '\0';
342                 if( strlen( Arg ) > CLIENT_INFO_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Info\" too long!", NGIRCd_ConfFile, Line );
343                 return;
344         }
345         if( strcasecmp( Var, "Password" ) == 0 )
346         {
347                 /* Global server password */
348                 strncpy( Conf_ServerPwd, Arg, CLIENT_PASS_LEN - 1 );
349                 Conf_ServerPwd[CLIENT_PASS_LEN - 1] = '\0';
350                 if( strlen( Arg ) > CLIENT_PASS_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Password\" too long!", NGIRCd_ConfFile, Line );
351                 return;
352         }
353         if( strcasecmp( Var, "AdminInfo1" ) == 0 )
354         {
355                 /* Administrative info #1 */
356                 strncpy( Conf_ServerAdmin1, Arg, CLIENT_INFO_LEN - 1 );
357                 Conf_ServerAdmin1[CLIENT_INFO_LEN - 1] = '\0';
358                 if( strlen( Arg ) > CLIENT_INFO_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminInfo1\" too long!", NGIRCd_ConfFile, Line );
359                 return;
360         }
361         if( strcasecmp( Var, "AdminInfo2" ) == 0 )
362         {
363                 /* Administrative info #2 */
364                 strncpy( Conf_ServerAdmin2, Arg, CLIENT_INFO_LEN - 1 );
365                 Conf_ServerAdmin2[CLIENT_INFO_LEN - 1] = '\0';
366                 if( strlen( Arg ) > CLIENT_INFO_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminInfo2\" too long!", NGIRCd_ConfFile, Line );
367                 return;
368         }
369         if( strcasecmp( Var, "AdminEMail" ) == 0 )
370         {
371                 /* Administrative email contact */
372                 strncpy( Conf_ServerAdminMail, Arg, CLIENT_INFO_LEN - 1 );
373                 Conf_ServerAdminMail[CLIENT_INFO_LEN - 1] = '\0';
374                 if( strlen( Arg ) > CLIENT_INFO_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminEMail\" too long!", NGIRCd_ConfFile, Line );
375                 return;
376         }
377         if( strcasecmp( Var, "Ports" ) == 0 )
378         {
379                 /* Ports on that the server should listen. More port numbers
380                  * must be separated by "," */
381                 ptr = strtok( Arg, "," );
382                 while( ptr )
383                 {
384                         ngt_TrimStr( ptr );
385                         port = atol( ptr );
386                         if( Conf_ListenPorts_Count + 1 > MAX_LISTEN_PORTS ) Config_Error( LOG_ERR, "Too many listen ports configured. Port %ld ignored.", port );
387                         else
388                         {
389                                 if( port > 0 && port < 0xFFFF ) Conf_ListenPorts[Conf_ListenPorts_Count++] = (UINT)port;
390                                 else Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Illegal port number %ld!", NGIRCd_ConfFile, Line, port );
391                         }
392                         ptr = strtok( NULL, "," );
393                 }
394                 return;
395         }
396         if( strcasecmp( Var, "MotdFile" ) == 0 )
397         {
398                 /* "Message of the day" (MOTD) file */
399                 strncpy( Conf_MotdFile, Arg, FNAME_LEN - 1 );
400                 Conf_MotdFile[FNAME_LEN - 1] = '\0';
401                 if( strlen( Arg ) > FNAME_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MotdFile\" too long!", NGIRCd_ConfFile, Line );
402                 return;
403         }
404         if( strcasecmp( Var, "ServerUID" ) == 0 )
405         {
406                 /* UID the daemon should switch to */
407                 pwd = getpwnam( Arg );
408                 if( pwd ) Conf_UID = pwd->pw_uid;
409                 else
410                 {
411 #ifdef HAVE_ISDIGIT
412                         if( ! isdigit( *Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ServerUID\" is not a number!", NGIRCd_ConfFile, Line );
413                         else
414 #endif
415                         Conf_UID = (UINT)atoi( Arg );
416                 }
417                 return;
418         }
419         if( strcasecmp( Var, "ServerGID" ) == 0 )
420         {
421                 /* GID the daemon should use */
422                 grp = getgrnam( Arg );
423                 if( grp ) Conf_GID = grp->gr_gid;
424                 else
425                 {
426 #ifdef HAVE_ISDIGIT
427                         if( ! isdigit( *Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ServerGID\" is not a number!", NGIRCd_ConfFile, Line );
428                         else
429 #endif
430                         Conf_GID = (UINT)atoi( Arg );
431                 }
432                 return;
433         }
434         if( strcasecmp( Var, "PingTimeout" ) == 0 )
435         {
436                 /* PING timeout */
437                 Conf_PingTimeout = atoi( Arg );
438                 if( Conf_PingTimeout < 5 )
439                 {
440                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PingTimeout\" too low!", NGIRCd_ConfFile, Line );
441                         Conf_PingTimeout = 5;
442                 }
443                 return;
444         }
445         if( strcasecmp( Var, "PongTimeout" ) == 0 )
446         {
447                 /* PONG timeout */
448                 Conf_PongTimeout = atoi( Arg );
449                 if( Conf_PongTimeout < 5 )
450                 {
451                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PongTimeout\" too low!", NGIRCd_ConfFile, Line );
452                         Conf_PongTimeout = 5;
453                 }
454                 return;
455         }
456         if( strcasecmp( Var, "ConnectRetry" ) == 0 )
457         {
458                 /* Seconds between connection attempts to other servers */
459                 Conf_ConnectRetry = atoi( Arg );
460                 if( Conf_ConnectRetry < 5 )
461                 {
462                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"ConnectRetry\" too low!", NGIRCd_ConfFile, Line );
463                         Conf_ConnectRetry = 5;
464                 }
465                 return;
466         }
467         if( strcasecmp( Var, "OperCanUseMode" ) == 0 )
468         {
469                 /* Are IRC operators allowed to use MODE in channels they aren't Op in? */
470                 if( strcasecmp( Arg, "yes" ) == 0 ) Conf_OperCanMode = TRUE;
471                 else if( strcasecmp( Arg, "true" ) == 0 ) Conf_OperCanMode = TRUE;
472                 else if( atoi( Arg ) != 0 ) Conf_OperCanMode = TRUE;
473                 else Conf_OperCanMode = FALSE;
474                 return;
475         }
476         if( strcasecmp( Var, "MaxConnections" ) == 0 )
477         {
478                 /* Maximum number of connections. Values <= 0 are equal to "no limit". */
479 #ifdef HAVE_ISDIGIT
480                 if( ! isdigit( *Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MaxConnections\" is not a number!", NGIRCd_ConfFile, Line );
481                 else
482 #endif
483                 Conf_MaxConnections = atol( Arg );
484                 return;
485         }
486         if( strcasecmp( Var, "MaxJoins" ) == 0 )
487         {
488                 /* Maximum number of channels a user can join. Values <= 0 are equal to "no limit". */
489 #ifdef HAVE_ISDIGIT
490                 if( ! isdigit( *Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MaxJoins\" is not a number!", NGIRCd_ConfFile, Line );
491                 else
492 #endif
493                 Conf_MaxJoins = atoi( Arg );
494                 return;
495         }
496
497         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
498 } /* Handle_GLOBAL */
499
500
501 LOCAL VOID
502 Handle_OPERATOR( INT Line, CHAR *Var, CHAR *Arg )
503 {
504         assert( Line > 0 );
505         assert( Var != NULL );
506         assert( Arg != NULL );
507         assert( Conf_Oper_Count > 0 );
508
509         if( strcasecmp( Var, "Name" ) == 0 )
510         {
511                 /* Name of IRC operator */
512                 strncpy( Conf_Oper[Conf_Oper_Count - 1].name, Arg, CLIENT_PASS_LEN - 1 );
513                 Conf_Oper[Conf_Oper_Count - 1].name[CLIENT_PASS_LEN - 1] = '\0';
514                 if( strlen( Arg ) > CLIENT_PASS_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
515                 return;
516         }
517         if( strcasecmp( Var, "Password" ) == 0 )
518         {
519                 /* Password of IRC operator */
520                 strncpy( Conf_Oper[Conf_Oper_Count - 1].pwd, Arg, CLIENT_PASS_LEN - 1 );
521                 Conf_Oper[Conf_Oper_Count - 1].pwd[CLIENT_PASS_LEN - 1] = '\0';
522                 if( strlen( Arg ) > CLIENT_PASS_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Password\" too long!", NGIRCd_ConfFile, Line );
523                 return;
524         }
525         
526         Config_Error( LOG_ERR, "%s, line %d (section \"Operator\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
527 } /* Handle_OPERATOR */
528
529
530 LOCAL VOID
531 Handle_SERVER( INT Line, CHAR *Var, CHAR *Arg )
532 {
533         LONG port;
534         
535         assert( Line > 0 );
536         assert( Var != NULL );
537         assert( Arg != NULL );
538
539         if( strcasecmp( Var, "Host" ) == 0 )
540         {
541                 /* Hostname of the server */
542                 strncpy( Conf_Server[Conf_Server_Count - 1].host, Arg, HOST_LEN - 1 );
543                 Conf_Server[Conf_Server_Count - 1].host[HOST_LEN - 1] = '\0';
544                 if( strlen( Arg ) > HOST_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Host\" too long!", NGIRCd_ConfFile, Line );
545                 return;
546         }
547         if( strcasecmp( Var, "Name" ) == 0 )
548         {
549                 /* Name of the server ("Nick"/"ID") */
550                 strncpy( Conf_Server[Conf_Server_Count - 1].name, Arg, CLIENT_ID_LEN - 1 );
551                 Conf_Server[Conf_Server_Count - 1].name[CLIENT_ID_LEN - 1] = '\0';
552                 if( strlen( Arg ) > CLIENT_ID_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
553                 return;
554         }
555         if( strcasecmp( Var, "MyPassword" ) == 0 )
556         {
557                 /* Password of this server which is sent to the peer */
558                 strncpy( Conf_Server[Conf_Server_Count - 1].pwd_in, Arg, CLIENT_PASS_LEN - 1 );
559                 Conf_Server[Conf_Server_Count - 1].pwd_in[CLIENT_PASS_LEN - 1] = '\0';
560                 if( strlen( Arg ) > CLIENT_PASS_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MyPassword\" too long!", NGIRCd_ConfFile, Line );
561                 return;
562         }
563         if( strcasecmp( Var, "PeerPassword" ) == 0 )
564         {
565                 /* Passwort of the peer which must be received */
566                 strncpy( Conf_Server[Conf_Server_Count - 1].pwd_out, Arg, CLIENT_PASS_LEN - 1 );
567                 Conf_Server[Conf_Server_Count - 1].pwd_out[CLIENT_PASS_LEN - 1] = '\0';
568                 if( strlen( Arg ) > CLIENT_PASS_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"PeerPassword\" too long!", NGIRCd_ConfFile, Line );
569                 return;
570         }
571         if( strcasecmp( Var, "Port" ) == 0 )
572         {
573                 /* Port to which this server should connect */
574                 port = atol( Arg );
575                 if( port > 0 && port < 0xFFFF ) Conf_Server[Conf_Server_Count - 1].port = (INT)port;
576                 else Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Illegal port number %ld!", NGIRCd_ConfFile, Line, port );
577                 return;
578         }
579         if( strcasecmp( Var, "Group" ) == 0 )
580         {
581                 /* Server group */
582 #ifdef HAVE_ISDIGIT
583                 if( ! isdigit( *Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Group\" is not a number!", NGIRCd_ConfFile, Line );
584                 else
585 #endif
586                 Conf_Server[Conf_Server_Count - 1].group = atoi( Arg );
587                 return;
588         }
589         
590         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
591 } /* Handle_SERVER */
592
593
594 LOCAL VOID
595 Handle_CHANNEL( INT Line, CHAR *Var, CHAR *Arg )
596 {
597         assert( Line > 0 );
598         assert( Var != NULL );
599         assert( Arg != NULL );
600
601         if( strcasecmp( Var, "Name" ) == 0 )
602         {
603                 /* Name of the channel */
604                 strncpy( Conf_Channel[Conf_Channel_Count - 1].name, Arg, CHANNEL_NAME_LEN - 1 );
605                 Conf_Channel[Conf_Channel_Count - 1].name[CHANNEL_NAME_LEN - 1] = '\0';
606                 if( strlen( Arg ) > CHANNEL_NAME_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
607                 return;
608         }
609         if( strcasecmp( Var, "Modes" ) == 0 )
610         {
611                 /* Initial modes */
612                 strncpy( Conf_Channel[Conf_Channel_Count - 1].modes, Arg, CHANNEL_MODE_LEN - 1 );
613                 Conf_Channel[Conf_Channel_Count - 1].modes[CHANNEL_MODE_LEN - 1] = '\0';
614                 if( strlen( Arg ) > CHANNEL_MODE_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Modes\" too long!", NGIRCd_ConfFile, Line );
615                 return;
616         }
617         if( strcasecmp( Var, "Topic" ) == 0 )
618         {
619                 /* Initial topic */
620                 strncpy( Conf_Channel[Conf_Channel_Count - 1].topic, Arg, CHANNEL_TOPIC_LEN - 1 );
621                 Conf_Channel[Conf_Channel_Count - 1].topic[CHANNEL_TOPIC_LEN - 1] = '\0';
622                 if( strlen( Arg ) > CHANNEL_TOPIC_LEN - 1 ) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Topic\" too long!", NGIRCd_ConfFile, Line );
623                 return;
624         }
625
626         Config_Error( LOG_ERR, "%s, line %d (section \"Channel\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
627 } /* Handle_CHANNEL */
628
629
630 LOCAL VOID
631 Validate_Config( BOOLEAN Configtest )
632 {
633         /* Validate configuration settings. */
634         
635         INT i;
636         
637         if( ! Conf_ServerName[0] )
638         {
639                 /* No server name configured! */
640                 Config_Error( LOG_ALERT, "No server name configured in \"%s\" ('ServerName')!", NGIRCd_ConfFile );
641                 if( ! Configtest )
642                 {
643                         Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
644                         exit( 1 );
645                 }
646         }
647
648 #ifdef STRICT_RFC
649         if( ! Conf_ServerAdminMail[0] )
650         {
651                 /* No administrative contact configured! */
652                 Config_Error( LOG_ALERT, "No administrator email address configured in \"%s\" ('AdminEMail')!", NGIRCd_ConfFile );
653                 if( ! Configtest )
654                 {
655                         Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
656                         exit( 1 );
657                 }
658         }
659 #endif
660
661         if( ! Conf_ServerAdmin1[0] && ! Conf_ServerAdmin2[0] && ! Conf_ServerAdminMail[0] )
662         {
663                 /* No administrative information configured! */
664                 Config_Error( LOG_WARNING, "No administrative information configured but required by RFC!" );
665         }
666 #ifdef FD_SETSIZE       
667         if(( Conf_MaxConnections > (LONG)FD_SETSIZE ) || ( Conf_MaxConnections < 1 ))
668         {
669                 Conf_MaxConnections = (LONG)FD_SETSIZE;
670                 Config_Error( LOG_ERR, "Setting MaxConnections to %ld, select() can't handle more file descriptors!", Conf_MaxConnections );
671         }
672 #else
673         Config_Error( LOG_WARN, "Don't know how many file descriptors select() can handle on this system, don't set MaxConnections too high!" );
674 #endif
675 } /* Validate_Config */
676
677
678 #ifdef PROTOTYPES
679 LOCAL VOID Config_Error( CONST INT Level, CONST CHAR *Format, ... )
680 #else
681 LOCAL VOID Config_Error( Level, Format, va_alist )
682 CONST INT Level;
683 CONST CHAR *Format;
684 va_dcl
685 #endif
686 {
687         /* Error! Write to console and/or logfile. */
688
689         CHAR msg[MAX_LOG_MSG_LEN];
690         va_list ap;
691
692         assert( Format != NULL );
693
694 #ifdef PROTOTYPES
695         va_start( ap, Format );
696 #else
697         va_start( ap );
698 #endif
699         vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
700         va_end( ap );
701         
702         /* During "normal operations" the log functions of the daemon should
703          * be used, but during testing of the configuration file, all messages
704          * should go directly to the console: */
705         if( Use_Log ) Log( Level, "%s", msg );
706         else puts( msg );
707 } /* Config_Error */
708
709
710 /* -eof- */