Fix Admin block documentation and behavior (SF bug#1477672).
[ircu2.10.12-pk.git] / ircd / ircd_parser.y
index e02f4ba8a65bee44d3b1f68251f3ca9e79079f27..758e080a5b4f3e3d89818bf854ca70d4a1914d1d 100644 (file)
@@ -32,7 +32,6 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
-#include "ircd_auth.h"
 #include "ircd_chattr.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -47,6 +46,7 @@
 #include "opercmds.h"
 #include "parse.h"
 #include "res.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_conf.h"
 #include "s_debug.h"
@@ -68,7 +68,7 @@
 
   int yylex(void);
   /* Now all the globals we need :/... */
-  int tping, tconn, maxlinks, sendq, port, invert, stringno;
+  int tping, tconn, maxlinks, sendq, port, invert, stringno, flags;
   char *name, *pass, *host, *ip, *username, *origin, *hub_limit;
   char *stringlist[MAX_STRINGS];
   struct ConnectionClass *c_class;
@@ -155,6 +155,8 @@ static void parse_error(char *pattern,...) {
 %token IAUTH
 %token TIMEOUT
 %token FAST
+%token AUTOCONNECT
+%token PROGRAM
 /* and now a lot of privileges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -181,7 +183,7 @@ blocks: blocks block | block;
 block: adminblock | generalblock | classblock | connectblock |
        uworldblock | operblock | portblock | jupeblock | clientblock |
        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
-       pseudoblock | iauthblock | error;
+       pseudoblock | iauthblock | error ';';
 
 /* The timespec, sizespec and expr was ripped straight from
  * ircd-hybrid-7. */
@@ -255,22 +257,26 @@ expr: NUMBER
 
 jupeblock: JUPE '{' jupeitems '}' ';' ;
 jupeitems: jupeitem jupeitems | jupeitem;
-jupeitem: jupenick | error;
-jupenick: NICK '=' QSTRING
+jupeitem: jupenick;
+jupenick: NICK '=' QSTRING ';'
 {
   addNickJupes($3);
   MyFree($3);
-} ';';
+};
 
-generalblock: GENERAL '{' generalitems '}'
+generalblock: GENERAL
 {
+    /* Zero out the vhost addresses, in case they were removed. */
+    memset(&VirtualHost_v4.addr, 0, sizeof(VirtualHost_v4.addr));
+    memset(&VirtualHost_v6.addr, 0, sizeof(VirtualHost_v6.addr));
+} '{' generalitems '}' ';' {
   if (localConf.name == NULL)
     parse_error("Your General block must contain a name.");
   if (localConf.numeric == 0)
     parse_error("Your General block must contain a numeric (between 1 and 4095).");
-} ';' ;
+};
 generalitems: generalitem generalitems | generalitem;
-generalitem: generalnumeric | generalname | generalvhost | generaldesc | error;
+generalitem: generalnumeric | generalname | generalvhost | generaldesc;
 generalnumeric: NUMERIC '=' NUMBER ';'
 {
   if (localConf.numeric == 0)
@@ -302,8 +308,11 @@ generaldesc: DESCRIPTION '=' QSTRING ';'
 generalvhost: VHOST '=' QSTRING ';'
 {
   struct irc_in_addr addr;
-  if (!ircd_aton(&addr, $3))
-      parse_error("Invalid virtual host '%s'.", $3);
+  if (!strcmp($3, "*")) {
+    /* This traditionally meant bind to all interfaces and connect
+     * from the default. */
+  } else if (!ircd_aton(&addr, $3))
+    parse_error("Invalid virtual host '%s'.", $3);
   else if (irc_in_addr_is_ipv4(&addr))
     memcpy(&VirtualHost_v4.addr, &addr, sizeof(addr));
   else
@@ -311,7 +320,14 @@ generalvhost: VHOST '=' QSTRING ';'
   MyFree($3);
 };
 
-adminblock: ADMIN '{' adminitems '}'
+adminblock: ADMIN
+{
+  MyFree(localConf.location1);
+  MyFree(localConf.location2);
+  MyFree(localConf.contact);
+  localConf.location1 = localConf.location2 = localConf.contact = NULL;
+}
+'{' adminitems '}' ';'
 {
   if (localConf.location1 == NULL)
     DupString(localConf.location1, "");
@@ -319,9 +335,9 @@ adminblock: ADMIN '{' adminitems '}'
     DupString(localConf.location2, "");
   if (localConf.contact == NULL)
     DupString(localConf.contact, "");
-} ';';
+};
 adminitems: adminitems adminitem | adminitem;
