29b14ef6fef7a441088cf140aaf2868bcba2871c
[ircu2.10.12-pk.git] / ircd / ircd_parser.y
1 /*
2  * ircd_parser.y: A yacc/bison parser for ircd config files.
3  * This is part of ircu, an Internet Relay Chat server.
4  * The contents of this file are Copyright 2001 Diane Bruce,
5  * Andrew Miller, the ircd-hybrid team and the ircu team.
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19  *  USA.
20  * $Id$
21  */
22 %{
23
24 #include "config.h"
25 #include "s_conf.h"
26 #include "class.h"
27 #include "client.h"
28 #include "crule.h"
29 #include "ircd_features.h"
30 #include "fileio.h"
31 #include "gline.h"
32 #include "hash.h"
33 #include "ircd.h"
34 #include "ircd_alloc.h"
35 #include "ircd_chattr.h"
36 #include "ircd_log.h"
37 #include "ircd_reply.h"
38 #include "ircd_snprintf.h"
39 #include "ircd_string.h"
40 #include "list.h"
41 #include "listener.h"
42 #include "match.h"
43 #include "motd.h"
44 #include "numeric.h"
45 #include "numnicks.h"
46 #include "opercmds.h"
47 #include "parse.h"
48 #include "res.h"
49 #include "s_auth.h"
50 #include "s_bsd.h"
51 #include "s_conf.h"
52 #include "s_debug.h"
53 #include "s_misc.h"
54 #include "send.h"
55 #include "struct.h"
56 #include "sys.h"
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <arpa/inet.h>
61
62 #define MAX_STRINGS 80 /* Maximum number of feature params. */
63 #define USE_IPV4 (1 << 0)
64 #define USE_IPV6 (1 << 1)
65
66   extern struct LocalConf   localConf;
67   extern struct DenyConf*   denyConfList;
68   extern struct CRuleConf*  cruleConfList;
69   extern struct ServerConf* serverConfList;
70   extern struct s_map*      GlobalServiceMapList;
71   extern struct qline*      GlobalQuarantineList;
72
73   int yylex(void);
74   /* Now all the globals we need :/... */
75   int tping, tconn, maxlinks, sendq, port, invert, stringno, flags;
76   char *name, *pass, *host, *ip, *username, *origin, *hub_limit;
77   char *stringlist[MAX_STRINGS];
78   struct ListenerFlags listen_flags;
79   struct ConnectionClass *c_class;
80   struct DenyConf *dconf;
81   struct ServerConf *sconf;
82   struct s_map *smap;
83   struct Privs privs;
84   struct Privs privs_dirty;
85
86 static void parse_error(char *pattern,...) {
87   static char error_buffer[1024];
88   va_list vl;
89   va_start(vl,pattern);
90   ircd_vsnprintf(NULL, error_buffer, sizeof(error_buffer), pattern, vl);
91   va_end(vl);
92   yyerror(error_buffer);
93 }
94
95 %}
96
97 %token <text> QSTRING
98 %token <num> NUMBER
99
100 %token GENERAL
101 %token ADMIN
102 %token LOCATION
103 %token CONTACT
104 %token CONNECT
105 %token CLASS
106 %token CHANNEL
107 %token PINGFREQ
108 %token CONNECTFREQ
109 %token MAXLINKS
110 %token MAXHOPS
111 %token SENDQ
112 %token NAME
113 %token HOST
114 %token IP
115 %token USERNAME
116 %token PASS
117 %token LOCAL
118 %token SECONDS
119 %token MINUTES
120 %token HOURS
121 %token DAYS
122 %token WEEKS
123 %token MONTHS
124 %token YEARS
125 %token DECADES
126 %token BYTES
127 %token KBYTES
128 %token MBYTES
129 %token GBYTES
130 %token TBYTES
131 %token SERVER
132 %token PORT
133 %token MASK
134 %token HUB
135 %token LEAF
136 %token UWORLD
137 %token YES
138 %token NO
139 %token OPER
140 %token VHOST
141 %token HIDDEN
142 %token MOTD
143 %token JUPE
144 %token NICK
145 %token NUMERIC
146 %token DESCRIPTION
147 %token CLIENT
148 %token KILL
149 %token CRULE
150 %token REAL
151 %token REASON
152 %token TFILE
153 %token RULE
154 %token ALL
155 %token FEATURES
156 %token QUARANTINE
157 %token PSEUDO
158 %token PREPEND
159 %token USERMODE
160 %token IAUTH
161 %token TIMEOUT
162 %token FAST
163 %token AUTOCONNECT
164 %token PROGRAM
165 %token TOK_IPV4 TOK_IPV6
166 %token DNS
167 /* and now a lot of privileges... */
168 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
169 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
170 %token TPRIV_GLINE TPRIV_LOCAL_GLINE TPRIV_LOCAL_JUPE TPRIV_LOCAL_BADCHAN
171 %token TPRIV_LOCAL_OPMODE TPRIV_OPMODE TPRIV_SET TPRIV_WHOX TPRIV_BADCHAN
172 %token TPRIV_SEE_CHAN TPRIV_SHOW_INVIS TPRIV_SHOW_ALL_INVIS TPRIV_PROPAGATE
173 %token TPRIV_UNLIMIT_QUERY TPRIV_DISPLAY TPRIV_SEE_OPERS TPRIV_WIDE_GLINE
174 %token TPRIV_FORCE_OPMODE TPRIV_FORCE_LOCAL_OPMODE TPRIV_APASS_OPMODE
175 %token TPRIV_LIST_CHAN
176 /* and some types... */
177 %type <num> sizespec
178 %type <num> timespec timefactor factoredtimes factoredtime
179 %type <num> expr yesorno privtype address_family
180 %left '+' '-'
181 %left '*' '/'
182
183 %union{
184  char *text;
185  int num;
186 }
187
188 %%
189 /* Blocks in the config file... */
190 blocks: blocks block | block;
191 block: adminblock | generalblock | classblock | connectblock |
192        uworldblock | operblock | portblock | jupeblock | clientblock |
193        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
194        pseudoblock | iauthblock | error ';';
195
196 /* The timespec, sizespec and expr was ripped straight from
197  * ircd-hybrid-7. */
198 timespec: expr | factoredtimes;
199
200 factoredtimes: factoredtimes factoredtime
201 {
202   $$ = $1 + $2;
203 } | factoredtime;
204
205 factoredtime: expr timefactor
206 {
207   $$ = $1 * $2;
208 };
209
210 timefactor: SECONDS { $$ = 1; }
211 | MINUTES { $$ = 60; }
212 | HOURS { $$ = 60 * 60; }
213 | DAYS { $$ = 60 * 60 * 24; }
214 | WEEKS { $$ = 60 * 60 * 24 * 7; }
215 | MONTHS { $$ = 60 * 60 * 24 * 7 * 4; }
216 | YEARS { $$ = 60 * 60 * 24 * 365; }
217 | DECADES { $$ = 60 * 60 * 24 * 365 * 10; };
218
219
220 sizespec:       expr    {
221                         $$ = $1;
222                 }
223                 | expr BYTES  { 
224                         $$ = $1;
225                 }
226                 | expr KBYTES {
227                         $$ = $1 * 1024;
228                 }
229                 | expr MBYTES {
230                         $$ = $1 * 1024 * 1024;
231                 }
232                 | expr GBYTES {
233                         $$ = $1 * 1024 * 1024 * 1024;
234                 }
235                 | expr TBYTES {
236                         $$ = $1 * 1024 * 1024 * 1024;
237                 }
238                 ;
239
240 /* this is an arithmetic expression */
241 expr: NUMBER
242                 { 
243                         $$ = $1;
244                 }
245                 | expr '+' expr { 
246                         $$ = $1 + $3;
247                 }
248                 | expr '-' expr { 
249                         $$ = $1 - $3;
250                 }
251                 | expr '*' expr { 
252                         $$ = $1 * $3;
253                 }
254                 | expr '/' expr { 
255                         $$ = $1 / $3;
256                 }
257 /* leave this out until we find why it makes BSD yacc dump core -larne
258                 | '-' expr  %prec NEG {
259                         $$ = -$2;
260                 } */
261                 | '(' expr ')' {
262                         $$ = $2;
263                 }
264                 ;
265
266 jupeblock: JUPE '{' jupeitems '}' ';' ;
267 jupeitems: jupeitem jupeitems | jupeitem;
268 jupeitem: jupenick;
269 jupenick: NICK '=' QSTRING ';'
270 {
271   addNickJupes($3);
272   MyFree($3);
273 };
274
275 generalblock: GENERAL
276 {
277     /* Zero out the vhost addresses, in case they were removed. */
278     memset(&VirtualHost_v4.addr, 0, sizeof(VirtualHost_v4.addr));
279     memset(&VirtualHost_v6.addr, 0, sizeof(VirtualHost_v6.addr));
280 } '{' generalitems '}' ';' {
281   if (localConf.name == NULL)
282     parse_error("Your General block must contain a name.");
283   if (localConf.numeric == 0)
284     parse_error("Your General block must contain a numeric (between 1 and 4095).");
285 };
286 generalitems: generalitem generalitems | generalitem;
287 generalitem: generalnumeric | generalname | generalvhost | generaldesc
288   | generaldnsvhost | generaldnsserver;
289
290 generalnumeric: NUMERIC '=' NUMBER ';'
291 {
292   if (localConf.numeric == 0)
293     localConf.numeric = $3;
294   else if (localConf.numeric != $3)
295     parse_error("Redefinition of server numeric %i (%i)", $3,
296                 localConf.numeric);
297 };
298
299 generalname: NAME '=' QSTRING ';'
300 {
301   if (localConf.name == NULL)
302     localConf.name = $3;
303   else {
304     if (strcmp(localConf.name, $3))
305       parse_error("Redefinition of server name %s (%s)", $3,
306                   localConf.name);
307     MyFree($3);
308   }
309 };
310
311 generaldesc: DESCRIPTION '=' QSTRING ';'
312 {
313   MyFree(localConf.description);
314   localConf.description = $3;
315   ircd_strncpy(cli_info(&me), $3, REALLEN);
316 };
317
318 generalvhost: VHOST '=' QSTRING ';'
319 {
320   struct irc_in_addr addr;
321   char *vhost = $3;
322
323   if (!strcmp(vhost, "*")) {
324     /* This traditionally meant bind to all interfaces and connect
325      * from the default. */
326   } else if (!ircd_aton(&addr, vhost))
327     parse_error("Invalid virtual host '%s'.", vhost);
328   else if (irc_in_addr_is_ipv4(&addr))
329     memcpy(&VirtualHost_v4.addr, &addr, sizeof(addr));
330   else
331     memcpy(&VirtualHost_v6.addr, &addr, sizeof(addr));
332   MyFree(vhost);
333 };
334
335 generaldnsvhost: DNS VHOST '=' address_family QSTRING ';'
336 {
337   struct irc_in_addr addr;
338   int families = $4;
339   char *vhost = $5;
340
341   if (!strcmp(vhost, "*")) {
342     /* Let the operating system assign the default. */
343   } else if (!ircd_aton(&addr, vhost))
344     parse_error("Invalid DNS virtual host '%s'.", vhost);
345   else
346   {
347     if ((families & USE_IPV4)
348         || (!families && irc_in_addr_is_ipv4(&addr)))
349       memcpy(&VirtualHost_dns_v4.addr, &addr, sizeof(addr));
350     if ((families & USE_IPV6)
351         || (!families && !irc_in_addr_is_ipv4(&addr)))
352       memcpy(&VirtualHost_dns_v6.addr, &addr, sizeof(addr));
353   }
354   MyFree(vhost);
355 };
356
357 generaldnsserver: DNS SERVER '=' QSTRING ';'
358 {
359   char *server = $4;
360
361   add_nameserver(server);
362   MyFree(server);
363 };
364
365 adminblock: ADMIN
366 {
367   MyFree(localConf.location1);
368   MyFree(localConf.location2);
369   MyFree(localConf.contact);
370   localConf.location1 = localConf.location2 = localConf.contact = NULL;
371 }
372 '{' adminitems '}' ';'
373 {
374   if (localConf.location1 == NULL)
375     DupString(localConf.location1, "");
376   if (localConf.location2 == NULL)
377     DupString(localConf.location2, "");
378   if (localConf.contact == NULL)
379     DupString(localConf.contact, "");
380 };
381 adminitems: adminitems adminitem | adminitem;
382 adminitem: adminlocation | admincontact;
383 adminlocation: LOCATION '=' QSTRING ';'
384 {
385   if (localConf.location1 == NULL)
386     localConf.location1 = $3;
387   else if (localConf.location2 == NULL)
388     localConf.location2 = $3;
389   else /* Otherwise just drop it. -A1kmm */
390     MyFree($3);
391 };
392 admincontact: CONTACT '=' QSTRING ';'
393 {
394  MyFree(localConf.contact);
395  localConf.contact = $3;
396 };
397
398 classblock: CLASS {
399   tping = 90;
400 } '{' classitems '}' ';'
401 {
402   if (name != NULL)
403   {
404     struct ConnectionClass *c_class;
405     add_class(name, tping, tconn, maxlinks, sendq);
406     c_class = find_class(name);
407     MyFree(c_class->default_umode);
408     c_class->default_umode = pass;
409     memcpy(&c_class->privs, &privs, sizeof(c_class->privs));
410     memcpy(&c_class->privs_dirty, &privs_dirty, sizeof(c_class->privs_dirty));
411   }
412   else {
413    parse_error("Missing name in class block");
414   }
415   name = NULL;
416   pass = NULL;
417   tconn = 0;
418   maxlinks = 0;
419   sendq = 0;
420   memset(&privs, 0, sizeof(privs));
421   memset(&privs_dirty, 0, sizeof(privs_dirty));
422 };
423 classitems: classitem classitems | classitem;
424 classitem: classname | classpingfreq | classconnfreq | classmaxlinks |
425            classsendq | classusermode | priv;
426 classname: NAME '=' QSTRING ';'
427 {
428   MyFree(name);
429   name = $3;
430 };
431 classpingfreq: PINGFREQ '=' timespec ';'
432 {
433   tping = $3;
434 };
435 classconnfreq: CONNECTFREQ '=' timespec ';'
436 {
437   tconn = $3;
438 };
439 classmaxlinks: MAXLINKS '=' expr ';'
440 {
441   maxlinks = $3;
442 };
443 classsendq: SENDQ '=' sizespec ';'
444 {
445   sendq = $3;
446 };
447 classusermode: USERMODE '=' QSTRING ';'
448 {
449   MyFree(pass);
450   pass = $3;
451 };
452
453 connectblock: CONNECT
454 {
455  maxlinks = 65535;
456  flags = CONF_AUTOCONNECT;
457 } '{' connectitems '}' ';'
458 {
459  struct ConfItem *aconf = NULL;
460  if (name == NULL)
461   parse_error("Missing name in connect block");
462  else if (pass == NULL)
463   parse_error("Missing password in connect block");
464  else if (strlen(pass) > PASSWDLEN)
465   parse_error("Password too long in connect block");
466  else if (host == NULL)
467   parse_error("Missing host in connect block");
468  else if (strchr(host, '*') || strchr(host, '?'))
469   parse_error("Invalid host '%s' in connect block", host);
470  else if (c_class == NULL)
471   parse_error("Missing or non-existent class in connect block");
472  else {
473    aconf = make_conf(CONF_SERVER);
474    aconf->name = name;
475    aconf->origin_name = origin;
476    aconf->passwd = pass;
477    aconf->conn_class = c_class;
478    aconf->address.port = port;
479    aconf->host = host;
480    aconf->maximum = maxlinks;
481    aconf->hub_limit = hub_limit;
482    aconf->flags = flags;
483    lookup_confhost(aconf);
484  }
485  if (!aconf) {
486    MyFree(name);
487    MyFree(pass);
488    MyFree(host);
489    MyFree(origin);
490    MyFree(hub_limit);
491  }
492  name = pass = host = origin = hub_limit = NULL;
493  c_class = NULL;
494  port = flags = 0;
495 };
496 connectitems: connectitem connectitems | connectitem;
497 connectitem: connectname | connectpass | connectclass | connecthost
498               | connectport | connectvhost | connectleaf | connecthub
499               | connecthublimit | connectmaxhops | connectauto;
500 connectname: NAME '=' QSTRING ';'
501 {
502  MyFree(name);
503  name = $3;
504 };
505 connectpass: PASS '=' QSTRING ';'
506 {
507  MyFree(pass);
508  pass = $3;
509 };
510 connectclass: CLASS '=' QSTRING ';'
511 {
512  c_class = find_class($3);
513  if (!c_class)
514   parse_error("No such connection class '%s' for Connect block", $3);
515  MyFree($3);
516 };
517 connecthost: HOST '=' QSTRING ';'
518 {
519  MyFree(host);
520  host = $3;
521 };
522 connectport: PORT '=' NUMBER ';'
523 {
524  port = $3;
525 };
526 connectvhost: VHOST '=' QSTRING ';'
527 {
528  MyFree(origin);
529  origin = $3;
530 };
531 connectleaf: LEAF ';'
532 {
533  maxlinks = 0;
534 };
535 connecthub: HUB ';'
536 {
537  MyFree(hub_limit);
538  DupString(hub_limit, "*");
539 };
540 connecthublimit: HUB '=' QSTRING ';'
541 {
542  MyFree(hub_limit);
543  hub_limit = $3;
544 };
545 connectmaxhops: MAXHOPS '=' expr ';'
546 {
547   maxlinks = $3;
548 };
549 connectauto: AUTOCONNECT '=' YES ';' { flags |= CONF_AUTOCONNECT; }
550  | AUTOCONNECT '=' NO ';' { flags &= ~CONF_AUTOCONNECT; };
551
552 uworldblock: UWORLD '{' uworlditems '}' ';';
553 uworlditems: uworlditem uworlditems | uworlditem;
554 uworlditem: uworldname;
555 uworldname: NAME '=' QSTRING ';'
556 {
557   make_conf(CONF_UWORLD)->host = $3;
558 };
559
560 operblock: OPER '{' operitems '}' ';'
561 {
562   struct ConfItem *aconf = NULL;
563   if (name == NULL)
564     parse_error("Missing name in operator block");
565   else if (pass == NULL)
566     parse_error("Missing password in operator block");
567   /* Do not check password length because it may be crypted. */
568   else if (host == NULL)
569     parse_error("Missing host in operator block");
570   else if (c_class == NULL)
571     parse_error("Invalid or missing class in operator block");
572   else if (!FlagHas(&privs_dirty, PRIV_PROPAGATE)
573            && !FlagHas(&c_class->privs_dirty, PRIV_PROPAGATE))
574     parse_error("Operator block for %s and class %s have no LOCAL setting", name, c_class->cc_name);
575   else {
576     aconf = make_conf(CONF_OPERATOR);
577     aconf->name = name;
578     aconf->passwd = pass;
579     conf_parse_userhost(aconf, host);
580     aconf->conn_class = c_class;
581     memcpy(&aconf->privs, &privs, sizeof(aconf->privs));
582     memcpy(&aconf->privs_dirty, &privs_dirty, sizeof(aconf->privs_dirty));
583   }
584   if (!aconf) {
585     MyFree(name);
586     MyFree(pass);
587     MyFree(host);
588   }
589   name = pass = host = NULL;
590   c_class = NULL;
591   memset(&privs, 0, sizeof(privs));
592   memset(&privs_dirty, 0, sizeof(privs_dirty));
593 };
594 operitems: operitem | operitems operitem;
595 operitem: opername | operpass | operhost | operclass | priv;
596 opername: NAME '=' QSTRING ';'
597 {
598   MyFree(name);
599   name = $3;
600 };
601 operpass: PASS '=' QSTRING ';'
602 {
603   MyFree(pass);
604   pass = $3;
605 };
606 operhost: HOST '=' QSTRING ';'
607 {
608  MyFree(host);
609  if (!strchr($3, '@'))
610  {
611    int uh_len;
612    host = (char*) MyMalloc((uh_len = strlen($3)+3));
613    ircd_snprintf(0, host, uh_len, "*@%s", $3);
614    MyFree($3);
615  }
616  else
617    host = $3;
618 };
619 operclass: CLASS '=' QSTRING ';'
620 {
621  c_class = find_class($3);
622  if (!c_class)
623   parse_error("No such connection class '%s' for Operator block", $3);
624  MyFree($3);
625 };
626
627 priv: privtype '=' yesorno ';'
628 {
629   FlagSet(&privs_dirty, $1);
630   if (($3 == 1) ^ invert)
631     FlagSet(&privs, $1);
632   else
633     FlagClr(&privs, $1);
634   invert = 0;
635 };
636
637 privtype: TPRIV_CHAN_LIMIT { $$ = PRIV_CHAN_LIMIT; } |
638           TPRIV_MODE_LCHAN { $$ = PRIV_MODE_LCHAN; } |
639           TPRIV_DEOP_LCHAN { $$ = PRIV_DEOP_LCHAN; } |
640           TPRIV_WALK_LCHAN { $$ = PRIV_WALK_LCHAN; } |
641           KILL { $$ = PRIV_KILL; } |
642           TPRIV_LOCAL_KILL { $$ = PRIV_LOCAL_KILL; } |
643           TPRIV_REHASH { $$ = PRIV_REHASH; } |
644           TPRIV_RESTART { $$ = PRIV_RESTART; } |
645           TPRIV_DIE { $$ = PRIV_DIE; } |
646           TPRIV_GLINE { $$ = PRIV_GLINE; } |
647           TPRIV_LOCAL_GLINE { $$ = PRIV_LOCAL_GLINE; } |
648           JUPE { $$ = PRIV_JUPE; } |
649           TPRIV_LOCAL_JUPE { $$ = PRIV_LOCAL_JUPE; } |
650           TPRIV_LOCAL_OPMODE { $$ = PRIV_LOCAL_OPMODE; } |
651           TPRIV_OPMODE { $$ = PRIV_OPMODE; }|
652           TPRIV_SET { $$ = PRIV_SET; } |
653           TPRIV_WHOX { $$ = PRIV_WHOX; } |
654           TPRIV_BADCHAN { $$ = PRIV_BADCHAN; } |
655           TPRIV_LOCAL_BADCHAN { $$ = PRIV_LOCAL_BADCHAN; } |
656           TPRIV_SEE_CHAN { $$ = PRIV_SEE_CHAN; } |
657           TPRIV_SHOW_INVIS { $$ = PRIV_SHOW_INVIS; } |
658           TPRIV_SHOW_ALL_INVIS { $$ = PRIV_SHOW_ALL_INVIS; } |
659           TPRIV_PROPAGATE { $$ = PRIV_PROPAGATE; } |
660           TPRIV_UNLIMIT_QUERY { $$ = PRIV_UNLIMIT_QUERY; } |
661           TPRIV_DISPLAY { $$ = PRIV_DISPLAY; } |
662           TPRIV_SEE_OPERS { $$ = PRIV_SEE_OPERS; } |
663           TPRIV_WIDE_GLINE { $$ = PRIV_WIDE_GLINE; } |
664           TPRIV_LIST_CHAN { $$ = PRIV_LIST_CHAN; } |
665           LOCAL { $$ = PRIV_PROPAGATE; invert = 1; } |
666           TPRIV_FORCE_OPMODE { $$ = PRIV_FORCE_OPMODE; } |
667           TPRIV_FORCE_LOCAL_OPMODE { $$ = PRIV_FORCE_LOCAL_OPMODE; } |
668           TPRIV_APASS_OPMODE { $$ = PRIV_APASS_OPMODE; } ;
669
670 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
671
672 /* not a recursive definition because some pedant will just come along
673  * and whine that the parser accepts "ipv4 ipv4 ipv4 ipv4"
674  */
675 address_family:
676                { $$ = 0; }
677     | TOK_IPV4 { $$ = USE_IPV4; }
678     | TOK_IPV6 { $$ = USE_IPV6; }
679     | TOK_IPV4 TOK_IPV6 { $$ = USE_IPV4 | USE_IPV6; }
680     | TOK_IPV6 TOK_IPV4 { $$ = USE_IPV6 | USE_IPV4; }
681     ;
682
683 /* The port block... */
684 portblock: PORT '{' portitems '}' ';'
685 {
686   if (!FlagHas(&listen_flags, LISTEN_IPV4)
687       && !FlagHas(&listen_flags, LISTEN_IPV6))
688   {
689     FlagSet(&listen_flags, LISTEN_IPV4);
690     FlagSet(&listen_flags, LISTEN_IPV6);
691   }
692   if (port > 0 && port <= 0xFFFF)
693     add_listener(port, host, pass, &listen_flags);
694   else
695     parse_error("Port %d is out of range", port);
696   MyFree(host);
697   MyFree(pass);
698   memset(&listen_flags, 0, sizeof(listen_flags));
699   host = pass = NULL;
700   port = 0;
701 };
702 portitems: portitem portitems | portitem;
703 portitem: portnumber | portvhost | portmask | portserver | porthidden;
704 portnumber: PORT '=' address_family NUMBER ';'
705 {
706   int families = $3;
707   if (families & USE_IPV4)
708     FlagSet(&listen_flags, LISTEN_IPV4);
709   else if (families & USE_IPV6)
710     FlagSet(&listen_flags, LISTEN_IPV6);
711   port = $4;
712 };
713
714 portvhost: VHOST '=' address_family QSTRING ';'
715 {
716   int families = $3;
717   if (families & USE_IPV4)
718     FlagSet(&listen_flags, LISTEN_IPV4);
719   else if (families & USE_IPV6)
720     FlagSet(&listen_flags, LISTEN_IPV6);
721   MyFree(host);
722   host = $4;
723 };
724
725 portmask: MASK '=' QSTRING ';'
726 {
727   MyFree(pass);
728   pass = $3;
729 };
730
731 portserver: SERVER '=' YES ';'
732 {
733   FlagSet(&listen_flags, LISTEN_SERVER);
734 } | SERVER '=' NO ';'
735 {
736   FlagClr(&listen_flags, LISTEN_SERVER);
737 };
738
739 porthidden: HIDDEN '=' YES ';'
740 {
741   FlagSet(&listen_flags, LISTEN_HIDDEN);
742 } | HIDDEN '=' NO ';'
743 {
744   FlagClr(&listen_flags, LISTEN_HIDDEN);
745 };
746
747 clientblock: CLIENT
748 {
749   maxlinks = 65535;
750   port = 0;
751 }
752 '{' clientitems '}' ';'
753 {
754   struct ConfItem *aconf = 0;
755   struct irc_in_addr addr;
756   unsigned char addrbits = 0;
757
758   if (!c_class)
759     parse_error("Invalid or missing class in Client block");
760   else if (pass && strlen(pass) > PASSWDLEN)
761     parse_error("Password too long in connect block");
762   else if (ip && !ipmask_parse(ip, &addr, &addrbits))
763     parse_error("Invalid IP address %s in Client block", ip);
764   else {
765     aconf = make_conf(CONF_CLIENT);
766     aconf->username = username;
767     aconf->host = host;
768     if (ip)
769       memcpy(&aconf->address.addr, &addr, sizeof(aconf->address.addr));
770     else
771       memset(&aconf->address.addr, 0, sizeof(aconf->address.addr));
772     aconf->address.port = port;
773     aconf->addrbits = addrbits;
774     aconf->name = ip;
775     aconf->conn_class = c_class;
776     aconf->maximum = maxlinks;
777     aconf->passwd = pass;
778   }
779   if (!aconf) {
780     MyFree(username);
781     MyFree(host);
782     MyFree(ip);
783     MyFree(pass);
784   }
785   host = NULL;
786   username = NULL;
787   c_class = NULL;
788   ip = NULL;
789   pass = NULL;
790   port = 0;
791 };
792 clientitems: clientitem clientitems | clientitem;
793 clientitem: clienthost | clientip | clientusername | clientclass | clientpass | clientmaxlinks | clientport;
794 clienthost: HOST '=' QSTRING ';'
795 {
796   char *sep = strchr($3, '@');
797   MyFree(host);
798   if (sep) {
799     *sep++ = '\0';
800     MyFree(username);
801     DupString(host, sep);
802     username = $3;
803   } else {
804     host = $3;
805   }
806 };
807 clientip: IP '=' QSTRING ';'
808 {
809   char *sep;
810   sep = strchr($3, '@');
811   MyFree(ip);
812   if (sep) {
813     *sep++ = '\0';
814     MyFree(username);
815     DupString(ip, sep);
816     username = $3;
817   } else {
818     ip = $3;
819   }
820 };
821 clientusername: USERNAME '=' QSTRING ';'
822 {
823   MyFree(username);
824   username = $3;
825 };
826 clientclass: CLASS '=' QSTRING ';'
827 {
828   c_class = find_class($3);
829   if (!c_class)
830     parse_error("No such connection class '%s' for Client block", $3);
831   MyFree($3);
832 };
833 clientpass: PASS '=' QSTRING ';'
834 {
835   MyFree(pass);
836   pass = $3;
837 };
838 clientmaxlinks: MAXLINKS '=' expr ';'
839 {
840   maxlinks = $3;
841 };
842 clientport: PORT '=' expr ';'
843 {
844   port = $3;
845 };
846
847 killblock: KILL
848 {
849   dconf = (struct DenyConf*) MyCalloc(1, sizeof(*dconf));
850 } '{' killitems '}' ';'
851 {
852   if (dconf->usermask || dconf->hostmask ||dconf->realmask) {
853     dconf->next = denyConfList;
854     denyConfList = dconf;
855   }
856   else
857   {
858     MyFree(dconf->usermask);
859     MyFree(dconf->hostmask);
860     MyFree(dconf->realmask);
861     MyFree(dconf->message);
862     MyFree(dconf);
863     parse_error("Kill block must match on at least one of username, host or realname");
864   }
865   dconf = NULL;
866 };
867 killitems: killitem killitems | killitem;
868 killitem: killuhost | killreal | killusername | killreasonfile | killreason;
869 killuhost: HOST '=' QSTRING ';'
870 {
871   char *h;
872   MyFree(dconf->hostmask);
873   MyFree(dconf->usermask);
874   if ((h = strchr($3, '@')) == NULL)
875   {
876     DupString(dconf->usermask, "*");
877     dconf->hostmask = $3;
878   }
879   else
880   {
881     *h++ = '\0';
882     DupString(dconf->hostmask, h);
883     dconf->usermask = $3;
884   }
885   ipmask_parse(dconf->hostmask, &dconf->address, &dconf->bits);
886 };
887
888 killusername: USERNAME '=' QSTRING ';'
889 {
890   MyFree(dconf->usermask);
891   dconf->usermask = $3;
892 };
893
894 killreal: REAL '=' QSTRING ';'
895 {
896  MyFree(dconf->realmask);
897  dconf->realmask = $3;
898 };
899
900 killreason: REASON '=' QSTRING ';'
901 {
902  dconf->flags &= ~DENY_FLAGS_FILE;
903  MyFree(dconf->message);
904  dconf->message = $3;
905 };
906
907 killreasonfile: TFILE '=' QSTRING ';'
908 {
909  dconf->flags |= DENY_FLAGS_FILE;
910  MyFree(dconf->message);
911  dconf->message = $3;
912 };
913
914 cruleblock: CRULE
915 {
916   tconn = CRULE_AUTO;
917 } '{' cruleitems '}' ';'
918 {
919   struct CRuleNode *node = NULL;
920   if (host == NULL)
921     parse_error("Missing host in crule block");
922   else if (pass == NULL)
923     parse_error("Missing rule in crule block");
924   else if ((node = crule_parse(pass)) == NULL)
925     parse_error("Invalid rule '%s' in crule block", pass);
926   else
927   {
928     struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p));
929     p->hostmask = host;
930     p->rule = pass;
931     p->type = tconn;
932     p->node = node;
933     p->next = cruleConfList;
934     cruleConfList = p;
935   }
936   if (!node)
937   {
938     MyFree(host);
939     MyFree(pass);
940   }
941   host = pass = NULL;
942   tconn = 0;
943 };
944
945 cruleitems: cruleitem cruleitems | cruleitem;
946 cruleitem: cruleserver | crulerule | cruleall;
947
948 cruleserver: SERVER '=' QSTRING ';'
949 {
950   MyFree(host);
951   collapse($3);
952   host = $3;
953 };
954
955 crulerule: RULE '=' QSTRING ';'
956 {
957  MyFree(pass);
958  pass = $3;
959 };
960
961 cruleall: ALL '=' YES ';'
962 {
963  tconn = CRULE_ALL;
964 } | ALL '=' NO ';'
965 {
966  tconn = CRULE_AUTO;
967 };
968
969 motdblock: MOTD '{' motditems '}' ';'
970 {
971   if (host != NULL && pass != NULL)
972     motd_add(host, pass);
973   MyFree(host);
974   MyFree(pass);
975   host = pass = NULL;
976 };
977
978 motditems: motditem motditems | motditem;
979 motditem: motdhost | motdfile;
980 motdhost: HOST '=' QSTRING ';'
981 {
982   host = $3;
983 };
984
985 motdfile: TFILE '=' QSTRING ';'
986 {
987   pass = $3;
988 };
989
990 featuresblock: FEATURES '{' featureitems '}' ';';
991 featureitems: featureitems featureitem | featureitem;
992
993 featureitem: QSTRING
994 {
995   stringlist[0] = $1;
996   stringno = 1;
997 } '=' stringlist ';' {
998   unsigned int ii;
999   feature_set(NULL, (const char * const *)stringlist, stringno);
1000   for (ii = 0; ii < stringno; ++ii)
1001     MyFree(stringlist[ii]);
1002 };
1003
1004 stringlist: stringlist extrastring | extrastring;
1005 extrastring: QSTRING
1006 {
1007   if (stringno < MAX_STRINGS)
1008     stringlist[stringno++] = $1;
1009   else
1010     MyFree($1);
1011 };
1012
1013 quarantineblock: QUARANTINE '{' quarantineitems '}' ';';
1014 quarantineitems: quarantineitems quarantineitem | quarantineitem;
1015 quarantineitem: QSTRING '=' QSTRING ';'
1016 {
1017   struct qline *qconf = MyCalloc(1, sizeof(*qconf));
1018   qconf->chname = $1;
1019   qconf->reason = $3;
1020   qconf->next = GlobalQuarantineList;
1021   GlobalQuarantineList = qconf;
1022 };
1023
1024 pseudoblock: PSEUDO QSTRING '{'
1025 {
1026   smap = MyCalloc(1, sizeof(struct s_map));
1027   smap->command = $2;
1028 }
1029 pseudoitems '}' ';'
1030 {
1031   int valid = 0;
1032
1033   if (!smap->name)
1034     parse_error("Missing name in pseudo %s block", smap->command);
1035   else if (!smap->services)
1036     parse_error("Missing nick in pseudo %s block", smap->command);
1037   else
1038     valid = 1;
1039   if (valid && register_mapping(smap))
1040   {
1041     smap->next = GlobalServiceMapList;
1042     GlobalServiceMapList = smap;
1043   }
1044   else
1045   {
1046     free_mapping(smap);
1047   }
1048   smap = NULL;
1049 };
1050
1051 pseudoitems: pseudoitem pseudoitems | pseudoitem;
1052 pseudoitem: pseudoname | pseudoprepend | pseudonick | pseudoflags;
1053 pseudoname: NAME '=' QSTRING ';'
1054 {
1055   MyFree(smap->name);
1056   smap->name = $3;
1057 };
1058 pseudoprepend: PREPEND '=' QSTRING ';'
1059 {
1060   MyFree(smap->prepend);
1061   smap->prepend = $3;
1062 };
1063 pseudonick: NICK '=' QSTRING ';'
1064 {
1065   char *sep = strchr($3, '@');
1066
1067   if (sep != NULL) {
1068     size_t slen = strlen($3);
1069     struct nick_host *nh = MyMalloc(sizeof(*nh) + slen);
1070     memcpy(nh->nick, $3, slen + 1);
1071     nh->nicklen = sep - $3;
1072     nh->next = smap->services;
1073     smap->services = nh;
1074   }
1075   MyFree($3);
1076 };
1077 pseudoflags: FAST ';'
1078 {
1079   smap->flags |= SMAP_FAST;
1080 };
1081
1082 iauthblock: IAUTH '{' iauthitems '}' ';'
1083 {
1084   auth_spawn(stringno, stringlist);
1085   while (stringno > 0)
1086     MyFree(stringlist[--stringno]);
1087 };
1088
1089 iauthitems: iauthitem iauthitems | iauthitem;
1090 iauthitem: iauthprogram;
1091 iauthprogram: PROGRAM '='
1092 {
1093   while (stringno > 0)
1094     MyFree(stringlist[--stringno]);
1095 } stringlist ';';