]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conf.c
b09113730ef185dff831ca6a0392c4a8d757ddcf
[ngircd-alex.git] / src / ngircd / conf.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
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
12 #include "portab.h"
13
14 /**
15  * @file
16  * Configuration management (reading, parsing & validation)
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #ifdef PROTOTYPES
23 #       include <stdarg.h>
24 #else
25 #       include <varargs.h>
26 #endif
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #ifdef HAVE_CTYPE_H
38 # include <ctype.h>
39 #endif
40
41 #include "array.h"
42 #include "ngircd.h"
43 #include "conn.h"
44 #include "channel.h"
45 #include "defines.h"
46 #include "log.h"
47 #include "match.h"
48 #include "tool.h"
49
50 #include "exp.h"
51 #include "conf.h"
52
53
54 static bool Use_Log = true, Using_MotdFile = true;
55 static CONF_SERVER New_Server;
56 static int New_Server_Idx;
57
58 static char Conf_MotdFile[FNAME_LEN];
59
60 static void Set_Defaults PARAMS(( bool InitServers ));
61 static bool Read_Config PARAMS(( bool TestOnly, bool IsStarting ));
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_LIMITS PARAMS(( int Line, char *Var, char *Arg ));
66 static void Handle_OPTIONS PARAMS(( int Line, char *Var, char *Arg ));
67 static void Handle_OPERATOR PARAMS(( int Line, char *Var, char *Arg ));
68 static void Handle_SERVER PARAMS(( int Line, char *Var, char *Arg ));
69 static void Handle_CHANNEL PARAMS(( int Line, char *Var, char *Arg ));
70
71 static void Config_Error PARAMS(( const int Level, const char *Format, ... ));
72
73 static void Config_Error_NaN PARAMS(( const int LINE, const char *Value ));
74 static void Config_Error_Section PARAMS(( const int Line, const char *Item,
75                                           const char *Section ));
76 static void Config_Error_TooLong PARAMS(( const int LINE, const char *Value ));
77
78 static void Init_Server_Struct PARAMS(( CONF_SERVER *Server ));
79
80
81 #ifdef WANT_IPV6
82 #define DEFAULT_LISTEN_ADDRSTR "::,0.0.0.0"
83 #else
84 #define DEFAULT_LISTEN_ADDRSTR "0.0.0.0"
85 #endif
86
87
88 #ifdef SSL_SUPPORT
89
90 static void Handle_SSL PARAMS(( int Line, char *Var, char *Ark ));
91
92 struct SSLOptions Conf_SSLOptions;
93
94 /**
95  * Initialize SSL configuration.
96  */
97 static void
98 ConfSSL_Init(void)
99 {
100         free(Conf_SSLOptions.KeyFile);
101         Conf_SSLOptions.KeyFile = NULL;
102
103         free(Conf_SSLOptions.CertFile);
104         Conf_SSLOptions.CertFile = NULL;
105
106         free(Conf_SSLOptions.DHFile);
107         Conf_SSLOptions.DHFile = NULL;
108         array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
109 }
110
111 /**
112  * Make sure that a configured file is readable.
113  *
114  * Currently, this function is only used for SSL-related options ...
115  *
116  * @param Var Configuration variable
117  * @param Filename Configured filename
118  */
119 static void
120 CheckFileReadable(const char *Var, const char *Filename)
121 {
122         FILE *fp;
123
124         if (!Filename)
125                 return;
126
127         fp = fopen(Filename, "r");
128         if (fp)
129                 fclose(fp);
130         else
131                 Config_Error(LOG_ERR, "Can't read \"%s\" (\"%s\"): %s",
132                              Filename, Var, strerror(errno));
133 }
134
135 #endif
136
137
138 /**
139  * Duplicate string and warn on errors.
140  *
141  * @returns Pointer to string on success, NULL otherwise.
142  */
143 static char *
144 strdup_warn(const char *str)
145 {
146         char *ptr = strdup(str);
147         if (!ptr)
148                 Config_Error(LOG_ERR,
149                              "Could not allocate memory for string: %s", str);
150         return ptr;
151 }
152
153 /**
154  * Output a comma separated list of ports (integer values).
155  */
156 static void
157 ports_puts(array *a)
158 {
159         size_t len;
160         UINT16 *ports;
161         len = array_length(a, sizeof(UINT16));
162         if (len--) {
163                 ports = (UINT16*) array_start(a);
164                 printf("%u", (unsigned int) *ports);
165                 while (len--) {
166                         ports++;
167                         printf(", %u", (unsigned int) *ports);
168                 }
169         }
170         putc('\n', stdout);
171 }
172
173 /**
174  * Parse a comma separated string into an array of port numbers (integers).
175  */
176 static void
177 ports_parse(array *a, int Line, char *Arg)
178 {
179         char *ptr;
180         int port;
181         UINT16 port16;
182
183         array_trunc(a);
184
185         ptr = strtok( Arg, "," );
186         while (ptr) {
187                 ngt_TrimStr(ptr);
188                 port = atoi(ptr);
189                 if (port > 0 && port < 0xFFFF) {
190                         port16 = (UINT16) port;
191                         if (!array_catb(a, (char*)&port16, sizeof port16))
192                                 Config_Error(LOG_ERR, "%s, line %d Could not add port number %ld: %s",
193                                                         NGIRCd_ConfFile, Line, port, strerror(errno));
194                 } else {
195                         Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Illegal port number %ld!",
196                                                                         NGIRCd_ConfFile, Line, port );
197                 }
198
199                 ptr = strtok( NULL, "," );
200         }
201 }
202
203 /**
204  * Initialize configuration module.
205  */
206 GLOBAL void
207 Conf_Init( void )
208 {
209         Read_Config(false, true);
210         Validate_Config(false, false);
211 }
212
213 /**
214  * "Rehash" (reload) server configuration.
215  *
216  * @returns true if configuration has been re-read, false on errors.
217  */
218 GLOBAL bool
219 Conf_Rehash( void )
220 {
221         if (!Read_Config(false, false))
222                 return false;
223         Validate_Config(false, true);
224
225         /* Update CLIENT structure of local server */
226         Client_SetInfo(Client_ThisServer(), Conf_ServerInfo);
227         return true;
228 }
229
230 /**
231  * Output a boolean value as "yes/no" string.
232  */
233 static const char*
234 yesno_to_str(int boolean_value)
235 {
236         if (boolean_value)
237                 return "yes";
238         return "no";
239 }
240
241 /**
242  * Free all IRC operator configuration structures.
243  */
244 static void
245 opers_free(void)
246 {
247         struct Conf_Oper *op;
248         size_t len;
249
250         len = array_length(&Conf_Opers, sizeof(*op));
251         op = array_start(&Conf_Opers);
252         while (len--) {
253                 free(op->mask);
254                 op++;
255         }
256         array_free(&Conf_Opers);
257 }
258
259 /**
260  * Output all IRC operator configuration structures.
261  */
262 static void
263 opers_puts(void)
264 {
265         struct Conf_Oper *op;
266         size_t count, i;
267
268         count = array_length(&Conf_Opers, sizeof(*op));
269         op = array_start(&Conf_Opers);
270         for (i = 0; i < count; i++, op++) {
271                 if (!op->name[0])
272                         continue;
273
274                 puts("[OPERATOR]");
275                 printf("  Name = %s\n", op->name);
276                 printf("  Password = %s\n", op->pwd);
277                 printf("  Mask = %s\n\n", op->mask ? op->mask : "");
278         }
279 }
280
281 /**
282  * Read configuration, validate and output it.
283  *
284  * This function waits for a keypress of the user when stdin/stdout are valid
285  * tty's ("you can read our nice message and we can read in your keypress").
286  *
287  * @return      0 on succes, 1 on failure(s); therefore the result code can
288  *              directly be used by exit() when running "ngircd --configtest".
289  */
290 GLOBAL int
291 Conf_Test( void )
292 {
293         struct passwd *pwd;
294         struct group *grp;
295         unsigned int i;
296         bool config_valid;
297         size_t predef_channel_count;
298         struct Conf_Channel *predef_chan;
299
300         Use_Log = false;
301
302         if (!Read_Config(true, true))
303                 return 1;
304
305         config_valid = Validate_Config(true, false);
306
307         /* Valid tty? */
308         if(isatty(fileno(stdin)) && isatty(fileno(stdout))) {
309                 puts("OK, press enter to see a dump of your server configuration ...");
310                 getchar();
311         } else
312                 puts("Ok, dump of your server configuration follows:\n");
313
314         puts("[GLOBAL]");
315         printf("  Name = %s\n", Conf_ServerName);
316         printf("  AdminInfo1 = %s\n", Conf_ServerAdmin1);
317         printf("  AdminInfo2 = %s\n", Conf_ServerAdmin2);
318         printf("  AdminEMail = %s\n", Conf_ServerAdminMail);
319         printf("  Info = %s\n", Conf_ServerInfo);
320         printf("  Listen = %s\n", Conf_ListenAddress);
321         if (Using_MotdFile) {
322                 printf("  MotdFile = %s\n", Conf_MotdFile);
323                 printf("  MotdPhrase =\n");
324         } else {
325                 printf("  MotdFile = \n");
326                 printf("  MotdPhrase = %s\n", array_bytes(&Conf_Motd)
327                        ? (const char*) array_start(&Conf_Motd) : "");
328         }
329 #ifndef PAM
330         printf("  Password = %s\n", Conf_ServerPwd);
331 #endif
332         printf("  PidFile = %s\n", Conf_PidFile);
333         printf("  Ports = ");
334         ports_puts(&Conf_ListenPorts);
335         grp = getgrgid(Conf_GID);
336         if (grp)
337                 printf("  ServerGID = %s\n", grp->gr_name);
338         else
339                 printf("  ServerGID = %ld\n", (long)Conf_GID);
340         pwd = getpwuid(Conf_UID);
341         if (pwd)
342                 printf("  ServerUID = %s\n", pwd->pw_name);
343         else
344                 printf("  ServerUID = %ld\n", (long)Conf_UID);
345         puts("");
346
347         puts("[LIMITS]");
348         printf("  ConnectRetry = %d\n", Conf_ConnectRetry);
349         printf("  MaxConnections = %ld\n", Conf_MaxConnections);
350         printf("  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
351         printf("  MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
352         printf("  MaxNickLength = %u\n", Conf_MaxNickLength - 1);
353         printf("  PingTimeout = %d\n", Conf_PingTimeout);
354         printf("  PongTimeout = %d\n", Conf_PongTimeout);
355         puts("");
356
357         puts("[OPTIONS]");
358         printf("  AllowRemoteOper = %s\n", yesno_to_str(Conf_AllowRemoteOper));
359         printf("  ChrootDir = %s\n", Conf_Chroot);
360         printf("  CloakHost = %s\n", Conf_CloakHost);
361         printf("  CloakHostModeX = %s\n", Conf_CloakHostModeX);
362         printf("  CloakHostSalt = %s\n", Conf_CloakHostSalt);
363         printf("  CloakUserToNick = %s\n", yesno_to_str(Conf_CloakUserToNick));
364 #ifdef WANT_IPV6
365         printf("  ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6));
366         printf("  ConnectIPv6 = %s\n", yesno_to_str(Conf_ConnectIPv4));
367 #endif
368         printf("  DNS = %s\n", yesno_to_str(Conf_DNS));
369 #ifdef IDENT
370         printf("  Ident = %s\n", yesno_to_str(Conf_Ident));
371 #endif
372         printf("  MorePrivacy = %s\n", yesno_to_str(Conf_MorePrivacy));
373         printf("  NoticeAuth = %s\n", yesno_to_str(Conf_NoticeAuth));
374         printf("  OperCanUseMode = %s\n", yesno_to_str(Conf_OperCanMode));
375         printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
376 #ifdef PAM
377         printf("  PAM = %s\n", yesno_to_str(Conf_PAM));
378         printf("  PAMIsOptional = %s\n", yesno_to_str(Conf_PAMIsOptional));
379 #endif
380         printf("  PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly));
381 #ifndef STRICT_RFC
382         printf("  RequireAuthPing = %s\n", yesno_to_str(Conf_AuthPing));
383 #endif
384         printf("  ScrubCTCP = %s\n", yesno_to_str(Conf_ScrubCTCP));
385 #ifdef SYSLOG
386         printf("  SyslogFacility = %s\n",
387                ngt_SyslogFacilityName(Conf_SyslogFacility));
388 #endif
389         printf("  WebircPassword = %s\n", Conf_WebircPwd);
390         puts("");
391
392 #ifdef SSL_SUPPORT
393         puts("[SSL]");
394         printf("  CertFile = %s\n", Conf_SSLOptions.CertFile
395                                         ? Conf_SSLOptions.CertFile : "");
396         printf("  DHFile = %s\n", Conf_SSLOptions.DHFile
397                                         ? Conf_SSLOptions.DHFile : "");
398         printf("  KeyFile = %s\n", Conf_SSLOptions.KeyFile
399                                         ? Conf_SSLOptions.KeyFile : "");
400         if (array_bytes(&Conf_SSLOptions.KeyFilePassword))
401                 puts("  KeyFilePassword = <secret>");
402         else
403                 puts("  KeyFilePassword = ");
404         array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
405         printf("  Ports = ");
406         ports_puts(&Conf_SSLOptions.ListenPorts);
407         puts("");
408 #endif
409
410         opers_puts();
411
412         for( i = 0; i < MAX_SERVERS; i++ ) {
413                 if( ! Conf_Server[i].name[0] ) continue;
414
415                 /* Valid "Server" section */
416                 puts( "[SERVER]" );
417                 printf( "  Name = %s\n", Conf_Server[i].name );
418                 printf( "  Host = %s\n", Conf_Server[i].host );
419                 printf( "  Port = %u\n", (unsigned int)Conf_Server[i].port );
420 #ifdef SSL_SUPPORT
421                 printf( "  SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
422 #endif
423                 printf( "  MyPassword = %s\n", Conf_Server[i].pwd_in );
424                 printf( "  PeerPassword = %s\n", Conf_Server[i].pwd_out );
425                 printf( "  ServiceMask = %s\n", Conf_Server[i].svs_mask);
426                 printf( "  Group = %d\n", Conf_Server[i].group );
427                 printf( "  Passive = %s\n\n", Conf_Server[i].flags & CONF_SFLAG_DISABLED ? "yes" : "no");
428         }
429
430         predef_channel_count = array_length(&Conf_Channels, sizeof(*predef_chan));
431         predef_chan = array_start(&Conf_Channels);
432
433         for (i = 0; i < predef_channel_count; i++, predef_chan++) {
434                 if (!predef_chan->name[0])
435                         continue;
436
437                 /* Valid "Channel" section */
438                 puts( "[CHANNEL]" );
439                 printf("  Name = %s\n", predef_chan->name);
440                 printf("  Modes = %s\n", predef_chan->modes);
441                 printf("  Key = %s\n", predef_chan->key);
442                 printf("  MaxUsers = %lu\n", predef_chan->maxusers);
443                 printf("  Topic = %s\n", predef_chan->topic);
444                 printf("  KeyFile = %s\n\n", predef_chan->keyfile);
445         }
446
447         return (config_valid ? 0 : 1);
448 }
449
450 /**
451  * Remove connection information from configured server.
452  *
453  * If the server is set as "once", delete it from our configuration;
454  * otherwise set the time for the next connection attempt.
455  *
456  * Non-server connections will be silently ignored.
457  */
458 GLOBAL void
459 Conf_UnsetServer( CONN_ID Idx )
460 {
461         int i;
462         time_t t;
463
464         /* Check all our configured servers */
465         for( i = 0; i < MAX_SERVERS; i++ ) {
466                 if( Conf_Server[i].conn_id != Idx ) continue;
467
468                 /* Gotcha! Mark server configuration as "unused": */
469                 Conf_Server[i].conn_id = NONE;
470
471                 if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
472                         /* Delete configuration here */
473                         Init_Server_Struct( &Conf_Server[i] );
474                 } else {
475                         /* Set time for next connect attempt */
476                         t = time(NULL);
477                         if (Conf_Server[i].lasttry < t - Conf_ConnectRetry) {
478                                 /* The connection has been "long", so we don't
479                                  * require the next attempt to be delayed. */
480                                 Conf_Server[i].lasttry =
481                                         t - Conf_ConnectRetry + RECONNECT_DELAY;
482                         } else
483                                 Conf_Server[i].lasttry = t;
484                 }
485         }
486 }
487
488 /**
489  * Set connection information for specified configured server.
490  */
491 GLOBAL void
492 Conf_SetServer( int ConfServer, CONN_ID Idx )
493 {
494         assert( ConfServer > NONE );
495         assert( Idx > NONE );
496
497         if (Conf_Server[ConfServer].conn_id > NONE &&
498             Conf_Server[ConfServer].conn_id != Idx) {
499                 Log(LOG_ALERT,
500                         "Trying to update connection index for already registered server \"%s\": %d/%d - ignored.",
501                         Conf_Server[ConfServer].name,
502                         Conf_Server[ConfServer].conn_id, Idx);
503                 return;
504         }
505         Conf_Server[ConfServer].conn_id = Idx;
506 }
507
508 /**
509  * Get index of server in configuration structure.
510  */
511 GLOBAL int
512 Conf_GetServer( CONN_ID Idx )
513 {
514         int i = 0;
515
516         assert( Idx > NONE );
517
518         for( i = 0; i < MAX_SERVERS; i++ ) {
519                 if( Conf_Server[i].conn_id == Idx ) return i;
520         }
521         return NONE;
522 }
523
524 /**
525  * Enable a server by name and adjust its port number.
526  *
527  * @returns     true if a server has been enabled and now has a valid port
528  *              number and host name for outgoing connections.
529  */
530 GLOBAL bool
531 Conf_EnableServer( const char *Name, UINT16 Port )
532 {
533         int i;
534
535         assert( Name != NULL );
536         for( i = 0; i < MAX_SERVERS; i++ ) {
537                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
538                         /* Gotcha! Set port and enable server: */
539                         Conf_Server[i].port = Port;
540                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
541                         return (Conf_Server[i].port && Conf_Server[i].host[0]);
542                 }
543         }
544         return false;
545 }
546
547 /**
548  * Enable a server by name.
549  *
550  * The server is only usable as outgoing server, if it has set a valid port
551  * number for outgoing connections!
552  * If not, you have to use Conf_EnableServer() function to make it available.
553  *
554  * @returns     true if a server has been enabled; false otherwise.
555  */
556 GLOBAL bool
557 Conf_EnablePassiveServer(const char *Name)
558 {
559         int i;
560
561         assert( Name != NULL );
562         for (i = 0; i < MAX_SERVERS; i++) {
563                 if ((strcasecmp( Conf_Server[i].name, Name ) == 0)
564                     && (Conf_Server[i].port > 0)) {
565                         /* BINGO! Enable server */
566                         Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
567                         return true;
568                 }
569         }
570         return false;
571 }
572
573 /**
574  * Disable a server by name.
575  * An already established connection will be disconnected.
576  *
577  * @returns     true if a server was found and has been disabled.
578  */
579 GLOBAL bool
580 Conf_DisableServer( const char *Name )
581 {
582         int i;
583
584         assert( Name != NULL );
585         for( i = 0; i < MAX_SERVERS; i++ ) {
586                 if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
587                         /* Gotcha! Disable and disconnect server: */
588                         Conf_Server[i].flags |= CONF_SFLAG_DISABLED;
589                         if( Conf_Server[i].conn_id > NONE )
590                                 Conn_Close(Conf_Server[i].conn_id, NULL,
591                                            "Server link terminated on operator request",
592                                            true);
593                         return true;
594                 }
595         }
596         return false;
597 }
598
599 /**
600  * Add a new remote server to our configuration.
601  *
602  * @param Name          Name of the new server.
603  * @param Port          Port number to connect to or 0 for incoming connections.
604  * @param Host          Host name to connect to.
605  * @param MyPwd         Password that will be sent to the peer.
606  * @param PeerPwd       Password that must be received from the peer.
607  * @returns             true if the new server has been added; false otherwise.
608  */
609 GLOBAL bool
610 Conf_AddServer(const char *Name, UINT16 Port, const char *Host,
611                const char *MyPwd, const char *PeerPwd)
612 {
613         int i;
614
615         assert( Name != NULL );
616         assert( Host != NULL );
617         assert( MyPwd != NULL );
618         assert( PeerPwd != NULL );
619
620         /* Search unused item in server configuration structure */
621         for( i = 0; i < MAX_SERVERS; i++ ) {
622                 /* Is this item used? */
623                 if( ! Conf_Server[i].name[0] ) break;
624         }
625         if( i >= MAX_SERVERS ) return false;
626
627         Init_Server_Struct( &Conf_Server[i] );
628         strlcpy( Conf_Server[i].name, Name, sizeof( Conf_Server[i].name ));
629         strlcpy( Conf_Server[i].host, Host, sizeof( Conf_Server[i].host ));
630         strlcpy( Conf_Server[i].pwd_out, MyPwd, sizeof( Conf_Server[i].pwd_out ));
631         strlcpy( Conf_Server[i].pwd_in, PeerPwd, sizeof( Conf_Server[i].pwd_in ));
632         Conf_Server[i].port = Port;
633         Conf_Server[i].flags = CONF_SFLAG_ONCE;
634
635         return true;
636 }
637
638 /**
639  * Check if the given nick name is an service.
640  *
641  * @returns true if the given nick name belongs to an "IRC service".
642  */
643 GLOBAL bool
644 Conf_IsService(int ConfServer, const char *Nick)
645 {
646         return MatchCaseInsensitive(Conf_Server[ConfServer].svs_mask, Nick);
647 }
648
649 /**
650  * Initialize configuration settings with their default values.
651  */
652 static void
653 Set_Defaults(bool InitServers)
654 {
655         int i;
656         char random[RANDOM_SALT_LEN];
657
658         /* Global */
659         strcpy(Conf_ServerName, "");
660         strcpy(Conf_ServerAdmin1, "");
661         strcpy(Conf_ServerAdmin2, "");
662         strcpy(Conf_ServerAdminMail, "");
663         snprintf(Conf_ServerInfo, sizeof Conf_ServerInfo, "%s %s",
664                  PACKAGE_NAME, PACKAGE_VERSION);
665         free(Conf_ListenAddress);
666         Conf_ListenAddress = NULL;
667         array_free(&Conf_Motd);
668         strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
669         strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
670         strcpy(Conf_ServerPwd, "");
671         strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
672         Conf_UID = Conf_GID = 0;
673
674         /* Limits */
675         Conf_ConnectRetry = 60;
676         Conf_MaxConnections = 0;
677         Conf_MaxConnectionsIP = 5;
678         Conf_MaxJoins = 10;
679         Conf_MaxNickLength = CLIENT_NICK_LEN_DEFAULT;
680         Conf_PingTimeout = 120;
681         Conf_PongTimeout = 20;
682
683         /* Options */
684         Conf_AllowRemoteOper = false;
685 #ifndef STRICT_RFC
686         Conf_AuthPing = false;
687 #endif
688         strlcpy(Conf_Chroot, CHROOT_DIR, sizeof(Conf_Chroot));
689         strcpy(Conf_CloakHost, "");
690         strcpy(Conf_CloakHostModeX, "");
691         strcpy(Conf_CloakHostSalt, ngt_RandomStr(random, RANDOM_SALT_LEN));
692         Conf_CloakUserToNick = false;
693         Conf_ConnectIPv4 = true;
694 #ifdef WANT_IPV6
695         Conf_ConnectIPv6 = true;
696 #else
697         Conf_ConnectIPv6 = false;
698 #endif
699         Conf_DNS = true;
700 #ifdef IDENTAUTH
701         Conf_Ident = true;
702 #else
703         Conf_Ident = false;
704 #endif
705         Conf_MorePrivacy = false;
706         Conf_NoticeAuth = false;
707         Conf_OperCanMode = false;
708         Conf_OperServerMode = false;
709 #ifdef PAM
710         Conf_PAM = true;
711 #else
712         Conf_PAM = false;
713 #endif
714         Conf_PAMIsOptional = false;
715         Conf_PredefChannelsOnly = false;
716 #ifdef SYSLOG
717         Conf_ScrubCTCP = false;
718 #ifdef LOG_LOCAL5
719         Conf_SyslogFacility = LOG_LOCAL5;
720 #else
721         Conf_SyslogFacility = 0;
722 #endif
723 #endif
724
725         /* Initialize server configuration structures */
726         if (InitServers) {
727                 for (i = 0; i < MAX_SERVERS;
728                      Init_Server_Struct(&Conf_Server[i++]));
729         }
730 }
731
732 /**
733  * Get number of configured listening ports.
734  *
735  * @returns The number of ports (IPv4+IPv6) on which the server should listen.
736  */
737 static bool
738 no_listenports(void)
739 {
740         size_t cnt = array_bytes(&Conf_ListenPorts);
741 #ifdef SSL_SUPPORT
742         cnt += array_bytes(&Conf_SSLOptions.ListenPorts);
743 #endif
744         return cnt == 0;
745 }
746
747 /**
748  * Read MOTD ("message of the day") file.
749  *
750  * @param filename      Name of the file to read.
751  */
752 static void
753 Read_Motd(const char *filename)
754 {
755         char line[127];
756         FILE *fp;
757
758         if (*filename == '\0')
759                 return;
760
761         fp = fopen(filename, "r");
762         if (!fp) {
763                 Config_Error(LOG_WARNING, "Can't read MOTD file \"%s\": %s",
764                                         filename, strerror(errno));
765                 return;
766         }
767
768         array_free(&Conf_Motd);
769         Using_MotdFile = true;
770
771         while (fgets(line, (int)sizeof line, fp)) {
772                 ngt_TrimLastChr( line, '\n');
773
774                 /* add text including \0 */
775                 if (!array_catb(&Conf_Motd, line, strlen(line) + 1)) {
776                         Log(LOG_WARNING, "Cannot add MOTD text: %s", strerror(errno));
777                         break;
778                 }
779         }
780         fclose(fp);
781 }
782
783 /**
784  * Read ngIRCd configuration file.
785  *
786  * Please note that this function uses exit(1) on fatal errors and therefore
787  * can result in ngIRCd terminating!
788  *
789  * @param ngircd_starting       Flag indicating if ngIRCd is starting or not.
790  * @returns                     true when the configuration file has been read
791  *                              successfully; false otherwise.
792  */
793 static bool
794 Read_Config(bool TestOnly, bool IsStarting)
795 {
796         char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
797         const UINT16 defaultport = 6667;
798         int line, i, n;
799         size_t count;
800         FILE *fd;
801
802         /* Open configuration file */
803         fd = fopen( NGIRCd_ConfFile, "r" );
804         if( ! fd ) {
805                 /* No configuration file found! */
806                 Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s",
807                                         NGIRCd_ConfFile, strerror( errno ));
808                 if (!IsStarting)
809                         return false;
810                 Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
811                 exit( 1 );
812         }
813
814         opers_free();
815         Set_Defaults(IsStarting);
816
817         if (TestOnly)
818                 Config_Error(LOG_INFO,
819                              "Reading configuration from \"%s\" ...",
820                              NGIRCd_ConfFile );
821
822         /* Clean up server configuration structure: mark all already
823          * configured servers as "once" so that they are deleted
824          * after the next disconnect and delete all unused servers.
825          * And delete all servers which are "duplicates" of servers
826          * that are already marked as "once" (such servers have been
827          * created by the last rehash but are now useless). */
828         for( i = 0; i < MAX_SERVERS; i++ ) {
829                 if( Conf_Server[i].conn_id == NONE ) Init_Server_Struct( &Conf_Server[i] );
830                 else {
831                         /* This structure is in use ... */
832                         if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
833                                 /* Check for duplicates */
834                                 for( n = 0; n < MAX_SERVERS; n++ ) {
835                                         if( n == i ) continue;
836
837                                         if( Conf_Server[i].conn_id == Conf_Server[n].conn_id ) {
838                                                 Init_Server_Struct( &Conf_Server[n] );
839 #ifdef DEBUG
840                                                 Log(LOG_DEBUG,"Deleted unused duplicate server %d (kept %d).",
841                                                                                                 n, i );
842 #endif
843                                         }
844                                 }
845                         } else {
846                                 /* Mark server as "once" */
847                                 Conf_Server[i].flags |= CONF_SFLAG_ONCE;
848                                 Log( LOG_DEBUG, "Marked server %d as \"once\"", i );
849                         }
850                 }
851         }
852
853         /* Initialize variables */
854         line = 0;
855         strcpy( section, "" );
856         Init_Server_Struct( &New_Server );
857         New_Server_Idx = NONE;
858 #ifdef SSL_SUPPORT
859         ConfSSL_Init();
860 #endif
861         /* Read configuration file */
862         while( true ) {
863                 if( ! fgets( str, LINE_LEN, fd )) break;
864                 ngt_TrimStr( str );
865                 line++;
866
867                 /* Skip comments and empty lines */
868                 if( str[0] == ';' || str[0] == '#' || str[0] == '\0' ) continue;
869
870                 /* Is this the beginning of a new section? */
871                 if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' )) {
872                         strlcpy( section, str, sizeof( section ));
873                         if (strcasecmp(section, "[GLOBAL]") == 0
874                             || strcasecmp(section, "[LIMITS]") == 0
875                             || strcasecmp(section, "[OPTIONS]") == 0
876 #ifdef SSL_SUPPORT
877                             || strcasecmp(section, "[SSL]") == 0
878 #endif
879                             )
880                                 continue;
881
882                         if( strcasecmp( section, "[SERVER]" ) == 0 ) {
883                                 /* Check if there is already a server to add */
884                                 if( New_Server.name[0] ) {
885                                         /* Copy data to "real" server structure */
886                                         assert( New_Server_Idx > NONE );
887                                         Conf_Server[New_Server_Idx] = New_Server;
888                                 }
889
890                                 /* Re-init structure for new server */
891                                 Init_Server_Struct( &New_Server );
892
893                                 /* Search unused item in server configuration structure */
894                                 for( i = 0; i < MAX_SERVERS; i++ ) {
895                                         /* Is this item used? */
896                                         if( ! Conf_Server[i].name[0] ) break;
897                                 }
898                                 if( i >= MAX_SERVERS ) {
899                                         /* Oops, no free item found! */
900                                         Config_Error( LOG_ERR, "Too many servers configured." );
901                                         New_Server_Idx = NONE;
902                                 }
903                                 else New_Server_Idx = i;
904                                 continue;
905                         }
906
907                         if (strcasecmp(section, "[CHANNEL]") == 0) {
908                                 count = array_length(&Conf_Channels,
909                                                      sizeof(struct Conf_Channel));
910                                 if (!array_alloc(&Conf_Channels,
911                                                  sizeof(struct Conf_Channel),
912                                                  count)) {
913                                         Config_Error(LOG_ERR,
914                                                      "Could not allocate memory for new operator (line %d)",
915                                                      line);
916                                 }
917                                 continue;
918                         }
919
920                         if (strcasecmp(section, "[OPERATOR]") == 0) {
921                                 count = array_length(&Conf_Opers,
922                                                      sizeof(struct Conf_Oper));
923                                 if (!array_alloc(&Conf_Opers,
924                                                  sizeof(struct Conf_Oper),
925                                                  count)) {
926                                         Config_Error(LOG_ERR,
927                                                      "Could not allocate memory for new channel (line &d)",
928                                                      line);
929                                 }
930                                 continue;
931                         }
932
933                         Config_Error(LOG_ERR,
934                                      "%s, line %d: Unknown section \"%s\"!",
935                                      NGIRCd_ConfFile, line, section);
936                         section[0] = 0x1;
937                 }
938                 if( section[0] == 0x1 ) continue;
939
940                 /* Split line into variable name and parameters */
941                 ptr = strchr( str, '=' );
942                 if( ! ptr ) {
943                         Config_Error( LOG_ERR, "%s, line %d: Syntax error!", NGIRCd_ConfFile, line );
944                         continue;
945                 }
946                 *ptr = '\0';
947                 var = str; ngt_TrimStr( var );
948                 arg = ptr + 1; ngt_TrimStr( arg );
949
950                 if(strcasecmp(section, "[GLOBAL]") == 0)
951                         Handle_GLOBAL(line, var, arg);
952                 else if(strcasecmp(section, "[LIMITS]") == 0)
953                         Handle_LIMITS(line, var, arg);
954                 else if(strcasecmp(section, "[OPTIONS]") == 0)
955                         Handle_OPTIONS(line, var, arg);
956 #ifdef SSL_SUPPORT
957                 else if(strcasecmp(section, "[SSL]") == 0)
958                         Handle_SSL(line, var, arg);
959 #endif
960                 else if(strcasecmp(section, "[OPERATOR]") == 0)
961                         Handle_OPERATOR(line, var, arg);
962                 else if(strcasecmp(section, "[SERVER]") == 0)
963                         Handle_SERVER(line, var, arg);
964                 else if(strcasecmp(section, "[CHANNEL]") == 0)
965                         Handle_CHANNEL(line, var, arg);
966                 else
967                         Config_Error(LOG_ERR,
968                                      "%s, line %d: Variable \"%s\" outside section!",
969                                      NGIRCd_ConfFile, line, var);
970         }
971
972         /* Close configuration file */
973         fclose( fd );
974
975         /* Check if there is still a server to add */
976         if( New_Server.name[0] ) {
977                 /* Copy data to "real" server structure */
978                 assert( New_Server_Idx > NONE );
979                 Conf_Server[New_Server_Idx] = New_Server;
980         }
981
982         /* not a single listening port? Add default. */
983         if (no_listenports() &&
984                 !array_copyb(&Conf_ListenPorts, (char*) &defaultport, sizeof defaultport))
985         {
986                 Config_Error(LOG_ALERT, "Could not add default listening Port %u: %s",
987                                         (unsigned int) defaultport, strerror(errno));
988
989                 exit(1);
990         }
991
992         if (!Conf_ListenAddress)
993                 Conf_ListenAddress = strdup_warn(DEFAULT_LISTEN_ADDRSTR);
994
995         if (!Conf_ListenAddress) {
996                 Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
997                 exit(1);
998         }
999
1000         /* No MOTD phrase configured? (re)try motd file. */
1001         if (array_bytes(&Conf_Motd) == 0)
1002                 Read_Motd(Conf_MotdFile);
1003
1004 #ifdef SSL_SUPPORT
1005         /* Make sure that all SSL-related files are readable */
1006         CheckFileReadable("CertFile", Conf_SSLOptions.CertFile);
1007         CheckFileReadable("DHFile", Conf_SSLOptions.DHFile);
1008         CheckFileReadable("KeyFile", Conf_SSLOptions.KeyFile);
1009 #endif
1010
1011         return true;
1012 }
1013
1014 /**
1015  * Check whether a string argument is "true" or "false".
1016  *
1017  * @param Arg   Input string.
1018  * @returns     true if the input string has been parsed as "yes", "true"
1019  *              (case insensitive) or a non-zero integer value.
1020  */
1021 static bool
1022 Check_ArgIsTrue(const char *Arg)
1023 {
1024         if (strcasecmp(Arg, "yes") == 0)
1025                 return true;
1026         if (strcasecmp(Arg, "true") == 0)
1027                 return true;
1028         if (atoi(Arg) != 0)
1029                 return true;
1030
1031         return false;
1032 }
1033
1034 /**
1035  * Handle setting of "MaxNickLength".
1036  *
1037  * @param Line  Line number in configuration file.
1038  * @raram Arg   Input string.
1039  * @returns     New configured maximum nick name length.
1040  */
1041 static unsigned int
1042 Handle_MaxNickLength(int Line, const char *Arg)
1043 {
1044         unsigned new;
1045
1046         new = (unsigned) atoi(Arg) + 1;
1047         if (new > CLIENT_NICK_LEN) {
1048                 Config_Error(LOG_WARNING,
1049                              "%s, line %d: Value of \"MaxNickLength\" exceeds %u!",
1050                              NGIRCd_ConfFile, Line, CLIENT_NICK_LEN - 1);
1051                 return CLIENT_NICK_LEN;
1052         }
1053         if (new < 2) {
1054                 Config_Error(LOG_WARNING,
1055                              "%s, line %d: Value of \"MaxNickLength\" must be at least 1!",
1056                              NGIRCd_ConfFile, Line);
1057                 return 2;
1058         }
1059         return new;
1060 }
1061
1062 /**
1063  * Output a warning messages if IDENT is configured but not compiled in.
1064  */
1065 static void
1066 WarnIdent(int UNUSED Line)
1067 {
1068 #ifndef IDENTAUTH
1069         if (Conf_Ident) {
1070                 /* user has enabled ident lookups explicitly, but ... */
1071                 Config_Error(LOG_WARNING,
1072                         "%s: line %d: \"Ident = yes\", but ngircd was built without IDENT support!",
1073                         NGIRCd_ConfFile, Line);
1074         }
1075 #endif
1076 }
1077
1078 /**
1079  * Output a warning messages if IPv6 is configured but not compiled in.
1080  */
1081 static void
1082 WarnIPv6(int UNUSED Line)
1083 {
1084 #ifndef WANT_IPV6
1085         if (Conf_ConnectIPv6) {
1086                 /* user has enabled IPv6 explicitly, but ... */
1087                 Config_Error(LOG_WARNING,
1088                         "%s: line %d: \"ConnectIPv6 = yes\", but ngircd was built without IPv6 support!",
1089                         NGIRCd_ConfFile, Line);
1090         }
1091 #endif
1092 }
1093
1094 /**
1095  * Output a warning messages if PAM is configured but not compiled in.
1096  */
1097 static void
1098 WarnPAM(int UNUSED Line)
1099 {
1100 #ifndef PAM
1101         if (Conf_PAM) {
1102                 Config_Error(LOG_WARNING,
1103                         "%s: line %d: \"PAM = yes\", but ngircd was built without PAM support!",
1104                         NGIRCd_ConfFile, Line);
1105         }
1106 #endif
1107 }
1108
1109 /**
1110  * Handle legacy "NoXXX" options in [GLOBAL] section.
1111  *
1112  * TODO: This function and support for "NoXXX" could be removed starting
1113  * with ngIRCd release 19 (one release after marking it "deprecated").
1114  *
1115  * @param Var   Variable name.
1116  * @param Arg   Argument string.
1117  * @returns     true if a NoXXX option has been processed; false otherwise.
1118  */
1119 static bool
1120 CheckLegacyNoOption(const char *Var, const char *Arg)
1121 {
1122         if(strcasecmp(Var, "NoDNS") == 0) {
1123                 Conf_DNS = !Check_ArgIsTrue( Arg );
1124                 return true;
1125         }
1126         if (strcasecmp(Var, "NoIdent") == 0) {
1127                 Conf_Ident = !Check_ArgIsTrue(Arg);
1128                 return true;
1129         }
1130         if(strcasecmp(Var, "NoPAM") == 0) {
1131                 Conf_PAM = !Check_ArgIsTrue(Arg);
1132                 return true;
1133         }
1134         return false;
1135 }
1136
1137 /**
1138  * Handle deprecated legacy options in [GLOBAL] section.
1139  *
1140  * TODO: This function and support for these options in the [Global] section
1141  * could be removed starting with ngIRCd release 19 (one release after
1142  * marking it "deprecated").
1143  *
1144  * @param Var   Variable name.
1145  * @param Arg   Argument string.
1146  * @returns     true if a legacy option has been processed; false otherwise.
1147  */
1148 static const char*
1149 CheckLegacyGlobalOption(int Line, char *Var, char *Arg)
1150 {
1151         if (strcasecmp(Var, "AllowRemoteOper") == 0
1152             || strcasecmp(Var, "ChrootDir") == 0
1153             || strcasecmp(Var, "ConnectIPv4") == 0
1154             || strcasecmp(Var, "ConnectIPv6") == 0
1155             || strcasecmp(Var, "OperCanUseMode") == 0
1156             || strcasecmp(Var, "OperServerMode") == 0
1157             || strcasecmp(Var, "PredefChannelsOnly") == 0
1158             || strcasecmp(Var, "SyslogFacility") == 0
1159             || strcasecmp(Var, "WebircPassword") == 0) {
1160                 Handle_OPTIONS(Line, Var, Arg);
1161                 return "[Options]";
1162         }
1163         if (strcasecmp(Var, "ConnectRetry") == 0
1164             || strcasecmp(Var, "MaxConnections") == 0
1165             || strcasecmp(Var, "MaxConnectionsIP") == 0
1166             || strcasecmp(Var, "MaxJoins") == 0
1167             || strcasecmp(Var, "MaxNickLength") == 0
1168             || strcasecmp(Var, "PingTimeout") == 0
1169             || strcasecmp(Var, "PongTimeout") == 0) {
1170                 Handle_LIMITS(Line, Var, Arg);
1171                 return "[Limits]";
1172         }
1173 #ifdef SSL_SUPPORT
1174         if (strcasecmp(Var, "SSLCertFile") == 0
1175             || strcasecmp(Var, "SSLDHFile") == 0
1176             || strcasecmp(Var, "SSLKeyFile") == 0
1177             || strcasecmp(Var, "SSLKeyFilePassword") == 0
1178             || strcasecmp(Var, "SSLPorts") == 0) {
1179                 Handle_SSL(Line, Var + 3, Arg);
1180                 return "[SSL]";
1181         }
1182 #endif
1183
1184         return NULL;
1185 }
1186
1187 /**
1188  * Strip "no" prefix of a string.
1189  *
1190  * TODO: This function and support for "NoXXX" should be removed starting
1191  * with ngIRCd release 19! (One release after marking it "deprecated").
1192  *
1193  * @param str   Pointer to input string starting with "no".
1194  * @returns     New pointer to string without "no" prefix.
1195  */
1196 static const char *
1197 NoNo(const char *str)
1198 {
1199         assert(strncasecmp("no", str, 2) == 0 && str[2]);
1200         return str + 2;
1201 }
1202
1203 /**
1204  * Invert "boolean" string.
1205  *
1206  * TODO: This function and support for "NoXXX" should be removed starting
1207  * with ngIRCd release 19! (One release after marking it "deprecated").
1208  *
1209  * @param arg   "Boolean" input string.
1210  * @returns     Pointer to inverted "boolean string".
1211  */
1212 static const char *
1213 InvertArg(const char *arg)
1214 {
1215         return yesno_to_str(!Check_ArgIsTrue(arg));
1216 }
1217
1218 /**
1219  * Handle variable in [Global] configuration section.
1220  *
1221  * @param Line  Line numer in configuration file.
1222  * @param Var   Variable name.
1223  * @param Arg   Variable argument.
1224  */
1225 static void
1226 Handle_GLOBAL( int Line, char *Var, char *Arg )
1227 {
1228         struct passwd *pwd;
1229         struct group *grp;
1230         size_t len;
1231         const char *section;
1232
1233         assert(Line > 0);
1234         assert(Var != NULL);
1235         assert(Arg != NULL);
1236
1237         if (strcasecmp(Var, "Name") == 0) {
1238                 len = strlcpy(Conf_ServerName, Arg, sizeof(Conf_ServerName));
1239                 if (len >= sizeof(Conf_ServerName))
1240                         Config_Error_TooLong(Line, Var);
1241                 return;
1242         }
1243         if (strcasecmp(Var, "AdminInfo1") == 0) {
1244                 len = strlcpy(Conf_ServerAdmin1, Arg, sizeof(Conf_ServerAdmin1));
1245                 if (len >= sizeof(Conf_ServerAdmin1))
1246                         Config_Error_TooLong(Line, Var);
1247                 return;
1248         }
1249         if (strcasecmp(Var, "AdminInfo2") == 0) {
1250                 len = strlcpy(Conf_ServerAdmin2, Arg, sizeof(Conf_ServerAdmin2));
1251                 if (len >= sizeof(Conf_ServerAdmin2))
1252                         Config_Error_TooLong(Line, Var);
1253                 return;
1254         }
1255         if (strcasecmp(Var, "AdminEMail") == 0) {
1256                 len = strlcpy(Conf_ServerAdminMail, Arg,
1257                         sizeof(Conf_ServerAdminMail));
1258                 if (len >= sizeof(Conf_ServerAdminMail))
1259                         Config_Error_TooLong(Line, Var);
1260                 return;
1261         }
1262         if (strcasecmp(Var, "Info") == 0) {
1263                 len = strlcpy(Conf_ServerInfo, Arg, sizeof(Conf_ServerInfo));
1264                 if (len >= sizeof(Conf_ServerInfo))
1265                         Config_Error_TooLong(Line, Var);
1266                 return;
1267         }
1268         if (strcasecmp(Var, "Listen") == 0) {
1269                 if (Conf_ListenAddress) {
1270                         Config_Error(LOG_ERR,
1271                                      "Multiple Listen= options, ignoring: %s",
1272                                      Arg);
1273                         return;
1274                 }
1275                 Conf_ListenAddress = strdup_warn(Arg);
1276                 /* If allocation fails, we're in trouble: we cannot ignore the
1277                  * error -- otherwise ngircd would listen on all interfaces. */
1278                 if (!Conf_ListenAddress) {
1279                         Config_Error(LOG_ALERT,
1280                                      "%s exiting due to fatal errors!",
1281                                      PACKAGE_NAME);
1282                         exit(1);
1283                 }
1284                 return;
1285         }
1286         if (strcasecmp(Var, "MotdFile") == 0) {
1287                 len = strlcpy(Conf_MotdFile, Arg, sizeof(Conf_MotdFile));
1288                 if (len >= sizeof(Conf_MotdFile))
1289                         Config_Error_TooLong(Line, Var);
1290                 return;
1291         }
1292         if (strcasecmp(Var, "MotdPhrase") == 0) {
1293                 len = strlen(Arg);
1294                 if (len == 0)
1295                         return;
1296                 if (len >= LINE_LEN) {
1297                         Config_Error_TooLong(Line, Var);
1298                         return;
1299                 }
1300                 if (!array_copyb(&Conf_Motd, Arg, len + 1))
1301                         Config_Error(LOG_WARNING,
1302                                      "%s, line %d: Could not append MotdPhrase: %s",
1303                                      NGIRCd_ConfFile, Line, strerror(errno));
1304                 Using_MotdFile = false;
1305                 return;
1306         }
1307         if(strcasecmp(Var, "Password") == 0) {
1308                 len = strlcpy(Conf_ServerPwd, Arg, sizeof(Conf_ServerPwd));
1309                 if (len >= sizeof(Conf_ServerPwd))
1310                         Config_Error_TooLong(Line, Var);
1311                 return;
1312         }
1313         if (strcasecmp(Var, "PidFile") == 0) {
1314                 len = strlcpy(Conf_PidFile, Arg, sizeof(Conf_PidFile));
1315                 if (len >= sizeof(Conf_PidFile))
1316                         Config_Error_TooLong(Line, Var);
1317                 return;
1318         }
1319         if (strcasecmp(Var, "Ports") == 0) {
1320                 ports_parse(&Conf_ListenPorts, Line, Arg);
1321                 return;
1322         }
1323         if (strcasecmp(Var, "ServerGID") == 0) {
1324                 grp = getgrnam(Arg);
1325                 if (grp)
1326                         Conf_GID = grp->gr_gid;
1327                 else {
1328                         Conf_GID = (unsigned int)atoi(Arg);
1329                         if (!Conf_GID && strcmp(Arg, "0"))
1330                                 Config_Error(LOG_WARNING,
1331                                              "%s, line %d: Value of \"%s\" is not a valid group name or ID!",
1332                                              NGIRCd_ConfFile, Line, Var);
1333                 }
1334                 return;
1335         }
1336         if (strcasecmp(Var, "ServerUID") == 0) {
1337                 pwd = getpwnam(Arg);
1338                 if (pwd)
1339                         Conf_UID = pwd->pw_uid;
1340                 else {
1341                         Conf_UID = (unsigned int)atoi(Arg);
1342                         if (!Conf_UID && strcmp(Arg, "0"))
1343                                 Config_Error(LOG_WARNING,
1344                                              "%s, line %d: Value of \"%s\" is not a valid user name or ID!",
1345                                              NGIRCd_ConfFile, Line, Var);
1346                 }
1347                 return;
1348         }
1349
1350         if (CheckLegacyNoOption(Var, Arg)) {
1351                 /* TODO: This function and support for "NoXXX" could be
1352                  * be removed starting with ngIRCd release 19 (one release
1353                  * after marking it "deprecated"). */
1354                 Config_Error(LOG_WARNING,
1355                              "%s, line %d (section \"Global\"): \"No\"-Prefix is deprecated, use \"%s = %s\" in [Options] section!",
1356                              NGIRCd_ConfFile, Line, NoNo(Var), InvertArg(Arg));
1357                 if (strcasecmp(Var, "NoIdent") == 0)
1358                         WarnIdent(Line);
1359                 else if (strcasecmp(Var, "NoPam") == 0)
1360                         WarnPAM(Line);
1361                 return;
1362         }
1363         if ((section = CheckLegacyGlobalOption(Line, Var, Arg))) {
1364                 /** TODO: This function and support for these options in the
1365                  * [Global] section could be removed starting with ngIRCd
1366                  * release 19 (one release after marking it "deprecated"). */
1367                 if (strncasecmp(Var, "SSL", 3) == 0) {
1368                         Config_Error(LOG_WARNING,
1369                                      "%s, line %d (section \"Global\"): \"%s\" is deprecated here, move it to %s and rename to \"%s\"!",
1370                                      NGIRCd_ConfFile, Line, Var, section,
1371                                      Var + 3);
1372                 } else {
1373                         Config_Error(LOG_WARNING,
1374                                      "%s, line %d (section \"Global\"): \"%s\" is deprecated here, move it to %s!",
1375                                      NGIRCd_ConfFile, Line, Var, section);
1376                 }
1377                 return;
1378         }
1379
1380         Config_Error_Section(Line, Var, "Global");
1381 }
1382
1383 /**
1384  * Handle variable in [Limits] configuration section.
1385  *
1386  * @param Line  Line numer in configuration file.
1387  * @param Var   Variable name.
1388  * @param Arg   Variable argument.
1389  */
1390 static void
1391 Handle_LIMITS(int Line, char *Var, char *Arg)
1392 {
1393         assert(Line > 0);
1394         assert(Var != NULL);
1395         assert(Arg != NULL);
1396
1397         if (strcasecmp(Var, "ConnectRetry") == 0) {
1398                 Conf_ConnectRetry = atoi(Arg);
1399                 if (Conf_ConnectRetry < 5) {
1400                         Config_Error(LOG_WARNING,
1401                                      "%s, line %d: Value of \"ConnectRetry\" too low!",
1402                                      NGIRCd_ConfFile, Line);
1403                         Conf_ConnectRetry = 5;
1404                 }
1405                 return;
1406         }
1407         if (strcasecmp(Var, "MaxConnections") == 0) {
1408                 Conf_MaxConnections = atol(Arg);
1409                 if (!Conf_MaxConnections && strcmp(Arg, "0"))
1410                         Config_Error_NaN(Line, Var);
1411                 return;
1412         }
1413         if (strcasecmp(Var, "MaxConnectionsIP") == 0) {
1414                 Conf_MaxConnectionsIP = atoi(Arg);
1415                 if (!Conf_MaxConnectionsIP && strcmp(Arg, "0"))
1416                         Config_Error_NaN(Line, Var);
1417                 return;
1418         }
1419         if (strcasecmp(Var, "MaxJoins") == 0) {
1420                 Conf_MaxJoins = atoi(Arg);
1421                 if (!Conf_MaxJoins && strcmp(Arg, "0"))
1422                         Config_Error_NaN(Line, Var);
1423                 return;
1424         }
1425         if (strcasecmp(Var, "MaxNickLength") == 0) {
1426                 Conf_MaxNickLength = Handle_MaxNickLength(Line, Arg);
1427                 return;
1428         }
1429         if (strcasecmp(Var, "PingTimeout") == 0) {
1430                 Conf_PingTimeout = atoi(Arg);
1431                 if (Conf_PingTimeout < 5) {
1432                         Config_Error(LOG_WARNING,
1433                                      "%s, line %d: Value of \"PingTimeout\" too low!",
1434                                      NGIRCd_ConfFile, Line);
1435                         Conf_PingTimeout = 5;
1436                 }
1437                 return;
1438         }
1439         if (strcasecmp(Var, "PongTimeout") == 0) {
1440                 Conf_PongTimeout = atoi(Arg);
1441                 if (Conf_PongTimeout < 5) {
1442                         Config_Error(LOG_WARNING,
1443                                      "%s, line %d: Value of \"PongTimeout\" too low!",
1444                                      NGIRCd_ConfFile, Line);
1445                         Conf_PongTimeout = 5;
1446                 }
1447                 return;
1448         }
1449
1450         Config_Error_Section(Line, Var, "Limits");
1451 }
1452
1453 /**
1454  * Handle variable in [Options] configuration section.
1455  *
1456  * @param Line  Line numer in configuration file.
1457  * @param Var   Variable name.
1458  * @param Arg   Variable argument.
1459  */
1460 static void
1461 Handle_OPTIONS(int Line, char *Var, char *Arg)
1462 {
1463         size_t len;
1464
1465         assert(Line > 0);
1466         assert(Var != NULL);
1467         assert(Arg != NULL);
1468
1469         if (strcasecmp(Var, "AllowRemoteOper") == 0) {
1470                 Conf_AllowRemoteOper = Check_ArgIsTrue(Arg);
1471                 return;
1472         }
1473         if (strcasecmp(Var, "ChrootDir") == 0) {
1474                 len = strlcpy(Conf_Chroot, Arg, sizeof(Conf_Chroot));
1475                 if (len >= sizeof(Conf_Chroot))
1476                         Config_Error_TooLong(Line, Var);
1477                 return;
1478         }
1479         if (strcasecmp(Var, "CloakHost") == 0) {
1480                 len = strlcpy(Conf_CloakHost, Arg, sizeof(Conf_CloakHost));
1481                 if (len >= sizeof(Conf_CloakHost))
1482                         Config_Error_TooLong(Line, Var);
1483                 return;
1484         }
1485         if (strcasecmp(Var, "CloakHostModeX") == 0) {
1486                 len = strlcpy(Conf_CloakHostModeX, Arg, sizeof(Conf_CloakHostModeX));
1487                 if (len >= sizeof(Conf_CloakHostModeX))
1488                         Config_Error_TooLong(Line, Var);
1489                 return;
1490         }
1491         if (strcasecmp(Var, "CloakHostSalt") == 0) {
1492                 len = strlcpy(Conf_CloakHostSalt, Arg, sizeof(Conf_CloakHostSalt));
1493                 if (len >= sizeof(Conf_CloakHostSalt))
1494                         Config_Error_TooLong(Line, Var);
1495                 return;
1496         }
1497         if (strcasecmp(Var, "CloakUserToNick") == 0) {
1498                 Conf_CloakUserToNick = Check_ArgIsTrue(Arg);
1499                 return;
1500         }
1501         if (strcasecmp(Var, "ConnectIPv6") == 0) {
1502                 Conf_ConnectIPv6 = Check_ArgIsTrue(Arg);
1503                 WarnIPv6(Line);
1504                 return;
1505         }
1506         if (strcasecmp(Var, "ConnectIPv4") == 0) {
1507                 Conf_ConnectIPv4 = Check_ArgIsTrue(Arg);
1508                 return;
1509         }
1510         if (strcasecmp(Var, "DNS") == 0) {
1511                 Conf_DNS = Check_ArgIsTrue(Arg);
1512                 return;
1513         }
1514         if (strcasecmp(Var, "Ident") == 0) {
1515                 Conf_Ident = Check_ArgIsTrue(Arg);
1516                 WarnIdent(Line);
1517                 return;
1518         }
1519         if (strcasecmp(Var, "MorePrivacy") == 0) {
1520                 Conf_MorePrivacy = Check_ArgIsTrue(Arg);
1521                 return;
1522         }
1523         if (strcasecmp(Var, "NoticeAuth") == 0) {
1524                 Conf_NoticeAuth = Check_ArgIsTrue(Arg);
1525                 return;
1526         }
1527         if (strcasecmp(Var, "OperCanUseMode") == 0) {
1528                 Conf_OperCanMode = Check_ArgIsTrue(Arg);
1529                 return;
1530         }
1531         if (strcasecmp(Var, "OperServerMode") == 0) {
1532                 Conf_OperServerMode = Check_ArgIsTrue(Arg);
1533                 return;
1534         }
1535         if (strcasecmp(Var, "PAM") == 0) {
1536                 Conf_PAM = Check_ArgIsTrue(Arg);
1537                 WarnPAM(Line);
1538                 return;
1539         }
1540         if (strcasecmp(Var, "PAMIsOptional") == 0 ) {
1541                 Conf_PAMIsOptional = Check_ArgIsTrue(Arg);
1542                 return;
1543         }
1544         if (strcasecmp(Var, "PredefChannelsOnly") == 0) {
1545                 Conf_PredefChannelsOnly = Check_ArgIsTrue(Arg);
1546                 return;
1547         }
1548 #ifndef STRICT_RFC
1549         if (strcasecmp(Var, "RequireAuthPing") == 0) {
1550                 Conf_AuthPing = Check_ArgIsTrue(Arg);
1551                 return;
1552         }
1553 #endif
1554         if (strcasecmp(Var, "ScrubCTCP") == 0) {
1555                 Conf_ScrubCTCP = Check_ArgIsTrue(Arg);
1556                 return;
1557         }
1558 #ifdef SYSLOG
1559         if (strcasecmp(Var, "SyslogFacility") == 0) {
1560                 Conf_SyslogFacility = ngt_SyslogFacilityID(Arg,
1561                                                            Conf_SyslogFacility);
1562                 return;
1563         }
1564 #endif
1565         if (strcasecmp(Var, "WebircPassword") == 0) {
1566                 len = strlcpy(Conf_WebircPwd, Arg, sizeof(Conf_WebircPwd));
1567                 if (len >= sizeof(Conf_WebircPwd))
1568                         Config_Error_TooLong(Line, Var);
1569                 return;
1570         }
1571
1572         Config_Error_Section(Line, Var, "Options");
1573 }
1574
1575 #ifdef SSL_SUPPORT
1576
1577 /**
1578  * Handle variable in [SSL] configuration section.
1579  *
1580  * @param Line  Line numer in configuration file.
1581  * @param Var   Variable name.
1582  * @param Arg   Variable argument.
1583  */
1584 static void
1585 Handle_SSL(int Line, char *Var, char *Arg)
1586 {
1587         assert(Line > 0);
1588         assert(Var != NULL);
1589         assert(Arg != NULL);
1590
1591         if (strcasecmp(Var, "CertFile") == 0) {
1592                 assert(Conf_SSLOptions.CertFile == NULL);
1593                 Conf_SSLOptions.CertFile = strdup_warn(Arg);
1594                 return;
1595         }
1596         if (strcasecmp(Var, "DHFile") == 0) {
1597                 assert(Conf_SSLOptions.DHFile == NULL);
1598                 Conf_SSLOptions.DHFile = strdup_warn(Arg);
1599                 return;
1600         }
1601         if (strcasecmp(Var, "KeyFile") == 0) {
1602                 assert(Conf_SSLOptions.KeyFile == NULL);
1603                 Conf_SSLOptions.KeyFile = strdup_warn(Arg);
1604                 return;
1605         }
1606         if (strcasecmp(Var, "KeyFilePassword") == 0) {
1607                 assert(array_bytes(&Conf_SSLOptions.KeyFilePassword) == 0);
1608                 if (!array_copys(&Conf_SSLOptions.KeyFilePassword, Arg))
1609                         Config_Error(LOG_ERR,
1610                                      "%s, line %d (section \"SSL\"): Could not copy %s: %s!",
1611                                      NGIRCd_ConfFile, Line, Var,
1612                                      strerror(errno));
1613                 return;
1614         }
1615         if (strcasecmp(Var, "Ports") == 0) {
1616                 ports_parse(&Conf_SSLOptions.ListenPorts, Line, Arg);
1617                 return;
1618         }
1619
1620         Config_Error_Section(Line, Var, "SSL");
1621 }
1622
1623 #endif
1624
1625 /**
1626  * Handle variable in [Operator] configuration section.
1627  *
1628  * @param Line  Line numer in configuration file.
1629  * @param Var   Variable name.
1630  * @param Arg   Variable argument.
1631  */
1632 static void
1633 Handle_OPERATOR( int Line, char *Var, char *Arg )
1634 {
1635         size_t len;
1636         struct Conf_Oper *op;
1637
1638         assert( Line > 0 );
1639         assert( Var != NULL );
1640         assert( Arg != NULL );
1641
1642         op = array_get(&Conf_Opers, sizeof(*op),
1643                          array_length(&Conf_Opers, sizeof(*op)) - 1);
1644         if (!op)
1645                 return;
1646
1647         if (strcasecmp(Var, "Name") == 0) {
1648                 /* Name of IRC operator */
1649                 len = strlcpy(op->name, Arg, sizeof(op->name));
1650                 if (len >= sizeof(op->name))
1651                                 Config_Error_TooLong(Line, Var);
1652                 return;
1653         }
1654         if (strcasecmp(Var, "Password") == 0) {
1655                 /* Password of IRC operator */
1656                 len = strlcpy(op->pwd, Arg, sizeof(op->pwd));
1657                 if (len >= sizeof(op->pwd))
1658                                 Config_Error_TooLong(Line, Var);
1659                 return;
1660         }
1661         if (strcasecmp(Var, "Mask") == 0) {
1662                 if (op->mask)
1663                         return; /* Hostname already configured */
1664                 op->mask = strdup_warn( Arg );
1665                 return;
1666         }
1667
1668         Config_Error_Section(Line, Var, "Operator");
1669 }
1670
1671 /**
1672  * Handle variable in [Server] configuration section.
1673  *
1674  * @param Line  Line numer in configuration file.
1675  * @param Var   Variable name.
1676  * @param Arg   Variable argument.
1677  */
1678 static void
1679 Handle_SERVER( int Line, char *Var, char *Arg )
1680 {
1681         long port;
1682         size_t len;
1683
1684         assert( Line > 0 );
1685         assert( Var != NULL );
1686         assert( Arg != NULL );
1687
1688         /* Ignore server block if no space is left in server configuration structure */
1689         if( New_Server_Idx <= NONE ) return;
1690
1691         if( strcasecmp( Var, "Host" ) == 0 ) {
1692                 /* Hostname of the server */
1693                 len = strlcpy( New_Server.host, Arg, sizeof( New_Server.host ));
1694                 if (len >= sizeof( New_Server.host ))
1695                         Config_Error_TooLong ( Line, Var );
1696                 return;
1697         }
1698         if( strcasecmp( Var, "Name" ) == 0 ) {
1699                 /* Name of the server ("Nick"/"ID") */
1700                 len = strlcpy( New_Server.name, Arg, sizeof( New_Server.name ));
1701                 if (len >= sizeof( New_Server.name ))
1702                         Config_Error_TooLong( Line, Var );
1703                 return;
1704         }
1705         if (strcasecmp(Var, "Bind") == 0) {
1706                 if (ng_ipaddr_init(&New_Server.bind_addr, Arg, 0))
1707                         return;
1708
1709                 Config_Error(LOG_ERR, "%s, line %d (section \"Server\"): Can't parse IP address \"%s\"",
1710                                 NGIRCd_ConfFile, Line, Arg);
1711                 return;
1712         }
1713         if( strcasecmp( Var, "MyPassword" ) == 0 ) {
1714                 /* Password of this server which is sent to the peer */
1715                 if (*Arg == ':') {
1716                         Config_Error(LOG_ERR,
1717                                 "%s, line %d (section \"Server\"): MyPassword must not start with ':'!",
1718                                                                                 NGIRCd_ConfFile, Line);
1719                 }
1720                 len = strlcpy( New_Server.pwd_in, Arg, sizeof( New_Server.pwd_in ));
1721                 if (len >= sizeof( New_Server.pwd_in ))
1722                         Config_Error_TooLong( Line, Var );
1723                 return;
1724         }
1725         if( strcasecmp( Var, "PeerPassword" ) == 0 ) {
1726                 /* Passwort of the peer which must be received */
1727                 len = strlcpy( New_Server.pwd_out, Arg, sizeof( New_Server.pwd_out ));
1728                 if (len >= sizeof( New_Server.pwd_out ))
1729                         Config_Error_TooLong( Line, Var );
1730                 return;
1731         }
1732         if( strcasecmp( Var, "Port" ) == 0 ) {
1733                 /* Port to which this server should connect */
1734                 port = atol( Arg );
1735                 if (port >= 0 && port < 0xFFFF)
1736                         New_Server.port = (UINT16)port;
1737                 else
1738                         Config_Error(LOG_ERR,
1739                                 "%s, line %d (section \"Server\"): Illegal port number %ld!",
1740                                 NGIRCd_ConfFile, Line, port );
1741                 return;
1742         }
1743 #ifdef SSL_SUPPORT
1744         if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
1745                 New_Server.SSLConnect = Check_ArgIsTrue(Arg);
1746                 return;
1747         }
1748 #endif
1749         if( strcasecmp( Var, "Group" ) == 0 ) {
1750                 /* Server group */
1751                 New_Server.group = atoi( Arg );
1752                 if (!New_Server.group && strcmp(Arg, "0"))
1753                         Config_Error_NaN(Line, Var);
1754                 return;
1755         }
1756         if( strcasecmp( Var, "Passive" ) == 0 ) {
1757                 if (Check_ArgIsTrue(Arg))
1758                         New_Server.flags |= CONF_SFLAG_DISABLED;
1759                 return;
1760         }
1761         if (strcasecmp(Var, "ServiceMask") == 0) {
1762                 len = strlcpy(New_Server.svs_mask, ngt_LowerStr(Arg),
1763                               sizeof(New_Server.svs_mask));
1764                 if (len >= sizeof(New_Server.svs_mask))
1765                         Config_Error_TooLong(Line, Var);
1766                 return;
1767         }
1768
1769         Config_Error_Section(Line, Var, "Server");
1770 }
1771
1772 /**
1773  * Copy channel name into channel structure.
1774  *
1775  * If the channel name is not valid because of a missing prefix ('#', '&'),
1776  * a default prefix of '#' will be added.
1777  *
1778  * @param new_chan      New already allocated channel structure.
1779  * @param name          Name of the new channel.
1780  * @returns             true on success, false otherwise.
1781  */
1782 static bool
1783 Handle_Channelname(struct Conf_Channel *new_chan, const char *name)
1784 {
1785         size_t size = sizeof(new_chan->name);
1786         char *dest = new_chan->name;
1787
1788         if (!Channel_IsValidName(name)) {
1789                 /*
1790                  * maybe user forgot to add a '#'.
1791                  * This is only here for user convenience.
1792                  */
1793                 *dest = '#';
1794                 --size;
1795                 ++dest;
1796         }
1797         return size > strlcpy(dest, name, size);
1798 }
1799
1800 /**
1801  * Handle variable in [Channel] configuration section.
1802  *
1803  * @param Line  Line numer in configuration file.
1804  * @param Var   Variable name.
1805  * @param Arg   Variable argument.
1806  */
1807 static void
1808 Handle_CHANNEL(int Line, char *Var, char *Arg)
1809 {
1810         size_t len;
1811         struct Conf_Channel *chan;
1812
1813         assert( Line > 0 );
1814         assert( Var != NULL );
1815         assert( Arg != NULL );
1816
1817         chan = array_get(&Conf_Channels, sizeof(*chan),
1818                          array_length(&Conf_Channels, sizeof(*chan)) - 1);
1819         if (!chan)
1820                 return;
1821
1822         if (strcasecmp(Var, "Name") == 0) {
1823                 if (!Handle_Channelname(chan, Arg))
1824                         Config_Error_TooLong(Line, Var);
1825                 return;
1826         }
1827         if (strcasecmp(Var, "Modes") == 0) {
1828                 /* Initial modes */
1829                 len = strlcpy(chan->modes, Arg, sizeof(chan->modes));
1830                 if (len >= sizeof(chan->modes))
1831                         Config_Error_TooLong( Line, Var );
1832                 return;
1833         }
1834         if( strcasecmp( Var, "Topic" ) == 0 ) {
1835                 /* Initial topic */
1836                 len = strlcpy(chan->topic, Arg, sizeof(chan->topic));
1837                 if (len >= sizeof(chan->topic))
1838                         Config_Error_TooLong( Line, Var );
1839                 return;
1840         }
1841         if( strcasecmp( Var, "Key" ) == 0 ) {
1842                 /* Initial Channel Key (mode k) */
1843                 len = strlcpy(chan->key, Arg, sizeof(chan->key));
1844                 if (len >= sizeof(chan->key))
1845                         Config_Error_TooLong(Line, Var);
1846                 return;
1847         }
1848         if( strcasecmp( Var, "MaxUsers" ) == 0 ) {
1849                 /* maximum user limit, mode l */
1850                 chan->maxusers = (unsigned long) atol(Arg);
1851                 if (!chan->maxusers && strcmp(Arg, "0"))
1852                         Config_Error_NaN(Line, Var);
1853                 return;
1854         }
1855         if (strcasecmp(Var, "KeyFile") == 0) {
1856                 /* channel keys */
1857                 len = strlcpy(chan->keyfile, Arg, sizeof(chan->keyfile));
1858                 if (len >= sizeof(chan->keyfile))
1859                         Config_Error_TooLong(Line, Var);
1860                 return;
1861         }
1862
1863         Config_Error_Section(Line, Var, "Channel");
1864 }
1865
1866 /**
1867  * Validate server configuration.
1868  *
1869  * Please note that this function uses exit(1) on fatal errors and therefore
1870  * can result in ngIRCd terminating!
1871  *
1872  * @param Configtest    true if the daemon has been called with "--configtest".
1873  * @param Rehash        true if re-reading configuration on runtime.
1874  * @returns             true if configuration is valid.
1875  */
1876 static bool
1877 Validate_Config(bool Configtest, bool Rehash)
1878 {
1879         /* Validate configuration settings. */
1880
1881 #ifdef DEBUG
1882         int i, servers, servers_once;
1883 #endif
1884         bool config_valid = true;
1885         char *ptr;
1886
1887         /* Validate configured server name, see RFC 2812 section 2.3.1 */
1888         ptr = Conf_ServerName;
1889         do {
1890                 if (*ptr >= 'a' && *ptr <= 'z') continue;
1891                 if (*ptr >= 'A' && *ptr <= 'Z') continue;
1892                 if (*ptr >= '0' && *ptr <= '9') continue;
1893                 if (ptr > Conf_ServerName) {
1894                         if (*ptr == '.' || *ptr == '-')
1895                                 continue;
1896                 }
1897                 Conf_ServerName[0] = '\0';
1898                 break;
1899         } while (*(++ptr));
1900
1901         if (!Conf_ServerName[0]) {
1902                 /* No server name configured! */
1903                 config_valid = false;
1904                 Config_Error(LOG_ALERT,
1905                              "No (valid) server name configured in \"%s\" (section 'Global': 'Name')!",
1906                              NGIRCd_ConfFile);
1907                 if (!Configtest && !Rehash) {
1908                         Config_Error(LOG_ALERT,
1909                                      "%s exiting due to fatal errors!",
1910                                      PACKAGE_NAME);
1911                         exit(1);
1912                 }
1913         }
1914
1915         if (Conf_ServerName[0] && !strchr(Conf_ServerName, '.')) {
1916                 /* No dot in server name! */
1917                 config_valid = false;
1918                 Config_Error(LOG_ALERT,
1919                              "Invalid server name configured in \"%s\" (section 'Global': 'Name'): Dot missing!",
1920                              NGIRCd_ConfFile);
1921                 if (!Configtest) {
1922                         Config_Error(LOG_ALERT,
1923                                      "%s exiting due to fatal errors!",
1924                                      PACKAGE_NAME);
1925                         exit(1);
1926                 }
1927         }
1928
1929 #ifdef STRICT_RFC
1930         if (!Conf_ServerAdminMail[0]) {
1931                 /* No administrative contact configured! */
1932                 config_valid = false;
1933                 Config_Error(LOG_ALERT,
1934                              "No administrator email address configured in \"%s\" ('AdminEMail')!",
1935                              NGIRCd_ConfFile);
1936                 if (!Configtest) {
1937                         Config_Error(LOG_ALERT,
1938                                      "%s exiting due to fatal errors!",
1939                                      PACKAGE_NAME);
1940                         exit(1);
1941                 }
1942         }
1943 #endif
1944
1945         if (!Conf_ServerAdmin1[0] && !Conf_ServerAdmin2[0]
1946             && !Conf_ServerAdminMail[0]) {
1947                 /* No administrative information configured! */
1948                 Config_Error(LOG_WARNING,
1949                              "No administrative information configured but required by RFC!");
1950         }
1951
1952 #ifdef PAM
1953         if (Conf_ServerPwd[0])
1954                 Config_Error(LOG_ERR,
1955                              "This server uses PAM, \"Password\" in [Global] section will be ignored!");
1956 #endif
1957
1958 #ifdef DEBUG
1959         servers = servers_once = 0;
1960         for (i = 0; i < MAX_SERVERS; i++) {
1961                 if (Conf_Server[i].name[0]) {
1962                         servers++;
1963                         if (Conf_Server[i].flags & CONF_SFLAG_ONCE)
1964                                 servers_once++;
1965                 }
1966         }
1967         Log(LOG_DEBUG,
1968             "Configuration: Operators=%ld, Servers=%d[%d], Channels=%ld",
1969             array_length(&Conf_Opers, sizeof(struct Conf_Oper)),
1970             servers, servers_once,
1971             array_length(&Conf_Channels, sizeof(struct Conf_Channel)));
1972 #endif
1973
1974         return config_valid;
1975 }
1976
1977 /**
1978  * Output "line too long" warning.
1979  *
1980  * @param Line  Line number in configuration file.
1981  * @param Item  Affected variable name.
1982  */
1983 static void
1984 Config_Error_TooLong ( const int Line, const char *Item )
1985 {
1986         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" too long!", NGIRCd_ConfFile, Line, Item );
1987 }
1988
1989 /**
1990  * Output "unknown variable" warning.
1991  *
1992  * @param Line          Line number in configuration file.
1993  * @param Item          Affected variable name.
1994  * @param Section       Section name.
1995  */
1996 static void
1997 Config_Error_Section(const int Line, const char *Item, const char *Section)
1998 {
1999         Config_Error(LOG_ERR, "%s, line %d (section \"%s\"): Unknown variable \"%s\"!",
2000                      NGIRCd_ConfFile, Line, Section, Item);
2001 }
2002
2003 /**
2004  * Output "not a number" warning.
2005  *
2006  * @param Line  Line number in configuration file.
2007  * @param Item  Affected variable name.
2008  */
2009 static void
2010 Config_Error_NaN( const int Line, const char *Item )
2011 {
2012         Config_Error( LOG_WARNING, "%s, line %d: Value of \"%s\" is not a number!",
2013                                                 NGIRCd_ConfFile, Line, Item );
2014 }
2015
2016 /**
2017  * Output configuration error to console and/or logfile.
2018  *
2019  * On runtime, the normal log functions of the daemon are used. But when
2020  * testing the configuration ("--configtest"), all messages go directly
2021  * to the console.
2022  *
2023  * @param Level         Severity level of the message.
2024  * @param Format        Format string; see printf() function.
2025  */
2026 #ifdef PROTOTYPES
2027 static void Config_Error( const int Level, const char *Format, ... )
2028 #else
2029 static void Config_Error( Level, Format, va_alist )
2030 const int Level;
2031 const char *Format;
2032 va_dcl
2033 #endif
2034 {
2035         char msg[MAX_LOG_MSG_LEN];
2036         va_list ap;
2037
2038         assert( Format != NULL );
2039
2040 #ifdef PROTOTYPES
2041         va_start( ap, Format );
2042 #else
2043         va_start( ap );
2044 #endif
2045         vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
2046         va_end( ap );
2047
2048         if (!Use_Log) {
2049                 if (Level <= LOG_WARNING)
2050                         printf(" - %s\n", msg);
2051                 else
2052                         puts(msg);
2053         } else
2054                 Log(Level, "%s", msg);
2055 }
2056
2057 #ifdef DEBUG
2058
2059 /**
2060  * Dump internal state of the "configuration module".
2061  */
2062 GLOBAL void
2063 Conf_DebugDump(void)
2064 {
2065         int i;
2066
2067         Log(LOG_DEBUG, "Configured servers:");
2068         for (i = 0; i < MAX_SERVERS; i++) {
2069                 if (! Conf_Server[i].name[0])
2070                         continue;
2071                 Log(LOG_DEBUG,
2072                     " - %s: %s:%d, last=%ld, group=%d, flags=%d, conn=%d",
2073                     Conf_Server[i].name, Conf_Server[i].host,
2074                     Conf_Server[i].port, Conf_Server[i].lasttry,
2075                     Conf_Server[i].group, Conf_Server[i].flags,
2076                     Conf_Server[i].conn_id);
2077         }
2078 }
2079
2080 #endif
2081
2082 /**
2083  * Initialize server configuration structur to default values.
2084  *
2085  * @param Server        Pointer to server structure to initialize.
2086  */
2087 static void
2088 Init_Server_Struct( CONF_SERVER *Server )
2089 {
2090         assert( Server != NULL );
2091
2092         memset( Server, 0, sizeof (CONF_SERVER) );
2093
2094         Server->group = NONE;
2095         Server->lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
2096
2097         if( NGIRCd_Passive ) Server->flags = CONF_SFLAG_DISABLED;
2098
2099         Proc_InitStruct(&Server->res_stat);
2100         Server->conn_id = NONE;
2101         memset(&Server->bind_addr, 0, sizeof(Server->bind_addr));
2102 }
2103
2104 /* -eof- */