Allow the resolver source address to be specified in the configuration.
[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_auth.h"
36 #include "ircd_chattr.h"
37 #include "ircd_log.h"
38 #include "ircd_reply.h"
39 #include "ircd_snprintf.h"
40 #include "ircd_string.h"
41 #include "list.h"
42 #include "listener.h"
43 #include "match.h"
44 #include "motd.h"
45 #include "numeric.h"
46 #include "numnicks.h"
47 #include "opercmds.h"
48 #include "parse.h"
49 #include "res.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 #define MAX_STRINGS 80 /* Maximum number of feature params. */
62   extern struct LocalConf   localConf;
63   extern struct DenyConf*   denyConfList;
64   extern struct CRuleConf*  cruleConfList;
65   extern struct ServerConf* serverConfList;
66   extern struct s_map*      GlobalServiceMapList;
67   extern struct qline*      GlobalQuarantineList;
68   extern struct irc_sockaddr ResolverAddr;
69
70   int yylex(void);
71   /* Now all the globals we need :/... */
72   int tping, tconn, maxlinks, sendq, port, invert, stringno;
73   char *name, *pass, *host, *ip, *username, *origin, *hub_limit;
74   char *stringlist[MAX_STRINGS];
75   struct ConnectionClass *c_class;
76   struct DenyConf *dconf;
77   struct ServerConf *sconf;
78   struct qline *qconf = NULL;
79   struct s_map *smap;
80   struct Privs privs;
81   struct Privs privs_dirty;
82
83 static void parse_error(char *pattern,...) {
84   static char error_buffer[1024];
85   va_list vl;
86   va_start(vl,pattern);
87   ircd_vsnprintf(NULL, error_buffer, sizeof(error_buffer), pattern, vl);
88   va_end(vl);
89   yyerror(error_buffer);
90 }
91
92 %}
93
94 %token <text> QSTRING
95 %token <num> NUMBER
96 %token <text> FNAME
97
98 %token GENERAL
99 %token ADMIN
100 %token LOCATION
101 %token CONTACT
102 %token CONNECT
103 %token CLASS
104 %token CHANNEL
105 %token PINGFREQ
106 %token CONNECTFREQ
107 %token MAXLINKS
108 %token MAXHOPS
109 %token SENDQ
110 %token NAME
111 %token HOST
112 %token IP
113 %token USERNAME
114 %token PASS
115 %token LOCAL
116 %token SECONDS
117 %token MINUTES
118 %token HOURS
119 %token DAYS
120 %token WEEKS
121 %token MONTHS
122 %token YEARS
123 %token DECADES
124 %token BYTES
125 %token KBYTES
126 %token MBYTES
127 %token GBYTES
128 %token TBYTES
129 %token SERVER
130 %token PORT
131 %token MASK
132 %token HUB
133 %token LEAF
134 %token UWORLD
135 %token YES
136 %token NO
137 %token OPER
138 %token VHOST
139 %token RESOLVER
140 %token HIDDEN
141 %token MOTD
142 %token JUPE
143 %token NICK
144 %token NUMERIC
145 %token DESCRIPTION
146 %token CLIENT
147 %token KILL
148 %token CRULE
149 %token REAL
150 %token REASON
151 %token TFILE
152 %token RULE
153 %token ALL
154 %token FEATURES
155 %token QUARANTINE
156 %token PSEUDO
157 %token PREPEND
158 %token USERMODE
159 %token IAUTH
160 %token TIMEOUT
161 /* and now a lot of priviledges... */
162 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
163 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
164 %token TPRIV_GLINE TPRIV_LOCAL_GLINE TPRIV_LOCAL_JUPE TPRIV_LOCAL_BADCHAN
165 %token TPRIV_LOCAL_OPMODE TPRIV_OPMODE TPRIV_SET TPRIV_WHOX TPRIV_BADCHAN
166 %token TPRIV_SEE_CHAN TPRIV_SHOW_INVIS TPRIV_SHOW_ALL_INVIS TPRIV_PROPAGATE
167 %token TPRIV_UNLIMIT_QUERY TPRIV_DISPLAY TPRIV_SEE_OPERS TPRIV_WIDE_GLINE
168 %token TPRIV_FORCE_OPMODE TPRIV_FORCE_LOCAL_OPMODE
169 /* and some types... */
170 %type <num> sizespec
171 %type <num> timespec timefactor factoredtimes factoredtime
172 %type <num> expr yesorno privtype
173 %left '+' '-'
174 %left '*' '/'
175
176 %union{
177  char *text;
178  int num;
179 }
180
181 %%
182 /* Blocks in the config file... */
183 blocks: blocks block | block;
184 block: adminblock | generalblock | classblock | connectblock |
185        uworldblock | operblock | portblock | jupeblock | clientblock |
186        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
187        pseudoblock | iauthblock | error;
188
189 /* The timespec, sizespec and expr was ripped straight from
190  * ircd-hybrid-7. */
191 timespec: expr | factoredtimes;
192
193 factoredtimes: factoredtimes factoredtime
194 {
195   $$ = $1 + $2;
196 } | factoredtime;
197
198 factoredtime: expr timefactor
199 {
200   $$ = $1 * $2;
201 };
202
203 timefactor: SECONDS { $$ = 1; }
204 | MINUTES { $$ = 60; }
205 | HOURS { $$ = 60 * 60; }
206 | DAYS { $$ = 60 * 60 * 24; }
207 | WEEKS { $$ = 60 * 60 * 24 * 7; }
208 | MONTHS { $$ = 60 * 60 * 24 * 7 * 4; }
209 | YEARS { $$ = 60 * 60 * 24 * 365; }
210 | DECADES { $$ = 60 * 60 * 24 * 365 * 10; };
211
212
213 sizespec:       expr    {
214                         $$ = $1;
215                 }
216                 | expr BYTES  { 
217                         $$ = $1;
218                 }
219                 | expr KBYTES {
220                         $$ = $1 * 1024;
221                 }
222                 | expr MBYTES {
223                         $$ = $1 * 1024 * 1024;
224                 }
225                 | expr GBYTES {
226                         $$ = $1 * 1024 * 1024 * 1024;
227                 }
228                 | expr TBYTES {
229                         $$ = $1 * 1024 * 1024 * 1024;
230                 }
231                 ;
232
233 /* this is an arithmatic expression */
234 expr: NUMBER
235                 { 
236                         $$ = $1;
237                 }
238                 | expr '+' expr { 
239                         $$ = $1 + $3;
240                 }
241                 | expr '-' expr { 
242                         $$ = $1 - $3;
243                 }
244                 | expr '*' expr { 
245                         $$ = $1 * $3;
246                 }
247                 | expr '/' expr { 
248                         $$ = $1 / $3;
249                 }
250 /* leave this out until we find why it makes BSD yacc dump core -larne
251                 | '-' expr  %prec NEG
252                 = {
253                         $$ = -$2;
254                 } */
255                 | '(' expr ')' {
256                         $$ = $2;
257                 }
258                 ;
259
260 jupeblock: JUPE '{' jupeitems '}' ';' ;
261 jupeitems: jupeitem jupeitems | jupeitem;
262 jupeitem: jupenick | error;
263 jupenick: NICK '=' QSTRING
264 {
265   addNickJupes($3);
266 } ';';
267
268 generalblock: GENERAL '{' generalitems '}'
269 {
270   if (localConf.name == NULL)
271     parse_error("Your General block must contain a name.");
272   if (localConf.numeric == 0)
273     parse_error("Your General block must contain a numeric (between 1 and 4095).");
274 } ';' ;
275 generalitems: generalitem generalitems | generalitem;
276 generalitem: generalnumeric | generalname | generalvhost | generalresolver | generaldesc | error;
277 generalnumeric: NUMERIC '=' NUMBER ';'
278 {
279   if (localConf.numeric == 0)
280     localConf.numeric = $3;
281   else if (localConf.numeric != $3)
282     parse_error("Redefinition of server numeric %i (%i)", $3,
283                 localConf.numeric);
284 };
285
286 generalname: NAME '=' QSTRING ';'
287 {
288   if (localConf.name == NULL)
289     DupString(localConf.name, $3);
290   else if (strcmp(localConf.name, $3))
291     parse_error("Redefinition of server name %s (%s)", $3,
292                 localConf.name);
293 };
294
295 generaldesc: DESCRIPTION '=' QSTRING ';'
296 {
297   MyFree(localConf.description);
298   DupString(localConf.description, $3);
299   ircd_strncpy(cli_info(&me), $3, REALLEN);
300 };
301
302 generalvhost: VHOST '=' QSTRING ';'
303 {
304   ircd_aton(&VirtualHost.addr, $3);
305 };
306
307 generalresolver: RESOLVER '=' QSTRING ';'
308 {
309   ircd_aton(&ResolverAddr.addr, $3);
310 };
311
312 adminblock: ADMIN '{' adminitems '}'
313 {
314   if (localConf.location1 == NULL)
315     DupString(localConf.location1, "");
316   if (localConf.location2 == NULL)
317     DupString(localConf.location2, "");
318   if (localConf.contact == NULL)
319     DupString(localConf.contact, "");
320 } ';';
321 adminitems: adminitems adminitem | adminitem;
322 adminitem: adminlocation | admincontact | error;
323 adminlocation: LOCATION '=' QSTRING ';'
324 {
325  if (localConf.location1 == NULL)
326   DupString(localConf.location1, $3);
327  else if (localConf.location2 == NULL)
328   DupString(localConf.location2, $3);
329  /* Otherwise just drop it. -A1kmm */
330 };
331 admincontact: CONTACT '=' QSTRING ';'
332 {
333  if (localConf.contact != NULL)
334    MyFree(localConf.contact);
335  DupString(localConf.contact, $3);
336 };
337
338 classblock: CLASS {
339   tping = 90;
340 } '{' classitems '}'
341 {
342   if (name != NULL)
343   {
344     struct ConnectionClass *c_class;
345     add_class(name, tping, tconn, maxlinks, sendq);
346     c_class = find_class(name);
347     c_class->default_umode = pass;
348     memcpy(&c_class->privs, &privs, sizeof(c_class->privs));
349     memcpy(&c_class->privs_dirty, &privs_dirty, sizeof(c_class->privs_dirty));
350   }
351   else {
352    parse_error("Missing name in class block");
353   }
354   name = NULL;
355   pass = NULL;
356   tconn = 0;
357   maxlinks = 0;
358   sendq = 0;
359   memset(&privs, 0, sizeof(privs));
360   memset(&privs_dirty, 0, sizeof(privs_dirty));
361 } ';';
362 classitems: classitem classitems | classitem;
363 classitem: classname | classpingfreq | classconnfreq | classmaxlinks |
364            classsendq | classusermode | priv | error;
365 classname: NAME '=' QSTRING ';'
366 {
367   MyFree(name);
368   DupString(name, $3);
369 };
370 classpingfreq: PINGFREQ '=' timespec ';'
371 {
372   tping = $3;
373 };
374 classconnfreq: CONNECTFREQ '=' timespec ';'
375 {
376   tconn = $3;
377 };
378 classmaxlinks: MAXLINKS '=' expr ';'
379 {
380   maxlinks = $3;
381 };
382 classsendq: SENDQ '=' sizespec ';'
383 {
384   sendq = $3;
385 };
386 classusermode: USERMODE '=' QSTRING ';'
387 {
388   MyFree(pass);
389   DupString(pass, $3);
390 };
391
392 connectblock: CONNECT
393 {
394  maxlinks = 65535;
395 } '{' connectitems '}'
396 {
397  if (name != NULL && pass != NULL && host != NULL && c_class != NULL
398      && !strchr(host, '*') && !strchr(host, '?'))
399  {
400    struct ConfItem *aconf = make_conf(CONF_SERVER);
401    aconf->name = name;
402    aconf->origin_name = origin;
403    aconf->passwd = pass;
404    aconf->conn_class = c_class;
405    aconf->address.port = port;
406    aconf->host = host;
407    aconf->maximum = maxlinks;
408    aconf->hub_limit = hub_limit;
409    lookup_confhost(aconf);
410  }
411  else
412  {
413    MyFree(name);
414    MyFree(pass);
415    MyFree(host);
416    MyFree(origin);
417    MyFree(hub_limit);
418    parse_error("Bad connect block");
419  }
420  name = pass = host = origin = hub_limit = NULL;
421  c_class = NULL;
422  port = 0;
423 }';';
424 connectitems: connectitem connectitems | connectitem;
425 connectitem: connectname | connectpass | connectclass | connecthost
426               | connectport | connectvhost | connectleaf | connecthub
427               | connecthublimit | connectmaxhops | error;
428 connectname: NAME '=' QSTRING ';'
429 {
430  MyFree(name);
431  DupString(name, $3);
432 };
433 connectpass: PASS '=' QSTRING ';'
434 {
435  MyFree(pass);
436  DupString(pass, $3);
437 };
438 connectclass: CLASS '=' QSTRING ';'
439 {
440  c_class = find_class($3);
441 };
442 connecthost: HOST '=' QSTRING ';'
443 {
444  MyFree(host);
445  DupString(host, $3);
446 };
447 connectport: PORT '=' NUMBER ';'
448 {
449  port = $3;
450 };
451 connectvhost: VHOST '=' QSTRING ';'
452 {
453  MyFree(origin);
454  DupString(origin, $3);
455 };
456 connectleaf: LEAF ';'
457 {
458  maxlinks = 0;
459 };
460 connecthub: HUB ';'
461 {
462  MyFree(hub_limit);
463  DupString(hub_limit, "*");
464 };
465 connecthublimit: HUB '=' QSTRING ';'
466 {
467  MyFree(hub_limit);
468  DupString(hub_limit, $3);
469 };
470 connectmaxhops: MAXHOPS '=' expr ';'
471 {
472   maxlinks = $3;
473 }
474
475 uworldblock: UWORLD '{' uworlditems '}' ';'
476 {
477  if (name)
478  {
479   struct ConfItem *aconf = make_conf(CONF_UWORLD);
480   aconf->name = name;
481  }
482  else
483  {
484   MyFree(name);
485   parse_error("Bad UWorld block");
486  }
487  name = NULL;
488 };
489 uworlditems: uworlditem uworlditems | uworlditem;
490 uworlditem: uworldname | error;
491 uworldname: NAME '=' QSTRING ';'
492 {
493  MyFree(name);
494  DupString(name, $3);
495 };
496
497 operblock: OPER '{' operitems '}' ';'
498 {
499   if (name && pass && host && c_class)
500   {
501     struct ConfItem *aconf = make_conf(CONF_OPERATOR);
502     aconf->name = name;
503     aconf->passwd = pass;
504     conf_parse_userhost(aconf, host);
505     aconf->conn_class = c_class;
506     memcpy(&aconf->privs, &privs, sizeof(aconf->privs));
507     memcpy(&aconf->privs_dirty, &privs_dirty, sizeof(aconf->privs_dirty));
508     if (!FlagHas(&privs_dirty, PRIV_PROPAGATE)
509         && !FlagHas(&c_class->privs_dirty, PRIV_PROPAGATE))
510       parse_error("Operator block for %s and class %s have no LOCAL setting", name, c_class->cc_name);
511   }
512   else
513   {
514     log_write(LS_CONFIG, L_ERROR, 0, "operator blocks need a name, password, and host.");
515     MyFree(name);
516     MyFree(pass);
517     MyFree(host);
518   }
519   name = pass = host = NULL;
520   c_class = NULL;
521   memset(&privs, 0, sizeof(privs));
522   memset(&privs_dirty, 0, sizeof(privs_dirty));
523 };
524 operitems: operitem | operitems operitem;
525 operitem: opername | operpass | operhost | operclass | priv | error;
526 opername: NAME '=' QSTRING ';'
527 {
528   MyFree(name);
529   DupString(name, $3);
530 };
531 operpass: PASS '=' QSTRING ';'
532 {
533   MyFree(pass);
534   DupString(pass, $3);
535 };
536 operhost: HOST '=' QSTRING ';'
537 {
538  MyFree(host);
539  if (!strchr($3, '@'))
540  {
541    int uh_len;
542    char *b = (char*) MyMalloc((uh_len = strlen($3)+3));
543    ircd_snprintf(0, b, uh_len, "*@%s", $3);
544    host = b;
545  }
546  else
547    DupString(host, $3);
548 };
549 operclass: CLASS '=' QSTRING ';'
550 {
551  c_class = find_class($3);
552 };
553
554 priv: privtype '=' yesorno ';'
555 {
556   FlagSet(&privs_dirty, $1);
557   if (($3 == 1) ^ invert)
558     FlagSet(&privs, $1);
559   else
560     FlagClr(&privs, $1);
561   invert = 0;
562 };
563
564 privtype: TPRIV_CHAN_LIMIT { $$ = PRIV_CHAN_LIMIT; } |
565           TPRIV_MODE_LCHAN { $$ = PRIV_MODE_LCHAN; } |
566           TPRIV_DEOP_LCHAN { $$ = PRIV_DEOP_LCHAN; } |
567           TPRIV_WALK_LCHAN { $$ = PRIV_WALK_LCHAN; } |
568           KILL { $$ = PRIV_KILL; } |
569           TPRIV_LOCAL_KILL { $$ = PRIV_LOCAL_KILL; } |
570           TPRIV_REHASH { $$ = PRIV_REHASH; } |
571           TPRIV_RESTART { $$ = PRIV_RESTART; } |
572           TPRIV_DIE { $$ = PRIV_DIE; } |
573           TPRIV_GLINE { $$ = PRIV_GLINE; } |
574           TPRIV_LOCAL_GLINE { $$ = PRIV_LOCAL_GLINE; } |
575           JUPE { $$ = PRIV_JUPE; } |
576           TPRIV_LOCAL_JUPE { $$ = PRIV_LOCAL_JUPE; } |
577           TPRIV_LOCAL_OPMODE { $$ = PRIV_LOCAL_OPMODE; } |
578           TPRIV_OPMODE { $$ = PRIV_OPMODE; }|
579           TPRIV_SET { $$ = PRIV_SET; } |
580           TPRIV_WHOX { $$ = PRIV_WHOX; } |
581           TPRIV_BADCHAN { $$ = PRIV_BADCHAN; } |
582           TPRIV_LOCAL_BADCHAN { $$ = TPRIV_LOCAL_BADCHAN; } |
583           TPRIV_SEE_CHAN { $$ = PRIV_SEE_CHAN; } |
584           TPRIV_SHOW_INVIS { $$ = PRIV_SHOW_INVIS; } |
585           TPRIV_SHOW_ALL_INVIS { $$ = PRIV_SHOW_ALL_INVIS; } |
586           TPRIV_PROPAGATE { $$ = PRIV_PROPAGATE; } |
587           TPRIV_UNLIMIT_QUERY { $$ = PRIV_UNLIMIT_QUERY; } |
588           TPRIV_DISPLAY { $$ = PRIV_DISPLAY; } |
589           TPRIV_SEE_OPERS { $$ = PRIV_SEE_OPERS; } |
590           TPRIV_WIDE_GLINE { $$ = PRIV_WIDE_GLINE; } |
591           LOCAL { $$ = PRIV_PROPAGATE; invert = 1; } |
592           TPRIV_FORCE_OPMODE { $$ = PRIV_FORCE_OPMODE; } |
593           TPRIV_FORCE_LOCAL_OPMODE { $$ = PRIV_FORCE_LOCAL_OPMODE; };
594
595 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
596
597 /* The port block... */
598 portblock: PORT '{' portitems '}' ';'
599 {
600   if (port > 0 && port <= 0xFFFF)
601   {
602     add_listener(port, host, pass, tconn, tping);
603   }
604   else
605   {
606     parse_error("Bad port block");
607   }
608   MyFree(host);
609   MyFree(pass);
610   host = pass = NULL;
611   port = tconn = tping = 0;
612 };
613 portitems: portitem portitems | portitem;
614 portitem: portnumber | portvhost | portmask | portserver | porthidden | error;
615 portnumber: PORT '=' NUMBER ';'
616 {
617   port = $3;
618 };
619
620 portvhost: VHOST '=' QSTRING ';'
621 {
622   MyFree(host);
623   DupString(host, $3);
624 };
625
626 portmask: MASK '=' QSTRING ';'
627 {
628   MyFree(pass);
629   DupString(pass, $3);
630 };
631
632 portserver: SERVER '=' YES ';'
633 {
634   tconn = -1;
635 } | SERVER '=' NO ';'
636 {
637   tconn = 0;
638 };
639
640 porthidden: HIDDEN '=' YES ';'
641 {
642   tping = -1;
643 } | HIDDEN '=' NO ';'
644 {
645   tping = 0;
646 };
647
648 clientblock: CLIENT
649 {
650   maxlinks = 65535;
651 }
652 '{' clientitems '}' ';'
653 {
654   struct ConfItem *aconf = make_conf(CONF_CLIENT);
655   unsigned char addrbits;
656   aconf->username = username;
657   aconf->host = host;
658   if (ip && ipmask_parse(ip, &aconf->address.addr, &addrbits))
659     aconf->addrbits = addrbits;
660   else
661     aconf->addrbits = -1;
662   aconf->conn_class = c_class ? c_class : find_class("default");
663   aconf->maximum = maxlinks;
664   host = NULL;
665   username = NULL;
666   c_class = NULL;
667   MyFree(ip);
668 };
669 clientitems: clientitem clientitems | clientitem;
670 clientitem: clienthost | clientip | clientusername | clientclass | clientpass | clientmaxlinks | error;
671 clienthost: HOST '=' QSTRING ';'
672 {
673   MyFree(host);
674   DupString(host, $3);
675 };
676 clientip: IP '=' QSTRING ';'
677 {
678   MyFree(ip);
679   DupString(ip, $3);
680 }
681 clientusername: USERNAME '=' QSTRING ';'
682 {
683   MyFree(username);
684   DupString(username, $3);
685 }
686 clientclass: CLASS '=' QSTRING ';'
687 {
688   c_class = find_class($3);
689 };
690 clientpass: PASS '=' QSTRING ';'
691 {
692   MyFree(pass);
693   DupString(pass, $3);
694 };
695 clientmaxlinks: MAXLINKS '=' expr ';'
696 {
697   maxlinks = $3;
698 };
699
700 killblock: KILL
701 {
702   dconf = (struct DenyConf*) MyCalloc(1, sizeof(*dconf));
703 } '{' killitems '}'
704 {
705   if (dconf->hostmask != NULL)
706   {
707     if (dconf->usermask == NULL)
708       DupString(dconf->usermask, "*");
709     dconf->next = denyConfList;
710     denyConfList = dconf;
711   }
712   else
713   {
714     MyFree(dconf->hostmask);
715     MyFree(dconf->message);
716     MyFree(dconf);
717     parse_error("Bad kill block");
718   }
719   dconf = NULL;
720 } ';';
721 killitems: killitem killitems | killitem;
722 killitem: killuhost | killreal | killreasonfile | killreason | error;
723 killuhost: HOST '=' QSTRING ';'
724 {
725   char *u, *h;
726   dconf->flags &= ~DENY_FLAGS_REALNAME;
727   MyFree(dconf->hostmask);
728   MyFree(dconf->usermask);
729   if ((h = strchr($3, '@')) == NULL)
730   {
731     u = "*";
732     h = $3;
733   }
734   else
735   {
736     u = $3;
737     h++;
738   }
739   DupString(dconf->hostmask, h);
740   DupString(dconf->usermask, u);
741   ipmask_parse(dconf->hostmask, &dconf->address, &dconf->bits);
742 };
743
744 killreal: REAL '=' QSTRING ';'
745 {
746  dconf->flags &= ~DENY_FLAGS_IP;
747  dconf->flags |= DENY_FLAGS_REALNAME;
748  MyFree(dconf->hostmask);
749  /* Leave usermask so you can specify user and real... */
750  DupString(dconf->hostmask, $3);
751 };
752
753 killreason: REASON '=' QSTRING ';'
754 {
755  dconf->flags &= DENY_FLAGS_FILE;
756  MyFree(dconf->message);
757  DupString(dconf->message, $3);
758 };
759
760 killreasonfile: TFILE '=' QSTRING ';'
761 {
762  dconf->flags |= DENY_FLAGS_FILE;
763  MyFree(dconf->message);
764  DupString(dconf->message, $3);
765 };
766
767 cruleblock: CRULE
768 {
769   tconn = CRULE_AUTO;
770 } '{' cruleitems '}' ';'
771 {
772   struct CRuleNode *node;
773   if (host != NULL && pass != NULL && (node=crule_parse(pass)) != NULL)
774   {
775     struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p));
776     p->hostmask = host;
777     p->rule = pass;
778     p->type = tconn;
779     p->node = node;
780     p->next = cruleConfList;
781     cruleConfList = p;
782   }
783   else
784   {
785     MyFree(host);
786     MyFree(pass);
787     parse_error("Bad CRule block");
788   }
789   host = pass = NULL;
790 };
791
792 cruleitems: cruleitem cruleitems | cruleitem;
793 cruleitem: cruleserver | crulerule | cruleall | error;
794
795 cruleserver: SERVER '=' QSTRING ';'
796 {
797   MyFree(host);
798   collapse($3);
799   DupString(host, $3);
800 };
801
802 crulerule: RULE '=' QSTRING ';'
803 {
804  MyFree(pass);
805  DupString(pass, $3);
806 };
807
808 cruleall: ALL '=' YES ';'
809 {
810  tconn = CRULE_ALL;
811 } | ALL '=' NO ';'
812 {
813  tconn = CRULE_AUTO;
814 };
815
816 motdblock: MOTD '{' motditems '}' ';'
817 {
818   if (host != NULL && pass != NULL)
819     motd_add(host, pass);
820   MyFree(host);
821   MyFree(pass);
822   host = pass = NULL;
823 };
824
825 motditems: motditem motditems | motditem;
826 motditem: motdhost | motdfile | error;
827 motdhost: HOST '=' QSTRING ';'
828 {
829   DupString(host, $3);
830 };
831
832 motdfile: TFILE '=' QSTRING ';'
833 {
834   DupString(pass, $3);
835 };
836
837 featuresblock: FEATURES '{' featureitems '}' ';';
838 featureitems: featureitems featureitem | featureitem;
839
840 featureitem: QSTRING
841 {
842   stringlist[0] = $1;
843   stringno = 1;
844 } '=' stringlist ';';
845
846 stringlist: QSTRING
847 {
848   stringlist[1] = $1;
849   stringno = 2;
850 } posextrastrings
851 {
852   feature_set(NULL, (const char * const *)stringlist, stringno);
853 };
854 posextrastrings: /* empty */ | extrastrings;
855 extrastrings: extrastrings extrastring | extrastring;
856 extrastring: QSTRING
857 {
858   if (stringno < MAX_STRINGS)
859     stringlist[stringno++] = $1;
860 };
861
862 quarantineblock: QUARANTINE '{'
863 {
864   qconf = (struct qline*) MyCalloc(1, sizeof(*qconf));
865 } quarantineitems '}' ';'
866 {
867   if (qconf->chname == NULL || qconf->reason == NULL)
868   {
869     log_write(LS_CONFIG, L_ERROR, 0, "quarantine blocks need a channel name "
870               "and a reason.");
871     return 0;
872   }
873   qconf->next = GlobalQuarantineList;
874   GlobalQuarantineList = qconf;
875   qconf = NULL;
876 };
877
878 quarantineitems: CHANNEL NAME '=' QSTRING ';'
879 {
880   DupString(qconf->chname, $4);
881 } | REASON '=' QSTRING ';'
882 {
883   DupString(qconf->reason, $3);
884 };
885
886 pseudoblock: PSEUDO QSTRING '{'
887 {
888   smap = MyCalloc(1, sizeof(struct s_map));
889   DupString(smap->command, $2);
890 }
891 pseudoitems '}' ';'
892 {
893   if (!smap->name || !smap->services)
894   {
895     log_write(LS_CONFIG, L_ERROR, 0, "pseudo commands need a service name and list of target nicks.");
896     return 0;
897   }
898   if (register_mapping(smap))
899   {
900     smap->next = GlobalServiceMapList;
901     GlobalServiceMapList = smap;
902   }
903   else
904   {
905     struct nick_host *nh, *next;
906     for (nh = smap->services; nh; nh = next)
907     {
908       next = nh->next;
909       MyFree(nh);
910     }
911     MyFree(smap->name);
912     MyFree(smap->command);
913     MyFree(smap->prepend);
914     MyFree(smap);
915   }
916   smap = NULL;
917 };
918
919 pseudoitems: pseudoitem pseudoitems | pseudoitem;
920 pseudoitem: pseudoname | pseudoprepend | pseudonick | error;
921 pseudoname: NAME '=' QSTRING ';'
922 {
923   DupString(smap->name, $3);
924 };
925 pseudoprepend: PREPEND '=' QSTRING ';'
926 {
927   DupString(smap->prepend, $3);
928 };
929 pseudonick: NICK '=' QSTRING ';'
930 {
931   char *sep = strchr($3, '@');
932
933   if (sep != NULL) {
934     size_t slen = strlen($3);
935     struct nick_host *nh = MyMalloc(sizeof(*nh) + slen);
936     memcpy(nh->nick, $3, slen + 1);
937     nh->nicklen = sep - $3;
938     nh->next = smap->services;
939     smap->services = nh;
940   }
941 };
942
943 iauthblock: IAUTH '{'
944 {
945   tconn = 60;
946   tping = 60;
947 } iauthitems '}' ';'
948 {
949   if (!host || !port) {
950     log_write(LS_CONFIG, L_ERROR, 0, "IAuth block needs a server name and port.");
951     return 0;
952   }
953   iauth_connect(host, port, pass, tconn, tping);
954   MyFree(pass);
955   MyFree(host);
956   pass = host = NULL;
957   port = 0;
958 };
959
960 iauthitems: iauthitem iauthitems | iauthitem;
961 iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout | error;
962 iauthpass: PASS '=' QSTRING ';'
963 {
964   MyFree(pass);
965   DupString(pass, $3);
966 };
967 iauthhost: HOST '=' QSTRING ';'
968 {
969   MyFree(host);
970   DupString(host, $3);
971 };
972 iauthport: PORT '=' NUMBER ';'
973 {
974   port = $3;
975 };
976 iauthconnfreq: CONNECTFREQ '=' timespec ';'
977 {
978   tconn = $3;
979 };
980 iauthtimeout: TIMEOUT '=' timespec ';'
981 {
982   tping = $3;
983 };