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