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