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