]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conf.c
Implement WEBIRC command
[ngircd-alex.git] / src / ngircd / conf.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2010 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 #include "imp.h"
18 #include <assert.h>
19 #include <errno.h>
20 #ifdef PROTOTYPES
21 #       include <stdarg.h>
22 #else
23 #       include <varargs.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <unistd.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34
35 #ifdef HAVE_CTYPE_H
36 # include <ctype.h>
37 #endif
38
39 #include "array.h"
40 #include "ngircd.h"
41 #include "conn.h"
42 #include "client.h"
43 #include "channel.h"
44 #include "defines.h"
45 #include "log.h"
46 #include "match.h"
47 #include "resolve.h"
48 #include "tool.h"
49
50 #include "exp.h"
51 #include "conf.h"
52
53
54 static bool Use_Log = true;
55 static CONF_SERVER New_Server;
56 static int New_Server_Idx;
57
58 static size_t Conf_Oper_Count;
59 static size_t Conf_Channel_Count;
60 static void Set_Defaults PARAMS(( bool InitServers ));
61 static bool Read_Config PARAMS(( bool ngircd_starting ));
62 static bool Validate_Config PARAMS(( bool TestOnly, bool Rehash ));
63
64 static void Handle_GLOBAL PARAMS(( int Line, char *Var, char *Arg ));
65 static void Handle_OPERATOR PARAMS(( int Line, char *Var, char *Arg ));
66 static void Handle_SERVER PARAMS(( int Line, char *Var, char *Arg ));
67 static void Handle_CHANNEL PARAMS(( int Line, char *Var, char *Arg ));
68
69 static void Config_Error PARAMS(( const int Level, const char *Format, ... ));
70
71 static void Config_Error_NaN PARAMS(( const int LINE, const char *Value ));
72 static void Config_Error_TooLong PARAMS(( const int LINE, const char *Value ));
73
74 static void Init_Server_Struct PARAMS(( CONF_SERVER *Server ));
75
76 #ifdef WANT_IPV6
77 #define DEFAULT_LISTEN_ADDRSTR "::,0.0.0.0"
78 #else
79 #define DEFAULT_LISTEN_ADDRSTR "0.0.0.0"
80 #endif
81
82 #ifdef SSL_SUPPORT
83 struct SSLOptions Conf_SSLOptions;
84
85 static void
86 ConfSSL_Init(void)
87 {
88         free(Conf_SSLOptions.KeyFile);
89         Conf_SSLOptions.KeyFile = NULL;
90
91         free(Conf_SSLOptions.CertFile);
92         Conf_SSLOptions.CertFile = NULL;
93
94         free(Conf_SSLOptions.DHFile);
95         Conf_SSLOptions.DHFile = NULL;
96         array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
97 }
98
99 static bool
100 ssl_print_configvar(const char *name, const char *file)
101 {
102         FILE *fp;
103
104         if (!file) {
105                 printf("  %s =\n", name);
106                 return true;
107         }
108
109         fp = fopen(file, "r");
110         if (fp)
111                 fclose(fp);
112         else
113                 fprintf(stderr, "ERROR: %s \"%s\": %s\n",
114                         name, file, strerror(errno));
115
116         printf("  %s = %s\n", name, file);
117         return fp != NULL;
118 }
119
120 static bool
121 ConfSSL_Puts(void)
122 {
123         bool ret;
124
125         ret = ssl_print_configvar("SSLKeyFile", Conf_SSLOptions.KeyFile);
126
127         if (!ssl_print_configvar("SSLCertFile", Conf_SSLOptions.CertFile))
128                 ret = false;
129
130         if (!ssl_print_configvar("SSLDHFile", Conf_SSLOptions.DHFile))
131                 ret = false;
132
133         if (array_bytes(&Conf_SSLOptions.KeyFilePassword))
134                 puts("  SSLKeyFilePassword = <secret>");
135
136         array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
137
138         return ret;
139 }
140 #endif
141
142 static char *
143 strdup_warn(const char *str)
144 {
145         char *ptr = strdup(str);
146         if (!ptr)
147                 Config_Error(LOG_ERR, "Could not allocate mem for string: %s", str);
148         return ptr;
149 }
150
151
152 static void
153 ports_puts(array *a)
154 {
155         size_t len;
156         UINT16 *ports;
157         len = array_length(a, sizeof(UINT16));
158         if (len--) {
159                 ports = (UINT16*) array_start(a);
160                 printf("%u", (unsigned int) *ports);
161                 while (len--) {
162                         ports++;
163                         printf(", %u", (unsigned int) *ports);
164                 }
165         }
166         putc('\n', stdout);
167 }
168
169
170 static void
171 ports_parse(array *a, int Line, char *Arg)
172 {
173         char *ptr;
174         int port;
175         UINT16 port16;
176
177         array_trunc(a);
178
179         /* Ports on that the server should listen. More port numbers
180          * must be separated by "," */
181         ptr = strtok( Arg, "," );
182         while (ptr) {
183                 ngt_TrimStr(ptr);
184                 port = atoi(ptr);
185                 if (port > 0 && port < 0xFFFF) {
186                         port16 = (UINT16) port;
187                         if (!array_catb(a, (char*)&port16, sizeof port16))
188                                 Config_Error(LOG_ERR, "%s, line %d Could not add port number %ld: %s",
189                                                         NGIRCd_ConfFile, Line, port, strerror(errno));
190                 } else {
191                         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Illegal port number %ld!",
192                                                                         NGIRCd_ConfFile, Line, port );
193                 }
194
195                 ptr = strtok( NULL, "," );
196         }
197 }
198
199
200 GLOBAL void
201 Conf_Init( void )
202 {
203         Read_Config( true );
204         Validate_Config(false, false);
205 } /* Config_Init */
206
207
208 GLOBAL bool
209 Conf_Rehash( void )
210 {
211         if (!Read_Config(false))
212                 return false;
213         Validate_Config(false, true);
214
215         /* Update CLIENT structure of local server */
216         Client_SetInfo(Client_ThisServer(), Conf_ServerInfo);
217         return true;
218 } /* Config_Rehash */
219
220
221 static const char*
222 yesno_to_str(int boolean_value)
223 {
224         if (boolean_value)
225                 return "yes";
226         return "no";
227 }
228
229
230 static void
231 opers_free(void)
232 {
233         struct Conf_Oper *op;
234         size_t len;
235         
236         len = array_length(&Conf_Opers, sizeof(*op));
237         op = array_start(&Conf_Opers);
238         while (len--) {
239                 free(op->mask);
240                 op++;
241         }
242         array_free(&Conf_Opers);
243 }
244
245 static void
246 opers_puts(void)
247 {
248         struct Conf_Oper *op;
249         size_t len;
250         
251         len = array_length(&Conf_Opers, sizeof(*op));
252         op = array_start(&Conf_Opers);
253         while (len--) {
254                 assert(op->name[0]);
255
256                 puts("[OPERATOR]");
257                 printf("  Name = %s\n", op->name);
258                 printf("  Password = %s\n", op->pwd);
259                 printf("  Mask = %s\n\n", op->mask ? op->mask : "");
260                 op++;
261         }
262 }
263
264
265 GLOBAL int
266 Conf_Test( void )
267 {
268         /* Read configuration, validate and output it. */
269
270         struct passwd *pwd;
271         struct group *grp;
272         unsigned int i;
273         bool config_valid;
274         size_t predef_channel_count;
275         struct Conf_Channel *predef_chan;
276
277         Use_Log = false;
278
279         if (! Read_Config(true))
280                 return 1;
281
282         config_valid = Validate_Config(true, false);
283
284         /* If stdin and stdout ("you can read our nice message and we can
285          * read in your keypress") are valid tty's, wait for a key: */
286         if( isatty( fileno( stdin )) && isatty( fileno( stdout ))) {
287                 puts( "OK, press enter to see a dump of your service configuration ..." );
288                 getchar( );
289         } else {
290                 puts( "Ok, dump of your server configuration follows:\n" );
291         }
292
293         puts( "[GLOBAL]" );
294         printf("  Name = %s\n", Conf_ServerName);
295         printf("  Info = %s\n", Conf_ServerInfo);
296         printf("  Password = %s\n", Conf_ServerPwd);
297         printf("  WebircPassword = %s\n", Conf_WebircPwd);
298         printf("  AdminInfo1 = %s\n", Conf_ServerAdmin1);
299         printf("  AdminInfo2 = %s\n", Conf_ServerAdmin2);
300         printf("  AdminEMail = %s\n", Conf_ServerAdminMail);
301         printf("  MotdFile = %s\n", Conf_MotdFile);
302         printf("  MotdPhrase = %s\n", Conf_MotdPhrase);
303         printf("  ChrootDir = %s\n", Conf_Chroot);
304         printf("  PidFile = %s\n", Conf_PidFile);
305         printf("  Listen = %s\n", Conf_ListenAddress);
306         fputs("  Ports = ", stdout);
307         ports_puts(&Conf_ListenPorts);
308 #ifdef SSL_SUPPORT
309         fputs("  SSLPorts = ", stdout);
310         ports_puts(&Conf_SSLOptions.ListenPorts);
311         if (!ConfSSL_Puts())
312                 config_valid = false;
313 #endif
314
315         pwd = getpwuid(Conf_UID);
316         if (pwd)
317                 printf("  ServerUID = %s\n", pwd->pw_name);
318         else
319                 printf("  ServerUID = %ld\n", (long)Conf_UID);
320         grp = getgrgid(Conf_GID);
321         if (grp)
322                 printf("  ServerGID = %s\n", grp->gr_name);
323         else
324                 printf("  ServerGID = %ld\n", (long)Conf_GID);
325         printf("  PingTimeout = %d\n", Conf_PingTimeout);
326         printf("  PongTimeout = %d\n", Conf_PongTimeout);
327         printf("  ConnectRetry = %d\n", Conf_ConnectRetry);
328         printf("  OperCanUseMode = %s\n", yesno_to_str(Conf_OperCanMode));
329         printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
330         printf("  AllowRemoteOper = %s\n", yesno_to_str(Conf_AllowRemoteOper));
331         printf("  PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly));
332         printf("  NoDNS = %s\n", yesno_to_str(Conf_NoDNS));
333         printf("  NoIdent = %s\n", yesno_to_str(Conf_NoIdent));
334
335 #ifdef WANT_IPV6
336         printf("  ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6));
337         printf("  ConnectIPv6 = %s\n", yesno_to_str(Conf_ConnectIPv4));
338 #endif
339         printf("  MaxConnections = %ld\n", Conf_MaxConnections);
340         printf("  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
341         printf("  MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
342         printf("  MaxNickLength = %u\n\n", Conf_MaxNickLength - 1);
343
344         opers_puts();
345
346         for( i = 0; i < MAX_SERVERS; i++ ) {
347                 if( ! Conf_Server[i].name[0] ) continue;
348
349                 /* Valid "Server" section */
350                 puts( "[SERVER]" );
351                 printf( "  Name = %s\n", Conf_Server[i].name );
352                 printf( "  Host = %s\n", Conf_Server[i].host );
353                 printf( "  Port = %u\n", (unsigned int)Conf_Server[i].port );
354 #ifdef SSL_SUPPORT
355                 printf( "  SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
356 #endif
357                 printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
358                 printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
359                 printf( "  ServiceMask = %s\n", Conf_Server[i].svs_mask);
360                 printf( "  Group = %d\n", Conf_Server[i].group );
361                 printf( "  Passive = %s\n\n", Conf_Server[i].flags & CONF_SFLAG_DISABLED ? "yes" : "no");
362         }
363
364         predef_channel_count = array_length(&Conf_Channels, sizeof(*predef_chan));
365         predef_chan = array_start(&Conf_Channels);
366
367         for (i = 0; i < predef_channel_count; i++, predef_chan++) {
368                 if (!predef_chan->name[0])
369                         continue;
370
371                 /* Valid "Channel" section */
372                 puts( "[CHANNEL]" );
373                 printf("  Name = %s\n", predef_chan->name);
374                 printf("  Modes = %s\n", predef_chan->modes);
375                 printf("  Key = %s\n", predef_chan->key);
376                 printf("  MaxUsers = %lu\n", predef_chan->maxusers);
377                 printf("  Topic = %s\n", predef_chan->topic);
378                 printf("  KeyFile = %s\n\n", predef_chan->keyfile);
379         }
380
381         return (config_valid ? 0 : 1);
382 } /* Conf_Test */
383
384
385 GLOBAL void
386 Conf_UnsetServer( CONN_ID Idx )
387 {
388         /* Set next time for next connection attempt, if this is a server
389          * link that is (still) configured here. If the server is set as
390          * "once", delete it from our configuration.
391          * Non-Server-Connections will be silently ignored. */
392
393         int i;
394         time_t t;
395
396         /* Check all our configured servers */
397         for( i = 0; i < MAX_SERVERS; i++ ) {
398                 if( Conf_Server[i].conn_id != Idx ) continue;
399
400                 /* Gotcha! Mark server configuration as "unused": */
401                 Conf_Server[i].conn_id = NONE;
402
403                 if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
404                         /* Delete configuration here */
405                         Init_Server_Struct( &Conf_Server[i] );
406                 } else {
407                         /* Set time for next connect attempt */
408                         t = time(NULL);
409                         if (Conf_Server[i].lasttry < t - Conf_ConnectRetry) {
410                                 /* The connection has been "long", so we don't
411                                  * require the next attempt to be delayed. */
412                                 Conf_Server[i].lasttry =
413                                         t - Conf_ConnectRetry + RECONNECT_DELAY;
414                         } else
415                                 Conf_Server[i].lasttry = t;
416                 }
417         }
418 } /* Conf_UnsetServer */
419
420
421 GLOBAL void
422 Conf_SetServer( int ConfServer, CONN_ID Idx )
423 {
424         /* Set connection for specified configured server */
425
426         assert( ConfServer > NONE );
427         assert( Idx > NONE );
428
429         Conf_Server[ConfServer].conn_id = Idx;
430 } /* Conf_SetServer */
431
432
433 GLOBAL int
434 Conf_GetServer( CONN_ID Idx )
435 {
436         /* Get index of server in configuration structure */
437
438         int i = 0;
439
440         assert( Idx > NONE );
441
442         for( i = 0; i < MAX_SERVERS; i++ ) {
443                 if( Conf_Server[i].conn_id == Idx ) return i;
444         }
445         return NONE;
446 } /* Conf_GetServer */
447
448
449 GLOBAL bool
450 Conf_EnableServer( const char *Name, UINT16 Port )
451 {
452         /* Enable specified server and adjust port */
453
454         int i;
455
456         assert( Name != NULL );
457
458         for( i = 0; i < MAX_SERVERS; i++ ) {
459                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
460                         /* Gotcha! Set port and enable server: */
461                         Conf_Server[i].port = Port;
462                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
463                         return (Conf_Server[i].port && Conf_Server[i].host[0]);
464                 }
465         }
466         return false;
467 } /* Conf_EnableServer */
468
469
470 GLOBAL bool
471 Conf_EnablePassiveServer(const char *Name)
472 {
473         /* Enable specified server */
474         int i;
475
476         assert( Name != NULL );
477         for (i = 0; i < MAX_SERVERS; i++) {
478                 if ((strcasecmp( Conf_Server[i].name, Name ) == 0) && (Conf_Server[i].port > 0)) {
479                         /* BINGO! Enable server */
480                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
481                         return true;
482                 }
483         }
484         return false;
485 } /* Conf_EnablePassiveServer */
486
487
488 GLOBAL bool
489 Conf_DisableServer( const char *Name )
490 {
491         /* Enable specified server and adjust port */
492
493         int i;
494
495         assert( Name != NULL );
496
497         for( i = 0; i < MAX_SERVERS; i++ ) {
498                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
499                         /* Gotcha! Disable and disconnect server: */
500                         Conf_Server[i].flags |= CONF_SFLAG_DISABLED;
501                         if( Conf_Server[i].conn_id > NONE ) Conn_Close( Conf_Server[i].conn_id, NULL, "Server link terminated on operator request", true);
502                         return true;
503                 }
504         }
505         return false;
506 } /* Conf_DisableServer */
507
508
509 GLOBAL bool
510 Conf_AddServer( const char *Name, UINT16 Port, const char *Host, const char *MyPwd, const char *PeerPwd )
511 {
512         /* Add new server to configuration */
513
514         int i;
515
516         assert( Name != NULL );
517         assert( Host != NULL );
518         assert( MyPwd != NULL );
519         assert( PeerPwd != NULL );
520
521         /* Search unused item in server configuration structure */
522         for( i = 0; i < MAX_SERVERS; i++ ) {
523                 /* Is this item used? */
524                 if( ! Conf_Server[i].name[0] ) break;
525         }
526         if( i >= MAX_SERVERS ) return false;
527
528         Init_Server_Struct( &Conf_Server[i] );
529         strlcpy( Conf_Server[i].name, Name, sizeof( Conf_Server[i].name ));
530         strlcpy( Conf_Server[i].host, Host, sizeof( Conf_Server[i].host ));
531         strlcpy( Conf_Server[i].pwd_out, MyPwd, sizeof( Conf_Server[i].pwd_out ));
532         strlcpy( Conf_Server[i].pwd_in, PeerPwd, sizeof( Conf_Server[i].pwd_in ));
533         Conf_Server[i].port = Port;
534         Conf_Server[i].flags = CONF_SFLAG_ONCE;
535
536         return true;
537 } /* Conf_AddServer */
538
539
540 /**
541  * Check if the given nick name is an service
542  */
543 GLOBAL bool
544 Conf_IsService(int ConfServer, const char *Nick)
545 {
546         return MatchCaseInsensitive(Conf_Server[ConfServer].svs_mask, Nick);
547 } /* Conf_IsService */
548
549
550 /**
551  * Initialize configuration settings with their default values.
552  */
553 static void
554 Set_Defaults(bool InitServers)
555 {
556         int i;
557
558         strcpy(Conf_ServerName, "");
559         snprintf(Conf_ServerInfo, sizeof Conf_ServerInfo, "%s %s",
560                  PACKAGE_NAME, PACKAGE_VERSION);
561         strcpy(Conf_ServerPwd, "");
562
563         strcpy(Conf_ServerAdmin1, "");
564         strcpy(Conf_ServerAdmin2, "");
565         strcpy(Conf_ServerAdminMail, "");
566
567         strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
568         strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
569         strlcpy(Conf_MotdPhrase, MOTD_PHRASE, sizeof(Conf_MotdPhrase));
570
571         Conf_UID = Conf_GID = 0;
572         strlcpy(Conf_Chroot, CHROOT_DIR, sizeof(Conf_Chroot));
573         strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
574
575         free(Conf_ListenAddress);
576         Conf_ListenAddress = NULL;
577
578         Conf_PingTimeout = 120;
579         Conf_PongTimeout = 20;
580         Conf_ConnectRetry = 60;
581         Conf_NoDNS = false;
582         Conf_NoIdent = false;
583
584         Conf_Oper_Count = 0;
585         Conf_Channel_Count = 0;
586
587         Conf_OperCanMode = false;
588         Conf_OperServerMode = false;
589         Conf_AllowRemoteOper = false;
590         Conf_PredefChannelsOnly = false;
591
592         Conf_ConnectIPv4 = true;
593         Conf_ConnectIPv6 = true;
594
595         Conf_MaxConnections = 0;
596         Conf_MaxConnectionsIP = 5;
597         Conf_MaxJoins = 10;
598         Conf_MaxNickLength = CLIENT_NICK_LEN_DEFAULT;
599
600         /* Initialize server configuration structures */
601         if (InitServers) {
602                 for (i = 0; i < MAX_SERVERS;
603                      Init_Server_Struct(&Conf_Server[i++]));
604         }
605 } /* Set_Defaults */
606
607
608 static bool
609 no_listenports(void)
610 {
611         size_t cnt = array_bytes(&Conf_ListenPorts);
612 #ifdef SSL_SUPPORT
613         cnt += array_bytes(&Conf_SSLOptions.ListenPorts);
614 #endif
615         return cnt == 0;
616 }
617
618 static bool
619 Read_Config( bool ngircd_starting )
620 {
621         /* Read configuration file. */
622
623         char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
624         const UINT16 defaultport = 6667;
625         int line, i, n;
626         FILE *fd;
627
628         /* Open configuration file */
629         fd = fopen( NGIRCd_ConfFile, "r" );
630         if( ! fd ) {
631                 /* No configuration file found! */
632                 Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s",
633                                         NGIRCd_ConfFile, strerror( errno ));
634                 if (!ngircd_starting)
635                         return false;
636                 Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
637                 exit( 1 );
638         }
639
640         opers_free();
641         Set_Defaults( ngircd_starting );
642
643         Config_Error( LOG_INFO, "Reading configuration from \"%s\" ...", NGIRCd_ConfFile );
644
645         /* Clean up server configuration structure: mark all already
646          * configured servers as "once" so that they are deleted
647          * after the next disconnect and delete all unused servers.
648          * And delete all servers which are "duplicates" of servers
649          * that are already marked as "once" (such servers have been
650          * created by the last rehash but are now useless). */
651         for( i = 0; i < MAX_SERVERS; i++ ) {
652                 if( Conf_Server[i].conn_id == NONE ) Init_Server_Struct( &Conf_Server[i] );
653                 else {
654                         /* This structure is in use ... */
655                         if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
656                                 /* Check for duplicates */
657                                 for( n = 0; n < MAX_SERVERS; n++ ) {
658                                         if( n == i ) continue;
659
660                                         if( Conf_Server[i].conn_id == Conf_Server[n].conn_id ) {
661                                                 Init_Server_Struct( &Conf_Server[n] );
662 #ifdef DEBUG
663                                                 Log(LOG_DEBUG,"Deleted unused duplicate server %d (kept %d).",
664                                                                                                 n, i );
665 #endif
666                                         }
667                                 }
668                         } else {
669                                 /* Mark server as "once" */
670                                 Conf_Server[i].flags |= CONF_SFLAG_ONCE;
671                                 Log( LOG_DEBUG, "Marked server %d as \"once\"", i );
672                         }
673                 }
674         }
675
676         /* Initialize variables */
677         line = 0;
678         strcpy( section, "" );
679         Init_Server_Struct( &New_Server );
680         New_Server_Idx = NONE;
681 #ifdef SSL_SUPPORT
682         ConfSSL_Init();
683 #endif
684         /* Read configuration file */
685         while( true ) {
686                 if( ! fgets( str, LINE_LEN, fd )) break;
687                 ngt_TrimStr( str );
688                 line++;
689
690                 /* Skip comments and empty lines */
691                 if( str[0] == ';' || str[0] == '#' || str[0] == '\0' ) continue;
692
693                 /* Is this the beginning of a new section? */
694                 if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' )) {
695                         strlcpy( section, str, sizeof( section ));
696                         if( strcasecmp( section, "[GLOBAL]" ) == 0 )
697                                 continue;
698
699                         if( strcasecmp( section, "[SERVER]" ) == 0 ) {
700                                 /* Check if there is already a server to add */
701                                 if( New_Server.name[0] ) {
702                                         /* Copy data to "real" server structure */
703                                         assert( New_Server_Idx > NONE );
704                                         Conf_Server[New_Server_Idx] = New_Server;
705                                 }
706
707                                 /* Re-init structure for new server */
708                                 Init_Server_Struct( &New_Server );
709
710                                 /* Search unused item in server configuration structure */
711                                 for( i = 0; i < MAX_SERVERS; i++ ) {
712                                         /* Is this item used? */
713                                         if( ! Conf_Server[i].name[0] ) break;
714                                 }
715                                 if( i >= MAX_SERVERS ) {
716                                         /* Oops, no free item found! */
717                                         Config_Error( LOG_ERR, "Too many servers configured." );
718                                         New_Server_Idx = NONE;
719                                 }
720                                 else New_Server_Idx = i;
721                                 continue;
722                         }
723                         if (strcasecmp(section, "[CHANNEL]") == 0) {
724                                 Conf_Channel_Count++;
725                                 continue;
726                         }
727                         if (strcasecmp(section, "[OPERATOR]") == 0) {
728                                 Conf_Oper_Count++;
729                                 continue;
730                         }
731
732                         Config_Error( LOG_ERR, "%s, line %d: Unknown section \"%s\"!", NGIRCd_ConfFile, line, section );
733                         section[0] = 0x1;
734                 }
735                 if( section[0] == 0x1 ) continue;
736
737                 /* Split line into variable name and parameters */
738                 ptr = strchr( str, '=' );
739                 if( ! ptr ) {
740                         Config_Error( LOG_ERR, "%s, line %d: Syntax error!", NGIRCd_ConfFile, line );
741                         continue;
742                 }
743                 *ptr = '\0';
744                 var = str; ngt_TrimStr( var );
745                 arg = ptr + 1; ngt_TrimStr( arg );
746
747                 if( strcasecmp( section, "[GLOBAL]" ) == 0 ) Handle_GLOBAL( line, var, arg );
748                 else if( strcasecmp( section, "[OPERATOR]" ) == 0 ) Handle_OPERATOR( line, var, arg );
749                 else if( strcasecmp( section, "[SERVER]" ) == 0 ) Handle_SERVER( line, var, arg );
750                 else if( strcasecmp( section, "[CHANNEL]" ) == 0 ) Handle_CHANNEL( line, var, arg );
751                 else Config_Error( LOG_ERR, "%s, line %d: Variable \"%s\" outside section!", NGIRCd_ConfFile, line, var );
752         }
753
754         /* Close configuration file */
755         fclose( fd );
756
757         /* Check if there is still a server to add */
758         if( New_Server.name[0] ) {
759                 /* Copy data to "real" server structure */
760                 assert( New_Server_Idx > NONE );
761                 Conf_Server[New_Server_Idx] = New_Server;
762         }
763
764         /* not a single listening port? Add default. */
765         if (no_listenports() &&
766                 !array_copyb(&Conf_ListenPorts, (char*) &defaultport, sizeof defaultport))
767         {
768                 Config_Error(LOG_ALERT, "Could not add default listening Port %u: %s",
769                                         (unsigned int) defaultport, strerror(errno));
770
771                 exit(1);
772         }
773
774         if (!Conf_ListenAddress)
775                 Conf_ListenAddress = strdup_warn(DEFAULT_LISTEN_ADDRSTR);
776
777         if (!Conf_ListenAddress) {
778                 Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
779                 exit(1);
780         }
781         return true;
782 } /* Read_Config */
783
784
785 static bool
786 Check_ArgIsTrue( const char *Arg )
787 {
788         if( strcasecmp( Arg, "yes" ) == 0 ) return true;
789         if( strcasecmp( Arg, "true" ) == 0 ) return true;
790         if( atoi( Arg ) != 0 ) return true;
791
792         return false;
793 } /* Check_ArgIsTrue */
794
795
796 static unsigned int Handle_MaxNickLength(int Line, const char *Arg)
797 {
798         unsigned new;
799
800         new = (unsigned) atoi(Arg) + 1;
801         if (new > CLIENT_NICK_LEN) {
802                 Config_Error(LOG_WARNING,
803                              "%s, line %d: Value of \"MaxNickLength\" exceeds %u!",
804                              NGIRCd_ConfFile, Line, CLIENT_NICK_LEN - 1);
805                 return CLIENT_NICK_LEN;
806         }
807         if (new < 2) {
808                 Config_Error(LOG_WARNING,
809                              "%s, line %d: Value of \"MaxNickLength\" must be at least 1!",
810                              NGIRCd_ConfFile, Line);
811                 return 2;
812         }
813         return new;
814 } /* Handle_MaxNickLength */
815
816
817 static void
818 Handle_GLOBAL( int Line, char *Var, char *Arg )
819 {
820         struct passwd *pwd;
821         struct group *grp;
822         size_t len;
823         
824         assert( Line > 0 );
825         assert( Var != NULL );
826         assert( Arg != NULL );
827         
828         if( strcasecmp( Var, "Name" ) == 0 ) {
829                 /* Server name */
830                 len = strlcpy( Conf_ServerName, Arg, sizeof( Conf_ServerName ));
831                 if (len >= sizeof( Conf_ServerName ))
832                         Config_Error_TooLong( Line, Var );
833                 return;
834         }
835         if( strcasecmp( Var, "Info" ) == 0 ) {
836                 /* Info text of server */
837                 len = strlcpy( Conf_ServerInfo, Arg, sizeof( Conf_ServerInfo ));
838                 if (len >= sizeof( Conf_ServerInfo ))
839                         Config_Error_TooLong ( Line, Var );
840                 return;
841         }
842         if( strcasecmp( Var, "Password" ) == 0 ) {
843                 /* Global server password */
844                 len = strlcpy( Conf_ServerPwd, Arg, sizeof( Conf_ServerPwd ));
845                 if (len >= sizeof( Conf_ServerPwd ))
846                         Config_Error_TooLong( Line, Var );
847                 return;
848         }
849         if (strcasecmp(Var, "WebircPassword") == 0) {
850                 /* Password required for WEBIRC command */
851                 len = strlcpy(Conf_WebircPwd, Arg, sizeof(Conf_WebircPwd));
852                 if (len >= sizeof(Conf_WebircPwd))
853                         Config_Error_TooLong(Line, Var);
854                 return;
855         }
856         if( strcasecmp( Var, "AdminInfo1" ) == 0 ) {
857                 /* Administrative info #1 */
858                 len = strlcpy( Conf_ServerAdmin1, Arg, sizeof( Conf_ServerAdmin1 ));
859                 if (len >= sizeof( Conf_ServerAdmin1 ))
860                         Config_Error_TooLong ( Line, Var );
861                 return;
862         }
863         if( strcasecmp( Var, "AdminInfo2" ) == 0 ) {
864                 /* Administrative info #2 */
865                 len = strlcpy( Conf_ServerAdmin2, Arg, sizeof( Conf_ServerAdmin2 ));
866                 if (len >= sizeof( Conf_ServerAdmin2 ))
867                         Config_Error_TooLong ( Line, Var );
868                 return;
869         }
870         if( strcasecmp( Var, "AdminEMail" ) == 0 ) {
871                 /* Administrative email contact */
872                 len = strlcpy( Conf_ServerAdminMail, Arg, sizeof( Conf_ServerAdminMail ));
873                 if (len >= sizeof( Conf_ServerAdminMail ))
874                         Config_Error_TooLong( Line, Var );
875                 return;
876         }
877
878         if( strcasecmp( Var, "Ports" ) == 0 ) {
879                 ports_parse(&Conf_ListenPorts, Line, Arg);
880                 return;
881         }
882         if( strcasecmp( Var, "MotdFile" ) == 0 ) {
883                 /* "Message of the day" (MOTD) file */
884                 len = strlcpy( Conf_MotdFile, Arg, sizeof( Conf_MotdFile ));
885                 if (len >= sizeof( Conf_MotdFile ))
886                         Config_Error_TooLong( Line, Var );
887                 return;
888         }
889         if( strcasecmp( Var, "MotdPhrase" ) == 0 ) {
890                 /* "Message of the day" phrase (instead of file) */
891                 len = strlcpy( Conf_MotdPhrase, Arg, sizeof( Conf_MotdPhrase ));
892                 if (len >= sizeof( Conf_MotdPhrase ))
893                         Config_Error_TooLong( Line, Var );
894                 return;
895         }
896         if( strcasecmp( Var, "ChrootDir" ) == 0 ) {
897                 /* directory for chroot() */
898                 len = strlcpy( Conf_Chroot, Arg, sizeof( Conf_Chroot ));
899                 if (len >= sizeof( Conf_Chroot ))
900                         Config_Error_TooLong( Line, Var );
901                 return;
902         }
903         if ( strcasecmp( Var, "PidFile" ) == 0 ) {
904                 /* name of pidfile */
905                 len = strlcpy( Conf_PidFile, Arg, sizeof( Conf_PidFile ));
906                 if (len >= sizeof( Conf_PidFile ))
907                         Config_Error_TooLong( Line, Var );
908                 return;
909         }
910         if( strcasecmp( Var, "ServerUID" ) == 0 ) {
911                 /* UID the daemon should switch to */
912                 pwd = getpwnam( Arg );
913                 if( pwd ) Conf_UID = pwd->pw_uid;
914                 else {
915 #ifdef HAVE_ISDIGIT
916                         if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
917                         else
918 #endif
919                         Conf_UID = (unsigned int)atoi( Arg );
920                 }
921                 return;
922         }
923         if( strcasecmp( Var, "ServerGID" ) == 0 ) {
924                 /* GID the daemon should use */
925                 grp = getgrnam( Arg );
926                 if( grp ) Conf_GID = grp->gr_gid;
927                 else {
928 #ifdef HAVE_ISDIGIT
929                         if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
930                         else
931 #endif
932                         Conf_GID = (unsigned int)atoi( Arg );
933                 }
934                 return;
935         }
936         if( strcasecmp( Var, "PingTimeout" ) == 0 ) {
937                 /* PING timeout */
938                 Conf_PingTimeout = atoi( Arg );
939                 if( Conf_PingTimeout < 5 ) {
940                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PingTimeout\" too low!",
941                                                                         NGIRCd_ConfFile, Line );
942                         Conf_PingTimeout = 5;
943                 }
944                 return;
945         }
946         if( strcasecmp( Var, "PongTimeout" ) == 0 ) {
947                 /* PONG timeout */
948                 Conf_PongTimeout = atoi( Arg );
949                 if( Conf_PongTimeout < 5 ) {
950                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PongTimeout\" too low!",
951                                                                         NGIRCd_ConfFile, Line );
952                         Conf_PongTimeout = 5;
953                 }
954                 return;
955         }
956         if( strcasecmp( Var, "ConnectRetry" ) == 0 ) {
957                 /* Seconds between connection attempts to other servers */
958                 Conf_ConnectRetry = atoi( Arg );
959                 if( Conf_ConnectRetry < 5 ) {
960                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"ConnectRetry\" too low!",
961                                                                         NGIRCd_ConfFile, Line );
962                         Conf_ConnectRetry = 5;
963                 }
964                 return;
965         }
966         if( strcasecmp( Var, "PredefChannelsOnly" ) == 0 ) {
967                 /* Should we only allow pre-defined-channels? (i.e. users cannot create their own channels) */
968                 Conf_PredefChannelsOnly = Check_ArgIsTrue( Arg );
969                 return;
970         }
971         if( strcasecmp( Var, "NoDNS" ) == 0 ) {
972                 /* don't do reverse dns lookups when clients connect? */
973                 Conf_NoDNS = Check_ArgIsTrue( Arg );
974                 return;
975         }
976         if (strcasecmp(Var, "NoIdent") == 0) {
977                 /* don't do IDENT lookups when clients connect? */
978                 Conf_NoIdent = Check_ArgIsTrue(Arg);
979 #ifndef IDENTAUTH
980                 if (!Conf_NoIdent) {
981                         /* user has enabled ident lookups explicitly, but ... */
982                         Config_Error(LOG_WARNING,
983                                 "%s: line %d: NoIdent=False, but ngircd was built without IDENT support",
984                                 NGIRCd_ConfFile, Line);
985                 }
986 #endif
987                 return;
988         }
989 #ifdef WANT_IPV6
990         /* the default setting for all the WANT_IPV6 special options is 'true' */
991         if( strcasecmp( Var, "ConnectIPv6" ) == 0 ) {
992                 /* connect to other hosts using ipv6, if they have an AAAA record? */
993                 Conf_ConnectIPv6 = Check_ArgIsTrue( Arg );
994                 return;
995         }
996         if( strcasecmp( Var, "ConnectIPv4" ) == 0 ) {
997                 /* connect to other hosts using ipv4.
998                  * again, this can be used for ipv6-only setups */
999                 Conf_ConnectIPv4 = Check_ArgIsTrue( Arg );
1000                 return;
1001         }
1002 #endif
1003         if( strcasecmp( Var, "OperCanUseMode" ) == 0 ) {
1004                 /* Are IRC operators allowed to use MODE in channels they aren't Op in? */
1005                 Conf_OperCanMode = Check_ArgIsTrue( Arg );
1006                 return;
1007         }
1008         if( strcasecmp( Var, "OperServerMode" ) == 0 ) {
1009                 /* Mask IRC operator as if coming from the server? (ircd-irc2 compat hack) */
1010                 Conf_OperServerMode = Check_ArgIsTrue( Arg );
1011                 return;
1012         }
1013         if(strcasecmp(Var, "AllowRemoteOper") == 0) {
1014                 /* Are remote IRC operators allowed to control this server? */
1015                 Conf_AllowRemoteOper = Check_ArgIsTrue(Arg);
1016                 return;
1017         }
1018         if( strcasecmp( Var, "MaxConnections" ) == 0 ) {
1019                 /* Maximum number of connections. 0 -> "no limit". */
1020 #ifdef HAVE_ISDIGIT
1021                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var);
1022                 else
1023 #endif
1024                 Conf_MaxConnections = atol( Arg );
1025                 return;
1026         }
1027         if( strcasecmp( Var, "MaxConnectionsIP" ) == 0 ) {
1028                 /* Maximum number of simultaneous connections from one IP. 0 -> "no limit" */
1029 #ifdef HAVE_ISDIGIT
1030                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
1031                 else
1032 #endif
1033                 Conf_MaxConnectionsIP = atoi( Arg );
1034                 return;
1035         }
1036         if( strcasecmp( Var, "MaxJoins" ) == 0 ) {
1037                 /* Maximum number of channels a user can join. 0 -> "no limit". */
1038 #ifdef HAVE_ISDIGIT
1039                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
1040                 else
1041 #endif
1042                 Conf_MaxJoins = atoi( Arg );
1043                 return;
1044         }
1045         if( strcasecmp( Var, "MaxNickLength" ) == 0 ) {
1046                 /* Maximum length of a nick name; must be same on all servers
1047                  * within the IRC network! */
1048                 Conf_MaxNickLength = Handle_MaxNickLength(Line, Arg);
1049                 return;
1050         }
1051
1052         if( strcasecmp( Var, "Listen" ) == 0 ) {
1053                 /* IP-Address to bind sockets */
1054                 if (Conf_ListenAddress) {
1055                         Config_Error(LOG_ERR, "Multiple Listen= options, ignoring: %s", Arg);
1056                         return;
1057                 }
1058                 Conf_ListenAddress = strdup_warn(Arg);
1059                 /*
1060                  * if allocation fails, we're in trouble:
1061                  * we cannot ignore the error -- otherwise ngircd
1062                  * would listen on all interfaces.
1063                  */
1064                 if (!Conf_ListenAddress) {
1065                         Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
1066                         exit(1);
1067                 }
1068                 return;
1069         }
1070
1071 #ifdef SSL_SUPPORT
1072         if( strcasecmp( Var, "SSLPorts" ) == 0 ) {
1073                 ports_parse(&Conf_SSLOptions.ListenPorts, Line, Arg);
1074                 return;
1075         }
1076
1077         if( strcasecmp( Var, "SSLKeyFile" ) == 0 ) {
1078                 assert(Conf_SSLOptions.KeyFile == NULL );
1079                 Conf_SSLOptions.KeyFile = strdup_warn(Arg);
1080                 return;
1081         }
1082         if( strcasecmp( Var, "SSLCertFile" ) == 0 ) {
1083                 assert(Conf_SSLOptions.CertFile == NULL );
1084                 Conf_SSLOptions.CertFile = strdup_warn(Arg);
1085                 return;
1086         }
1087
1088         if( strcasecmp( Var, "SSLKeyFilePassword" ) == 0 ) {
1089                 assert(array_bytes(&Conf_SSLOptions.KeyFilePassword) == 0);
1090                 if (!array_copys(&Conf_SSLOptions.KeyFilePassword, Arg))
1091                         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Could not copy %s: %s!",
1092                                                                 NGIRCd_ConfFile, Line, Var, strerror(errno));
1093                 return;
1094         }
1095         if( strcasecmp( Var, "SSLDHFile" ) == 0 ) {
1096                 assert(Conf_SSLOptions.DHFile == NULL);
1097                 Conf_SSLOptions.DHFile = strdup_warn( Arg );
1098                 return;
1099         }
1100 #endif
1101         Config_Error(LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!",
1102                                                                 NGIRCd_ConfFile, Line, Var);
1103 } /* Handle_GLOBAL */
1104
1105
1106 static void
1107 Handle_OPERATOR( int Line, char *Var, char *Arg )
1108 {
1109         size_t len;
1110         struct Conf_Oper *op;
1111
1112         assert( Line > 0 );
1113         assert( Var != NULL );
1114         assert( Arg != NULL );
1115         assert( Conf_Oper_Count > 0 );
1116
1117         op = array_alloc(&Conf_Opers, sizeof(*op), Conf_Oper_Count - 1);
1118         if (!op) {
1119                 Config_Error(LOG_ERR, "Could not allocate memory for operator (%d:%s = %s)", Line, Var, Arg);
1120                 return;
1121         }
1122
1123         if (strcasecmp(Var, "Name") == 0) {
1124                 /* Name of IRC operator */
1125                 len = strlcpy(op->name, Arg, sizeof(op->name));
1126                 if (len >= sizeof(op->name))
1127                                 Config_Error_TooLong(Line, Var);
1128                 return;
1129         }
1130         if (strcasecmp(Var, "Password") == 0) {
1131                 /* Password of IRC operator */
1132                 len = strlcpy(op->pwd, Arg, sizeof(op->pwd));
1133                 if (len >= sizeof(op->pwd))
1134                                 Config_Error_TooLong(Line, Var);
1135                 return;
1136         }
1137         if (strcasecmp(Var, "Mask") == 0) {
1138                 if (op->mask)
1139                         return; /* Hostname already configured */
1140                 op->mask = strdup_warn( Arg );
1141                 return;
1142         }
1143         Config_Error( LOG_ERR, "%s, line %d (section \"Operator\"): Unknown variable \"%s\"!",
1144                                                                 NGIRCd_ConfFile, Line, Var );
1145 } /* Handle_OPERATOR */
1146
1147
1148 static void
1149 Handle_SERVER( int Line, char *Var, char *Arg )
1150 {
1151         long port;
1152         size_t len;
1153         
1154         assert( Line > 0 );
1155         assert( Var != NULL );
1156         assert( Arg != NULL );
1157
1158         /* Ignore server block if no space is left in server configuration structure */
1159         if( New_Server_Idx <= NONE ) return;
1160
1161         if( strcasecmp( Var, "Host" ) == 0 ) {
1162                 /* Hostname of the server */
1163                 len = strlcpy( New_Server.host, Arg, sizeof( New_Server.host ));
1164                 if (len >= sizeof( New_Server.host ))
1165                         Config_Error_TooLong ( Line, Var );
1166                 return;
1167         }
1168         if( strcasecmp( Var, "Name" ) == 0 ) {
1169                 /* Name of the server ("Nick"/"ID") */
1170                 len = strlcpy( New_Server.name, Arg, sizeof( New_Server.name ));
1171                 if (len >= sizeof( New_Server.name ))
1172                         Config_Error_TooLong( Line, Var );
1173                 return;
1174         }
1175         if (strcasecmp(Var, "Bind") == 0) {
1176                 if (ng_ipaddr_init(&New_Server.bind_addr, Arg, 0))
1177                         return;
1178
1179                 Config_Error(LOG_ERR, "%s, line %d (section \"Server\"): Can't parse IP address \"%s\"",
1180                                 NGIRCd_ConfFile, Line, Arg);
1181                 return;
1182         }
1183         if( strcasecmp( Var, "MyPassword" ) == 0 ) {
1184                 /* Password of this server which is sent to the peer */
1185                 if (*Arg == ':') {
1186                         Config_Error(LOG_ERR,
1187                                 "%s, line %d (section \"Server\"): MyPassword must not start with ':'!",
1188                                                                                 NGIRCd_ConfFile, Line);
1189                 }
1190                 len = strlcpy( New_Server.pwd_in, Arg, sizeof( New_Server.pwd_in ));
1191                 if (len >= sizeof( New_Server.pwd_in ))
1192                         Config_Error_TooLong( Line, Var );
1193                 return;
1194         }
1195         if( strcasecmp( Var, "PeerPassword" ) == 0 ) {
1196                 /* Passwort of the peer which must be received */
1197                 len = strlcpy( New_Server.pwd_out, Arg, sizeof( New_Server.pwd_out ));
1198                 if (len >= sizeof( New_Server.pwd_out ))
1199                         Config_Error_TooLong( Line, Var );
1200                 return;
1201         }
1202         if( strcasecmp( Var, "Port" ) == 0 ) {
1203                 /* Port to which this server should connect */
1204                 port = atol( Arg );
1205                 if( port > 0 && port < 0xFFFF )
1206                         New_Server.port = (UINT16)port;
1207                 else
1208                         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Illegal port number %ld!",
1209                                                                                 NGIRCd_ConfFile, Line, port );
1210                 return;
1211         }
1212 #ifdef SSL_SUPPORT
1213         if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
1214                 New_Server.SSLConnect = Check_ArgIsTrue(Arg);
1215                 return;
1216         }
1217 #endif
1218         if( strcasecmp( Var, "Group" ) == 0 ) {
1219                 /* Server group */
1220 #ifdef HAVE_ISDIGIT
1221                 if( ! isdigit( (int)*Arg ))
1222                         Config_Error_NaN( Line, Var );
1223                 else
1224 #endif
1225                 New_Server.group = atoi( Arg );
1226                 return;
1227         }
1228         if( strcasecmp( Var, "Passive" ) == 0 ) {
1229                 if (Check_ArgIsTrue(Arg))
1230                         New_Server.flags |= CONF_SFLAG_DISABLED;
1231                 return;
1232         }
1233         if (strcasecmp(Var, "ServiceMask") == 0) {
1234                 len = strlcpy(New_Server.svs_mask, ngt_LowerStr(Arg),
1235                               sizeof(New_Server.svs_mask));
1236                 if (len >= sizeof(New_Server.svs_mask))
1237                         Config_Error_TooLong(Line, Var);
1238                 return;
1239         }
1240
1241         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Unknown variable \"%s\"!",
1242                                                                 NGIRCd_ConfFile, Line, Var );
1243 } /* Handle_SERVER */
1244
1245
1246 static bool
1247 Handle_Channelname(struct Conf_Channel *new_chan, const char *name)
1248 {
1249         size_t size = sizeof(new_chan->name);
1250         char *dest = new_chan->name;
1251
1252         if (!Channel_IsValidName(name)) {
1253                 /*
1254                  * maybe user forgot to add a '#'.
1255                  * This is only here for user convenience.
1256                  */
1257                 *dest = '#';
1258                 --size;
1259                 ++dest;
1260         }
1261         return size > strlcpy(dest, name, size);
1262 }
1263
1264
1265 static void
1266 Handle_CHANNEL(int Line, char *Var, char *Arg)
1267 {
1268         size_t len;
1269         size_t chancount;
1270         struct Conf_Channel *chan;
1271
1272         assert( Line > 0 );
1273         assert( Var != NULL );
1274         assert( Arg != NULL );
1275         assert(Conf_Channel_Count > 0);
1276
1277         chancount = Conf_Channel_Count - 1;
1278
1279         chan = array_alloc(&Conf_Channels, sizeof(*chan), chancount);
1280         if (!chan) {
1281                 Config_Error(LOG_ERR, "Could not allocate memory for predefined channel (%d:%s = %s)", Line, Var, Arg);
1282                 return;
1283         }
1284         if (strcasecmp(Var, "Name") == 0) {
1285                 if (!Handle_Channelname(chan, Arg))
1286                         Config_Error_TooLong(Line, Var);
1287                 return;
1288         }
1289         if (strcasecmp(Var, "Modes") == 0) {
1290                 /* Initial modes */
1291                 len = strlcpy(chan->modes, Arg, sizeof(chan->modes));
1292                 if (len >= sizeof(chan->modes))
1293                         Config_Error_TooLong( Line, Var );
1294                 return;
1295         }
1296         if( strcasecmp( Var, "Topic" ) == 0 ) {
1297                 /* Initial topic */
1298                 len = strlcpy(chan->topic, Arg, sizeof(chan->topic));
1299                 if (len >= sizeof(chan->topic))
1300                         Config_Error_TooLong( Line, Var );
1301                 return;
1302         }
1303         if( strcasecmp( Var, "Key" ) == 0 ) {
1304                 /* Initial Channel Key (mode k) */
1305                 len = strlcpy(chan->key, Arg, sizeof(chan->key));
1306                 if (len >= sizeof(chan->key))
1307                         Config_Error_TooLong(Line, Var);
1308                 return;
1309         }
1310         if( strcasecmp( Var, "MaxUsers" ) == 0 ) {
1311                 /* maximum user limit, mode l */
1312                 chan->maxusers = (unsigned long) atol(Arg);
1313                 if (chan->maxusers == 0)
1314                         Config_Error_NaN(Line, Var);
1315                 return;
1316         }
1317         if (strcasecmp(Var, "KeyFile") == 0) {
1318                 /* channel keys */
1319                 len = strlcpy(chan->keyfile, Arg, sizeof(chan->keyfile));
1320                 if (len >= sizeof(chan->keyfile))
1321                         Config_Error_TooLong(Line, Var);
1322                 return;
1323         }
1324
1325         Config_Error( LOG_ERR, "%s, line %d (section \"Channel\"): Unknown variable \"%s\"!",
1326                                                                 NGIRCd_ConfFile, Line, Var );
1327 } /* Handle_CHANNEL */
1328
1329
1330 static bool
1331 Validate_Config(bool Configtest, bool Rehash)
1332 {
1333         /* Validate configuration settings. */
1334
1335 #ifdef DEBUG
1336         int i, servers, servers_once;
1337 #endif
1338         bool config_valid = true;
1339         char *ptr;
1340
1341         /* Validate configured server name, see RFC 2812 section 2.3.1 */
1342         ptr = Conf_ServerName;
1343         do {
1344                 if (*ptr >= 'a' && *ptr <= 'z') continue;
1345                 if (*ptr >= 'A' && *ptr <= 'Z') continue;
1346                 if (*ptr >= '0' && *ptr <= '9') continue;
1347                 if (ptr > Conf_ServerName) {
1348                         if (*ptr == '.' || *ptr == '-')
1349                                 continue;
1350                 }
1351                 Conf_ServerName[0] = '\0';
1352                 break;
1353         } while (*(++ptr));
1354
1355         if (!Conf_ServerName[0]) {
1356                 /* No server name configured! */
1357                 config_valid = false;
1358                 Config_Error(LOG_ALERT,
1359                              "No (valid) server name configured in \"%s\" (section 'Global': 'Name')!",
1360                              NGIRCd_ConfFile);
1361                 if (!Configtest && !Rehash) {
1362                         Config_Error(LOG_ALERT,
1363                                      "%s exiting due to fatal errors!",
1364                                      PACKAGE_NAME);
1365                         exit(1);
1366                 }
1367         }
1368
1369         if (Conf_ServerName[0] && !strchr(Conf_ServerName, '.')) {
1370                 /* No dot in server name! */
1371                 config_valid = false;
1372                 Config_Error(LOG_ALERT,
1373                              "Invalid server name configured in \"%s\" (section 'Global': 'Name'): Dot missing!",
1374                              NGIRCd_ConfFile);
1375                 if (!Configtest) {
1376                         Config_Error(LOG_ALERT,
1377                                      "%s exiting due to fatal errors!",
1378                                      PACKAGE_NAME);
1379                         exit(1);
1380                 }
1381         }
1382
1383 #ifdef STRICT_RFC
1384         if (!Conf_ServerAdminMail[0]) {
1385                 /* No administrative contact configured! */
1386                 config_valid = false;
1387                 Config_Error(LOG_ALERT,
1388                              "No administrator email address configured in \"%s\" ('AdminEMail')!",
1389                              NGIRCd_ConfFile);
1390                 if (!Configtest) {
1391                         Config_Error(LOG_ALERT,
1392                                      "%s exiting due to fatal errors!",
1393                                      PACKAGE_NAME);
1394                         exit(1);
1395                 }
1396         }
1397 #endif
1398
1399         if (!Conf_ServerAdmin1[0] && !Conf_ServerAdmin2[0]
1400             && !Conf_ServerAdminMail[0]) {
1401                 /* No administrative information configured! */
1402                 Config_Error(LOG_WARNING,
1403                              "No administrative information configured but required by RFC!");
1404         }
1405
1406 #ifdef DEBUG
1407         servers = servers_once = 0;
1408         for (i = 0; i < MAX_SERVERS; i++) {
1409                 if (Conf_Server[i].name[0]) {
1410                         servers++;
1411                         if (Conf_Server[i].flags & CONF_SFLAG_ONCE)
1412                                 servers_once++;
1413                 }
1414         }
1415         Log(LOG_DEBUG,
1416             "Configuration: Operators=%d, Servers=%d[%d], Channels=%d",
1417             Conf_Oper_Count, servers, servers_once, Conf_Channel_Count);
1418 #endif
1419
1420         return config_valid;
1421 } /* Validate_Config */
1422
1423
1424 static void
1425 Config_Error_TooLong ( const int Line, const char *Item )
1426 {
1427         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" too long!", NGIRCd_ConfFile, Line, Item );
1428 }
1429
1430
1431 static void
1432 Config_Error_NaN( const int Line, const char *Item )
1433 {
1434         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" is not a number!",
1435                                                 NGIRCd_ConfFile, Line, Item );
1436 }
1437
1438
1439 #ifdef PROTOTYPES
1440 static void Config_Error( const int Level, const char *Format, ... )
1441 #else
1442 static void Config_Error( Level, Format, va_alist )
1443 const int Level;
1444 const char *Format;
1445 va_dcl
1446 #endif
1447 {
1448         /* Error! Write to console and/or logfile. */
1449
1450         char msg[MAX_LOG_MSG_LEN];
1451         va_list ap;
1452
1453         assert( Format != NULL );
1454
1455 #ifdef PROTOTYPES
1456         va_start( ap, Format );
1457 #else
1458         va_start( ap );
1459 #endif
1460         vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
1461         va_end( ap );
1462         
1463         /* During "normal operations" the log functions of the daemon should
1464          * be used, but during testing of the configuration file, all messages
1465          * should go directly to the console: */
1466         if (Use_Log) Log( Level, "%s", msg );
1467         else puts( msg );
1468 } /* Config_Error */
1469
1470
1471 static void
1472 Init_Server_Struct( CONF_SERVER *Server )
1473 {
1474         /* Initialize server configuration structur to default values */
1475
1476         assert( Server != NULL );
1477
1478         memset( Server, 0, sizeof (CONF_SERVER) );
1479
1480         Server->group = NONE;
1481         Server->lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
1482
1483         if( NGIRCd_Passive ) Server->flags = CONF_SFLAG_DISABLED;
1484
1485         Resolve_Init(&Server->res_stat);
1486         Server->conn_id = NONE;
1487         memset(&Server->bind_addr, 0, sizeof(&Server->bind_addr));
1488 } /* Init_Server_Struct */
1489
1490
1491 /* -eof- */