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