diff --git a/Config b/Config index 6e99982..7941910 100755 --- a/Config +++ b/Config @@ -104,49 +104,59 @@ fi echo $CONF $CONF || exit 1 cd "$UNREALCWD" -if [ "$QUICK" != "1" ] ; then -if [ ! -f $CONFDIR/tls/server.cert.pem -a ! -f $CONFDIR/ssl/server.cert.pem ]; then -export OPENSSLPATH -TEST="" -while [ -z "$TEST" ] ; do - if [ "$GENCERTIFICATE" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to generate an SSL certificate for the IRCd?" - echo "Only answer No if you already have one." - echo $n "[$TEST] -> $c" - read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in - [Yy]*) - GENCERTIFICATE="1" - ;; - [Nn]*) - GENCERTIFICATE="" - ;; - *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac -done -if [ "$GENCERTIFICATE" = 1 ]; then - make pem - echo "Certificate created successfully." - sleep 1 -else - echo "Ok, not generating SSL certificate. Make sure that the certificate and key" - echo "are installed in conf/tls/server.crt.pem and conf/tls/server.key.pem prior to starting the IRCd." -fi -else -echo "SSL certificate already exists in configuration directory, no need to regenerate." -fi + +if [ "$QUICK" != "1" ] ; then + if [ ! -f $CONFDIR/tls/server.cert.pem -a ! -f $CONFDIR/ssl/server.cert.pem ]; then + export OPENSSLPATH + TEST="" + while [ -z "$TEST" ] ; do + if [ "$GENCERTIFICATE" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "UnrealIRCd requires an SSL certificate in order to work." + echo "Do you want to generate an SSL certificate for the IRCd?" + echo "Only answer No if you already have one." + echo $n "[$TEST] -> $c" + read cc + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in + [Yy]*) + GENCERTIFICATE="1" + ;; + [Nn]*) + GENCERTIFICATE="" + ;; + *) + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac + done + if [ "$GENCERTIFICATE" = 1 ]; then + echo + echo "*******************************************************************************" + echo "Next you will be asked some questions in order to generate the SSL certificate." + echo "IMPORTANT: If you don't own a domain or don't know what to answer, then you can" + echo " simply press ENTER or use fictional names for each question!" + echo "*******************************************************************************" + echo "Press ENTER to continue" + read cc + make pem + echo "Certificate created successfully." + sleep 1 + else + echo "Ok, not generating SSL certificate. Make sure that the certificate and key" + echo "are installed in conf/tls/server.cert.pem and conf/tls/server.key.pem prior to starting the IRCd." + fi + else + echo "SSL certificate already exists in configuration directory, no need to regenerate." + fi fi # Silently force a 'make clean' as otherwise part (or whole) of the @@ -158,89 +168,89 @@ make clean 1>/dev/null 2>&1 RUN_ADVANCED () { TEST="" while [ -z "$TEST" ] ; do - if [ "$SHOWLISTMODES" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to show the modes a channel has set in the /list output?" - echo $n "[$TEST] -> $c" + if [ "$SHOWLISTMODES" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Do you want to show the modes a channel has set in the /list output?" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in [Yy]*) - SHOWLISTMODES="1" - ;; + SHOWLISTMODES="1" + ;; [Nn]*) - SHOWLISTMODES="" - ;; + SHOWLISTMODES="" + ;; *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac done TEST="" while [ -z "$TEST" ] ; do - if [ "$NOOPEROVERRIDE" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to disable oper override?" - echo $n "[$TEST] -> $c" + if [ "$NOOPEROVERRIDE" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Do you want to disable oper override?" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in [Yy]*) - NOOPEROVERRIDE="1" - ;; + NOOPEROVERRIDE="1" + ;; [Nn]*) - NOOPEROVERRIDE="" - ;; + NOOPEROVERRIDE="" + ;; *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac done TEST="" while [ -z "$TEST" ] ; do - if [ "$OPEROVERRIDEVERIFY" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to require opers to /invite themselves into a +s or +p channel?" - echo $n "[$TEST] -> $c" + if [ "$OPEROVERRIDEVERIFY" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Do you want to require opers to /invite themselves into a +s or +p channel?" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in [Yy]*) - OPEROVERRIDEVERIFY="1" - ;; + OPEROVERRIDEVERIFY="1" + ;; [Nn]*) - OPEROVERRIDEVERIFY="" - ;; + OPEROVERRIDEVERIFY="" + ;; *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac done } @@ -250,7 +260,7 @@ UNREALCWD="`pwd`" BASEPATH="$HOME/unrealircd" DEFPERM="0600" SSLDIR="" -NICKNAMEHISTORYLENGTH="100" +NICKNAMEHISTORYLENGTH="2000" MAXCONNECTIONS_REQUEST="auto" REMOTEINC="1" CURLDIR="" @@ -261,9 +271,9 @@ OPEROVERRIDEVERIFY="" GENCERTIFICATE="1" EXTRAPARA="" if [ "`eval echo -n 'a'`" = "-n a" ] ; then - c="\c" + c="\c" else - n="-n" + n="-n" fi @@ -314,19 +324,19 @@ fi clear - if [ -f "doc/Config.header" -a -z "$NOINTRO" ] ; then - more doc/Config.header - echo "" - echo $n "[Press Enter to continue]" - read cc - clear - fi +if [ -f "doc/Config.header" -a -z "$NOINTRO" ] ; then + more doc/Config.header + echo "" + echo $n "[Press Enter to continue]" + read cc + clear +fi echo "We will now ask you a number of questions. You can just press ENTER to accept the defaults!" echo "" # This needs to be updated each release so auto-upgrading works for settings, modules, etc!!: -UNREALRELEASES="unrealircd-5.0.7-rc1 unrealircd-5.0.6 unrealircd-5.0.5.1 unrealircd-5.0.5 unrealircd-5.0.4 unrealircd-5.0.3.1 unrealircd-5.0.3 unrealircd-5.0.2 unrealircd-5.0.1 unrealircd-5.0.0 unrealircd-5.0.0-rc2 unrealircd-5.0.0-rc1" +UNREALRELEASES="unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6 unrealircd-5.0.5.1 unrealircd-5.0.5 unrealircd-5.0.4 unrealircd-5.0.3.1 unrealircd-5.0.3 unrealircd-5.0.2 unrealircd-5.0.1 unrealircd-5.0.0" if [ -f "config.settings" ]; then . ./config.settings else @@ -405,9 +415,9 @@ echo " If this directory does not exist it will be created.)" echo $n "[$TEST] -> $c" read cc if [ -z "$cc" ] ; then - BASEPATH=$TEST + BASEPATH=$TEST else - BASEPATH=`eval echo $cc` # modified + BASEPATH=`eval echo $cc` # modified fi if [ "$BASEPATH" = "$UNREALCWD" ]; then echo "" @@ -431,32 +441,28 @@ PRIVATELIBDIR="$BASEPATH/lib" TEST="" while [ -z "$TEST" ] ; do - TEST="$DEFPERM" - echo "" - echo "What should the default permissions for your configuration files be? (Set this to 0 to disable)" - echo "It is strongly recommended that you use 0600 to prevent unwanted reading of the file" - echo $n "[$TEST] -> $c" + TEST="$DEFPERM" + echo "" + echo "What should the default permissions for your configuration files be? (Set this to 0 to disable)" + echo "It is strongly recommended that you use 0600 to prevent unwanted reading of the file" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - DEFPERM=$TEST - break - fi - case "$cc" in + if [ -z "$cc" ] ; then + DEFPERM=$TEST + break + fi + case "$cc" in [0-9]*) - DEFPERM="$cc" - ;; + DEFPERM="$cc" + ;; *) - echo "" - echo "You must enter a number" - TEST="" - ;; - esac - + echo "" + echo "You must enter a number" + TEST="" + ;; + esac done - - - echo "" echo "If you want, you can manually enter the path to OpenSSL/LibreSSL here." echo "In most cases you can leave this blank and it will be detected automatically." @@ -475,43 +481,43 @@ fi TEST="$SSLDIR" echo $n "[$TEST] -> $c" - read cc +read cc if [ -z "$cc" ] ; then - SSLDIR="$TEST" + SSLDIR="$TEST" else - SSLDIR=`eval echo $cc` # modified + SSLDIR=`eval echo $cc` # modified fi TEST="" while [ -z "$TEST" ] ; do - if [ "$REMOTEINC" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to enable remote includes?" - echo "This allows stuff like this in your configuration file:" - echo "include \"https://www.somesite.org/files/opers.conf\";" - echo $n "[$TEST] -> $c" + if [ "$REMOTEINC" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Do you want to enable remote includes?" + echo "This allows stuff like this in your configuration file:" + echo "include \"https://www.somesite.org/files/opers.conf\";" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in [Yy]*) - REMOTEINC="1" - ;; + REMOTEINC="1" + ;; [Nn]*) - REMOTEINC="" - CURLDIR="" - ;; + REMOTEINC="" + CURLDIR="" + ;; *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac done if [ "$REMOTEINC" = "1" ] ; then @@ -589,108 +595,108 @@ if [ "$REMOTEINC" = "1" ] ; then fi fi - if [ "x$CURLDIR" = "x" ]; then - # Still empty? - TEST="" - while [ -z "$TEST" ] ; do - TEST="Yes" - echo "" - echo "Do you want me to automatically download and install curl for you?" - echo $n "[$TEST] -> $c" - read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in - [Yy]*) - INSTALLCURL="1" - CURLDIR="$UNREALCWD/extras/curl" - ;; - [Nn]*) - INSTALLCURL="0" - ;; - *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac - done - fi + if [ "x$CURLDIR" = "x" ]; then + # Still empty? + TEST="" + while [ -z "$TEST" ] ; do + TEST="Yes" + echo "" + echo "Do you want me to automatically download and install curl for you?" + echo $n "[$TEST] -> $c" + read cc + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in + [Yy]*) + INSTALLCURL="1" + CURLDIR="$UNREALCWD/extras/curl" + ;; + [Nn]*) + INSTALLCURL="0" + ;; + *) + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac + done + fi if [ "$INSTALLCURL" != "1" ]; then - TEST="" - while [ -z "$TEST" ] ; do - TEST="$CURLDIR" - echo "" - echo "Specify the directory you installed libcurl to" - echo $n "[$TEST] -> $c" - read cc - if [ -z "$cc" ] ; then - cc=$TEST - else - TEST=$cc - CURLDIR=`eval echo $cc` # modified - fi - done - fi + TEST="" + while [ -z "$TEST" ] ; do + TEST="$CURLDIR" + echo "" + echo "Specify the directory you installed libcurl to" + echo $n "[$TEST] -> $c" + read cc + if [ -z "$cc" ] ; then + cc=$TEST + else + TEST=$cc + CURLDIR=`eval echo $cc` # modified + fi + done + fi fi TEST="" while [ -z "$TEST" ] ; do - if [ "$PREFIXAQ" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to enable prefixes for chanadmin and chanowner?" - echo "This will give +a the & prefix and ~ for +q (just like +o is @)" - echo "Supported by the major clients (mIRC, xchat, epic, eggdrop, Klient," - echo "PJIRC, irssi, CGI:IRC, etc.)" - echo "This feature should be enabled/disabled network-wide." - echo $n "[$TEST] -> $c" + if [ "$PREFIXAQ" = "1" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Do you want to enable prefixes for chanadmin and chanowner?" + echo "This will give +a the & prefix and ~ for +q (just like +o is @)" + echo "Supported by the major clients (mIRC, xchat, epic, eggdrop, Klient," + echo "PJIRC, irssi, CGI:IRC, etc.)" + echo "This feature should be enabled/disabled network-wide." + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in [Yy]*) - PREFIXAQ="1" - ;; + PREFIXAQ="1" + ;; [Nn]*) - PREFIXAQ="" - ;; + PREFIXAQ="" + ;; *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac done TEST="" while [ -z "$TEST" ] ; do - TEST="$NICKNAMEHISTORYLENGTH" - echo "" - echo "How far back do you want to keep the nickname history?" - echo $n "[$TEST] -> $c" + TEST="$NICKNAMEHISTORYLENGTH" + echo "" + echo "How far back do you want to keep the nickname history?" + echo $n "[$TEST] -> $c" read cc - if [ -z "$cc" ] ; then - NICKNAMEHISTORYLENGTH=$TEST - break - fi - case "$cc" in + if [ -z "$cc" ] ; then + NICKNAMEHISTORYLENGTH=$TEST + break + fi + case "$cc" in [1-9]*) - NICKNAMEHISTORYLENGTH="$cc" - ;; + NICKNAMEHISTORYLENGTH="$cc" + ;; *) - echo "" - echo "You must enter a number" - TEST="" - ;; - esac + echo "" + echo "You must enter a number" + TEST="" + ;; + esac done echo "" @@ -737,7 +743,7 @@ echo "Otherwise, see \`./configure --help' and write them here:" echo $n "[$TEST] -> $c" read EXTRAPARA if [ -z "$EXTRAPARA" ]; then - EXTRAPARA="$TEST" + EXTRAPARA="$TEST" fi rm -f config.settings diff --git a/Makefile.in b/Makefile.in index 98f2cc7..a987fde 100644 --- a/Makefile.in +++ b/Makefile.in @@ -164,54 +164,56 @@ depend: done install: all - $(INSTALL) -m 0700 -d @BINDIR@ - $(INSTALL) -m 0700 src/ircd @BINDIR@/unrealircd - $(INSTALL) -m 0700 -d @DOCDIR@ - $(INSTALL) -m 0600 doc/Authors doc/coding-guidelines doc/tao.of.irc @DOCDIR@ - $(INSTALL) -m 0700 -d @CONFDIR@ - $(INSTALL) -m 0600 doc/conf/*.conf @CONFDIR@ - $(INSTALL) -m 0600 doc/conf/*.motd @CONFDIR@ - $(INSTALL) -m 0600 doc/conf/modules.sources.list @CONFDIR@ ; \ - $(INSTALL) -m 0700 unrealircd @SCRIPTDIR@ - $(INSTALL) -m 0700 -d @MODULESDIR@ - @rm -f @MODULESDIR@/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/*.so @MODULESDIR@ - $(INSTALL) -m 0700 -d @MODULESDIR@/usermodes - @rm -f @MODULESDIR@/usermodes/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/usermodes/*.so @MODULESDIR@/usermodes - $(INSTALL) -m 0700 -d @MODULESDIR@/chanmodes - @rm -f @MODULESDIR@/chanmodes/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/chanmodes/*.so @MODULESDIR@/chanmodes - $(INSTALL) -m 0700 -d @MODULESDIR@/snomasks - @rm -f @MODULESDIR@/snomasks/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/snomasks/*.so @MODULESDIR@/snomasks - $(INSTALL) -m 0700 -d @MODULESDIR@/extbans - @rm -f @MODULESDIR@/extbans/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/extbans/*.so @MODULESDIR@/extbans + $(INSTALL) -m 0700 -d $(DESTDIR)@BINDIR@ + $(INSTALL) -m 0700 src/ircd $(DESTDIR)@BINDIR@/unrealircd + $(INSTALL) -m 0700 -d $(DESTDIR)@DOCDIR@ + $(INSTALL) -m 0600 doc/Authors doc/coding-guidelines doc/tao.of.irc $(DESTDIR)@DOCDIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@CONFDIR@ + $(INSTALL) -m 0600 doc/conf/*.conf $(DESTDIR)@CONFDIR@ + $(INSTALL) -m 0600 doc/conf/*.motd $(DESTDIR)@CONFDIR@ + $(INSTALL) -m 0600 doc/conf/modules.sources.list $(DESTDIR)@CONFDIR@ ; \ + $(INSTALL) -m 0700 unrealircd $(DESTDIR)@SCRIPTDIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@ + @rm -f $(DESTDIR)@MODULESDIR@/*.so 1>/dev/null 2>&1 + $(INSTALL) -m 0700 src/modules/*.so $(DESTDIR)@MODULESDIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/usermodes + @rm -f $(DESTDIR)@MODULESDIR@/usermodes/*.so 1>/dev/null 2>&1 + $(INSTALL) -m 0700 src/modules/usermodes/*.so $(DESTDIR)@MODULESDIR@/usermodes + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/chanmodes + @rm -f $(DESTDIR)@MODULESDIR@/chanmodes/*.so 1>/dev/null 2>&1 + $(INSTALL) -m 0700 src/modules/chanmodes/*.so $(DESTDIR)@MODULESDIR@/chanmodes + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/snomasks + @rm -f $(DESTDIR)@MODULESDIR@/snomasks/*.so 1>/dev/null 2>&1 + $(INSTALL) -m 0700 src/modules/snomasks/*.so $(DESTDIR)@MODULESDIR@/snomasks + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/extbans + @rm -f $(DESTDIR)@MODULESDIR@/extbans/*.so 1>/dev/null 2>&1 + $(INSTALL) -m 0700 src/modules/extbans/*.so $(DESTDIR)@MODULESDIR@/extbans @#If the conf/ssl directory exists then rename it here to conf/tls @#and add a symlink for backwards compatibility (so that f.e. certbot @#doesn't randomly fail after an upgrade to U5). - -@if [ -d "@CONFDIR@/ssl" ] ; then \ - mv "@CONFDIR@/ssl" "@CONFDIR@/tls" ; \ - ln -s "@CONFDIR@/tls" "@CONFDIR@/ssl" ; \ + -@if [ -d "$(DESTDIR)@CONFDIR@/ssl" ] ; then \ + mv "$(DESTDIR)@CONFDIR@/ssl" "$(DESTDIR)@CONFDIR@/tls" ; \ + ln -s "$(DESTDIR)@CONFDIR@/tls" "$(DESTDIR)@CONFDIR@/ssl" ; \ fi - $(INSTALL) -m 0700 -d @CONFDIR@/tls - $(INSTALL) -m 0600 doc/conf/tls/curl-ca-bundle.crt @CONFDIR@/tls + $(INSTALL) -m 0700 -d $(DESTDIR)@CONFDIR@/tls + $(INSTALL) -m 0600 doc/conf/tls/curl-ca-bundle.crt $(DESTDIR)@CONFDIR@/tls @# delete modules/cap directory, to avoid confusing with U4 to U5 upgrades: - rm -rf @MODULESDIR@/cap - $(INSTALL) -m 0700 -d @MODULESDIR@/third - @rm -f @MODULESDIR@/third/*.so 1>/dev/null 2>&1 + rm -rf $(DESTDIR)@MODULESDIR@/cap + $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/third + @rm -f $(DESTDIR)@MODULESDIR@/third/*.so 1>/dev/null 2>&1 @#This step can fail with zero files, so we ignore exit status: - -$(INSTALL) -m 0700 src/modules/third/*.so @MODULESDIR@/third - $(INSTALL) -m 0700 -d @TMPDIR@ - $(INSTALL) -m 0700 -d @CACHEDIR@ - $(INSTALL) -m 0700 -d @PERMDATADIR@ - $(INSTALL) -m 0700 -d @LOGDIR@ - -@if [ ! -f "@CONFDIR@/tls/server.cert.pem" ] ; then \ - $(INSTALL) -m 0600 server.req.pem @CONFDIR@/tls ; \ - $(INSTALL) -m 0600 server.key.pem @CONFDIR@/tls ; \ - $(INSTALL) -m 0600 server.cert.pem @CONFDIR@/tls ; \ + -$(INSTALL) -m 0700 src/modules/third/*.so $(DESTDIR)@MODULESDIR@/third + $(INSTALL) -m 0700 -d $(DESTDIR)@TMPDIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@CACHEDIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@PERMDATADIR@ + $(INSTALL) -m 0700 -d $(DESTDIR)@LOGDIR@ + -@if [ ! -f "$(DESTDIR)@CONFDIR@/tls/server.cert.pem" ] ; then \ + $(INSTALL) -m 0600 server.req.pem $(DESTDIR)@CONFDIR@/tls ; \ + $(INSTALL) -m 0600 server.key.pem $(DESTDIR)@CONFDIR@/tls ; \ + $(INSTALL) -m 0600 server.cert.pem $(DESTDIR)@CONFDIR@/tls ; \ fi + @rm -f $(DESTDIR)@SCRIPTDIR@/source + ln -s @BUILDDIR@ $(DESTDIR)@SCRIPTDIR@/source @echo '' @echo '* UnrealIRCd is now installed.' diff --git a/Makefile.windows b/Makefile.windows index 4cdf5c0..e89ceea 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -269,6 +269,7 @@ DLL_FILES=SRC/MODULES/CLOAK.DLL \ SRC/MODULES/EXTBANS/MSGBYPASS.DLL \ SRC/MODULES/EXTBANS/TIMEDBAN.DLL \ SRC/MODULES/EXTBANS/PARTMSG.DLL \ + SRC/MODULES/EXTBANS/SECURITYGROUP.DLL \ SRC/MODULES/ACCOUNT-NOTIFY.DLL \ SRC/MODULES/MESSAGE-TAGS.DLL \ SRC/MODULES/BATCH.DLL \ @@ -1018,6 +1019,9 @@ src/modules/extbans/timedban.dll: src/modules/extbans/timedban.c $(INCLUDES) src/modules/extbans/partmsg.dll: src/modules/extbans/partmsg.c $(INCLUDES) $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/partmsg.c $(MODLFLAGS) +src/modules/extbans/securitygroup.dll: src/modules/extbans/securitygroup.c $(INCLUDES) + $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/securitygroup.c $(MODLFLAGS) + src/modules/account-notify.dll: src/modules/account-notify.c $(INCLUDES) $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/account-notify.c $(MODLFLAGS) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7708d00 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions +* The latest *stable* release of the 5.x branch + +See [UnrealIRCd releases](https://www.unrealircd.org/docs/UnrealIRCd_releases) for information on older versions and End Of Life dates. + +## Reporting a Vulnerability + +Please report issues on the [bug tracker](https://bugs.unrealircd.org) and in the bug submit form **set the 'View Status' to 'private'**. + +Do not report security issues on the forums or in a public IRC channel such as #unreal-support. +If you insist on e-mail then you can use syzop@unrealircd.org or security@unrealircd.org. Again, the bug tracker is preferred. + +If you are *unsure* if something is a security issue, then report it at the bug tracker as a 'private' bug anyway. Better safe than sorry. +Do not ask around in public channels or forums. + +You should get a response or at least an acknowledgement soon. If you don't hear back within 24 hours, then please try to contact us again. + +## Full policy +See https://www.unrealircd.org/docs/Policy:_Handling_of_security_issues for full information. diff --git a/autoconf/m4/unreal.m4 b/autoconf/m4/unreal.m4 index ec2c85d..345fdf7 100644 --- a/autoconf/m4/unreal.m4 +++ b/autoconf/m4/unreal.m4 @@ -272,3 +272,43 @@ else AC_MSG_RESULT([no]) fi ]) + +AC_DEFUN([CHECK_ASN1_TIME_diff], +[ +AC_MSG_CHECKING([for ASN1_TIME_diff in SSL library]) +AC_LANG_PUSH(C) +SAVE_LIBS="$LIBS" +LIBS="$LIBS $CRYPTOLIB" +AC_TRY_LINK([#include ], + [int one, two; ASN1_TIME_diff(&one, &two, NULL, NULL);], + has_function=1, + has_function=0) +LIBS="$SAVE_LIBS" +AC_LANG_POP(C) +if test $has_function = 1; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAS_ASN1_TIME_diff], [], [Define if ssl library has ASN1_TIME_diff]) +else + AC_MSG_RESULT([no]) +fi +]) + +AC_DEFUN([CHECK_X509_get0_notAfter], +[ +AC_MSG_CHECKING([for X509_get0_notAfter in SSL library]) +AC_LANG_PUSH(C) +SAVE_LIBS="$LIBS" +LIBS="$LIBS $CRYPTOLIB" +AC_TRY_LINK([#include ], + [X509_get0_notAfter(NULL);], + has_function=1, + has_function=0) +LIBS="$SAVE_LIBS" +AC_LANG_POP(C) +if test $has_function = 1; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAS_X509_get0_notAfter], [], [Define if ssl library has X509_get0_notAfter]) +else + AC_MSG_RESULT([no]) +fi +]) diff --git a/configure b/configure index d25ba0b..b32ae67 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.7. +# Generated by GNU Autoconf 2.69 for unrealircd 5.0.8. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unrealircd' PACKAGE_TARNAME='unrealircd' -PACKAGE_VERSION='5.0.7' -PACKAGE_STRING='unrealircd 5.0.7' +PACKAGE_VERSION='5.0.8' +PACKAGE_STRING='unrealircd 5.0.8' 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.7 to adapt to many kinds of systems. +\`configure' configures unrealircd 5.0.8 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.7:";; + short | recursive ) echo "Configuration of unrealircd 5.0.8:";; 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.7 +unrealircd configure 5.0.8 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.7, which was +It was created by unrealircd $as_me 5.0.8, 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="7" +UNREAL_VERSION_MINOR="8" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_MINOR $UNREAL_VERSION_MINOR @@ -6528,6 +6528,100 @@ else $as_echo "no" >&6; } fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ASN1_TIME_diff in SSL library" >&5 +$as_echo_n "checking for ASN1_TIME_diff in SSL library... " >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +SAVE_LIBS="$LIBS" +LIBS="$LIBS $CRYPTOLIB" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int one, two; ASN1_TIME_diff(&one, &two, NULL, NULL); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + has_function=1 +else + has_function=0 +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS="$SAVE_LIBS" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test $has_function = 1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAS_ASN1_TIME_diff /**/" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for X509_get0_notAfter in SSL library" >&5 +$as_echo_n "checking for X509_get0_notAfter in SSL library... " >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +SAVE_LIBS="$LIBS" +LIBS="$LIBS $CRYPTOLIB" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +X509_get0_notAfter(NULL); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + has_function=1 +else + has_function=0 +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS="$SAVE_LIBS" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test $has_function = 1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAS_X509_get0_notAfter /**/" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + # Check whether --enable-dynamic-linking was given. if test "${enable_dynamic_linking+set}" = set; then : enableval=$enable_dynamic_linking; enable_dynamic_linking=$enableval @@ -8398,7 +8492,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.7, which was +This file was extended by unrealircd $as_me 5.0.8, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -8461,7 +8555,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.7 +unrealircd config.status 5.0.8 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 1a4569a..4c6fb10 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.7], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) +AC_INIT([unrealircd], [5.0.8], [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=["7"] +UNREAL_VERSION_MINOR=["8"] 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 @@ -504,6 +504,8 @@ CHECK_SSL CHECK_SSL_CTX_SET1_CURVES_LIST CHECK_SSL_CTX_SET_MIN_PROTO_VERSION CHECK_SSL_CTX_SET_SECURITY_LEVEL +CHECK_ASN1_TIME_diff +CHECK_X509_get0_notAfter AC_ARG_ENABLE(dynamic-linking, [AS_HELP_STRING([--disable-dynamic-linking], [Make the IRCd statically link with shared objects rather than dynamically (noone knows if disabling dynamic linking actually does anything or not)])], [enable_dynamic_linking=$enableval], [enable_dynamic_linking="yes"]) AS_IF([test $enable_dynamic_linking = "yes"], diff --git a/doc/Config.header b/doc/Config.header index a489d28..afc833c 100644 --- a/doc/Config.header +++ b/doc/Config.header @@ -7,7 +7,7 @@ \___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_| Configuration Program - for UnrealIRCd 5.0.7 + for UnrealIRCd 5.0.8 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 fa1151e..47a6590 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -1,6 +1,72 @@ -UnrealIRCd 5.0.7 Release Notes +UnrealIRCd 5.0.8 Release Notes =============================== +The main purpose of this release is to enhance the +[reputation](https://www.unrealircd.org/docs/Reputation_score) +functionality. There have also been some other changes and minor +bug fixes. For more information, see below. + +Enhancements: +* Support for [security groups](https://www.unrealircd.org/docs/Security-group_block), + of which four groups always exist by default: known-users, unknown-users, + tls-users and tls-and-known-users. +* New extended ban ```~G:securitygroupname```. Typical usage would be + ```MODE #chan +b ~G:unknown-users``` which will ban all users from the + channel that are not identified to services and have a reputation + score below 25 (by default). The exact settings can be tweaked in the + [security group block](https://www.unrealircd.org/docs/Security-group_block). +* The reputation command (IRCOp-only) has been extended to make it + easier to look for potential troublemakers: + * ```REPUTATION Nick``` shows reputation about the nick name + * ```REPUTATION IP``` shows reputation about the IP address + * ```REPUTATION #channel``` lists users in channel with their reputation score + * ```REPUTATION prev) -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. +/** Walk through client lists (with examples). + * @param pos The variable to use as a loop cursor + * @param head The head of your list + * @param member The name of the list_struct within the struct. + * @ingroup ListFunctions + * @section Examples + * @subsection client_list List all clients + * @code + * CMD_FUNC(cmd_listallclients) + * { + * Client *acptr; + * sendnotice(client, "List of all clients:"); + * list_for_each_entry(acptr, &client_list, client_node) + * sendnotice(client, "Client %s", acptr->name); + * } + * @endcode + * @subsection lclient_list List all LOCAL clients + * @code + * CMD_FUNC(cmd_listalllocalclients) + * { + * Client *acptr; + * sendnotice(client, "List of all local clients:"); + * list_for_each_entry(acptr, &lclient_list, lclient_node) + * sendnotice(client, "Client %s", acptr->name); + * } + * @endcode + * @subsection global_server_list List all servers + * @code + * CMD_FUNC(cmd_listallservers) + * { + * Client *acptr; + * sendnotice(client, "List of all servers:"); + * list_for_each_entry(acptr, &global_server_list, client_node) + * sendnotice(client, "Server %s", acptr->name); + * } + * @endcode + * @subsection server_list List all LOCALLY connected servers + * @code + * CMD_FUNC(cmd_listallservers) + * { + * Client *acptr; + * sendnotice(client, "List of all LOCAL servers:"); + * list_for_each_entry(acptr, &server_list, special_node) + * sendnotice(client, "Server %s", acptr->name); + * } + * @endcode + * @subsection oper_list List all LOCALLY connected IRCOps + * @code + * CMD_FUNC(cmd_listlocalircops) + * { + * Client *acptr; + * sendnotice(client, "List of all LOCAL IRCOps:"); + * list_for_each_entry(acptr, &oper_list, special_node) + * sendnotice(client, "User %s", acptr->name); + * } + * @endcode */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ @@ -514,12 +565,16 @@ SINLINE void list_splice_tail_init(struct list_head *list, for (; &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. +/** Walk through client lists - special 'safe' version. + * This is a special version, in case clients are removed from the list + * while the list is iterated. It is unlikely that you need to use this + * from modules, so use list_for_each_entry() instead. + * Examples are also in list_for_each_entry(). + * @param pos The variable to use as a loop cursor + * @param n Variable to be used for temporary storage + * @param head The head of your list + * @param member The name of the list_struct within the struct. + * @ingroup ListFunctions */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ diff --git a/include/modules.h b/include/modules.h index a457ad8..478780e 100644 --- a/include/modules.h +++ b/include/modules.h @@ -206,7 +206,7 @@ typedef struct { /** unique flag (like 0x10) */ Cmode_t mode; - /** Number of paramters (1 or 0) */ + /** Number of parameters (1 or 0) */ int paracount; /** Check access or parameter of the channel mode. @@ -911,111 +911,219 @@ extern int LoadPersistentLongX(ModuleInfo *modinfo, char *varshortname, long *va extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long var); #define SavePersistentLong(modinfo, var) SavePersistentLongX(modinfo, #var, var) +/** Hooks trigger on "events", such as a new user connecting or joining a channel, + * see https://www.unrealircd.org/docs/Dev:Hook_API for background info. + * You are suggested to use CTRL+F on this page to search for any useful hook, + * see also the example session on how to find and use a hook at + * https://www.unrealircd.org/docs/Dev:Hook_API#Example_session_finding_and_using_a_hook + * + * @defgroup HookAPI Hook API + * @{ + */ + /* Hook types */ -#define HOOKTYPE_LOCAL_QUIT 1 -#define HOOKTYPE_LOCAL_NICKCHANGE 2 -#define HOOKTYPE_LOCAL_CONNECT 3 -#define HOOKTYPE_REHASHFLAG 4 -#define HOOKTYPE_PRE_LOCAL_PART 5 -#define HOOKTYPE_CONFIGPOSTTEST 6 -#define HOOKTYPE_REHASH 7 -#define HOOKTYPE_PRE_LOCAL_CONNECT 8 -#define HOOKTYPE_PRE_LOCAL_QUIT 9 -#define HOOKTYPE_SERVER_CONNECT 11 -#define HOOKTYPE_SERVER_QUIT 12 -#define HOOKTYPE_STATS 13 -#define HOOKTYPE_LOCAL_JOIN 14 -#define HOOKTYPE_CONFIGTEST 15 -#define HOOKTYPE_CONFIGRUN 16 -/* If you ever change the number of usermsg & chanmsg, notify Syzop first, kthx! ;p */ -#define HOOKTYPE_USERMSG 17 -#define HOOKTYPE_CHANMSG 18 -#define HOOKTYPE_LOCAL_PART 19 -#define HOOKTYPE_LOCAL_KICK 20 -#define HOOKTYPE_LOCAL_CHANMODE 21 -#define HOOKTYPE_LOCAL_TOPIC 22 -#define HOOKTYPE_LOCAL_OPER 23 -#define HOOKTYPE_UNKUSER_QUIT 24 -#define HOOKTYPE_LOCAL_PASS 25 -#define HOOKTYPE_REMOTE_CONNECT 26 -#define HOOKTYPE_REMOTE_QUIT 27 -#define HOOKTYPE_PRE_LOCAL_JOIN 28 -#define HOOKTYPE_PRE_LOCAL_KICK 29 -#define HOOKTYPE_PRE_LOCAL_TOPIC 30 -#define HOOKTYPE_REMOTE_NICKCHANGE 31 -#define HOOKTYPE_CHANNEL_CREATE 32 -#define HOOKTYPE_CHANNEL_DESTROY 33 -#define HOOKTYPE_REMOTE_CHANMODE 34 -#define HOOKTYPE_TKL_EXCEPT 35 -#define HOOKTYPE_UMODE_CHANGE 36 -#define HOOKTYPE_TOPIC 37 -#define HOOKTYPE_REHASH_COMPLETE 38 -#define HOOKTYPE_TKL_ADD 39 -#define HOOKTYPE_TKL_DEL 40 -#define HOOKTYPE_LOCAL_KILL 41 -#define HOOKTYPE_LOG 42 -#define HOOKTYPE_REMOTE_JOIN 43 -#define HOOKTYPE_REMOTE_PART 44 -#define HOOKTYPE_REMOTE_KICK 45 -#define HOOKTYPE_LOCAL_SPAMFILTER 46 -#define HOOKTYPE_SILENCED 47 -#define HOOKTYPE_POST_SERVER_CONNECT 48 -#define HOOKTYPE_RAWPACKET_IN 49 -#define HOOKTYPE_PACKET 51 -#define HOOKTYPE_HANDSHAKE 52 -#define HOOKTYPE_AWAY 53 -#define HOOKTYPE_INVITE 55 -#define HOOKTYPE_CAN_JOIN 56 -#define HOOKTYPE_CAN_SEND_TO_CHANNEL 57 -#define HOOKTYPE_CAN_KICK 58 -#define HOOKTYPE_FREE_CLIENT 59 -#define HOOKTYPE_FREE_USER 60 -#define HOOKTYPE_PRE_CHANMSG 61 -#define HOOKTYPE_KNOCK 63 -#define HOOKTYPE_MODECHAR_ADD 64 -#define HOOKTYPE_MODECHAR_DEL 65 -#define HOOKTYPE_CAN_JOIN_LIMITEXCEEDED 67 -#define HOOKTYPE_VISIBLE_IN_CHANNEL 68 -#define HOOKTYPE_PRE_LOCAL_CHANMODE 69 -#define HOOKTYPE_PRE_REMOTE_CHANMODE 70 -#define HOOKTYPE_JOIN_DATA 71 -#define HOOKTYPE_PRE_KNOCK 72 -#define HOOKTYPE_PRE_INVITE 73 -#define HOOKTYPE_OPER_INVITE_BAN 74 -#define HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL 75 -#define HOOKTYPE_CHAN_PERMIT_NICK_CHANGE 76 -#define HOOKTYPE_IS_CHANNEL_SECURE 77 -#define HOOKTYPE_SEND_CHANNEL 78 -#define HOOKTYPE_CHANNEL_SYNCED 79 -#define HOOKTYPE_CAN_SAJOIN 80 -#define HOOKTYPE_WHOIS 81 -#define HOOKTYPE_CHECK_INIT 82 -#define HOOKTYPE_WHO_STATUS 83 -#define HOOKTYPE_MODE_DEOP 84 -#define HOOKTYPE_PRE_KILL 85 -#define HOOKTYPE_SEE_CHANNEL_IN_WHOIS 86 -#define HOOKTYPE_DCC_DENIED 87 -#define HOOKTYPE_SERVER_HANDSHAKE_OUT 88 -#define HOOKTYPE_SERVER_SYNCED 89 -#define HOOKTYPE_SECURE_CONNECT 90 -#define HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION 91 -#define HOOKTYPE_REQUIRE_SASL 92 -#define HOOKTYPE_SASL_CONTINUATION 93 -#define HOOKTYPE_SASL_RESULT 94 -#define HOOKTYPE_PLACE_HOST_BAN 95 -#define HOOKTYPE_FIND_TKLINE_MATCH 96 -#define HOOKTYPE_WELCOME 97 -#define HOOKTYPE_PRE_COMMAND 98 -#define HOOKTYPE_POST_COMMAND 99 -#define HOOKTYPE_NEW_MESSAGE 100 -#define HOOKTYPE_IS_HANDSHAKE_FINISHED 101 -#define HOOKTYPE_PRE_LOCAL_QUIT_CHAN 102 -#define HOOKTYPE_IDENT_LOOKUP 103 -#define HOOKTYPE_CONFIGRUN_EX 104 -#define HOOKTYPE_CAN_SEND_TO_USER 105 -#define HOOKTYPE_SERVER_SYNC 106 -#define HOOKTYPE_ACCOUNT_LOGIN 107 -#define HOOKTYPE_CLOSE_CONNECTION 108 +/** See hooktype_pre_local_connect() */ +#define HOOKTYPE_PRE_LOCAL_CONNECT 1 +/** See hooktype_local_connect() */ +#define HOOKTYPE_LOCAL_CONNECT 2 +/** See hooktype_remote_connect() */ +#define HOOKTYPE_REMOTE_CONNECT 3 +/** See hooktype_pre_local_quit() */ +#define HOOKTYPE_PRE_LOCAL_QUIT 4 +/** See hooktype_local_quit() */ +#define HOOKTYPE_LOCAL_QUIT 5 +/** See hooktype_remote_quit() */ +#define HOOKTYPE_REMOTE_QUIT 6 +/** See hooktype_unkuser_quit() */ +#define HOOKTYPE_UNKUSER_QUIT 7 +/** See hooktype_server_connect() */ +#define HOOKTYPE_SERVER_CONNECT 8 +/** See hooktype_server_handshake_out() */ +#define HOOKTYPE_SERVER_HANDSHAKE_OUT 9 +/** See hooktype_server_sync() */ +#define HOOKTYPE_SERVER_SYNC 10 +/** See hooktype_post_server_connect() */ +#define HOOKTYPE_POST_SERVER_CONNECT 11 +/** See hooktype_server_synced() */ +#define HOOKTYPE_SERVER_SYNCED 12 +/** See hooktype_server_quit() */ +#define HOOKTYPE_SERVER_QUIT 13 +/** See hooktype_local_nickchange() */ +#define HOOKTYPE_LOCAL_NICKCHANGE 14 +/** See hooktype_remote_nickchange() */ +#define HOOKTYPE_REMOTE_NICKCHANGE 15 +/** See hooktype_can_join() */ +#define HOOKTYPE_CAN_JOIN 16 +/** See hooktype_pre_local_join() */ +#define HOOKTYPE_PRE_LOCAL_JOIN 17 +/** See hooktype_local_join() */ +#define HOOKTYPE_LOCAL_JOIN 18 +/** See hooktype_remote_join() */ +#define HOOKTYPE_REMOTE_JOIN 19 +/** See hooktype_pre_local_part() */ +#define HOOKTYPE_PRE_LOCAL_PART 20 +/** See hooktype_local_part() */ +#define HOOKTYPE_LOCAL_PART 21 +/** See hooktype_remote_part() */ +#define HOOKTYPE_REMOTE_PART 22 +/** See hooktype_pre_local_kick() */ +#define HOOKTYPE_PRE_LOCAL_KICK 23 +/** See hooktype_can_kick() */ +#define HOOKTYPE_CAN_KICK 24 +/** See hooktype_local_kick() */ +#define HOOKTYPE_LOCAL_KICK 25 +/** See hooktype_remote_kick() */ +#define HOOKTYPE_REMOTE_KICK 26 +/** See hooktype_pre_chanmsg() */ +#define HOOKTYPE_PRE_CHANMSG 28 +/** See hooktype_can_send_to_user() */ +#define HOOKTYPE_CAN_SEND_TO_USER 29 +/** See hooktype_can_send_to_channel() */ +#define HOOKTYPE_CAN_SEND_TO_CHANNEL 30 +/** See hooktype_usermsg() */ +#define HOOKTYPE_USERMSG 31 +/** See hooktype_chanmsg() */ +#define HOOKTYPE_CHANMSG 32 +/** See hooktype_pre_local_topic() */ +#define HOOKTYPE_PRE_LOCAL_TOPIC 33 +/** See hooktype_topic() */ +#define HOOKTYPE_TOPIC 35 +/** See hooktype_pre_local_chanmode() */ +#define HOOKTYPE_PRE_LOCAL_CHANMODE 36 +/** See hooktype_pre_remote_chanmode() */ +#define HOOKTYPE_PRE_REMOTE_CHANMODE 37 +/** See hooktype_local_chanmode() */ +#define HOOKTYPE_LOCAL_CHANMODE 38 +/** See hooktype_remote_chanmode() */ +#define HOOKTYPE_REMOTE_CHANMODE 39 +/** See hooktype_modechar_del() */ +#define HOOKTYPE_MODECHAR_DEL 40 +/** See hooktype_modechar_add() */ +#define HOOKTYPE_MODECHAR_ADD 41 +/** See hooktype_away() */ +#define HOOKTYPE_AWAY 42 +/** See hooktype_pre_invite() */ +#define HOOKTYPE_PRE_INVITE 43 +/** See hooktype_invite() */ +#define HOOKTYPE_INVITE 44 +/** See hooktype_pre_knock() */ +#define HOOKTYPE_PRE_KNOCK 45 +/** See hooktype_knock() */ +#define HOOKTYPE_KNOCK 46 +/** See hooktype_whois() */ +#define HOOKTYPE_WHOIS 47 +/** See hooktype_who_status() */ +#define HOOKTYPE_WHO_STATUS 48 +/** See hooktype_pre_kill() */ +#define HOOKTYPE_PRE_KILL 49 +/** See hooktype_local_kill() */ +#define HOOKTYPE_LOCAL_KILL 50 +/** See hooktype_rehashflag() */ +#define HOOKTYPE_REHASHFLAG 51 +/** See hooktype_configposttest() */ +#define HOOKTYPE_CONFIGPOSTTEST 52 +/** See hooktype_rehash() */ +#define HOOKTYPE_REHASH 53 +/** See hooktype_rehash_complete() */ +#define HOOKTYPE_REHASH_COMPLETE 54 +/** See hooktype_configtest() */ +#define HOOKTYPE_CONFIGTEST 55 +/** See hooktype_configrun() */ +#define HOOKTYPE_CONFIGRUN 56 +/** See hooktype_configrun_ex() */ +#define HOOKTYPE_CONFIGRUN_EX 57 +/** See hooktype_stats() */ +#define HOOKTYPE_STATS 58 +/** See hooktype_local_oper() */ +#define HOOKTYPE_LOCAL_OPER 59 +/** See hooktype_local_pass() */ +#define HOOKTYPE_LOCAL_PASS 60 +/** See hooktype_channel_create() */ +#define HOOKTYPE_CHANNEL_CREATE 61 +/** See hooktype_channel_destroy() */ +#define HOOKTYPE_CHANNEL_DESTROY 62 +/** See hooktype_tkl_except() */ +#define HOOKTYPE_TKL_EXCEPT 63 +/** See hooktype_umode_change() */ +#define HOOKTYPE_UMODE_CHANGE 64 +/** See hooktype_tkl_add() */ +#define HOOKTYPE_TKL_ADD 65 +/** See hooktype_tkl_del() */ +#define HOOKTYPE_TKL_DEL 66 +/** See hooktype_log() */ +#define HOOKTYPE_LOG 67 +/** See hooktype_local_spamfilter() */ +#define HOOKTYPE_LOCAL_SPAMFILTER 68 +/** See hooktype_silenced() */ +#define HOOKTYPE_SILENCED 69 +/** See hooktype_rawpacket_in() */ +#define HOOKTYPE_RAWPACKET_IN 70 +/** See hooktype_packet() */ +#define HOOKTYPE_PACKET 71 +/** See hooktype_handshake() */ +#define HOOKTYPE_HANDSHAKE 72 +/** See hooktype_free_client() */ +#define HOOKTYPE_FREE_CLIENT 73 +/** See hooktype_free_user() */ +#define HOOKTYPE_FREE_USER 74 +/** See hooktype_can_join_limitexceeded() */ +#define HOOKTYPE_CAN_JOIN_LIMITEXCEEDED 75 +/** See hooktype_visible_in_channel() */ +#define HOOKTYPE_VISIBLE_IN_CHANNEL 76 +/** See hooktype_see_channel_in_whois() */ +#define HOOKTYPE_SEE_CHANNEL_IN_WHOIS 77 +/** See hooktype_join_data() */ +#define HOOKTYPE_JOIN_DATA 78 +/** See hooktype_oper_invite_ban() */ +#define HOOKTYPE_OPER_INVITE_BAN 79 +/** See hooktype_view_topic_outside_channel() */ +#define HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL 80 +/** See hooktype_chan_permit_nick_change() */ +#define HOOKTYPE_CHAN_PERMIT_NICK_CHANGE 81 +/** See hooktype_is_channel_secure() */ +#define HOOKTYPE_IS_CHANNEL_SECURE 82 +/** See hooktype_channel_synced() */ +#define HOOKTYPE_CHANNEL_SYNCED 83 +/** See hooktype_can_sajoin() */ +#define HOOKTYPE_CAN_SAJOIN 84 +/** See hooktype_check_init() */ +#define HOOKTYPE_CHECK_INIT 85 +/** See hooktype_mode_deop() */ +#define HOOKTYPE_MODE_DEOP 86 +/** See hooktype_dcc_denied() */ +#define HOOKTYPE_DCC_DENIED 87 +/** See hooktype_secure_connect() */ +#define HOOKTYPE_SECURE_CONNECT 88 +/** See hooktype_can_bypass_channel_message_restriction() */ +#define HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION 89 +/** See hooktype_require_sasl() */ +#define HOOKTYPE_REQUIRE_SASL 90 +/** See hooktype_sasl_continuation() */ +#define HOOKTYPE_SASL_CONTINUATION 91 +/** See hooktype_sasl_result() */ +#define HOOKTYPE_SASL_RESULT 92 +/** See hooktype_place_host_ban() */ +#define HOOKTYPE_PLACE_HOST_BAN 93 +/** See hooktype_find_tkline_match() */ +#define HOOKTYPE_FIND_TKLINE_MATCH 94 +/** See hooktype_welcome() */ +#define HOOKTYPE_WELCOME 95 +/** See hooktype_pre_command() */ +#define HOOKTYPE_PRE_COMMAND 96 +/** See hooktype_post_command() */ +#define HOOKTYPE_POST_COMMAND 97 +/** See hooktype_new_message() */ +#define HOOKTYPE_NEW_MESSAGE 98 +/** See hooktype_is_handshake_finished() */ +#define HOOKTYPE_IS_HANDSHAKE_FINISHED 99 +/** See hooktype_pre_local_quit_chan() */ +#define HOOKTYPE_PRE_LOCAL_QUIT_CHAN 100 +/** See hooktype_ident_lookup() */ +#define HOOKTYPE_IDENT_LOOKUP 101 +/** See hooktype_account_login() */ +#define HOOKTYPE_ACCOUNT_LOGIN 102 +/** See hooktype_close_connection() */ +#define HOOKTYPE_CLOSE_CONNECTION 103 /* Adding a new hook here? * 1) Add the #define HOOKTYPE_.... with a new number @@ -1025,111 +1133,931 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va */ /* Hook prototypes */ -int hooktype_local_connect(Client *client); -int hooktype_remote_connect(Client *client); -int hooktype_server_connect(Client *client); -int hooktype_server_sync(Client *client); -int hooktype_post_server_connect(Client *client); -char *hooktype_pre_local_quit(Client *client, char *comment); -int hooktype_local_quit(Client *client, MessageTag *mtags, char *comment); -int hooktype_remote_quit(Client *client, MessageTag *mtags, char *comment); -int hooktype_unkuser_quit(Client *client, MessageTag *mtags, char *comment); +/** Called when a local user connects, allows pausing or rejecting the user (function prototype for HOOKTYPE_PRE_LOCAL_CONNECT). + * @param client The client + * @retval HOOK_DENY Stop the connection (hold/pause it). + * @retval HOOK_ALLOW Allow the connection (stop processing other modules) + * @retval HOOK_CONTINUE Allow the connection, unless another module blocks it. + */ int hooktype_pre_local_connect(Client *client); -int hooktype_server_quit(Client *client, MessageTag *mtags); -int hooktype_local_nickchange(Client *client, char *newnick); -int hooktype_remote_nickchange(Client *client, char *newnick); -int hooktype_can_join(Client *client, Channel *channel, char *key, char *parv[]); -int hooktype_pre_local_join(Client *client, Channel *channel, char *parv[]); -int hooktype_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); -int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); -char *hooktype_pre_local_part(Client *client, Channel *channel, char *comment); -int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); -int hooktype_remote_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); -char *hooktype_pre_local_kick(Client *client, Client *victim, Channel *channel, char *comment); -int hooktype_can_kick(Client *client, Client *victim, Channel *channel, char *comment, long client_flags, long victim_flags, char **error); -int hooktype_local_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); -int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); -char *hooktype_pre_usermsg(Client *client, Client *to, char *text, SendType sendtype); -int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, SendType sendtype); -int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, char **text, char **errmsg, SendType sendtype); -int hooktype_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); -int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, SendType sendtype); -int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); -char *hooktype_pre_local_topic(Client *client, Channel *channel, char *topic); -int hooktype_local_topic(Client *client, Channel *channel, char *topic); -int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, char *topic); -int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); -int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); -int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); -int hooktype_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); -int hooktype_modechar_del(Channel *channel, int modechar); -int hooktype_modechar_add(Channel *channel, int modechar); -int hooktype_away(Client *client, MessageTag *mtags, char *reason); -int hooktype_pre_invite(Client *client, Client *acptr, Channel *channel, int *override); -int hooktype_invite(Client *from, Client *to, Channel *channel, MessageTag *mtags); -int hooktype_pre_knock(Client *client, Channel *channel); -int hooktype_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment); -int hooktype_whois(Client *client, Client *target); -int hooktype_who_status(Client *client, Client *target, Channel *channel, Member *member, char *status, int cansee); -int hooktype_pre_kill(Client *client, Client *victim, char *killpath); -int hooktype_local_kill(Client *client, Client *victim, char *comment); -int hooktype_rehashflag(Client *client, char *str); -int hooktype_configposttest(int *errors); -int hooktype_rehash(void); -int hooktype_stats(Client *client, char *str); -int hooktype_configtest(ConfigFile *cfptr, ConfigEntry *ce, int section, int *errors); -int hooktype_configrun(ConfigFile *cfptr, ConfigEntry *ce, int section); -int hooktype_configrun_ex(ConfigFile *cfptr, ConfigEntry *ce, int section, void *ptr); -int hooktype_local_oper(Client *client, int add); -int hooktype_local_pass(Client *client, char *password); -int hooktype_channel_create(Client *client, Channel *channel); -int hooktype_channel_destroy(Channel *channel, int *should_destroy); -int hooktype_tkl_except(Client *cptr, int ban_type); -int hooktype_umode_change(Client *client, long setflags, long newflags); -int hooktype_rehash_complete(void); -int hooktype_tkl_add(Client *client, TKL *tkl); -int hooktype_tkl_del(Client *client, TKL *tkl); -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, SendType sendtype); -int hooktype_rawpacket_in(Client *client, char *readbuf, int *length); -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); -int hooktype_free_user(Client *acptr); -int hooktype_can_join_limitexceeded(Client *client, Channel *channel, char *key, char *parv[]); -int hooktype_visible_in_channel(Client *client, Channel *channel); -int hooktype_join_data(Client *who, Channel *channel); -int hooktype_oper_invite_ban(Client *client, Channel *channel); -int hooktype_view_topic_outside_channel(Client *client, Channel *channel); -int hooktype_chan_permit_nick_change(Client *client, Channel *channel); -int hooktype_is_channel_secure(Channel *channel); -int hooktype_can_send_to_channel_secure(Client *client, Channel *channel); -int hooktype_channel_synced(Channel *channel, int merge, int removetheirs, int nomode); -int hooktype_can_sajoin(Client *target, Channel *channel, Client *client); -int hooktype_check_init(Client *cptr, char *sockname, size_t size); -int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int what, int modechar, long my_access, char **badmode); -int hooktype_see_channel_in_whois(Client *client, Client *target, Channel *channel); -int hooktype_dcc_denied(Client *client, char *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *denydcc); + +/** Called when a local user connects (function prototype for HOOKTYPE_LOCAL_CONNECT). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_local_connect(Client *client); + +/** Called when a remote user connects (function prototype for HOOKTYPE_REMOTE_CONNECT). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_connect(Client *client); + +/** Called when a local user disconnects, allows changing the quit/disconnect reason (function prototype for HOOKTYPE_PRE_LOCAL_QUIT). + * @param client The client + * @param client The quit/disconnect reason + * @return The quit reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason. + */ +char *hooktype_pre_local_quit(Client *client, char *comment); + +/** Called when a local user quits or otherwise disconnects (function prototype for HOOKTYPE_PRE_LOCAL_QUIT). + * @param client The client + * @param mtags Message tags associated with the quit + * @param comment The quit/exit reason + * @return The return value is ignored (use return 0) + */ +int hooktype_local_quit(Client *client, MessageTag *mtags, char *comment); + +/** Called when a remote user qutis or otherwise disconnects (function prototype for HOOKTYPE_REMOTE_QUIT). + * @param client The client + * @param mtags Message tags associated with the quit + * @param comment The quit/exit reason + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_quit(Client *client, MessageTag *mtags, char *comment); + +/** Called when an unregistered user disconnects, so before the user was fully online (function prototype for HOOKTYPE_UNKUSER_QUIT). + * @param client The client + * @param mtags Message tags associated with the quit + * @param comment The quit/exit reason + * @return The return value is ignored (use return 0) + */ +int hooktype_unkuser_quit(Client *client, MessageTag *mtags, char *comment); + +/** Called when a local or remote server connects / links in (function prototype for HOOKTYPE_SERVER_CONNECT). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_server_connect(Client *client); + +/** Called very early when doing an outgoing server connect (function prototype for HOOKTYPE_SERVER_HANDSHAKE_OUT). + * @param client The client + * @return The return value is ignored (use return 0) + */ int hooktype_server_handshake_out(Client *client); + +/** Called on new locally connected server, in or out, after all users/channels/TKLs/etc have been synced, but before EOS (function prototype for HOOKTYPE_SERVER_SYNC). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_server_sync(Client *client); + +/** Called when a local or remote server connects / links in, but only after EOS (End Of Sync) has been received or sent (function prototype for HOOKTYPE_POST_SERVER_CONNECT). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_post_server_connect(Client *client); + +/** Called when a local or remote server is linked in and fully synced, after EOS / End Of Sync (function prototype for HOOKTYPE_SERVER_SYNCED). + * @param client The client + * @return The return value is ignored (use return 0) + */ int hooktype_server_synced(Client *client); + +/** Called when a local or remote server disconnects (function prototype for HOOKTYPE_SERVER_QUIT). + * @param client The client + * @param mtags Message tags associated with the disconnect + * @return The return value is ignored (use return 0) + */ +int hooktype_server_quit(Client *client, MessageTag *mtags); + +/** Called when a local user changes the nick name (function prototype for HOOKTYPE_LOCAL_NICKCHANGE). + * @param client The client + * @param newnick The new nick name + * @return The return value is ignored (use return 0) + */ +int hooktype_local_nickchange(Client *client, char *newnick); + +/** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE). + * @param client The client + * @param newnick The new nick name + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_nickchange(Client *client, char *newnick); + +/** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_CAN_JOIN). + * @param client The client + * @param channel The channel the user wants to join + * @param key The key supplied by the client + * @param parv The parameters from the JOIN. Normally you should not use this. + * @return Return 0 to allow the user, any other value should be an IRC numeric (eg: ERR_BANNEDFROMCHAN). + */ +int hooktype_can_join(Client *client, Channel *channel, char *key, char *parv[]); + +/** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_PRE_LOCAL_JOIN). + * FIXME: It's not entirely clear why we have both hooktype_can_join() and hooktype_pre_local_join(). + * @param client The client + * @param channel The channel the user wants to join + * @param parv The parameters from the JOIN. May contain channel key in parv[2]. + * @retval HOOK_DENY Deny the join. + * @retval HOOK_ALLOW Allow the join (stop processing other modules) + * @retval HOOK_CONTINUE Allow the join, unless another module blocks it. + */ +int hooktype_pre_local_join(Client *client, Channel *channel, char *parv[]); + +/** Called when a local user joins a channel (function prototype for HOOKTYPE_LOCAL_JOIN). + * @param client The client + * @param channel The channel the user wants to join + * @param mtags Message tags associated with the event + * @param parv The parameters from the JOIN. May contain channel key in parv[2]. + * @return The return value is ignored (use return 0) + */ +int hooktype_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); + +/** Called when a remote user joins a channel (function prototype for HOOKTYPE_REMOTE_JOIN). + * @param client The client + * @param channel The channel the user wants to join + * @param mtags Message tags associated with the event + * @param parv The parameters from the JOIN. May contain channel key in parv[2]. + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); + +/** Called when a local user wants to part a channel (function prototype for HOOKTYPE_PRE_LOCAL_PART). + * @param client The client + * @param channel The channel the user wants to part + * @param comment The PART reason, this may be NULL. + * @return The part reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason. + */ +char *hooktype_pre_local_part(Client *client, Channel *channel, char *comment); + +/** Called when a local user parts a channel (function prototype for HOOKTYPE_LOCAL_PART). + * @param client The client + * @param channel The channel the user is leaving + * @param mtags Message tags associated with the event + * @param comment The PART reason, this may be NULL. + * @return The return value is ignored (use return 0) + */ +int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); + +/** Called when a remote user parts a channel (function prototype for HOOKTYPE_REMOTE_PART). + * @param client The client + * @param channel The channel the user is leaving + * @param mtags Message tags associated with the event + * @param comment The PART reason, this may be NULL. + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); + +/** Do not use this function, use hooktype_can_kick() instead! + */ +char *hooktype_pre_local_kick(Client *client, Client *victim, Channel *channel, char *comment); + +/** Called when a local user wants to kick another user from a channel (function prototype for HOOKTYPE_CAN_KICK). + * @param client The client issuing the command + * @param victim The victim that should be kicked + * @param channel The channel the user should be kicked from + * @param comment The KICK reason, this may be NULL. + * @param client_flags The access flags of 'client', one of CHFL_*, eg CHFL_CHANOP. + * @param victim_flags The access flags of 'victim', one of CHFL_*, eg CHFL_VOICE. + * @param error The error message that should be shown to the user (full IRC protocol line). + * @retval EX_DENY Deny the KICK (unless IRCOp with sufficient override rights). + * @retval EX_ALWAYS_DENY Deny the KICK always (even if IRCOp). + * @retval EX_ALLOW Allow the kick, unless another module blocks it. + */ +int hooktype_can_kick(Client *client, Client *victim, Channel *channel, char *comment, long client_flags, long victim_flags, char **error); + +/** Called when a local user is kicked (function prototype for HOOKTYPE_LOCAL_KICK). + * @param client The client issuing the command + * @param victim The victim that should be kicked + * @param channel The channel the user should be kicked from + * @param mtags Message tags associated with the event + * @param comment The KICK reason, this may be NULL. + * @return The return value is ignored (use return 0) + */ +int hooktype_local_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); + +/** Called when a remote user is kicked (function prototype for HOOKTYPE_REMOTE_KICK). + * @param client The client issuing the command + * @param victim The victim that should be kicked + * @param channel The channel the user should be kicked from + * @param mtags Message tags associated with the event + * @param comment The KICK reason, this may be NULL. + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); + +/** Called right before a message is sent to the channel (function prototype for HOOKTYPE_PRE_CHANMSG). + * This function is only used by delayjoin. It cannot block a message. See hooktype_can_send_to_user() instead! + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param text The text that will be sent + * @return The return value is ignored (use return 0) + */ +int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, SendType sendtype); + +/** Called when a user wants to send a message to another user (function prototype for HOOKTYPE_CAN_SEND_TO_USER). + * @param client The sender + * @param target The recipient + * @param text The text to be sent (double pointer!) + * @param errmsg The error message. If you block the message (HOOK_DENY) then you MUST set this! + * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. + * @retval HOOK_DENY Deny the message. The 'errmsg' will be sent to the user. + * @retval HOOK_CONTINUE Allow the message, unless other modules block it. + */ +int hooktype_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); + +/** Called when a user wants to send a message to a channel (function prototype for HOOKTYPE_CAN_SEND_TO_CHANNEL). + * @param client The sender + * @param channel The channel to send to + * @param member The membership struct, so you can see for example op status. + * @param text The text to be sent (double pointer!) + * @param errmsg The error message. If you block the message (HOOK_DENY) then you MUST set this! + * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. + * @retval HOOK_DENY Deny the message. The 'errmsg' will be sent to the user. + * @retval HOOK_CONTINUE Allow the message, unless other modules block it. + */ +int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, char **text, char **errmsg, SendType sendtype); + +/** Called when a message is sent from one user to another user (function prototype for HOOKTYPE_USERMSG). + * @param client The sender + * @param to The recipient + * @param mtags Message tags associated with the event + * @param text The text + * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. + * @return The return value is ignored (use return 0) + */ +int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, SendType sendtype); + +/** Called when a message is sent to a channel (function prototype for HOOKTYPE_CHANMSG). + * @param client The sender + * @param channel The channel + * @param sendflags One of SEND_* (eg SEND_ALL, SKIP_DEAF). + * @param prefix Either zero, one or a combination of PREFIX_*. + * @param target Target string, usually this is "#channel", but it can also contain prefixes like "@#channel" + * @param mtags Message tags associated with the event + * @param text The text + * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. + * @return The return value is ignored (use return 0) + */ +int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); + +/** Called when a local user wants to change the channel topic (function prototype for HOOKTYPE_PRE_LOCAL_TOPIC). + * @param client The client + * @param channel The channel + * @param topic The new requested topic + * @return The new topic (you may also return 'topic'), or NULL if the topic change request should be rejected. + */ +char *hooktype_pre_local_topic(Client *client, Channel *channel, char *topic); + +/** Called when the channel topic is changed (function prototype for HOOKTYPE_TOPIC). + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param topic The new topic + * @return The return value is ignored (use return 0) + */ +int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, char *topic); + +/** Called when a local user changes channel modes, called early (function prototype for HOOKTYPE_PRE_LOCAL_CHANMODE). + * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the + * mode is changed. It is currently only used by the delayjoin module. + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param modebuf The mode buffer, for example "+o" + * @param parabuf The parameter buffer, for example "NiceOp" + * @param sendts Send timestamp + * @param samode Is this an SAMODE? + * @return The return value is ignored (use return 0) + */ +int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); + +/** Called when a remote user changes channel modes, called early (function prototype for HOOKTYPE_PRE_REMOTE_CHANMODE). + * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the + * mode is changed. It is currently only used by the delayjoin module. + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param modebuf The mode buffer, for example "+o" + * @param parabuf The parameter buffer, for example "NiceOp" + * @param sendts Send timestamp + * @param samode Is this an SAMODE? + * @return The return value is ignored (use return 0) + */ +int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); + +/** Called when a local user changes channel modes (function prototype for HOOKTYPE_LOCAL_CHANMODE). + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param modebuf The mode buffer, for example "+o" + * @param parabuf The parameter buffer, for example "NiceOp" + * @param sendts Send timestamp + * @param samode Is this an SAMODE? + * @return The return value is ignored (use return 0) + */ +int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); + +/** Called when a remote user changes channel modes (function prototype for HOOKTYPE_REMOTE_CHANMODE). + * @param client The client + * @param channel The channel + * @param mtags Message tags associated with the event + * @param modebuf The mode buffer, for example "+o" + * @param parabuf The parameter buffer, for example "NiceOp" + * @param sendts Send timestamp + * @param samode Is this an SAMODE? + * @return The return value is ignored (use return 0) + */ +int hooktype_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); + +/** Called when a channel mode is removed by a local or remote user (function prototype for HOOKTYPE_MODECHAR_DEL). + * NOTE: This is currently not terribly useful for most modules. It is used by by the floodprot and noknock modules. + * @param channel The channel + * @param modechar The mode character, eg 'k' + * @return The return value is ignored (use return 0) + */ +int hooktype_modechar_del(Channel *channel, int modechar); + +/** Called when a channel mode is set by a local or remote user (function prototype for HOOKTYPE_MODECHAR_ADD). + * NOTE: This is currently not terribly useful for most modules. It is used by by the floodprot and noknock modules. + * @param channel The channel + * @param modechar The mode character, eg 'k' + * @return The return value is ignored (use return 0) + */ +int hooktype_modechar_add(Channel *channel, int modechar); + +/** Called when a user sets away status or unsets away status (function prototype for HOOKTYPE_AWAY). + * @param client The client + * @param mtags Message tags associated with the event + * @param reason The away reason, or NULL if away is unset. + * @return The return value is ignored (use return 0) + */ +int hooktype_away(Client *client, MessageTag *mtags, char *reason); + +/** Called when a user wants to invite another user to a channel (function prototype for HOOKTYPE_PRE_INVITE). + * @param client The client + * @param acptr The user who is invited (victim) + * @param channel The channel the user is invited to + * @param override If this was an override (1) or not. Note: pointer to an int! + * @retval HOOK_DENY Deny the invite. + * @retval HOOK_ALLOW Allow the invite (stop processing other modules) + * @retval HOOK_CONTINUE Allow the invite, unless another module blocks it. + */ +int hooktype_pre_invite(Client *client, Client *acptr, Channel *channel, int *override); + +/** Called when a user invites another user to a channel (function prototype for HOOKTYPE_INVITE). + * @param client The client + * @param acptr The user who is invited (victim) + * @param channel The channel the user is invited to + * @param mtags Message tags associated with the event + * @return The return value is ignored (use return 0) + */ +int hooktype_invite(Client *client, Client *acptr, Channel *channel, MessageTag *mtags); + +/** Called when a user wants to knock on a channel (function prototype for HOOKTYPE_PRE_KNOCK). + * FIXME: where is the knock reason ? + * @param client The client + * @param channel The channel to knock on + * @retval HOOK_DENY Deny the knock. + * @retval HOOK_ALLOW Allow the knock (stop processing other modules) + * @retval HOOK_CONTINUE Allow the knock, unless another module blocks it. + */ +int hooktype_pre_knock(Client *client, Channel *channel); + +/** Called when a user knocks on a channel (function prototype for HOOKTYPE_KNOCK). + * @param client The client + * @param channel The channel to knock on + * @param mtags Message tags associated with the event + * @param comment The knock reason + * @return The return value is ignored (use return 0) + */ +int hooktype_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment); + +/** Called when a user whoises someone (function prototype for HOOKTYPE_WHOIS). + * @param client The client issuing the command + * @param target The user who is the target of the /WHOIS. + * @return The return value is ignored (use return 0) + */ +int hooktype_whois(Client *client, Client *target); + +/** Called to add letters to the WHO status column (function prototype for HOOKTYPE_WHO_STATUS). + * If a user does a /WHO request, then WHO will show a number of status flags + * such as B to show the user is a bot (see "HELPOP WHO" for the full list). + * @param client The client + * @param target The target, the user for which we should display WHO status flags + * @param channel The channel if a channel WHO, or NULL + * @param member The membership information, or NULL + * @param status The current status flags, so far + * @param cansee If 'client' can see 'target' (eg: in same channel or -i) + * @return Return 0 if no WHO status flags need to be added, otherwise return the ascii character (eg: return 'B'). + */ +int hooktype_who_status(Client *client, Client *target, Channel *channel, Member *member, char *status, int cansee); + +/** Called when an IRCOp wants to kill another user (function prototype for HOOKTYPE_PRE_KILL). + * @param client The client + * @param victim The user who should be killed + * @param reason The kill reason + * @retval EX_DENY Deny the KICK (unless IRCOp with sufficient override rights). + * @retval EX_ALWAYS_DENY Deny the KICK always (even if IRCOp). + * @retval EX_ALLOW Allow the kick, unless another module blocks it. + */ +int hooktype_pre_kill(Client *client, Client *victim, char *reason); + +/** Called when a local user kills another user (function prototype for HOOKTYPE_LOCAL_KILL). + * Note that kills from remote IRCOps will show up as regular quits, so use hooktype_remote_quit() and hooktype_local_quit(). + * @param client The client + * @param victim The victim + * @param comment The kill reason + * @return The return value is ignored (use return 0) + */ +int hooktype_local_kill(Client *client, Client *victim, char *comment); + +/** Called when an IRCOp /REHASH'es, and passes the parameters (function prototype for HOOKTYPE_REHASHFLAG). + * FIXME: shouldn't this be merged with hooktype_rehash() ? + * @param client The client issuing the command, or NULL if rehashing due to system signal. + * @param str The rehash flag (eg: "-all") + * @return The return value is ignored (use return 0) + */ +int hooktype_rehashflag(Client *client, char *str); + +/** Called when the server is rehashing (function prototype for HOOKTYPE_REHASH). + * @return The return value is ignored (use return 0) + */ +int hooktype_rehash(void); + +/** Called when the server has completed rehashing (function prototype for HOOKTYPE_REHASH_COMPLETE). + * @return The return value is ignored (use return 0) + */ +int hooktype_rehash_complete(void); + +/** Called when searching for a test function for a specific configuration item (function prototype for HOOKTYPE_CONFIGTEST). + * This is part of the configuration API, which is better documented at the + * wiki at https://www.unrealircd.org/docs/Dev:Configuration_API + * @param cfptr Configuration file + * @param ce Configuration entry + * @param section One of CONFIG_*, eg: CONFIG_MAIN. + * @param errors Counter for errors + * @retval 0 This entry is not for us, we don't know anything about it. + * @retval -1 Errors encountered (the number of errors is stored in *errors) + * @retval 1 This entry is handled and is without any errors. + */ +int hooktype_configtest(ConfigFile *cfptr, ConfigEntry *ce, int section, int *errors); + +/** Called after all hooktype_configtest() have run, to check for missing config items (function prototype for HOOKTYPE_CONFIGPOSTTEST). + * @param errors The number of errors + * @returns In case of errors, return -1. + */ +int hooktype_configposttest(int *errors); + +/** Called to run/do the active configuration for this configuration item (function prototype for HOOKTYPE_CONFIGRUN). + * This is part of the configuration API, which is better documented at the + * wiki at https://www.unrealircd.org/docs/Dev:Configuration_API + * @param cfptr Configuration file + * @param ce Configuration entry + * @param section One of CONFIG_*, eg: CONFIG_MAIN. + * @retval 0 This entry is not for us, we don't know anything about it. + * @retval 1 This entry is for us, it is now handled, don't call any other modules for it anymore. + */ +int hooktype_configrun(ConfigFile *cfptr, ConfigEntry *ce, int section); + +/** Called to run/do the active configuration for this configuration item - extended version (function prototype for HOOKTYPE_CONFIGRUN_EX). + * This particular "extended version" is only used for extending listen { } options, so you probably don't need this one. + * Use hooktype_configrun() instead! + * @param cfptr Configuration file + * @param ce Configuration entry + * @param section One of CONFIG_*, eg: CONFIG_MAIN. + * @param ptr Pointer to something + * @retval 0 This entry is not for us, we don't know anything about it. + * @retval 1 This entry is for us, it is now handled, don't call any other modules for it anymore. + */ +int hooktype_configrun_ex(ConfigFile *cfptr, ConfigEntry *ce, int section, void *ptr); + +/** Called when a user types /STATS (function prototype for HOOKTYPE_STATS). + * This way a module can add a new STATS item, eg 'STATS something' + * @param client The client issuing the command + * @param str The parameter to the STATS command, eg 'something'. + * @return The return value is ignored (use return 0) + */ +int hooktype_stats(Client *client, char *str); + +/** Called when a user becomes IRCOp or is no longer an IRCOp (function prototype for HOOKTYPE_LOCAL_OPER). + * @param client The client + * @param add 1 if the user becomes IRCOp, 0 if the user is no longer IRCOp + * @return The return value is ignored (use return 0) + */ +int hooktype_local_oper(Client *client, int add); + +/** Called when a client sends a PASS command (function prototype for HOOKTYPE_LOCAL_PASS). + * @param client The client + * @param password The password supplied by the client + * @return The return value is ignored (use return 0) + */ +int hooktype_local_pass(Client *client, char *password); + +/** Called when a channel is created (function prototype for HOOKTYPE_CHANNEL_CREATE). + * @param client The client + * @param channel The channel that just got created + * @note This function is not used much, use hooktype_local_join() and hooktype_remote_join() instead. + * @return The return value is ignored (use return 0) + */ +int hooktype_channel_create(Client *client, Channel *channel); + +/** Called when a channel is completely destroyed (function prototype for HOOKTYPE_CHANNEL_DESTROY). + * @param channel The channel that is about to be destroyed + * @param should_destroy Module can set this to 1 to prevent destriction + * @note A channel is usually destroyed due to the last user leaving. But in some cases + * a channel is created and then immediately destroyed within nanoseconds. Just so you know. + * @return The return value is ignored (use return 0) + */ +int hooktype_channel_destroy(Channel *channel, int *should_destroy); + +/** Called when a user matches a TKL and is pending to be killed (function prototype for HOOKTYPE_TKL_EXCEPT). + * @param client The client + * @param ban_type The TKL type, one of TKL_*. For example TKL_GLOBAL|TKL_KILL for a gline. + * @retval 0 Ban/kill the user. + * @retval 1 User is exempt, do NOT kill or ban. + */ +int hooktype_tkl_except(Client *client, int ban_type); + +/** Called when the user modes of a user change (function prototype for HOOKTYPE_UMODE_CHANGE). + * @param client The client + * @param setflags The current user modes + * @param newflags The new user modes + * @note The user mode can be changed due to a MODE by the user itself, by a server, or by SVSMODE/SVS2MODE from Services. + * @return The return value is ignored (use return 0) + */ +int hooktype_umode_change(Client *client, long setflags, long newflags); + +/** Called when a new TKL is added (function prototype for HOOKTYPE_TKL_ADD). + * @param client The client adding the TKL (this can be &me) + * @param tkl The TKL entry + * @return The return value is ignored (use return 0) + */ +int hooktype_tkl_add(Client *client, TKL *tkl); + +/** Called when removing an existing TKL (function prototype for HOOKTYPE_TKL_DEL). + * @param client The client removing the TKL (this can be &me) + * @param tkl The TKL entry + * @return The return value is ignored (use return 0) + */ +int hooktype_tkl_del(Client *client, TKL *tkl); + +/** Called when something is logged via the ircd_log() function (function prototype for HOOKTYPE_LOG). + * @param flags One of LOG_*, such as LOG_ERROR. + * @param timebuf The time buffer, such as "[2030-01-01 12:00:00]" + * @param buf The text to be logged + * @return The return value is ignored (use return 0) + */ +int hooktype_log(int flags, char *timebuf, char *buf); + +/** Called when a local user matches a spamfilter (function prototype for HOOKTYPE_LOCAL_SPAMFILTER). + * @param client The client + * @param str The text that matched, this may be stripped from color and control codes. + * @param str_in The original text + * @param target The spamfilter type, one of SPAMF_*, such as SPAMF_CHANMSG. + * @param destination The destination, such as the name of another client or channel + * @param tkl The spamfilter TKL entry that matched + * @return The return value is ignored (use return 0) + */ +int hooktype_local_spamfilter(Client *client, char *str, char *str_in, int type, char *target, TKL *tkl); + +/** Called when a user sends something to a user that has the sender silenced (function prototype for HOOKTYPE_SILENCED). + * UnrealIRCd support a SILENCE list. If the target user has added someone on the silence list, eg via SILENCE +BadUser, + * and then 'BadUser' tries to send a message to this user, this hook will be triggered. + * @param client The client trying to send a message/notice + * @param target The intended recipient of the message + * @param sendtype Indicating if it is a PRIVMSG, NOTICE or something else. + * @note This function is rarely used. + * @return The return value is ignored (use return 0) + */ +int hooktype_silenced(Client *client, Client *target, SendType sendtype); + +/** Called on every incoming packet (function prototype for HOOKTYPE_RAWPACKET_IN). + * This is quite invasive, so only use this if you cannot do the same via some other means (eg overrides or hooks). + * The typical use cases are things like: handling an entirely different protocol (eg: websocket module), + * or old stuff like codepage conversions, basically: things that work on entire packets. + * @param client The client + * @param readbuf The buffer + * @param length The length of the buffer + * @note If you want to alter the buffer contents then replace 'readbuf' with your own buffer and set 'length' appropriately. + * @return The return value is ignored (use return 0) + */ +int hooktype_rawpacket_in(Client *client, char *readbuf, int *length); + +/** Called when a packet is received or sent (function prototype for HOOKTYPE_PACKET). + * @param client The locally connected sender, this can be &me + * @param to The locally connected recipient, this can be &me + * @param intended_to The originally intended recipient, this could be a remote user + * @param msg The buffer + * @param length The length of the buffer + * @note When reading a packet, 'client' will indicate the locally connected user and 'to' will be &me. + * When sending a pcket, 'client' will be &me and 'to' will be the locally connected user. + * If you want to alter the buffer contents then replace 'msg' with your own buffer and set 'length' appropriately. + * @return The return value is ignored (use return 0) + */ +int hooktype_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length); + +/** Called very early when a client connects (function prototype for HOOKTYPE_HANDSHAKE). + * This is called as soon as the socket is connected and the client is being set up, + * so before the client has sent any application data, and certainly before it is + * known whether this client will become a user or a server. + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_handshake(Client *client); + +/** Called when a client structure is freed (function prototype for HOOKTYPE_FREE_CLIENT). + * @param client The client + * @note Normally you use hooktype_local_quit(), hooktype_remote_quit() and hooktype_unkuser_quit() for this. + * @return The return value is ignored (use return 0) + */ +int hooktype_free_client(Client *client); + +/** Called when the user structure, client->user, is being freed (function prototype for HOOKTYPE_FREE_USER). + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_free_user(Client *client); + +/** Called when +l limit is exceeded when joining (function prototype for HOOKTYPE_CAN_JOIN_LIMITEXCEEDED). + * @param client The client + * @param channel The channel + * @param key The channel key + * @param parv The join parameters + * @note I don't think this works? + * @return Unclear.. + */ +int hooktype_can_join_limitexceeded(Client *client, Channel *channel, char *key, char *parv[]); + +/** Called to check if the user is visible in the channel (function prototype for HOOKTYPE_VISIBLE_IN_CHANNEL). + * For example, the delayjoin module (+d/+D) will 'return 0' here if the user is hidden due to delayed join. + * @param client The client + * @param channel The channel + * @retval 0 The user is NOT visible + * @retval 1 The user is visible + */ +int hooktype_visible_in_channel(Client *client, Channel *channel); + +/** Called to check if the channel of a user should be shown in WHOIS/WHO (function prototype for HOOKTYPE_SEE_CHANNEL_IN_WHOIS). + * @param client The client ASKING, eg doing the /WHOIS. + * @param target The client who is being interrogated + * @param channel The channel that 'client' is in + * @retval 0 The channel should NOT be visible + * @retval 1 Show the channel + */ +int hooktype_see_channel_in_whois(Client *client, Client *target, Channel *channel); + +/** Called when a user is added to a channel (function prototype for HOOKTYPE_JOIN_DATA). + * Note that normally you use hooktype_local_join() and hooktype_remote_join() for this. + * This function only exists so it is easy to work with dynamic data, and even + * that is an old idea now that we have the moddata system. + * @param client The client joining + * @param channel The channel the client joined to + * @return The return value is ignored (use return 0) + */ +int hooktype_join_data(Client *who, Channel *channel); + +/** Should the user be able to bypass bans? (function prototype for HOOKTYPE_OPER_INVITE_BAN). + * @param client The client + * @param channel The channel + * @note The actual meaning of this hook is more complex, you are unlikely to use it, anyway. + * @retval HOOK_DENY Deny the join if the user is also banned + * @retval HOOK_CONTINUE Obey the normal rules + */ +int hooktype_oper_invite_ban(Client *client, Channel *channel); + +/** Should a user be able to view the topic when not in the channel? (function prototype for HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL). + * @param client The client requesting the topic + * @param channel The channel + * @note This visibility check is only partially implemented. Do not count on it. + * @retval HOOK_DENY Deny the topic request + * @retval HOOK_CONTINUE Obey the normal rules + */ +int hooktype_view_topic_outside_channel(Client *client, Channel *channel); + +/** Is a user permitted to change its nickname? (function prototype for HOOKTYPE_CHAN_PERMIT_NICK_CHANGE). + * This is called for each channel the user is in. This is used by the +N (nonickchange) channel mode. + * @param client The client + * @param channel The channel the user is in + * @retval HOOK_DENY Deny the nick change + * @retval HOOK_CONTINUE Obey the normal rules (allow it, unless denied by something else) + */ +int hooktype_chan_permit_nick_change(Client *client, Channel *channel); + +/** Is the channel considered "secure"? (function prototype for HOOKTYPE_IS_CHANNEL_SECURE). + * This is used by the +z/+Z modules. + * @param channel The channel + * @retval 0 No, the channel is not secure + * @retval 1 Yes, the channel is secure + */ +int hooktype_is_channel_secure(Channel *channel); + +/** Called after a channel is synced due to netmerge (function prototype for HOOKTYPE_CHANNEL_SYNCED). + * When a server connects channel status is exchanged in order to synchronize the two sides of channels. + * After each SJOIN command this function is called to check if anything special + * needs to be join. At the moment this function is only used by channel mode +z + * which will kick out any insecure users if we are the "loosing" side of a split. + * @param channel The channel + * @param merge Set to 1 if merging due to equal timestamps on both sides, 0 otherwise + * @param removetheirs Set to 1 if the other side is the loosing side and we are the winning side. + * @param nomode Set to 1 if this is a SJOIN without modes (rare? services?) + * @retval HOOK_DENY Deny the channel merge. Important: only return this after you have destroyed the channel! + * @retval HOOK_CONTINUE Continue normally + */ +int hooktype_channel_synced(Channel *channel, int merge, int removetheirs, int nomode); + +/** Can the target client be SAJOIN'ed to a particular channel? (function prototype for HOOKTYPE_CAN_SAJOIN). + * @param target The client that should be joined + * @param channel The channel that the client should be joined to + * @param client The client issuing the request (usually IRCOp) + * @retval HOOK_DENY Deny the SAJOIN + * @retval HOOK_CONTINUE Allow the SAJOIN, unless blocked by something else + */ +int hooktype_can_sajoin(Client *target, Channel *channel, Client *client); + +/** Called when the hostname is initialized for a client (function prototype for HOOKTYPE_CHECK_INIT). + * This is a very specific call, it is only meant for the WEBIRC module. + * @param client The client + * @param sockname The socket name + * @param size The size of the socket name? :D + * @retval HOOK_CONTINUE Proceed normally + * @retval HOOK_DENY Reject the connection(?) + */ +int hooktype_check_init(Client *client, char *sockname, size_t size); + +/** May the target user be deoped? (function prototype for HOOKTYPE_MODE_DEOP). + * This is for example used by the +S (Services bot) user mode to block deop requests to services bots. + * @param client The client issuing the command + * @param victim The victim that should be deoped (MODE -o) + * @param channel The channel + * @param what Always MODE_DEL at the moment + * @param modechar The mode character: q/a/o/h/v + * @param my_access Cached result of get_access(), so one of CHFL_*, for example CHFL_CHANOP. + * @param badmode The error string that should be sent to the client + * @retval HOOK_CONTINUE Proceed normally (allow it) + * @retval HOOK_DENY Reject the mode change + * @retval HOOK_ALWAYS_DENY Reject the mode change, even if IRCOp/Services/.. + */ +int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int what, int modechar, long my_access, char **badmode); + +/** Called when a DCC request was denied by the IRCd (function prototype for HOOKTYPE_DCC_DENIED). + * @param client The client who tried to send a file + * @param target The intended recipient + * @param realfile The original file name, may contain strange characters or be very long + * @param displayfile The file name for displaying purposes, properly filtered. + * @param denydcc The deny dcc { ] rule that triggered. + * @return The return value is ignored (use return 0) + */ +int hooktype_dcc_denied(Client *client, char *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *denydcc); + +/** Called in the user accept procedure, when setting the +z user mode (function prototype for HOOKTYPE_SECURE_CONNECT). + * This is only meant to be used by the WEBIRC module, so it can do -z for fake secure users. + * @param client The client + * @return The return value is ignored (use return 0) + */ int hooktype_secure_connect(Client *client); + +/** Can the user bypass a particular channel message restriction? (function prototype for HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION). + * This is for example used to bypass +S (stripcolor) via ~m:color:*!*@a.b.c.d if the user matches that extban. + * @param client The client (sender) + * @param channel The channel + * @param bypass_type The restriction to bypass, for example BYPASS_CHANMSG_COLOR + * @retval HOOK_ALLOW Allow to bypass the restriction + * @retval HOOK_CONTINUE Continue as normal, obey normal rules, deny bypassing the restriction. + */ int hooktype_can_bypass_channel_message_restriction(Client *client, Channel *channel, BypassChannelMessageRestrictionType bypass_type); + +/** Called when xxxx (function prototype for HOOKTYPE_REQUIRE_SASL). + * FIXME: this hook is never called!? + * @param client The client + * @return The return value is ignored (use return 0) + */ int hooktype_require_sasl(Client *client, char *reason); + +/** Called when a SASL continuation response is received (function prototype for HOOKTYPE_SASL_CONTINUATION). + * This is only used by the authprompt module, it unlikely that you need it. + * @param client The client for which the SASL authentication is taking place + * @param buf The AUTHENTICATE buffer + * @retval HOOK_CONTINUE Continue as normal + * @retval HOOK_DENY Do not handle the SASL request, or at least don't show the response to the client. + */ int hooktype_sasl_continuation(Client *client, char *buf); + +/** Called when a SASL result response is received (function prototype for HOOKTYPE_SASL_RESULT). + * This is only used by the authprompt module. + * @param client The client for which the SASL authentication is taking place + * @param successs Whether the SASL authentication was successful (1) or not (0) + * @retval HOOK_CONTINUE Continue as normal + * @retval HOOK_DENY Do not handle the SASL response, or at least don't show the response to the client. + */ int hooktype_sasl_result(Client *client, int success); + +/** Called when a TKL ban should be added on the host (function prototype for HOOKTYPE_PLACE_HOST_BAN). + * This is called for automated bans such as spamfilter hits, flooding, etc. + * This hook can be used to prevent the ban, or as used by the authprompt to delay it. + * @param client The client that should be banned + * @param action The TKL type, such as BAN_ACT_GLINE + * @param reason The ban reason + * @param duration The duration of the ban, 0 for permanent ban + * @return The magic value 99 is used to exempt the user (=do not ban!), otherwise the ban is added. + */ int hooktype_place_host_ban(Client *client, int action, char *reason, long duration); + +/** Called when a TKL ban is hit by this user (function prototype for HOOKTYPE_FIND_TKLINE_MATCH). + * This is called when an existing TKL entry is hit by the user. + * To prevent an automated ban to be added on a host/ip, see hooktype_place_host_ban(). + * @param client The client + * @param tkl The TKL entry + * @return The magic value 99 is used to exempt the user (=do not kill!), otherwise the ban is executed. + */ int hooktype_find_tkline_match(Client *client, TKL *tk); + +/** Called when the user connects for each welcome numeric (function prototype for HOOKTYPE_WELCOME). + * This can be used to send some additional notice or data to the user at a step of your choosing. + * This is called before all numerics with 'after_numeric' set to 0, and then after numeric + * 001, 002, 003, 005, 396, 266, 376. In the last call, 'after_numeric' is 999 when all initial + * numerics have been sent but before the user is auto-joined to channels (if any). + * @param client The client + * @param after_numeric Which numeric has just been sent + * @return The return value is ignored (use return 0) + */ int hooktype_welcome(Client *client, int after_numeric); + +/** Called right before parsing a line and client command (function prototype for HOOKTYPE_PRE_COMMAND). + * This is only used by labeled-reponse. If you think this hook is useful then you + * should probably use the CommandOverride API instead! + * @param client The direct local client connection from which the line is received. + * @param mtags Message tags, if any. + * @param buf The buffer (without message tags) + * @return The return value is ignored (use return 0) + */ int hooktype_pre_command(Client *from, MessageTag *mtags, char *buf); + +/** Called right after finishing a client command (function prototype for HOOKTYPE_POST_COMMAND). + * This is only used by labeled-reponse. If you think this hook is useful then you + * should probably use the CommandOverride API instead! + * @param client The direct local client connection from which the line is received. + * @param mtags Message tags, if any. + * @param buf The buffer (without message tags) + * @return The return value is ignored (use return 0) + */ int hooktype_post_command(Client *from, MessageTag *mtags, char *buf); + +/** Called when new_message() is executed (function prototype for HOOKTYPE_NEW_MESSAGE). + * When a new message with message tags is prepared, code in UnrealIRCd + * and in modules will call new_message(). From that function this hook + * is also called. The purpose of this hook is so you can add additional + * message tags that belong the user. For example it is used + * by the account-tag module to add account=xyz information, see that module for a good example. + * @param sender The client from which the message will be sent + * @param recv_mtags The message tags as originally received before, or NULL if completely new. + * @param mtag_list The newly created message tag list that we are building + * @param signature Special signature when used through new_message_special() + * @return The return value is ignored (use return 0) + */ void hooktype_new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); -int hooktype_is_handshake_finished(Client *acptr); + +/** Is the client handshake finished? (function prototype for HOOKTYPE_IS_HANDSHAKE_FINISHED). + * This is called by the is_handshake_finished() function to check if the user + * can be accepted on IRC, or if there are still other checks/input pending. + * This can be used to "hold" a user temporarily until something happens, such + * as the user typing a password or waiting for a remote access check to return a result. + * For an example usage, see the cap module, which uses it to "hold" the connection + * if a "CAP LS" has been sent and no "CAP END" has been received yet. + * @param client The client + * @retval 1 Yes, the handshake is finished, as far as we are concerned. + * @retval 0 No, the handshake is not yet finished, do not allow the user in yet. + */ +int hooktype_is_handshake_finished(Client *client); + +/** Called upon a local client quit, allows altering the quit message on a per-channel basis (function prototype for HOOKTYPE_PRE_LOCAL_QUIT_CHAN). + * If you don't need to change the quit message on a per-channel basis, but want to change it regardless of channels, then use hooktype_pre_local_quit(). + * If you don't need to change the quit message at all, then use hooktype_local_quit() and hooktype_remote_quit() instead. + * @param client The client + * @param channel The channel + * @param comment The quit message + * @return The original quit message (comment), the new quit message (pointing to your own static buffer), or NULL (no quit message) + */ char *hooktype_pre_local_quit_chan(Client *client, Channel *channel, char *comment); -int hooktype_ident_lookup(Client *acptr); + +/** Called when an ident lookup should be made (function prototype for HOOKTYPE_IDENT_LOOKUP). + * This is used by the ident_lookup module. + * @param client The client + * @return The return value is ignored (use return 0) + */ +int hooktype_ident_lookup(Client *client); + +/** Called when someone logs in/out a services account (function prototype for HOOKTYPE_ACCOUNT_LOGIN). + * The account name can be found in client->user->svid. It will be the string "0" if the user is logged out. + * @param client The client + * @param mtags Message tags associated with the event + * @return The return value is ignored (use return 0) + */ int hooktype_account_login(Client *client, MessageTag *mtags); + +/** Called when closing the connection of a local user (function prototype for HOOKTYPE_CLOSE_CONNECTION). + * This is called from close_connection(). Note that a lot of client information + * has already been freed, so normally you should use the quit/exit functions instead: + * hooktype_local_quit(), hooktype_remote_quit() and hooktype_unkuser_quit(). + * @param client The client + * @return The return value is ignored (use return 0) + */ int hooktype_close_connection(Client *client); +/** @} */ + #ifdef GCC_TYPECHECKING #define ValidateHook(validatefunc, func) __builtin_types_compatible_p(__typeof__(func), __typeof__(validatefunc)) @@ -1157,7 +2085,6 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_LOCAL_PART) && !ValidateHook(hooktype_local_part, func)) || \ ((hooktype == HOOKTYPE_LOCAL_KICK) && !ValidateHook(hooktype_local_kick, func)) || \ ((hooktype == HOOKTYPE_LOCAL_CHANMODE) && !ValidateHook(hooktype_local_chanmode, func)) || \ - ((hooktype == HOOKTYPE_LOCAL_TOPIC) && !ValidateHook(hooktype_local_topic, func)) || \ ((hooktype == HOOKTYPE_LOCAL_OPER) && !ValidateHook(hooktype_local_oper, func)) || \ ((hooktype == HOOKTYPE_UNKUSER_QUIT) && !ValidateHook(hooktype_unkuser_quit, func)) || \ ((hooktype == HOOKTYPE_LOCAL_PASS) && !ValidateHook(hooktype_local_pass, func)) || \ @@ -1210,7 +2137,6 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL) && !ValidateHook(hooktype_view_topic_outside_channel, func)) || \ ((hooktype == HOOKTYPE_CHAN_PERMIT_NICK_CHANGE) && !ValidateHook(hooktype_chan_permit_nick_change, func)) || \ ((hooktype == HOOKTYPE_IS_CHANNEL_SECURE) && !ValidateHook(hooktype_is_channel_secure, func)) || \ - ((hooktype == HOOKTYPE_SEND_CHANNEL) && !ValidateHook(hooktype_can_send_to_channel_secure, func)) || \ ((hooktype == HOOKTYPE_CHANNEL_SYNCED) && !ValidateHook(hooktype_channel_synced, func)) || \ ((hooktype == HOOKTYPE_CAN_SAJOIN) && !ValidateHook(hooktype_can_sajoin, func)) || \ ((hooktype == HOOKTYPE_WHOIS) && !ValidateHook(hooktype_whois, func)) || \ diff --git a/include/setup.h.in b/include/setup.h.in index 071688f..f0f20bf 100644 --- a/include/setup.h.in +++ b/include/setup.h.in @@ -28,6 +28,9 @@ /* Define if you have the header file. */ #undef GLOBH +/* Define if ssl library has ASN1_TIME_diff */ +#undef HAS_ASN1_TIME_diff + /* Define if ssl library has SSL_CTX_set1_curves_list */ #undef HAS_SSL_CTX_SET1_CURVES_LIST @@ -37,6 +40,9 @@ /* Define if ssl library has SSL_CTX_set_security_level */ #undef HAS_SSL_CTX_SET_SECURITY_LEVEL +/* Define if ssl library has X509_get0_notAfter */ +#undef HAS_X509_get0_notAfter + /* Define if you have crypt */ #undef HAVE_CRYPT diff --git a/include/struct.h b/include/struct.h index 101acfd..ce42b5f 100644 --- a/include/struct.h +++ b/include/struct.h @@ -108,6 +108,7 @@ typedef struct ConfigItem_include ConfigItem_include; typedef struct ConfigItem_blacklist_module ConfigItem_blacklist_module; typedef struct ConfigItem_help ConfigItem_help; typedef struct ConfigItem_offchans ConfigItem_offchans; +typedef struct SecurityGroup SecurityGroup; typedef struct ListStruct ListStruct; typedef struct ListStructPrio ListStructPrio; @@ -285,7 +286,7 @@ typedef enum ClientStatus { #define SetUser(x) ((x)->status = CLIENT_STATUS_USER) #define SetLog(x) ((x)->status = CLIENT_STATUS_LOG) -/* @} */ +/** @} */ /** Used for checking certain properties of clients, such as IsSecure() and IsULine(). * @defgroup ClientFlags Client flags @@ -487,7 +488,7 @@ typedef enum ClientStatus { #define ClearULine(x) do { (x)->flags &= ~CLIENT_FLAG_ULINE; } while(0) #define ClearVirus(x) do { (x)->flags &= ~CLIENT_FLAG_VIRUS; } while(0) #define ClearIdentLookupSent(x) do { (x)->flags &= ~CLIENT_FLAG_IDENTLOOKUPSENT; } while(0) -/* @} */ +/** @} */ /* Others that access client structs: */ @@ -498,6 +499,9 @@ typedef enum ClientStatus { #define IsSynched(x) (x->serv->flags.synced) #define IsServerSent(x) (x->serv && x->serv->flags.server_sent) +/* And more that access client stuff - but actually modularized */ +#define GetReputation(client) (moddata_client_get(client, "reputation") ? atoi(moddata_client_get(client, "reputation")) : 0) /**< Get reputation value for a client */ + /* PROTOCTL (Server protocol) stuff */ #ifndef DEBUGMODE #define CHECKSERVERPROTO(x,y) (((x)->local->proto & y) == y) @@ -792,7 +796,8 @@ struct SWhois { char *setby; }; -/** The command API - used by modules and the core. +/** The command API - used by modules and the core to add commands, overrides, etc. + * See also https://www.unrealircd.org/docs/Dev:Command_API for a higher level overview and example. * @defgroup CommandAPI Command API * @{ */ @@ -829,7 +834,7 @@ struct SWhois { * E.g. parv[3] in the above example is out of bounds. */ #define CMD_FUNC(x) void (x) (Client *client, MessageTag *recv_mtags, int parc, char *parv[]) -/* @} */ +/** @} */ /** Command override function - used by all command override handlers. * This is used in the code like
CMD_OVERRIDE_FUNC(ovr_somecmd)
as a function definition. @@ -1217,7 +1222,7 @@ struct Server { } features; }; -/* @} */ +/** @} */ struct MessageTag { MessageTag *prev, *next; @@ -1379,6 +1384,7 @@ struct ConfigFlag_allow { unsigned noident :1; unsigned useip :1; unsigned tls :1; + unsigned reject_on_auth_failure :1; }; struct ConfigItem_allow { @@ -1715,6 +1721,16 @@ struct ConfigItem_offchans { char *topic; }; +#define SECURITYGROUPLEN 48 +struct SecurityGroup { + SecurityGroup *prev, *next; + int priority; + char name[SECURITYGROUPLEN+1]; + int identified; + int reputation_score; + int webirc; + int tls; +}; #define HM_HOST 1 #define HM_IPV4 2 @@ -2010,18 +2026,6 @@ extern MODVAR SSL_CTX *ctx; extern MODVAR SSL_CTX *ctx_server; extern MODVAR SSL_CTX *ctx_client; -extern SSL_METHOD *meth; -extern int early_init_ssl(); -extern int init_ssl(); -extern int ssl_handshake(Client *); /* Handshake the accpeted con.*/ -extern int ssl_client_handshake(Client *, ConfigItem_link *); /* and the initiated con.*/ -extern int ircd_SSL_accept(Client *acptr, int fd); -extern int ircd_SSL_connect(Client *acptr, int fd); -extern int SSL_smart_shutdown(SSL *ssl); -extern void ircd_SSL_client_handshake(int, int, void *); -extern void SSL_set_nonblocking(SSL *s); -extern SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server); - #define TLS_PROTOCOL_TLSV1 0x0001 #define TLS_PROTOCOL_TLSV1_1 0x0002 #define TLS_PROTOCOL_TLSV1_2 0x0004 diff --git a/include/windows/setup.h b/include/windows/setup.h index ffda2de..18d1aad 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 7 +#define UNREAL_VERSION_MINOR 8 /* 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 a655dd7..3237da9 100644 --- a/src/api-channelmode.c +++ b/src/api-channelmode.c @@ -26,7 +26,9 @@ #include "unrealircd.h" -/** This is the extended channel mode API +/** This is the extended channel mode API, + * see also https://www.unrealircd.org/docs/Dev:Channel_Mode_API + * for more information. * @defgroup ChannelModeAPI Channel mode API * @{ */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index ad7ec0a..a739fb3 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -63,7 +63,7 @@ int (*find_shun)(Client *client); int(*find_spamfilter_user)(Client *client, int flags); TKL *(*find_qline)(Client *client, char *nick, int *ishold); TKL *(*find_tkline_match_zap)(Client *client); -void (*tkl_stats)(Client *client, int type, char *para); +void (*tkl_stats)(Client *client, int type, char *para, int *cnt); void (*tkl_sync)(Client *client); void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, char *parv[]); int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); diff --git a/src/api-event.c b/src/api-event.c index 2044e5b..83cc0dd 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -240,4 +240,5 @@ void SetupEvents(void) EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0); + EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0); } diff --git a/src/api-messagetag.c b/src/api-messagetag.c index 458c5cf..293848d 100644 --- a/src/api-messagetag.c +++ b/src/api-messagetag.c @@ -25,7 +25,9 @@ */ #include "unrealircd.h" -/** This is the message tags API (message-tags) +/** This is the message tags API (message-tags). + * For an overview of message tags in general (not the API) + * see https://www.unrealircd.org/docs/Message_tags * @defgroup MessagetagAPI Message tag API * @{ */ diff --git a/src/channel.c b/src/channel.c index e849c58..a15fddf 100644 --- a/src/channel.c +++ b/src/channel.c @@ -28,7 +28,16 @@ long opermode = 0; /** Lazy way to signal an SAJOIN MODE */ long sajoinmode = 0; -/** List of all channels on the server */ +/** List of all channels on the server. + * @ingroup ListFunctions + * @section channels_example Example + * This code will list all channels on the network. + * @code + * sendnotice(client, "List of all channels:"); + * for (channel = channels; channel; channel=channel->nextch) + * sendnotice(client, "Channel %s", channel->name); + * @endcode + */ Channel *channels = NULL; /* some buffers for rebuilding channel/nick lists with comma's */ @@ -1144,7 +1153,7 @@ void set_channel_mlock(Client *client, Channel *channel, const char *newmlock, i * @in modebuf_in Buffer pointing to mode characters (eg: +snk-l) * @in parabuf_in Buffer pointing to all parameters (eg: key 123) * @retval Returns 1 if we have valid data to return, 0 if at end of mode line. - * @section ex1 Example: + * @section parse_chanmode_example Example: * @code * ParseMode pm; * int ret; diff --git a/src/conf.c b/src/conf.c index 2b7e593..ef80cb7 100644 --- a/src/conf.c +++ b/src/conf.c @@ -69,7 +69,8 @@ static int _conf_log (ConfigFile *conf, ConfigEntry *ce); static int _conf_alias (ConfigFile *conf, ConfigEntry *ce); static int _conf_help (ConfigFile *conf, ConfigEntry *ce); static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce); -static int _conf_sni (ConfigFile *conf, ConfigEntry *ce); +static int _conf_sni (ConfigFile *conf, ConfigEntry *ce); +static int _conf_security_group (ConfigFile *conf, ConfigEntry *ce); /* * Validation commands @@ -101,7 +102,8 @@ static int _test_log (ConfigFile *conf, ConfigEntry *ce); static int _test_alias (ConfigFile *conf, ConfigEntry *ce); static int _test_help (ConfigFile *conf, ConfigEntry *ce); static int _test_offchans (ConfigFile *conf, ConfigEntry *ce); -static int _test_sni (ConfigFile *conf, ConfigEntry *ce); +static int _test_sni (ConfigFile *conf, ConfigEntry *ce); +static int _test_security_group (ConfigFile *conf, ConfigEntry *ce); /* This MUST be alphabetized */ static ConfigCommand _ConfigCommands[] = { @@ -126,6 +128,7 @@ static ConfigCommand _ConfigCommands[] = { { "oper", _conf_oper, _test_oper }, { "operclass", _conf_operclass, _test_operclass }, { "require", _conf_require, _test_require }, + { "security-group", _conf_security_group, _test_security_group }, { "set", _conf_set, _test_set }, { "sni", _conf_sni, _test_sni }, { "tld", _conf_tld, _test_tld }, @@ -254,6 +257,7 @@ ConfigItem_include *conf_include = NULL; ConfigItem_blacklist_module *conf_blacklist_module = NULL; ConfigItem_help *conf_help = NULL; ConfigItem_offchans *conf_offchans = NULL; +SecurityGroup *securitygroups = NULL; MODVAR Configuration iConf; MODVAR Configuration tempiConf; @@ -1902,6 +1906,7 @@ void postconf(void) postconf_fixes(); do_weird_shun_stuff(); isupport_init(); /* for all the 005 values that changed.. */ + tls_check_expiry(NULL); } int isanyserverlinked(void) @@ -2072,6 +2077,7 @@ int init_conf(char *rootconf, int rehash) callbacks_switchover(); efunctions_switchover(); set_targmax_defaults(); + set_security_group_defaults(); if (rehash) { Hook *h; @@ -5350,6 +5356,8 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce) allow->flags.useip = 1; else if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) allow->flags.tls = 1; + else if (!strcmp(cepp->ce_varname, "reject-on-auth-failure")) + allow->flags.reject_on_auth_failure = 1; } } } @@ -5545,6 +5553,8 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce) {} else if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) {} + else if (!strcmp(cepp->ce_varname, "reject-on-auth-failure")) + {} else if (!strcmp(cepp->ce_varname, "sasl")) { config_error("%s:%d: The option allow::options::sasl no longer exists. " @@ -9317,7 +9327,7 @@ int _test_offchans(ConfigFile *conf, ConfigEntry *ce) return 1; } - config_warn("set::oficial-channels is deprecated. It often does not do what you want. " + config_warn("set::official-channels is deprecated. It often does not do what you want. " "You're better of creating a channel, setting all modes, topic, etc. to your liking " "and then making the channel permanent (MODE #channel +P). " "The channel will then be stored in a database to preserve it between restarts."); @@ -10044,6 +10054,91 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce) return errors; } +int _test_security_group(ConfigFile *conf, ConfigEntry *ce) +{ + int errors = 0; + ConfigEntry *cep; + + if (!ce->ce_vardata) + { + config_error("%s:%i: security-group block needs a name, eg: security-group web-users {", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + errors++; + } else { + if (!strcasecmp(ce->ce_vardata, "unknown-users")) + { + config_error("%s:%i: The 'unknown-users' group is a special group that is the " + "inverse of 'known-users', you cannot create or adjust it in the " + "config file, as it is created automatically by UnrealIRCd.", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + errors++; + return errors; + } + if (!security_group_valid_name(ce->ce_vardata)) + { + config_error("%s:%i: security-group block name '%s' contains invalid characters or is too long. " + "Only letters, numbers, underscore and hyphen are allowed.", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + errors++; + } + } + + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "webirc")) + { + CheckNull(cep); + } else + if (!strcmp(cep->ce_varname, "identified")) + { + CheckNull(cep); + } else + if (!strcmp(cep->ce_varname, "reputation-score")) + { + int v; + CheckNull(cep); + v = atoi(cep->ce_vardata); + if ((v < 1) || (v > 10000)) + { + config_error("%s:%i: security-group::reputation-score needs to be a value of 1-10000", + cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + errors++; + } + } else + { + config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + "security-group", cep->ce_varname); + errors++; + continue; + } + } + + return errors; +} + +int _conf_security_group(ConfigFile *conf, ConfigEntry *ce) +{ + ConfigEntry *cep; + SecurityGroup *s = add_security_group(ce->ce_vardata, 1); + + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "webirc")) + s->webirc = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->ce_varname, "identified")) + s->identified = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->ce_varname, "reputation-score")) + s->reputation_score = atoi(cep->ce_vardata); + else if (!strcmp(cep->ce_varname, "priority")) + { + s->priority = atoi(cep->ce_vardata); + DelListItem(s, securitygroups); + AddListItemPrio(s, securitygroups, s->priority); + } + } + return 1; +} + #ifdef USE_LIBCURL static void conf_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *inc_key) { @@ -10641,6 +10736,7 @@ void link_generator(void) " outgoing {\n" " hostname %s;\n" " port %d;\n" + " options { tls; autoconnect; }\n" " }\n" " password \"%s\" { spkifp; }\n" " class servers;\n" diff --git a/src/crashreport.c b/src/crashreport.c index aaf9901..29b8ad0 100644 --- a/src/crashreport.c +++ b/src/crashreport.c @@ -13,17 +13,6 @@ extern void StartUnrealAgain(void); extern char *getosname(void); - -time_t get_file_time(char *fname) -{ - struct stat st; - - if (stat(fname, &st) != 0) - return 0; - - return (time_t)st.st_ctime; -} - char *find_best_coredump(void) { static char best_fname[512]; @@ -560,17 +549,6 @@ int running_interactive(void) #define REPORT_ASK 0 #define REPORT_AUTO 1 - -int getfilesize(char *fname) -{ - struct stat st; - - if (stat(fname, &st) != 0) - return -1; - - return (int)st.st_size; -} - #define CRASH_REPORT_HOST "crash.unrealircd.org" SSL_CTX *crashreport_init_ssl(void) @@ -614,7 +592,7 @@ int crashreport_send(char *fname) int xfr = 0; char *errstr = NULL; - filesize = getfilesize(fname); + filesize = get_file_size(fname); if (filesize < 0) return 0; diff --git a/src/hash.c b/src/hash.c index 149e2cb..bbadea2 100644 --- a/src/hash.c +++ b/src/hash.c @@ -500,11 +500,21 @@ Client *hash_find_server(const char *server, Client *def) return def; } +/** Find a client, user (person), server or channel by name. + * If you are looking for "other find functions", then the alphabetical index of functions + * at 'f' is your best bet: https://www.unrealircd.org/api/5/globals_func_f.html#index_f + * @defgroup FindFunctions Find functions + * @{ + */ + /** Find a client by name. + * This searches in the list of all types of clients, user/person, servers or an unregistered clients. + * If you know what type of client to search for, then use find_server() or find_person() instead! * @param name The name to search for (eg: "nick" or "irc.example.net") * @param requester The client that is searching for this name * @note If 'requester' is a server or NULL, then we also check * the ID table, otherwise not. + * @returns If the client is found then the Client is returned, otherwise NULL. */ Client *find_client(char *name, Client *requester) { @@ -525,6 +535,7 @@ Client *find_client(char *name, Client *requester) * @param requester The client searching for the name. * @note If 'requester' is a server or NULL, then we also check * the ID table, otherwise not. + * @returns If the server is found then the Client is returned, otherwise NULL. */ Client *find_server(char *name, Client *requester) { @@ -539,13 +550,14 @@ Client *find_server(char *name, Client *requester) return NULL; } -/** Find a person. +/** Find a person (a user). * @param name The name to search for (eg: "nick" or "001ABCDEFG") * @param requester The client that is searching for this name * @note If 'requester' is a server or NULL, then we also check * the ID table, otherwise not. + * @returns If the user is found then the Client is returned, otherwise NULL. */ -Client *find_person(char *name, Client *requester) +Client *find_person(char *name, Client *requester) /* TODO: this should have been called find_user() to be consistent */ { Client *c2ptr; @@ -558,10 +570,12 @@ Client *find_person(char *name, Client *requester) } -/* - * hash_find_channel +/** Find a channel by name. + * @param name The channel name to search for + * @param default_result If the channel is not found, this value is returned. + * @returns If the channel exists then the Channel is returned, otherwise default_result is returned. */ -Channel *hash_find_channel(char *name, Channel *channel) +Channel *find_channel(char *name, Channel *default_result) { unsigned int hashv; Channel *tmp; @@ -573,9 +587,11 @@ Channel *hash_find_channel(char *name, Channel *channel) if (smycmp(name, tmp->chname) == 0) return tmp; } - return channel; + return default_result; } +/** @} */ + Channel *hash_get_chan_bucket(uint64_t hashv) { if (hashv > CHAN_HASH_TABLE_SIZE) @@ -963,18 +979,14 @@ EVENT(throttling_check_expire) char *p = serveropts + strlen(serveropts); Module *mi; t = TStime(); - if (!Hooks[17] && strchr(serveropts, 'm')) + if (!Hooks[HOOKTYPE_USERMSG] && strchr(serveropts, 'm')) { p = strchr(serveropts, 'm'); *p = '\0'; } - if (!Hooks[18] && strchr(serveropts, 'M')) + if (!Hooks[HOOKTYPE_CHANMSG] && strchr(serveropts, 'M')) { p = strchr(serveropts, 'M'); *p = '\0'; } - if (!Hooks[49] && !Hooks[51] && strchr(serveropts, 'R')) - { p = strchr(serveropts, 'R'); *p = '\0'; } - if (Hooks[17] && !strchr(serveropts, 'm')) + if (Hooks[HOOKTYPE_USERMSG] && !strchr(serveropts, 'm')) *p++ = 'm'; - if (Hooks[18] && !strchr(serveropts, 'M')) + if (Hooks[HOOKTYPE_CHANMSG] && !strchr(serveropts, 'M')) *p++ = 'M'; - if ((Hooks[49] || Hooks[51]) && !strchr(serveropts, 'R')) - *p++ = 'R'; *p = '\0'; for (mi = Modules; mi; mi = mi->next) if (!(mi->options & MOD_OPT_OFFICIAL)) diff --git a/src/ircd.c b/src/ircd.c index 6a8b972..cc6a6ea 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -96,7 +96,7 @@ void s_die() #else unload_all_modules(); unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); - exit(-1); + exit(0); #endif } @@ -269,14 +269,7 @@ EVENT(garbage_collect) loop.do_garbage_collect = 0; } -/* -** try_connections -** -** Scan through configuration and try new connections. -** Returns the calendar time when the next call to this -** function should be made latest. (No harm done if this -** is called earlier or later...) -*/ +/** Perform autoconnect to servers that are not linked yet. */ EVENT(try_connections) { ConfigItem_link *aconf; @@ -287,7 +280,7 @@ EVENT(try_connections) for (aconf = conf_link; aconf; aconf = aconf->next) { - /* We're only interested in autoconnect blocks that are valid (and ignore temporary link blocks) */ + /* We're only interested in autoconnect blocks that are valid. Also, we ignore temporary link blocks. */ if (!(aconf->outgoing.options & CONNECT_AUTO) || !aconf->outgoing.hostname || (aconf->flag.temporary == 1)) continue; @@ -296,6 +289,7 @@ EVENT(try_connections) /* Only do one connection attempt per seconds (for the same server) */ if ((aconf->hold > TStime())) continue; + confrq = class->connfreq; aconf->hold = TStime() + confrq; @@ -380,8 +374,7 @@ int match_tkls(Client *client) return 0; } -/** Time out connections that are still in handshake. - */ +/** Time out connections that are still in handshake. */ EVENT(handshake_timeout) { Client *client, *next; @@ -466,11 +459,7 @@ void check_ping(Client *client) return; } -/* - * Check registered connections for PING timeout. - * XXX: also does some other stuff still, need to sort this. --nenolod - * Perhaps it would be wise to ping servers as well mr nenolod, just an idea -- Syzop - */ +/** Check registered connections for ping timeout. Also, check for server bans. */ EVENT(check_pings) { Client *client, *next; @@ -493,6 +482,7 @@ EVENT(check_pings) /* done */ } +/** Check for clients that are pending to be terminated */ EVENT(check_deadsockets) { Client *client, *next; @@ -550,18 +540,10 @@ static int bad_command(const char *argv0) if (!argv0) argv0 = "unrealircd"; - (void)printf - ("Usage: %s [-f ] [-F]\n" - "\n" - "UnrealIRCd\n" - " -f Load configuration from instead of the default\n" - " (%s).\n" - " -F Don't fork() when starting up. Use this when running\n" - " UnrealIRCd under gdb or when playing around with settings\n" - " on a non-production setup.\n" - "\n", - argv0, CONFIGFILE); - (void)printf("Server not started\n\n"); + printf("ERROR: Incorrect command line argument encountered.\n" + "This is the unrealircd BINARY. End-users should NOT call this binary directly.\n" + "Please run the SCRIPT instead: %s/unrealircd\n", SCRIPTDIR); + printf("Server not started\n\n"); #else if (!IsService) { MessageBox(NULL, @@ -1098,7 +1080,7 @@ int InitUnrealIRCd(int argc, char *argv[]) bootopt |= BOOT_TTY; break; case 'v': - (void)printf("%s build %s\n", version, buildid); + (void)printf("%s\n", version); #else case 'v': if (!IsService) { diff --git a/src/misc.c b/src/misc.c index 7f42ffc..237352c 100644 --- a/src/misc.c +++ b/src/misc.c @@ -810,7 +810,6 @@ void exit_client(Client *client, MessageTag *recv_mtags, char *comment) exit_one_client(client, recv_mtags, comment); free_message_tags(mtags_generated); - } /** Initialize the (quite useless) IRC statistics */ @@ -851,7 +850,7 @@ void verify_opercount(Client *orig, char *tag) int valid_host(char *host) { char *p; - + if (strlen(host) > HOSTLEN) return 0; /* too long hosts are invalid too */ @@ -1032,7 +1031,7 @@ int is_autojoin_chan(char *chname) if (!strcasecmp(name, chname)) return 1; } - + if (AUTO_JOIN_CHANS) { strlcpy(buf, AUTO_JOIN_CHANS, sizeof(buf)); @@ -1069,7 +1068,7 @@ int char_to_channelflag(char c) int mixed_network(void) { Client *client; - + list_for_each_entry(client, &server_list, special_node) { if (!IsServer(client) || IsULine(client)) @@ -1083,7 +1082,7 @@ int mixed_network(void) void unreal_delete_masks(ConfigItem_mask *m) { ConfigItem_mask *m_next; - + for (; m; m = m_next) { m_next = m->next; @@ -1104,7 +1103,7 @@ static void unreal_add_mask(ConfigItem_mask **head, ConfigEntry *ce) safe_strdup(m->mask, ce->ce_vardata); else safe_strdup(m->mask, ce->ce_varname); - + add_ListItem((ListStruct *)m, (ListStruct **)head); } @@ -1137,7 +1136,7 @@ int unreal_mask_match(Client *client, ConfigItem_mask *m) return 1; } } - + return 0; } @@ -1191,7 +1190,7 @@ int swhois_add(Client *client, char *tag, int priority, char *swhois, Client *fr safe_strdup(s->setby, tag); s->priority = priority; AddListItemPrio(s, client->user->swhois, s->priority); - + sendto_server(skip, 0, PROTO_EXTSWHOIS, NULL, ":%s SWHOIS %s :%s", from->id, client->id, swhois); @@ -1215,11 +1214,11 @@ int swhois_delete(Client *client, char *tag, char *swhois, Client *from, Client { SWhois *s, *s_next; int ret = -1; /* default to 'not found' */ - + for (s = client->user->swhois; s; s = s_next) { s_next = s->next; - + /* If ( same swhois or "*" ) AND same tag */ if ( ((!strcmp(s->line, swhois) || !strcmp(swhois, "*")) && !strcmp(s->setby, tag))) @@ -1234,7 +1233,7 @@ int swhois_delete(Client *client, char *tag, char *swhois, Client *from, Client sendto_server(skip, PROTO_EXTSWHOIS, 0, NULL, ":%s SWHOIS %s - %s %d :%s", from->id, client->id, tag, 0, swhois); - + ret = 0; } } @@ -1893,6 +1892,41 @@ int filename_has_suffix(const char *fname, const char *suffix) return 0; } +/** Check if the specified file exists */ +int file_exists(char *file) +{ + FILE *fd; + + fd = fopen(file, "r"); + if (!fd) + return 0; + + fclose(fd); + return 1; +} + +/** Get the file creation time */ +time_t get_file_time(char *fname) +{ + struct stat st; + + if (stat(fname, &st) != 0) + return 0; + + return (time_t)st.st_ctime; +} + +/** Get the size of a file */ +long get_file_size(char *fname) +{ + struct stat st; + + if (stat(fname, &st) != 0) + return -1; + + return (long)st.st_size; +} + /** Add a line to a MultiLine list */ void addmultiline(MultiLine **l, char *line) { diff --git a/src/modulemanager.c b/src/modulemanager.c index 6012bda..854f2a0 100644 --- a/src/modulemanager.c +++ b/src/modulemanager.c @@ -1643,11 +1643,33 @@ void mm_parse_c_file(int argc, char *args[]) exit(0); } +void mm_self_test(void) +{ + char name[512]; + snprintf(name, sizeof(name), "%s/src/modules/third", BUILDDIR); + if (file_exists(name)) + return; + if (!file_exists(BUILDDIR)) + { + fprintf(stderr, "ERROR: Directory %s does not exist.\n" + "The UnrealIRCd source is required for the module manager to work!\n", + BUILDDIR); + } else { + fprintf(stderr, "ERROR: Directory %s exists, but %s does not.\n" + "The UnrealIRCd source is required for the module manager to work.\n" + "It seems you only have a partial build directory??\n", + BUILDDIR, name); + } + exit(-1); +} + void modulemanager(int argc, char *args[]) { if (!args[0]) mm_usage(); + mm_self_test(); + /* The following operations do not require reading * of the repository list and are always available: */ diff --git a/src/modules/chanmodes/secureonly.c b/src/modules/chanmodes/secureonly.c index 9d3bd68..4bd3dcb 100644 --- a/src/modules/chanmodes/secureonly.c +++ b/src/modules/chanmodes/secureonly.c @@ -34,7 +34,6 @@ Cmode_t EXTCMODE_SECUREONLY; int secureonly_check_join(Client *client, Channel *channel, char *key, char *parv[]); int secureonly_channel_sync (Channel *channel, int merge, int removetheirs, int nomode); -int secureonly_send_channel(Client *client, Channel *channel); int secureonly_check_secure(Channel *channel); int secureonly_check_sajoin(Client *target, Channel *channel, Client *requester); int secureonly_specialcheck(Client *client, Channel *channel, char *parv[]); @@ -58,7 +57,6 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, secureonly_check_join); HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_SYNCED, 0, secureonly_channel_sync); HookAdd(modinfo->handle, HOOKTYPE_IS_CHANNEL_SECURE, 0, secureonly_check_secure); - HookAdd(modinfo->handle, HOOKTYPE_SEND_CHANNEL, 0, secureonly_send_channel); HookAdd(modinfo->handle, HOOKTYPE_CAN_SAJOIN, 0, secureonly_check_sajoin); @@ -165,15 +163,6 @@ int secureonly_channel_sync(Channel *channel, int merge, int removetheirs, int n return 0; } -int secureonly_send_channel(Client *client, Channel *channel) -{ - if (IsSecureOnly(channel)) - if (!IsSecure(client)) - return HOOK_DENY; - - return HOOK_CONTINUE; -} - int secureonly_check_sajoin(Client *target, Channel *channel, Client *requester) { if (IsSecureOnly(channel) && !IsSecure(target)) diff --git a/src/modules/connthrottle.c b/src/modules/connthrottle.c index b86feff..608e07c 100644 --- a/src/modules/connthrottle.c +++ b/src/modules/connthrottle.c @@ -67,8 +67,6 @@ UCounter *ucounter = NULL; #define MSG_THROTTLE "THROTTLE" -#define GetReputation(client) (moddata_client_get(client, "reputation") ? atoi(moddata_client_get(client, "reputation")) : 0) - /* Forward declarations */ int ct_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int ct_config_posttest(int *errs); diff --git a/src/modules/extbans/Makefile.in b/src/modules/extbans/Makefile.in index fa85302..b286cc3 100644 --- a/src/modules/extbans/Makefile.in +++ b/src/modules/extbans/Makefile.in @@ -34,7 +34,7 @@ INCLUDES = ../../include/channel.h \ R_MODULES= \ join.so quiet.so nickchange.so inchannel.so realname.so \ account.so operclass.so certfp.so textban.so msgbypass.so \ - timedban.so partmsg.so + timedban.so partmsg.so securitygroup.so MODULES=$(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -102,3 +102,7 @@ timedban.so: timedban.c $(INCLUDES) partmsg.so: partmsg.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o partmsg.so partmsg.c + +securitygroup.so: securitygroup.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o securitygroup.so securitygroup.c diff --git a/src/modules/extbans/securitygroup.c b/src/modules/extbans/securitygroup.c new file mode 100644 index 0000000..e45782e --- /dev/null +++ b/src/modules/extbans/securitygroup.c @@ -0,0 +1,141 @@ +/* + * Extended ban to ban based on security groups such as "unknown-users" + * (C) Copyright 2020 Bram Matthys (Syzop) and the UnrealIRCd team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "unrealircd.h" + +ModuleHeader MOD_HEADER += { + "extbans/securitygroup", + "4.2", + "ExtBan ~G - Ban based on security-group", + "UnrealIRCd Team", + "unrealircd-5", +}; + +/* Forward declarations */ +char *extban_securitygroup_conv_param(char *para); +int extban_securitygroup_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2); +int extban_securitygroup_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); + +/** Called upon module init */ +MOD_INIT() +{ + ExtbanInfo req; + + req.flag = 'G'; + req.conv_param = extban_securitygroup_conv_param; + req.is_ok = extban_securitygroup_is_ok; + req.is_banned = extban_securitygroup_is_banned; + req.options = EXTBOPT_INVEX|EXTBOPT_TKL; + if (!ExtbanAdd(modinfo->handle, req)) + { + config_error("could not register extended ban type ~G"); + return MOD_FAILED; + } + + MARK_AS_OFFICIAL_MODULE(modinfo); + + return MOD_SUCCESS; +} + +/** Called upon module load */ +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +/** Called upon unload */ +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/* Helper function for extban_securitygroup_is_ok() and extban_securitygroup_conv_param() + * to do ban validation. + */ +int extban_securitygroup_generic(char *para, int strict) +{ + char *mask; + + mask = para+3; + + /* ! at the start means negative match */ + if (*mask == '!') + mask++; + + /* Check if the rest of the security group name is valid */ + if (strict) + { + if (!security_group_exists(mask)) + return 0; /* security group does not exist */ + } else { + if (!security_group_valid_name(mask)) + return 0; /* invalid characters or too long */ + } + + if (!*mask) + return 0; /* don't allow "~G:" nor "~G:!" */ + + if (strlen(mask) > SECURITYGROUPLEN + 3) + mask[SECURITYGROUPLEN + 3] = '\0'; + + return 1; +} + +int extban_securitygroup_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2) +{ + if (MyUser(client) && (what == MODE_ADD) && (checkt == EXBCHK_PARAM)) + { + char banbuf[SECURITYGROUPLEN+8]; + strlcpy(banbuf, para, sizeof(banbuf)); + if (!extban_securitygroup_generic(banbuf, 1)) + { + SecurityGroup *s; + sendnotice(client, "ERROR: Unknown security-group '%s'. Syntax: +b ~G:securitygroup or +b ~G:!securitygroup", para+3); + sendnotice(client, "Available security groups:"); + for (s = securitygroups; s; s = s->next) + sendnotice(client, "%s", s->name); + sendnotice(client, "unknown-users"); + sendnotice(client, "End of security group list."); + return 0; + } + } + return 1; +} + +/** Security group extban - conv_param */ +char *extban_securitygroup_conv_param(char *para) +{ + static char retbuf[SECURITYGROUPLEN + 8]; + + strlcpy(retbuf, para, sizeof(retbuf)); + if (!extban_securitygroup_generic(retbuf, 0)) + return NULL; + + return retbuf; +} + +/** Is the user banned by ~G:something ? */ +int extban_securitygroup_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +{ + char *ban = banin+3; + + if (*ban == '!') + return !user_allowed_by_security_group_name(client, ban+1); + return user_allowed_by_security_group_name(client, ban); +} diff --git a/src/modules/mode.c b/src/modules/mode.c index 8f11c22..3cb9c84 100644 --- a/src/modules/mode.c +++ b/src/modules/mode.c @@ -1367,6 +1367,9 @@ int paracount_for_chanmode_from_server(Client *client, u_int what, char mode) if (mode == '&') return 0; /* & indicates bounce, it is not an actual mode character */ + if (mode == 'F') + return (what == MODE_ADD) ? 1 : 0; /* Future compatibility */ + /* If we end up here it means we have no idea if it is a parameter-eating or paramless * channel mode. That's actually pretty bad. This shouldn't happen since CHANMODES= * is sent since 2003 and the (often also required) EAUTH PROTOCTL is in there since 2010. diff --git a/src/modules/nick.c b/src/modules/nick.c index a39c49c..dbcfb74 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -315,8 +315,8 @@ CMD_FUNC(cmd_nick_local) { client->local->since += 4; /* lag them up */ sendnumeric(client, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason); - sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s.", - nick, get_client_name(cptr, FALSE)); + sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s (%s)", + nick, get_client_name(cptr, FALSE), tklban->ptr.nameban->reason); return; /* NICK message ignored */ } /* fallthrough for ircops that have sufficient privileges */ @@ -866,6 +866,12 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char /* Check G/Z lines before shuns -- kill before quite -- codemastr */ if (find_tkline_match(client, 0)) { + if (!IsDead(client) && client->local->class) + { + /* Fix client count bug, in case that it was a hold such as via authprompt */ + client->local->class->clients--; + client->local->class = NULL; + } ircstats.is_ref++; return 0; } @@ -892,7 +898,17 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char { i = (*(h->func.intfunc))(client); if (i == HOOK_DENY) + { + if (!IsDead(client) && client->local->class) + { + /* Fix client count bug, in case that + * the HOOK_DENY was only meant temporarily. + */ + client->local->class->clients--; + client->local->class = NULL; + } return 0; + } if (i == HOOK_ALLOW) break; } @@ -1316,12 +1332,9 @@ int AllowClient(Client *client, char *username) for (aconf = conf_allow; aconf; aconf = aconf->next) { - if (!aconf->hostname || !aconf->ip) - goto attach; - if (aconf->auth && !client->local->passwd && !moddata_client_get(client, "certfp")) - continue; if (aconf->flags.tls && !IsSecure(client)) continue; + if (hp && hp->h_name) { hname = hp->h_name; @@ -1376,8 +1389,21 @@ int AllowClient(Client *client, char *username) goto attach; } - continue; + continue; /* No match */ attach: + /* Check authentication */ + if (aconf->auth && !Auth_Check(client, aconf->auth, client->local->passwd)) + { + /* Incorrect password/authentication - but was is it required? */ + if (aconf->flags.reject_on_auth_failure) + { + exit_client(client, NULL, iConf.reject_message_unauthorized); + return 0; + } else { + continue; /* Continue (this is the default behavior) */ + } + } + if (!aconf->flags.noident) SetUseIdent(client); if (!aconf->flags.useip && hp) @@ -1393,12 +1419,6 @@ int AllowClient(Client *client, char *username) return 0; } - if (aconf->auth && !Auth_Check(client, aconf->auth, client->local->passwd)) - { - /* Always continue if password was wrong. */ - continue; - } - if (!((aconf->class->clients + 1) > aconf->class->maxclients)) { client->local->class = aconf->class; diff --git a/src/modules/protoctl.c b/src/modules/protoctl.c index 0b765b5..c1b3c4e 100644 --- a/src/modules/protoctl.c +++ b/src/modules/protoctl.c @@ -343,9 +343,9 @@ CMD_FUNC(cmd_protoctl) (long long)(TStime() - t)); snprintf(msg, sizeof(msg), "Rejecting link %s: our clock is %lld seconds ahead. " - "Correct time is very important in IRC. Please " - "verify the clock on both %s (them) and %s (us), " - "fix it and then try linking again", + "Please verify the clock on both %s (them) and %s (us). " + "Correct time is very important for IRC servers, " + "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", get_client_name(client, TRUE), (long long)(TStime() - t), client->name, me.name); @@ -359,9 +359,9 @@ CMD_FUNC(cmd_protoctl) (long long)(t - TStime())); snprintf(msg, sizeof(msg), "Rejecting link %s: our clock is %lld seconds behind. " - "Correct time is very important in IRC. Please " - "verify the clock on both %s (them) and %s (us), " - "fix it and then try linking again", + "Please verify the clock on both %s (them) and %s (us). " + "Correct time is very important for IRC servers, " + "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", get_client_name(client, TRUE), (long long)(t - TStime()), client->name, me.name); diff --git a/src/modules/reputation.c b/src/modules/reputation.c index 5f12d1f..90f457e 100644 --- a/src/modules/reputation.c +++ b/src/modules/reputation.c @@ -682,6 +682,107 @@ int count_reputation_records(void) return total; } +void reputation_channel_query(Client *client, Channel *channel) +{ + Member *m; + char buf[512]; + char tbuf[256]; + char **nicks; + int *scores; + int cnt = 0, i, j; + ReputationEntry *e; + + sendtxtnumeric(client, "Users and reputation scores for %s:", channel->chname); + + /* Step 1: build a list of nicks and their reputation */ + nicks = safe_alloc((channel->users+1) * sizeof(char *)); + scores = safe_alloc((channel->users+1) * sizeof(int)); + for (m = channel->members; m; m = m->next) + { + nicks[cnt] = m->client->name; + if (m->client->ip) + { + e = find_reputation_entry(m->client->ip); + if (e) + scores[cnt] = e->score; + } + if (++cnt > channel->users) + { + sendto_ops("[BUG] reputation_channel_query() expected %d users but %d (or more) were present in %s", + channel->users, cnt, channel->chname); +#ifdef DEBUGMODE + abort(); +#endif + break; /* safety net */ + } + } + + /* Step 2: lazy selection sort */ + for (i = 0; i < cnt && nicks[i]; i++) + { + for (j = i+1; j < cnt && nicks[j]; j++) + { + if (scores[i] < scores[j]) + { + char *nick_tmp; + int score_tmp; + nick_tmp = nicks[i]; + score_tmp = scores[i]; + nicks[i] = nicks[j]; + scores[i] = scores[j]; + nicks[j] = nick_tmp; + scores[j] = score_tmp; + } + } + } + + /* Step 3: send the (ordered) list to the user */ + *buf = '\0'; + for (i = 0; i < cnt && nicks[i]; i++) + { + snprintf(tbuf, sizeof(tbuf), "%s\00314(%d)\003 ", nicks[i], scores[i]); + if ((strlen(tbuf)+strlen(buf) > 400) || !nicks[i+1]) + { + sendtxtnumeric(client, "%s%s", buf, tbuf); + *buf = '\0'; + } else { + strlcat(buf, tbuf, sizeof(buf)); + } + } + sendtxtnumeric(client, "End of list."); + safe_free(nicks); + safe_free(scores); +} + +void reputation_list_query(Client *client, int maxscore) +{ + Client *target; + ReputationEntry *e; + + sendtxtnumeric(client, "Users and reputation scores <%d:", maxscore); + + list_for_each_entry(target, &client_list, client_node) + { + int score = 0; + + if (!IsUser(target) || IsULine(target) || !target->ip) + continue; + + e = find_reputation_entry(target->ip); + if (e) + score = e->score; + if (score >= maxscore) + continue; + sendtxtnumeric(client, "%s!%s@%s [%s] \017(score: %d)", + target->name, + target->user->username, + target->user->realhost, + target->ip, + score); + } + sendtxtnumeric(client, "End of list."); +} + CMD_FUNC(reputation_user_cmd) { ReputationEntry *e; @@ -709,13 +810,45 @@ CMD_FUNC(reputation_user_cmd) } sendnotice(client, "Current number of records (IP's): %d", count_reputation_records()); sendnotice(client, "-"); - sendnotice(client, "For more specific information, use: /REPUTATION [nick|IP-address]"); + sendnotice(client, "Available commands:"); + sendnotice(client, "/REPUTATION [nick] Show reputation info about nick name"); + sendnotice(client, "/REPUTATION [ip] Show reputation info about IP address"); + sendnotice(client, "/REPUTATION [channel] List users in channel along with their reputation score"); + sendnotice(client, "/REPUTATION chname); + return; + } + reputation_channel_query(client, channel); + return; + } else + if (parv[1][0] == '<') + { + int max = atoi(parv[1] + 1); + if (max < 1) + { + sendnotice(client, "REPUTATION: Invalid search value specified. Use for example '/REPUTATION <5' to search on less-than-five"); + return; + } + reputation_list_query(client, max); + return; } else { Client *target = find_person(parv[1], NULL); if (!target) diff --git a/src/modules/restrict-commands.c b/src/modules/restrict-commands.c index ce71f44..ffa1a04 100644 --- a/src/modules/restrict-commands.c +++ b/src/modules/restrict-commands.c @@ -27,8 +27,6 @@ ModuleHeader MOD_HEADER = { "unrealircd-5", }; -#define GetReputation(client) (moddata_client_get(client, "reputation") ? atoi(moddata_client_get(client, "reputation")) : 0) - typedef struct RestrictedCommand RestrictedCommand; struct RestrictedCommand { RestrictedCommand *prev, *next; diff --git a/src/modules/stats.c b/src/modules/stats.c index 9985a59..72bc364 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -462,22 +462,25 @@ int stats_denylinkall(Client *client, char *para) int stats_gline(Client *client, char *para) { - tkl_stats(client, TKL_GLOBAL|TKL_KILL, para); - tkl_stats(client, TKL_GLOBAL|TKL_ZAP, para); + int cnt = 0; + tkl_stats(client, TKL_GLOBAL|TKL_KILL, para, &cnt); + tkl_stats(client, TKL_GLOBAL|TKL_ZAP, para, &cnt); return 0; } int stats_spamfilter(Client *client, char *para) { - tkl_stats(client, TKL_SPAMF, para); - tkl_stats(client, TKL_GLOBAL|TKL_SPAMF, para); + int cnt = 0; + tkl_stats(client, TKL_SPAMF, para, &cnt); + tkl_stats(client, TKL_GLOBAL|TKL_SPAMF, para, &cnt); return 0; } int stats_except(Client *client, char *para) { - tkl_stats(client, TKL_EXCEPTION, para); - tkl_stats(client, TKL_EXCEPTION|TKL_GLOBAL, para); + int cnt = 0; + tkl_stats(client, TKL_EXCEPTION, para, &cnt); + tkl_stats(client, TKL_EXCEPTION|TKL_GLOBAL, para, &cnt); return 0; } @@ -564,8 +567,9 @@ int stats_port(Client *client, char *para) int stats_bannick(Client *client, char *para) { - tkl_stats(client, TKL_NAME, para); - tkl_stats(client, TKL_GLOBAL|TKL_NAME, para); + int cnt = 0; + tkl_stats(client, TKL_NAME, para, &cnt); + tkl_stats(client, TKL_GLOBAL|TKL_NAME, para, &cnt); return 0; } @@ -699,8 +703,9 @@ int stats_denylinkauto(Client *client, char *para) int stats_kline(Client *client, char *para) { - tkl_stats(client, TKL_KILL, NULL); - tkl_stats(client, TKL_ZAP, NULL); + int cnt = 0; + tkl_stats(client, TKL_KILL, NULL, &cnt); + tkl_stats(client, TKL_ZAP, NULL, &cnt); return 0; } @@ -720,7 +725,8 @@ int stats_banrealname(Client *client, char *para) int stats_sqline(Client *client, char *para) { - tkl_stats(client, TKL_NAME|TKL_GLOBAL, para); + int cnt = 0; + tkl_stats(client, TKL_NAME|TKL_GLOBAL, para, &cnt); return 0; } @@ -741,7 +747,8 @@ int stats_chanrestrict(Client *client, char *para) int stats_shun(Client *client, char *para) { - tkl_stats(client, TKL_GLOBAL|TKL_SHUN, para); + int cnt = 0; + tkl_stats(client, TKL_GLOBAL|TKL_SHUN, para, &cnt); return 0; } diff --git a/src/modules/targetfloodprot.c b/src/modules/targetfloodprot.c index 3094013..be1e4d5 100644 --- a/src/modules/targetfloodprot.c +++ b/src/modules/targetfloodprot.c @@ -234,8 +234,8 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member if (!MyUser(client)) return HOOK_CONTINUE; - /* Really, only IRCOps override */ - if (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL)) + /* IRCOps and U-Lines override */ + if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL))) return HOOK_CONTINUE; what = sendtypetowhat(sendtype); @@ -280,8 +280,8 @@ int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text if (!MyUser(target)) return HOOK_CONTINUE; - /* Really, only IRCOps override */ - if (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL)) + /* IRCOps and U-Lines override */ + if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL))) return HOOK_CONTINUE; what = sendtypetowhat(sendtype); diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 75aade9..fcfdc85 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -39,6 +39,8 @@ int tkl_config_test_ban(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_ban(ConfigFile *, ConfigEntry *, int); int tkl_config_test_except(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_except(ConfigFile *, ConfigEntry *, int); +int tkl_config_test_set(ConfigFile *, ConfigEntry *, int, int *); +int tkl_config_run_set(ConfigFile *, ConfigEntry *, int); CMD_FUNC(cmd_gline); CMD_FUNC(cmd_shun); CMD_FUNC(cmd_tempshun); @@ -76,7 +78,7 @@ int _find_shun(Client *client); int _find_spamfilter_user(Client *client, int flags); TKL *_find_qline(Client *client, char *nick, int *ishold); TKL *_find_tkline_match_zap(Client *client); -void _tkl_stats(Client *client, int type, char *para); +void _tkl_stats(Client *client, int type, char *para, int *cnt); void _tkl_sync(Client *client); CMD_FUNC(_cmd_tkl); int _place_host_ban(Client *client, BanAction action, char *reason, long duration); @@ -142,12 +144,15 @@ TKLTypeTable tkl_types[] = { }; #define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, unknown-data-flood, antirandom, antimixedutf8, ban-version" +int max_stats_matches = 1000; + MOD_TEST() { MARK_AS_OFFICIAL_MODULE(modinfo); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_spamfilter); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_ban); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_except); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_set); EfunctionAdd(modinfo->handle, EFUNC_TKL_HASH, _tkl_hash); EfunctionAdd(modinfo->handle, EFUNC_TKL_TYPETOCHAR, TO_INTFUNC(_tkl_typetochar)); EfunctionAdd(modinfo->handle, EFUNC_TKL_CHARTOTYPE, TO_INTFUNC(_tkl_chartotype)); @@ -190,6 +195,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_match_spamfilter); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_ban); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_except); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_set); CommandAdd(modinfo->handle, "GLINE", cmd_gline, 3, CMD_OPER); CommandAdd(modinfo->handle, "SHUN", cmd_shun, 3, CMD_OPER); CommandAdd(modinfo->handle, "TEMPSHUN", cmd_tempshun, 2, CMD_OPER); @@ -902,6 +908,44 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) return 1; } +int tkl_config_test_set(ConfigFile *cf, ConfigEntry *ce, int configtype, int *errs) +{ + int errors = 0; + + /* We are only interested in set { } blocks */ + if (configtype != CONFIG_SET) + return 0; + + if (!strcmp(ce->ce_varname, "max-stats-matches")) + { + if (!ce->ce_vardata) + { + config_error("%s:%i: set::max-stats-matches: no value specified", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + errors++; + } + // allow any other value, including 0 and negative. + *errs = errors; + return errors ? -1 : 1; + } + return 0; +} + +int tkl_config_run_set(ConfigFile *cf, ConfigEntry *ce, int configtype) +{ + /* We are only interested in set { } blocks */ + if (configtype != CONFIG_SET) + return 0; + + if (!strcmp(ce->ce_varname, "max-stats-matches")) + { + max_stats_matches = atoi(ce->ce_vardata); + return 1; + } + + return 0; +} + /** Return unique spamfilter id for TKL */ char *spamfilter_id(TKL *tk) { @@ -1269,7 +1313,7 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) mask++; } - if (strchr(mask, '!')) + if ((*mask != '~') && strchr(mask, '!')) { sendnotice(client, "[error] Cannot have '!' in masks."); return; @@ -1590,7 +1634,7 @@ CMD_FUNC(cmd_eline) reason = parv[4]; } - if (strchr(mask, '!')) + if ((*mask != '~') && strchr(mask, '!')) { sendnotice(client, "[error] Cannot have '!' in masks."); return; @@ -3298,7 +3342,7 @@ static void parse_stats_params(char *para, TKLFlag *flag) /** Does this TKL entry match the search terms? * This is a helper function for tkl_stats(). */ -void tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, TKL *tkl) +int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, TKL *tkl) { /***** First, handle the selection ******/ @@ -3306,66 +3350,66 @@ void tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, { if (tklflags->flags & BY_SETBY) if (!match_simple(tklflags->set_by, tkl->set_by)) - return; + return 0; if (tklflags->flags & NOT_BY_SETBY) if (match_simple(tklflags->set_by, tkl->set_by)) - return; + return 0; if (TKLIsServerBan(tkl)) { if (tklflags->flags & BY_MASK) { if (!match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) - return; + return 0; } if (tklflags->flags & NOT_BY_MASK) { if (match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) - return; + return 0; } if (tklflags->flags & BY_REASON) if (!match_simple(tklflags->reason, tkl->ptr.serverban->reason)) - return; + return 0; if (tklflags->flags & NOT_BY_REASON) if (match_simple(tklflags->reason, tkl->ptr.serverban->reason)) - return; + return 0; } else if (TKLIsNameBan(tkl)) { if (tklflags->flags & BY_MASK) { if (!match_simple(tklflags->mask, tkl->ptr.nameban->name)) - return; + return 0; } if (tklflags->flags & NOT_BY_MASK) { if (match_simple(tklflags->mask, tkl->ptr.nameban->name)) - return; + return 0; } if (tklflags->flags & BY_REASON) if (!match_simple(tklflags->reason, tkl->ptr.nameban->reason)) - return; + return 0; if (tklflags->flags & NOT_BY_REASON) if (match_simple(tklflags->reason, tkl->ptr.nameban->reason)) - return; + return 0; } else if (TKLIsBanException(tkl)) { if (tklflags->flags & BY_MASK) { if (!match_simple(tklflags->mask, make_user_host(tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask))) - return; + return 0; } if (tklflags->flags & NOT_BY_MASK) { if (match_simple(tklflags->mask, make_user_host(tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask))) - return; + return 0; } if (tklflags->flags & BY_REASON) if (!match_simple(tklflags->reason, tkl->ptr.banexception->reason)) - return; + return 0; if (tklflags->flags & NOT_BY_REASON) if (match_simple(tklflags->reason, tkl->ptr.banexception->reason)) - return; + return 0; } } @@ -3444,16 +3488,24 @@ void tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, tkl->ptr.banexception->bantypes, (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason); + } else + { + /* That's weird, unknown TKL type */ + return 0; } + return 1; } /* TKL Stats. This is used by /STATS gline and all the others */ -void _tkl_stats(Client *client, int type, char *para) +void _tkl_stats(Client *client, int type, char *para, int *cnt) { TKL *tk; TKLFlag tklflags; int index, index2; + if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) + return; + if (!BadPtr(para)) parse_stats_params(para, &tklflags); @@ -3467,7 +3519,16 @@ void _tkl_stats(Client *client, int type, char *para) { if (type && tk->type != type) continue; - tkl_stats_matcher(client, type, para, &tklflags, tk); + if (tkl_stats_matcher(client, type, para, &tklflags, tk)) + { + *cnt += 1; + if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) + { + sendnumeric(client, ERR_TOOMANYMATCHES, "STATS", "too many matches (set::max-stats-matches)"); + sendnotice(client, "Consider searching on something more specific, eg '/STATS gline +m *.nl'. See '/STATS' (without parameters) for help."); + return; + } + } } } } @@ -3479,7 +3540,16 @@ void _tkl_stats(Client *client, int type, char *para) { if (type && tk->type != type) continue; - tkl_stats_matcher(client, type, para, &tklflags, tk); + if (tkl_stats_matcher(client, type, para, &tklflags, tk)) + { + *cnt += 1; + if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) + { + sendnumeric(client, ERR_TOOMANYMATCHES, "STATS", "too many matches (set::max-stats-matches)"); + sendnotice(client, "Consider searching on something more specific, eg '/STATS gline +m *.nl'. See '/STATS' (without parameters) for help."); + return; + } + } } } diff --git a/src/modules/whox.c b/src/modules/whox.c index 38de40c..e9d4338 100644 --- a/src/modules/whox.c +++ b/src/modules/whox.c @@ -351,45 +351,6 @@ CMD_FUNC(cmd_whox) return; } - /* '/who nick' */ - if (((acptr = find_person(mask, NULL)) != NULL) && - (!(fmt.matchsel & WMATCH_MODES)) && - (!(fmt.matchsel & WMATCH_OPER) || IsOper(acptr))) - { - int isinvis = 0; - int i = 0; - Hook *h; - - isinvis = IsInvisible(acptr); - for (lp = acptr->user->channel; lp; lp = lp->next) - { - member = IsMember(client, lp->channel); - - if (isinvis && !member) - continue; - - for (h = Hooks[HOOKTYPE_VISIBLE_IN_CHANNEL]; h; h = h->next) - { - i = (*(h->func.intfunc))(acptr,lp->channel); - if (i != 0) - break; - } - - if (i != 0 && !(is_skochanop(client, lp->channel)) && !(is_skochanop(acptr, lp->channel) || has_voice(acptr,lp->channel))) - continue; - - if (member || (!isinvis && PubChannel(lp->channel))) - break; - } - if (lp != NULL) - do_who(client, acptr, lp->channel, &fmt); - else - do_who(client, acptr, NULL, &fmt); - - sendnumeric(client, RPL_ENDOFWHO, orig_mask); - return; - } - if (ValidatePermissionsForPath("channel:see:who:secret",client,NULL,NULL,NULL) || ValidatePermissionsForPath("channel:see:whois",client,NULL,NULL,NULL)) { @@ -561,9 +522,14 @@ static void who_common_channel(Client *client, Channel *channel, static void who_global(Client *client, char *mask, int operspy, struct who_format *fmt) { + Client *hunted = NULL; Client *acptr; int maxmatches = IsOper(client) ? INT_MAX : WHOLIMIT; + /* If searching for a nick explicitly, then include it later on in the result: */ + if (mask && ((fmt->matchsel & WMATCH_NICK) || (fmt->matchsel == 0))) + hunted = find_person(mask, NULL); + /* Initialize the markers to zero */ list_for_each_entry(acptr, &client_list, client_node) ClearMark(acptr); @@ -583,7 +549,7 @@ static void who_global(Client *client, char *mask, int operspy, struct who_forma if (!IsUser(acptr)) continue; - if (IsInvisible(acptr) && !operspy && (client != acptr)) + if (IsInvisible(acptr) && !operspy && (client != acptr) && (acptr != hunted)) continue; if (IsMarked(acptr)) diff --git a/src/parse.c b/src/parse.c index 80ab224..8e4c356 100644 --- a/src/parse.c +++ b/src/parse.c @@ -417,7 +417,10 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) /* If you're a user, and this command does not permit users or opers, deny */ if ((flags & CMD_USER) && !(cmptr->flags & CMD_USER) && !(cmptr->flags & CMD_OPER)) { - sendnumeric(cptr, ERR_NOTFORUSERS, cmptr->cmd); + if (cmptr->flags & CMD_UNREGISTERED) + sendnumeric(cptr, ERR_ALREADYREGISTRED); /* only for unregistered phase */ + else + sendnumeric(cptr, ERR_NOTFORUSERS, cmptr->cmd); /* really never for users */ return; } diff --git a/src/send.c b/src/send.c index eaf3e0b..79a5f30 100644 --- a/src/send.c +++ b/src/send.c @@ -164,11 +164,29 @@ void mark_data_to_send(Client *to) } } +/** Send data to clients, servers, channels, IRCOps, etc. + * There are a lot of send functions. The most commonly functions + * are: sendto_one() to send to an individual user, + * sendnumeric() to send a numeric to an individual user + * and sendto_channel() to send a message to a channel. + * @defgroup SendFunctions Send functions + * @{ + */ + /** Send a message to a single client. + * This function is used quite a lot, after sendnumeric() it is the most-used send function. * @param to The client to send to * @param mtags Any message tags associated with this message (can be NULL) * @param pattern The format string / pattern to use. * @param ... Format string parameters. + * @section sendto_one_examples Examples + * @subsection sendto_one_mode_r Send "MODE -r" + * This will send the `:serv.er.name MODE yournick -r` message. + * Note that it will send only this message to illustrate the sendto_one() function. + * It does *not* set anyone actually -r. + * @code + * sendto_one(client, NULL, ":%s MODE %s :-r", me.name, client->name); + * @endcode */ void sendto_one(Client *to, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) { @@ -180,8 +198,8 @@ void sendto_one(Client *to, MessageTag *mtags, FORMAT_STRING(const char *pattern /** Send a message to a single client - va_list variant. * This function is similar to sendto_one() except that it - * doesn't use varargs but a va_list instead. - * Generally this is NOT used outside send.c, so not by modules. + * doesn't use varargs but uses a va_list instead. + * Generally this function is NOT used outside send.c, so not by modules. * @param to The client to send to * @param mtags Any message tags associated with this message (can be NULL) * @param pattern The format string / pattern to use. @@ -363,9 +381,9 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) } /** A single function to send data to a channel. - * Previously there were 6, now there is 1. This means there - * are likely some parameters that you will pass as NULL or 0 - * but at least we can all use one single function. + * Previously there were 6 different functions to send channel data, + * now there is 1 single function. This also means that you most + * likely will pass NULL or 0 as some parameters. * @param channel The channel to send to * @param from The source of the message * @param skip The client to skip (can be NULL). @@ -380,6 +398,36 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * @param mtags The message tags to attach to this message * @param pattern The pattern (eg: ":%s PRIVMSG %s :%s") * @param ... The parameters for the pattern. + * @note For all channel messages, it is important to attach the correct + * message tags (mtags) via a new_message() call, as can be seen + * in the example. + * @section sendto_channel_examples Examples + * @subsection sendto_channel_privmsg Send a PRIVMSG to a channel + * This command will send the message "Hello everyone!!!" to the channel when executed. + * @code + * CMD_FUNC(cmd_sayhello) + * { + * MessageTag *mtags = NULL; + * Channel *channel = NULL; + * if ((parc < 2) || BadPtr(parv[1])) + * { + * sendnumeric(client, ERR_NEEDMOREPARAMS, "SAYHELLO"); + * return; + * } + * channel = find_channel(parv[1], NULL); + * if (!channel) + * { + * sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); + * return; + * } + * new_message(client, recv_mtags, &mtags); + * sendto_channel(channel, client, client->direction, 0, 0, + * SEND_LOCAL|SEND_REMOTE, mtags, + * ":%s PRIVMSG %s :Hello everyone!!!", + * client->name, channel->name); + * free_message_tags(mtags); + * } + * @endcode */ void sendto_channel(Channel *channel, Client *from, Client *skip, int prefix, long clicap, int sendflags, @@ -593,11 +641,15 @@ static int match_it(Client *one, char *mask, int what) } } -/* - * sendto_match_butone - * - * Send to all clients which match the mask in a way defined on 'what'; - * either by user hostname or user servername. +/** Send to all clients which match the mask. + * This function is rarely used. + * @param one The client to skip + * @param from The sender + * @param mask The mask + * @param what One of MATCH_HOST or MATCH_SERVER + * @param mtags Message tags associated with the message + * @param pattern Format string + * @param ... Parameters to the format string */ void sendto_match_butone(Client *one, Client *from, char *mask, int what, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) @@ -641,10 +693,9 @@ void sendto_match_butone(Client *one, Client *from, char *mask, int what, } } -/* - * sendto_ops - * - * Send to *local* ops only. +/** Send a message to all locally connected IRCOps + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. */ void sendto_ops(FORMAT_STRING(const char *pattern), ...) { @@ -664,10 +715,98 @@ void sendto_ops(FORMAT_STRING(const char *pattern), ...) } } -/* - * sendto_umode - * - * Send to specified umode +/* Hmm.. so local sending is called sendto_ops() and local+remote is sendto_ops_butone(), + * that is weird naming... (TODO fix some day in a new major series) + */ + +/** Send a message to all IRCOps (local and remote), except one. + * @param one Skip sending the message to this client/direction + * @param from The sender (can not be NULL) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ +void sendto_ops_butone(Client *one, Client *from, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + Client *acptr; + + ++current_serial; + list_for_each_entry(acptr, &client_list, client_node) + { + if (!SendWallops(acptr)) + continue; + if (acptr->direction->local->serial == current_serial) /* sent message along it already ? */ + continue; + if (acptr->direction == one) + continue; /* ...was the one I should skip */ + acptr->direction->local->serial = current_serial; + + va_start(vl, pattern); + vsendto_prefix_one(acptr->direction, from, NULL, pattern, vl); + va_end(vl); + } +} + +/** This function does exactly the same as sendto_ops() in practice in 5.x. + * There used to be a difference between sendto_ops() and sendto_realops() + * with regards to user-settable snomasks, but this is no longer the case. + * TODO: remove this function in some future cleanup + */ +void sendto_realops(FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + Client *acptr; + char nbuf[1024]; + + list_for_each_entry(acptr, &oper_list, special_node) + { + ircsnprintf(nbuf, sizeof(nbuf), ":%s NOTICE %s :*** ", me.name, acptr->name); + strlcat(nbuf, pattern, sizeof nbuf); + + va_start(vl, pattern); + vsendto_one(acptr, NULL, nbuf, vl); + va_end(vl); + } +} + +/** Send a message to all locally connected IRCOps and also log the error. + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ +void sendto_ops_and_log(FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + char buf[1024]; + + va_start(vl, pattern); + ircvsnprintf(buf, sizeof(buf), pattern, vl); + va_end(vl); + + ircd_log(LOG_ERROR, "%s", buf); + sendto_umode(UMODE_OPER, "%s", buf); +} + +/** This function does exactly the same as sendto_ops_and_log() + * TODO: remove this function in some future cleanup + */ +void sendto_realops_and_log(FORMAT_STRING(const char *fmt), ...) +{ + va_list vl; + static char buf[2048]; + + va_start(vl, fmt); + vsnprintf(buf, sizeof(buf), fmt, vl); + va_end(vl); + + sendto_realops("%s", buf); + ircd_log(LOG_ERROR, "%s", buf); +} + + +/** Send a message to all locally connected users with specified user mode. + * @param umodes The umode that the recipient should have set (one of UMODE_) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. */ void sendto_umode(int umodes, FORMAT_STRING(const char *pattern), ...) { @@ -687,10 +826,10 @@ void sendto_umode(int umodes, FORMAT_STRING(const char *pattern), ...) } } -/* - * sendto_umode_global - * - * Send to specified umode *GLOBALLY* (on all servers) +/** Send a message to all users with specified user mode (local & remote users). + * @param umodes The umode that the recipient should have set (one of UMODE_*) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. */ void sendto_umode_global(int umodes, FORMAT_STRING(const char *pattern), ...) { @@ -734,10 +873,10 @@ void sendto_umode_global(int umodes, FORMAT_STRING(const char *pattern), ...) } } -/** Send to specified snomask - local / operonly. - * @param snomask Snomask to send to (can be a bitmask [AND]) - * @param pattern printf-style pattern, followed by parameters. - * This function does not send snomasks to non-opers. +/** Send a message to all locally connected users with specified snomask. + * @param snomask The snomask that the recipient should have set (one of SNO_*) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. */ void sendto_snomask(int snomask, FORMAT_STRING(const char *pattern), ...) { @@ -756,10 +895,10 @@ void sendto_snomask(int snomask, FORMAT_STRING(const char *pattern), ...) } } -/** Send to specified snomask - global / operonly. - * @param snomask Snomask to send to (can be a bitmask [AND]) - * @param pattern printf-style pattern, followed by parameters - * This function does not send snomasks to non-opers. +/** Send a message to all users with specified snomask (local and remote users). + * @param snomask The snomask that the recipient should have set (one of SNO_*) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. */ void sendto_snomask_global(int snomask, FORMAT_STRING(const char *pattern), ...) { @@ -788,10 +927,10 @@ void sendto_snomask_global(int snomask, FORMAT_STRING(const char *pattern), ...) sendto_server(NULL, 0, 0, NULL, ":%s SENDSNO %s :%s", me.id, snobuf, nbuf); } -/* - * send_cap_notify - * - * Send CAP DEL or CAP NEW to clients supporting this. +/** Send CAP DEL and CAP NEW notification to clients supporting it. + * This function is mostly meant to be used by the CAP and SASL modules. + * @param add Whether the CAP token is added (1) or removed (0) + * @param token The CAP token */ void send_cap_notify(int add, char *token) { @@ -829,33 +968,6 @@ void send_cap_notify(int add, char *token) } } -/* ** sendto_ops_butone -** Send message to all operators. -** one - client not to send message to -** from- client which message is from *NEVER* NULL!! -*/ -void sendto_ops_butone(Client *one, Client *from, FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - - ++current_serial; - list_for_each_entry(acptr, &client_list, client_node) - { - if (!SendWallops(acptr)) - continue; - if (acptr->direction->local->serial == current_serial) /* sent message along it already ? */ - continue; - if (acptr->direction == one) - continue; /* ...was the one I should skip */ - acptr->direction->local->serial = current_serial; - - va_start(vl, pattern); - vsendto_prefix_one(acptr->direction, from, NULL, pattern, vl); - va_end(vl); - } -} - /* Prepare buffer based on format string and 'from' for LOCAL delivery. * The prefix (:) will be expanded to :nick!user@host if 'from' * is a person, taking into account the rules for hidden/cloaked host. @@ -908,6 +1020,32 @@ static int vmakebuf_local_withprefix(char *buf, size_t buflen, Client *from, con return len; } +/** Send a message to a client, expand the sender prefix. + * This is similar to sendto_one() except that it will expand the source part :%s + * to :nick!user@host if needed, while with sendto_one() it will be :nick. + * @param to The client to send to + * @param mtags Any message tags associated with this message (can be NULL) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ +void sendto_prefix_one(Client *to, Client *from, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + va_start(vl, pattern); + vsendto_prefix_one(to, from, mtags, pattern, vl); + va_end(vl); +} + +/** Send a message to a single client, expand the sender prefix - va_list variant. + * This is similar to vsendto_one() except that it will expand the source part :%s + * to :nick!user@host if needed, while with sendto_one() it will be :nick. + * This function is also similar to sendto_prefix_one(), but this is the va_list + * variant. + * @param to The client to send to + * @param mtags Any message tags associated with this message (can be NULL) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char *pattern, va_list vl) { char *mtags_str = mtags ? mtags_to_string(mtags, to) : NULL; @@ -928,60 +1066,6 @@ void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char } } -/* - * sendto_prefix_one - * - * to - destination client - * from - client which message is from - * - * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!! - * -avalon - */ - -void sendto_prefix_one(Client *to, Client *from, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - va_start(vl, pattern); - vsendto_prefix_one(to, from, mtags, pattern, vl); - va_end(vl); -} - -/* - * sendto_realops - * - * Send to *local* ops only but NOT +s nonopers. - */ -void sendto_realops(FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - char nbuf[1024]; - - list_for_each_entry(acptr, &oper_list, special_node) - { - ircsnprintf(nbuf, sizeof(nbuf), ":%s NOTICE %s :*** ", me.name, acptr->name); - strlcat(nbuf, pattern, sizeof nbuf); - - va_start(vl, pattern); - vsendto_one(acptr, NULL, nbuf, vl); - va_end(vl); - } -} - -/* Sends a message to all (local) opers AND logs to the ircdlog (as LOG_ERROR) */ -void sendto_realops_and_log(FORMAT_STRING(const char *fmt), ...) -{ -va_list vl; -static char buf[2048]; - - va_start(vl, fmt); - vsnprintf(buf, sizeof(buf), fmt, vl); - va_end(vl); - - sendto_realops("%s", buf); - ircd_log(LOG_ERROR, "%s", buf); -} - void sendto_connectnotice(Client *newuser, int disconnect, char *comment) { Client *acptr; @@ -1102,6 +1186,11 @@ void sendto_one_nickcmd(Client *server, Client *client, char *umodes) * has a % in their nick, which is a safe assumption since % is illegal. */ +/** Send a server notice to a client. + * @param to The client to send to + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ void sendnotice(Client *to, FORMAT_STRING(const char *pattern), ...) { static char realpattern[1024]; @@ -1115,25 +1204,34 @@ void sendnotice(Client *to, FORMAT_STRING(const char *pattern), ...) va_end(vl); } -/** Send MultiLine list as a notice, one for each line */ +/** Send MultiLine list as a notice, one for each line. + * @param client The client to send to + * @param m The MultiLine list. + */ void sendnotice_multiline(Client *client, MultiLine *m) { for (; m; m = m->next) sendnotice(client, "%s", m->line); } -void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) -{ - static char realpattern[1024]; - va_list vl; - ircsnprintf(realpattern, sizeof(realpattern), ":%s %d %s :%s", me.name, RPL_TEXT, to->name, pattern); - va_start(vl, pattern); - vsendto_one(to, NULL, realpattern, vl); - va_end(vl); -} - -/** Send numeric to IRC client */ +/** Send numeric message to a client. + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see src/numeric.c + * @param ... The parameters for the numeric + * @note Be sure to provide the correct number and type of parameters that belong to the numeric. Check src/numeric.c when in doubt! + * @section sendnumeric_examples Examples + * @subsection sendnumeric_permission_denied Send "Permission Denied" numeric + * This numeric has no parameter, so is simple: + * @code + * sendnumeric(client, ERR_NOPRIVILEGES); + * @endcode + * @subsection sendnumeric_notenoughparameters Send "Not enough parameters" numeric + * This numeric requires 1 parameter: the name of the command. + * @code + * sendnumeric(client, ERR_NEEDMOREPARAMS, "SOMECOMMAND"); + * @endcode + */ void sendnumeric(Client *to, int numeric, ...) { va_list vl; @@ -1146,7 +1244,15 @@ void sendnumeric(Client *to, int numeric, ...) va_end(vl); } -/** Send numeric to IRC client */ +/** Send numeric message to a client - format to user specific needs. + * This will ignore the numeric definition of src/numeric.c and always send ":me.name numeric clientname " + * followed by the pattern and format string you choose. + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see src/numeric.c + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + * @note Don't forget to add a colon if you need it (eg `:%%s`), this is a common mistake. + */ void sendnumericfmt(Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) { va_list vl; @@ -1159,7 +1265,27 @@ void sendnumericfmt(Client *to, int numeric, FORMAT_STRING(const char *pattern), va_end(vl); } -/** Send raw data directly to socket, bypassing everything. +/** Send text numeric message to a client (RPL_TEXT). + * Because this generic output numeric is commonly used it got a special function for it. + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see src/numeric.c + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + * @note Don't forget to add a colon if you need it (eg `:%%s`), this is a common mistake. + */ +void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) +{ + static char realpattern[1024]; + va_list vl; + + ircsnprintf(realpattern, sizeof(realpattern), ":%s %d %s :%s", me.name, RPL_TEXT, to->name, pattern); + + va_start(vl, pattern); + vsendto_one(to, NULL, realpattern, vl); + va_end(vl); +} + +/* Send raw data directly to socket, bypassing everything. * Looks like an interesting function to call? NO! STOP! * Don't use this function. It may only be used by the initial * Z-Line check via the codepath to banned_client(). @@ -1186,17 +1312,4 @@ void send_raw_direct(Client *user, FORMAT_STRING(FORMAT_STRING(const char *patte (void)send(user->local->fd, sendbuf, sendlen, 0); } -/** Send a message to all locally connected IRCOps and log the error. - */ -void sendto_ops_and_log(FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - char buf[1024]; - - va_start(vl, pattern); - ircvsnprintf(buf, sizeof(buf), pattern, vl); - va_end(vl); - - ircd_log(LOG_ERROR, "%s", buf); - sendto_umode(UMODE_OPER, "%s", buf); -} +/** @} */ diff --git a/src/serv.c b/src/serv.c index 70aa904..e90fd94 100644 --- a/src/serv.c +++ b/src/serv.c @@ -32,7 +32,6 @@ /* for uname(), is POSIX so should be OK... */ #include #endif -extern void s_die(); MODVAR int max_connection_count = 1, max_client_count = 1; extern int do_garbage_collect; diff --git a/src/support.c b/src/support.c index a4196a7..7d34e01 100644 --- a/src/support.c +++ b/src/support.c @@ -724,17 +724,6 @@ void outofmemory(size_t bytes) exit(7); } -/** Check if the specified file exists */ -int file_exists(char *file) -{ - FILE *fd; - fd = fopen(file, "r"); - if (!fd) - return 0; - fclose(fd); - return 1; -} - /** Returns a unique filename in the specified directory * using the specified suffix. The returned value will * be of the form /. diff --git a/src/tls.c b/src/tls.c index f30a240..961e1a2 100644 --- a/src/tls.c +++ b/src/tls.c @@ -36,9 +36,10 @@ extern HWND hwIRCDWnd; #define SAFE_SSL_ACCEPT 3 #define SAFE_SSL_CONNECT 4 +/* Forward declarations */ static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *client); -extern int cipher_check(SSL_CTX *ctx, char **errstr); -extern int certificate_quality_check(SSL_CTX *ctx, char **errstr); +int cipher_check(SSL_CTX *ctx, char **errstr); +int certificate_quality_check(SSL_CTX *ctx, char **errstr); /* The SSL structures */ SSL_CTX *ctx_server; @@ -1040,6 +1041,8 @@ int verify_certificate(SSL *ssl, char *hostname, char **errstr) if (SSL_get_verify_result(ssl) != X509_V_OK) { + // FIXME: there are actually about 25+ different possible errors, + // this is only the most common one: strlcpy(buf, "Certificate is not issued by a trusted Certificate Authority", sizeof(buf)); if (errstr) *errstr = buf; @@ -1333,3 +1336,100 @@ char *outdated_tls_client_build_string(char *pattern, Client *client) buildvarstring(pattern, buf, sizeof(buf), name, value); return buf; } + +int check_certificate_expiry_ctx(SSL_CTX *ctx, char **errstr) +{ +#if !defined(HAS_ASN1_TIME_diff) || !defined(HAS_X509_get0_notAfter) + return 0; +#else + static char errbuf[512]; + SSL *ssl; + X509 *cert; + const ASN1_TIME *cert_expiry_time; + int days_expiry = 0, seconds_expiry = 0; + long duration; + + *errstr = NULL; + + ssl = SSL_new(ctx); + if (!ssl) + return 0; + + cert = SSL_get_certificate(ssl); + if (!cert) + { + SSL_free(ssl); + return 0; + } + + /* get certificate time */ + cert_expiry_time = X509_get0_notAfter(cert); + + /* calculate difference */ + ASN1_TIME_diff(&days_expiry, &seconds_expiry, cert_expiry_time, NULL); + duration = (days_expiry * 86400) + seconds_expiry; + + /* certificate expiry? */ + if ((days_expiry > 0) || (seconds_expiry > 0)) + { + snprintf(errbuf, sizeof(errbuf), "certificate expired %s ago", pretty_time_val(duration)); + SSL_free(ssl); + *errstr = errbuf; + return 1; + } else + /* or near-expiry? */ + if (((days_expiry < 0) || (seconds_expiry < 0)) && (days_expiry > -7)) + { + snprintf(errbuf, sizeof(errbuf), "certificate will expire in %s", pretty_time_val(0 - duration)); + SSL_free(ssl); + *errstr = errbuf; + return 1; + } + + /* All good */ + SSL_free(ssl); + return 0; +#endif +} + +void check_certificate_expiry_tlsoptions_and_warn(TLSOptions *tlsoptions) +{ + SSL_CTX *ctx; + int ret; + char *errstr = NULL; + + ctx = init_ctx(tlsoptions, 1); + if (!ctx) + return; + + if (check_certificate_expiry_ctx(ctx, &errstr)) + { + sendto_umode_global(UMODE_OPER, "Warning: TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); + ircd_log(LOG_ERROR, "[warning] TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); + } + SSL_CTX_free(ctx); +} + +EVENT(tls_check_expiry) +{ + ConfigItem_listen *listen; + ConfigItem_sni *sni; + ConfigItem_link *link; + + /* set block */ + check_certificate_expiry_tlsoptions_and_warn(iConf.tls_options); + + for (listen = conf_listen; listen; listen = listen->next) + if (listen->tls_options) + check_certificate_expiry_tlsoptions_and_warn(listen->tls_options); + + /* sni::tls-options.... */ + for (sni = conf_sni; sni; sni = sni->next) + if (sni->tls_options) + check_certificate_expiry_tlsoptions_and_warn(sni->tls_options); + + /* link::outgoing::tls-options.... */ + for (link = conf_link; link; link = link->next) + if (link->tls_options) + check_certificate_expiry_tlsoptions_and_warn(link->tls_options); +} diff --git a/src/user.c b/src/user.c index 9e0bf1f..ec6f74b 100644 --- a/src/user.c +++ b/src/user.c @@ -726,3 +726,149 @@ int hide_idle_time(Client *client, Client *target) return 0; } } + +/** Check if the name of the security-group contains only valid characters. + * @param name The name of the group + * @returns 1 if name is valid, 0 if not (eg: illegal characters) + */ +int security_group_valid_name(char *name) +{ + char *p; + if (strlen(name) > SECURITYGROUPLEN) + return 0; /* Too long */ + for (p = name; *p; p++) + { + if (!isalnum(*p) && !strchr("_-", *p)) + return 0; /* Character not allowed */ + } + return 1; +} + +/** Find a security-group. + * @param name The name of the security group + * @returns A SecurityGroup struct, or NULL if not found. + */ +SecurityGroup *find_security_group(char *name) +{ + SecurityGroup *s; + for (s = securitygroups; s; s = s->next) + if (!strcasecmp(name, s->name)) + return s; + return NULL; +} + +/** Checks if a security-group exists. + * This function takes the 'unknown-users' magic group into account as well. + * @param name The name of the security group + * @returns 1 if it exists, 0 if not + */ +int security_group_exists(char *name) +{ + if (!strcmp(name, "unknown-users") || find_security_group(name)) + return 1; + return 0; +} + +/** Add a new security-group and add it to the list, but search for existing one first. + * @param name The name of the security group + * @returns A SecurityGroup struct (already added to the 'securitygroups' linked list) + */ +SecurityGroup *add_security_group(char *name, int priority) +{ + SecurityGroup *s = find_security_group(name); + + /* Existing? */ + if (s) + return s; + + /* Otherwise, create a new entry */ + s = safe_alloc(sizeof(SecurityGroup)); + strlcpy(s->name, name, sizeof(s->name)); + s->priority = priority; + AddListItemPrio(s, securitygroups, priority); + return s; +} + +/** Free a SecurityGroup struct */ +void free_security_group(SecurityGroup *s) +{ + /* atm there is nothing else to free, + * but who knows this may change in the future + */ + safe_free(s); +} + +/** Initialize the default security-group blocks */ +void set_security_group_defaults(void) +{ + SecurityGroup *s, *s_next; + + /* First free all security groups */ + for (s = securitygroups; s; s = s_next) + { + s_next = s->next; + free_security_group(s); + } + securitygroups = NULL; + + /* Default group: known-users */ + s = add_security_group("known-users", 100); + s->identified = 1; + s->reputation_score = 25; + s->webirc = 0; + + /* Default group: tls-and-known-users */ + s = add_security_group("tls-and-known-users", 200); + s->identified = 1; + s->reputation_score = 25; + s->webirc = 0; + s->tls = 1; + + /* Default group: tls-users */ + s = add_security_group("tls-users", 300); + s->tls = 1; +} + +/** Returns 1 if the user is OK as far as the security-group is concerned. + * @param client The client to check + * @param s The security-group to check against + * @retval 1 if user is allowed by security-group, 0 if not. + */ +int user_allowed_by_security_group(Client *client, SecurityGroup *s) +{ + if (s->identified && IsLoggedIn(client)) + return 1; + if (s->webirc && moddata_client_get(client, "webirc")) + return 1; + if (s->reputation_score && (GetReputation(client) >= s->reputation_score)) + return 1; + if (s->tls && (IsSecureConnect(client) || IsSecure(client))) + return 1; + return 0; +} + +/** Returns 1 if the user is OK as far as the security-group is concerned - "by name" version. + * @param client The client to check + * @param secgroupname The name of the security-group to check against + * @retval 1 if user is allowed by security-group, 0 if not. + */ +int user_allowed_by_security_group_name(Client *client, char *secgroupname) +{ + SecurityGroup *s; + + /* Handle the magical 'unknown-users' case. */ + if (!strcmp(secgroupname, "unknown-users")) + { + /* This is simply the inverse of 'known-users' */ + s = find_security_group("known-users"); + if (!s) + return 0; /* that's weird!? pretty impossible. */ + return !user_allowed_by_security_group(client, s); + } + + /* Find the group and evaluate it */ + s = find_security_group(secgroupname); + if (!s) + return 0; /* security group not found: no match */ + return user_allowed_by_security_group(client, s); +} diff --git a/src/version.c.SH b/src/version.c.SH index cc282b7..4c2d101 100644 --- a/src/version.c.SH +++ b/src/version.c.SH @@ -4,7 +4,7 @@ echo "Extracting src/version.c..." #id=`grep '$Id: Changes,v' ../Changes` #id=`echo $id |sed 's/.* Changes\,v \(.*\) .* Exp .*/\1/'` -id="5.0.7" +id="5.0.8" echo "$id" if test -r version.c diff --git a/src/windows/UnrealIRCd.exe.manifest b/src/windows/UnrealIRCd.exe.manifest index e4655c4..a34eb7f 100644 --- a/src/windows/UnrealIRCd.exe.manifest +++ b/src/windows/UnrealIRCd.exe.manifest @@ -3,7 +3,7 @@ Internet Relay Chat Daemon diff --git a/src/windows/unrealinst.iss b/src/windows/unrealinst.iss index da2d54a..e571e7a 100644 --- a/src/windows/unrealinst.iss +++ b/src/windows/unrealinst.iss @@ -6,7 +6,7 @@ [Setup] AppName=UnrealIRCd 5 -AppVerName=UnrealIRCd 5.0.7 +AppVerName=UnrealIRCd 5.0.8 AppPublisher=UnrealIRCd Team AppPublisherURL=https://www.unrealircd.org AppSupportURL=https://www.unrealircd.org @@ -68,6 +68,7 @@ Source: "src\modules\chanmodes\*.dll"; DestDir: "{app}\modules\chanmodes"; Flags Source: "src\modules\usermodes\*.dll"; DestDir: "{app}\modules\usermodes"; Flags: ignoreversion Source: "src\modules\snomasks\*.dll"; DestDir: "{app}\modules\snomasks"; Flags: ignoreversion Source: "src\modules\extbans\*.dll"; DestDir: "{app}\modules\extbans"; Flags: ignoreversion +Source: "src\modules\third\*.dll"; DestDir: "{app}\modules\third"; Flags: ignoreversion skipifsourcedoesntexist Source: "c:\dev\unrealircd-5-libs\pcre2\bin\pcre*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\argon2\vs2015\build\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion