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