Преглед изворни кода

Merge branch 'newhub'

* newhub:
  * Write userfile after receiving it
  * Don't write out hubs if not a localhub/hub
  * Write userfile when adding/removing users/bots
  * Don't rewrite binary / notify bots if the hubs didn't change
  * Signal child leaf bots to reread the binary (new hubs) when writing userfile
  * Update docs
  * Support legacy hubs format when loading internal users
  * Align pack data to 16 byte boundary as well
  * Keep settings.hubs around as a default and populate into settings.conf_hubs via -C
  * Write out userfile in more places to trigger hub saving for localhubs
  * Save hubs to binary when saving userfile
  * Load hubs from conf.hubs on start
  * Read/Write hubs to conf
  * Move hubs to CONF binary area
  * Add cmd_newhub
  * Add help for cmd_newhub
  * Update bdlib
  * Cleanup help flags for newleaf

Conflicts:
	src/chanprog.c
Bryan Drewery пре 14 година
родитељ
комит
fe60598329
14 измењених фајлова са 245 додато и 93 уклоњено
  1. 2 0
      doc/UPDATES
  2. 18 11
      doc/help.txt
  3. 6 2
      src/binary.c
  4. 31 64
      src/chanprog.c
  5. 63 0
      src/cmds.c
  6. 83 1
      src/conf.c
  7. 3 0
      src/conf.h
  8. 1 2
      src/debug.c
  9. 10 1
      src/main.c
  10. 3 5
      src/misc.c
  11. 8 0
      src/mod/share.mod/share.c
  12. 8 4
      src/settings.h
  13. 8 2
      src/userrec.c
  14. 1 1
      src/userrec.h

+ 2 - 0
doc/UPDATES

@@ -15,6 +15,8 @@
   * When 'mdop' protection is on, re-op all previously opped clients automatically.
   * When 'mdop' protection is on, re-op all previously opped clients automatically.
   * When 'mop' protection is on, deop all previously regular clients automatically.
   * When 'mop' protection is on, deop all previously regular clients automatically.
   * Add './wraith -V' which will display the packconfig that the bot is using.
   * Add './wraith -V' which will display the packconfig that the bot is using.
+  * Add cmd_newhub for adding a new hub. All bots will add this to their config.
+  * Hubs can now be overridden inside the botconfig (-C)
 
 
 maint
 maint
   * Fix various compile warnings with newer GCC
   * Fix various compile warnings with newer GCC

+ 18 - 11
doc/help.txt

@@ -56,7 +56,7 @@ See also: invites, -invite, stick, unstick
    have no flags, an optional hostmask, and a random pass/secpass.
    have no flags, an optional hostmask, and a random pass/secpass.
    List as many hosts as needed.
    List as many hosts as needed.
  
  
-See also: -user, +host, -host, clearhosts%{+ni}, newleaf%{-}
+See also: -user, +host, -host, clearhosts%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::-ban
 ::-ban
 ###  $b-ban$b <banmask or number> [channel]
 ###  $b-ban$b <banmask or number> [channel]
    Removes the specified ban from the list of bans stored on the bot. You may
    Removes the specified ban from the list of bans stored on the bot. You may
@@ -68,7 +68,7 @@ See also: bans, +ban, stick, unstick
    This is exactly the same as $b'%d-user'$b (it removes a user record). It is
    This is exactly the same as $b'%d-user'$b (it removes a user record). It is
    included for convenience.
    included for convenience.
  
  
-See also: +user, -user%{+n}, newleaf%{-}
+See also: +user, -user%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::-chan
 ::-chan
 ### $b-chan$b <channel>
 ### $b-chan$b <channel>
    This removes ALL information about a channel from the bot. 
    This removes ALL information about a channel from the bot. 
@@ -121,7 +121,7 @@ See also: invites, +invite, stick, unstick
 ###  $b-user$b <handle> [anotherhandle] ...
 ###  $b-user$b <handle> [anotherhandle] ...
    Removes the specified handles user records.
    Removes the specified handles user records.
  
  
-See also: +user%{+ai}, -bot%{-}%{+nai}, newleaf%{-}
+See also: +user%{+ai}, -bot%{-}%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::about:
 ::about:
 ###  $babout$b
 ###  $babout$b
    If you feel you are missing from the list feel free to contact bryan.
    If you feel you are missing from the list feel free to contact bryan.
@@ -354,7 +354,7 @@ See also: version, netversion, about%{+m|m}, status%{-}
    and update all bots instead.
    and update all bots instead.
 %{-}
 %{-}
  
  
-See also: link
+See also: link%{+a}, newhub%{-}
 ::chaninfo
 ::chaninfo
 ###  $bchaninfo$b <channel>
 ###  $bchaninfo$b <channel>
    This lists all the settings for the bot on the given channel.
    This lists all the settings for the bot on the given channel.
@@ -775,7 +775,7 @@ See also: console, echo, login, page, strip
                if you change any of the following: 
                if you change any of the following: 
     $bhomedir$b, $bbinpath$b, $bbinname$b, $bportmin$b, $bportmax$b
     $bhomedir$b, $bbinpath$b, $bbinname$b, $bportmin$b, $bportmax$b
  
  
-See also: newleaf
+See also: newleaf, newhub
 ::console:
 ::console:
 ###  $bconsole$b [channel] [modes]
 ###  $bconsole$b [channel] [modes]
    Changes your console level so that you will see only the types of console
    Changes your console level so that you will see only the types of console
