🢀︎ unrealircd :: f805315


commit f8053150a313892095ec8ae866bca85cf9c950e5
Author: acidvegas <acid.vegas@acid.vegas>
Date:   Mon Jan 27 02:53:11 2020 -0500

    Updated to 5.0.2 + added some lines for future tor support w/ sasl forced

diff --git a/.CHANGES.NEW b/.CHANGES.NEW
deleted file mode 100644
index 1e2f0eb..0000000
--- a/.CHANGES.NEW
+++ /dev/null
@@ -1,20 +0,0 @@
-
- _   _                      _ ___________  _____     _
-| | | |                    | |_   _| ___ \/  __ \   | |
-| | | |_ __  _ __ ___  __ _| | | | | |_/ /| /  \/ __| |
-| | | | '_ \| '__/ _ \/  _ | | | | |    / | |    /  _ |
-| |_| | | | | | |  __/ (_| | |_| |_| |\ \ | \__/\ (_| |
- \___/|_| |_|_|  \___|\__,_|_|\___/\_| \_| \____/\__,_|
-
-                               Configuration Program
-                                for UnrealIRCd 4.2.4
-                                    
-This program will help you to compile your IRC server, and ask you
-questions regarding the compile-time settings of it during the process. 
-regarding the setup of it, during the process.
-
-A short installation guide is available online at:
-https://www.unrealircd.org/docs/Installing_from_source
-
-Full documentation is available at:
-https://www.unrealircd.org/docs/UnrealIRCd_4_documentation
diff --git a/.gitignore b/.gitignore
index 12c12e7..46780c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,9 +6,7 @@ conftest.*
 config.settings
 extras/pcre2*
 extras/c-ares*
-extras/regexp*
 config.status
-extras/tre*
 extras/ircdcron/ircd.cron
 extras/ircdcron/ircdchk
 src/modules/snomasks/Makefile
@@ -33,7 +31,7 @@ tags
 server.cert.pem
 server.key.pem
 server.req.pem
-ssl.rnd
+tls.rnd
 
 # Ignores for platform stuff
 .DS_Store
@@ -72,3 +70,6 @@ xcuserdata
 src/macosx/build/
 DerivedData
 src/macosx/pods/
+
+# Doxygen generated files
+doc/doxygen/
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 3cda009..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "extras/tests/ircfly"]
-	path = extras/tests/ircfly
-	url = https://github.com/unrealircd/ircfly.git
-[submodule "extras/tests/functional-tests"]
-	path = extras/tests/functional-tests
-	url = https://github.com/unrealircd/unrealircd-tests.git
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5860f17..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-language: c
-os: linux
-dist: xenial
-compiler:
-  - clang
-  - gcc
-script: extras/build-tests/nix/build $BUILDCONFIG
-env:
-  - BUILDCONFIG=""
-  - BUILDCONFIG="system-cares"
-  - BUILDCONFIG="system-cares system-curl"
-  - BUILDCONFIG="local-curl"
-matrix:
-  include:
-  - os: osx
-    env: BUILDCONFIG=""
-  - os: osx
-    env: BUILDCONFIG="system-cares"
-  - os: osx
-    env: BUILDCONFIG="system-cares system-curl"
-  - os: osx
-    env: BUILDCONFIG="local-curl"
-  - env: BUILDCONFIG="libressl-27"
-  - env: BUILDCONFIG="libressl-28"
-  - env: BUILDCONFIG="libressl-29"
-  - env: BUILDCONFIG="openssl-102"
-  - env: BUILDCONFIG="openssl-110"
-  - env: BUILDCONFIG="openssl-111"
diff --git a/Config b/Config
index 0969736..1e9718e 100755
--- a/Config
+++ b/Config
@@ -237,12 +237,12 @@ UNREALCWD="`pwd`"
 BASEPATH="$HOME/unrealircd"
 DEFPERM="0600"
 SSLDIR=""
-NICKNAMEHISTORYLENGTH="2000"
+NICKNAMEHISTORYLENGTH="100"
 MAXCONNECTIONS_REQUEST="auto"
-REMOTEINC=""
+REMOTEINC="1"
 CURLDIR=""
-PREFIXAQ="1"
-SHOWLISTMODES="1"
+PREFIXAQ="0"
+SHOWLISTMODES="0"
 NOOPEROVERRIDE=""
 OPEROVERRIDEVERIFY=""
 GENCERTIFICATE="1"
@@ -326,7 +326,7 @@ echo ""
 
 if [ -z "$NOCACHE" ] ; then
 	# This needs to be updated each release so auto-upgrading works for settings, modules, etc!!:
-	UNREALRELEASES="unrealircd-5.0.0-rc2 unrealircd-5.0.0-rc1"
+	UNREALRELEASES="unrealircd-5.0.1 unrealircd-5.0.0 unrealircd-5.0.0-rc2 unrealircd-5.0.0-rc1"
 	if [ -f "config.settings" ]; then
 		. ./config.settings
 	else
@@ -361,6 +361,11 @@ if [ -z "$NOCACHE" ] ; then
 			IMPORTEDSETTINGS=""
 		fi
 		if [ "$IMPORTEDSETTINGS" != "" ]; then
+			if [ -d $IMPORTEDSETTINGS/conf ]; then
+				echo "ERROR: Directory $IMPORTEDSETTINGS is an INSTALLATION directory (eg /home/irc/unrealircd)."
+				echo "This question was about a SOURCE directory (eg /home/irc/unrealircd-5.0.0)."
+				exit
+			fi
 			if [ ! -f $IMPORTEDSETTINGS/config.settings ]; then
 				echo "Directory $IMPORTEDSETTINGS does not exist or does not contain a config.settings file"
 				exit
diff --git a/configure b/configure
index 14ccd56..f2d3a78 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for unrealircd 5.0.0.
+# Generated by GNU Autoconf 2.69 for unrealircd 5.0.2.
 #
 # Report bugs to <https://bugs.unrealircd.org/>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='unrealircd'
 PACKAGE_TARNAME='unrealircd'
-PACKAGE_VERSION='5.0.0'
-PACKAGE_STRING='unrealircd 5.0.0'
+PACKAGE_VERSION='5.0.2'
+PACKAGE_STRING='unrealircd 5.0.2'
 PACKAGE_BUGREPORT='https://bugs.unrealircd.org/'
 PACKAGE_URL='https://unrealircd.org/'
 
@@ -1325,7 +1325,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures unrealircd 5.0.0 to adapt to many kinds of systems.
+\`configure' configures unrealircd 5.0.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1391,7 +1391,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of unrealircd 5.0.0:";;
+     short | recursive ) echo "Configuration of unrealircd 5.0.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1544,7 +1544,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-unrealircd configure 5.0.0
+unrealircd configure 5.0.2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1913,7 +1913,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by unrealircd $as_me 5.0.0, which was
+It was created by unrealircd $as_me 5.0.2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2321,7 +2321,7 @@ _ACEOF
 
 
 # Minor version number (e.g.: Z in X.Y.Z)
-UNREAL_VERSION_MINOR="0"
+UNREAL_VERSION_MINOR="2"
 
 cat >>confdefs.h <<_ACEOF
 #define UNREAL_VERSION_MINOR $UNREAL_VERSION_MINOR
@@ -8249,7 +8249,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by unrealircd $as_me 5.0.0, which was
+This file was extended by unrealircd $as_me 5.0.2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -8312,7 +8312,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-unrealircd config.status 5.0.0
+unrealircd config.status 5.0.2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 1d10fed..6dc2be2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7,7 +7,7 @@ dnl src/windows/unrealinst.iss
 dnl doc/Config.header
 dnl src/version.c.SH
 
-AC_INIT([unrealircd], [5.0.0], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
+AC_INIT([unrealircd], [5.0.2], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
 AC_CONFIG_SRCDIR([src/ircd.c])
 AC_CONFIG_HEADER([include/setup.h])
 AC_CONFIG_AUX_DIR([autoconf])
@@ -34,7 +34,7 @@ UNREAL_VERSION_MAJOR=["0"]
 AC_DEFINE_UNQUOTED([UNREAL_VERSION_MAJOR], [$UNREAL_VERSION_MAJOR], [Major version number (e.g.: Y for X.Y.Z)])
 
 # Minor version number (e.g.: Z in X.Y.Z)
-UNREAL_VERSION_MINOR=["0"]
+UNREAL_VERSION_MINOR=["2"]
 AC_DEFINE_UNQUOTED([UNREAL_VERSION_MINOR], [$UNREAL_VERSION_MINOR], [Minor version number (e.g.: Z for X.Y.Z)])
 
 # The version suffix such as a beta marker or release candidate
diff --git a/doc/Config.header b/doc/Config.header
index 28d5c87..5f42397 100644
--- a/doc/Config.header
+++ b/doc/Config.header
@@ -7,7 +7,7 @@
  \___/|_| |_|_|  \___|\__,_|_|\___/\_| \_| \____/\__,_|
 
                                Configuration Program
-                                for UnrealIRCd 5.0.0
+                                for UnrealIRCd 5.0.2
                                     
 This program will help you to compile your IRC server, and ask you
 questions regarding the compile-time settings of it during the process. 
diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md
index 85a56c0..8dc86f3 100644
--- a/doc/RELEASE-NOTES.md
+++ b/doc/RELEASE-NOTES.md
@@ -1,6 +1,73 @@
-UnrealIRCd 5.0.0 Release Notes
+UnrealIRCd 5.0.2 Release Notes
 ===============================
 
+UnrealIRCd 5.0.2
+-----------------
+
+Fixes:
+* Halfop users are not synced correctly, resulting in missing users across links.
+* [Channel history](https://www.unrealircd.org/docs/Channel_history) used
+incorrect time internally, resulting in messages expiring too soon.
+The syntax is now really ```/MODE #chan +H lines:time-in-minutes```.
+To make clear that the time is in minutes, an 'm' will be added
+automatically by the server (eg ```+H 15:1440m```).
+* Documentation: to exempt someone from gline via /ELINE you have to use type 'G', not 'g'.
+  Similarly, to exempt from spamfilter, use type 'F' and not 'f'.
+* Exempting IPs from throttling via [except throttle](https://www.unrealircd.org/docs/Except_throttle_block) was not working.
+* Unable to customize [set::tls::outdated-protocols](https://www.unrealircd.org/docs/Set_block#set::ssl::outdated-protocols)
+  and [set::tls::outdated-ciphers](https://www.unrealircd.org/docs/Set_block#set::ssl::outdated-ciphers).
+* Specifying multiple channels did not work in [set::auto-join](https://www.unrealircd.org/docs/Set_block#set::auto-join),
+  [set::oper-auto-join](https://www.unrealircd.org/docs/Set_block#set::oper-auto-join) and
+  [tld::channel](https://www.unrealircd.org/docs/Tld_block).
+
+Enhancements:
+* [Extended server bans](https://www.unrealircd.org/docs/Extended_server_bans) in *LINE and /ELINE allow
+  you to ban or exempt users on criteria other than host/IP. These use a
+  similar syntax to extended bans. Currently supported are ~a, ~S and ~r. Examples:
+  * ```/ELINE ~a:TrustedAccount kG 0 This user can bypass kline/gline when using SASL```
+  * ```/ELINE ~S:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef kGF 0 Trusted user with this certificate fingerprint```
+  * ```/GLINE ~r:*some*stupid*real*name*```
+  * These can also be used in the configuration file, eg: ```except ban { mask ~S:11223344etc; type all; };```
+* New options that may not be used much, but can be useful on specific networks:
+  * The IRCd may add automatic bans, for example due to a blacklist hit,
+    a spamfilter hit, or because of antirandom or antimixedutf8. The new
+    option [set::automatic-ban-target](https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target) specifies on *what* the ban should
+    be placed. The default is *ip*. Other options are: userip, host, userhost, account, certfp.
+  * Similarly, an oper may type ```/GLINE nickname```. The new option
+    [set::manual-ban-target](https://www.unrealircd.org/docs/Set_block#set::manual-ban-target) specifies on what the ban should be placed.
+    By default this is *host* (fallback to *ip*).
+* New options to exempt webirc users: [set::connthrottle::webirc-bypass](https://www.unrealircd.org/docs/Connthrottle),
+  [set::restrict-commands::name-of-command::exempt-webirc](https://www.unrealircd.org/docs/Set_block#set::restrict-commands).
+
+Are you upgrading from UnrealIRCd 4.x to UnrealIRCd 5? If so,
+then check out the *UnrealIRCd 5* release notes further down. At the
+very least, check out [Upgrading from 4.x](https://www.unrealircd.org/docs/Upgrading_from_4.x).
+
+UnrealIRCd 5.0.1
+-----------------
+
+Fixes:
+* IRCd may hang in rare circumstances
+* Windows: fix repeated "ERROR renaming 'data/reputation.db.tmp'" warnings
+* Antirandom and blacklist did not deal properly with 'warn' actions
+* [Authprompt](https://www.unrealircd.org/docs/Authentication#How_it_looks_like)
+  did not always work properly
+* Line numbers were incorrect in config file warnings/errors when using @if or @define
+
+Enhancements:
+* New /ELINE exception type 'm' to bypass allow::maxperip.
+  Or in the configuration file: ```except ban { mask 203.0.113.0/24; type maxperip; };```
+* IRCOps can override MLOCK restrictions when services are down,
+  if they have the channel:override:mlock operclass permission,
+  such as opers which use the operclass 'netadmin-with-override'.
+
+Other:
+* Gottem and k4be have [uploaded their 3rd party modules](https://modules.unrealircd.org/)
+  to unrealircd-contrib so *NIX users can now easily install them using the new
+  [Module manager](https://www.unrealircd.org/docs/Module_manager)
+
+UnrealIRCd 5
+-------------
 After more than 6 months of hard work, UnrealIRCd 5 is now our new "stable" branch.
 In particular I would like to thank Gottem and 'i' for their source code
 contributions and PeGaSuS and westor for testing releases.
@@ -9,9 +76,9 @@ When we transitioned from 3.2.x to 4.0.0 there were 175,000 lines of source code
 added/removed during 3 years of development. This time it was 120,000 lines in
 only 6 months, a major effort!
 
-If you are upgrading from 4.x to 5.x, then it would be wise to read
+**If you are upgrading from 4.x to 5.x, then it would be wise to read
 [Upgrading from 4.x](https://www.unrealircd.org/docs/Upgrading_from_4.x).
-In any case, be sure to upgrade your services package first! (if you use any)
+In any case, be sure to upgrade your services package first! (if you use any)**
 
 UnrealIRCd 5 is compatible with the following services:
 * [anope](https://www.anope.org/) (version 2.0.7 or higher) -
@@ -239,6 +306,7 @@ Changed
 * IRCOps now need to use SSL/TLS in order to oper up, as the
   [set::plaintext-policy::oper](https://www.unrealircd.org/docs/Set_block#set::plaintext-policy) default setting is now 'deny'.
   Similarly, [set::outdated-tls-policy::oper](https://www.unrealircd.org/docs/Set_block#set::outdated-tls-policy) is now also 'deny'.
+  You can change this, if you want, but it is not recommended.
 * [set::outdated-tls-policy::server](https://www.unrealircd.org/docs/Set_block#set::outdated-tls-policy) is now 'deny' as well, since all
   servers should use reasonable SSL/TLS protocols and ciphers.
 * The default generated certificated has been changed from RSA 4096 bits
@@ -282,6 +350,8 @@ Removed
   4.x then they should be able to link to 5.x as well. More information
   about this change and why it was done
   [can be found here](https://www.unrealircd.org/docs/FAQ#old-server-protocol).
+* Connecting with a server password will no longer send that password
+  to NickServ. Use [SASL](https://www.unrealircd.org/docs/SASL) instead!
 * Extended ban ~R (registered nick): this was the old method to match
   registered users. Everyone should use ~a (services account) instead.
 * The old TRE **posix** regex method has been removed because the TRE
@@ -360,6 +430,23 @@ Developers
 * The parameters in several hooks have changed. Many now have an
   extra ```MessageTag *mtags``` parameter. Sometimes there are other changes
   as well, for example ```HOOKTYPE_CHANMSG``` now has 4 extra parameters.
+* You can call do_cmd() with NULL mtags. Usually this is the correct way.
+* If you used ```HOOKTYPE_PRE_USERMSG``` to block a message then you
+  should now use ```HOOKTYPE_CAN_SEND_TO_USER```. Similarly, the hook
+  ```HOOKTYPE_CAN_SEND``` which deals with channels is now called
+  ```HOOKTYPE_CAN_SEND_TO_CHANNEL```. Some other remarks:
+  * You CANNOT use HOOKTYPE_PRE_USERMSG anymore.
+  * The hooks require you to set an error message if you return HOOK_DENY.
+  * You should not send an error message yourself from these hooks.
+    In other words: do not use sendnumeric(). This is done by the
+    hook caller, based on the error message you return.
+  * Thanks to this, all rejecting of user messages now use generic
+    numeric 531 and all rejecting of channel messages use numeric 404.
+    See also under *Client protocol* later in this document.
+* If you use CommandOverrideAddEx() to specify a priority value (rare)
+  then be aware that in 5.0.1 we now use the 4.0.x behavior again to
+  match the same style of priorities in hooks: overrides with the
+  lowest priority are run first.
 * If you ever send a timestamp in a printf-like function, such as
   in ```sendto_server()```, then be sure to use ```%lld``` and cast the timestamp
   to *long long* so that it is compatible with both *NIX and Windows.
@@ -411,7 +498,7 @@ Server protocol
   error. For the other options, support is *assumed*, no warning or
   error is shown when you lack support. These are options that most,
   if not all, services support since UnrealIRCd 4.x so it shouldn't be
-  a problem. More information (here)[https://www.unrealircd.org/docs/FAQ#old-server-protocol]
+  a problem. More information [here](https://www.unrealircd.org/docs/FAQ#old-server-protocol)
 * ```PROTOCTL MTAGS``` indicates that the server is capable of handling
   message tags and that the server can cope with 4K lines. (Note that
   the ordinary non-message-tag part is still limited to 512 bytes).
@@ -420,7 +507,6 @@ Server protocol
 
 Client protocol
 ----------------
-TODO: expand with other new things / changes
 * Support for message tags and other IRCv3 features. See the IRCv3
   specifications for more details.
 * When a message is blocked, for whatever reason, we now use a generic
@@ -438,3 +524,8 @@ TODO: expand with other new things / changes
 * The 470 numeric, which is sent on /JOIN #channel redirect to #redirect
   now uses the following format:
   ```:server 470 yournick #channel #redirect :[Link] Cannot join channel...etc..```
+* Clients are recommended to implement and enable the
+  [server-time](https://ircv3.net/specs/extensions/server-time-3.2)
+  extension by default. When enabled, channel history is played back
+  on-join (if any) when the channel has channel mode +H.
+  Otherwise your users will not see channel history.
diff --git a/doc/conf/aliases.conf b/doc/conf/aliases.conf
index 9dccdad..ca96d27 100644
--- a/doc/conf/aliases.conf
+++ b/doc/conf/aliases.conf
@@ -22,8 +22,8 @@ alias chanserv { type services; }
 alias cs { target chanserv; type services; }
 alias hostserv { type services; }
 alias hs { target hostserv; type services; }
-alias memoserv { type services; spamfilter yes; }
-alias ms { target memoserv; type services; spamfilter yes; }
+#alias memoserv { type services; spamfilter yes; }
+#alias ms { target memoserv; type services; spamfilter yes; }
 alias nickserv { type services; }
 alias ns { target nickserv; type services; }
 alias operserv { type services; }
diff --git a/doc/conf/help.conf b/doc/conf/help.conf
index 59050b0..9dd2f15 100644
--- a/doc/conf/help.conf
+++ b/doc/conf/help.conf
@@ -51,6 +51,8 @@ help Opercmds {
 	" Use /HELPOP <command name> to get more information about";
 	" a specific command.";
 	" -";
+	" See also https://www.unrealircd.org/docs/IRCOp_guide";
+	" -";
 	" ==-------------------------oOo-------------------------==";
 	"                 DNS                             SETIDENT";
 	" ADDMOTD         GLINE           OPER            SHUN";
@@ -234,7 +236,7 @@ help ExtBans {
 	" These bantypes introduce new criteria which can be used:";
 	" ==-Type--------Name---------------------------Explanation-----------------------==";
 	"         |               | If a user is logged in to services with this account    ";
-	"   ~a    |   account     | name, then this ban will match.                         ";
+	"    ~a   |    account    | name, then this ban will match.                         ";
 	"         |               | Example: +e ~a:Name                                     ";
 	"-----------------------------------------------------------------------------------";
 	"         |               | If the user is in this channel then (s)he is unable to  ";
@@ -331,32 +333,70 @@ help Whois {
 
 help Who {
 	" Retrieves information about users";
+	" In its most simple form the syntax is 'WHO #channel' or 'WHO nickname'";
+	" However we also support the extended who syntax (WHOX):";
 	" -";
 	" Syntax:";
-	" /WHO [+|-][acghimnsuMRI] [args]";
-	" Flags are specified like channel modes, the flags cgmnsu all have arguments";
-	" Flags are set to a positive check by +, a negative check by -";
-	" The flags available:";
-	" Flag a: user is away";
-	" Flag c <channel>: user is on <channel>, no wildcards accepted";
-	" Flag g <gcos/realname>: user has string <gcos> in his/her GCOS,";
-	" wildcards accepted, oper only";
-	" Flag h <host>: user has string <host> in his/her hostname, wildcards are accepted";
-	" Flag i <ip>: user has string <ip> in his/her IP address";
-	" Flag m <usermodes>: user has <usermodes> set, only o/C/A/a/N for nonopers";
-	" Flag n <nick>: user has string <nick> in his/her nickname, wildcards accepted";
-	" Flag s <server>: user is on server <server>, wildcards not accepted";
-	" Flag u <user>: user has string <user> in his/her username, wildcards accepted";
-	" Behavior flags:";
-	" Flag M: check for user in channels I am a member of";
-	" Flag R: show users' real hostnames";
-	" Flag I: show users' IP addresses";
-	" -";
-	" For backwards compatibility, /who 0 o still shows +o users";
-	" Example: WHO +m o";
+	" /WHO <mask> [options]";
+	" /WHO <mask> [options [mask2]]";
+	" -";
+	" The mask can contain wildcards such as * and ?";
+	" -";
+	" The options consist of [<flags>][%[<fields>[,<querytype>]]]";
+	" Where:";
+	" The <flags> define where to SEARCH on, and may contain one or more letters:";
+	"  n: nick name";
+	"  u: user name (ident)";
+	"  h: host name [*]";
+	"  i: IP address [*]";
+	"  s: server name [*]";
+	"  r: real name (gecos)";
+	"  a: account name (services account)";
+	"  m: user modes (the mask contains for example +z or -z) [*]";
+	"  R: sets output to show real hostnames [*]";
+	"  I: sets output to show IP addresses [*]";
+	"  Items marked with [*] mean that IRCOp privileges are (possibly) required.";
+	" The <fields> decide which fields appear in the WHO output";
+	" (note that any fields are always outputed in this order:)";
+	"  t: querytype (the <querytype> parameter that was provided in <options>)";
+	"  c: first channel name the user is on";
+	"  u: user name (ident)";
+	"  i: IP Address [*]";
+	"  h: hostname [*]";
+	"  H: real hostname [*]";
+	"  s: server name";
+	"  n: nick name";
+	"  f: status flags (explained later)";
+	"  m: user modes [*]";
+	"  d: number of server hops (eg: 0 means user is on same server)";
+	"  l: seconds idle (only for users on the same server as you, otherwise 0)";
+	"  a: account name (services account)";
+	"  o: operclass name [*]";
+	"  r: real name (gecos)";
+	"  Items marked with [*] mean that IRCOp privileges are (possibly) required.";
+	" And finally, the <querytype> is a word that clients can use to tag";
+	" WHO requests so they can easily see which WHO response belongs to";
+	" what WHO request.";
+	" -";
+	" Examples of simple WHO requests:";
+	"  WHO #channel        - To list all users in a channel";
+	"  WHO 1.2.3.4         - To show all users with this IP address (IRCOp only)";
+	" Examples of WHOX requests:";
+	"  WHO Servic* n       - Show all users which name starts with Servic";
+	"                        (Only IRCOps are likely to see the full list)";
+	"  WHO #chan cI        - Show all users in channel #chan with their";
+	"                        IP address instead of hostname (IRCOp only)";
+	"  WHO z m             - Show all users with user mode z set, that is:";
+	"                        all users on SSL/TLS. (IRCOp only command)";
+	"  WHO -z m            - Show all insecure users, without umode z.";
+	"                        (IRCOp only command)";
+	" Examples of WHOX requests using output modifiers:";
+	"  WHO #test %acfhnru  - Show all users in the channel #test and show";
+	"                        various fields, among which 'a' (services";
+	"                        account) is not displayed by normal WHO.";
 	" -";
 	" Status flags:";
-	" The who command shows several flags in the returned result to indicate";
+	" The WHO command shows several flags in the returned result to indicate";
 	" different information about the user. These flags are explained below:";
 	" G - User is /away (gone)";
 	" H - User is not /away (here)";
@@ -370,7 +410,7 @@ help Who {
 	" % - User is a Halfop (+h)";
 	" + - User is Voiced (+v)";
 	" ! - User is +H and you are an IRC Operator";
-	" ? - User is only visible because you are an IRC Operator";
+	"-";
 };
 
 help Whowas {
diff --git a/doc/conf/opers.conf b/doc/conf/opers.conf
index 1d5497f..ba0100b 100644
--- a/doc/conf/opers.conf
+++ b/doc/conf/opers.conf
@@ -156,10 +156,4 @@ operclass fuckyou {
 		}
 		sacmd { sajoin; }
 	}
-}
-
-operclass spider {
-	permissions {
-		sacmd { sajoin; }
-	}
 }
\ No newline at end of file
diff --git a/doc/conf/unrealircd.hub.conf b/doc/conf/unrealircd.hub.conf
index 64e85c6..9614605 100644
--- a/doc/conf/unrealircd.hub.conf
+++ b/doc/conf/unrealircd.hub.conf
@@ -53,4 +53,5 @@ set {
 	hiddenhost-prefix "SUPER";
 	default-bantime 1d;
 	modef-default-unsettime 5;
+	manual-ban-target ip;
 }
\ No newline at end of file
diff --git a/doc/conf/unrealircd.link.conf b/doc/conf/unrealircd.link.conf
index 1cf2703..a93bb2b 100644
--- a/doc/conf/unrealircd.link.conf
+++ b/doc/conf/unrealircd.link.conf
@@ -1,9 +1,9 @@
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/aliases.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/badwords.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/except.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/help.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/ircd.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/modules.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/opers.conf";
-include "http://USERNAME:PASSWORD@HOSTNAME:PORT/spamfilter.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/aliases.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/badwords.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/except.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/help.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/ircd.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/modules.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/opers.conf";
+include "https://USERNAME:PASSWORD@HOSTNAME:PORT/spamfilter.conf";
 me { name "example.supernets.org"; info "SuperNETS IRC Network"; sid XXX; }
\ No newline at end of file
diff --git a/doc/conf/unrealircd.remote.conf b/doc/conf/unrealircd.remote.conf
index 1d0723b..19ce206 100644
--- a/doc/conf/unrealircd.remote.conf
+++ b/doc/conf/unrealircd.remote.conf
@@ -9,6 +9,11 @@ listen { ip *; port 6667;      options { clientsonly;      } }
 listen { ip *; port 6697;      options { clientsonly; tls; } }
 listen { ip *; port REDACTED;  options { serversonly; tls; } }
 
+#require authentication {
+#	mask *@*;
+#	reason "Tor users must authenticate via SASL!";
+#}
+
 link irc.supernets.org {
 	incoming { mask REDACTED; }
 	outgoing {
@@ -154,6 +159,13 @@ set {
 			max-storage-per-channel { lines 1000; time 1w; }
 		}
 	}
+	manual-ban-target ip;
+	#authentication-prompt {
+	#	enabled yes;
+	#	message "Tor users are required to authenticate via SASL or now by typing /QUOTE AUTH nick:password";
+	#	fail-message "8,4   E N T E R   T H E   V O I D   ";
+	#}
+	#set { handshake-timeout 60s; }
 }
 
 hideserver {
diff --git a/extras/build-tests/nix/build b/extras/build-tests/nix/build
index 6f1c76b..c589458 100755
--- a/extras/build-tests/nix/build
+++ b/extras/build-tests/nix/build
@@ -22,6 +22,12 @@ export CPPFLAGS="-DFAKELAG_CONFIGURABLE -DNOREMOVETMP"
 # !! temporary use this:
 cp extras/build-tests/nix/configs/default ./config.settings
 
+# Debian 8 workaround:
+if lsb_release -av 2>&1|egrep 'Debian.*jessie'; then
+	echo "Disabling ASan due to false positives on deb8"
+	echo 'EXTRAPARA="--enable-werror --disable-asan"' >>config.settings
+fi
+
 # Read config.settings, this makes a couple of variables available to us.
 . ./config.settings
 if [ "$SSLDIR" != "" ]; then
diff --git a/extras/build-tests/nix/run-tests b/extras/build-tests/nix/run-tests
index 1e7bb4f..5e9808e 100755
--- a/extras/build-tests/nix/run-tests
+++ b/extras/build-tests/nix/run-tests
@@ -24,8 +24,14 @@ git clone -q https://github.com/unrealircd/unrealircd-tests.git
 cd unrealircd-tests
 
 # Run the test framework, testing both services:
-./run -services anope || exit 1
-./run -services atheme || exit 1
+if uname -a|grep -q FreeBSD; then
+	# FreeBSD runs without services since they fail mysteriously:
+	./run -services none || exit 1
+else
+	# Linux tests both with anope and atheme services:
+	./run -services anope || exit 1
+	./run -services atheme || exit 1
+fi
 
 # Do cipherscan test at the end
 if [[ "$OSTYPE" != "freebsd"* ]]; then
diff --git a/extras/doxygen/Doxyfile b/extras/doxygen/Doxyfile
index b067c71..96283bc 100644
--- a/extras/doxygen/Doxyfile
+++ b/extras/doxygen/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME           = "UnrealIRCd"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 5.0.0
+PROJECT_NUMBER         = 5.0.2
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/include/dynconf.h b/include/dynconf.h
index 55ff05e..07f9698 100644
--- a/include/dynconf.h
+++ b/include/dynconf.h
@@ -53,6 +53,8 @@ typedef enum BroadcastChannelMessagesOption { BROADCAST_CHANNEL_MESSAGES_AUTO=1,
 
 typedef enum AllowedChannelChars { ALLOWED_CHANNELCHARS_ANY=1, ALLOWED_CHANNELCHARS_ASCII=2, ALLOWED_CHANNELCHARS_UTF8=3 } AllowedChannelChars;
 
+typedef enum BanTarget { BAN_TARGET_IP=1, BAN_TARGET_USERIP=2, BAN_TARGET_HOST=3, BAN_TARGET_USERHOST=4, BAN_TARGET_ACCOUNT=5, BAN_TARGET_CERTFP=6 } BanTarget;
+
 /** The set { } block configuration */
 typedef struct Configuration Configuration;
 struct Configuration {
@@ -156,7 +158,8 @@ struct Configuration {
 	long handshake_timeout;
 	long sasl_timeout;
 	long handshake_delay;
-	int ban_include_username;
+	BanTarget automatic_ban_target;
+	BanTarget manual_ban_target;
 	char *reject_message_too_many_connections;
 	char *reject_message_server_full;
 	char *reject_message_unauthorized;
diff --git a/include/h.h b/include/h.h
index 021c015..d2b39f2 100644
--- a/include/h.h
+++ b/include/h.h
@@ -99,6 +99,7 @@ extern void  module_loadall(void);
 extern long set_usermode(char *umode);
 extern char *get_usermode_string_raw(long umodes);
 extern ConfigFile *config_parse(char *filename, char *confdata);
+extern ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned int line_offset);
 extern void config_error(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2)));
 extern void config_warn(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2)));
 extern void config_error_missing(const char *filename, int line, const char *entry);
@@ -887,6 +888,7 @@ extern void send_cap_notify(int add, char *token);
 extern void sendbufto_one(Client *to, char *msg, unsigned int quick);
 extern MODVAR int current_serial;
 extern char *spki_fingerprint(Client *acptr);
+extern char *spki_fingerprint_ex(X509 *x509_cert);
 extern int is_module_loaded(char *name);
 extern void close_std_descriptors(void);
 extern void banned_client(Client *acptr, char *bantype, char *reason, int global, int noexit);
@@ -961,3 +963,4 @@ extern void unreal_del_quotes(char *i);
 extern char *unreal_add_quotes(char *str);
 extern int unreal_add_quotes_r(char *i, char *o, size_t len);
 extern void user_account_login(MessageTag *recv_mtags, Client *client);
+extern void link_generator(void);
diff --git a/include/modules.h b/include/modules.h
index 1d19a68..f26f6e1 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -340,17 +340,20 @@ typedef struct {
 
 /*** Extended bans ***/
 
+// TODO: These should be enums!
+
 #define EXBCHK_ACCESS		0 /* Check access */
 #define EXBCHK_ACCESS_ERR	1 /* Check access and send error */
 #define EXBCHK_PARAM		2 /* Check if the parameter is valid */
 
-#define EXBTYPE_BAN			0 /* a ban */
+#define EXBTYPE_BAN		0 /* a ban */
 #define EXBTYPE_EXCEPT		1 /* an except */
 #define EXBTYPE_INVEX		2 /* an invite exception */
+#define EXBTYPE_TKL		3 /* TKL or other generic matcher outside banning routines */
 
 #define EXTBANTABLESZ		32
 
-typedef enum ExtbanOptions { EXTBOPT_CHSVSMODE=0x1, EXTBOPT_ACTMODIFIER=0x2, EXTBOPT_NOSTACKCHILD=0x4, EXTBOPT_INVEX=0x8 } ExtbanOptions;
+typedef enum ExtbanOptions { EXTBOPT_CHSVSMODE=0x1, EXTBOPT_ACTMODIFIER=0x2, EXTBOPT_NOSTACKCHILD=0x4, EXTBOPT_INVEX=0x8, EXTBOPT_TKL=0x10 } ExtbanOptions;
 
 typedef struct {
 	/** extbans module */
@@ -949,7 +952,6 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va
 #define HOOKTYPE_SILENCED 47
 #define HOOKTYPE_POST_SERVER_CONNECT 48
 #define HOOKTYPE_RAWPACKET_IN 49
-#define HOOKTYPE_LOCAL_NICKPASS 50
 #define HOOKTYPE_PACKET 51
 #define HOOKTYPE_HANDSHAKE 52
 #define HOOKTYPE_AWAY 53
@@ -1080,7 +1082,6 @@ int hooktype_log(int flags, char *timebuf, char *buf);
 int hooktype_local_spamfilter(Client *acptr, char *str, char *str_in, int type, char *target, TKL *tkl);
 int hooktype_silenced(Client *client, Client *to, int notice);
 int hooktype_rawpacket_in(Client *client, char *readbuf, int *length);
-int hooktype_local_nickpass(Client *client, Client *nickserv);
 int hooktype_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length);
 int hooktype_handshake(Client *client);
 int hooktype_free_client(Client *acptr);
@@ -1171,7 +1172,6 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
         ((hooktype == HOOKTYPE_SILENCED) && !ValidateHook(hooktype_silenced, func)) || \
         ((hooktype == HOOKTYPE_POST_SERVER_CONNECT) && !ValidateHook(hooktype_post_server_connect, func)) || \
         ((hooktype == HOOKTYPE_RAWPACKET_IN) && !ValidateHook(hooktype_rawpacket_in, func)) || \
-        ((hooktype == HOOKTYPE_LOCAL_NICKPASS) && !ValidateHook(hooktype_local_nickpass, func)) || \
         ((hooktype == HOOKTYPE_PACKET) && !ValidateHook(hooktype_packet, func)) || \
         ((hooktype == HOOKTYPE_HANDSHAKE) && !ValidateHook(hooktype_handshake, func)) || \
         ((hooktype == HOOKTYPE_AWAY) && !ValidateHook(hooktype_away, func)) || \
diff --git a/include/struct.h b/include/struct.h
index 83986c3..845874f 100644
--- a/include/struct.h
+++ b/include/struct.h
@@ -741,6 +741,7 @@ struct LoopStruct {
 	unsigned tainted : 1;
 	Client *rehash_save_cptr, *rehash_save_client;
 	int rehash_save_sig;
+	void (*boot_function)();
 };
 
 /** Matching types for Match.type */
@@ -861,10 +862,11 @@ typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag
 /* these are not real tkl types, but only used for exceptions: */
 #define TKL_BLACKLIST		0x0001000
 #define TKL_CONNECT_FLOOD	0x0002000
-#define TKL_UNKNOWN_DATA_FLOOD	0x0004000
-#define TKL_ANTIRANDOM          0x0008000
-#define TKL_ANTIMIXEDUTF8       0x0010000
-#define TKL_BAN_VERSION         0x0020000
+#define TKL_MAXPERIP		0x0004000
+#define TKL_UNKNOWN_DATA_FLOOD	0x0008000
+#define TKL_ANTIRANDOM          0x0010000
+#define TKL_ANTIMIXEDUTF8       0x0020000
+#define TKL_BAN_VERSION         0x0040000
 
 #define TKLIsServerBan(tkl)		((tkl)->type & (TKL_KILL|TKL_ZAP|TKL_SHUN))
 #define TKLIsServerBanType(tpe)		((tpe) & (TKL_KILL|TKL_ZAP|TKL_SHUN))
@@ -1014,7 +1016,6 @@ struct RealCommand {
 	Module 			*owner;
 	RealCommand		*friend; /* cmd if token, token if cmd */
 	CommandOverride		*overriders;
-	CommandOverride		*overridetail;
 #ifdef DEBUGMODE
 	unsigned long 		lticks;
 	unsigned long 		rticks;
@@ -1893,15 +1894,20 @@ struct Ban {
 #define is_half_op(cptr,channel) (get_access(cptr,channel) & CHFL_HALFOP)
 /** Has voice (+v) */
 #define has_voice(cptr,channel) (get_access(cptr,channel) & CHFL_VOICE)
-#define CHFL_CHANOWNER	0x0040	/**< Channel owner (+q) */
-#define CHFL_CHANADMIN	0x0080	/**< Channel admin (+a) */
+/* Important:
+ * Do not blindly change the values of CHFL_* as they must match the
+ * ones in MODE_*. I already screwed this up twice. -- Syzop
+ * Obviously these should be decoupled in a code cleanup.
+ */
 #define	CHFL_CHANOP     0x0001	/**< Channel operator (+o) */
-#define CHFL_HALFOP	0x0100	/**< Channel halfop (+h) */
 #define	CHFL_VOICE      0x0002	/**< Voice (+v, can speak through bans and +m) */
 #define	CHFL_DEOPPED	0x0004	/**< De-oped by a server (temporary state) */
-#define	CHFL_BAN     	0x0100	/**< Channel ban (+b) - not a real flag, only used in sjoin.c */
-#define CHFL_EXCEPT	0x0200	/**< Channel except (+e) - not a real flag, only used in sjoin.c */
-#define CHFL_INVEX	0x0400  /**< Channel invite exception (+I) - not a real flag, only used in sjoin.c */
+#define CHFL_CHANOWNER	0x0040	/**< Channel owner (+q) */
+#define CHFL_CHANADMIN	0x0080	/**< Channel admin (+a) */
+#define CHFL_HALFOP	0x0100	/**< Channel halfop (+h) */
+#define	CHFL_BAN     	0x0200	/**< Channel ban (+b) - not a real flag, only used in sjoin.c */
+#define CHFL_EXCEPT	0x0400	/**< Channel except (+e) - not a real flag, only used in sjoin.c */
+#define CHFL_INVEX	0x0800  /**< Channel invite exception (+I) - not a real flag, only used in sjoin.c */
 /** @} */
 
 #define CHFL_REJOINING	0x8000  /* used internally by rejoin_* */
@@ -1909,7 +1915,7 @@ struct Ban {
 #define	CHFL_OVERLAP    (CHFL_CHANOWNER|CHFL_CHANADMIN|CHFL_CHANOP|CHFL_VOICE|CHFL_HALFOP)
 
 /* Channel macros */
-
+/* Don't blindly change these MODE_* values, see comment 20 lines up! */
 #define	MODE_CHANOP		CHFL_CHANOP
 #define	MODE_VOICE		CHFL_VOICE
 #define	MODE_PRIVATE		0x0004
@@ -2083,6 +2089,7 @@ struct MaxTarget {
 #define BANCHK_MSG		1	/* checking if a ban forbids the person from sending messages */
 #define BANCHK_NICK		2	/* checking if a ban forbids the person from changing his/her nick */
 #define BANCHK_LEAVE_MSG	3	/* checking if a ban forbids the person from leaving a message in PART or QUIT */
+#define BANCHK_TKL		4	/* called from a server ban routine, or other match_user() usage */
 
 #define TKLISTLEN		26
 #define TKLIPHASHLEN1		4
@@ -2092,9 +2099,10 @@ struct MaxTarget {
 #define MATCH_CHECK_REAL_HOST       0x0002
 #define MATCH_CHECK_CLOAKED_HOST    0x0004
 #define MATCH_CHECK_VISIBLE_HOST    0x0008
+#define MATCH_CHECK_EXTENDED        0x0010
 
-#define MATCH_CHECK_ALL             (MATCH_CHECK_IP|MATCH_CHECK_REAL_HOST|MATCH_CHECK_CLOAKED_HOST|MATCH_CHECK_VISIBLE_HOST)
-#define MATCH_CHECK_REAL            (MATCH_CHECK_IP|MATCH_CHECK_REAL_HOST)
+#define MATCH_CHECK_ALL             (MATCH_CHECK_IP|MATCH_CHECK_REAL_HOST|MATCH_CHECK_CLOAKED_HOST|MATCH_CHECK_VISIBLE_HOST|MATCH_CHECK_EXTENDED)
+#define MATCH_CHECK_REAL            (MATCH_CHECK_IP|MATCH_CHECK_REAL_HOST|MATCH_CHECK_EXTENDED)
 
 #define MATCH_MASK_IS_UHOST         0x1000
 #define MATCH_MASK_IS_HOST          0x2000
diff --git a/include/version.h b/include/version.h
index 3417ab2..55badd4 100644
--- a/include/version.h
+++ b/include/version.h
@@ -54,9 +54,9 @@
  * Can be useful if the above 3 versionids are insufficient for you (eg: you want to support CVS).
  * This is updated automatically on the CVS server every Monday. so don't touch it.
  */
-#define UNREAL_VERSION_TIME         201915
+#define UNREAL_VERSION_TIME	202001
 
-#define UnrealProtocol 		5000
+#define UnrealProtocol 		5002
 #define PATCH1  		macro_to_str(UNREAL_VERSION_GENERATION)
 #define PATCH2  		"." macro_to_str(UNREAL_VERSION_MAJOR)
 #define PATCH3  		"." macro_to_str(UNREAL_VERSION_MINOR)
diff --git a/include/windows/setup.h b/include/windows/setup.h
index 3ef2677..b3aec60 100644
--- a/include/windows/setup.h
+++ b/include/windows/setup.h
@@ -63,7 +63,7 @@
 #define UNREAL_VERSION_MAJOR 0
 
 /* Minor version number (e.g.: 1 for Unreal3.2.1) */
-#define UNREAL_VERSION_MINOR 0
+#define UNREAL_VERSION_MINOR 2
 
 /* Version suffix such as a beta marker or release candidate marker. (e.g.:
    -rcX for unrealircd-3.2.9-rcX) */
diff --git a/src/api-channelmode.c b/src/api-channelmode.c
index 1426f69..a655dd7 100644
--- a/src/api-channelmode.c
+++ b/src/api-channelmode.c
@@ -142,7 +142,7 @@ void extcmodes_check_for_changes(void)
 		sendto_realops("Channel modes changed at runtime: %s -> %s",
 			previous_chanmodes, chanmodes);
 		/* Broadcast change to all (locally connected) servers */
-		sendto_server(&me, 0, 0, NULL, "PROTOCTL CHANMODES=%s", chanmodes);
+		sendto_server(NULL, 0, 0, NULL, "PROTOCTL CHANMODES=%s", chanmodes);
 	}
 
 	strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes));
diff --git a/src/api-command.c b/src/api-command.c
index 7cf5691..e32b5c7 100644
--- a/src/api-command.c
+++ b/src/api-command.c
@@ -173,8 +173,8 @@ void CommandDel(Command *command)
 
 /** Calls the specified command for the user, as if it was received
  * that way on IRC.
- * @param client		Client that is the source.
- * @param mtags		Message tags for this command.
+ * @param client	Client that is the source.
+ * @param mtags		Message tags for this command (or NULL).
  * @param cmd		Command to run, eg "JOIN".
  * @param parc		Parameter count plus 1.
  * @param parv		Parameter array.
@@ -188,6 +188,8 @@ void CommandDel(Command *command)
  * @note Do not pass insane parameters. The combined size of all parameters
  *       should not exceed 510 bytes, since that is what all code expects.
  *       Similarly, you should not exceed MAXPARA for parc.
+ * @note If mtags is NULL then new message tags are created for the command
+ *       (and destroyed before return).
  */
 void do_cmd(Client *client, MessageTag *mtags, char *cmd, int parc, char *parv[])
 {
@@ -195,7 +197,14 @@ void do_cmd(Client *client, MessageTag *mtags, char *cmd, int parc, char *parv[]
 
 	cmptr = find_command_simple(cmd);
 	if (cmptr)
+	{
+		int gen_mtags = (mtags == NULL) ? 1 : 0;
+		if (gen_mtags)
+			new_message(client, NULL, &mtags);
 		(*cmptr->func) (client, mtags, parc, parv);
+		if (gen_mtags)
+			free_message_tags(mtags);
+	}
 }
 
 /** @} */
diff --git a/src/api-event.c b/src/api-event.c
index 65b327b..997b8bd 100644
--- a/src/api-event.c
+++ b/src/api-event.c
@@ -155,24 +155,25 @@ int EventMod(Event *event, EventInfo *mods)
 
 void DoEvents(void)
 {
-	Event *eventptr;
-	Event temp;
+	Event *e, *e_next;
 
-	for (eventptr = events; eventptr; eventptr = eventptr->next)
+	for (e = events; e; e = e_next)
 	{
-		if (eventptr->count == -1)
-			goto freeit;
-		if ((eventptr->every_msec == 0) || minimum_msec_since_last_run(&eventptr->last_run, eventptr->every_msec))
+		e_next = e->next;
+		if (e->count == -1)
+		{
+			EventDel(e);
+			continue;
+		}
+		if ((e->every_msec == 0) || minimum_msec_since_last_run(&e->last_run, e->every_msec))
 		{
-			(*eventptr->event)(eventptr->data);