-adminitem: adminlocation | admincontact | error;
+adminitem: adminlocation | admincontact;
 adminlocation: LOCATION '=' QSTRING ';'
 {
   if (localConf.location1 == NULL)
@@ -339,7 +355,7 @@ admincontact: CONTACT '=' QSTRING ';'
 
 classblock: CLASS {
   tping = 90;
-} '{' classitems '}'
+} '{' classitems '}' ';'
 {
   if (name != NULL)
   {
@@ -361,10 +377,10 @@ classblock: CLASS {
   sendq = 0;
   memset(&privs, 0, sizeof(privs));
   memset(&privs_dirty, 0, sizeof(privs_dirty));
-} ';';
+};
 classitems: classitem classitems | classitem;
 classitem: classname | classpingfreq | classconnfreq | classmaxlinks |
-           classsendq | classusermode | priv | error;
+           classsendq | classusermode | priv;
 classname: NAME '=' QSTRING ';'
 {
   MyFree(name);
@@ -395,7 +411,8 @@ classusermode: USERMODE '=' QSTRING ';'
 connectblock: CONNECT
 {
  maxlinks = 65535;
-} '{' connectitems '}'
+ flags = CONF_AUTOCONNECT;
+} '{' connectitems '}' ';'
 {
  struct ConfItem *aconf = NULL;
  if (name == NULL)
@@ -407,7 +424,7 @@ connectblock: CONNECT
  else if (strchr(host, '*') || strchr(host, '?'))
   parse_error("Invalid host '%s' in connect block", host);
  else if (c_class == NULL)
-  parse_error("Missing class in connect block");
+  parse_error("Missing or non-existent class in connect block");
  else {
    aconf = make_conf(CONF_SERVER);
    aconf->name = name;
@@ -418,6 +435,7 @@ connectblock: CONNECT
    aconf->host = host;
    aconf->maximum = maxlinks;
    aconf->hub_limit = hub_limit;
+   aconf->flags = flags;
    lookup_confhost(aconf);
  }
  if (!aconf) {
@@ -429,12 +447,12 @@ connectblock: CONNECT
  }
  name = pass = host = origin = hub_limit = NULL;
  c_class = NULL;
- port = 0;
-}';';
+ port = flags = 0;
+};
 connectitems: connectitem connectitems | connectitem;
 connectitem: connectname | connectpass | connectclass | connecthost
               | connectport | connectvhost | connectleaf | connecthub
