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