@@ -1248,7 +1248,7 @@ See also: netlag
    Attempts to link to another hub.  This command is deprecated and not 
    Attempts to link to another hub.  This command is deprecated and not 
    recommended for use.
    recommended for use.
  
  
-See also: unlink, newleaf%{+a}, -bot%{-}
+See also: unlink%{+ni}, newleaf%{-}%{+a}, newhub, -bot%{-}
 ::login
 ::login
 ###  $blogin$b <banner|bots|channels|whom> [on/off]
 ###  $blogin$b <banner|bots|channels|whom> [on/off]
    Sets various login options.
    Sets various login options.
@@ -1433,6 +1433,11 @@ See also: botversion, version, about%{+m|m}, status%{-}
    the results over DCC.
    the results over DCC.
  
  
 See also: w
 See also: w
+:hub:newhub:
+###  $bnewhub$b <handle> <address> <port> [hublevel]
+    Add a new hub to the net.
+ 
+See also: chaddr, newleaf
 :hub:newleaf:
 :hub:newleaf:
 ###  $bnewleaf$b <handle> [hostmask] [anotherhostmask] ...
 ###  $bnewleaf$b <handle> [hostmask] [anotherhostmask] ...
    Adds a new leaf to the botnet with the specified handle and hostname.
    Adds a new leaf to the botnet with the specified handle and hostname.
@@ -1440,6 +1445,8 @@ See also: w
  
  
    The binary config botline will also be outputted, but should be
    The binary config botline will also be outputted, but should be
    checked for correctness.
    checked for correctness.
+ 
+%{+a}See also: newhub%{-}
 ::newpass
 ::newpass
 ###  $bnewpass$b <password|rand>
 ###  $bnewpass$b <password|rand>
    Changes your password on the bot. This is similar to the '/msg <bot> pass'
    Changes your password on the bot. This is similar to the '/msg <bot> pass'
@@ -1520,7 +1527,7 @@ See also: restart
    The local bot must, however, have a bot record for the bot you wish to
    The local bot must, however, have a bot record for the bot you wish to
    relay to. Typing '%dquit' or "*bye*" on a line by itself will end the relay.
    relay to. Typing '%dquit' or "*bye*" on a line by itself will end the relay.
  
  
-See also: bots%{+n}, newleaf%{-}%{+a}, -bot%{-}
+See also: bots%{+n}, newleaf%{-}%{+a}, newhub, -bot%{-}
 :leaf:release
 :leaf:release
 ###  $brelease$b
 ###  $brelease$b
    Releases the bot's nick if it is on the jupenick. It gives an estimated
    Releases the bot's nick if it is on the jupenick. It gives an estimated
@@ -1624,8 +1631,8 @@ See also: reload, backup
 [N]  $bserver-port$b     Default port to use for server connections.
 [N]  $bserver-port$b     Default port to use for server connections.
 [N]  $bserver-port-ssl$b Default port to use for SSL server connections.
 [N]  $bserver-port-ssl$b Default port to use for SSL server connections.
 [B]  $bserver-use-ssl$b  Use SSL for IRC server connections?
 [B]  $bserver-use-ssl$b  Use SSL for IRC server connections?
-
-
+ 
+ 
 [B]  $bauth-chan$b       If set, auth cmds will work in channels as well as in msg, otherwise 
 [B]  $bauth-chan$b       If set, auth cmds will work in channels as well as in msg, otherwise 
                           only in msg.
                           only in msg.
 [S]  $bauth-key$b        The authkey used during authing. Give to users if they need to auth. 
 [S]  $bauth-key$b        The authkey used during authing. Give to users if they need to auth. 
@@ -1665,7 +1672,7 @@ See also: reload, backup
 [N]  $bmsgburst$b        How many messages to burst at once to server. (Too high will excess flood)
 [N]  $bmsgburst$b        How many messages to burst at once to server. (Too high will excess flood)
 [N]  $bmsgrate$b         How often (msecs) to dequeue msgs to the server. Only used on
 [N]  $bmsgrate$b         How often (msecs) to dequeue msgs to the server. Only used on
                            non-ratbox servers. Too small a value can result in Excess Flood.
                            non-ratbox servers. Too small a value can result in Excess Flood.
-
+ 
 [L]  $brbl-servers$b     Servers to use for RBL checking in channels that are +rbl.
 [L]  $brbl-servers$b     Servers to use for RBL checking in channels that are +rbl.
  
  
 [S]  $brealname$b        The bot's "real name" when connecting. (supports '$n' expansion)
 [S]  $brealname$b        The bot's "real name" when connecting. (supports '$n' expansion)
@@ -1896,7 +1903,7 @@ See also: dump
    to unlink them. If "*" is specified as the parameter, all bots will be
    to unlink them. If "*" is specified as the parameter, all bots will be
    unlinked.
    unlinked.
  
  
-See also: %{+n}link, %{-}bots, downbots%{+n}, newleaf, bottree%{-}
+See also: %{+n}link, %{-}bots, downbots%{+n}, newleaf, bottree%{-}%{+a}, newhub%{-}
 ::unstick
 ::unstick
 ###  $bunstick$b [ban/exempt/invite] <hostmask/number> [channel]
 ###  $bunstick$b [ban/exempt/invite] <hostmask/number> [channel]
    Makes a "sticky" ban, exempt, or invite normal again.
    Makes a "sticky" ban, exempt, or invite normal again.

