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