ef910fb138e6b8498e42fc313e838f685a42bbb1
[ircu2.10.12-pk.git] / ircd / ircd_parser.y
1 /*
2  * ircd_parser.y: A yacc/bison parser for ircd config files.
3  * This is part of ircu, an Internet Relay Chat server.
4  * The contents of this file are Copyright 2001 Diane Bruce,
5  * Andrew Miller, the ircd-hybrid team and the ircu team.
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19  *  USA.
20  * $Id$
21  */
22 %{
23
24 #include "config.h"
25 #include "s_conf.h"
26 #include "class.h"
27 #include "client.h"
28 #include "crule.h"
29 #include "ircd_features.h"
30 #include "fileio.h"
31 #include "gline.h"
32 #include "hash.h"
33 #include "ircd.h"
34 #include "ircd_alloc.h"
35 #include "ircd_auth.h"
36 #include "ircd_chattr.h"
37 #include "ircd_log.h"
38 #include "ircd_reply.h"
39 #include "ircd_snprintf.h"
40 #include "ircd_string.h"
41 #include "list.h"
42 #include "listener.h"
43 #include "match.h"
44 #include "motd.h"
45 #include "numeric.h"
46 #include "numnicks.h"
47 #include "opercmds.h"
48 #include "parse.h"
49 #include "res.h"
50 #include "s_bsd.h"
51 #include "s_conf.h"
52 #include "s_debug.h"
53 #include "s_misc.h"
54 #include "send.h"
55 #include "struct.h"
56 #include "sys.h"
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <arpa/inet.h>
61 #define MAX_STRINGS 80 /* Maximum number of feature params. */
62   extern struct LocalConf   localConf;
63   extern struct DenyConf*   denyConfList;
64   extern struct CRuleConf*  cruleConfList;
65   extern struct ServerConf* serverConfList;
66   extern struct s_map*      GlobalServiceMapList;
67   extern struct qline*      GlobalQuarantineList;
68  
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, *origin;
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 VHOST
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 %token USERMODE
158 %token IAUTH
159 %token TIMEOUT
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 | iauthblock | 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($3);
265 } ';';
266
267 generalblock: GENERAL '{' generalitems '}'
268 {
269   if (localConf.name == NULL)
270     parse_error("Your General block must contain a name.");
271   if (localConf.numeric == 0)
272     parse_error("Your General block must contain a numeric (between 1 and 4095).");
273 } ';' ;
274 generalitems: generalitem generalitems | generalitem;
275 generalitem: generalnumeric | generalname | generalvhost | generaldesc | error;
276 generalnumeric: NUMERIC '=' NUMBER ';'
277 {
278   if (localConf.numeric == 0)
279     localConf.numeric = $3;
280   else if (localConf.numeric != $3)
281     parse_error("Redefinition of server numeric %i (%i)", $3,
282                 localConf.numeric);
283 };
284
285 generalname: NAME '=' QSTRING ';'
286 {
287   if (localConf.name == NULL)
288     DupString(localConf.name, $3);
289   else if (strcmp(localConf.name, $3))
290     parse_error("Redefinition of server name %s (%s)", $3,
291                 localConf.name);
292 };
293
294 generaldesc: DESCRIPTION '=' QSTRING ';'
295 {
296   MyFree(localConf.description);
297   DupString(localConf.description, $3);
298   ircd_strncpy(cli_info(&me), $3, REALLEN);
299 };
300
301 generalvhost: VHOST '=' QSTRING ';'
302 {
303   ircd_aton(&VirtualHost.addr, $3);
304 };
305
306 adminblock: ADMIN '{' adminitems '}'
307 {
308   if (localConf.location1 == NULL)
309     DupString(localConf.location1, "");
310   if (localConf.location2 == NULL)
311     DupString(localConf.location2, "");
312   if (localConf.contact == NULL)
313     DupString(localConf.contact, "");
314 } ';';
315 adminitems: adminitems adminitem | adminitem;
316 adminitem: adminlocation | admincontact | error;
317 adminlocation: LOCATION '=' QSTRING ';'
318 {
319  if (localConf.location1 == NULL)
320   DupString(localConf.location1, $3);
321  else if (localConf.location2 == NULL)
322   DupString(localConf.location2, $3);
323  /* Otherwise just drop it. -A1kmm */
324 };
325 admincontact: CONTACT '=' QSTRING ';'
326 {
327  if (localConf.contact != NULL)
328    MyFree(localConf.contact);
329  DupString(localConf.contact, $3);
330 };
331
332 classblock: CLASS {
333   name = NULL;
334   tping = 90;
335   tconn = 0;
336   maxlinks = 0;
337   sendq = 0;
338   pass = NULL;
339   memset(&privs, 0, sizeof(privs));
340   memset(&privs_dirty, 0, sizeof(privs_dirty));
341 } '{' classitems '}'
342 {
343   if (name != NULL)
344   {
345     struct ConnectionClass *c_class;
346     add_class(name, tping, tconn, maxlinks, sendq);
347     c_class = find_class(name);
348     c_class->default_umode = pass;
349     memcpy(&c_class->privs, &privs, sizeof(c_class->privs));
350     memcpy(&c_class->privs_dirty, &privs_dirty, sizeof(c_class->privs_dirty));
351   }
352   else {
353    parse_error("Missing name in class block");
354   }
355   pass = NULL;
356 } ';';
357 classitems: classitem classitems | classitem;
358 classitem: classname | classpingfreq | classconnfreq | classmaxlinks |
359            classsendq | classusermode | priv | error;
360 classname: NAME '=' QSTRING ';'
361 {
362   MyFree(name);
363   DupString(name, $3);
364 };
365 classpingfreq: PINGFREQ '=' timespec ';'
366 {
367   tping = $3;
368 };
369 classconnfreq: CONNECTFREQ '=' timespec ';'
370 {
371   tconn = $3;
372 };
373 classmaxlinks: MAXLINKS '=' expr ';'
374 {
375   maxlinks = $3;
376 };
377 classsendq: SENDQ '=' sizespec ';'
378 {
379   sendq = $3;
380 };
381 classusermode: USERMODE '=' QSTRING ';'
382 {
383   if (pass)
384     MyFree(pass);
385   DupString(pass, $3);
386 };
387
388 connectblock: CONNECT
389 {
390  name = pass = host = origin = NULL;
391  c_class = NULL;
392  port = 0;
393 } '{' connectitems '}'
394 {
395  if (name != NULL && pass != NULL && host != NULL && c_class != NULL
396      && !strchr(host, '*') && !strchr(host, '?'))
397  {
398    aconf = make_conf();
399    aconf->status = CONF_SERVER;
400    aconf->name = name;
401    aconf->origin_name = origin;
402    aconf->passwd = pass;
403    aconf->conn_class = c_class;
404    aconf->address.port = port;
405    aconf->status = CONF_SERVER;
406    aconf->host = host;
407    aconf->next = GlobalConfList;
408    lookup_confhost(aconf);
409    GlobalConfList = aconf;
410  }
411  else
412  {
413    MyFree(name);
414    MyFree(pass);
415    MyFree(host);
416    MyFree(origin);
417    parse_error("Bad connect block");
418  }
419  name = pass = host = origin = NULL;
420 }';';
421 connectitems: connectitem connectitems | connectitem;
422 connectitem: connectname | connectpass | connectclass | connecthost
423               | connectport | connectvhost | error;
424 connectname: NAME '=' QSTRING ';'
425 {
426  MyFree(name);
427  DupString(name, $3);
428 };
429 connectpass: PASS '=' QSTRING ';'
430 {
431  MyFree(pass);
432  DupString(pass, $3);
433 };
434 connectclass: CLASS '=' QSTRING ';'
435 {
436  c_class = find_class($3);
437 };
438 connecthost: HOST '=' QSTRING ';'
439 {
440  MyFree(host);
441  DupString(host, $3);
442 };
443 connectport: PORT '=' NUMBER ';'
444 {
445  port = $3;
446 };
447 connectvhost: VHOST '=' QSTRING ';'
448 {
449  MyFree(origin);
450  DupString(origin, $3);
451 };
452
453 serverblock: SERVER
454 {
455  aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf));
456  memset(aconf, 0, sizeof(*aconf));
457  aconf->status = CONF_LEAF;
458 } '{' serveritems '}'
459 {
460  if (aconf->status == 0)
461  {
462    MyFree(aconf->host);
463    MyFree(aconf->name);
464    MyFree(aconf);
465    aconf = NULL;
466    parse_error("Bad server block");
467  }
468  else
469  {
470    aconf->next = GlobalConfList;
471    GlobalConfList = aconf;
472  }
473 } ';';
474 serveritems: serveritem serveritems | serveritem;
475 serveritem: servername | servermask | serverhub | serverleaf |
476              serveruworld | error;
477 servername: NAME '=' QSTRING
478 {
479  MyFree(aconf->name);
480  DupString(aconf->name, $3);
481 } ';' ;
482 servermask: MASK '=' QSTRING
483 {
484  MyFree(aconf->host);
485  DupString(aconf->host, $3);
486 } ';' ;
487 /* XXX - perhaps we should do this the hybrid way in connect blocks
488  * instead -A1kmm. */
489 serverhub: HUB '=' YES ';'
490 {
491  aconf->status |= CONF_HUB;
492  aconf->status &= ~CONF_LEAF;
493 }
494 | HUB '=' NO
495 {
496  aconf->status &= ~CONF_HUB;
497 } ';'; 
498 serverleaf: LEAF '=' YES ';'
499 {
500  if (!(aconf->status & CONF_HUB && aconf->status & CONF_UWORLD))
501   aconf->status |= CONF_LEAF;
502  else
503   parse_error("Server is both leaf and a hub");
504 }
505 | LEAF '=' NO ';'
506 {
507  aconf->status &= ~CONF_LEAF;
508 };
509 serveruworld: UWORLD '=' YES ';'
510 {
511  aconf->status |= CONF_UWORLD;
512  aconf->status &= ~CONF_LEAF;
513 }
514 | UWORLD '=' NO ';'
515 {
516   aconf->status &= ~CONF_UWORLD;
517 };
518
519 operblock: OPER
520 {
521   aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf));
522   memset(aconf, 0, sizeof(*aconf));
523   memset(&privs, 0, sizeof(privs));
524   memset(&privs_dirty, 0, sizeof(privs_dirty));
525   aconf->status = CONF_OPERATOR;
526 } '{' operitems '}' ';'
527 {
528   if (aconf->name != NULL && aconf->passwd != NULL && aconf->host != NULL
529       && aconf->conn_class != NULL)
530   {
531     memcpy(&aconf->privs, &privs, sizeof(aconf->privs));
532     memcpy(&aconf->privs_dirty, &privs_dirty, sizeof(aconf->privs_dirty));
533     if (!PrivHas(&privs_dirty, PRIV_PROPAGATE)
534         && !PrivHas(&aconf->conn_class->privs_dirty, PRIV_PROPAGATE))
535       parse_error("Operator block for %s and class %s have no LOCAL setting", aconf->name, aconf->conn_class->cc_name);
536     aconf->next = GlobalConfList;
537     GlobalConfList = aconf;
538   }
539   else
540   {
541     log_write(LS_CONFIG, L_ERROR, 0, "operator blocks need a name, password, and host.");
542     MyFree(aconf->name);
543     MyFree(aconf->passwd);
544     MyFree(aconf->host);
545     MyFree(aconf);
546     aconf = NULL;
547   }
548 };
549 operitems: operitem | operitems operitem;
550 operitem: opername | operpass | operhost | operclass | priv | error;
551
552 opername: NAME '=' QSTRING ';'
553 {
554   MyFree(aconf->name);
555   DupString(aconf->name, $3);
556 };
557
558 operpass: PASS '=' QSTRING ';'
559 {
560   MyFree(aconf->passwd);
561   DupString(aconf->passwd, $3);
562 };
563
564 operhost: HOST '=' QSTRING ';'
565 {
566  MyFree(aconf->host);
567  if (!strchr($3, '@'))
568  {
569    int uh_len;
570    char *b = (char*) MyMalloc((uh_len = strlen($3)+3));
571    ircd_snprintf(0, b, uh_len, "*@%s", $3);
572    aconf->host = b;
573  }
574  else
575    DupString(aconf->host, $3);
576 };
577
578 operclass: CLASS '=' QSTRING ';'
579 {
580  aconf->conn_class = find_class($3);
581 };
582
583 priv: privtype '=' yesorno ';'
584 {
585   PrivSet(&privs_dirty, $1);
586   if (($3 == 1) ^ invert)
587     PrivSet(&privs, $1);
588   else
589     PrivClr(&privs, $1);
590   invert = 0;
591 };
592
593 privtype: TPRIV_CHAN_LIMIT { $$ = PRIV_CHAN_LIMIT; } |
594           TPRIV_MODE_LCHAN { $$ = PRIV_MODE_LCHAN; } |
595           TPRIV_DEOP_LCHAN { $$ = PRIV_DEOP_LCHAN; } |
596           TPRIV_WALK_LCHAN { $$ = PRIV_WALK_LCHAN; } |
597           KILL { $$ = PRIV_KILL; } |
598           TPRIV_LOCAL_KILL { $$ = PRIV_LOCAL_KILL; } |
599           TPRIV_REHASH { $$ = PRIV_REHASH; } |
600           TPRIV_RESTART { $$ = PRIV_RESTART; } |
601           TPRIV_DIE { $$ = PRIV_DIE; } |
602           TPRIV_GLINE { $$ = PRIV_GLINE; } |
603           TPRIV_LOCAL_GLINE { $$ = PRIV_LOCAL_GLINE; } |
604           JUPE { $$ = PRIV_JUPE; } |
605           TPRIV_LOCAL_JUPE { $$ = PRIV_LOCAL_JUPE; } |
606           TPRIV_LOCAL_OPMODE { $$ = PRIV_LOCAL_OPMODE; } |
607           TPRIV_OPMODE { $$ = PRIV_OPMODE; }|
608           TPRIV_SET { $$ = PRIV_SET; } |
609           TPRIV_WHOX { $$ = PRIV_WHOX; } |
610           TPRIV_BADCHAN { $$ = PRIV_BADCHAN; } |
611           TPRIV_LOCAL_BADCHAN { $$ = TPRIV_LOCAL_BADCHAN; } |
612           TPRIV_SEE_CHAN { $$ = PRIV_SEE_CHAN; } |
613           TPRIV_SHOW_INVIS { $$ = PRIV_SHOW_INVIS; } |
614           TPRIV_SHOW_ALL_INVIS { $$ = PRIV_SHOW_ALL_INVIS; } |
615           TPRIV_PROPAGATE { $$ = PRIV_PROPAGATE; } |
616           TPRIV_UNLIMIT_QUERY { $$ = PRIV_UNLIMIT_QUERY; } |
617           TPRIV_DISPLAY { $$ = PRIV_DISPLAY; } |
618           TPRIV_SEE_OPERS { $$ = PRIV_SEE_OPERS; } |
619           TPRIV_WIDE_GLINE { $$ = PRIV_WIDE_GLINE; } |
620           LOCAL { $$ = PRIV_PROPAGATE; invert = 1; } |
621           TPRIV_FORCE_OPMODE { $$ = PRIV_FORCE_OPMODE; } |
622           TPRIV_FORCE_LOCAL_OPMODE { $$ = PRIV_FORCE_LOCAL_OPMODE; };
623
624 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
625
626 /* The port block... */
627 portblock: PORT {
628   port = 0;
629   host = NULL;
630   /* Hijack these for is_server, is_hidden to cut down on globals... */
631   tconn = 0;
632   tping = 0;
633   /* and this for mask... */
634   pass = NULL;
635 } '{' portitems '}' ';'
636 {
637   if (port > 0 && port <= 0xFFFF)
638   {
639     add_listener(port, host, pass, tconn, tping);
640   }
641   else
642   {
643     parse_error("Bad port block");
644   }
645   MyFree(host);
646   MyFree(pass);
647   host = pass = NULL;
648 };
649 portitems: portitem portitems | portitem;
650 portitem: portnumber | portvhost | portmask | portserver | porthidden | error;
651 portnumber: PORT '=' NUMBER ';'
652 {
653   port = $3;
654 };
655
656 portvhost: VHOST '=' QSTRING ';'
657 {
658   MyFree(host);
659   DupString(host, $3);
660 };
661
662 portmask: MASK '=' QSTRING ';'
663 {
664   MyFree(pass);
665   DupString(pass, $3);
666 };
667
668 portserver: SERVER '=' YES ';'
669 {
670   tconn = -1;
671 } | SERVER '=' NO ';'
672 {
673   tconn = 0;
674 };
675
676 porthidden: HIDDEN '=' YES ';'
677 {
678   tping = -1;
679 } | HIDDEN '=' NO ';'
680 {
681   tping = 0;
682 };
683
684 clientblock: CLIENT
685 {
686   aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf));
687   memset(aconf, 0, sizeof(*aconf));
688   aconf->status = CONF_CLIENT;
689 } '{' clientitems '}'
690 {
691   if ((aconf->host != NULL || aconf->name!=NULL))
692   {
693     if (aconf->host == NULL)
694       DupString(aconf->host, "");
695     if (aconf->name == NULL)
696       DupString(aconf->name, "");
697     if (aconf->conn_class == NULL)
698       aconf->conn_class = find_class("default");
699     aconf->next = GlobalConfList;
700     GlobalConfList = aconf;
701     aconf = NULL;
702   }
703   else
704   {
705    MyFree(aconf->host);
706    MyFree(aconf->passwd);
707    MyFree(aconf);
708    aconf = NULL;
709    parse_error("Bad client block");
710   }
711 } ';';
712 clientitems: clientitem clientitems | clientitem;
713 clientitem: clienthost | clientclass | clientpass | clientip | error;
714 clientip: IP '=' QSTRING ';'
715 {
716   MyFree(aconf->host);
717   DupString(aconf->host, $3);
718 };
719
720 clienthost: HOST '=' QSTRING ';'
721 {
722   MyFree(aconf->name);
723   DupString(aconf->name, $3);
724 };
725
726 clientclass: CLASS '=' QSTRING ';'
727 {
728   aconf->conn_class = find_class($3);
729 };
730
731 clientpass: PASS '=' QSTRING ';'
732 {
733   MyFree(aconf->passwd);
734   DupString(aconf->passwd, $3);
735 };
736
737 killblock: KILL
738 {
739   dconf = (struct DenyConf*) MyMalloc(sizeof(*dconf));
740   memset(dconf, 0, sizeof(*dconf));
741 } '{' killitems '}'
742 {
743   if (dconf->hostmask != NULL)
744   {
745     if (dconf->usermask == NULL)
746       DupString(dconf->usermask, "*");
747     dconf->next = denyConfList;
748     denyConfList = dconf;
749     dconf = NULL;
750   }
751   else
752   {
753     MyFree(dconf->hostmask);
754     MyFree(dconf->message);
755     MyFree(dconf);
756     dconf = NULL;
757     parse_error("Bad kill block");
758   }
759 } ';';
760 killitems: killitem killitems | killitem;
761 killitem: killuhost | killreal | killreasonfile | killreason | error;
762 killuhost: HOST '=' QSTRING ';'
763 {
764   char *u, *h;
765   dconf->flags &= ~DENY_FLAGS_REALNAME;
766   MyFree(dconf->hostmask);
767   MyFree(dconf->usermask);
768   if ((h = strchr($3, '@')) == NULL)
769   {
770     u = "*";
771     h = $3;
772   }
773   else
774   {
775     u = $3;
776     h++;
777   }
778   DupString(dconf->hostmask, h);
779   DupString(dconf->usermask, u);
780   ipmask_parse(dconf->hostmask, &dconf->address, &dconf->bits);
781 };
782
783 killreal: REAL '=' QSTRING ';'
784 {
785  dconf->flags &= ~DENY_FLAGS_IP;
786  dconf->flags |= DENY_FLAGS_REALNAME;
787  MyFree(dconf->hostmask);
788  /* Leave usermask so you can specify user and real... */
789  DupString(dconf->hostmask, $3);
790 };
791
792 killreason: REASON '=' QSTRING ';'
793 {
794  dconf->flags &= DENY_FLAGS_FILE;
795  MyFree(dconf->message);
796  DupString(dconf->message, $3);
797 };
798
799 killreasonfile: TFILE '=' QSTRING ';'
800 {
801  dconf->flags |= DENY_FLAGS_FILE;
802  MyFree(dconf->message);
803  DupString(dconf->message, $3);
804 };
805
806 cruleblock: CRULE
807 {
808   host = pass = NULL;
809   tconn = CRULE_AUTO;
810 } '{' cruleitems '}'
811 {
812   struct CRuleNode *node;
813   if (host != NULL && pass != NULL && (node=crule_parse(pass)) != NULL)
814   {
815     struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p));
816     p->hostmask = host;
817     p->rule = pass;
818     p->type = tconn;
819     p->node = node;
820     p->next = cruleConfList;
821     cruleConfList = p;
822   }
823   else
824   {
825     MyFree(host);
826     MyFree(pass);
827     parse_error("Bad CRule block");
828   }
829 } ';';
830
831 cruleitems: cruleitem cruleitems | cruleitem;
832 cruleitem: cruleserver | crulerule | cruleall | error;
833
834 cruleserver: SERVER '=' QSTRING ';'
835 {
836   MyFree(host);
837   collapse($3);
838   DupString(host, $3);
839 };
840
841 crulerule: RULE '=' QSTRING ';'
842 {
843  MyFree(pass);
844  DupString(pass, $3);
845 };
846
847 cruleall: ALL '=' YES ';'
848 {
849  tconn = CRULE_ALL;
850 } | ALL '=' NO ';'
851 {
852  tconn = CRULE_AUTO;
853 };
854
855 motdblock: MOTD {
856  pass = host = NULL;
857 } '{' motditems '}'
858 {
859   if (host != NULL && pass != NULL)
860     motd_add(host, pass);
861   MyFree(host);
862   MyFree(pass);
863   host = pass = NULL;
864 } ';';
865
866 motditems: motditem motditems | motditem;
867 motditem: motdhost | motdfile | error;
868 motdhost: HOST '=' QSTRING ';'
869 {
870   DupString(host, $3);
871 };
872
873 motdfile: TFILE '=' QSTRING ';'
874 {
875   DupString(pass, $3);
876 };
877
878 featuresblock: FEATURES '{' featureitems '}' ';';
879 featureitems: featureitems featureitem | featureitem;
880
881 featureitem: QSTRING
882 {
883   stringlist[0] = $1;
884   stringno = 1;
885 } '=' stringlist ';';
886
887 stringlist: QSTRING
888 {
889   stringlist[1] = $1;
890   stringno = 2;
891 } posextrastrings
892 {
893   feature_set(NULL, (const char * const *)stringlist, stringno);
894 };
895 posextrastrings: /* empty */ | extrastrings;
896 extrastrings: extrastrings extrastring | extrastring;
897 extrastring: QSTRING
898 {
899   if (stringno < MAX_STRINGS)
900     stringlist[stringno++] = $1;
901 };
902
903 quarantineblock: QUARANTINE '{'
904 {
905   if (qconf != NULL)
906     qconf = (struct qline*) MyMalloc(sizeof(*qconf));
907   else
908   {
909     if (qconf->chname != NULL)
910       MyFree(qconf->chname);
911     if (qconf->reason != NULL)
912       MyFree(qconf->reason);
913   }
914   memset(qconf, 0, sizeof(*qconf));
915 } quarantineitems '}' ';'
916 {
917   if (qconf->chname == NULL || qconf->reason == NULL)
918   {
919     log_write(LS_CONFIG, L_ERROR, 0, "quarantine blocks need a channel name "
920               "and a reason.");
921     return 0;
922   }
923   qconf->next = GlobalQuarantineList;
924   GlobalQuarantineList = qconf;
925   qconf = NULL;
926 };
927
928 quarantineitems: CHANNEL NAME '=' QSTRING ';'
929 {
930   DupString(qconf->chname, $4);
931 } | REASON '=' QSTRING ';'
932 {
933   DupString(qconf->reason, $3);
934 };
935
936 pseudoblock: PSEUDO QSTRING '{'
937 {
938   smap = MyCalloc(1, sizeof(struct s_map));
939   DupString(smap->command, $2);
940 }
941 pseudoitems '}' ';'
942 {
943   if (!smap->name || !smap->services)
944   {
945     log_write(LS_CONFIG, L_ERROR, 0, "pseudo commands need a service name and list of target nicks.");
946     return 0;
947   }
948   if (register_mapping(smap))
949   {
950     smap->next = GlobalServiceMapList;
951     GlobalServiceMapList = smap;
952   }
953   else
954   {
955     struct nick_host *nh, *next;
956     for (nh = smap->services; nh; nh = next)
957     {
958       next = nh->next;
959       MyFree(nh);
960     }
961     MyFree(smap->name);
962     MyFree(smap->command);
963     MyFree(smap->prepend);
964     MyFree(smap);
965   }
966   smap = NULL;
967 };
968
969 pseudoitems: pseudoitem pseudoitems | pseudoitem;
970 pseudoitem: pseudoname | pseudoprepend | pseudonick | error;
971 pseudoname: NAME '=' QSTRING ';'
972 {
973   DupString(smap->name, $3);
974 };
975 pseudoprepend: PREPEND '=' QSTRING ';'
976 {
977   DupString(smap->prepend, $3);
978 };
979 pseudonick: NICK '=' QSTRING ';'
980 {
981   char *sep = strchr($3, '@');
982
983   if (sep != NULL) {
984     size_t slen = strlen($3);
985     struct nick_host *nh = MyMalloc(sizeof(*nh) + slen);
986     memcpy(nh->nick, $3, slen + 1);
987     nh->nicklen = sep - $3;
988     nh->next = smap->services;
989     smap->services = nh;
990   }
991 };
992
993 iauthblock: IAUTH '{'
994 {
995   pass = host = NULL;
996   port = 0;
997   tconn = 60;
998   tping = 60;
999 } iauthitems '}' ';'
1000 {
1001   if (!host || !port) {
1002     log_write(LS_CONFIG, L_ERROR, 0, "IAuth block needs a server name and port.");
1003     return 0;
1004   }
1005   iauth_connect(host, port, pass, tconn, tping);
1006   MyFree(pass);
1007   MyFree(host);
1008   pass = host = NULL;
1009 };
1010
1011 iauthitems: iauthitem iauthitems | iauthitem;
1012 iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout | error;
1013 iauthpass: PASS '=' QSTRING ';'
1014 {
1015   MyFree(pass);
1016   DupString(pass, $3);
1017 };
1018 iauthhost: HOST '=' QSTRING ';'
1019 {
1020   MyFree(host);
1021   DupString(host, $3);
1022 };
1023 iauthport: PORT '=' NUMBER ';'
1024 {
1025   port = $3;
1026 };
1027 iauthconnfreq: CONNECTFREQ '=' timespec ';'
1028 {
1029   tconn = $3;
1030 };
1031 iauthtimeout: TIMEOUT '=' timespec ';'
1032 {
1033   tping = $3;
1034 };