+ 6 - 2
src/binary.c

@@ -52,9 +52,9 @@
 settings_t settings = {
 settings_t settings = {
   SETTINGS_HEADER,
   SETTINGS_HEADER,
   /* -- STATIC -- */
   /* -- STATIC -- */
-  "", "", "", "", "", "", "", "", "",
+  "", "", "", "", "", "", "", "", "", "",
   /* -- DYNAMIC -- */
   /* -- DYNAMIC -- */
-  "", "", "", "", "", "", "", "", "",
+  "", "", "", "", "", "", "", "", "", "",
   /* -- PADDING */
   /* -- PADDING */
   ""
   ""
 };
 };
@@ -519,6 +519,7 @@ static void edpack(settings_t *incfg, const char *in_hash, int what)
 
 
   /* -- DYNAMIC -- */
   /* -- DYNAMIC -- */
   dofield(incfg->dynamic_initialized);
   dofield(incfg->dynamic_initialized);
+  dofield(incfg->conf_hubs);
   dofield(incfg->bots);
   dofield(incfg->bots);
   dofield(incfg->uid);
   dofield(incfg->uid);
   dofield(incfg->autocron);
   dofield(incfg->autocron);
@@ -552,6 +553,7 @@ tellconfig(settings_t *incfg)
 //  dofield(incfg->salt2);
 //  dofield(incfg->salt2);
   // -- DYNAMIC --
   // -- DYNAMIC --
   dofield(incfg->dynamic_initialized);
   dofield(incfg->dynamic_initialized);
+  dofield(incfg->conf_hubs);
   dofield(incfg->bots);
   dofield(incfg->bots);
   dofield(incfg->uid);
   dofield(incfg->uid);
   dofield(incfg->autocron);
   dofield(incfg->autocron);
@@ -728,6 +730,8 @@ void conf_to_bin(conf_t *in, bool move, int die)
                            bot->net.ip6 ? bot->net.ip6 : "");
                            bot->net.ip6 ? bot->net.ip6 : "");
     }
     }
 
 
+  simple_snprintf(settings.conf_hubs, sizeof(settings.conf_hubs), in->hubs.join(',').c_str());
+
   newbin = binname;
   newbin = binname;
 //  tellconfig(&settings); 
 //  tellconfig(&settings); 
   write_settings(newbin, -1, 1);
   write_settings(newbin, -1, 1);

+ 31 - 64
src/chanprog.c

@@ -382,81 +382,48 @@ void reaffirm_owners()
 }
 }
 
 
 bool is_hub(const char* nick) {
 bool is_hub(const char* nick) {
-  char *p = settings.hubs, *p2 = NULL, hubbuf[HANDLEN + 1] ="";
-  size_t len = 0;
-
-  while (p && *p) {
-    if ((p2 = strchr(p, ' '))) {
-
-      len = p2 - p + 1;
-      if (len > sizeof(hubbuf))
-        len = sizeof(hubbuf);
-      strlcpy(hubbuf, p, len);
-      if (!strncasecmp(nick, hubbuf, HANDLEN))
-        return 1;
+  for (size_t idx = 0; idx < conf.hubs.length(); ++idx) {
+    bd::String hub(conf.hubs[idx]);
+    if (!strncasecmp(nick, newsplit(hub).c_str(), HANDLEN)) {
+      return true;
     }
     }
-    if ((p = strchr(p, ',')))
-      p++;
   }
   }
-  return 0;
+  return false;
 }
 }
 
 
 void load_internal_users()
 void load_internal_users()
 {
 {
-  char *p = NULL, *ln = NULL, *hand = NULL, *ip = NULL, *port = NULL, *pass = NULL, *q = NULL;
+  char *p = NULL, *ln = NULL, *hand = NULL, *pass = NULL, *q = NULL;
   char *hosts = NULL, buf[2048] = "", tmp[51] = "";
   char *hosts = NULL, buf[2048] = "", tmp[51] = "";
-  int i, hublevel = 0;
-  struct bot_addr *bi = NULL;
+  int i;
   struct userrec *u = NULL;
   struct userrec *u = NULL;
 
 
   /* hubs */
   /* hubs */
-  strlcpy(buf, settings.hubs, sizeof(buf));
-  p = buf;
-  while (p) {
-    ln = p;
-    p = strchr(p, ',');
-    if (p)
-      *p++ = 0;
-    hand = ln;
-    ip = NULL;
-    port = NULL;
-    u = NULL;
-    for (i = 0; ln; i++) {
-      switch (i) {
-        case 0:
-          hand = ln;
-          break;
-        case 1:
-          ip = ln;
-          break;
-        case 2:
-          port = ln;
-          hublevel++;		/* We must increment this even if it is already added */
-          if (!get_user_by_handle(userlist, hand)) {
-            userlist = adduser(userlist, hand, "none", "-", USER_OP, 1);
-            u = get_user_by_handle(userlist, hand);
-
-            simple_snprintf(tmp, sizeof(tmp), "%li [internal]", (long)now);
-            set_user(&USERENTRY_ADDED, u, tmp);
-
-            bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
-
-            bi->address = strdup(ip);
-            bi->telnet_port = atoi(port) ? atoi(port) : 0;
-            bi->relay_port = bi->telnet_port;
-            bi->hublevel = hublevel;
-            if (conf.bot->hub && (!bi->hublevel) && (!strcasecmp(hand, conf.bot->nick)))
-              bi->hublevel = 99;
-            bi->uplink = (char *) my_calloc(1, 1);
-            set_user(&USERENTRY_BOTADDR, u, bi);
-            /* set_user(&USERENTRY_PASS, get_user_by_handle(userlist, hand), SALT2); */
-          }
-          break;
-        default:
-          break;
+  for (size_t idx = 0; idx < conf.hubs.length(); ++idx) {
+    bd::Array<bd::String> params(static_cast<bd::String>(conf.hubs[idx]).split(' '));
+    bd::String handle(params[0]);
+    if (!get_user_by_handle(userlist, const_cast<char*>(handle.c_str()))) {
+      bd::String address(params[1]);
+      port_t port = atoi(static_cast<bd::String>(params[2]).c_str());
+      unsigned short hublevel = params.length() == 4 ? atoi(static_cast<bd::String>(params[3]).c_str()) : (idx + 1);
+
+      userlist = adduser(userlist, handle.c_str(), "none", "-", USER_OP, 1);
+      u = get_user_by_handle(userlist, const_cast<char*>(handle.c_str()));
+
+      simple_snprintf(tmp, sizeof(tmp), "%li [internal]", (long)now);
+      set_user(&USERENTRY_ADDED, u, tmp);
+
+      struct bot_addr *bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
+
+      bi->address = strdup(address.c_str());
+      bi->telnet_port = bi->relay_port = port;
+      bi->hublevel = hublevel;
+      if (conf.bot->hub && (!bi->hublevel) && (!strcasecmp(handle.c_str(), conf.bot->nick))) {
+        bi->hublevel = 99;
       }
       }
-      if (ln && (ln = strchr(ln, ' ')))
-        *ln++ = 0;
+      bi->uplink = (char *) my_calloc(1, 1);
+      set_user(&USERENTRY_BOTADDR, u, bi);
+      /* set_user(&USERENTRY_PASS, get_user_by_handle(userlist, handle.c_str()), SALT2); */
     }
     }
   }
   }
 
 

+ 63 - 0
src/cmds.c

@@ -3250,6 +3250,68 @@ static void cmd_newleaf(int idx, char *par)
   }
   }
 }
 }
 
 
