New "chroot" feature (from Benjamin Pineau), introducing new configuration
[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.64 2004/05/07 11:19:21 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 <strings.h>
27 #include <unistd.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32
33 #ifdef HAVE_CTYPE_H
34 # include <ctype.h>
35 #endif
36
37 #include "ngircd.h"
38 #include "conn.h"
39 #include "client.h"
40 #include "defines.h"
41 #include "log.h"
42 #include "resolve.h"
43 #include "tool.h"
44
45 #include "exp.h"
46 #include "conf.h"
47
48
49 LOCAL BOOLEAN Use_Log = TRUE;
50 LOCAL CONF_SERVER New_Server;
51 LOCAL INT New_Server_Idx;
52
53
54 LOCAL VOID Set_Defaults PARAMS(( BOOLEAN InitServers ));
55 LOCAL VOID Read_Config PARAMS(( VOID ));
56 LOCAL VOID Validate_Config PARAMS(( BOOLEAN TestOnly ));
57
58 LOCAL VOID Handle_GLOBAL PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
59 LOCAL VOID Handle_OPERATOR PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
60 LOCAL VOID Handle_SERVER PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
61 LOCAL VOID Handle_CHANNEL PARAMS(( INT Line, CHAR *Var, CHAR *Arg ));
62
63 LOCAL VOID Config_Error PARAMS(( CONST INT Level, CONST CHAR *Format, ... ));
64
65 LOCAL VOID Init_Server_Struct PARAMS(( CONF_SERVER *Server ));
66
67
68 GLOBAL VOID
69 Conf_Init( VOID )
70 {
71         Set_Defaults( TRUE );
72         Read_Config( );
73         Validate_Config( FALSE );
74 } /* Config_Init */
75
76
77 GLOBAL VOID
78 Conf_Rehash( VOID )
79 {
80         Set_Defaults( FALSE );
81         Read_Config( );
82         Validate_Config( FALSE );
83 } /* Config_Rehash */
84
85
86 GLOBAL INT
87 Conf_Test( VOID )
88 {
89         /* Read configuration, validate and output it. */
90
91         struct passwd *pwd;
92         struct group *grp;
93         INT i;
94
95         Use_Log = FALSE;
96         Set_Defaults( TRUE );
97
98         Read_Config( );
99         Validate_Config( TRUE );
100
101         /* If stdin is a valid tty wait for a key: */
102         if( isatty( fileno( stdout )))
103         {
104                 puts( "OK, press enter to see a dump of your service configuration ..." );
105                 getchar( );
106         }
107         else puts( "Ok, dump of your server configuration follows:\n" );
108
109         puts( "[GLOBAL]" );
110         printf( "  ServerName = %s\n", Conf_ServerName );
111         printf( "  ServerInfo = %s\n", Conf_ServerInfo );
112         printf( "  Password = %s\n", Conf_ServerPwd );
113         printf( "  AdminInfo1 = %s\n", Conf_ServerAdmin1 );
114         printf( "  AdminInfo2 = %s\n", Conf_ServerAdmin2 );
115         printf( "  AdminEMail = %s\n", Conf_ServerAdminMail );
116         printf( "  MotdFile = %s\n", Conf_MotdFile );
117         printf( "  MotdPhrase = %s\n", Conf_MotdPhrase );
118         printf( "  ChrootDir= %s\n", Conf_Chroot );
119         printf( "  Ports = " );
120         for( i = 0; i < Conf_ListenPorts_Count; i++ )
121         {
122                 if( i != 0 ) printf( ", " );
123                 printf( "%u", Conf_ListenPorts[i] );
124         }
125         puts( "" );
126         printf( "  Listen = %s\n", Conf_ListenAddress );
127         pwd = getpwuid( Conf_UID );
128         if( pwd ) printf( "  ServerUID = %s\n", pwd->pw_name );
129         else printf( "  ServerUID = %ld\n", (LONG)Conf_UID );
130         grp = getgrgid( Conf_GID );
131         if( grp ) printf( "  ServerGID = %s\n", grp->gr_name );
132         else printf( "  ServerGID = %ld\n", (LONG)Conf_GID );
133         printf( "  PingTimeout = %d\n", Conf_PingTimeout );
134         printf( "  PongTimeout = %d\n", Conf_PongTimeout );
135         printf( "  ConnectRetry = %d\n", Conf_ConnectRetry );
136         printf( "  OperCanUseMode = %s\n", Conf_OperCanMode == TRUE ? "yes" : "no" );
137         if( Conf_MaxConnections > 0 ) printf( "  MaxConnections = %ld\n", Conf_MaxConnections );
138         else printf( "  MaxConnections = -1\n" );
139         if( Conf_MaxConnectionsIP > 0 ) printf( "  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP );
140         else printf( "  MaxConnectionsIP = -1\n" );
141         if( Conf_MaxJoins > 0 ) printf( "  MaxJoins = %d\n", Conf_MaxJoins );
142         else printf( "  MaxJoins = -1\n" );
143         puts( "" );
144
145         for( i = 0; i < Conf_Oper_Count; i++ )
146         {
147                 if( ! Conf_Oper[i].name[0] ) continue;
148                 
149                 /* Valid "Operator" section */
150                 puts( "[OPERATOR]" );
151                 printf( "  Name = %s\n", Conf_Oper[i].name );
152                 printf( "  Password = %s\n", Conf_Oper[i].pwd );
153                 puts( "" );
154         }
155
156         for( i = 0; i < MAX_SERVERS; i++ )
157         {
158                 if( ! Conf_Server[i].name[0] ) continue;
159                 
160                 /* Valid "Server" section */
161                 puts( "[SERVER]" );
162                 printf( "  Name = %s\n", Conf_Server[i].name );
163                 printf( "  Host = %s\n", Conf_Server[i].host );
164                 printf( "  Port = %d\n", Conf_Server[i].port );
165                 printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
166                 printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
167                 printf( "  Group = %d\n", Conf_Server[i].group );
168                 puts( "" );
169         }
170
171         for( i = 0; i < Conf_Channel_Count; i++ )
172         {
173                 if( ! Conf_Channel[i].name[0] ) continue;
174                 
175                 /* Valid "Channel" section */
176                 puts( "[CHANNEL]" );
177                 printf( "  Name = %s\n", Conf_Channel[i].name );
178                 printf( "  Modes = %s\n", Conf_Channel[i].modes );
179                 printf( "  Topic = %s\n", Conf_Channel[i].topic );
180                 puts( "" );
181         }
182         
183         return 0;
184 } /* Conf_Test */
185
186
187 GLOBAL VOID
188 Conf_UnsetServer( CONN_ID Idx )
189 {
190         /* Set next time for next connection attempt, if this is a server
191          * link that is (still) configured here. If the server is set as
192          * "once", delete it from our configuration.
193          * Non-Server-Connections will be silently ignored. */
194
195         INT i;
196
197         /* Check all our configured servers */
198         for( i = 0; i < MAX_SERVERS; i++ )
199         {
200                 if( Conf_Server[i].conn_id != Idx ) continue;
201
202                 /* Gotcha! Mark server configuration as "unused": */
203                 Conf_Server[i].conn_id = NONE;
204
205                 if( Conf_Server[i].flags & CONF_SFLAG_ONCE )
206                 {
207                         /* Delete configuration here */
208                         Init_Server_Struct( &Conf_Server[i] );
209                 }
210                 else
211                 {
212                         /* Set time for next connect attempt */
213                         if( Conf_Server[i].lasttry <  time( NULL ) - Conf_ConnectRetry )
214                         {
215                                 /* Okay, the connection was established "long enough": */
216                                 Conf_Server[i].lasttry = time( NULL ) - Conf_ConnectRetry + RECONNECT_DELAY;
217                         }
218                 }
219         }
220 } /* Conf_UnsetServer */
221
222
223 GLOBAL VOID
224 Conf_SetServer( INT ConfServer, CONN_ID Idx )
225 {
226         /* Set connection for specified configured server */
227
228         assert( ConfServer > NONE );
229         assert( Idx > NONE );
230
231         Conf_Server[ConfServer].conn_id = Idx;
232 } /* Conf_SetServer */
233
234
235 GLOBAL INT
236 Conf_GetServer( CONN_ID Idx )
237 {
238         /* Get index of server in configuration structure */
239         
240         INT i = 0;
241         
242         assert( Idx > NONE );
243
244         for( i = 0; i < MAX_SERVERS; i++ )
245         {
246                 if( Conf_Server[i].conn_id == Idx ) return i;
247         }
248         return NONE;
249 } /* Conf_GetServer */
250
251
252 GLOBAL BOOLEAN
253 Conf_EnableServer( CHAR *Name, INT Port )
254 {
255         /* Enable specified server and adjust port */
256
257         INT i;
258
259         assert( Name != NULL );
260
261         for( i = 0; i < MAX_SERVERS; i++ )
262         {
263                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 )
264                 {
265                         /* Gotcha! Set port and enable server: */
266                         Conf_Server[i].port = Port;
267                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
268                         return TRUE;
269                 }
270         }
271         return FALSE;
272 } /* Conf_EnableServer */
273
274
275 GLOBAL BOOLEAN
276 Conf_DisableServer( CHAR *Name )
277 {
278         /* Enable specified server and adjust port */
279
280         INT i;
281
282         assert( Name != NULL );
283
284         for( i = 0; i < MAX_SERVERS; i++ )
285         {
286                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 )
287                 {
288                         /* Gotcha! Disable and disconnect server: */
289                         Conf_Server[i].flags |= CONF_SFLAG_DISABLED;
290                         if( Conf_Server[i].conn_id > NONE ) Conn_Close( Conf_Server[i].conn_id, NULL, "Server link terminated on operator request", TRUE );
291                         return TRUE;
292                 }
293         }
294         return FALSE;
295 } /* Conf_DisableServer */
296
297
298 GLOBAL BOOLEAN
299 Conf_AddServer( CHAR *Name, INT Port, CHAR *Host, CHAR *MyPwd, CHAR *PeerPwd )
300 {
301         /* Add new server to configuration */
302
303         INT i;
304
305         assert( Name != NULL );
306         assert( Host != NULL );
307         assert( MyPwd != NULL );
308         assert( PeerPwd != NULL );
309
310         /* Search unused item in server configuration structure */
311         for( i = 0; i < MAX_SERVERS; i++ )
312         {
313                 /* Is this item used? */
314                 if( ! Conf_Server[i].name[0] ) break;
315         }
316         if( i >= MAX_SERVERS ) return FALSE;
317
318         Init_Server_Struct( &Conf_Server[i] );
319         strlcpy( Conf_Server[i].name, Name, sizeof( Conf_Server[i].name ));
320         strlcpy( Conf_Server[i].host, Host, sizeof( Conf_Server[i].host ));
321         strlcpy( Conf_Server[i].pwd_out, MyPwd, sizeof( Conf_Server[i].pwd_out ));
322         strlcpy( Conf_Server[i].pwd_in, PeerPwd, sizeof( Conf_Server[i].pwd_in ));
323         Conf_Server[i].port = Port;
324         Conf_Server[i].flags = CONF_SFLAG_ONCE;
325         
326         return TRUE;
327 } /* Conf_AddServer */
328
329
330 LOCAL VOID
331 Set_Defaults( BOOLEAN InitServers )
332 {
333         /* Initialize configuration variables with default values. */
334
335         INT i;
336
337         strcpy( Conf_ServerName, "" );
338         sprintf( Conf_ServerInfo, "%s %s", PACKAGE_NAME, PACKAGE_VERSION );
339         strcpy( Conf_ServerPwd, "" );
340
341         strcpy( Conf_ServerAdmin1, "" );
342         strcpy( Conf_ServerAdmin2, "" );
343         strcpy( Conf_ServerAdminMail, "" );
344
345         strlcpy( Conf_MotdFile, SYSCONFDIR, sizeof( Conf_MotdFile ));
346         strlcat( Conf_MotdFile, MOTD_FILE, sizeof( Conf_MotdFile ));
347
348         strlcpy( Conf_MotdPhrase, MOTD_PHRASE, sizeof( Conf_MotdPhrase ));
349
350         strlcpy( Conf_Chroot, CHROOT_DIR, sizeof( Conf_Chroot ));
351
352         Conf_ListenPorts_Count = 0;
353         strcpy( Conf_ListenAddress, "" );
354
355         Conf_UID = Conf_GID = 0;
356         
357         Conf_PingTimeout = 120;
358         Conf_PongTimeout = 20;
359
360         Conf_ConnectRetry = 60;
361
362         Conf_Oper_Count = 0;
363         Conf_Channel_Count = 0;
364
365         Conf_OperCanMode = FALSE;
366         
367         Conf_MaxConnections = -1;
368         Conf_MaxConnectionsIP = 5;
369         Conf_MaxJoins = 10;
370
371         /* Initialize server configuration structures */
372         if( InitServers ) for( i = 0; i < MAX_SERVERS; Init_Server_Struct( &Conf_Server[i++] ));
373 } /* Set_Defaults */
374
375
376 LOCAL VOID
377 Read_Config( VOID )
378 {
379         /* Read configuration file. */
380
381         CHAR section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
382         INT line, i, n;
383         FILE *fd;
384
385         /* Open configuration file */
386         fd = fopen( NGIRCd_ConfFile, "r" );
387         if( ! fd )
388         {
389                 /* No configuration file found! */
390                 Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s", NGIRCd_ConfFile, strerror( errno ));
391                 Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
392                 exit( 1 );
393         }
394
395         Config_Error( LOG_INFO, "Reading configuration from \"%s\" ...", NGIRCd_ConfFile );
396
397         /* Clean up server configuration structure: mark all already
398          * configured servers as "once" so that they are deleted
399          * after the next disconnect and delete all unused servers.
400          * And delete all servers which are "duplicates" of servers
401          * that are already marked as "once" (such servers have been
402          * created by the last rehash but are now useless). */
403         for( i = 0; i < MAX_SERVERS; i++ )
404         {
405                 if( Conf_Server[i].conn_id == NONE ) Init_Server_Struct( &Conf_Server[i] );
406                 else
407                 {
408                         /* This structure is in use ... */
409                         if( Conf_Server[i].flags & CONF_SFLAG_ONCE )
410                         {
411                                 /* Check for duplicates */
412                                 for( n = 0; n < MAX_SERVERS; n++ )
413                                 {
414                                         if( n == i ) continue;
415
416                                         if( Conf_Server[i].conn_id == Conf_Server[n].conn_id )
417                                         {
418                                                 Init_Server_Struct( &Conf_Server[n] );
419                                                 Log( LOG_DEBUG, "Deleted unused duplicate server %d (kept %d).", n, i );
420                                         }
421                                 }
422                         }
423                         else
424                         {
425                                 /* Mark server as "once" */
426                                 Conf_Server[i].flags |= CONF_SFLAG_ONCE;
427                                 Log( LOG_DEBUG, "Marked server %d as \"once\"", i );
428                         }
429                 }
430         }
431
432         /* Initialize variables */
433         line = 0;
434         strcpy( section, "" );
435         Init_Server_Struct( &New_Server );
436         New_Server_Idx = NONE;
437
438         /* Read configuration file */
439         while( TRUE )
440         {
441                 if( ! fgets( str, LINE_LEN, fd )) break;
442                 ngt_TrimStr( str );
443                 line++;
444
445                 /* Skip comments and empty lines */
446                 if( str[0] == ';' || str[0] == '#' || str[0] == '\0' ) continue;
447
448                 /* Is this the beginning of a new section? */
449                 if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' ))
450                 {
451                         strlcpy( section, str, sizeof( section ));
452                         if( strcasecmp( section, "[GLOBAL]" ) == 0 ) continue;
453                         if( strcasecmp( section, "[OPERATOR]" ) == 0 )
454                         {
455                                 if( Conf_Oper_Count + 1 > MAX_OPERATORS ) Config_Error( LOG_ERR, "Too many operators configured." );
456                                 else
457                                 {
458                                         /* Initialize new operator structure */
459                                         strcpy( Conf_Oper[Conf_Oper_Count].name, "" );
460                                         strcpy( Conf_Oper[Conf_Oper_Count].pwd, "" );
461                                         Conf_Oper_Count++;
462                                 }
463                                 continue;
464                         }
465                         if( strcasecmp( section, "[SERVER]" ) == 0 )
466                         {
467                                 /* Check if there is already a server to add */
468                                 if( New_Server.name[0] )
469                                 {
470                                         /* Copy data to "real" server structure */
471                                         assert( New_Server_Idx > NONE );
472                                         Conf_Server[New_Server_Idx] = New_Server;
473                                 }
474
475                                 /* Re-init structure for new server */
476                                 Init_Server_Struct( &New_Server );
477
478                                 /* Search unused item in server configuration structure */
479                                 for( i = 0; i < MAX_SERVERS; i++ )
480                                 {
481                                         /* Is this item used? */
482                                         if( ! Conf_Server[i].name[0] ) break;
483                                 }
484                                 if( i >= MAX_SERVERS )
485                                 {
486                                         /* Oops, no free item found! */
487                                         Config_Error( LOG_ERR, "Too many servers configured." );
488                                         New_Server_Idx = NONE;
489                                 }
490                                 else New_Server_Idx = i;
491                                 continue;
492                         }
493                         if( strcasecmp( section, "[CHANNEL]" ) == 0 )
494                         {
495                                 if( Conf_Channel_Count + 1 > MAX_DEFCHANNELS ) Config_Error( LOG_ERR, "Too many pre-defined channels configured." );
496                                 else
497                                 {
498                                         /* Initialize new channel structure */
499                                         strcpy( Conf_Channel[Conf_Channel_Count].name, "" );
500                                         strcpy( Conf_Channel[Conf_Channel_Count].modes, "" );
501                                         strcpy( Conf_Channel[Conf_Channel_Count].topic, "" );
502                                         Conf_Channel_Count++;
503                                 }
504                                 continue;
505                         }
506                         Config_Error( LOG_ERR, "%s, line %d: Unknown section \"%s\"!", NGIRCd_ConfFile, line, section );
507                         section[0] = 0x1;
508                 }
509                 if( section[0] == 0x1 ) continue;
510
511                 /* Split line into variable name and parameters */
512                 ptr = strchr( str, '=' );
513                 if( ! ptr )
514                 {
515                         Config_Error( LOG_ERR, "%s, line %d: Syntax error!", NGIRCd_ConfFile, line );
516                         continue;
517                 }
518                 *ptr = '\0';
519                 var = str; ngt_TrimStr( var );
520                 arg = ptr + 1; ngt_TrimStr( arg );
521
522                 if( strcasecmp( section, "[GLOBAL]" ) == 0 ) Handle_GLOBAL( line, var, arg );
523                 else if( strcasecmp( section, "[OPERATOR]" ) == 0 ) Handle_OPERATOR( line, var, arg );
524                 else if( strcasecmp( section, "[SERVER]" ) == 0 ) Handle_SERVER( line, var, arg );
525                 else if( strcasecmp( section, "[CHANNEL]" ) == 0 ) Handle_CHANNEL( line, var, arg );
526                 else Config_Error( LOG_ERR, "%s, line %d: Variable \"%s\" outside section!", NGIRCd_ConfFile, line, var );
527         }
528
529         /* Close configuration file */
530         fclose( fd );
531
532         /* Check if there is still a server to add */
533         if( New_Server.name[0] )
534         {
535                 /* Copy data to "real" server structure */
536                 assert( New_Server_Idx > NONE );
537                 Conf_Server[New_Server_Idx] = New_Server;
538         }
539         
540         /* If there are no ports configured use the default: 6667 */
541         if( Conf_ListenPorts_Count < 1 )
542         {
543                 Conf_ListenPorts_Count = 1;
544                 Conf_ListenPorts[0] = 6667;
545         }
546 } /* Read_Config */
547
548
549 LOCAL VOID
550 Handle_GLOBAL( INT Line, CHAR *Var, CHAR *Arg )
551 {
552         struct passwd *pwd;
553         struct group *grp;
554         CHAR *ptr;
555         LONG port;
556         
557         assert( Line > 0 );
558         assert( Var != NULL );
559         assert( Arg != NULL );
560         
561         if( strcasecmp( Var, "Name" ) == 0 )
562         {
563                 /* Server name */
564                 if( strlcpy( Conf_ServerName, Arg, sizeof( Conf_ServerName )) >= sizeof( Conf_ServerName )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
565                 return;
566         }
567         if( strcasecmp( Var, "Info" ) == 0 )
568         {
569                 /* Info text of server */
570                 if( strlcpy( Conf_ServerInfo, Arg, sizeof( Conf_ServerInfo )) >= sizeof( Conf_ServerInfo )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Info\" too long!", NGIRCd_ConfFile, Line );
571                 return;
572         }
573         if( strcasecmp( Var, "Password" ) == 0 )
574         {
575                 /* Global server password */
576                 if( strlcpy( Conf_ServerPwd, Arg, sizeof( Conf_ServerPwd )) >= sizeof( Conf_ServerPwd )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Password\" too long!", NGIRCd_ConfFile, Line );
577                 return;
578         }
579         if( strcasecmp( Var, "AdminInfo1" ) == 0 )
580         {
581                 /* Administrative info #1 */
582                 if( strlcpy( Conf_ServerAdmin1, Arg, sizeof( Conf_ServerAdmin1 )) >= sizeof( Conf_ServerAdmin1 )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminInfo1\" too long!", NGIRCd_ConfFile, Line );
583                 return;
584         }
585         if( strcasecmp( Var, "AdminInfo2" ) == 0 )
586         {
587                 /* Administrative info #2 */
588                 if( strlcpy( Conf_ServerAdmin2, Arg, sizeof( Conf_ServerAdmin2 )) >= sizeof( Conf_ServerAdmin2 )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminInfo2\" too long!", NGIRCd_ConfFile, Line );
589                 return;
590         }
591         if( strcasecmp( Var, "AdminEMail" ) == 0 )
592         {
593                 /* Administrative email contact */
594                 if( strlcpy( Conf_ServerAdminMail, Arg, sizeof( Conf_ServerAdminMail )) >= sizeof( Conf_ServerAdminMail )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"AdminEMail\" too long!", NGIRCd_ConfFile, Line );
595                 return;
596         }
597         if( strcasecmp( Var, "Ports" ) == 0 )
598         {
599                 /* Ports on that the server should listen. More port numbers
600                  * must be separated by "," */
601                 ptr = strtok( Arg, "," );
602                 while( ptr )
603                 {
604                         ngt_TrimStr( ptr );
605                         port = atol( ptr );
606                         if( Conf_ListenPorts_Count + 1 > MAX_LISTEN_PORTS ) Config_Error( LOG_ERR, "Too many listen ports configured. Port %ld ignored.", port );
607                         else
608                         {
609                                 if( port > 0 && port < 0xFFFF ) Conf_ListenPorts[Conf_ListenPorts_Count++] = (UINT)port;
610                                 else Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Illegal port number %ld!", NGIRCd_ConfFile, Line, port );
611                         }
612                         ptr = strtok( NULL, "," );
613                 }
614                 return;
615         }
616         if( strcasecmp( Var, "MotdFile" ) == 0 )
617         {
618                 /* "Message of the day" (MOTD) file */
619                 if( strlcpy( Conf_MotdFile, Arg, sizeof( Conf_MotdFile )) >= sizeof( Conf_MotdFile )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MotdFile\" too long!", NGIRCd_ConfFile, Line );
620                 return;
621         }
622         if( strcasecmp( Var, "MotdPhrase" ) == 0 )
623         {
624                 /* "Message of the day" phrase (instead of file) */
625                 if( strlcpy( Conf_MotdPhrase, Arg, sizeof( Conf_MotdPhrase )) >= sizeof( Conf_MotdPhrase )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MotdPhrase\" too long!", NGIRCd_ConfFile, Line );
626                 return;
627         }
628         if( strcasecmp( Var, "ChrootDir" ) == 0 )
629         {
630                 /* directory for chroot() */
631                 if( strlcpy( Conf_Chroot, Arg, sizeof( Conf_Chroot )) >= sizeof( Conf_Chroot )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ChrootDir\" too long!", NGIRCd_ConfFile, Line );
632                 return;
633         }
634         if( strcasecmp( Var, "ServerUID" ) == 0 )
635         {
636                 /* UID the daemon should switch to */
637                 pwd = getpwnam( Arg );
638                 if( pwd ) Conf_UID = pwd->pw_uid;
639                 else
640                 {
641 #ifdef HAVE_ISDIGIT
642                         if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ServerUID\" is not a number!", NGIRCd_ConfFile, Line );
643                         else
644 #endif
645                         Conf_UID = (UINT)atoi( Arg );
646                 }
647                 return;
648         }
649         if( strcasecmp( Var, "ServerGID" ) == 0 )
650         {
651                 /* GID the daemon should use */
652                 grp = getgrnam( Arg );
653                 if( grp ) Conf_GID = grp->gr_gid;
654                 else
655                 {
656 #ifdef HAVE_ISDIGIT
657                         if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ServerGID\" is not a number!", NGIRCd_ConfFile, Line );
658                         else
659 #endif
660                         Conf_GID = (UINT)atoi( Arg );
661                 }
662                 return;
663         }
664         if( strcasecmp( Var, "PingTimeout" ) == 0 )
665         {
666                 /* PING timeout */
667                 Conf_PingTimeout = atoi( Arg );
668                 if( Conf_PingTimeout < 5 )
669                 {
670                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PingTimeout\" too low!", NGIRCd_ConfFile, Line );
671                         Conf_PingTimeout = 5;
672                 }
673                 return;
674         }
675         if( strcasecmp( Var, "PongTimeout" ) == 0 )
676         {
677                 /* PONG timeout */
678                 Conf_PongTimeout = atoi( Arg );
679                 if( Conf_PongTimeout < 5 )
680                 {
681                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PongTimeout\" too low!", NGIRCd_ConfFile, Line );
682                         Conf_PongTimeout = 5;
683                 }
684                 return;
685         }
686         if( strcasecmp( Var, "ConnectRetry" ) == 0 )
687         {
688                 /* Seconds between connection attempts to other servers */
689                 Conf_ConnectRetry = atoi( Arg );
690                 if( Conf_ConnectRetry < 5 )
691                 {
692                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"ConnectRetry\" too low!", NGIRCd_ConfFile, Line );
693                         Conf_ConnectRetry = 5;
694                 }
695                 return;
696         }
697         if( strcasecmp( Var, "OperCanUseMode" ) == 0 )
698         {
699                 /* Are IRC operators allowed to use MODE in channels they aren't Op in? */
700                 if( strcasecmp( Arg, "yes" ) == 0 ) Conf_OperCanMode = TRUE;
701                 else if( strcasecmp( Arg, "true" ) == 0 ) Conf_OperCanMode = TRUE;
702                 else if( atoi( Arg ) != 0 ) Conf_OperCanMode = TRUE;
703                 else Conf_OperCanMode = FALSE;
704                 return;
705         }
706         if( strcasecmp( Var, "MaxConnections" ) == 0 )
707         {
708                 /* Maximum number of connections. Values <= 0 are equal to "no limit". */
709 #ifdef HAVE_ISDIGIT
710                 if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MaxConnections\" is not a number!", NGIRCd_ConfFile, Line );
711                 else
712 #endif
713                 Conf_MaxConnections = atol( Arg );
714                 return;
715         }
716         if( strcasecmp( Var, "MaxConnectionsIP" ) == 0 )
717         {
718                 /* Maximum number of simoultanous connections from one IP. Values <= 0 are equal to "no limit". */
719 #ifdef HAVE_ISDIGIT
720                 if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MaxConnectionsIP\" is not a number!", NGIRCd_ConfFile, Line );
721                 else
722 #endif
723                 Conf_MaxConnectionsIP = atoi( Arg );
724                 return;
725         }
726         if( strcasecmp( Var, "MaxJoins" ) == 0 )
727         {
728                 /* Maximum number of channels a user can join. Values <= 0 are equal to "no limit". */
729 #ifdef HAVE_ISDIGIT
730                 if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MaxJoins\" is not a number!", NGIRCd_ConfFile, Line );
731                 else
732 #endif
733                 Conf_MaxJoins = atoi( Arg );
734                 return;
735         }
736         if( strcasecmp( Var, "Listen" ) == 0 )
737         {
738                 /* IP-Address to bind sockets */
739                 if( strlcpy( Conf_ListenAddress, Arg, sizeof( Conf_ListenAddress )) >= sizeof( Conf_ListenAddress ))
740                 {
741                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"Listen\" too long!", NGIRCd_ConfFile, Line );
742                 }
743                 return;
744         }
745
746         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
747 } /* Handle_GLOBAL */
748
749
750 LOCAL VOID
751 Handle_OPERATOR( INT Line, CHAR *Var, CHAR *Arg )
752 {
753         assert( Line > 0 );
754         assert( Var != NULL );
755         assert( Arg != NULL );
756         assert( Conf_Oper_Count > 0 );
757
758         if( strcasecmp( Var, "Name" ) == 0 )
759         {
760                 /* Name of IRC operator */
761                 if( strlcpy( Conf_Oper[Conf_Oper_Count - 1].name, Arg, sizeof( Conf_Oper[Conf_Oper_Count - 1].name )) >= sizeof( Conf_Oper[Conf_Oper_Count - 1].name )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
762                 return;
763         }
764         if( strcasecmp( Var, "Password" ) == 0 )
765         {
766                 /* Password of IRC operator */
767                 if( strlcpy( Conf_Oper[Conf_Oper_Count - 1].pwd, Arg, sizeof( Conf_Oper[Conf_Oper_Count - 1].pwd )) >= sizeof( Conf_Oper[Conf_Oper_Count - 1].pwd )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Password\" too long!", NGIRCd_ConfFile, Line );
768                 return;
769         }
770         
771         Config_Error( LOG_ERR, "%s, line %d (section \"Operator\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
772 } /* Handle_OPERATOR */
773
774
775 LOCAL VOID
776 Handle_SERVER( INT Line, CHAR *Var, CHAR *Arg )
777 {
778         LONG port;
779         
780         assert( Line > 0 );
781         assert( Var != NULL );
782         assert( Arg != NULL );
783
784         /* Ignore server block if no space is left in server configuration structure */
785         if( New_Server_Idx <= NONE ) return;
786
787         if( strcasecmp( Var, "Host" ) == 0 )
788         {
789                 /* Hostname of the server */
790                 if( strlcpy( New_Server.host, Arg, sizeof( New_Server.host )) >= sizeof( New_Server.host )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Host\" too long!", NGIRCd_ConfFile, Line );
791                 return;
792         }
793         if( strcasecmp( Var, "Name" ) == 0 )
794         {
795                 /* Name of the server ("Nick"/"ID") */
796                 if( strlcpy( New_Server.name, Arg, sizeof( New_Server.name )) >= sizeof( New_Server.name )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
797                 return;
798         }
799         if( strcasecmp( Var, "MyPassword" ) == 0 )
800         {
801                 /* Password of this server which is sent to the peer */
802                 if( strlcpy( New_Server.pwd_in, Arg, sizeof( New_Server.pwd_in )) >= sizeof( New_Server.pwd_in )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MyPassword\" too long!", NGIRCd_ConfFile, Line );
803                 return;
804         }
805         if( strcasecmp( Var, "PeerPassword" ) == 0 )
806         {
807                 /* Passwort of the peer which must be received */
808                 if( strlcpy( New_Server.pwd_out, Arg, sizeof( New_Server.pwd_out )) >= sizeof( New_Server.pwd_out )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"PeerPassword\" too long!", NGIRCd_ConfFile, Line );
809                 return;
810         }
811         if( strcasecmp( Var, "Port" ) == 0 )
812         {
813                 /* Port to which this server should connect */
814                 port = atol( Arg );
815                 if( port > 0 && port < 0xFFFF ) New_Server.port = (INT)port;
816                 else Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Illegal port number %ld!", NGIRCd_ConfFile, Line, port );
817                 return;
818         }
819         if( strcasecmp( Var, "Group" ) == 0 )
820         {
821                 /* Server group */
822 #ifdef HAVE_ISDIGIT
823                 if( ! isdigit( (INT)*Arg )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Group\" is not a number!", NGIRCd_ConfFile, Line );
824                 else
825 #endif
826                 New_Server.group = atoi( Arg );
827                 return;
828         }
829         
830         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
831 } /* Handle_SERVER */
832
833
834 LOCAL VOID
835 Handle_CHANNEL( INT Line, CHAR *Var, CHAR *Arg )
836 {
837         assert( Line > 0 );
838         assert( Var != NULL );
839         assert( Arg != NULL );
840
841         if( strcasecmp( Var, "Name" ) == 0 )
842         {
843                 /* Name of the channel */
844                 if( strlcpy( Conf_Channel[Conf_Channel_Count - 1].name, Arg, sizeof( Conf_Channel[Conf_Channel_Count - 1].name )) >= sizeof( Conf_Channel[Conf_Channel_Count - 1].name )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Name\" too long!", NGIRCd_ConfFile, Line );
845                 return;
846         }
847         if( strcasecmp( Var, "Modes" ) == 0 )
848         {
849                 /* Initial modes */
850                 if( strlcpy( Conf_Channel[Conf_Channel_Count - 1].modes, Arg, sizeof( Conf_Channel[Conf_Channel_Count - 1].modes )) >= sizeof( Conf_Channel[Conf_Channel_Count - 1].modes )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Modes\" too long!", NGIRCd_ConfFile, Line );
851                 return;
852         }
853         if( strcasecmp( Var, "Topic" ) == 0 )
854         {
855                 /* Initial topic */
856                 if( strlcpy( Conf_Channel[Conf_Channel_Count - 1].topic, Arg, sizeof( Conf_Channel[Conf_Channel_Count - 1].topic )) >= sizeof( Conf_Channel[Conf_Channel_Count - 1].topic )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"Topic\" too long!", NGIRCd_ConfFile, Line );
857                 return;
858         }
859
860         Config_Error( LOG_ERR, "%s, line %d (section \"Channel\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var );
861 } /* Handle_CHANNEL */
862
863
864 LOCAL VOID
865 Validate_Config( BOOLEAN Configtest )
866 {
867         /* Validate configuration settings. */
868
869 #ifdef DEBUG
870         INT i, servers, servers_once;
871 #endif
872
873         if( ! Conf_ServerName[0] )
874         {
875                 /* No server name configured! */
876                 Config_Error( LOG_ALERT, "No server name configured in \"%s\" (section 'Global': 'Name')!", NGIRCd_ConfFile );
877                 if( ! Configtest )
878                 {
879                         Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
880                         exit( 1 );
881                 }
882         }
883         
884         if( Conf_ServerName[0] && ! strchr( Conf_ServerName, '.' ))
885         {
886                 /* No dot in server name! */
887                 Config_Error( LOG_ALERT, "Invalid server name configured in \"%s\" (section 'Global': 'Name'): Dot missing!", NGIRCd_ConfFile );
888                 if( ! Configtest )
889                 {
890                         Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
891                         exit( 1 );
892                 }
893         }
894
895 #ifdef STRICT_RFC
896         if( ! Conf_ServerAdminMail[0] )
897         {
898                 /* No administrative contact configured! */
899                 Config_Error( LOG_ALERT, "No administrator email address configured in \"%s\" ('AdminEMail')!", NGIRCd_ConfFile );
900                 if( ! Configtest )
901                 {
902                         Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
903                         exit( 1 );
904                 }
905         }
906 #endif
907
908         if( ! Conf_ServerAdmin1[0] && ! Conf_ServerAdmin2[0] && ! Conf_ServerAdminMail[0] )
909         {
910                 /* No administrative information configured! */
911                 Config_Error( LOG_WARNING, "No administrative information configured but required by RFC!" );
912         }
913 #ifdef FD_SETSIZE       
914         if(( Conf_MaxConnections > (LONG)FD_SETSIZE ) || ( Conf_MaxConnections < 1 ))
915         {
916                 Conf_MaxConnections = (LONG)FD_SETSIZE;
917                 Config_Error( LOG_ERR, "Setting MaxConnections to %ld, select() can't handle more file descriptors!", Conf_MaxConnections );
918         }
919 #else
920         Config_Error( LOG_WARN, "Don't know how many file descriptors select() can handle on this system, don't set MaxConnections too high!" );
921 #endif
922
923 #ifdef DEBUG
924         servers = servers_once = 0;
925         for( i = 0; i < MAX_SERVERS; i++ )
926         {
927                 if( Conf_Server[i].name[0] )
928                 {
929                         servers++;
930                         if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) servers_once++;
931                 }
932         }
933         Log( LOG_DEBUG, "Configuration: Operators=%d, Servers=%d[%d], Channels=%d", Conf_Oper_Count, servers, servers_once, Conf_Channel_Count );
934 #endif
935 } /* Validate_Config */
936
937
938 #ifdef PROTOTYPES
939 LOCAL VOID Config_Error( CONST INT Level, CONST CHAR *Format, ... )
940 #else
941 LOCAL VOID Config_Error( Level, Format, va_alist )
942 CONST INT Level;
943 CONST CHAR *Format;
944 va_dcl
945 #endif
946 {
947         /* Error! Write to console and/or logfile. */
948
949         CHAR msg[MAX_LOG_MSG_LEN];
950         va_list ap;
951
952         assert( Format != NULL );
953
954 #ifdef PROTOTYPES
955         va_start( ap, Format );
956 #else
957         va_start( ap );
958 #endif
959         vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
960         va_end( ap );
961         
962         /* During "normal operations" the log functions of the daemon should
963          * be used, but during testing of the configuration file, all messages
964          * should go directly to the console: */
965         if( Use_Log ) Log( Level, "%s", msg );
966         else puts( msg );
967 } /* Config_Error */
968
969
970 LOCAL VOID
971 Init_Server_Struct( CONF_SERVER *Server )
972 {
973         /* Initialize server configuration structur to default values */
974
975         assert( Server != NULL );
976
977         strcpy( Server->host, "" );
978         strcpy( Server->ip, "" );
979         strcpy( Server->name, "" );
980         strcpy( Server->pwd_in, "" );
981         strcpy( Server->pwd_out, "" );
982         Server->port = 0;
983         Server->group = NONE;
984         Server->lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
985         Server->res_stat = NULL;
986         if( NGIRCd_Passive ) Server->flags = CONF_SFLAG_DISABLED;
987         else Server->flags = 0;
988         Server->conn_id = NONE;
989 } /* Init_Server_Struct */
990
991
992 /* -eof- */