-              | connecthublimit | connectmaxhops | error;
+              | connecthublimit | connectmaxhops | connectauto;
 connectname: NAME '=' QSTRING ';'
 {
  MyFree(name);
@@ -448,6 +466,8 @@ connectpass: PASS '=' QSTRING ';'
 connectclass: CLASS '=' QSTRING ';'
 {
  c_class = find_class($3);
+ if (!c_class)
+  parse_error("No such connection class '%s' for Connect block", $3);
  MyFree($3);
 };
 connecthost: HOST '=' QSTRING ';'
@@ -482,10 +502,12 @@ connectmaxhops: MAXHOPS '=' expr ';'
 {
   maxlinks = $3;
 };
+connectauto: AUTOCONNECT '=' YES ';' { flags |= CONF_AUTOCONNECT; }
+ | AUTOCONNECT '=' NO ';' { flags &= ~CONF_AUTOCONNECT; };
 
 uworldblock: UWORLD '{' uworlditems '}' ';';
 uworlditems: uworlditem uworlditems | uworlditem;
-uworlditem: uworldname | error;
+uworlditem: uworldname;
 uworldname: NAME '=' QSTRING ';'
 {
   make_conf(CONF_UWORLD)->host = $3;
@@ -501,7 +523,7 @@ operblock: OPER '{' operitems '}' ';'
   else if (host == NULL)
     parse_error("Missing host in operator block");
   else if (c_class == NULL)
-    parse_error("Missing class in operator block");
+    parse_error("Invalid or missing class in operator block");
   else {
     aconf = make_conf(CONF_OPERATOR);
     aconf->name = name;
@@ -525,7 +547,7 @@ operblock: OPER '{' operitems '}' ';'
   memset(&privs_dirty, 0, sizeof(privs_dirty));
 };
 operitems: operitem | operitems operitem;
-operitem: opername | operpass | operhost | operclass | priv | error;
+operitem: opername | operpass | operhost | operclass | priv;
 opername: NAME '=' QSTRING ';'
 {
   MyFree(name);
@@ -552,6 +574,8 @@ operhost: HOST '=' QSTRING ';'
 operclass: CLASS '=' QSTRING ';'
 {
  c_class = find_class($3);
+ if (!c_class)
+  parse_error("No such connection class '%s' for Operator block", $3);
  MyFree($3);
 };
 
@@ -612,7 +636,7 @@ portblock: PORT '{' portitems '}' ';'
   port = tconn = tping = 0;
 };
 portitems: portitem portitems | portitem;
-portitem: portnumber | portvhost | portmask | portserver | porthidden | error;
+portitem: portnumber | portvhost | portmask | portserver | porthidden;
 portnumber: PORT '=' NUMBER ';'
 {
   port = $3;
@@ -649,40 +673,48 @@ porthidden: HIDDEN '=' YES ';'
 clientblock: CLIENT
 {
   maxlinks = 65535;
+  port = 0;
 }
 '{' clientitems '}' ';'
 {
+  struct ConfItem *aconf = 0;
   struct irc_in_addr addr;
   unsigned char addrbits = 0;
 
-  if (ip && !ipmask_parse(ip, &addr, &addrbits)) {
-    parse_error("Invalid IP address %s in block", ip);
-    MyFree(username);
-    MyFree(host);
-    MyFree(ip);
-    MyFree(pass);
-  } else {
-    struct ConfItem *aconf = make_conf(CONF_CLIENT);
+  if (!c_class)
+    parse_error("Invalid or missing class in Client block");
+  else if (ip && !ipmask_parse(ip, &addr, &addrbits))
+    parse_error("Invalid IP address %s in Client block", ip);
+  else {
+    aconf = make_conf(CONF_CLIENT);
     aconf->username = username;
     aconf->host = host;
     if (ip)
       memcpy(&aconf->address.addr, &addr, sizeof(aconf->address.addr));
     else
       memset(&aconf->address.addr, 0, sizeof(aconf->address.addr));
+    aconf->address.port = port;
     aconf->addrbits = addrbits;
     aconf->name = ip;
-    aconf->conn_class = c_class ? c_class : find_class("default");
+    aconf->conn_class = c_class;
     aconf->maximum = maxlinks;
     aconf->passwd = pass;
   }
+  if (!aconf) {
+    MyFree(username);
+    MyFree(host);
+    MyFree(ip);
+    MyFree(pass);
+  }
   host = NULL;
   username = NULL;
   c_class = NULL;
   ip = NULL;
   pass = NULL;
+  port = 0;
 };
 clientitems: clientitem clientitems | clientitem;
-clientitem: clienthost | clientip | clientusername | clientclass | clientpass | clientmaxlinks | error;
+clientitem: clienthost | clientip | clientusername | clientclass | clientpass | clientmaxlinks | clientport;
 clienthost: HOST '=' QSTRING ';'
 {
   char *sep = strchr($3, '@');
@@ -718,6 +750,8 @@ clientusername: USERNAME '=' QSTRING ';'
 clientclass: CLASS '=' QSTRING ';'
 {
   c_class = find_class($3);
+  if (!c_class)
+    parse_error("No such connection class '%s' for Client block", $3);
   MyFree($3);
 };
 clientpass: PASS '=' QSTRING ';'
@@ -729,11 +763,15 @@ clientmaxlinks: MAXLINKS '=' expr ';'
 {
   maxlinks = $3;
 };
+clientport: PORT '=' expr ';'
+{
+  port = $3;
+};
 
 killblock: KILL
 {
   dconf = (struct DenyConf*) MyCalloc(1, sizeof(*dconf));
-} '{' killitems '}'
+} '{' killitems '}' ';'
 {
   if (dconf->usermask || dconf->hostmask ||dconf->realmask) {
     dconf->next = denyConfList;
@@ -749,9 +787,9 @@ killblock: KILL
     parse_error("Kill block must match on at least one of username, host or realname");
   }
   dconf = NULL;
-} ';';
+};
 killitems: killitem killitems | killitem;
-killitem: killuhost | killreal | killusername | killreasonfile | killreason | error;
+killitem: killuhost | killreal | killusername | killreasonfile | killreason;
 killuhost: HOST '=' QSTRING ';'
 {
   char *h;
@@ -829,7 +867,7 @@ cruleblock: CRULE
 };
 
 cruleitems: cruleitem cruleitems | cruleitem;
-cruleitem: cruleserver | crulerule | cruleall | error;
+cruleitem: cruleserver | crulerule | cruleall;
 
 cruleserver: SERVER '=' QSTRING ';'
 {
@@ -862,7 +900,7 @@ motdblock: MOTD '{' motditems '}' ';'
 };
 
 motditems: motditem motditems | motditem;
-motditem: motdhost | motdfile | error;
+motditem: motdhost | motdfile;
 motdhost: HOST '=' QSTRING ';'
 {
   host = $3;
@@ -880,21 +918,14 @@ featureitem: QSTRING
 {
   stringlist[0] = $1;
   stringno = 1;
-} '=' stringlist ';';
-
-stringlist: QSTRING
-{
-  stringlist[1] = $1;
-  stringno = 2;
-} posextrastrings
-{
+} '=' stringlist ';' {
   unsigned int ii;
   feature_set(NULL, (const char * const *)stringlist, stringno);
   for (ii = 0; ii < stringno; ++ii)
     MyFree(stringlist[ii]);
 };
-posextrastrings: /* empty */ | extrastrings;
-extrastrings: extrastrings extrastring | extrastring;
+
+stringlist: stringlist extrastring | extrastring;
 extrastring: QSTRING
 {
   if (stringno < MAX_STRINGS)
@@ -936,22 +967,13 @@ pseudoitems '}' ';'
   }
   else
   {
-    struct nick_host *nh, *next;
-    for (nh = smap->services; nh; nh = next)
-    {
-      next = nh->next;
-      MyFree(nh);
-    }
-    MyFree(smap->name);
-    MyFree(smap->command);
-    MyFree(smap->prepend);
-    MyFree(smap);
+    free_mapping(smap);
   }
   smap = NULL;
 };
 
 pseudoitems: pseudoitem pseudoitems | pseudoitem;
-pseudoitem: pseudoname | pseudoprepend | pseudonick | pseudoflags | error;
+pseudoitem: pseudoname | pseudoprepend | pseudonick | pseudoflags;
 pseudoname: NAME '=' QSTRING ';'
 {
   MyFree(smap->name);
@@ -981,45 +1003,17 @@ pseudoflags: FAST ';'
   smap->flags |= SMAP_FAST;
 };
 
-iauthblock: IAUTH '{'
+iauthblock: IAUTH '{' iauthitems '}' ';'
 {
-  tconn = 60;
-  tping = 60;
-} iauthitems '}' ';'
-{
-  if (!host)
-    parse_error("Missing host in iauth block");
-  else if (!port)
-    parse_error("Missing port in iauth block");
-  else
-    iauth_connect(host, port, pass, tconn, tping);
-  MyFree(pass);
-  MyFree(host);
-  pass = host = NULL;
-  port = tconn = tping = 0;
+  auth_spawn(stringno, stringlist);
+  while (stringno > 0)
+    MyFree(stringlist[stringno--]);
 };
 
 iauthitems: iauthitem iauthitems | iauthitem;
-iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout | error;
-iauthpass: PASS '=' QSTRING ';'
-{
-  MyFree(pass);
-  pass = $3;
-};
-iauthhost: HOST '=' QSTRING ';'
+iauthitem: iauthprogram;
+iauthprogram: PROGRAM '='
 {
-  MyFree(host);
-  host = $3;
-};
-iauthport: PORT '=' NUMBER ';'
-{
-  port = $3;
-};
-iauthconnfreq: CONNECTFREQ '=' timespec ';'
-{
-  tconn = $3;
-};
-iauthtimeout: TIMEOUT '=' timespec ';'
-{
-  tping = $3;
-};
+  while (stringno > 0)
+    MyFree(stringlist[stringno--]);
+} stringlist ';';