+static void cmd_newhub(int idx, char *par)
+{
+  bd::Array<bd::String> params(bd::String(par).split(' '));
+  if (!par[0] || params.length() < 3) {
+    dprintf(idx, "Usage: newhub <handle> <address> <port> [hublevel]\n");
+    return;
+  }
+
+  putlog(LOG_CMDS, "*", "#%s# newhub %s", dcc[idx].nick, par);
+
+  bd::String handle(params[0]);
+
+  if (handle.length() > HANDLEN) {
+    handle.resize(HANDLEN);
+  }
+  if (get_user_by_handle(userlist, const_cast<char*>(handle.c_str()))) {
+    dprintf(idx, "Already got a %s hub\n", handle.c_str());
+    return;
+  } else if (strchr(BADHANDCHARS, handle[0]) != NULL) {
+    dprintf(idx, "You can't start a botnick with '%c'.\n", static_cast<char>(handle[0]));
+    return;
+  }
+
+  bd::String address(params[1]);
+  port_t port = atoi(static_cast<bd::String>(params[2]).c_str());
+
+  unsigned short hublevel = 0;
+  // Was a hublevel passed in?
+  if (params.length() == 4) {
+    hublevel = atoi(static_cast<bd::String>(params[3]).c_str());
+  } else {
+    // Find next available hublevel
+    for (struct userrec *u = userlist; u; u = u->next) {
+      unsigned short u_bot_hublevel = bot_hublevel(u);
+      if (u->bot && u_bot_hublevel < 999 && u_bot_hublevel > hublevel) {
+        hublevel = u_bot_hublevel;
+      }
+    }
+    ++hublevel;
+  }
+
+  struct bot_addr *bi = NULL;
+  struct userrec *u1 = NULL;
+  char tmp[81] = "";
+
+  userlist = adduser(userlist, handle.c_str(), "none", "-", USER_OP, 1);
+  u1 = get_user_by_handle(userlist, const_cast<char*>(handle.c_str()));
+  bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
+  bi->uplink = (char *) my_calloc(1, 1);
+  bi->address = strdup(address.c_str());
+  bi->telnet_port = port;
+  bi->relay_port = port;
+  bi->hublevel = hublevel;
+
+  set_user(&USERENTRY_BOTADDR, u1, bi);
+  simple_snprintf(tmp, sizeof(tmp), "%li %s", (long) now, dcc[idx].nick);
+  set_user(&USERENTRY_ADDED, u1, tmp);
+  dprintf(idx, "Added new hub: %s\n", handle.c_str());
+
+  write_userfile(idx);
+}
+
 static void cmd_nopass(int idx, char *par)
 static void cmd_nopass(int idx, char *par)
 {
 {
   int cnt = 0;
   int cnt = 0;
@@ -4615,6 +4677,7 @@ cmd_t C_dcc[] =
   {"me",		"",	(Function) cmd_me,		NULL, 0},
   {"me",		"",	(Function) cmd_me,		NULL, 0},
   {"motd",		"",	(Function) cmd_motd,		NULL, 0},
   {"motd",		"",	(Function) cmd_motd,		NULL, 0},
   {"newleaf",		"n",	(Function) cmd_newleaf,		NULL, HUB},
   {"newleaf",		"n",	(Function) cmd_newleaf,		NULL, HUB},
+  {"newhub",		"a",	(Function) cmd_newhub,		NULL, HUB},
   {"nopass",		"m",	(Function) cmd_nopass,		NULL, HUB},
   {"nopass",		"m",	(Function) cmd_nopass,		NULL, HUB},
   {"newpass",		"",	(Function) cmd_newpass,		NULL, 0},
   {"newpass",		"",	(Function) cmd_newpass,		NULL, 0},
   {"secpass",		"",	(Function) cmd_secpass,		NULL, 0},
   {"secpass",		"",	(Function) cmd_secpass,		NULL, 0},

+ 83 - 1
src/conf.c

@@ -322,6 +322,12 @@ init_conf()
 //  conf.bots->next = NULL;
 //  conf.bots->next = NULL;
   conf.bots = NULL;
   conf.bots = NULL;
   conf.bot = NULL;
   conf.bot = NULL;
+  // If conf_hubs is blank, revert to pack hubs
+  if (strlen(settings.conf_hubs)) {
+    conf.hubs = bd::String(settings.conf_hubs).split(',');
+  } else {
+    conf.hubs = bd::String(settings.hubs).split(',');
+  }
 
 
   conf.localhub = NULL;
   conf.localhub = NULL;
   conf.autocron = 1;
   conf.autocron = 1;
@@ -465,7 +471,7 @@ conf_addbot(const char *nick, const char *ip, const char *host, const char *ip6)
 
 
 //  bot->pid = checkpid(nick, bot);
 //  bot->pid = checkpid(nick, bot);
 
 
-  if (settings.hubs && is_hub(bot->nick))
+  if (is_hub(bot->nick))
     bot->hub = 1;
     bot->hub = 1;
 
 
   /* not a hub 
   /* not a hub 
@@ -536,6 +542,7 @@ free_conf()
     free(conf.datadir);
     free(conf.datadir);
   if (conf.homedir)
   if (conf.homedir)
     free(conf.homedir);
     free(conf.homedir);
+  conf.hubs.clear();
   init_conf();
   init_conf();
 }
 }
 
 
@@ -602,9 +609,11 @@ readconf(const char *fname, int bits)
     fatal(STR("Cannot read config"), 0);
     fatal(STR("Cannot read config"), 0);
   }
   }
 
 
+  conf.hubs.clear();
   free_conf_bots(conf.bots);
   free_conf_bots(conf.bots);
 
 
   bd::String line, option;
   bd::String line, option;
+  unsigned short hublevel = 0;
 
 
   while (stream->tell() < stream->length()) {
   while (stream->tell() < stream->length()) {
     line = stream->getline().chomp().trim();
     line = stream->getline().chomp().trim();
@@ -654,6 +663,11 @@ readconf(const char *fname, int bits)
         if (str_isdigit(line.c_str()))
         if (str_isdigit(line.c_str()))
           conf.uid = atoi(line.c_str());
           conf.uid = atoi(line.c_str());
 
 
+      } else if (option == STR("hub")) {
+        if (line.split(' ').length() == 3) {
+          conf.hubs << bd::String::printf("%s %d", line.c_str(), ++hublevel);
+        }
+
       } else {
       } else {
         putlog(LOG_MISC, "*", STR("Unrecognized config option '%s'"), option.c_str());
         putlog(LOG_MISC, "*", STR("Unrecognized config option '%s'"), option.c_str());
 
 
@@ -687,6 +701,20 @@ readconf(const char *fname, int bits)
 
 
 char s1_9[3] = "",s1_5[3] = "",s1_1[3] = "";
 char s1_9[3] = "",s1_5[3] = "",s1_1[3] = "";
 
 
+bool hubSort (bd::String hub1, bd::String hub2) {
+  bd::Array<bd::String> hub1params(static_cast<bd::String>(hub1).split(' '));
+  bd::Array<bd::String> hub2params(static_cast<bd::String>(hub2).split(' '));
+
+  unsigned short hub1level = 99, hub2level = 99;
+  if (hub1params.length() == 4) {
+    hub1level = atoi(static_cast<bd::String>(hub1params[3]).c_str());
+  }
+  if (hub2params.length() == 4) {
+    hub2level = atoi(static_cast<bd::String>(hub2params[3]).c_str());
+  }
+  return hub1level < hub2level;
+}
+
 int
 int
 writeconf(char *filename, int fd, int bits)
 writeconf(char *filename, int fd, int bits)
 {
 {
@@ -774,6 +802,21 @@ writeconf(char *filename, int fd, int bits)
     comment("");
     comment("");
   }
   }
 
 
+  if (conf.hubs.length()) {
+    comment("# Hubs this bot will connect to");
+    // Sort hub list by hublevel
+    bd::Array<bd::String> sortedhubs(conf.hubs);
+    std::sort(sortedhubs.begin(), sortedhubs.end(), hubSort);
+
+    for (size_t idx = 0; idx < sortedhubs.length(); ++idx) {
+      bd::Array<bd::String> hubparams(static_cast<bd::String>(sortedhubs[idx]).split(' '));
+      bd::String hubnick(hubparams[0]), address(hubparams[1]);
+      port_t port = atoi(static_cast<bd::String>(hubparams[2]).c_str());
+      *stream << bd::String::printf(STR("! hub %s %s %d\n"), hubnick.c_str(), address.c_str(), port);
+    }
+    comment("");
+  }
+
   comment("# '|' means OR, [] means the enclosed is optional");
   comment("# '|' means OR, [] means the enclosed is optional");
   comment("# A '+' in front of HOST means the HOST is ipv6");
   comment("# A '+' in front of HOST means the HOST is ipv6");
   comment("# A '/' in front of BOT will disable that bot.");
   comment("# A '/' in front of BOT will disable that bot.");
@@ -980,6 +1023,45 @@ bin_to_conf(bool error)
   tellconf();
   tellconf();
 }
 }
 
 
+void conf_update_hubs(struct userrec* list) {
+  if (!conf.bot->hub && !conf.bot->localhub) {
+    return;
+  }
+
+  bd::Array<bd::String> hubUsers, oldhubs(conf.hubs);
+
+  // Count how many hubs there are
+  for (struct userrec *u = list; u; u = u->next) {
+    if (bot_hublevel(u) < 999) {
+      hubUsers << u->handle;
+    }
+  }
+
+  conf.hubs.clear();
+  conf.hubs.Reserve(hubUsers.length());
+  for (size_t idx = 0; idx < hubUsers.length(); ++idx) {
+    struct userrec *u = get_user_by_handle(list, const_cast<char*>(static_cast<bd::String>(hubUsers[idx]).c_str()));
+    struct bot_addr *bi = (struct bot_addr *) get_user(&USERENTRY_BOTADDR, u);
+    conf.hubs << bd::String::printf("%s %s %d %d", u->handle, bi->address, bi->telnet_port, bi->hublevel);
+  }
+
+  // Only rewrite binary / notify bots if the hubs changed
+  if (conf.hubs == oldhubs) {
+    return;
+  }
+
+  if (conf.bot->hub || conf.bot->localhub) {
+    /* rewrite our binary */
+    conf_to_bin(&conf, 0, -1);
+
+    /* Now signal all of the old bots with SIGUSR1
+     * They will auto die and determine new localhub, etc..
+     */
+    conf_checkpids(conf.bots);
+    conf_killbot(conf.bots, conf.bot->nick, NULL, SIGUSR1, 1); /* Don't kill me. */
+  }
+}
+
 void conf_add_userlist_bots()
 void conf_add_userlist_bots()
 {
 {
   conf_bot *bot = NULL;
   conf_bot *bot = NULL;

+ 3 - 0
src/conf.h

@@ -6,6 +6,7 @@
 #include "types.h"
 #include "types.h"
 #include "eggdrop.h"
 #include "eggdrop.h"
 #include "settings.h"
 #include "settings.h"
+#include <bdlib/src/Array.h>
 
 
 typedef struct conf_net_b {
 typedef struct conf_net_b {
   char *host;
   char *host;
@@ -28,6 +29,7 @@ typedef struct conf_bot_b {
 } conf_bot;
 } conf_bot;
 
 
 typedef struct conf_b {
 typedef struct conf_b {
+  bd::Array<bd::String> hubs;
   conf_bot *bots;       /* the list of bots */
   conf_bot *bots;       /* the list of bots */
   conf_bot *bot;        /* single bot (me) */
   conf_bot *bot;        /* single bot (me) */
   int features;		/* Pack features (take, mdop, beta... etc..) */
   int features;		/* Pack features (take, mdop, beta... etc..) */
@@ -73,5 +75,6 @@ void deluser_removed_bots(conf_bot *, conf_bot *);
 conf_bot *conf_getlocalhub(conf_bot *);
 conf_bot *conf_getlocalhub(conf_bot *);
 void conf_setmypid(pid_t);
 void conf_setmypid(pid_t);
 void conf_bot_dup(conf_bot *dest, conf_bot *src);
 void conf_bot_dup(conf_bot *dest, conf_bot *src);
+void conf_update_hubs(struct userrec* list);
 
 
 #endif /* !_CONF_H */
 #endif /* !_CONF_H */

+ 1 - 2
src/debug.c

@@ -211,8 +211,7 @@ static void got_term(int) __attribute__ ((noreturn));
 
 
 static void got_term(int z)
 static void got_term(int z)
 {
 {
-  if (conf.bot->hub)
-    write_userfile(-1);
+  write_userfile(-1);
   fatal("Received SIGTERM", 0);
   fatal("Received SIGTERM", 0);
   exit(1);		/* for GCC noreturn */
   exit(1);		/* for GCC noreturn */
 }
 }

+ 10 - 1
src/main.c

@@ -430,7 +430,16 @@ static void dtx_arg(int& argc, char *argv[])
         printf(STR("BuildOS: %s%s%s BuildArch: %s%s%s\n"), BOLD(-1), BUILD_OS, BOLD_END(-1), BOLD(-1), BUILD_ARCH, BOLD_END(-1));
         printf(STR("BuildOS: %s%s%s BuildArch: %s%s%s\n"), BOLD(-1), BUILD_OS, BOLD_END(-1), BOLD(-1), BUILD_ARCH, BOLD_END(-1));
         printf(STR("- http://wraith.botpack.net -\n"));
         printf(STR("- http://wraith.botpack.net -\n"));
 #ifdef DEBUG
 #ifdef DEBUG
-	printf(STR("pack: %zu conf: %zu settings_t: %zu prefix: %zu pad: %zu needed padding: %zu\n"), SIZE_PACK, SIZE_CONF, sizeof(settings_t), PREFIXLEN, SIZE_PAD, (16 - ((sizeof(settings_t) - sizeof(settings.padding)) % 16)) % 16);
+	printf(STR("pack: %zu conf: %zu settings_t: %zu prefix: %zu pad: %zu/%zu needed padding: %zu/%zu\n"),
+            SIZE_PACK,
+            SIZE_CONF,
+            SIZE_SETTINGS,
+            PREFIXLEN,
+            SIZE_PAD_PACK,
+            SIZE_PAD,
+            (SIZE_PAD_ALIGN - ((SIZE_PACK - SIZE_PAD_PACK) % SIZE_PAD_ALIGN)) % SIZE_PAD_ALIGN,
+            (SIZE_PAD_ALIGN - ((SIZE_SETTINGS - SIZE_PAD) % SIZE_PAD_ALIGN)) % SIZE_PAD_ALIGN
+        );
 #endif
 #endif
         if (settings.dynamic_initialized[0]) {
         if (settings.dynamic_initialized[0]) {
           bin_to_conf();
           bin_to_conf();

+ 3 - 5
src/misc.c

@@ -609,9 +609,8 @@ int str_isdigit(const char *str)
  */
  */
 void kill_bot(char *s1, char *s2)
 void kill_bot(char *s1, char *s2)
 {
 {
-  if (conf.bot->hub)
-    write_userfile(-1);
-  else
+  write_userfile(-1);
+  if (!conf.bot->hub)
     server_die();
     server_die();
   chatout("*** %s\n", s1);
   chatout("*** %s\n", s1);
   botnet_send_chat(-1, conf.bot->nick, s1);
   botnet_send_chat(-1, conf.bot->nick, s1);
@@ -818,8 +817,7 @@ restart(int idx)
 
 
   socks->my_close();
   socks->my_close();
 
 
-  if (conf.bot->hub)
-    write_userfile(idx);
+  write_userfile(idx);
 /*
 /*
   if (server_online) {
   if (server_online) {
     do_chanset(NULL, NULL, STR("+inactive"), DO_LOCAL);
     do_chanset(NULL, NULL, STR("+inactive"), DO_LOCAL);

+ 8 - 0
src/mod/share.mod/share.c

@@ -533,6 +533,7 @@ share_newuser(int idx, char *par)
       noshare = 0;
       noshare = 0;
       if (conf.bot->hub)
       if (conf.bot->hub)
         putlog(LOG_CMDS, "@", "%s: newuser %s %s", dcc[idx].nick, nick, s);
         putlog(LOG_CMDS, "@", "%s: newuser %s %s", dcc[idx].nick, nick, s);
+      write_userfile(-1);
     }
     }
   }
   }
 }
 }
@@ -551,6 +552,7 @@ share_killuser(int idx, char *par)
       shareout_but(idx, "k %s\n", par);
       shareout_but(idx, "k %s\n", par);
       if (conf.bot->hub)
       if (conf.bot->hub)
         putlog(LOG_CMDS, "@", "%s: killuser %s", dcc[idx].nick, par);
         putlog(LOG_CMDS, "@", "%s: killuser %s", dcc[idx].nick, par);
+      write_userfile(-1);
     }
     }
     noshare = 0;
     noshare = 0;
   }
   }
@@ -670,6 +672,10 @@ share_change(int idx, char *par)
       }
       }
       noshare = 0;
       noshare = 0;
     }
     }
+
+    if (uet == &USERENTRY_BOTADDR) {
+      write_userfile(-1);
+    }
   }
   }
 }
 }
 
 
@@ -1444,6 +1450,8 @@ static void share_read_stream(int idx, bd::Stream& stream) {
     Auth::FillUsers();
     Auth::FillUsers();
   }
   }
 
 
+  write_userfile(-1);
+
   cmdpass_free(old_cmdpass);
   cmdpass_free(old_cmdpass);
 
 
   checkchans(1);                /* remove marked channels */
   checkchans(1);                /* remove marked channels */

+ 8 - 4
src/settings.h

@@ -6,7 +6,7 @@
 #define PREFIXLEN sizeof(SETTINGS_HEADER)
 #define PREFIXLEN sizeof(SETTINGS_HEADER)
 
 
 /* !!! THIS MUST BE CHANGED WHEN CHANGING THE PACK STRUCT OR ALGORITHMS !!! */
 /* !!! THIS MUST BE CHANGED WHEN CHANGING THE PACK STRUCT OR ALGORITHMS !!! */
-#define SETTINGS_VER 2
+#define SETTINGS_VER 3
 
 
 #define DYNAMIC_HEADER dynamic_initialized
 #define DYNAMIC_HEADER dynamic_initialized
 
 
@@ -23,8 +23,10 @@ typedef struct settings_struct {
   char salt2[17];
   char salt2[17];
   char dcc_prefix[17];
   char dcc_prefix[17];
   char features[17];
   char features[17];
+  char pack_padding[7];
   /* -- DYNAMIC -- */
   /* -- DYNAMIC -- */
   char dynamic_initialized[17];
   char dynamic_initialized[17];
+  char conf_hubs[513];
   char bots[1025];
   char bots[1025];
   char uid[17];
   char uid[17];
   char autocron[17];         /* should the bot auto crontab itself? */
   char autocron[17];         /* should the bot auto crontab itself? */
@@ -34,7 +36,7 @@ typedef struct settings_struct {
   char portmin[17];       /* for hubs, the reserved port range for incoming connections */
   char portmin[17];       /* for hubs, the reserved port range for incoming connections */
   char portmax[17];       /* for hubs, the reserved port range for incoming connections */
   char portmax[17];       /* for hubs, the reserved port range for incoming connections */
   /* -- PADDING -- */
   /* -- PADDING -- */
-  char padding[14];        // (16 - (sizeof(settings_t) % 16)) % 16]
+  char padding[6];        // (16 - (sizeof(settings_t) % 16)) % 16]
 } settings_t;
 } settings_t;
 
 
 #define SALT1 {s1_1[0],s1_1[1],s1_5[0],s1_5[1],s1_8[0],s1_8[1],s1_4[0],s1_9[1],s1_2[0],s1_13[0],s1_6[0],s1_6[1],s1_7[0],s1_7[1],s1_3[0],s1_13[1],s1_16[1],s1_4[1],s1_15[0],s1_10[1],s1_14[0],s1_14[1],s1_12[0],s1_12[1],s1_2[1],s1_3[1],s1_11[0],s1_11[1],s1_10[0],s1_15[1],s1_16[0],s1_9[0],'\0'}
 #define SALT1 {s1_1[0],s1_1[1],s1_5[0],s1_5[1],s1_8[0],s1_8[1],s1_4[0],s1_9[1],s1_2[0],s1_13[0],s1_6[0],s1_6[1],s1_7[0],s1_7[1],s1_3[0],s1_13[1],s1_16[1],s1_4[1],s1_15[0],s1_10[1],s1_14[0],s1_14[1],s1_12[0],s1_12[1],s1_2[1],s1_3[1],s1_11[0],s1_11[1],s1_10[0],s1_15[1],s1_16[0],s1_9[0],'\0'}
@@ -47,14 +49,16 @@ extern char s1_3[3],s1_2[3],s1_1[3],s2_7[3],s1_9[3],s1_13[3],s1_14[3],s2_2[3],s1
 
 
 #define SIZE_PACK sizeof(settings.hash) + sizeof(settings.packname) + sizeof(settings.shellhash) + \
 #define SIZE_PACK sizeof(settings.hash) + sizeof(settings.packname) + sizeof(settings.shellhash) + \
 sizeof(settings.owners) + sizeof(settings.hubs) + \
 sizeof(settings.owners) + sizeof(settings.hubs) + \
-sizeof(settings.salt1) + sizeof(settings.salt2) + sizeof(settings.dcc_prefix) + sizeof(settings.features)
+sizeof(settings.salt1) + sizeof(settings.salt2) + sizeof(settings.dcc_prefix) + sizeof(settings.features) + sizeof(settings.pack_padding)
 
 
-#define SIZE_CONF sizeof(settings.dynamic_initialized) + sizeof(settings.bots) + sizeof(settings.uid) + \
+#define SIZE_CONF sizeof(settings.dynamic_initialized) + sizeof(settings.conf_hubs) + sizeof(settings.bots) + sizeof(settings.uid) + \
 sizeof(settings.autocron) + \
 sizeof(settings.autocron) + \
 sizeof(settings.username) + sizeof(settings.homedir) + \
 sizeof(settings.username) + sizeof(settings.homedir) + \
 sizeof(settings.portmin) + sizeof(settings.portmin) + sizeof(settings.datadir)
 sizeof(settings.portmin) + sizeof(settings.portmin) + sizeof(settings.datadir)
 
 
+#define SIZE_PAD_ALIGN PREFIXLEN
 #define SIZE_PAD sizeof(settings.padding)
 #define SIZE_PAD sizeof(settings.padding)
+#define SIZE_PAD_PACK sizeof(settings.pack_padding)
 
 
 #define SIZE_SETTINGS sizeof(settings_t)
 #define SIZE_SETTINGS sizeof(settings_t)
 
 

+ 8 - 2
src/userrec.c

@@ -476,7 +476,13 @@ void stream_writeuserfile(bd::Stream& stream, const struct userrec *bu, bool old
  */
  */
 int write_userfile(int idx)
 int write_userfile(int idx)
 {
 {
-  if (userlist == NULL || !conf.bot->hub)
+  if (!userlist) {
+    return 1;
+  }
+
+  conf_update_hubs(userlist);
+
+  if (!conf.bot->hub)
     return 1;			/* No point in saving userfile */
     return 1;			/* No point in saving userfile */
 
 
   if (idx >= 0)
   if (idx >= 0)
@@ -539,7 +545,7 @@ int change_handle(struct userrec *u, char *newh)
   return 1;
   return 1;
 }
 }
 
 
-struct userrec *adduser(struct userrec *bu, char *handle, char *host, char *pass, flag_t flags, int bot)
+struct userrec *adduser(struct userrec *bu, const char *handle, char *host, char *pass, flag_t flags, int bot)
 {
 {
   struct userrec *u = NULL, *x = NULL;
   struct userrec *u = NULL, *x = NULL;
   int oldshare = noshare;
   int oldshare = noshare;

+ 1 - 1
src/userrec.h

@@ -5,7 +5,7 @@ namespace bd {
   class Stream;
   class Stream;
 }
 }
 
 
-struct userrec *adduser(struct userrec *, char *, char *, char *, flag_t, int);
+struct userrec *adduser(struct userrec *, const char *, char *, char *, flag_t, int);
 void addhost_by_handle(char *, char *);
 void addhost_by_handle(char *, char *);
 void clear_masks(struct maskrec *);
 void clear_masks(struct maskrec *);
 void clear_userlist(struct userrec *);
 void clear_userlist(struct userrec *);