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