New configuration option "NoZeroConf" to disable ZeroConf registration
[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 "channel.h"
43 #include "defines.h"
44 #include "log.h"
45 #include "match.h"
46 #include "tool.h"
47
48 #include "exp.h"
49 #include "conf.h"
50
51
52 static bool Use_Log = true;
53 static CONF_SERVER New_Server;
54 static int New_Server_Idx;
55
56 static size_t Conf_Oper_Count;
57 static size_t Conf_Channel_Count;
58 static char Conf_MotdFile[FNAME_LEN];
59
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 #ifndef PAM
297         printf("  Password = %s\n", Conf_ServerPwd);
298 #endif
299         printf("  WebircPassword = %s\n", Conf_WebircPwd);
300         printf("  AdminInfo1 = %s\n", Conf_ServerAdmin1);
301         printf("  AdminInfo2 = %s\n", Conf_ServerAdmin2);
302         printf("  AdminEMail = %s\n", Conf_ServerAdminMail);
303         printf("  MotdFile = %s\n", Conf_MotdFile);
304         printf("  MotdPhrase = %.32s\n", array_bytes(&Conf_Motd) ? (const char*) array_start(&Conf_Motd) : "");
305         printf("  ChrootDir = %s\n", Conf_Chroot);
306         printf("  PidFile = %s\n", Conf_PidFile);
307         printf("  Listen = %s\n", Conf_ListenAddress);
308         fputs("  Ports = ", stdout);
309         ports_puts(&Conf_ListenPorts);
310 #ifdef SSL_SUPPORT
311         fputs("  SSLPorts = ", stdout);
312         ports_puts(&Conf_SSLOptions.ListenPorts);
313         if (!ConfSSL_Puts())
314                 config_valid = false;
315 #endif
316
317         pwd = getpwuid(Conf_UID);
318         if (pwd)
319                 printf("  ServerUID = %s\n", pwd->pw_name);
320         else
321                 printf("  ServerUID = %ld\n", (long)Conf_UID);
322         grp = getgrgid(Conf_GID);
323         if (grp)
324                 printf("  ServerGID = %s\n", grp->gr_name);
325         else
326                 printf("  ServerGID = %ld\n", (long)Conf_GID);
327 #ifdef SYSLOG
328         printf("  SyslogFacility = %s\n",
329                ngt_SyslogFacilityName(Conf_SyslogFacility));
330 #endif
331         printf("  PingTimeout = %d\n", Conf_PingTimeout);
332         printf("  PongTimeout = %d\n", Conf_PongTimeout);
333         printf("  ConnectRetry = %d\n", Conf_ConnectRetry);
334         printf("  OperCanUseMode = %s\n", yesno_to_str(Conf_OperCanMode));
335         printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
336         printf("  AllowRemoteOper = %s\n", yesno_to_str(Conf_AllowRemoteOper));
337         printf("  PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly));
338         printf("  NoDNS = %s\n", yesno_to_str(Conf_NoDNS));
339         printf("  NoIdent = %s\n", yesno_to_str(Conf_NoIdent));
340         printf("  NoPAM = %s\n", yesno_to_str(Conf_NoPAM));
341         printf("  NoZeroConf = %s\n", yesno_to_str(Conf_NoZeroConf));
342
343 #ifdef WANT_IPV6
344         printf("  ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6));
345         printf("  ConnectIPv6 = %s\n", yesno_to_str(Conf_ConnectIPv4));
346 #endif
347         printf("  MaxConnections = %ld\n", Conf_MaxConnections);
348         printf("  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
349         printf("  MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
350         printf("  MaxNickLength = %u\n\n", Conf_MaxNickLength - 1);
351
352         opers_puts();
353
354         for( i = 0; i < MAX_SERVERS; i++ ) {
355                 if( ! Conf_Server[i].name[0] ) continue;
356
357                 /* Valid "Server" section */
358                 puts( "[SERVER]" );
359                 printf( "  Name = %s\n", Conf_Server[i].name );
360                 printf( "  Host = %s\n", Conf_Server[i].host );
361                 printf( "  Port = %u\n", (unsigned int)Conf_Server[i].port );
362 #ifdef SSL_SUPPORT
363                 printf( "  SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
364 #endif
365                 printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
366                 printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
367                 printf( "  ServiceMask = %s\n", Conf_Server[i].svs_mask);
368                 printf( "  Group = %d\n", Conf_Server[i].group );
369                 printf( "  Passive = %s\n\n", Conf_Server[i].flags & CONF_SFLAG_DISABLED ? "yes" : "no");
370         }
371
372         predef_channel_count = array_length(&Conf_Channels, sizeof(*predef_chan));
373         predef_chan = array_start(&Conf_Channels);
374
375         for (i = 0; i < predef_channel_count; i++, predef_chan++) {
376                 if (!predef_chan->name[0])
377                         continue;
378
379                 /* Valid "Channel" section */
380                 puts( "[CHANNEL]" );
381                 printf("  Name = %s\n", predef_chan->name);
382                 printf("  Modes = %s\n", predef_chan->modes);
383                 printf("  Key = %s\n", predef_chan->key);
384                 printf("  MaxUsers = %lu\n", predef_chan->maxusers);
385                 printf("  Topic = %s\n", predef_chan->topic);
386                 printf("  KeyFile = %s\n\n", predef_chan->keyfile);
387         }
388
389         return (config_valid ? 0 : 1);
390 } /* Conf_Test */
391
392
393 GLOBAL void
394 Conf_UnsetServer( CONN_ID Idx )
395 {
396         /* Set next time for next connection attempt, if this is a server
397          * link that is (still) configured here. If the server is set as
398          * "once", delete it from our configuration.
399          * Non-Server-Connections will be silently ignored. */
400
401         int i;
402         time_t t;
403
404         /* Check all our configured servers */
405         for( i = 0; i < MAX_SERVERS; i++ ) {
406                 if( Conf_Server[i].conn_id != Idx ) continue;
407
408                 /* Gotcha! Mark server configuration as "unused": */
409                 Conf_Server[i].conn_id = NONE;
410
411                 if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
412                         /* Delete configuration here */
413                         Init_Server_Struct( &Conf_Server[i] );
414                 } else {
415                         /* Set time for next connect attempt */
416                         t = time(NULL);
417                         if (Conf_Server[i].lasttry < t - Conf_ConnectRetry) {
418                                 /* The connection has been "long", so we don't
419                                  * require the next attempt to be delayed. */
420                                 Conf_Server[i].lasttry =
421                                         t - Conf_ConnectRetry + RECONNECT_DELAY;
422                         } else
423                                 Conf_Server[i].lasttry = t;
424                 }
425         }
426 } /* Conf_UnsetServer */
427
428
429 GLOBAL void
430 Conf_SetServer( int ConfServer, CONN_ID Idx )
431 {
432         /* Set connection for specified configured server */
433
434         assert( ConfServer > NONE );
435         assert( Idx > NONE );
436
437         Conf_Server[ConfServer].conn_id = Idx;
438 } /* Conf_SetServer */
439
440
441 GLOBAL int
442 Conf_GetServer( CONN_ID Idx )
443 {
444         /* Get index of server in configuration structure */
445
446         int i = 0;
447
448         assert( Idx > NONE );
449
450         for( i = 0; i < MAX_SERVERS; i++ ) {
451                 if( Conf_Server[i].conn_id == Idx ) return i;
452         }
453         return NONE;
454 } /* Conf_GetServer */
455
456
457 GLOBAL bool
458 Conf_EnableServer( const char *Name, UINT16 Port )
459 {
460         /* Enable specified server and adjust port */
461
462         int i;
463
464         assert( Name != NULL );
465
466         for( i = 0; i < MAX_SERVERS; i++ ) {
467                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
468                         /* Gotcha! Set port and enable server: */
469                         Conf_Server[i].port = Port;
470                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
471                         return (Conf_Server[i].port && Conf_Server[i].host[0]);
472                 }
473         }
474         return false;
475 } /* Conf_EnableServer */
476
477
478 GLOBAL bool
479 Conf_EnablePassiveServer(const char *Name)
480 {
481         /* Enable specified server */
482         int i;
483
484         assert( Name != NULL );
485         for (i = 0; i < MAX_SERVERS; i++) {
486                 if ((strcasecmp( Conf_Server[i].name, Name ) == 0) && (Conf_Server[i].port > 0)) {
487                         /* BINGO! Enable server */
488                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
489                         return true;
490                 }
491         }
492         return false;
493 } /* Conf_EnablePassiveServer */
494
495
496 GLOBAL bool
497 Conf_DisableServer( const char *Name )
498 {
499         /* Enable specified server and adjust port */
500
501         int i;
502
503         assert( Name != NULL );
504
505         for( i = 0; i < MAX_SERVERS; i++ ) {
506                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
507                         /* Gotcha! Disable and disconnect server: */
508                         Conf_Server[i].flags |= CONF_SFLAG_DISABLED;
509                         if( Conf_Server[i].conn_id > NONE ) Conn_Close( Conf_Server[i].conn_id, NULL, "Server link terminated on operator request", true);
510                         return true;
511                 }
512         }
513         return false;
514 } /* Conf_DisableServer */
515
516
517 GLOBAL bool
518 Conf_AddServer( const char *Name, UINT16 Port, const char *Host, const char *MyPwd, const char *PeerPwd )
519 {
520         /* Add new server to configuration */
521
522         int i;
523
524         assert( Name != NULL );
525         assert( Host != NULL );
526         assert( MyPwd != NULL );
527         assert( PeerPwd != NULL );
528
529         /* Search unused item in server configuration structure */
530         for( i = 0; i < MAX_SERVERS; i++ ) {
531                 /* Is this item used? */
532                 if( ! Conf_Server[i].name[0] ) break;
533         }
534         if( i >= MAX_SERVERS ) return false;
535
536         Init_Server_Struct( &Conf_Server[i] );
537         strlcpy( Conf_Server[i].name, Name, sizeof( Conf_Server[i].name ));
538         strlcpy( Conf_Server[i].host, Host, sizeof( Conf_Server[i].host ));
539         strlcpy( Conf_Server[i].pwd_out, MyPwd, sizeof( Conf_Server[i].pwd_out ));
540         strlcpy( Conf_Server[i].pwd_in, PeerPwd, sizeof( Conf_Server[i].pwd_in ));
541         Conf_Server[i].port = Port;
542         Conf_Server[i].flags = CONF_SFLAG_ONCE;
543
544         return true;
545 } /* Conf_AddServer */
546
547
548 /**
549  * Check if the given nick name is an service
550  */
551 GLOBAL bool
552 Conf_IsService(int ConfServer, const char *Nick)
553 {
554         return MatchCaseInsensitive(Conf_Server[ConfServer].svs_mask, Nick);
555 } /* Conf_IsService */
556
557
558 /**
559  * Initialize configuration settings with their default values.
560  */
561 static void
562 Set_Defaults(bool InitServers)
563 {
564         int i;
565
566         strcpy(Conf_ServerName, "");
567         snprintf(Conf_ServerInfo, sizeof Conf_ServerInfo, "%s %s",
568                  PACKAGE_NAME, PACKAGE_VERSION);
569         strcpy(Conf_ServerPwd, "");
570
571         strcpy(Conf_ServerAdmin1, "");
572         strcpy(Conf_ServerAdmin2, "");
573         strcpy(Conf_ServerAdminMail, "");
574
575         strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
576         strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
577
578         Conf_UID = Conf_GID = 0;
579         strlcpy(Conf_Chroot, CHROOT_DIR, sizeof(Conf_Chroot));
580         strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
581
582         free(Conf_ListenAddress);
583         Conf_ListenAddress = NULL;
584
585         Conf_PingTimeout = 120;
586         Conf_PongTimeout = 20;
587         Conf_ConnectRetry = 60;
588         Conf_NoDNS = false;
589         Conf_NoIdent = false;
590         Conf_NoPAM = false;
591         Conf_NoZeroConf = false;
592
593         Conf_Oper_Count = 0;
594         Conf_Channel_Count = 0;
595
596         Conf_OperCanMode = false;
597         Conf_OperServerMode = false;
598         Conf_AllowRemoteOper = false;
599         Conf_PredefChannelsOnly = false;
600
601         Conf_ConnectIPv4 = true;
602         Conf_ConnectIPv6 = true;
603
604         Conf_MaxConnections = 0;
605         Conf_MaxConnectionsIP = 5;
606         Conf_MaxJoins = 10;
607         Conf_MaxNickLength = CLIENT_NICK_LEN_DEFAULT;
608
609 #ifdef SYSLOG
610 #ifdef LOG_LOCAL5
611         Conf_SyslogFacility = LOG_LOCAL5;
612 #else
613         Conf_SyslogFacility = 0;
614 #endif
615 #endif
616
617         /* Initialize server configuration structures */
618         if (InitServers) {
619                 for (i = 0; i < MAX_SERVERS;
620                      Init_Server_Struct(&Conf_Server[i++]));
621         }
622 } /* Set_Defaults */
623
624
625 static bool
626 no_listenports(void)
627 {
628         size_t cnt = array_bytes(&Conf_ListenPorts);
629 #ifdef SSL_SUPPORT
630         cnt += array_bytes(&Conf_SSLOptions.ListenPorts);
631 #endif
632         return cnt == 0;
633 }
634
635 static void
636 Read_Motd(const char *filename)
637 {
638         char line[127];
639         FILE *fp;
640
641         if (*filename == '\0')
642                 return;
643
644         fp = fopen(filename, "r");
645         if (!fp) {
646                 Log(LOG_WARNING, "Can't read MOTD file \"%s\": %s",
647                                         filename, strerror(errno));
648                 return;
649         }
650
651         array_free(&Conf_Motd);
652
653         while (fgets(line, (int)sizeof line, fp)) {
654                 ngt_TrimLastChr( line, '\n');
655
656                 /* add text including \0 */
657                 if (!array_catb(&Conf_Motd, line, strlen(line) + 1)) {
658                         Log(LOG_WARNING, "Cannot add MOTD text: %s", strerror(errno));
659                         break;
660                 }
661         }
662         fclose(fp);
663 }
664
665 static bool
666 Read_Config( bool ngircd_starting )
667 {
668         /* Read configuration file. */
669
670         char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
671         const UINT16 defaultport = 6667;
672         int line, i, n;
673         FILE *fd;
674
675         /* Open configuration file */
676         fd = fopen( NGIRCd_ConfFile, "r" );
677         if( ! fd ) {
678                 /* No configuration file found! */
679                 Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s",
680                                         NGIRCd_ConfFile, strerror( errno ));
681                 if (!ngircd_starting)
682                         return false;
683                 Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
684                 exit( 1 );
685         }
686
687         opers_free();
688         Set_Defaults( ngircd_starting );
689
690         Config_Error( LOG_INFO, "Reading configuration from \"%s\" ...", NGIRCd_ConfFile );
691
692         /* Clean up server configuration structure: mark all already
693          * configured servers as "once" so that they are deleted
694          * after the next disconnect and delete all unused servers.
695          * And delete all servers which are "duplicates" of servers
696          * that are already marked as "once" (such servers have been
697          * created by the last rehash but are now useless). */
698         for( i = 0; i < MAX_SERVERS; i++ ) {
699                 if( Conf_Server[i].conn_id == NONE ) Init_Server_Struct( &Conf_Server[i] );
700                 else {
701                         /* This structure is in use ... */
702                         if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
703                                 /* Check for duplicates */
704                                 for( n = 0; n < MAX_SERVERS; n++ ) {
705                                         if( n == i ) continue;
706
707                                         if( Conf_Server[i].conn_id == Conf_Server[n].conn_id ) {
708                                                 Init_Server_Struct( &Conf_Server[n] );
709 #ifdef DEBUG
710                                                 Log(LOG_DEBUG,"Deleted unused duplicate server %d (kept %d).",
711                                                                                                 n, i );
712 #endif
713                                         }
714                                 }
715                         } else {
716                                 /* Mark server as "once" */
717                                 Conf_Server[i].flags |= CONF_SFLAG_ONCE;
718                                 Log( LOG_DEBUG, "Marked server %d as \"once\"", i );
719                         }
720                 }
721         }
722
723         /* Initialize variables */
724         line = 0;
725         strcpy( section, "" );
726         Init_Server_Struct( &New_Server );
727         New_Server_Idx = NONE;
728 #ifdef SSL_SUPPORT
729         ConfSSL_Init();
730 #endif
731         /* Read configuration file */
732         while( true ) {
733                 if( ! fgets( str, LINE_LEN, fd )) break;
734                 ngt_TrimStr( str );
735                 line++;
736
737                 /* Skip comments and empty lines */
738                 if( str[0] == ';' || str[0] == '#' || str[0] == '\0' ) continue;
739
740                 /* Is this the beginning of a new section? */
741                 if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' )) {
742                         strlcpy( section, str, sizeof( section ));
743                         if( strcasecmp( section, "[GLOBAL]" ) == 0 )
744                                 continue;
745
746                         if( strcasecmp( section, "[SERVER]" ) == 0 ) {
747                                 /* Check if there is already a server to add */
748                                 if( New_Server.name[0] ) {
749                                         /* Copy data to "real" server structure */
750                                         assert( New_Server_Idx > NONE );
751                                         Conf_Server[New_Server_Idx] = New_Server;
752                                 }
753
754                                 /* Re-init structure for new server */
755                                 Init_Server_Struct( &New_Server );
756
757                                 /* Search unused item in server configuration structure */
758                                 for( i = 0; i < MAX_SERVERS; i++ ) {
759                                         /* Is this item used? */
760                                         if( ! Conf_Server[i].name[0] ) break;
761                                 }
762                                 if( i >= MAX_SERVERS ) {
763                                         /* Oops, no free item found! */
764                                         Config_Error( LOG_ERR, "Too many servers configured." );
765                                         New_Server_Idx = NONE;
766                                 }
767                                 else New_Server_Idx = i;
768                                 continue;
769                         }
770                         if (strcasecmp(section, "[CHANNEL]") == 0) {
771                                 Conf_Channel_Count++;
772                                 continue;
773                         }
774                         if (strcasecmp(section, "[OPERATOR]") == 0) {
775                                 Conf_Oper_Count++;
776                                 continue;
777                         }
778
779                         Config_Error( LOG_ERR, "%s, line %d: Unknown section \"%s\"!", NGIRCd_ConfFile, line, section );
780                         section[0] = 0x1;
781                 }
782                 if( section[0] == 0x1 ) continue;
783
784                 /* Split line into variable name and parameters */
785                 ptr = strchr( str, '=' );
786                 if( ! ptr ) {
787                         Config_Error( LOG_ERR, "%s, line %d: Syntax error!", NGIRCd_ConfFile, line );
788                         continue;
789                 }
790                 *ptr = '\0';
791                 var = str; ngt_TrimStr( var );
792                 arg = ptr + 1; ngt_TrimStr( arg );
793
794                 if( strcasecmp( section, "[GLOBAL]" ) == 0 ) Handle_GLOBAL( line, var, arg );
795                 else if( strcasecmp( section, "[OPERATOR]" ) == 0 ) Handle_OPERATOR( line, var, arg );
796                 else if( strcasecmp( section, "[SERVER]" ) == 0 ) Handle_SERVER( line, var, arg );
797                 else if( strcasecmp( section, "[CHANNEL]" ) == 0 ) Handle_CHANNEL( line, var, arg );
798                 else Config_Error( LOG_ERR, "%s, line %d: Variable \"%s\" outside section!", NGIRCd_ConfFile, line, var );
799         }
800
801         /* Close configuration file */
802         fclose( fd );
803
804         /* Check if there is still a server to add */
805         if( New_Server.name[0] ) {
806                 /* Copy data to "real" server structure */
807                 assert( New_Server_Idx > NONE );
808                 Conf_Server[New_Server_Idx] = New_Server;
809         }
810
811         /* not a single listening port? Add default. */
812         if (no_listenports() &&
813                 !array_copyb(&Conf_ListenPorts, (char*) &defaultport, sizeof defaultport))
814         {
815                 Config_Error(LOG_ALERT, "Could not add default listening Port %u: %s",
816                                         (unsigned int) defaultport, strerror(errno));
817
818                 exit(1);
819         }
820
821         if (!Conf_ListenAddress)
822                 Conf_ListenAddress = strdup_warn(DEFAULT_LISTEN_ADDRSTR);
823
824         if (!Conf_ListenAddress) {
825                 Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
826                 exit(1);
827         }
828
829         /* No MOTD phrase configured? (re)try motd file. */
830         if (array_bytes(&Conf_Motd) == 0)
831                 Read_Motd(Conf_MotdFile);
832         return true;
833 } /* Read_Config */
834
835
836 static bool
837 Check_ArgIsTrue( const char *Arg )
838 {
839         if( strcasecmp( Arg, "yes" ) == 0 ) return true;
840         if( strcasecmp( Arg, "true" ) == 0 ) return true;
841         if( atoi( Arg ) != 0 ) return true;
842
843         return false;
844 } /* Check_ArgIsTrue */
845
846
847 static unsigned int Handle_MaxNickLength(int Line, const char *Arg)
848 {
849         unsigned new;
850
851         new = (unsigned) atoi(Arg) + 1;
852         if (new > CLIENT_NICK_LEN) {
853                 Config_Error(LOG_WARNING,
854                              "%s, line %d: Value of \"MaxNickLength\" exceeds %u!",
855                              NGIRCd_ConfFile, Line, CLIENT_NICK_LEN - 1);
856                 return CLIENT_NICK_LEN;
857         }
858         if (new < 2) {
859                 Config_Error(LOG_WARNING,
860                              "%s, line %d: Value of \"MaxNickLength\" must be at least 1!",
861                              NGIRCd_ConfFile, Line);
862                 return 2;
863         }
864         return new;
865 } /* Handle_MaxNickLength */
866
867
868
869 static void
870 Handle_GLOBAL( int Line, char *Var, char *Arg )
871 {
872         struct passwd *pwd;
873         struct group *grp;
874         size_t len;
875         
876         assert( Line > 0 );
877         assert( Var != NULL );
878         assert( Arg != NULL );
879         
880         if( strcasecmp( Var, "Name" ) == 0 ) {
881                 /* Server name */
882                 len = strlcpy( Conf_ServerName, Arg, sizeof( Conf_ServerName ));
883                 if (len >= sizeof( Conf_ServerName ))
884                         Config_Error_TooLong( Line, Var );
885                 return;
886         }
887         if( strcasecmp( Var, "Info" ) == 0 ) {
888                 /* Info text of server */
889                 len = strlcpy( Conf_ServerInfo, Arg, sizeof( Conf_ServerInfo ));
890                 if (len >= sizeof( Conf_ServerInfo ))
891                         Config_Error_TooLong ( Line, Var );
892                 return;
893         }
894         if( strcasecmp( Var, "Password" ) == 0 ) {
895                 /* Global server password */
896                 len = strlcpy( Conf_ServerPwd, Arg, sizeof( Conf_ServerPwd ));
897                 if (len >= sizeof( Conf_ServerPwd ))
898                         Config_Error_TooLong( Line, Var );
899                 return;
900         }
901         if (strcasecmp(Var, "WebircPassword") == 0) {
902                 /* Password required for WEBIRC command */
903                 len = strlcpy(Conf_WebircPwd, Arg, sizeof(Conf_WebircPwd));
904                 if (len >= sizeof(Conf_WebircPwd))
905                         Config_Error_TooLong(Line, Var);
906                 return;
907         }
908         if( strcasecmp( Var, "AdminInfo1" ) == 0 ) {
909                 /* Administrative info #1 */
910                 len = strlcpy( Conf_ServerAdmin1, Arg, sizeof( Conf_ServerAdmin1 ));
911                 if (len >= sizeof( Conf_ServerAdmin1 ))
912                         Config_Error_TooLong ( Line, Var );
913                 return;
914         }
915         if( strcasecmp( Var, "AdminInfo2" ) == 0 ) {
916                 /* Administrative info #2 */
917                 len = strlcpy( Conf_ServerAdmin2, Arg, sizeof( Conf_ServerAdmin2 ));
918                 if (len >= sizeof( Conf_ServerAdmin2 ))
919                         Config_Error_TooLong ( Line, Var );
920                 return;
921         }
922         if( strcasecmp( Var, "AdminEMail" ) == 0 ) {
923                 /* Administrative email contact */
924                 len = strlcpy( Conf_ServerAdminMail, Arg, sizeof( Conf_ServerAdminMail ));
925                 if (len >= sizeof( Conf_ServerAdminMail ))
926                         Config_Error_TooLong( Line, Var );
927                 return;
928         }
929
930         if( strcasecmp( Var, "Ports" ) == 0 ) {
931                 ports_parse(&Conf_ListenPorts, Line, Arg);
932                 return;
933         }
934         if( strcasecmp( Var, "MotdFile" ) == 0 ) {
935                 len = strlcpy( Conf_MotdFile, Arg, sizeof( Conf_MotdFile ));
936                 if (len >= sizeof( Conf_MotdFile ))
937                         Config_Error_TooLong( Line, Var );
938                 Read_Motd(Arg);
939                 return;
940         }
941         if( strcasecmp( Var, "MotdPhrase" ) == 0 ) {
942                 /* "Message of the day" phrase (instead of file) */
943                 len = strlen(Arg);
944                 if (len == 0)
945                         return;
946                 if (len >= LINE_LEN) {
947                         Config_Error_TooLong( Line, Var );
948                         return;
949                 }
950                 if (!array_copyb(&Conf_Motd, Arg, len + 1))
951                         Config_Error(LOG_WARNING, "%s, line %d: Could not append MotdPhrase: %s",
952                                                         NGIRCd_ConfFile, Line, strerror(errno));
953                 return;
954         }
955         if( strcasecmp( Var, "ChrootDir" ) == 0 ) {
956                 /* directory for chroot() */
957                 len = strlcpy( Conf_Chroot, Arg, sizeof( Conf_Chroot ));
958                 if (len >= sizeof( Conf_Chroot ))
959                         Config_Error_TooLong( Line, Var );
960                 return;
961         }
962         if ( strcasecmp( Var, "PidFile" ) == 0 ) {
963                 /* name of pidfile */
964                 len = strlcpy( Conf_PidFile, Arg, sizeof( Conf_PidFile ));
965                 if (len >= sizeof( Conf_PidFile ))
966                         Config_Error_TooLong( Line, Var );
967                 return;
968         }
969         if( strcasecmp( Var, "ServerUID" ) == 0 ) {
970                 /* UID the daemon should switch to */
971                 pwd = getpwnam( Arg );
972                 if( pwd ) Conf_UID = pwd->pw_uid;
973                 else {
974 #ifdef HAVE_ISDIGIT
975                         if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
976                         else
977 #endif
978                         Conf_UID = (unsigned int)atoi( Arg );
979                 }
980                 return;
981         }
982         if( strcasecmp( Var, "ServerGID" ) == 0 ) {
983                 /* GID the daemon should use */
984                 grp = getgrnam( Arg );
985                 if( grp ) Conf_GID = grp->gr_gid;
986                 else {
987 #ifdef HAVE_ISDIGIT
988                         if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
989                         else
990 #endif
991                         Conf_GID = (unsigned int)atoi( Arg );
992                 }
993                 return;
994         }
995         if( strcasecmp( Var, "PingTimeout" ) == 0 ) {
996                 /* PING timeout */
997                 Conf_PingTimeout = atoi( Arg );
998                 if( Conf_PingTimeout < 5 ) {
999                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PingTimeout\" too low!",
1000                                                                         NGIRCd_ConfFile, Line );
1001                         Conf_PingTimeout = 5;
1002                 }
1003                 return;
1004         }
1005         if( strcasecmp( Var, "PongTimeout" ) == 0 ) {
1006                 /* PONG timeout */
1007                 Conf_PongTimeout = atoi( Arg );
1008                 if( Conf_PongTimeout < 5 ) {
1009                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"PongTimeout\" too low!",
1010                                                                         NGIRCd_ConfFile, Line );
1011                         Conf_PongTimeout = 5;
1012                 }
1013                 return;
1014         }
1015         if( strcasecmp( Var, "ConnectRetry" ) == 0 ) {
1016                 /* Seconds between connection attempts to other servers */
1017                 Conf_ConnectRetry = atoi( Arg );
1018                 if( Conf_ConnectRetry < 5 ) {
1019                         Config_Error( LOG_WARNING, "%s, line %d: Value of \"ConnectRetry\" too low!",
1020                                                                         NGIRCd_ConfFile, Line );
1021                         Conf_ConnectRetry = 5;
1022                 }
1023                 return;
1024         }
1025         if( strcasecmp( Var, "PredefChannelsOnly" ) == 0 ) {
1026                 /* Should we only allow pre-defined-channels? (i.e. users cannot create their own channels) */
1027                 Conf_PredefChannelsOnly = Check_ArgIsTrue( Arg );
1028                 return;
1029         }
1030         if( strcasecmp( Var, "NoDNS" ) == 0 ) {
1031                 /* don't do reverse dns lookups when clients connect? */
1032                 Conf_NoDNS = Check_ArgIsTrue( Arg );
1033                 return;
1034         }
1035         if (strcasecmp(Var, "NoIdent") == 0) {
1036                 /* don't do IDENT lookups when clients connect? */
1037                 Conf_NoIdent = Check_ArgIsTrue(Arg);
1038 #ifndef IDENTAUTH
1039                 if (!Conf_NoIdent) {
1040                         /* user has enabled ident lookups explicitly, but ... */
1041                         Config_Error(LOG_WARNING,
1042                                 "%s: line %d: NoIdent=False, but ngircd was built without IDENT support",
1043                                 NGIRCd_ConfFile, Line);
1044                 }
1045 #endif
1046                 return;
1047         }
1048         if(strcasecmp(Var, "NoPAM") == 0) {
1049                 /* don't use PAM library to authenticate users */
1050                 Conf_NoPAM = Check_ArgIsTrue(Arg);
1051                 return;
1052         }
1053         if(strcasecmp(Var, "NoZeroConf") == 0) {
1054                 /* don't register services using ZeroConf */
1055                 Conf_NoZeroConf = Check_ArgIsTrue(Arg);
1056                 return;
1057         }
1058 #ifdef WANT_IPV6
1059         /* the default setting for all the WANT_IPV6 special options is 'true' */
1060         if( strcasecmp( Var, "ConnectIPv6" ) == 0 ) {
1061                 /* connect to other hosts using ipv6, if they have an AAAA record? */
1062                 Conf_ConnectIPv6 = Check_ArgIsTrue( Arg );
1063                 return;
1064         }
1065         if( strcasecmp( Var, "ConnectIPv4" ) == 0 ) {
1066                 /* connect to other hosts using ipv4.
1067                  * again, this can be used for ipv6-only setups */
1068                 Conf_ConnectIPv4 = Check_ArgIsTrue( Arg );
1069                 return;
1070         }
1071 #endif
1072         if( strcasecmp( Var, "OperCanUseMode" ) == 0 ) {
1073                 /* Are IRC operators allowed to use MODE in channels they aren't Op in? */
1074                 Conf_OperCanMode = Check_ArgIsTrue( Arg );
1075                 return;
1076         }
1077         if( strcasecmp( Var, "OperServerMode" ) == 0 ) {
1078                 /* Mask IRC operator as if coming from the server? (ircd-irc2 compat hack) */
1079                 Conf_OperServerMode = Check_ArgIsTrue( Arg );
1080                 return;
1081         }
1082         if(strcasecmp(Var, "AllowRemoteOper") == 0) {
1083                 /* Are remote IRC operators allowed to control this server? */
1084                 Conf_AllowRemoteOper = Check_ArgIsTrue(Arg);
1085                 return;
1086         }
1087         if( strcasecmp( Var, "MaxConnections" ) == 0 ) {
1088                 /* Maximum number of connections. 0 -> "no limit". */
1089 #ifdef HAVE_ISDIGIT
1090                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var);
1091                 else
1092 #endif
1093                 Conf_MaxConnections = atol( Arg );
1094                 return;
1095         }
1096         if( strcasecmp( Var, "MaxConnectionsIP" ) == 0 ) {
1097                 /* Maximum number of simultaneous connections from one IP. 0 -> "no limit" */
1098 #ifdef HAVE_ISDIGIT
1099                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
1100                 else
1101 #endif
1102                 Conf_MaxConnectionsIP = atoi( Arg );
1103                 return;
1104         }
1105         if( strcasecmp( Var, "MaxJoins" ) == 0 ) {
1106                 /* Maximum number of channels a user can join. 0 -> "no limit". */
1107 #ifdef HAVE_ISDIGIT
1108                 if( ! isdigit( (int)*Arg )) Config_Error_NaN( Line, Var );
1109                 else
1110 #endif
1111                 Conf_MaxJoins = atoi( Arg );
1112                 return;
1113         }
1114         if( strcasecmp( Var, "MaxNickLength" ) == 0 ) {
1115                 /* Maximum length of a nick name; must be same on all servers
1116                  * within the IRC network! */
1117                 Conf_MaxNickLength = Handle_MaxNickLength(Line, Arg);
1118                 return;
1119         }
1120
1121         if( strcasecmp( Var, "Listen" ) == 0 ) {
1122                 /* IP-Address to bind sockets */
1123                 if (Conf_ListenAddress) {
1124                         Config_Error(LOG_ERR, "Multiple Listen= options, ignoring: %s", Arg);
1125                         return;
1126                 }
1127                 Conf_ListenAddress = strdup_warn(Arg);
1128                 /*
1129                  * if allocation fails, we're in trouble:
1130                  * we cannot ignore the error -- otherwise ngircd
1131                  * would listen on all interfaces.
1132                  */
1133                 if (!Conf_ListenAddress) {
1134                         Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
1135                         exit(1);
1136                 }
1137                 return;
1138         }
1139
1140 #ifdef SSL_SUPPORT
1141         if( strcasecmp( Var, "SSLPorts" ) == 0 ) {
1142                 ports_parse(&Conf_SSLOptions.ListenPorts, Line, Arg);
1143                 return;
1144         }
1145
1146         if( strcasecmp( Var, "SSLKeyFile" ) == 0 ) {
1147                 assert(Conf_SSLOptions.KeyFile == NULL );
1148                 Conf_SSLOptions.KeyFile = strdup_warn(Arg);
1149                 return;
1150         }
1151         if( strcasecmp( Var, "SSLCertFile" ) == 0 ) {
1152                 assert(Conf_SSLOptions.CertFile == NULL );
1153                 Conf_SSLOptions.CertFile = strdup_warn(Arg);
1154                 return;
1155         }
1156
1157         if( strcasecmp( Var, "SSLKeyFilePassword" ) == 0 ) {
1158                 assert(array_bytes(&Conf_SSLOptions.KeyFilePassword) == 0);
1159                 if (!array_copys(&Conf_SSLOptions.KeyFilePassword, Arg))
1160                         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Could not copy %s: %s!",
1161                                                                 NGIRCd_ConfFile, Line, Var, strerror(errno));
1162                 return;
1163         }
1164         if( strcasecmp( Var, "SSLDHFile" ) == 0 ) {
1165                 assert(Conf_SSLOptions.DHFile == NULL);
1166                 Conf_SSLOptions.DHFile = strdup_warn( Arg );
1167                 return;
1168         }
1169 #endif
1170 #ifdef SYSLOG
1171         if (strcasecmp(Var, "SyslogFacility") == 0) {
1172                 Conf_SyslogFacility = ngt_SyslogFacilityID(Arg,
1173                                                            Conf_SyslogFacility);
1174                 return;
1175         }
1176 #endif
1177         Config_Error(LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!",
1178                                                                 NGIRCd_ConfFile, Line, Var);
1179 } /* Handle_GLOBAL */
1180
1181
1182 static void
1183 Handle_OPERATOR( int Line, char *Var, char *Arg )
1184 {
1185         size_t len;
1186         struct Conf_Oper *op;
1187
1188         assert( Line > 0 );
1189         assert( Var != NULL );
1190         assert( Arg != NULL );
1191         assert( Conf_Oper_Count > 0 );
1192
1193         op = array_alloc(&Conf_Opers, sizeof(*op), Conf_Oper_Count - 1);
1194         if (!op) {
1195                 Config_Error(LOG_ERR, "Could not allocate memory for operator (%d:%s = %s)", Line, Var, Arg);
1196                 return;
1197         }
1198
1199         if (strcasecmp(Var, "Name") == 0) {
1200                 /* Name of IRC operator */
1201                 len = strlcpy(op->name, Arg, sizeof(op->name));
1202                 if (len >= sizeof(op->name))
1203                                 Config_Error_TooLong(Line, Var);
1204                 return;
1205         }
1206         if (strcasecmp(Var, "Password") == 0) {
1207                 /* Password of IRC operator */
1208                 len = strlcpy(op->pwd, Arg, sizeof(op->pwd));
1209                 if (len >= sizeof(op->pwd))
1210                                 Config_Error_TooLong(Line, Var);
1211                 return;
1212         }
1213         if (strcasecmp(Var, "Mask") == 0) {
1214                 if (op->mask)
1215                         return; /* Hostname already configured */
1216                 op->mask = strdup_warn( Arg );
1217                 return;
1218         }
1219         Config_Error( LOG_ERR, "%s, line %d (section \"Operator\"): Unknown variable \"%s\"!",
1220                                                                 NGIRCd_ConfFile, Line, Var );
1221 } /* Handle_OPERATOR */
1222
1223
1224 static void
1225 Handle_SERVER( int Line, char *Var, char *Arg )
1226 {
1227         long port;
1228         size_t len;
1229         
1230         assert( Line > 0 );
1231         assert( Var != NULL );
1232         assert( Arg != NULL );
1233
1234         /* Ignore server block if no space is left in server configuration structure */
1235         if( New_Server_Idx <= NONE ) return;
1236
1237         if( strcasecmp( Var, "Host" ) == 0 ) {
1238                 /* Hostname of the server */
1239                 len = strlcpy( New_Server.host, Arg, sizeof( New_Server.host ));
1240                 if (len >= sizeof( New_Server.host ))
1241                         Config_Error_TooLong ( Line, Var );
1242                 return;
1243         }
1244         if( strcasecmp( Var, "Name" ) == 0 ) {
1245                 /* Name of the server ("Nick"/"ID") */
1246                 len = strlcpy( New_Server.name, Arg, sizeof( New_Server.name ));
1247                 if (len >= sizeof( New_Server.name ))
1248                         Config_Error_TooLong( Line, Var );
1249                 return;
1250         }
1251         if (strcasecmp(Var, "Bind") == 0) {
1252                 if (ng_ipaddr_init(&New_Server.bind_addr, Arg, 0))
1253                         return;
1254
1255                 Config_Error(LOG_ERR, "%s, line %d (section \"Server\"): Can't parse IP address \"%s\"",
1256                                 NGIRCd_ConfFile, Line, Arg);
1257                 return;
1258         }
1259         if( strcasecmp( Var, "MyPassword" ) == 0 ) {
1260                 /* Password of this server which is sent to the peer */
1261                 if (*Arg == ':') {
1262                         Config_Error(LOG_ERR,
1263                                 "%s, line %d (section \"Server\"): MyPassword must not start with ':'!",
1264                                                                                 NGIRCd_ConfFile, Line);
1265                 }
1266                 len = strlcpy( New_Server.pwd_in, Arg, sizeof( New_Server.pwd_in ));
1267                 if (len >= sizeof( New_Server.pwd_in ))
1268                         Config_Error_TooLong( Line, Var );
1269                 return;
1270         }
1271         if( strcasecmp( Var, "PeerPassword" ) == 0 ) {
1272                 /* Passwort of the peer which must be received */
1273                 len = strlcpy( New_Server.pwd_out, Arg, sizeof( New_Server.pwd_out ));
1274                 if (len >= sizeof( New_Server.pwd_out ))
1275                         Config_Error_TooLong( Line, Var );
1276                 return;
1277         }
1278         if( strcasecmp( Var, "Port" ) == 0 ) {
1279                 /* Port to which this server should connect */
1280                 port = atol( Arg );
1281                 if( port > 0 && port < 0xFFFF )
1282                         New_Server.port = (UINT16)port;
1283                 else
1284                         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Illegal port number %ld!",
1285                                                                                 NGIRCd_ConfFile, Line, port );
1286                 return;
1287         }
1288 #ifdef SSL_SUPPORT
1289         if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
1290                 New_Server.SSLConnect = Check_ArgIsTrue(Arg);
1291                 return;
1292         }
1293 #endif
1294         if( strcasecmp( Var, "Group" ) == 0 ) {
1295                 /* Server group */
1296 #ifdef HAVE_ISDIGIT
1297                 if( ! isdigit( (int)*Arg ))
1298                         Config_Error_NaN( Line, Var );
1299                 else
1300 #endif
1301                 New_Server.group = atoi( Arg );
1302                 return;
1303         }
1304         if( strcasecmp( Var, "Passive" ) == 0 ) {
1305                 if (Check_ArgIsTrue(Arg))
1306                         New_Server.flags |= CONF_SFLAG_DISABLED;
1307                 return;
1308         }
1309         if (strcasecmp(Var, "ServiceMask") == 0) {
1310                 len = strlcpy(New_Server.svs_mask, ngt_LowerStr(Arg),
1311                               sizeof(New_Server.svs_mask));
1312                 if (len >= sizeof(New_Server.svs_mask))
1313                         Config_Error_TooLong(Line, Var);
1314                 return;
1315         }
1316
1317         Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Unknown variable \"%s\"!",
1318                                                                 NGIRCd_ConfFile, Line, Var );
1319 } /* Handle_SERVER */
1320
1321
1322 static bool
1323 Handle_Channelname(struct Conf_Channel *new_chan, const char *name)
1324 {
1325         size_t size = sizeof(new_chan->name);
1326         char *dest = new_chan->name;
1327
1328         if (!Channel_IsValidName(name)) {
1329                 /*
1330                  * maybe user forgot to add a '#'.
1331                  * This is only here for user convenience.
1332                  */
1333                 *dest = '#';
1334                 --size;
1335                 ++dest;
1336         }
1337         return size > strlcpy(dest, name, size);
1338 }
1339
1340
1341 static void
1342 Handle_CHANNEL(int Line, char *Var, char *Arg)
1343 {
1344         size_t len;
1345         size_t chancount;
1346         struct Conf_Channel *chan;
1347
1348         assert( Line > 0 );
1349         assert( Var != NULL );
1350         assert( Arg != NULL );
1351         assert(Conf_Channel_Count > 0);
1352
1353         chancount = Conf_Channel_Count - 1;
1354
1355         chan = array_alloc(&Conf_Channels, sizeof(*chan), chancount);
1356         if (!chan) {
1357                 Config_Error(LOG_ERR, "Could not allocate memory for predefined channel (%d:%s = %s)", Line, Var, Arg);
1358                 return;
1359         }
1360         if (strcasecmp(Var, "Name") == 0) {
1361                 if (!Handle_Channelname(chan, Arg))
1362                         Config_Error_TooLong(Line, Var);
1363                 return;
1364         }
1365         if (strcasecmp(Var, "Modes") == 0) {
1366                 /* Initial modes */
1367                 len = strlcpy(chan->modes, Arg, sizeof(chan->modes));
1368                 if (len >= sizeof(chan->modes))
1369                         Config_Error_TooLong( Line, Var );
1370                 return;
1371         }
1372         if( strcasecmp( Var, "Topic" ) == 0 ) {
1373                 /* Initial topic */
1374                 len = strlcpy(chan->topic, Arg, sizeof(chan->topic));
1375                 if (len >= sizeof(chan->topic))
1376                         Config_Error_TooLong( Line, Var );
1377                 return;
1378         }
1379         if( strcasecmp( Var, "Key" ) == 0 ) {
1380                 /* Initial Channel Key (mode k) */
1381                 len = strlcpy(chan->key, Arg, sizeof(chan->key));
1382                 if (len >= sizeof(chan->key))
1383                         Config_Error_TooLong(Line, Var);
1384                 return;
1385         }
1386         if( strcasecmp( Var, "MaxUsers" ) == 0 ) {
1387                 /* maximum user limit, mode l */
1388                 chan->maxusers = (unsigned long) atol(Arg);
1389                 if (chan->maxusers == 0)
1390                         Config_Error_NaN(Line, Var);
1391                 return;
1392         }
1393         if (strcasecmp(Var, "KeyFile") == 0) {
1394                 /* channel keys */
1395                 len = strlcpy(chan->keyfile, Arg, sizeof(chan->keyfile));
1396                 if (len >= sizeof(chan->keyfile))
1397                         Config_Error_TooLong(Line, Var);
1398                 return;
1399         }
1400
1401         Config_Error( LOG_ERR, "%s, line %d (section \"Channel\"): Unknown variable \"%s\"!",
1402                                                                 NGIRCd_ConfFile, Line, Var );
1403 } /* Handle_CHANNEL */
1404
1405
1406 static bool
1407 Validate_Config(bool Configtest, bool Rehash)
1408 {
1409         /* Validate configuration settings. */
1410
1411 #ifdef DEBUG
1412         int i, servers, servers_once;
1413 #endif
1414         bool config_valid = true;
1415         char *ptr;
1416
1417         /* Validate configured server name, see RFC 2812 section 2.3.1 */
1418         ptr = Conf_ServerName;
1419         do {
1420                 if (*ptr >= 'a' && *ptr <= 'z') continue;
1421                 if (*ptr >= 'A' && *ptr <= 'Z') continue;
1422                 if (*ptr >= '0' && *ptr <= '9') continue;
1423                 if (ptr > Conf_ServerName) {
1424                         if (*ptr == '.' || *ptr == '-')
1425                                 continue;
1426                 }
1427                 Conf_ServerName[0] = '\0';
1428                 break;
1429         } while (*(++ptr));
1430
1431         if (!Conf_ServerName[0]) {
1432                 /* No server name configured! */
1433                 config_valid = false;
1434                 Config_Error(LOG_ALERT,
1435                              "No (valid) server name configured in \"%s\" (section 'Global': 'Name')!",
1436                              NGIRCd_ConfFile);
1437                 if (!Configtest && !Rehash) {
1438                         Config_Error(LOG_ALERT,
1439                                      "%s exiting due to fatal errors!",
1440                                      PACKAGE_NAME);
1441                         exit(1);
1442                 }
1443         }
1444
1445         if (Conf_ServerName[0] && !strchr(Conf_ServerName, '.')) {
1446                 /* No dot in server name! */
1447                 config_valid = false;
1448                 Config_Error(LOG_ALERT,
1449                              "Invalid server name configured in \"%s\" (section 'Global': 'Name'): Dot missing!",
1450                              NGIRCd_ConfFile);
1451                 if (!Configtest) {
1452                         Config_Error(LOG_ALERT,
1453                                      "%s exiting due to fatal errors!",
1454                                      PACKAGE_NAME);
1455                         exit(1);
1456                 }
1457         }
1458
1459 #ifdef STRICT_RFC
1460         if (!Conf_ServerAdminMail[0]) {
1461                 /* No administrative contact configured! */
1462                 config_valid = false;
1463                 Config_Error(LOG_ALERT,
1464                              "No administrator email address configured in \"%s\" ('AdminEMail')!",
1465                              NGIRCd_ConfFile);
1466                 if (!Configtest) {
1467                         Config_Error(LOG_ALERT,
1468                                      "%s exiting due to fatal errors!",
1469                                      PACKAGE_NAME);
1470                         exit(1);
1471                 }
1472         }
1473 #endif
1474
1475         if (!Conf_ServerAdmin1[0] && !Conf_ServerAdmin2[0]
1476             && !Conf_ServerAdminMail[0]) {
1477                 /* No administrative information configured! */
1478                 Config_Error(LOG_WARNING,
1479                              "No administrative information configured but required by RFC!");
1480         }
1481
1482 #ifdef PAM
1483         if (Conf_ServerPwd[0])
1484                 Config_Error(LOG_ERR,
1485                              "This server uses PAM, \"Password\" will be ignored!");
1486 #endif
1487
1488 #ifdef DEBUG
1489         servers = servers_once = 0;
1490         for (i = 0; i < MAX_SERVERS; i++) {
1491                 if (Conf_Server[i].name[0]) {
1492                         servers++;
1493                         if (Conf_Server[i].flags & CONF_SFLAG_ONCE)
1494                                 servers_once++;
1495                 }
1496         }
1497         Log(LOG_DEBUG,
1498             "Configuration: Operators=%d, Servers=%d[%d], Channels=%d",
1499             Conf_Oper_Count, servers, servers_once, Conf_Channel_Count);
1500 #endif
1501
1502         return config_valid;
1503 } /* Validate_Config */
1504
1505
1506 static void
1507 Config_Error_TooLong ( const int Line, const char *Item )
1508 {
1509         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" too long!", NGIRCd_ConfFile, Line, Item );
1510 }
1511
1512
1513 static void
1514 Config_Error_NaN( const int Line, const char *Item )
1515 {
1516         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" is not a number!",
1517                                                 NGIRCd_ConfFile, Line, Item );
1518 }
1519
1520
1521 #ifdef PROTOTYPES
1522 static void Config_Error( const int Level, const char *Format, ... )
1523 #else
1524 static void Config_Error( Level, Format, va_alist )
1525 const int Level;
1526 const char *Format;
1527 va_dcl
1528 #endif
1529 {
1530         /* Error! Write to console and/or logfile. */
1531
1532         char msg[MAX_LOG_MSG_LEN];
1533         va_list ap;
1534
1535         assert( Format != NULL );
1536
1537 #ifdef PROTOTYPES
1538         va_start( ap, Format );
1539 #else
1540         va_start( ap );
1541 #endif
1542         vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
1543         va_end( ap );
1544         
1545         /* During "normal operations" the log functions of the daemon should
1546          * be used, but during testing of the configuration file, all messages
1547          * should go directly to the console: */
1548         if (Use_Log) Log( Level, "%s", msg );
1549         else puts( msg );
1550 } /* Config_Error */
1551
1552
1553 #ifdef DEBUG
1554
1555 GLOBAL void
1556 Conf_DebugDump(void)
1557 {
1558         int i;
1559
1560         Log(LOG_DEBUG, "Configured servers:");
1561         for (i = 0; i < MAX_SERVERS; i++) {
1562                 if (! Conf_Server[i].name[0])
1563                         continue;
1564                 Log(LOG_DEBUG,
1565                     " - %s: %s:%d, last=%ld, group=%d, flags=%d, conn=%d",
1566                     Conf_Server[i].name, Conf_Server[i].host,
1567                     Conf_Server[i].port, Conf_Server[i].lasttry,
1568                     Conf_Server[i].group, Conf_Server[i].flags,
1569                     Conf_Server[i].conn_id);
1570         }
1571 } /* Conf_DebugDump */
1572
1573 #endif
1574
1575
1576 static void
1577 Init_Server_Struct( CONF_SERVER *Server )
1578 {
1579         /* Initialize server configuration structur to default values */
1580
1581         assert( Server != NULL );
1582
1583         memset( Server, 0, sizeof (CONF_SERVER) );
1584
1585         Server->group = NONE;
1586         Server->lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
1587
1588         if( NGIRCd_Passive ) Server->flags = CONF_SFLAG_DISABLED;
1589
1590         Proc_InitStruct(&Server->res_stat);
1591         Server->conn_id = NONE;
1592         memset(&Server->bind_addr, 0, sizeof(&Server->bind_addr));
1593 } /* Init_Server_Struct */
1594
1595
1596 /* -eof- */