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