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