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