mirror of
git://git.acid.vegas/unrealircd.git
synced 2024-12-28 09:16:38 +00:00
Updated to 5.2.0.1
This commit is contained in:
parent
72c7ba196c
commit
fc973f2667
31
Config
31
Config
@ -95,8 +95,19 @@ fi
|
||||
if [ "x$MAKE" = "x" ]; then
|
||||
if [ "x$MAKEFLAGS" = "x" ]; then
|
||||
if make --version 2>&1|grep -q "GNU Make"; then
|
||||
echo "Running with 4 concurrent build processes by default (make -j4)."
|
||||
export MAKE='make -j4'
|
||||
LOWMEM=0
|
||||
if [ -f /proc/meminfo ]; then
|
||||
FREEKB="`cat /proc/meminfo |grep MemAvailable|awk '{ print $2 }'`"
|
||||
if [ "$FREEKB" != "" -a "$FREEKB" -lt 768000 ]; then
|
||||
LOWMEM=1
|
||||
fi
|
||||
fi
|
||||
if [ "$LOWMEM" = 0 ]; then
|
||||
echo "Running with 4 concurrent build processes by default (make -j4)."
|
||||
export MAKE='make -j4'
|
||||
else
|
||||
echo "System detected with less than 750MB available memory, not forcing parallel build."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@ -336,7 +347,7 @@ echo "We will now ask you a number of questions. You can just press ENTER to acc
|
||||
echo ""
|
||||
|
||||
# This needs to be updated each release so auto-upgrading works for settings, modules, etc!!:
|
||||
UNREALRELEASES="unrealircd-5.0.9-rc1 unrealircd-5.0.8 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"
|
||||
UNREALRELEASES="unrealircd-5.2.0 unrealircd-5.2.0-rc1 unrealircd-5.0.9.1 unrealircd-5.0.9 unrealircd-5.0.9-rc1 unrealircd-5.0.8 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
|
||||
@ -639,6 +650,13 @@ if [ "$REMOTEINC" = "1" ] ; then
|
||||
CURLDIR=`eval echo $cc` # modified
|
||||
fi
|
||||
done
|
||||
if [ "x$CURLDIR" != "x" ]; then
|
||||
"$CURLDIR/bin/curl-config" --libs 1>/dev/null 2>&1
|
||||
if [ "$?" != 0 ]; then
|
||||
echo "Curl from $CURLDIR seems unusable ($CURLDIR/bin/curl-config does not exist)"
|
||||
CURLDIR=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -749,6 +767,13 @@ fi
|
||||
rm -f config.settings
|
||||
cat > config.settings << __EOF__
|
||||
#
|
||||
# These are the settings saved from running './Config'.
|
||||
# Note that it is not recommended to edit config.settings by hand!
|
||||
# Chances are you misunderstand what a variable does or what the
|
||||
# supported values are. You better just re-run the ./Config script
|
||||
# and answer appropriately there, to get a correct config.settings
|
||||
# file.
|
||||
#
|
||||
BASEPATH="$BASEPATH"
|
||||
BINDIR="$BINDIR"
|
||||
DATADIR="$DATADIR"
|
||||
|
@ -34,11 +34,11 @@ FROMDOS=/home/cmunk/bin/4dos
|
||||
#
|
||||
|
||||
#XCFLAGS=-O -g -export-dynamic
|
||||
IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @PTHREAD_LIBS@
|
||||
IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @PTHREAD_LIBS@
|
||||
CRYPTOLIB=@CRYPTOLIB@
|
||||
OPENSSLINCLUDES=
|
||||
|
||||
XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@
|
||||
XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@
|
||||
#
|
||||
# use the following on MIPS:
|
||||
#CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR)
|
||||
|
@ -24,6 +24,11 @@ MT=mt
|
||||
#ARGON2_INC_DIR="C:\dev\argon2\include"
|
||||
#ARGON2LIB="Argon2RefDll.lib"
|
||||
|
||||
### SODIUM ###
|
||||
#SODIUM_LIB_DIR="C:\dev\unrealircd-5-libs\libsodium\......."
|
||||
#SODIUM_INC_DIR="C:\dev\unrealircd-5-libs\libsodium\......."
|
||||
#SODIUMLIB="libsodium.lib"
|
||||
|
||||
### C-ARES ####
|
||||
#CARES_LIB_DIR="C:\dev\c-ares\vc\cares\dll-release"
|
||||
#CARES_INC_DIR="C:\dev\c-ares"
|
||||
@ -94,6 +99,13 @@ ARGON2_INC=/I "$(ARGON2_INC_DIR)"
|
||||
ARGON2_LIB=/LIBPATH:"$(ARGON2_LIB_DIR)"
|
||||
!ENDIF
|
||||
|
||||
!IFDEF SODIUM_INC_DIR
|
||||
SODIUM_INC=/I "$(SODIUM_INC_DIR)"
|
||||
!ENDIF
|
||||
!IFDEF SODIUM_LIB_DIR
|
||||
SODIUM_LIB=/LIBPATH:"$(SODIUM_LIB_DIR)"
|
||||
!ENDIF
|
||||
|
||||
!IFDEF USE_REMOTEINC
|
||||
CURLCFLAGS=/D USE_LIBCURL
|
||||
CURLOBJ=SRC/URL.OBJ
|
||||
@ -125,13 +137,13 @@ DBGLFLAG=/debug
|
||||
MODDBGCFLAG=/LDd /MD /Zi
|
||||
!ENDIF
|
||||
|
||||
STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) /J /I ./INCLUDE /nologo \
|
||||
STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(SODIUM_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) /J /I ./INCLUDE /nologo \
|
||||
$(CURLCFLAGS) /D FD_SETSIZE=16384 $(SSLCFLAGS) /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE \
|
||||
/D FAKELAG_CONFIGURABLE=1 \
|
||||
/W3 /wd4267 /wd4101 /wd4018 /wd4244 /wd4996 /WX \
|
||||
/analyze:ruleset extras\VStudioAnalyze.ruleset
|
||||
STDLIBS=$(CARES_LIB) $(CARESLIB) $(PCRE2_LIB) $(PCRE2LIB) $(ARGON2_LIB) $(ARGON2LIB) \
|
||||
$(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB)
|
||||
$(SODIUM_LIB) $(SODIUMLIB) $(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB)
|
||||
CFLAGS=$(DBGCFLAG) $(STDOPTIONS) /c /Fosrc/
|
||||
CFLAGSST=$(DBGCFLAGST) $(STDOPTIONS) /c /Fosrc/
|
||||
LFLAGS=kernel32.lib user32.lib gdi32.lib shell32.lib ws2_32.lib advapi32.lib \
|
||||
@ -161,7 +173,7 @@ EXP_OBJ_FILES=SRC/CHANNEL.OBJ SRC/SEND.OBJ SRC/SOCKET.OBJ \
|
||||
SRC/DISPATCH.OBJ SRC/API-ISUPPORT.OBJ SRC/API-COMMAND.OBJ \
|
||||
SRC/API-CLICAP.OBJ SRC/API-MESSAGETAG.OBJ SRC/API-HISTORY-BACKEND.OBJ \
|
||||
SRC/API-EXTBAN.OBJ SRC/API-EFUNCTIONS.OBJ SRC/CRYPT_BLOWFISH.OBJ \
|
||||
SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ SRC/CRASHREPORT.OBJ \
|
||||
SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ SRC/CRASHREPORT.OBJ SRC/UNREALDB.OBJ \
|
||||
SRC/OPENSSL_HOSTNAME_VALIDATION.OBJ \
|
||||
SRC/UTF8.OBJ $(CURLOBJ)
|
||||
|
||||
@ -289,9 +301,12 @@ DLL_FILES=SRC/MODULES/CLOAK.DLL \
|
||||
SRC/MODULES/ECHO-MESSAGE.DLL \
|
||||
SRC/MODULES/USERIP-TAG.DLL \
|
||||
SRC/MODULES/USERHOST-TAG.DLL \
|
||||
SRC/MODULES/BOT-TAG.DLL \
|
||||
SRC/MODULES/REPLY-TAG.DLL \
|
||||
SRC/MODULES/REQUIRE-MODULE.DLL \
|
||||
SRC/MODULES/IDENT_LOOKUP.DLL \
|
||||
SRC/MODULES/HISTORY.DLL \
|
||||
SRC/MODULES/CHATHISTORY.DLL \
|
||||
SRC/MODULES/TARGETFLOODPROT.DLL \
|
||||
SRC/MODULES/TYPING-INDICATOR.DLL \
|
||||
SRC/MODULES/CLIENTTAGDENY.DLL
|
||||
@ -530,6 +545,12 @@ src/operclass.obj: src/operclass.c $(INCLUDES) ./include/dbuf.h
|
||||
src/updconf.obj: src/updconf.c $(INCLUDES) ./include/dbuf.h
|
||||
$(CC) $(CFLAGS) src/updconf.c
|
||||
|
||||
src/crashreport.obj: src/crashreport.c $(INCLUDES) ./include/dbuf.h
|
||||
$(CC) $(CFLAGS) src/crashreport.c
|
||||
|
||||
src/unrealdb.obj: src/unrealdb.c $(INCLUDES) ./include/dbuf.h
|
||||
$(CC) $(CFLAGS) src/unrealdb.c
|
||||
|
||||
src/utf8.obj: src/utf8.c $(INCLUDES) ./include/dbuf.h
|
||||
$(CC) $(CFLAGS) src/utf8.c
|
||||
|
||||
@ -1079,6 +1100,12 @@ src/modules/userip-tag.dll: src/modules/userip-tag.c $(INCLUDES)
|
||||
src/modules/userhost-tag.dll: src/modules/userhost-tag.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/userhost-tag.c $(MODLFLAGS)
|
||||
|
||||
src/modules/bot-tag.dll: src/modules/bot-tag.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/bot-tag.c $(MODLFLAGS)
|
||||
|
||||
src/modules/reply-tag.dll: src/modules/reply-tag.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/reply-tag.c $(MODLFLAGS)
|
||||
|
||||
src/modules/require-module.dll: src/modules/require-module.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/require-module.c $(MODLFLAGS)
|
||||
|
||||
@ -1088,6 +1115,9 @@ src/modules/ident_lookup.dll: src/modules/ident_lookup.c $(INCLUDES)
|
||||
src/modules/history.dll: src/modules/history.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history.c $(MODLFLAGS)
|
||||
|
||||
src/modules/chathistory.dll: src/modules/chathistory.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/chathistory.c $(MODLFLAGS)
|
||||
|
||||
src/modules/targetfloodprot.dll: src/modules/targetfloodprot.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/targetfloodprot.c $(MODLFLAGS)
|
||||
|
||||
|
72
configure.ac
72
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.9], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
|
||||
AC_INIT([unrealircd], [5.2.0.1], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
|
||||
AC_CONFIG_SRCDIR([src/ircd.c])
|
||||
AC_CONFIG_HEADER([include/setup.h])
|
||||
AC_CONFIG_AUX_DIR([autoconf])
|
||||
@ -30,17 +30,17 @@ UNREAL_VERSION_GENERATION=["5"]
|
||||
AC_DEFINE_UNQUOTED([UNREAL_VERSION_GENERATION], [$UNREAL_VERSION_GENERATION], [Generation version number (e.g.: X for X.Y.Z)])
|
||||
|
||||
# Major version number (e.g.: Y in X.Y.Z)
|
||||
UNREAL_VERSION_MAJOR=["0"]
|
||||
UNREAL_VERSION_MAJOR=["2"]
|
||||
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=["9"]
|
||||
UNREAL_VERSION_MINOR=["0"]
|
||||
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
|
||||
# marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a
|
||||
# string instead of an integer because it contains arbitrary data.
|
||||
UNREAL_VERSION_SUFFIX=[""]
|
||||
UNREAL_VERSION_SUFFIX=[".1"]
|
||||
AC_DEFINE_UNQUOTED([UNREAL_VERSION_SUFFIX], ["$UNREAL_VERSION_SUFFIX"], [Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX)])
|
||||
|
||||
AC_PATH_PROG(RM,rm)
|
||||
@ -83,6 +83,18 @@ AC_CHECK_LIB(descrypt, crypt,
|
||||
[AC_DEFINE([HAVE_CRYPT], [], [Define if you have crypt])
|
||||
IRCDLIBS="$IRCDLIBS-lcrypt "])])
|
||||
|
||||
dnl Check for big-endian system, even though these hardly exist anymore...
|
||||
AS_CASE([$host_cpu],
|
||||
[i?86|amd64|x86_64],
|
||||
[ac_cv_c_bigendian=no]
|
||||
)
|
||||
AC_C_BIGENDIAN(
|
||||
AC_DEFINE(NATIVE_BIG_ENDIAN, 1, [machine is bigendian]),
|
||||
AC_DEFINE(NATIVE_LITTLE_ENDIAN, 1, [machine is littleendian]),
|
||||
AC_MSG_ERROR([unknown endianness]),
|
||||
AC_MSG_ERROR([universal endianness is not supported - compile separately and use lipo(1)])
|
||||
)
|
||||
|
||||
dnl HARDENING START
|
||||
dnl This is taken from https://github.com/kmcallister/autoharden
|
||||
dnl With some very small modifications (to remove C++ checking for instance)
|
||||
@ -499,6 +511,7 @@ AC_ARG_WITH(operoverride-verify, [AS_HELP_STRING([--with-operoverride-verify], [
|
||||
[AC_DEFINE([OPEROVERRIDE_VERIFY], [], [Define if you want opers to have to use /invite to join +s/+p channels])])])
|
||||
AC_ARG_WITH(system-pcre2, [AS_HELP_STRING([--without-system-pcre2], [Use the system pcre2 package instead of bundled, discovered using pkg-config])], [], [with_system_pcre2=yes])
|
||||
AC_ARG_WITH(system-argon2, [AS_HELP_STRING([--without-system-argon2], [Use bundled version instead of system argon2 library. Normally autodetected via pkg-config])], [], [with_system_argon2=yes])
|
||||
AC_ARG_WITH(system-sodium, [AS_HELP_STRING([--without-system-sodium], [Use bundled version instead of system sodium library. Normally autodetected via pkg-config])], [], [with_system_sodium=yes])
|
||||
AC_ARG_WITH(system-cares, [AS_HELP_STRING([--without-system-cares], [Use bundled version instead of system c-ares. Normally autodetected via pkg-config.])], [], [with_system_cares=yes])
|
||||
CHECK_SSL
|
||||
CHECK_SSL_CTX_SET1_CURVES_LIST
|
||||
@ -625,6 +638,55 @@ AC_SUBST(ARGON2_LIBS)
|
||||
cd $cur_dir
|
||||
])
|
||||
|
||||
dnl Use system sodium when available, unless --without-system-sodium
|
||||
has_system_sodium="no"
|
||||
AS_IF([test "x$with_system_sodium" = "xyes"],[
|
||||
PKG_CHECK_MODULES([SODIUM], [libsodium >= 1.0.16],[has_system_sodium=yes
|
||||
AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libsodium*])],[has_system_sodium=no])])
|
||||
|
||||
AS_IF([test "$has_system_sodium" = "no"],[
|
||||
dnl REMEMBER TO CHANGE WITH A NEW SODIUM RELEASE!
|
||||
sodium_version="1.0.18"
|
||||
AC_MSG_RESULT(extracting sodium library)
|
||||
cur_dir=`pwd`
|
||||
cd extras
|
||||
dnl remove old sodium directory to force a recompile...
|
||||
dnl and remove its installation prefix just to clean things up.
|
||||
rm -rf sodium-$sodium_version sodium
|
||||
if test "x$ac_cv_path_GUNZIP" = "x" ; then
|
||||
tar xfz libsodium.tar.gz
|
||||
else
|
||||
cp libsodium.tar.gz libsodium.tar.gz.bak
|
||||
gunzip -f libsodium.tar.gz
|
||||
cp libsodium.tar.gz.bak libsodium.tar.gz
|
||||
tar xf libsodium.tar
|
||||
fi
|
||||
AC_MSG_RESULT(compiling sodium library)
|
||||
cd libsodium-$sodium_version
|
||||
save_cflags="$CFLAGS"
|
||||
CFLAGS="$orig_cflags"
|
||||
export CFLAGS
|
||||
./configure --prefix=$cur_dir/extras/sodium --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1
|
||||
CFLAGS="$save_cflags"
|
||||
AC_MSG_RESULT(compiling sodium resolver library)
|
||||
$ac_cv_prog_MAKER || exit 1
|
||||
AC_MSG_RESULT(installing sodium resolver library)
|
||||
$ac_cv_prog_MAKER install || exit 1
|
||||
SODIUM_CFLAGS="-I$cur_dir/extras/sodium/include"
|
||||
AC_SUBST(SODIUM_CFLAGS)
|
||||
SODIUM_LIBS=
|
||||
dnl See c-ares's compilation section for more info on this hack.
|
||||
dnl ensure that we're linking against the bundled version
|
||||
dnl (we only reach this code if linking against the bundled version is desired).
|
||||
AS_IF([test -n "$ac_cv_path_PKGCONFIG"],
|
||||
[SODIUM_LIBS="`$ac_cv_path_PKGCONFIG --libs libsodium.pc`"])
|
||||
dnl For when pkg-config isn't available
|
||||
AS_IF([test -z "$SODIUM_LIBS"],
|
||||
[SODIUM_LIBS="-L$PRIVATELIBDIR -lsodium"])
|
||||
AC_SUBST(SODIUM_LIBS)
|
||||
cd $cur_dir
|
||||
])
|
||||
|
||||
dnl Use system c-ares when available, unless --without-system-cares.
|
||||
has_system_cares="no"
|
||||
AS_IF([test "x$with_system_cares" = "xyes"],[
|
||||
@ -654,7 +716,7 @@ cd c-ares-$cares_version
|
||||
save_cflags="$CFLAGS"
|
||||
CFLAGS="$orig_cflags"
|
||||
export CFLAGS
|
||||
./configure --prefix=$cur_dir/extras/c-ares --libdir=$PRIVATELIBDIR --enable-shared || exit 1
|
||||
./configure --prefix=$cur_dir/extras/c-ares --libdir=$PRIVATELIBDIR --enable-shared --disable-tests || exit 1
|
||||
CFLAGS="$save_cflags"
|
||||
AC_MSG_RESULT(compiling c-ares resolver library)
|
||||
$ac_cv_prog_MAKER || exit 1
|
||||
|
@ -7,7 +7,7 @@
|
||||
\___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_|
|
||||
|
||||
Configuration Program
|
||||
for UnrealIRCd 5.0.9
|
||||
for UnrealIRCd 5.2.0.1
|
||||
|
||||
This program will help you to compile your IRC server, and ask you
|
||||
questions regarding the compile-time settings of it during the process.
|
||||
@ -22,7 +22,7 @@ https://www.unrealircd.org/docs/UnrealIRCd_5_documentation
|
||||
|
||||
The full release notes are available in doc/RELEASE-NOTES.md
|
||||
For easier viewing, check out the latest online release notes at:
|
||||
https://github.com/unrealircd/unrealircd/blob/unreal50/doc/RELEASE-NOTES.md
|
||||
https://github.com/unrealircd/unrealircd/blob/unreal52/doc/RELEASE-NOTES.md
|
||||
|
||||
UnrealIRCd 5 is compatible with the following services:
|
||||
* anope with the "unreal4" protocol module - version 2.0.7 or higher required!
|
||||
|
@ -1,7 +1,176 @@
|
||||
UnrealIRCd 5.0.9 Release Notes
|
||||
===============================
|
||||
UnrealIRCd 5.2.0.1 Release Notes
|
||||
=================================
|
||||
|
||||
This release comes with several nice feature enhancements. There are no major bug fixes.
|
||||
About 5.2.0.1
|
||||
--------------
|
||||
5.2.0.1 fixes an issue with spamfilter that was present in 5.2.0.
|
||||
In channels spamfilters were processed for type ```p``` instead of ```c```.
|
||||
Existing 5.2.0 users on *NIX can upgrade without restart by running
|
||||
```./unrealircd hot-patch wrongspamfilter520```
|
||||
|
||||
UnrealIRCd 5.2.0 is out!
|
||||
-------------------------
|
||||
|
||||
This is UnrealIRCd 5.2.0, a release with lots of new features.
|
||||
The two main new features are: an improved and more flexible anti-flood block
|
||||
and channel history which can now be stored encrypted on disk and allows
|
||||
clients to fetch hundreds/thousands of lines.
|
||||
|
||||
Upgrading and the 5.0.x series
|
||||
-------------------------------
|
||||
UnrealIRCd 5.2.0 is the direct successor to 5.0.9/5.0.9.1.
|
||||
There will be [no further 5.0.x releases](https://www.unrealircd.org/docs/FAQ#About_the_new_5.2.x_series),
|
||||
in particular there will be no 5.0.10.
|
||||
|
||||
Only four bugs that affect a limited number of people/networks were fixed.
|
||||
UnrealIRCd 5.2.0 is mostly a feature release.
|
||||
Admins wishing to take a conservative approach don't need to rush an
|
||||
upgrade from 5.0.x to 5.2.0, they can wait for a 5.2.1 or 5.2.2 release.
|
||||
|
||||
If you are upgrading from 5.0.9(.1) to 5.2.0 then feel free to try the new
|
||||
```./unrealircd upgrade``` command.
|
||||
|
||||
The only configuration change is in the set::anti-flood block (as explained
|
||||
further down under *Enhancements*). When starting UnrealIRCd will give you
|
||||
clear instructions if anything needs to be changed (and what).
|
||||
This process is really minor, the server will usually tell you to just
|
||||
delete a few old lines from the configuration file.
|
||||
|
||||
Enhancements
|
||||
-------------
|
||||
* The set::anti-flood block has been redone so you can have different limits
|
||||
for *unknown-users* and *known-users*.
|
||||
* As a reminder, by default, *known-users* are users who are identified
|
||||
to services OR are on an IP that has been connected for over 2 hours
|
||||
in the past X days. The exact definition of "known-users" is in the
|
||||
[security-group block](https://www.unrealircd.org/docs/Security-group_block).
|
||||
* See [here](https://www.unrealircd.org/docs/Anti-flood_settings)
|
||||
for more information on the layout of the new set::anti-flood block.
|
||||
* All violations of target-flood, nick-flood, join-flood, away-flood,
|
||||
invite-flood, knock-flood, max-concurrent-conversations are now
|
||||
reported to opers with the snomask ```f``` (flood).
|
||||
* Add support for database encryption. The way this works
|
||||
is that you define an encryption password in a
|
||||
[secret { } block](https://www.unrealircd.org/docs/Secret_block).
|
||||
Then from the various modules you can refer to this secret
|
||||
block, from
|
||||
[set::reputation::db-secret](https://www.unrealircd.org/docs/Set_block#set::reputation),
|
||||
[set::tkldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::tkldb)
|
||||
and [set::channeldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::channeldb).
|
||||
This way you can encrypt the reputation, TKL and channel
|
||||
database for increased privacy.
|
||||
* Add optional support for
|
||||
[persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history):
|
||||
* This stores channel history on disk for channels that have
|
||||
both ```+H``` and ```+P``` set.
|
||||
* If you enable this then we ALWAYS require you to set an
|
||||
encryption password, as we do not allow storing of
|
||||
channel history in plain text.
|
||||
* If you enable the option, then the history is stored in
|
||||
```data/history/``` in individual .db files. No channel
|
||||
names are visible in the filenames for optimal privacy.
|
||||
* See [Persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history)
|
||||
on how to enable this. By default it is off.
|
||||
* Add support for IRCv3
|
||||
[draft/chathistory](https://ircv3.net/specs/extensions/chathistory).
|
||||
* The maximums for channel mode ```+H``` have been raised and are now
|
||||
different for ```+r``` (registered) and ```-r``` channels. For unregistered
|
||||
channels the limit is now 200 lines / 31 days. For registered channels
|
||||
the limit is 5000 lines / 31 days. The old limit for both was 200 lines / 7 days.
|
||||
These maximums can be changed in the now slightly different
|
||||
[set::history::channel::max-storage-per-channel](https://www.unrealircd.org/docs/Set_block#set::history)
|
||||
block.
|
||||
* Add c-ares and libsodium version output to boot screen and /VERSION.
|
||||
* WHOX now supports displaying the
|
||||
[reputation score](https://www.unrealircd.org/docs/Reputation_score).
|
||||
If you are an IRCOp then you can use e.g. ```WHO * %cuhsnfmdaRr```.
|
||||
* Add ability to [spamfilter](https://www.unrealircd.org/docs/Spamfilter)
|
||||
message tags via the new ```T``` target. Right now it would be unusual
|
||||
to use this, but some day when we have more
|
||||
[message tags](https://www.unrealircd.org/docs/Message_tags) it
|
||||
may come in handy.
|
||||
* Support [```+draft/reply```](https://ircv3.net/specs/client-tags/reply) IRCv3
|
||||
client tag. Can be used by bots (and others) to indicate to what message
|
||||
people are replying to. This module, reply-tag, is loaded by default.
|
||||
* Send [```draft/bot```](https://ircv3.net/specs/extensions/bot-mode) IRCv3
|
||||
message tag if the user has mode ```+B``` set.
|
||||
* [Websockets](https://www.unrealircd.org/docs/WebSocket_support):
|
||||
add support for clients to negotiate an explicit type via
|
||||
```Sec-WebSocket-Protocol```, instead of only the default type from
|
||||
[listen::websocket::type](https://www.unrealircd.org/docs/WebSocket_support#2._Enable_websocket_on_the_port).
|
||||
This is based on an IRCv3 websocket draft specification.
|
||||
Note that UnrealIRCd refuses type text if your configuration allows
|
||||
non-UTF8 characters in channel or nick names because it would lead
|
||||
to security and compatibility issues.
|
||||
* [set::restrict-commands](https://www.unrealircd.org/docs/Set_block#set::restrict-commands):
|
||||
new option *exempt-tls* which allows SSL/TLS users to bypass a restriction.
|
||||
|
||||
Fixes
|
||||
------
|
||||
* Server squiting the wrong side. Often harmless, but when (re)connecting
|
||||
rapidly to multiple servers with autoconnect this could cause the
|
||||
network to fall apart.
|
||||
* Forbid using [extended server bans](https://www.unrealircd.org/docs/Extended_server_bans)
|
||||
in ZLINE/GZLINE since they won't work there.
|
||||
* Extended server ban ```~a:accname``` was not working for shun, and only
|
||||
partially working for kline/gline.
|
||||
* More accurate /ELINE error message.
|
||||
|
||||
Changed
|
||||
--------
|
||||
* Channel mode ```+H``` always showed time in minutes (```m```) until now.
|
||||
From now on it will show it in minutes (```m```), hours (```h```) or
|
||||
days (```d```) depending on the actual value. Eg ```+H 50:7d```.
|
||||
* If you ran ```./unrealircd stop``` we used to wait only 1 second.
|
||||
From now on we will wait up to 10 seconds max. This gives UnrealIRCd
|
||||
plenty of time to write database files.
|
||||
* If you have zero [log blocks](https://www.unrealircd.org/docs/Log_block)
|
||||
then we already automatically logged errors to ```ircd.log```.
|
||||
From now on we will log everything (not only errors) to that file.
|
||||
|
||||
Removed
|
||||
--------
|
||||
* Version check for curl and openssl as nowadays they have ABI guarantees.
|
||||
|
||||
Module coders / Developers
|
||||
---------------------------
|
||||
* New UnrealDB API and disk format, see
|
||||
https://www.unrealircd.org/docs/Dev:UnrealDB
|
||||
* We now use libsodium for file encryption routines as well
|
||||
as some helpers to lock/clear passwords in memory.
|
||||
* Updated ```HOOKTYPE_LOCAL_NICKCHANGE``` and
|
||||
```HOOKTYPE_REMOTE_NICKCHANGE``` to include an
|
||||
```MessageTag *mtags``` argument in the middle.
|
||||
You can use ```#if UNREAL_VERSION_TIME>=202115``` to detect this.
|
||||
* Updated channel mode ```conv_param``` function to
|
||||
include a ```Channel *channel``` argument at the end.
|
||||
You can use ```#if UNREAL_VERSION_TIME>=202120``` to detect this.
|
||||
* New: ```ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, priority);```.
|
||||
This can be used for modules to indicate they wish to be unloaded
|
||||
before or after others. It is used by for example the channel
|
||||
and history modules so they can save their databases before
|
||||
channel mode modules or other modules get unloaded.
|
||||
* New CAP [```draft/chathistory```](https://ircv3.net/specs/extensions/chathistory).
|
||||
If a client REQ's this CAP then UnrealIRCd won't send history on-join as
|
||||
it assumes the client will fetch it when they feel the need for it.
|
||||
* New informative CAP:
|
||||
[```unrealircd.org/history-backend```](https://www.unrealircd.org/history-backend)
|
||||
|
||||
Reminder: UnrealIRCd 4 is no longer supported
|
||||
----------------------------------------------
|
||||
|
||||
UnrealIRCd 4.x is [no longer supported](https://www.unrealircd.org/docs/UnrealIRCd_4_EOL).
|
||||
Admins must [upgrade to UnrealIRCd 5](https://www.unrealircd.org/docs/Upgrading_from_4.x).
|
||||
|
||||
UnrealIRCd 5.0.9.1
|
||||
-------------------
|
||||
The only change between 5.0.9 and 5.0.9.1 is:
|
||||
* Build improvements on *NIX (faster compiling and lower memory requirements)
|
||||
* Windows version is unchanged and still 5.0.9
|
||||
|
||||
UnrealIRCd 5.0.9
|
||||
-----------------
|
||||
The 5.0.9 release comes with several nice feature enhancements. There are no major bug fixes.
|
||||
|
||||
Enhancements:
|
||||
* Changes to the "Client connecting" notice on IRC (for IRCOps):
|
||||
@ -38,7 +207,7 @@ Fixes:
|
||||
missing.
|
||||
|
||||
Changes:
|
||||
* Add doc/KEYS which contains the public key(s) used to sign UnrealIRCd releases
|
||||
* Add ```doc/KEYS``` which contains the public key(s) used to sign UnrealIRCd releases
|
||||
* The options set::anti-flood::unknown-flood-* have been renamed and
|
||||
integrated in a new block called
|
||||
[set::anti-flood::handshake-data-flood](https://www.unrealircd.org/docs/Set_block#set::anti-flood::handshake-data-flood).
|
||||
@ -49,18 +218,6 @@ That is, when in "auto" mode, which is like for 99% of the users.
|
||||
Note that the system may still limit the actual number of connections
|
||||
to a lower value, epending on the value of ```ulimit -n -H```.
|
||||
|
||||
Reminder: UnrealIRCd 4 is no longer supported
|
||||
----------------------------------------------
|
||||
|
||||
UnrealIRCd 4.x is [no longer supported](https://www.unrealircd.org/docs/UnrealIRCd_4_EOL).
|
||||
Admins must upgrade to UnrealIRCd 5.
|
||||
|
||||
Upgrading from 4.x to 5.x?
|
||||
Then check out the *UnrealIRCd 5* release notes [further down](#unrealircd-5).
|
||||
Or, at the very least, check out
|
||||
[Upgrading from 4.x](https://www.unrealircd.org/docs/Upgrading_from_4.x).
|
||||
|
||||
|
||||
UnrealIRCd 5.0.8
|
||||
-----------------
|
||||
|
||||
|
@ -162,6 +162,8 @@ loadmodule "extbans/securitygroup"; /* +b ~G */
|
||||
loadmodule "account-notify";
|
||||
loadmodule "account-tag";
|
||||
loadmodule "batch";
|
||||
loadmodule "bot-tag";
|
||||
loadmodule "chathistory";
|
||||
loadmodule "clienttagdeny";
|
||||
loadmodule "echo-message";
|
||||
loadmodule "labeled-response";
|
||||
@ -169,6 +171,7 @@ loadmodule "link-security";
|
||||
loadmodule "message-ids";
|
||||
loadmodule "message-tags";
|
||||
loadmodule "plaintext-policy";
|
||||
loadmodule "reply-tag";
|
||||
loadmodule "server-time";
|
||||
loadmodule "sts";
|
||||
loadmodule "typing-indicator";
|
||||
@ -186,7 +189,7 @@ loadmodule "history_backend_mem";
|
||||
#loadmodule "history_backend_null";
|
||||
loadmodule "ident_lookup";
|
||||
loadmodule "jointhrottle";
|
||||
#loadmodule "targetfloodprot";
|
||||
loadmodule "targetfloodprot";
|
||||
loadmodule "tkldb";
|
||||
loadmodule "tls_antidos";
|
||||
loadmodule "userhost-tag";
|
||||
|
@ -5,7 +5,7 @@ oper acidvegas {
|
||||
operclass netadmin;
|
||||
require-modes z;
|
||||
maxlogins 1;
|
||||
vhost super.nets;
|
||||
vhost most.dangerous.motherfuck;
|
||||
swhois "1,1 1,5 1,1 ";
|
||||
swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,1 ";
|
||||
swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,7 1,5 1,1 0 1 ";
|
||||
|
@ -26,6 +26,10 @@ listen { ip *; port 6667; options { clientsonly; } }
|
||||
listen { ip *; port 6697; options { clientsonly; tls; } }
|
||||
listen { ip *; port REDACTED; options { serversonly; tls; } }
|
||||
|
||||
deny channel { channel "#pumpcoin"; reason "This channel has moved to #exchange"; redirect "#exchange"; }
|
||||
deny channel { channel "#dev"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; }
|
||||
deny channel { channel "#help"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; }
|
||||
|
||||
link irc.supernets.org {
|
||||
incoming { mask REDACTED; }
|
||||
outgoing {
|
||||
@ -106,7 +110,7 @@ set {
|
||||
private-notice { connect-delay 3600; exempt-identified yes; exempt-reputation-score 100; }
|
||||
}
|
||||
#auto-join "#superbowl";
|
||||
oper-auto-join "#help";
|
||||
oper-auto-join "#superbowl";
|
||||
static-quit "EMO-QUIT";
|
||||
static-part "EMO-PART";
|
||||
who-limit 100;
|
||||
@ -141,28 +145,43 @@ set {
|
||||
oper-message "Network operators must connect using an up-to-date SSL/TLS protocol or cipher";
|
||||
}
|
||||
anti-flood {
|
||||
away-flood 3:300;
|
||||
connect-flood 3:300;
|
||||
invite-flood 3:300;
|
||||
join-flood 3:300;
|
||||
knock-flood 3:300;
|
||||
nick-flood 3:300;
|
||||
max-concurrent-conversations {
|
||||
users 5;
|
||||
new-user-every 60s;
|
||||
everyone {
|
||||
connect-flood 3:300;
|
||||
handshake-data-flood {
|
||||
amount 4k;
|
||||
ban-action gzline;
|
||||
ban-time 1h;
|
||||
}
|
||||
target-flood {
|
||||
channel-notice 15:5;
|
||||
channel-privmsg 45:5;
|
||||
channel-tagmsg 15:5;
|
||||
private-notice 10:5;
|
||||
private-privmsg 30:5;
|
||||
private-tagmsg 10:5;
|
||||
}
|
||||
}
|
||||
#target-flood {
|
||||
# channel-privmsg 45:5;
|
||||
# channel-notice 15:5;
|
||||
# channel-tagmsg 15:5;
|
||||
# private-privmsg 30:5;
|
||||
# private-notice 10:5;
|
||||
# private-tagmsg 10:5;
|
||||
#}
|
||||
handshake-data-flood {
|
||||
amount 4k;
|
||||
ban-action gzline;
|
||||
ban-time 1h;
|
||||
known-users {
|
||||
away-flood 3:300;
|
||||
invite-flood 3:300;
|
||||
join-flood 3:300;
|
||||
knock-flood 3:300;
|
||||
nick-flood 3:300;
|
||||
max-concurrent-conversations {
|
||||
users 5;
|
||||
new-user-every 60s;
|
||||
}
|
||||
}
|
||||
unknown-users {
|
||||
away-flood 3:300;
|
||||
invite-flood 3:300;
|
||||
join-flood 3:300;
|
||||
knock-flood 3:300;
|
||||
nick-flood 3:300;
|
||||
max-concurrent-conversations {
|
||||
users 3;
|
||||
new-user-every 60s;
|
||||
}
|
||||
}
|
||||
}
|
||||
default-bantime 30d;
|
||||
@ -183,19 +202,22 @@ set {
|
||||
unauthorized "8,4 E N T E R T H E V O I D ";
|
||||
}
|
||||
antimixedutf8 {
|
||||
score 10;
|
||||
score 8;
|
||||
ban-action block;
|
||||
ban-reason "8,4 E N T E R T H E V O I D ";
|
||||
}
|
||||
connthrottle {
|
||||
known-users { minimum-reputation-score 24; sasl-bypass yes; }
|
||||
new-users { local-throttle 20:60; global-throttle 30:60; }
|
||||
disabled-when { reputation-gathering 1w; start-delay 3m; }
|
||||
known-users { minimum-reputation-score 100; sasl-bypass yes; }
|
||||
new-users { local-throttle 20:60; global-throttle 30:60; }
|
||||
disabled-when { reputation-gathering 1w; start-delay 3m; }
|
||||
}
|
||||
history {
|
||||
channel {
|
||||
playback-on-join { lines 100; time 1d; }
|
||||
max-storage-per-channel { lines 100; time 1d; }
|
||||
playback-on-join { lines 100; time 1d; }
|
||||
max-storage-per-channel {
|
||||
registered { lines 100; time 1d; }
|
||||
unregistered { lines 50; time 12h; }
|
||||
}
|
||||
}
|
||||
}
|
||||
hide-idle-time { policy usermode; }
|
||||
@ -206,4 +228,11 @@ hideserver {
|
||||
disable-links yes;
|
||||
map-deny-message "Denied";
|
||||
links-deny-message "Denied";
|
||||
}
|
||||
|
||||
security-group known-users {
|
||||
identified yes;
|
||||
webirc no;
|
||||
tls no;
|
||||
reputation-score 100;
|
||||
}
|
@ -40,6 +40,14 @@ else
|
||||
./run -services atheme || exit 1
|
||||
fi
|
||||
|
||||
# Database writing/reading tests
|
||||
## unencrypted:
|
||||
./run -services none -boot tests/db/writing/* || exit 1
|
||||
./run -services none -keepdbs -boot tests/db/reading/* || exit 1
|
||||
## encrypted:
|
||||
./run -services none -include db_crypted.conf -boot tests/db/writing/* || exit 1
|
||||
./run -services none -include db_crypted.conf -keepdbs -boot tests/db/reading/* || exit 1
|
||||
|
||||
# Do cipherscan test at the end
|
||||
# Has problems on non-Linux-64-bit, so we skip there:
|
||||
if [ "$FREEBSD" = 0 -a "$HOSTNAME" != "ub18-ia32" ]; then
|
||||
|
@ -78,11 +78,26 @@ if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
cd unrealircd-tests
|
||||
dir
|
||||
|
||||
rem All tests except db:
|
||||
"C:\Program Files\Git\bin\bash.exe" ./runwin
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
|
||||
rem Test unencrypted db's:
|
||||
"C:\Program Files\Git\bin\bash.exe" ./runwin -boot tests/db/writing/*
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
"C:\Program Files\Git\bin\bash.exe" ./runwin -keepdbs -boot tests/db/reading/*
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
|
||||
rem Test encrypted db's:
|
||||
"C:\Program Files\Git\bin\bash.exe" ./runwin -include db_crypted.conf -boot tests/db/writing/*
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
"C:\Program Files\Git\bin\bash.exe" ./runwin -include db_crypted.conf -keepdbs -boot tests/db/reading/*
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /B 1
|
||||
|
||||
goto end
|
||||
|
||||
|
||||
|
||||
:installerfailed
|
||||
type setup.log
|
||||
echo INSTALLATION FAILED
|
||||
|
@ -15,4 +15,7 @@ PCRE2_LIB_DIR="c:\projects\unrealircd-5-libs\pcre2\lib" ^
|
||||
PCRE2LIB="pcre2-8.lib" ^
|
||||
ARGON2_LIB_DIR="c:\projects\unrealircd-5-libs\argon2\vs2015\build" ^
|
||||
ARGON2_INC_DIR="c:\projects\unrealircd-5-libs\argon2\include" ^
|
||||
ARGON2LIB="Argon2RefDll.lib" %*
|
||||
ARGON2LIB="Argon2RefDll.lib" ^
|
||||
SODIUM_LIB_DIR="c:\projects\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic" ^
|
||||
SODIUM_INC_DIR="c:\projects\unrealircd-5-libs\libsodium\src\libsodium\include" ^
|
||||
SODIUMLIB="libsodium.lib" %*
|
||||
|
@ -90,7 +90,7 @@ cd "$OUTD" || exit 1
|
||||
|
||||
echo "Building and installing libcurl"
|
||||
CPPFLAGS="-I$ARESPATH/include" ./configure --prefix=$UNREALDIR/extras/curl --libdir=$LIBDIR --enable-shared \
|
||||
--disable-thread --enable-ares=$ARESPATH --disable-ipv6
|
||||
--enable-ares=$ARESPATH --with-openssl
|
||||
cp -R $ARESPATH/lib ares
|
||||
make && make install
|
||||
|
||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = "UnrealIRCd"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 5.0.9
|
||||
PROJECT_NUMBER = 5.2.0.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
BIN
extras/libsodium.tar.gz
Normal file
BIN
extras/libsodium.tar.gz
Normal file
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
/************************************************************************
|
||||
* Unreal Internet Relay Chat Daemon, include/dynconf.h
|
||||
* Copyright (C) 1999 Carsten Munk
|
||||
* Copyright (C) 1999-2003 Carsten Munk
|
||||
* Copyright (C) 2003-2021 Bram Matthys
|
||||
*
|
||||
* 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
|
||||
@ -22,6 +23,15 @@
|
||||
|
||||
#define DYNCONF_H
|
||||
|
||||
typedef struct FloodSettings FloodSettings;
|
||||
|
||||
struct FloodSettings {
|
||||
FloodSettings *prev, *next;
|
||||
char *name;
|
||||
int limit[MAXFLOODOPTIONS];
|
||||
long period[MAXFLOODOPTIONS];
|
||||
};
|
||||
|
||||
typedef struct NetworkConfiguration NetworkConfiguration;
|
||||
struct NetworkConfiguration {
|
||||
unsigned x_inah:1;
|
||||
@ -118,16 +128,7 @@ struct Configuration {
|
||||
int handshake_data_flood_ban_action;
|
||||
struct ChMode modes_on_join;
|
||||
int level_on_join;
|
||||
unsigned char away_count;
|
||||
long away_period;
|
||||
unsigned char nick_count;
|
||||
long nick_period;
|
||||
unsigned char invite_count;
|
||||
long invite_period;
|
||||
unsigned char knock_count;
|
||||
long knock_period;
|
||||
unsigned char max_concurrent_conversations_users;
|
||||
unsigned char max_concurrent_conversations_new_user_every;
|
||||
FloodSettings *floodsettings;
|
||||
int ident_connect_timeout;
|
||||
int ident_read_timeout;
|
||||
long default_bantime;
|
||||
@ -234,15 +235,6 @@ extern MODVAR int ipv6_disabled;
|
||||
#define MODES_ON_JOIN iConf.modes_on_join.mode
|
||||
#define LEVEL_ON_JOIN iConf.level_on_join
|
||||
|
||||
#define AWAY_PERIOD iConf.away_period
|
||||
#define AWAY_COUNT iConf.away_count
|
||||
#define NICK_PERIOD iConf.nick_period
|
||||
#define NICK_COUNT iConf.nick_count
|
||||
#define KNOCK_PERIOD iConf.knock_period
|
||||
#define KNOCK_COUNT iConf.knock_count
|
||||
#define INVITE_PERIOD iConf.invite_period
|
||||
#define INVITE_COUNT iConf.invite_count
|
||||
|
||||
#define IDENT_CONNECT_TIMEOUT iConf.ident_connect_timeout
|
||||
#define IDENT_READ_TIMEOUT iConf.ident_read_timeout
|
||||
|
||||
@ -325,17 +317,8 @@ struct SetCheck {
|
||||
unsigned has_restrict_channelmodes:1;
|
||||
unsigned has_restrict_extendedbans:1;
|
||||
unsigned has_channel_command_prefix:1;
|
||||
unsigned has_anti_flood_handshake_data_flood_amount:1;
|
||||
unsigned has_anti_flood_handshake_data_flood_ban_action:1;
|
||||
unsigned has_anti_flood_handshake_data_flood_ban_time:1;
|
||||
unsigned has_modes_on_join:1;
|
||||
unsigned has_level_on_join:1;
|
||||
unsigned has_anti_flood_away_count:1;
|
||||
unsigned has_anti_flood_away_period:1;
|
||||
unsigned has_anti_flood_nick_flood:1;
|
||||
unsigned has_anti_flood_connect_flood:1;
|
||||
unsigned has_anti_flood_invite_flood:1;
|
||||
unsigned has_anti_flood_knock_flood:1;
|
||||
unsigned has_ident_connect_timeout:1;
|
||||
unsigned has_ident_read_timeout:1;
|
||||
unsigned has_default_bantime:1;
|
||||
|
74
include/h.h
74
include/h.h
@ -285,6 +285,7 @@ extern char *myctime(time_t);
|
||||
extern char *short_date(time_t, char *buf);
|
||||
extern char *long_date(time_t);
|
||||
extern void exit_client(Client *client, MessageTag *recv_mtags, char *comment);
|
||||
extern void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment);
|
||||
extern void initstats(), tstats(Client *, char *);
|
||||
extern char *check_string(char *);
|
||||
extern char *make_nick_user_host(char *, char *, char *);
|
||||
@ -312,7 +313,7 @@ extern int find_str_match_link(Link *, char *);
|
||||
extern void free_str_list(Link *);
|
||||
extern Link *make_link();
|
||||
extern Ban *make_ban();
|
||||
extern ClientUser *make_user(Client *);
|
||||
extern User *make_user(Client *);
|
||||
extern Server *make_server();
|
||||
extern Client *make_client(Client *, Client *);
|
||||
extern Member *find_channel_link(Member *, Channel *);
|
||||
@ -447,6 +448,7 @@ extern void Auth_FreeAuthConfig(AuthConfig *as);
|
||||
extern int Auth_Check(Client *cptr, AuthConfig *as, char *para);
|
||||
extern char *Auth_Hash(int type, char *para);
|
||||
extern int Auth_CheckError(ConfigEntry *ce);
|
||||
extern int Auth_AutoDetectHashType(char *hash);
|
||||
|
||||
extern void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen);
|
||||
extern int channel_canjoin(Client *client, char *name);
|
||||
@ -545,9 +547,31 @@ extern void *safe_alloc(size_t size);
|
||||
*/
|
||||
#define raw_strldup(str, max) our_strldup(str, max)
|
||||
|
||||
extern void *safe_alloc_sensitive(size_t size);
|
||||
|
||||
/** Free previously allocate memory pointer - this is the sensitive version which
|
||||
* may ONLY be called on allocations returned by safe_alloc_sensitive() / safe_strdup_sensitive().
|
||||
* This will set the memory to all zeroes before actually deallocating.
|
||||
* It also sets the pointer to NULL, since that would otherwise be common to forget.
|
||||
* @note If you call this function on normally allocated memory (non-sensitive) then we will crash.
|
||||
*/
|
||||
#define safe_free_sensitive(x) do { if (x) sodium_free(x); x = NULL; } while(0)
|
||||
|
||||
/** Free previous memory (if any) and then save a duplicate of the specified string -
|
||||
* This is the 'sensitive' version which should only be used for HIGHLY sensitive data,
|
||||
* as it wastes about 8000 bytes even if you only duplicate a string of 32 bytes (this is by design).
|
||||
* @param dst The current pointer and the pointer where a new copy of the string will be stored.
|
||||
* @param str The string you want to copy
|
||||
*/
|
||||
#define safe_strdup_sensitive(dst,str) do { if (dst) sodium_free(dst); if (!(str)) dst = NULL; else dst = our_strdup_sensitive(str); } while(0)
|
||||
|
||||
/** Safely destroy a string in memory (but do not free!) */
|
||||
#define destroy_string(str) sodium_memzero(str, strlen(str))
|
||||
|
||||
/** @} */
|
||||
extern char *our_strdup(const char *str);
|
||||
extern char *our_strldup(const char *str, size_t max);
|
||||
extern char *our_strdup_sensitive(const char *str);
|
||||
|
||||
extern long config_checkval(char *value, unsigned short flags);
|
||||
extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2)));
|
||||
@ -701,7 +725,8 @@ extern MODVAR void (*tkl_stats)(Client *cptr, int type, char *para, int *cnt);
|
||||
extern MODVAR void (*tkl_sync)(Client *client);
|
||||
extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, char *parv[]);
|
||||
extern MODVAR int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration);
|
||||
extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk);
|
||||
extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
|
||||
extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd);
|
||||
extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type);
|
||||
extern MODVAR unsigned char *(*StripColors)(unsigned char *text);
|
||||
extern MODVAR const char *(*StripControlCodes)(unsigned char *text);
|
||||
@ -787,6 +812,7 @@ extern char *clean_ban_mask(char *, int, Client *);
|
||||
extern int find_invex(Channel *channel, Client *client);
|
||||
extern void DoMD5(char *mdout, const char *src, unsigned long n);
|
||||
extern char *md5hash(char *dst, const char *src, unsigned long n);
|
||||
extern char *sha256hash(char *dst, const char *src, unsigned long n);
|
||||
extern MODVAR TKL *tklines[TKLISTLEN];
|
||||
extern MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2];
|
||||
extern char *cmdname_by_spamftarget(int target);
|
||||
@ -944,8 +970,12 @@ extern void free_message_tags(MessageTag *m);
|
||||
extern time_t server_time_to_unix_time(const char *tbuf);
|
||||
extern int history_set_limit(char *object, int max_lines, long max_t);
|
||||
extern int history_add(char *object, MessageTag *mtags, char *line);
|
||||
extern int history_request(Client *acptr, char *object, HistoryFilter *filter);
|
||||
extern HistoryResult *history_request(char *object, HistoryFilter *filter);
|
||||
extern int history_destroy(char *object);
|
||||
extern int can_receive_history(Client *client);
|
||||
extern void history_send_result(Client *client, HistoryResult *r);
|
||||
extern void free_history_result(HistoryResult *r);
|
||||
extern void free_history_filter(HistoryFilter *f);
|
||||
extern void special_delayed_unloading(void);
|
||||
extern int write_int64(FILE *fd, uint64_t t);
|
||||
extern int write_int32(FILE *fd, uint32_t t);
|
||||
@ -1004,3 +1034,41 @@ extern NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name);
|
||||
extern void free_nvplist(NameValuePrioList *lst);
|
||||
extern char *get_connect_extinfo(Client *client);
|
||||
extern char *unreal_strftime(char *str);
|
||||
extern void strtolower_safe(char *dst, char *src, int size);
|
||||
extern int running_interactively(void);
|
||||
extern void skip_whitespace(char **p);
|
||||
extern void read_until(char **p, char *stopchars);
|
||||
/* src/unrealdb.c start */
|
||||
extern UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_block);
|
||||
extern int unrealdb_close(UnrealDB *c);
|
||||
extern char *unrealdb_test_db(const char *filename, char *secret_block);
|
||||
extern int unrealdb_write_int64(UnrealDB *c, uint64_t t);
|
||||
extern int unrealdb_write_int32(UnrealDB *c, uint32_t t);
|
||||
extern int unrealdb_write_int16(UnrealDB *c, uint16_t t);
|
||||
extern int unrealdb_write_str(UnrealDB *c, char *x);
|
||||
extern int unrealdb_write_char(UnrealDB *c, char t);
|
||||
extern int unrealdb_read_int64(UnrealDB *c, uint64_t *t);
|
||||
extern int unrealdb_read_int32(UnrealDB *c, uint32_t *t);
|
||||
extern int unrealdb_read_int16(UnrealDB *c, uint16_t *t);
|
||||
extern int unrealdb_read_str(UnrealDB *c, char **x);
|
||||
extern int unrealdb_read_char(UnrealDB *c, char *t);
|
||||
extern char *unrealdb_test_secret(char *name);
|
||||
extern UnrealDBConfig *unrealdb_copy_config(UnrealDBConfig *src);
|
||||
extern UnrealDBConfig *unrealdb_get_config(UnrealDB *db);
|
||||
extern void unrealdb_free_config(UnrealDBConfig *c);
|
||||
extern UnrealDBError unrealdb_get_error_code(void);
|
||||
extern char *unrealdb_get_error_string(void);
|
||||
/* src/unrealdb.c end */
|
||||
/* secret { } related stuff */
|
||||
extern Secret *find_secret(char *secret_name);
|
||||
extern void free_secret_cache(SecretCache *c);
|
||||
extern void free_secret(Secret *s);
|
||||
extern Secret *secrets;
|
||||
/* end */
|
||||
extern int check_password_strength(char *pass, int min_length, int strict, char **err);
|
||||
extern int valid_secret_password(char *pass, char **err);
|
||||
extern int flood_limit_exceeded(Client *client, FloodOption opt);
|
||||
extern FloodSettings *find_floodsettings_block(const char *name);
|
||||
extern FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt);
|
||||
extern MODVAR char *floodoption_names[];
|
||||
extern void flood_limit_exceeded_log(Client *client, char *floodname);
|
||||
|
@ -241,14 +241,15 @@ typedef struct {
|
||||
*
|
||||
* This function pointer is NULL (unused) for modes without parameters.
|
||||
* @param para The input parameter.
|
||||
* @param client The client that the mode request came from (can be NULL)
|
||||
* @param client The client that the mode request came from (can be NULL!)
|
||||
* @param channel The channel that the mode request came from (can be NULL!)
|
||||
* @returns pointer to output string (temporary storage)
|
||||
* @note The 'client' field will be NULL if for example called for set::modes-on-join.
|
||||
* @note You should probably not use 'client' in most cases.
|
||||
* @note You should probably not use 'client' or 'channel' in most cases.
|
||||
* In particular you MUST NOT SEND ERRORS to the client.
|
||||
* This should be done in is_ok() and not in conv_param().
|
||||
*/
|
||||
char *(*conv_param)(char *para, Client *client);
|
||||
char *(*conv_param)(char *para, Client *client, Channel *channel);
|
||||
|
||||
/** Free and remove parameter from list.
|
||||
* This function pointer is NULL (unused) for modes without parameters.
|
||||
@ -309,7 +310,7 @@ typedef struct {
|
||||
int (*is_ok)(Client *,Channel *, char mode, char *para, int, int);
|
||||
void * (*put_param)(void *, char *);
|
||||
char * (*get_param)(void *);
|
||||
char * (*conv_param)(char *, Client *);
|
||||
char * (*conv_param)(char *, Client *, Channel *);
|
||||
void (*free_param)(void *);
|
||||
void * (*dup_struct)(void *);
|
||||
int (*sjoin_check)(Channel *, void *, void *);
|
||||
@ -486,11 +487,43 @@ typedef struct {
|
||||
|
||||
/** @} */
|
||||
|
||||
/** Filter for history: the command / type of the request */
|
||||
typedef enum HistoryFilterCommand {
|
||||
HFC_SIMPLE=1, /**< Simple history request for lines / unixtime */
|
||||
HFC_BEFORE=2, /**< CHATHISTORY BEFORE */
|
||||
HFC_AFTER=3, /**< CHATHISTORY AFTER */
|
||||
HFC_LATEST=4, /**< CHATHISTORY LATEST */
|
||||
HFC_AROUND=5, /**< CHATHISTORY AROUND */
|
||||
HFC_BETWEEN=6 /**< CHATHISTORY BETWEEN */
|
||||
} HistoryFilterCommand;
|
||||
|
||||
/** Filter for history get requests */
|
||||
typedef struct HistoryFilter HistoryFilter;
|
||||
struct HistoryFilter {
|
||||
int last_lines;
|
||||
int last_seconds;
|
||||
HistoryFilterCommand cmd; /**< Filter command, one of HistoryFilterCommand */
|
||||
int last_lines; /**< Used by HFC_SIMPLE */
|
||||
int last_seconds; /**< Used by HFC_SIMPLE */
|
||||
char *timestamp_a; /**< First parameter of HFC_* (either this or msgid_a) */
|
||||
char *msgid_a; /**< First parameter of HFC_* (either this or timestamp_a) */
|
||||
char *timestamp_b; /**< Second parameter of HFC_BETWEEN (either this or msgid_b) */
|
||||
char *msgid_b; /**< Second parameter of HFC_BETWEEN (either this or timestamp_b) */
|
||||
int limit; /**< Maximum number of lines to return */
|
||||
};
|
||||
|
||||
/** History log lines, used by HistoryResult among others */
|
||||
typedef struct HistoryLogLine HistoryLogLine;
|
||||
struct HistoryLogLine {
|
||||
HistoryLogLine *prev, *next;
|
||||
time_t t;
|
||||
MessageTag *mtags;
|
||||
char line[1];
|
||||
};
|
||||
|
||||
typedef struct HistoryResult HistoryResult;
|
||||
struct HistoryResult {
|
||||
char *object; /**< Name of the history object, eg '#test' */
|
||||
HistoryLogLine *log; /**< The resulting log lines */
|
||||
HistoryLogLine *log_tail; /**< Last entry in the log lines */
|
||||
};
|
||||
|
||||
/** History Backend */
|
||||
@ -500,7 +533,7 @@ struct HistoryBackend {
|
||||
char *name; /**< The name of the history backend (eg: "mem") */
|
||||
int (*history_set_limit)(char *object, int max_lines, long max_time); /**< Impose a limit on a history object */
|
||||
int (*history_add)(char *object, MessageTag *mtags, char *line); /**< Add to history */
|
||||
int (*history_request)(Client *acptr, char *object, HistoryFilter *filter); /**< Request history */
|
||||
HistoryResult *(*history_request)(char *object, HistoryFilter *filter); /**< Request history */
|
||||
int (*history_destroy)(char *object); /**< Destroy history of this object completely */
|
||||
Module *owner; /**< Module introducing this */
|
||||
char unloaded; /**< Internal flag to indicate module is being unloaded */
|
||||
@ -513,7 +546,7 @@ typedef struct {
|
||||
char *name;
|
||||
int (*history_set_limit)(char *object, int max_lines, long max_time);
|
||||
int (*history_add)(char *object, MessageTag *mtags, char *line);
|
||||
int (*history_request)(Client *acptr, char *object, HistoryFilter *filter);
|
||||
HistoryResult *(*history_request)(char *object, HistoryFilter *filter);
|
||||
int (*history_destroy)(char *object);
|
||||
} HistoryBackendInfo;
|
||||
|
||||
@ -614,11 +647,12 @@ typedef struct ModuleObject {
|
||||
extern unsigned int ModuleGetError(Module *module);
|
||||
extern const char *ModuleGetErrorStr(Module *module);
|
||||
extern unsigned int ModuleGetOptions(Module *module);
|
||||
extern unsigned int ModuleSetOptions(Module *module, unsigned int options, int action);
|
||||
extern void ModuleSetOptions(Module *module, unsigned int options, int action);
|
||||
|
||||
struct Module
|
||||
{
|
||||
struct Module *prev, *next;
|
||||
int priority;
|
||||
ModuleHeader *header; /* The module's header */
|
||||
#ifdef _WIN32
|
||||
HMODULE dll; /* Return value of LoadLibrary */
|
||||
@ -644,6 +678,7 @@ struct Module
|
||||
#define MOD_OPT_OFFICIAL 0x0002 /* Official module, do not set "tainted" */
|
||||
#define MOD_OPT_PERM_RELOADABLE 0x0004 /* Module is semi-permanent: it can be re-loaded but not un-loaded */
|
||||
#define MOD_OPT_GLOBAL 0x0008 /* Module is required to be loaded globally (i.e. across the entire network) */
|
||||
#define MOD_OPT_UNLOAD_PRIORITY 0x1000 /* Module wants a higher or lower unload priority */
|
||||
#define MOD_Dep(name, container,module) {#name, (vFP *) &container, module}
|
||||
|
||||
/** Event structs */
|
||||
@ -1223,17 +1258,19 @@ 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 mtags Message tags associated with the event
|
||||
* @param newnick The new nick name
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_local_nickchange(Client *client, char *newnick);
|
||||
int hooktype_local_nickchange(Client *client, MessageTag *mtags, char *newnick);
|
||||
|
||||
/** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE).
|
||||
* @param client The client
|
||||
* @param mtags Message tags associated with the event
|
||||
* @param newnick The new nick name
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_remote_nickchange(Client *client, char *newnick);
|
||||
int hooktype_remote_nickchange(Client *client, MessageTag *mtags, 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
|
||||
@ -2222,8 +2259,9 @@ enum EfunctionType {
|
||||
EFUNC_TKL_SYNCH,
|
||||
EFUNC_CMD_TKL,
|
||||
EFUNC_PLACE_HOST_BAN,
|
||||
EFUNC_DOSPAMFILTER,
|
||||
EFUNC_DOSPAMFILTER_VIRUSCHAN,
|
||||
EFUNC_MATCH_SPAMFILTER,
|
||||
EFUNC_MATCH_SPAMFILTER_MTAGS,
|
||||
EFUNC_JOIN_VIRUSCHAN,
|
||||
EFUNC_FIND_TKLINE_MATCH_ZAP_EX,
|
||||
EFUNC_SEND_LIST,
|
||||
EFUNC_STRIPCOLORS,
|
||||
@ -2308,6 +2346,7 @@ enum EfunctionType {
|
||||
#define CONFIG_REQUIRE 9
|
||||
#define CONFIG_LISTEN 10
|
||||
#define CONFIG_LISTEN_OPTIONS 11
|
||||
#define CONFIG_SET_HISTORY_CHANNEL 12
|
||||
|
||||
#define MOD_HEADER Mod_Header
|
||||
#define MOD_TEST() DLLFUNC int Mod_Test(ModuleInfo *modinfo)
|
||||
|
@ -142,6 +142,12 @@
|
||||
/* Define the location of the modules */
|
||||
#undef MODULESDIR
|
||||
|
||||
/* machine is bigendian */
|
||||
#undef NATIVE_BIG_ENDIAN
|
||||
|
||||
/* machine is littleendian */
|
||||
#undef NATIVE_LITTLE_ENDIAN
|
||||
|
||||
/* Set to the nickname history length you want */
|
||||
#undef NICKNAMEHISTORYLENGTH
|
||||
|
||||
@ -226,3 +232,15 @@
|
||||
/* Define if you have libcurl installed to get remote includes and MOTD
|
||||
support */
|
||||
#undef USE_LIBCURL
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
# undef WORDS_BIGENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
143
include/struct.h
143
include/struct.h
@ -109,6 +109,7 @@ 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 Secret Secret;
|
||||
typedef struct ListStruct ListStruct;
|
||||
typedef struct ListStructPrio ListStructPrio;
|
||||
|
||||
@ -120,7 +121,7 @@ typedef struct Watch Watch;
|
||||
typedef struct Client Client;
|
||||
typedef struct LocalClient LocalClient;
|
||||
typedef struct Channel Channel;
|
||||
typedef struct User ClientUser;
|
||||
typedef struct User User;
|
||||
typedef struct Server Server;
|
||||
typedef struct Link Link;
|
||||
typedef struct Ban Ban;
|
||||
@ -211,7 +212,7 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia
|
||||
#define LOG_CHGCMDS 0x0100
|
||||
#define LOG_OVERRIDE 0x0200
|
||||
#define LOG_SPAMFILTER 0x0400
|
||||
#define LOG_DBG 0x0800 /* fixme */
|
||||
#define LOG_FLOOD 0x0800
|
||||
|
||||
/*
|
||||
** 'offsetof' is defined in ANSI-C. The following definition
|
||||
@ -240,6 +241,14 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia
|
||||
*/
|
||||
#define SIPHASH_KEY_LENGTH 16
|
||||
|
||||
/** The length of a standard 'msgid' tag (note that special
|
||||
* msgid tags will be longer).
|
||||
* The 22 alphanumeric characters provide slightly more
|
||||
* than 128 bits of randomness (62^22 > 2^128).
|
||||
* See mtag_add_or_inherit_msgid() for more information.
|
||||
*/
|
||||
#define MSGIDLEN 22
|
||||
|
||||
/** This specifies the current client status or the client type - see @link ClientStatus @endlink in particular.
|
||||
* You may think "server" or "client" are the only choices here, but there are many more
|
||||
* such as states where the user is in the middle of an SSL/TLS handshake.
|
||||
@ -661,6 +670,7 @@ struct ListStructPrio {
|
||||
CHECK_PRIO_LIST_ENTRY(list) \
|
||||
CHECK_PRIO_LIST_ENTRY(item) \
|
||||
CHECK_NULL_LIST_ITEM(item) \
|
||||
item->priority = prio; \
|
||||
add_ListItemPrio((ListStructPrio *)item, (ListStructPrio **)&list, prio); \
|
||||
} while(0)
|
||||
|
||||
@ -754,6 +764,7 @@ struct LoopStruct {
|
||||
unsigned do_bancheck_spamf_user : 1; /* perform 'user' spamfilter bancheck */
|
||||
unsigned do_bancheck_spamf_away : 1; /* perform 'away' spamfilter bancheck */
|
||||
unsigned ircd_rehashing : 1;
|
||||
unsigned ircd_terminating : 1;
|
||||
unsigned tainted : 1;
|
||||
Client *rehash_save_cptr, *rehash_save_client;
|
||||
int rehash_save_sig;
|
||||
@ -862,6 +873,97 @@ typedef void (*CmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[
|
||||
typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd);
|
||||
typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]);
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
/* This is the 'chunk size', the size of encryption blocks.
|
||||
* We choose 4K here since that is a decent amount as of 2021 and
|
||||
* more would not benefit performance anyway.
|
||||
* Note that you cannot change this value easily afterwards
|
||||
* (you cannot read files with a different chunk size).
|
||||
*/
|
||||
#define UNREALDB_CRYPT_FILE_CHUNK_SIZE 4096
|
||||
|
||||
/** The salt length. Don't change. */
|
||||
#define UNREALDB_SALT_LEN 16
|
||||
|
||||
/** Database modes of operation (read or write)
|
||||
* @ingroup UnrealDBFunctions
|
||||
*/
|
||||
typedef enum UnrealDBMode {
|
||||
UNREALDB_MODE_READ = 0,
|
||||
UNREALDB_MODE_WRITE = 1
|
||||
} UnrealDBMode;
|
||||
|
||||
typedef enum UnrealDBCipher {
|
||||
UNREALDB_CIPHER_XCHACHA20 = 0x0001
|
||||
} UnrealDBCipher;
|
||||
|
||||
typedef enum UnrealDBKDF {
|
||||
UNREALDB_KDF_ARGON2ID = 0x0001
|
||||
} UnrealDBKDF;
|
||||
|
||||
/** Database configuration for a particular file */
|
||||
typedef struct UnrealDBConfig {
|
||||
uint16_t kdf; /**< Key derivation function (always 0x01) */
|
||||
uint16_t t_cost; /**< Time cost (number of rounds) */
|
||||
uint16_t m_cost; /**< Memory cost (in number of bitshifts, eg 15 means 1<<15=32M) */
|
||||
uint16_t p_cost; /**< Parallel cost (number of concurrent threads) */
|
||||
uint16_t saltlen; /**< Length of the salt (normally UNREALDB_SALT_LEN) */
|
||||
char *salt; /**< Salt */
|
||||
uint16_t cipher; /**< Encryption cipher (always 0x01) */
|
||||
uint16_t keylen; /**< Key length */
|
||||
char *key; /**< The key used for encryption/decryption */
|
||||
} UnrealDBConfig;
|
||||
|
||||
/** Error codes returned by @ref UnrealDBFunctions
|
||||
* @ingroup UnrealDBFunctions
|
||||
*/
|
||||
typedef enum UnrealDBError {
|
||||
UNREALDB_ERROR_SUCCESS = 0, /**< Success, not an error */
|
||||
UNREALDB_ERROR_FILENOTFOUND = 1, /**< File does not exist */
|
||||
UNREALDB_ERROR_CRYPTED = 2, /**< File is crypted but no password provided */
|
||||
UNREALDB_ERROR_NOTCRYPTED = 3, /**< File is not crypted and a password was provided */
|
||||
UNREALDB_ERROR_HEADER = 4, /**< Header is corrupt, invalid or unknown format */
|
||||
UNREALDB_ERROR_SECRET = 5, /**< Invalid secret { } block provided - either does not exist or does not meet requirements */
|
||||
UNREALDB_ERROR_PASSWORD = 6, /**< Invalid password provided */
|
||||
UNREALDB_ERROR_IO = 7, /**< I/O error */
|
||||
UNREALDB_ERROR_API = 8, /**< API call violation, eg requesting to write on a file opened for reading */
|
||||
UNREALDB_ERROR_INTERNAL = 9, /**< Internal error, eg crypto routine returned something unexpected */
|
||||
} UnrealDBError;
|
||||
|
||||
/** Database handle
|
||||
* This is returned by unrealdb_open() and used by all other @ref UnrealDBFunctions
|
||||
* @ingroup UnrealDBFunctions
|
||||
*/
|
||||
typedef struct UnrealDB {
|
||||
FILE *fd; /**< File descriptor */
|
||||
UnrealDBMode mode; /**< UNREALDB_MODE_READ / UNREALDB_MODE_WRITE */
|
||||
int crypted; /**< Are we doing any encryption or just plaintext? */
|
||||
uint64_t creationtime; /**< When this file was created/updates */
|
||||
crypto_secretstream_xchacha20poly1305_state st; /**< Internal state for crypto engine */
|
||||
char buf[UNREALDB_CRYPT_FILE_CHUNK_SIZE]; /**< Buffer used for reading/writing */
|
||||
int buflen; /**< Length of current data in buffer */
|
||||
UnrealDBError error_code; /**< Last error code. Whenever this happens we will set this, never overwrite, and block further I/O */
|
||||
char *error_string; /**< Error string upon failure */
|
||||
UnrealDBConfig *config; /**< Config */
|
||||
} UnrealDB;
|
||||
|
||||
/** Used for speeding up reading/writing of DBs (so we don't have to run argon2 repeatedly) */
|
||||
typedef struct SecretCache SecretCache;
|
||||
struct SecretCache {
|
||||
SecretCache *prev, *next;
|
||||
UnrealDBConfig *config;
|
||||
time_t cache_hit;
|
||||
};
|
||||
|
||||
/** Used for storing secret { } blocks */
|
||||
struct Secret {
|
||||
Secret *prev, *next;
|
||||
char *name;
|
||||
char *password;
|
||||
SecretCache *cache;
|
||||
};
|
||||
|
||||
|
||||
/* tkl:
|
||||
* TKL_KILL|TKL_GLOBAL = Global K-Line (GLINE)
|
||||
@ -898,12 +1000,13 @@ typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag
|
||||
#define SPAMF_USERMSG 0x0002 /* p */
|
||||
#define SPAMF_USERNOTICE 0x0004 /* n */
|
||||
#define SPAMF_CHANNOTICE 0x0008 /* N */
|
||||
#define SPAMF_PART 0x0010 /* P */
|
||||
#define SPAMF_QUIT 0x0020 /* q */
|
||||
#define SPAMF_DCC 0x0040 /* d */
|
||||
#define SPAMF_USER 0x0080 /* u */
|
||||
#define SPAMF_AWAY 0x0100 /* a */
|
||||
#define SPAMF_TOPIC 0x0200 /* t */
|
||||
#define SPAMF_PART 0x0010 /* P */
|
||||
#define SPAMF_QUIT 0x0020 /* q */
|
||||
#define SPAMF_DCC 0x0040 /* d */
|
||||
#define SPAMF_USER 0x0080 /* u */
|
||||
#define SPAMF_AWAY 0x0100 /* a */
|
||||
#define SPAMF_TOPIC 0x0200 /* t */
|
||||
#define SPAMF_MTAG 0x0400 /* m */
|
||||
|
||||
/* Other flags only for function calls: */
|
||||
#define SPAMFLAG_NOWARN 0x0001
|
||||
@ -1107,6 +1210,25 @@ extern void unload_all_unused_moddata(void);
|
||||
#define TLSFLAG_NOSTARTTLS 0x8
|
||||
#define TLSFLAG_DISABLECLIENTCERT 0x10
|
||||
|
||||
/** Flood counters for local clients */
|
||||
typedef struct FloodCounter {
|
||||
int count;
|
||||
long t;
|
||||
} FloodCounter;
|
||||
|
||||
/** This is the list of different flood counters that we keep for local clients. */
|
||||
/* IMPORTANT: If you change this, update floodoption_names[] in src/user.c too !!!!!!!!!!!! */
|
||||
typedef enum FloodOption {
|
||||
FLD_NICK = 0, /**< nick-flood */
|
||||
FLD_JOIN = 1, /**< join-flood */
|
||||
FLD_AWAY = 2, /**< away-flood */
|
||||
FLD_INVITE = 3, /**< invite-flood */
|
||||
FLD_KNOCK = 4, /**< knock-flood */
|
||||
FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */
|
||||
} FloodOption;
|
||||
#define MAXFLOODOPTIONS 10
|
||||
|
||||
|
||||
/** This shows the Client struct (any client), the User struct (a user), Server (a server) that are commonly accessed both in the core and by 3rd party coders.
|
||||
* @defgroup CommonStructs Common structs
|
||||
* @{
|
||||
@ -1119,7 +1241,7 @@ struct Client {
|
||||
struct list_head lclient_node; /**< For local client list (lclient_list) */
|
||||
struct list_head special_node; /**< For special lists (server || unknown || oper) */
|
||||
LocalClient *local; /**< Additional information regarding locally connected clients */
|
||||
ClientUser *user; /**< Additional information, if this client is a user */
|
||||
User *user; /**< Additional information, if this client is a user */
|
||||
Server *serv; /**< Additional information, if this is a server */
|
||||
ClientStatus status; /**< Client status, one of CLIENT_STATUS_* */
|
||||
struct list_head client_hash; /**< For name hash table (clientTable) */
|
||||
@ -1186,6 +1308,7 @@ struct LocalClient {
|
||||
struct hostent *hostp; /**< Host record for this client (used by DNS code) */
|
||||
char sockhost[HOSTLEN + 1]; /**< Hostname from the socket */
|
||||
u_short port; /**< Remote TCP port of client */
|
||||
FloodCounter flood[MAXFLOODOPTIONS];
|
||||
};
|
||||
|
||||
/** User information (persons, not servers), you use client->user to access these (see also @link Client @endlink).
|
||||
@ -1208,11 +1331,9 @@ struct User {
|
||||
char *operlogin; /**< Which oper { } block was used to oper up, otherwise NULL - used by oper::maxlogins */
|
||||
struct {
|
||||
time_t nick_t; /**< For set::anti-flood::nick-flood: time */
|
||||
time_t away_t; /**< For set::anti-flood::away-flood: time */
|
||||
time_t knock_t; /**< For set::anti-flood::knock-flood: time */
|
||||
time_t invite_t; /**< For set::anti-flood::invite-flood: time */
|
||||
unsigned char nick_c; /**< For set::anti-flood::nick-flood: counter */
|
||||
unsigned char away_c; /**< For set::anti-flood::away-flood: counter */
|
||||
unsigned char knock_c; /**< For set::anti-flood::knock-flood: counter */
|
||||
unsigned char invite_c; /**< For set::anti-flood::invite-flood: counter */
|
||||
} flood; /**< Anti-flood counters */
|
||||
|
@ -224,4 +224,15 @@ extern char OSName[256];
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NATIVE_BIG_ENDIAN
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
#include <sys/endian.h>
|
||||
#define bswap_64 bswap64
|
||||
#define bswap_32 bswap32
|
||||
#define bswap_16 bswap16
|
||||
#else
|
||||
#include <byteswap.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* __sys_include__ */
|
||||
|
@ -26,6 +26,7 @@
|
||||
#else
|
||||
#include <sys/resource.h>
|
||||
#include <utime.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@ -35,3 +36,4 @@
|
||||
#ifdef USE_LIBCURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
#include <argon2.h>
|
||||
|
@ -54,7 +54,7 @@
|
||||
* Can be useful if the above 3 versionids are insufficient for you (eg: you want to support CVS).
|
||||
* This is updated automatically on the CVS server every Monday. so don't touch it.
|
||||
*/
|
||||
#define UNREAL_VERSION_TIME 202001
|
||||
#define UNREAL_VERSION_TIME 202120
|
||||
|
||||
#define UnrealProtocol 5002
|
||||
#define PATCH1 macro_to_str(UNREAL_VERSION_GENERATION)
|
||||
|
@ -60,13 +60,13 @@
|
||||
#define UNREAL_VERSION_GENERATION 5
|
||||
|
||||
/* Major version number (e.g.: 2 for Unreal3.2*) */
|
||||
#define UNREAL_VERSION_MAJOR 0
|
||||
#define UNREAL_VERSION_MAJOR 2
|
||||
|
||||
/* Minor version number (e.g.: 1 for Unreal3.2.1) */
|
||||
#define UNREAL_VERSION_MINOR 9
|
||||
#define UNREAL_VERSION_MINOR 0
|
||||
|
||||
/* Version suffix such as a beta marker or release candidate marker. (e.g.:
|
||||
-rcX for unrealircd-3.2.9-rcX) */
|
||||
#define UNREAL_VERSION_SUFFIX ""
|
||||
#define UNREAL_VERSION_SUFFIX ".1"
|
||||
|
||||
#endif
|
||||
|
@ -31,7 +31,7 @@ OBJS=dns.o auth.o channel.o crule.o dbuf.o \
|
||||
api-moddata.o api-extban.o api-isupport.o api-command.o \
|
||||
api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \
|
||||
api-event.o \
|
||||
crypt_blowfish.o updconf.o crashreport.o modulemanager.o \
|
||||
crypt_blowfish.o unrealdb.o updconf.o crashreport.o modulemanager.o \
|
||||
utf8.o \
|
||||
openssl_hostname_validation.o $(URL)
|
||||
|
||||
@ -224,6 +224,9 @@ api-efunctions.o: api-efunctions.c $(INCLUDES)
|
||||
crypt_blowfish.o: crypt_blowfish.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c crypt_blowfish.c
|
||||
|
||||
unrealdb.o: unrealdb.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c unrealdb.c
|
||||
|
||||
updconf.o: updconf.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(BINCFLAGS) -c updconf.c
|
||||
|
||||
|
@ -66,7 +66,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if (SERVICES_NAME && (acptr = find_person(alias->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
|
||||
return;
|
||||
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
|
||||
alias->nick, SERVICES_NAME, parv[1]);
|
||||
@ -78,7 +78,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if (STATS_SERVER && (acptr = find_person(alias->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
|
||||
return;
|
||||
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
|
||||
alias->nick, STATS_SERVER, parv[1]);
|
||||
@ -90,7 +90,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if ((acptr = find_person(alias->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
|
||||
return;
|
||||
if (MyUser(acptr))
|
||||
sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name,
|
||||
@ -112,7 +112,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
char *errmsg = NULL;
|
||||
if (can_send_to_channel(client, channel, &msg, &errmsg, 0))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, channel->chname, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, cmd, channel->chname, 0, NULL))
|
||||
return;
|
||||
new_message(client, NULL, &mtags);
|
||||
sendto_channel(channel, client, client->direction,
|
||||
@ -203,7 +203,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if (SERVICES_NAME && (acptr = find_person(format->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
|
||||
return;
|
||||
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
|
||||
format->nick, SERVICES_NAME, output);
|
||||
@ -214,7 +214,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if (STATS_SERVER && (acptr = find_person(format->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
|
||||
return;
|
||||
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
|
||||
format->nick, STATS_SERVER, output);
|
||||
@ -225,7 +225,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
{
|
||||
if ((acptr = find_person(format->nick, NULL)))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
|
||||
return;
|
||||
if (MyUser(acptr))
|
||||
sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name,
|
||||
@ -247,7 +247,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
|
||||
char *errmsg = NULL;
|
||||
if (!can_send_to_channel(client, channel, &msg, &errmsg, 0))
|
||||
{
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, channel->chname, 0, NULL))
|
||||
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, cmd, channel->chname, 0, NULL))
|
||||
return;
|
||||
new_message(client, NULL, &mtags);
|
||||
sendto_channel(channel, client, client->direction,
|
||||
|
@ -67,7 +67,8 @@ 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);
|
||||
int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk);
|
||||
int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
|
||||
int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd);
|
||||
int (*join_viruschan)(Client *client, TKL *tk, int type);
|
||||
unsigned char *(*StripColors)(unsigned char *text);
|
||||
const char *(*StripControlCodes)(unsigned char *text);
|
||||
@ -306,8 +307,9 @@ void efunctions_init(void)
|
||||
efunc_init_function(EFUNC_TKL_SYNCH, tkl_sync, NULL);
|
||||
efunc_init_function(EFUNC_CMD_TKL, cmd_tkl, NULL);
|
||||
efunc_init_function(EFUNC_PLACE_HOST_BAN, place_host_ban, NULL);
|
||||
efunc_init_function(EFUNC_DOSPAMFILTER, match_spamfilter, NULL);
|
||||
efunc_init_function(EFUNC_DOSPAMFILTER_VIRUSCHAN, join_viruschan, NULL);
|
||||
efunc_init_function(EFUNC_MATCH_SPAMFILTER, match_spamfilter, NULL);
|
||||
efunc_init_function(EFUNC_MATCH_SPAMFILTER_MTAGS, match_spamfilter_mtags, NULL);
|
||||
efunc_init_function(EFUNC_JOIN_VIRUSCHAN, join_viruschan, NULL);
|
||||
efunc_init_function(EFUNC_STRIPCOLORS, StripColors, NULL);
|
||||
efunc_init_function(EFUNC_STRIPCONTROLCODES, StripControlCodes, NULL);
|
||||
efunc_init_function(EFUNC_SPAMFILTER_BUILD_USER_STRING, spamfilter_build_user_string, NULL);
|
||||
|
@ -28,6 +28,7 @@ ID_Copyright("(C) Carsten Munk 2001");
|
||||
MODVAR Event *events = NULL;
|
||||
|
||||
extern EVENT(unrealdns_removeoldrecords);
|
||||
extern EVENT(unrealdb_expire_secret_cache);
|
||||
|
||||
/** Add an event, a function that will run at regular intervals.
|
||||
* @param module Module that this event belongs to
|
||||
@ -241,4 +242,5 @@ void SetupEvents(void)
|
||||
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);
|
||||
EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0);
|
||||
}
|
||||
|
@ -166,14 +166,21 @@ int history_add(char *object, MessageTag *mtags, char *line)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int history_request(Client *client, char *object, HistoryFilter *filter)
|
||||
HistoryResult *history_request(char *object, HistoryFilter *filter)
|
||||
{
|
||||
HistoryBackend *hb;
|
||||
HistoryBackend *hb = historybackends;
|
||||
HistoryResult *r;
|
||||
HistoryLogLine *l;
|
||||
|
||||
for (hb = historybackends; hb; hb=hb->next)
|
||||
hb->history_request(client, object, filter);
|
||||
if (!hb)
|
||||
return 0; /* no history backend loaded */
|
||||
|
||||
return 1;
|
||||
/* Right now we return whenever the first backend has a result. */
|
||||
for (hb = historybackends; hb; hb = hb->next)
|
||||
if ((r = hb->history_request(object, filter)))
|
||||
return r;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int history_destroy(char *object)
|
||||
@ -195,3 +202,83 @@ int history_set_limit(char *object, int max_lines, long max_t)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Free a HistoryResult object that was returned from request_result() earlier */
|
||||
void free_history_result(HistoryResult *r)
|
||||
{
|
||||
HistoryLogLine *l, *l_next;
|
||||
for (l = r->log; l; l = l_next)
|
||||
{
|
||||
l_next = l->next;
|
||||
free_message_tags(l->mtags);
|
||||
safe_free(l);
|
||||
}
|
||||
safe_free(r->object);
|
||||
safe_free(r);
|
||||
}
|
||||
|
||||
/** Returns 1 if the client can receive channel history, 0 if not.
|
||||
* @param client The client to check.
|
||||
* @note It is recommend to call this function BEFORE trying to
|
||||
* retrieve channel history via history_request(),
|
||||
* as to not waste useless resources.
|
||||
*/
|
||||
int can_receive_history(Client *client)
|
||||
{
|
||||
if (HasCapability(client, "server-time"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void history_send_result_line(Client *client, HistoryLogLine *l, char *batchid)
|
||||
{
|
||||
if (BadPtr(batchid))
|
||||
{
|
||||
sendto_one(client, l->mtags, "%s", l->line);
|
||||
} else {
|
||||
MessageTag *m = safe_alloc(sizeof(MessageTag));
|
||||
m->name = "batch";
|
||||
m->value = batchid;
|
||||
AddListItem(m, l->mtags);
|
||||
sendto_one(client, l->mtags, "%s", l->line);
|
||||
DelListItem(m, l->mtags);
|
||||
safe_free(m);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send the result of a history_request() to the client.
|
||||
* @param client The client to send to.
|
||||
* @param r The history result retrieved via history_request().
|
||||
*/
|
||||
void history_send_result(Client *client, HistoryResult *r)
|
||||
{
|
||||
char batch[BATCHLEN+1];
|
||||
HistoryLogLine *l;
|
||||
|
||||
if (!can_receive_history(client))
|
||||
return;
|
||||
|
||||
batch[0] = '\0';
|
||||
if (HasCapability(client, "batch"))
|
||||
{
|
||||
/* Start a new batch */
|
||||
generate_batch_id(batch);
|
||||
sendto_one(client, NULL, ":%s BATCH +%s chathistory %s", me.name, batch, r->object);
|
||||
}
|
||||
|
||||
for (l = r->log; l; l = l->next)
|
||||
history_send_result_line(client, l, batch);
|
||||
|
||||
/* End of batch */
|
||||
if (*batch)
|
||||
sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
|
||||
}
|
||||
|
||||
void free_history_filter(HistoryFilter *f)
|
||||
{
|
||||
safe_free(f->timestamp_a);
|
||||
safe_free(f->msgid_a);
|
||||
safe_free(f->timestamp_b);
|
||||
safe_free(f->msgid_b);
|
||||
safe_free(f);
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "unrealircd.h"
|
||||
#include "crypt_blowfish.h"
|
||||
#include <argon2.h>
|
||||
|
||||
typedef struct AuthTypeList AuthTypeList;
|
||||
struct AuthTypeList {
|
||||
|
1154
src/conf.c
1154
src/conf.c
File diff suppressed because it is too large
Load Diff
@ -12,16 +12,6 @@ extern ConfigFile *conf;
|
||||
|
||||
NameValueList *config_defines = NULL; /**< List of @defines, only valid during configuration reading */
|
||||
|
||||
void skip_whitespace(char **p)
|
||||
{
|
||||
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
|
||||
}
|
||||
|
||||
void read_until(char **p, char *stopchars)
|
||||
{
|
||||
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
|
||||
}
|
||||
|
||||
static inline int ValidVarCharacter(char x)
|
||||
{
|
||||
if (isupper(x) || isdigit(x) || strchr("_", x))
|
||||
|
@ -4,9 +4,7 @@
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
#ifndef _WIN32
|
||||
#include <dirent.h>
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
extern void StartUnrealAgain(void);
|
||||
#endif
|
||||
#include "version.h"
|
||||
@ -527,24 +525,6 @@ char *generate_crash_report(char *coredump, int *thirdpartymods)
|
||||
return reportfname;
|
||||
}
|
||||
|
||||
int running_interactive(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
char *s;
|
||||
|
||||
if (!isatty(0))
|
||||
return 0;
|
||||
|
||||
s = getenv("TERM");
|
||||
if (!s || !strcasecmp(s, "dumb") || !strcasecmp(s, "none"))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return IsService ? 0 : 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define REPORT_NEVER -1
|
||||
#define REPORT_ASK 0
|
||||
#define REPORT_AUTO 1
|
||||
@ -728,13 +708,41 @@ void report_crash_not_sent(char *fname)
|
||||
" (if you do, please set the option 'View Status' at the end of the bug report page to 'private'!!)\n", fname);
|
||||
}
|
||||
|
||||
/** This checks if there are indications that 3rd party modules are
|
||||
* loaded. This is used to provide a small warning to the user that
|
||||
* the crash may be likely due to that.
|
||||
*/
|
||||
int check_third_party_mods_present(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct dirent *dir;
|
||||
DIR *fd = opendir(TMPDIR);
|
||||
|
||||
if (!fd)
|
||||
return 0;
|
||||
|
||||
/* We search for files like tmp/FC5C3116.third.somename.so */
|
||||
while ((dir = readdir(fd)))
|
||||
{
|
||||
char *fname = dir->d_name;
|
||||
if (strstr(fname, ".third.") && strstr(fname, ".so"))
|
||||
{
|
||||
closedir(fd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
closedir(fd);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void report_crash(void)
|
||||
{
|
||||
char *coredump, *fname;
|
||||
int thirdpartymods = 0;
|
||||
int crashed_secs_ago;
|
||||
|
||||
if (!running_interactive() && (report_pref != REPORT_AUTO))
|
||||
if (!running_interactively() && (report_pref != REPORT_AUTO))
|
||||
exit(0); /* don't bother if we run through cron or something similar */
|
||||
|
||||
coredump = find_best_coredump();
|
||||
@ -750,6 +758,8 @@ void report_crash(void)
|
||||
if (!fname)
|
||||
return;
|
||||
|
||||
if (thirdpartymods == 0)
|
||||
thirdpartymods = check_third_party_mods_present();
|
||||
#ifndef _WIN32
|
||||
printf("The IRCd has been started now (and is running), but it did crash %d seconds ago.\n", crashed_secs_ago);
|
||||
printf("Crash report generated in: %s\n\n", fname);
|
||||
@ -761,10 +771,10 @@ void report_crash(void)
|
||||
"by someone other than the UnrealIRCd team). If you installed new 3rd party\n"
|
||||
"module(s) in the past few weeks we suggest to unload these modules and see if\n"
|
||||
"the crash issue dissapears. If so, that module is probably to blame.\n"
|
||||
"If you keep crashing without 3rd party modules then please do report it to\n"
|
||||
"the UnrealIRCd team.\n"
|
||||
"The reason we ask you to do this is because more than 95%% of the crash issues\n"
|
||||
"reported nowadays are caused by 3rd party modules and not by an UnrealIRCd bug.\n"
|
||||
"If you keep crashing without any 3rd party modules loaded then please do report\n"
|
||||
"it to the UnrealIRCd team.\n"
|
||||
"The reason we ask you to do this is because MORE THAN 95%% OF ALL CRASH ISSUES\n"
|
||||
"ARE CAUSED BY 3RD PARTY MODULES and not by an UnrealIRCd bug.\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
@ -778,7 +788,9 @@ void report_crash(void)
|
||||
char answerbuf[64], *answer;
|
||||
printf("Shall I send a crash report to the UnrealIRCd developers?\n");
|
||||
if (!thirdpartymods)
|
||||
printf("Crash reports help us greatly with fixing bugs that affect you and others\n");
|
||||
printf("Crash reports help us greatly with fixing bugs that affect you and others\n");
|
||||
else
|
||||
printf("NOTE: If the crash is caused by a 3rd party module then UnrealIRCd devs can't fix that.\n");
|
||||
printf("\n");
|
||||
|
||||
do
|
||||
@ -805,7 +817,7 @@ void report_crash(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (running_interactive())
|
||||
if (running_interactively())
|
||||
{
|
||||
char buf[8192], *line;
|
||||
|
||||
|
@ -465,7 +465,7 @@ Client *hash_find_nickatserver(const char *str, Client *def)
|
||||
if (serv)
|
||||
*serv++ = '\0';
|
||||
|
||||
client = find_client(nick, NULL);
|
||||
client = find_person(nick, NULL);
|
||||
if (!client)
|
||||
return NULL; /* client not found */
|
||||
|
||||
|
100
src/ircd.c
100
src/ircd.c
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
#include <ares.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
char *malloc_options = "h" MALLOC_FLAGS_EXTRA;
|
||||
@ -60,6 +61,7 @@ static void open_debugfile(), setup_signals();
|
||||
extern void init_glines(void);
|
||||
extern void tkl_init(void);
|
||||
extern void process_clients(void);
|
||||
extern void unrealdb_test(void);
|
||||
|
||||
#ifndef _WIN32
|
||||
MODVAR char **myargv;
|
||||
@ -80,6 +82,7 @@ void s_die()
|
||||
Client *client;
|
||||
if (!IsService)
|
||||
{
|
||||
loop.ircd_terminating = 1;
|
||||
unload_all_modules();
|
||||
|
||||
list_for_each_entry(client, &lclient_list, lclient_node)
|
||||
@ -94,6 +97,7 @@ void s_die()
|
||||
ControlService(hService, SERVICE_CONTROL_STOP, &status);
|
||||
}
|
||||
#else
|
||||
loop.ircd_terminating = 1;
|
||||
unload_all_modules();
|
||||
unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE);
|
||||
exit(0);
|
||||
@ -366,7 +370,7 @@ int match_tkls(Client *client)
|
||||
|
||||
if (loop.do_bancheck_spamf_away && IsUser(client) &&
|
||||
client->user->away != NULL &&
|
||||
match_spamfilter(client, client->user->away, SPAMF_AWAY, NULL, SPAMFLAG_NOWARN, NULL))
|
||||
match_spamfilter(client, client->user->away, SPAMF_AWAY, "AWAY", NULL, SPAMFLAG_NOWARN, NULL))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -573,84 +577,6 @@ char buf[1024];
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Ugly version checker that ensures ssl/curl runtime libraries match the
|
||||
* version we compiled for.
|
||||
*/
|
||||
static void do_version_check()
|
||||
{
|
||||
const char *compiledfor, *runtime;
|
||||
int error = 0;
|
||||
char *p;
|
||||
|
||||
/* OPENSSL:
|
||||
* Nowadays (since openssl 1.0.0) they retain binary compatibility
|
||||
* when the first two version numbers are the same: eg 1.0.0 and 1.0.2
|
||||
*/
|
||||
compiledfor = OPENSSL_VERSION_TEXT;
|
||||
runtime = SSLeay_version(SSLEAY_VERSION);
|
||||
p = strchr(compiledfor, '.');
|
||||
if (p)
|
||||
{
|
||||
p = strchr(p+1, '.');
|
||||
if (p)
|
||||
{
|
||||
int versionlen = p - compiledfor + 1;
|
||||
|
||||
if (strncasecmp(compiledfor, runtime, versionlen))
|
||||
{
|
||||
version_check_logerror("OpenSSL version mismatch: compiled for '%s', library is '%s'",
|
||||
compiledfor, runtime);
|
||||
error=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_LIBCURL
|
||||
/* Perhaps someone should tell them to do this a bit more easy ;)
|
||||
* problem is runtime output is like: 'libcurl/7.11.1 c-ares/1.2.0'
|
||||
* while header output is like: '7.11.1'.
|
||||
*/
|
||||
{
|
||||
char buf[128], *p;
|
||||
|
||||
runtime = curl_version();
|
||||
compiledfor = LIBCURL_VERSION;
|
||||
if (!strncmp(runtime, "libcurl/", 8))
|
||||
{
|
||||
strlcpy(buf, runtime+8, sizeof(buf));
|
||||
p = strchr(buf, ' ');
|
||||
if (p)
|
||||
{
|
||||
*p = '\0';
|
||||
if (strcmp(compiledfor, buf))
|
||||
{
|
||||
version_check_logerror("Curl version mismatch: compiled for '%s', library is '%s'",
|
||||
compiledfor, buf);
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (error)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
|
||||
"Make sure you don't have multiple versions of openssl installed (eg: "
|
||||
"one in /usr and one in /usr/local). And, if you recently upgraded them, "
|
||||
"be sure to recompile UnrealIRCd.");
|
||||
#else
|
||||
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
|
||||
"This should never happen with official Windows builds... unless "
|
||||
"you overwrote any .dll files with newer/older ones or something.");
|
||||
win_error();
|
||||
#endif
|
||||
tainted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
extern void applymeblock(void);
|
||||
|
||||
extern MODVAR Event *events;
|
||||
@ -927,6 +853,11 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
safe_strdup(configfile, CONFIGFILE);
|
||||
|
||||
init_random(); /* needs to be done very early!! */
|
||||
if (sodium_init() < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize sodium library -- error accessing random device?\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(&botmotd, '\0', sizeof(MOTDFile));
|
||||
memset(&rules, '\0', sizeof(MOTDFile));
|
||||
@ -1070,9 +1001,10 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
#if 1
|
||||
case 'S':
|
||||
charsys_dump_table(p ? p : "*");
|
||||
//charsys_dump_table(p ? p : "*");
|
||||
unrealdb_test();
|
||||
exit(0);
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
@ -1165,8 +1097,6 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
do_version_check();
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#ifndef _WIN32
|
||||
mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */
|
||||
@ -1215,11 +1145,13 @@ int InitUnrealIRCd(int argc, char *argv[])
|
||||
fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop), Gottem and i\n\n");
|
||||
|
||||
fprintf(stderr, "Using the following libraries:\n");
|
||||
fprintf(stderr, "* %s\n", pcre2_version());
|
||||
fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION));
|
||||
fprintf(stderr, "* libsodium %s\n", sodium_version_string());
|
||||
#ifdef USE_LIBCURL
|
||||
fprintf(stderr, "* %s\n", curl_version());
|
||||
#endif
|
||||
fprintf(stderr, "* c-ares %s\n", ares_version(NULL));
|
||||
fprintf(stderr, "* %s\n", pcre2_version());
|
||||
#endif
|
||||
check_user_limit();
|
||||
#ifndef _WIN32
|
||||
|
20
src/list.c
20
src/list.c
@ -76,7 +76,7 @@ void initlists(void)
|
||||
|
||||
client_pool = mp_pool_new(sizeof(Client), 512 * 1024);
|
||||
local_client_pool = mp_pool_new(sizeof(LocalClient), 512 * 1024);
|
||||
user_pool = mp_pool_new(sizeof(ClientUser), 512 * 1024);
|
||||
user_pool = mp_pool_new(sizeof(User), 512 * 1024);
|
||||
link_pool = mp_pool_new(sizeof(Link), 512 * 1024);
|
||||
}
|
||||
|
||||
@ -184,30 +184,20 @@ void free_client(Client *client)
|
||||
** 'make_user' add's an User information block to a client
|
||||
** if it was not previously allocated.
|
||||
*/
|
||||
ClientUser *make_user(Client *client)
|
||||
User *make_user(Client *client)
|
||||
{
|
||||
ClientUser *user;
|
||||
User *user;
|
||||
|
||||
user = client->user;
|
||||
if (!user)
|
||||
{
|
||||
user = mp_pool_get(user_pool);
|
||||
memset(user, 0, sizeof(ClientUser));
|
||||
memset(user, 0, sizeof(User));
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
users.inuse++;
|
||||
#endif
|
||||
user->swhois = NULL;
|
||||
user->away = NULL;
|
||||
user->flood.away_t = 0;
|
||||
user->flood.away_c = 0;
|
||||
user->joined = 0;
|
||||
user->channel = NULL;
|
||||
user->invited = NULL;
|
||||
user->server = NULL;
|
||||
strlcpy(user->svid, "0", sizeof(user->svid));
|
||||
user->whowas = NULL;
|
||||
user->snomask = 0;
|
||||
if (client->ip)
|
||||
{
|
||||
/* initially set client->user->realhost to IP */
|
||||
@ -450,6 +440,8 @@ void del_ListItem(ListStruct *item, ListStruct **list)
|
||||
item->next->prev = item->prev;
|
||||
if (*list == item)
|
||||
*list = item->next; /* new head */
|
||||
/* And update 'item', prev/next should point nowhere anymore */
|
||||
item->prev = item->next = NULL;
|
||||
}
|
||||
|
||||
/** Add item to list with a 'priority'.
|
||||
|
@ -506,11 +506,6 @@ mp_pool_new(size_t item_size, size_t chunk_capacity)
|
||||
pool->next = mp_allocated_pools;
|
||||
mp_allocated_pools = pool;
|
||||
|
||||
ircd_log(LOG_DBG, "Capacity is %lu, item size is %lu, alloc size is %lu",
|
||||
(unsigned long)pool->new_chunk_capacity,
|
||||
(unsigned long)pool->item_alloc_size,
|
||||
(unsigned long)(pool->new_chunk_capacity*pool->item_alloc_size));
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
@ -701,19 +696,12 @@ mp_pool_log_status(mp_pool_t *pool)
|
||||
for (chunk = pool->empty_chunks; chunk; chunk = chunk->next)
|
||||
bytes_allocated += chunk->mem_size;
|
||||
|
||||
ircd_log(LOG_DBG, "%llu bytes in %d empty chunks",
|
||||
bytes_allocated, pool->n_empty_chunks);
|
||||
for (chunk = pool->used_chunks; chunk; chunk = chunk->next) {
|
||||
++n_used;
|
||||
bu += chunk->n_allocated * pool->item_alloc_size;
|
||||
ba += chunk->mem_size;
|
||||
|
||||
ircd_log(LOG_DBG, " used chunk: %d items allocated",
|
||||
chunk->n_allocated);
|
||||
}
|
||||
|
||||
ircd_log(LOG_DBG, "%llu/%llu bytes in %d partially full chunks",
|
||||
bu, ba, n_used);
|
||||
bytes_used += bu;
|
||||
bytes_allocated += ba;
|
||||
bu = ba = 0;
|
||||
@ -724,22 +712,7 @@ mp_pool_log_status(mp_pool_t *pool)
|
||||
ba += chunk->mem_size;
|
||||
}
|
||||
|
||||
ircd_log(LOG_DBG, "%llu/%llu bytes in %d full chunks",
|
||||
bu, ba, n_full);
|
||||
bytes_used += bu;
|
||||
bytes_allocated += ba;
|
||||
|
||||
ircd_log(LOG_DBG, "Total: %llu/%llu bytes allocated "
|
||||
"for cell pools are full.",
|
||||
bytes_used, bytes_allocated);
|
||||
|
||||
#ifdef MEMPOOL_STATS
|
||||
ircd_log(LOG_DBG, "%llu cell allocations ever; "
|
||||
"%llu chunk allocations ever; "
|
||||
"%llu chunk frees ever.",
|
||||
(long long)pool->total_items_allocated,
|
||||
(long long)pool->total_chunks_allocated,
|
||||
(long long)pool->total_chunks_freed);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
177
src/misc.c
177
src/misc.c
@ -81,16 +81,17 @@ typedef struct {
|
||||
} SpamfilterTargetTable;
|
||||
|
||||
SpamfilterTargetTable spamfiltertargettable[] = {
|
||||
{ SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" },
|
||||
{ SPAMF_USERMSG, 'p', "private", "PRIVMSG" },
|
||||
{ SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" },
|
||||
{ SPAMF_USERMSG, 'p', "private", "PRIVMSG" },
|
||||
{ SPAMF_USERNOTICE, 'n', "private-notice", "NOTICE" },
|
||||
{ SPAMF_CHANNOTICE, 'N', "channel-notice", "NOTICE" },
|
||||
{ SPAMF_PART, 'P', "part", "PART" },
|
||||
{ SPAMF_QUIT, 'q', "quit", "QUIT" },
|
||||
{ SPAMF_DCC, 'd', "dcc", "PRIVMSG" },
|
||||
{ SPAMF_USER, 'u', "user", "NICK" },
|
||||
{ SPAMF_AWAY, 'a', "away", "AWAY" },
|
||||
{ SPAMF_TOPIC, 't', "topic", "TOPIC" },
|
||||
{ SPAMF_PART, 'P', "part", "PART" },
|
||||
{ SPAMF_QUIT, 'q', "quit", "QUIT" },
|
||||
{ SPAMF_DCC, 'd', "dcc", "PRIVMSG" },
|
||||
{ SPAMF_USER, 'u', "user", "NICK" },
|
||||
{ SPAMF_AWAY, 'a', "away", "AWAY" },
|
||||
{ SPAMF_TOPIC, 't', "topic", "TOPIC" },
|
||||
{ SPAMF_MTAG, 'T', "message-tag", "message-tag" },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@ -583,7 +584,7 @@ static void recurse_send_quits(Client *cptr, Client *client, Client *from, Clien
|
||||
recurse_send_quits(cptr, acptr, from, to, mtags, comment, splitstr);
|
||||
}
|
||||
|
||||
if (cptr == client && to != from)
|
||||
if (cptr == client && to != from && !(to->direction && (to->direction == from)))
|
||||
sendto_one(to, mtags, "SQUIT %s :%s", client->name, comment);
|
||||
}
|
||||
|
||||
@ -699,6 +700,16 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com
|
||||
* @param comment The (s)quit message
|
||||
*/
|
||||
void exit_client(Client *client, MessageTag *recv_mtags, char *comment)
|
||||
{
|
||||
exit_client_ex(client, client->direction, recv_mtags, comment);
|
||||
}
|
||||
|
||||
/** Exit this IRC client, and all the dependents (users, servers) if this is a server.
|
||||
* @param client The client to exit.
|
||||
* @param recv_mtags Message tags to use as a base (if any).
|
||||
* @param comment The (s)quit message
|
||||
*/
|
||||
void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment)
|
||||
{
|
||||
long long on_for;
|
||||
ConfigItem_listen *listen_conf;
|
||||
@ -812,7 +823,7 @@ void exit_client(Client *client, MessageTag *recv_mtags, char *comment)
|
||||
else
|
||||
ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->srvptr->name, client->name);
|
||||
|
||||
remove_dependents(client, client->direction, recv_mtags, comment, splitstr);
|
||||
remove_dependents(client, origin, recv_mtags, comment, splitstr);
|
||||
|
||||
RunHook2(HOOKTYPE_SERVER_QUIT, client, recv_mtags);
|
||||
}
|
||||
@ -1187,6 +1198,7 @@ char *our_strcasestr(char *haystack, char *needle)
|
||||
* @param tag A tag used internally and for server-to-server traffic,
|
||||
* not visible to end-users.
|
||||
* @param priority Priority - for ordering multiple swhois entries
|
||||
* (lower number = further up in the swhoises list in WHOIS)
|
||||
* @param swhois The actual special whois title (string) you want to add to the user
|
||||
* @param from Who added this entry
|
||||
* @param skip Which server(-side) to skip broadcasting this entry to.
|
||||
@ -1785,6 +1797,23 @@ int read_str(FILE *fd, char **x)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Convert binary 'data' of size 'len' to a hexadecimal string 'str'.
|
||||
* The caller is responsible to ensure that 'str' is sufficiently large.
|
||||
*/
|
||||
void binarytohex(void *data, size_t len, char *str)
|
||||
{
|
||||
const char hexchars[16] = "0123456789abcdef";
|
||||
char *datastr = (char *)data;
|
||||
int i, n = 0;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
{
|
||||
str[n++] = hexchars[(datastr[i] >> 4) & 0xF];
|
||||
str[n++] = hexchars[datastr[i] & 0xF];
|
||||
}
|
||||
str[n] = '\0';
|
||||
}
|
||||
|
||||
/** Generates an MD5 checksum.
|
||||
* @param mdout[out] Buffer to store result in, the result will be 16 bytes in binary
|
||||
* (not ascii printable!).
|
||||
@ -1809,31 +1838,27 @@ void DoMD5(char *mdout, const char *src, unsigned long n)
|
||||
char *md5hash(char *dst, const char *src, unsigned long n)
|
||||
{
|
||||
char tmp[16];
|
||||
SHA256_CTX hash;
|
||||
|
||||
DoMD5(tmp, src, n);
|
||||
sprintf(dst, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8],
|
||||
tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15]);
|
||||
|
||||
binarytohex(tmp, sizeof(tmp), dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** Convert binary 'data' of size 'len' to a hexadecimal string 'str'.
|
||||
* The caller is responsible to ensure that 'str' is sufficiently large.
|
||||
/** Generates a SHA256 checksum - ASCII printable string (0011223344..etc..).
|
||||
* @param dst[out] Buffer to store result in, which needs to be 65 bytes minimum.
|
||||
* @param src[in] The input data used to generate the checksum.
|
||||
* @param n[in] Length of data.
|
||||
*/
|
||||
void binarytohex(void *data, size_t len, char *str)
|
||||
char *sha256hash(char *dst, const char *src, unsigned long n)
|
||||
{
|
||||
const char hexchars[16] = "0123456789abcdef";
|
||||
char *datastr = (char *)data;
|
||||
int i, n = 0;
|
||||
SHA256_CTX hash;
|
||||
char binaryhash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
{
|
||||
str[n++] = hexchars[(datastr[i] >> 4) & 0xF];
|
||||
str[n++] = hexchars[datastr[i] & 0xF];
|
||||
}
|
||||
str[n] = '\0';
|
||||
SHA256_Init(&hash);
|
||||
SHA256_Update(&hash, src, n);
|
||||
SHA256_Final(binaryhash, &hash);
|
||||
binarytohex(binaryhash, sizeof(binaryhash), dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** Calculate the SHA256 checksum of a file */
|
||||
@ -1973,3 +1998,101 @@ char *sendtype_to_cmd(SendType sendtype)
|
||||
return "TAGMSG";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Check password strength.
|
||||
* @param pass The password to check
|
||||
* @param min_length The minimum length of the password
|
||||
* @param strict Whether to require UPPER+lower+digits
|
||||
* @returns 1 if good, 0 if not.
|
||||
*/
|
||||
int check_password_strength(char *pass, int min_length, int strict, char **err)
|
||||
{
|
||||
char has_lowercase=0, has_uppercase=0, has_digit=0;
|
||||
char *p;
|
||||
static char buf[256];
|
||||
|
||||
if (err)
|
||||
*err = NULL;
|
||||
|
||||
if (strlen(pass) < min_length)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Password must be at least %d characters", min_length);
|
||||
*err = buf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (p=pass; *p; p++)
|
||||
{
|
||||
if (islower(*p))
|
||||
has_lowercase = 1;
|
||||
else if (isupper(*p))
|
||||
has_uppercase = 1;
|
||||
else if (isdigit(*p))
|
||||
has_digit = 1;
|
||||
}
|
||||
|
||||
if (strict)
|
||||
{
|
||||
if (!has_lowercase)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 lowercase character";
|
||||
return 0;
|
||||
} else
|
||||
if (!has_uppercase)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 UPPERcase character";
|
||||
return 0;
|
||||
} else
|
||||
if (!has_digit)
|
||||
{
|
||||
if (err)
|
||||
*err = "Password must contain at least 1 digit (number)";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int valid_secret_password(char *pass, char **err)
|
||||
{
|
||||
return check_password_strength(pass, 10, 1, err);
|
||||
}
|
||||
|
||||
int running_interactively(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
char *s;
|
||||
|
||||
if (!isatty(0))
|
||||
return 0;
|
||||
|
||||
s = getenv("TERM");
|
||||
if (!s || !strcasecmp(s, "dumb") || !strcasecmp(s, "none"))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return IsService ? 0 : 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Skip whitespace (if any) */
|
||||
void skip_whitespace(char **p)
|
||||
{
|
||||
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
|
||||
}
|
||||
|
||||
/** Keep reading '*p' until we hit any of the 'stopchars'.
|
||||
* Actually behaves like strstr() but then hit the end
|
||||
* of the string (\0) i guess?
|
||||
*/
|
||||
void read_until(char **p, char *stopchars)
|
||||
{
|
||||
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "unrealircd.h"
|
||||
#ifndef _WIN32
|
||||
#include <dirent.h>
|
||||
|
||||
#define MODULEMANAGER_CONNECT_TIMEOUT 7
|
||||
#define MODULEMANAGER_READ_TIMEOUT 20
|
||||
|
@ -30,9 +30,6 @@
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#ifndef RTLD_NOW
|
||||
#define RTLD_NOW RTLD_LAZY
|
||||
#endif
|
||||
@ -444,7 +441,7 @@ char *Module_Create(char *path_)
|
||||
}
|
||||
}
|
||||
mod->flags = MODFLAG_TESTING;
|
||||
AddListItem(mod, Modules);
|
||||
AddListItemPrio(mod, Modules, 0);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@ -1187,6 +1184,9 @@ void unload_all_modules(void)
|
||||
int (*Mod_Unload)();
|
||||
for (m = Modules; m; m = m->next)
|
||||
{
|
||||
#ifdef DEBUGMODE
|
||||
ircd_log(LOG_ERROR, "Unloading %s...", m->header->name);
|
||||
#endif
|
||||
irc_dlsym(m->dll, "Mod_Unload", Mod_Unload);
|
||||
if (Mod_Unload)
|
||||
(*Mod_Unload)(&m->modinfo);
|
||||
@ -1194,15 +1194,21 @@ void unload_all_modules(void)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ModuleSetOptions(Module *module, unsigned int options, int action)
|
||||
void ModuleSetOptions(Module *module, unsigned int options, int action)
|
||||
{
|
||||
unsigned int oldopts = module->options;
|
||||
|
||||
if (action)
|
||||
module->options |= options;
|
||||
else
|
||||
module->options &= ~options;
|
||||
return oldopts;
|
||||
if (options == MOD_OPT_UNLOAD_PRIORITY)
|
||||
{
|
||||
DelListItem(module, Modules);
|
||||
AddListItemPrio(module, Modules, action);
|
||||
} else {
|
||||
/* Simple bit flag(s) */
|
||||
if (action)
|
||||
module->options |= options;
|
||||
else
|
||||
module->options &= ~options;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ModuleGetOptions(Module *module)
|
||||
|
@ -71,8 +71,9 @@ R_MODULES= \
|
||||
account-tag.so labeled-response.so link-security.so \
|
||||
message-ids.so plaintext-policy.so server-time.so sts.so \
|
||||
echo-message.so userip-tag.so userhost-tag.so \
|
||||
typing-indicator.so \
|
||||
ident_lookup.so history.so \
|
||||
bot-tag.so \
|
||||
reply-tag.so typing-indicator.so \
|
||||
ident_lookup.so history.so chathistory.so \
|
||||
targetfloodprot.so clienttagdeny.so
|
||||
|
||||
MODULES=cloak.so $(R_MODULES)
|
||||
@ -612,6 +613,14 @@ userhost-tag.so: userhost-tag.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o userhost-tag.so userhost-tag.c
|
||||
|
||||
bot-tag.so: bot-tag.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o bot-tag.so bot-tag.c
|
||||
|
||||
reply-tag.so: reply-tag.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o reply-tag.so reply-tag.c
|
||||
|
||||
typing-indicator.so: typing-indicator.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o typing-indicator.so typing-indicator.c
|
||||
@ -632,6 +641,10 @@ history.so: history.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o history.so history.c
|
||||
|
||||
chathistory.so: chathistory.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o chathistory.so chathistory.c
|
||||
|
||||
targetfloodprot.so: targetfloodprot.c $(INCLUDES)
|
||||
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
|
||||
-o targetfloodprot.so targetfloodprot.c
|
||||
|
@ -858,20 +858,6 @@ static int internal_getscore(char *str)
|
||||
return score;
|
||||
}
|
||||
|
||||
void strtolower_safe(char *dst, char *src, int size)
|
||||
{
|
||||
if (!size)
|
||||
return; /* size of 0 is unworkable */
|
||||
size--; /* for \0 */
|
||||
|
||||
for (; *src && size; src++)
|
||||
{
|
||||
*dst++ = tolower(*src);
|
||||
size--;
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
/** Returns "spam score".
|
||||
* @note a user is expected, do not call for anything else (eg: servers)
|
||||
*/
|
||||
|
@ -451,7 +451,7 @@ int authprompt_sasl_continuation(Client *client, char *buf)
|
||||
sendto_one(agent, NULL, ":%s SASL %s %s C %s",
|
||||
me.id, AGENT_SID(agent), client->id, SEUSER(client)->authmsg);
|
||||
}
|
||||
SEUSER(client)->authmsg = NULL;
|
||||
safe_free(SEUSER(client)->authmsg);
|
||||
}
|
||||
return 1; /* inhibit displaying of message */
|
||||
}
|
||||
|
@ -86,24 +86,16 @@ CMD_FUNC(cmd_away)
|
||||
}
|
||||
|
||||
/* Check spamfilters */
|
||||
if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, NULL, 0, NULL))
|
||||
if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, "AWAY", NULL, 0, NULL))
|
||||
return;
|
||||
|
||||
/* Check set::anti-flood::away-flood */
|
||||
if (MyUser(client) && AWAY_PERIOD && !ValidatePermissionsForPath("immune:away-flood",client,NULL,NULL,NULL))
|
||||
/* Check away-flood */
|
||||
if (MyUser(client) &&
|
||||
!ValidatePermissionsForPath("immune:away-flood",client,NULL,NULL,NULL) &&
|
||||
flood_limit_exceeded(client, FLD_AWAY))
|
||||
{
|
||||
if ((client->user->flood.away_t + AWAY_PERIOD) <= timeofday)
|
||||
{
|
||||
client->user->flood.away_c = 0;
|
||||
client->user->flood.away_t = timeofday;
|
||||
}
|
||||
if (client->user->flood.away_c <= AWAY_COUNT)
|
||||
client->user->flood.away_c++;
|
||||
if (client->user->flood.away_c > AWAY_COUNT)
|
||||
{
|
||||
sendnumeric(client, ERR_TOOMANYAWAY);
|
||||
return;
|
||||
}
|
||||
sendnumeric(client, ERR_TOOMANYAWAY);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Obey set::away-length */
|
||||
|
90
src/modules/bot-tag.c
Normal file
90
src/modules/bot-tag.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* IRC - Internet Relay Chat, src/modules/bot-tag.c
|
||||
* (C) 2021 Syzop & The UnrealIRCd Team
|
||||
*
|
||||
* See file AUTHORS in IRC package for additional names of
|
||||
* the programmers.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* This implements the message tag that is mentioned in
|
||||
* https://ircv3.net/specs/extensions/bot-mode
|
||||
* The B mode and 005 is in the modules/usermodes/bot module.
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"bot-tag",
|
||||
"5.0",
|
||||
"bot message tag",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
int bottag_mtag_is_ok(Client *client, char *name, char *value);
|
||||
void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature);
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MessageTagHandlerInfo mtag;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
memset(&mtag, 0, sizeof(mtag));
|
||||
mtag.name = "draft/bot";
|
||||
mtag.is_ok = bottag_mtag_is_ok;
|
||||
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
|
||||
MessageTagHandlerAdd(modinfo->handle, &mtag);
|
||||
|
||||
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_bottag);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
/** This function verifies if the client sending the mtag is permitted to do so.
|
||||
*/
|
||||
int bottag_mtag_is_ok(Client *client, char *name, char *value)
|
||||
{
|
||||
if (IsServer(client) && (value == NULL))
|
||||
return 1; /* OK */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature)
|
||||
{
|
||||
MessageTag *m;
|
||||
|
||||
if (IsUser(client) && has_user_mode(client, 'B'))
|
||||
{
|
||||
MessageTag *m = safe_alloc(sizeof(MessageTag));
|
||||
safe_strdup(m->name, "draft/bot");
|
||||
m->value = NULL;
|
||||
AddListItem(m, *mtag_list);
|
||||
}
|
||||
}
|
@ -28,13 +28,13 @@ ModuleHeader MOD_HEADER
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
#define FLD_CTCP 0 /* c */
|
||||
#define FLD_JOIN 1 /* j */
|
||||
#define FLD_KNOCK 2 /* k */
|
||||
#define FLD_MSG 3 /* m */
|
||||
#define FLD_NICK 4 /* n */
|
||||
#define FLD_TEXT 5 /* t */
|
||||
#define FLD_REPEAT 6 /* r */
|
||||
#define CHFLD_CTCP 0 /* c */
|
||||
#define CHFLD_JOIN 1 /* j */
|
||||
#define CHFLD_KNOCK 2 /* k */
|
||||
#define CHFLD_MSG 3 /* m */
|
||||
#define CHFLD_NICK 4 /* n */
|
||||
#define CHFLD_TEXT 5 /* t */
|
||||
#define CHFLD_REPEAT 6 /* r */
|
||||
|
||||
#define NUMFLD 7 /* 7 flood types */
|
||||
|
||||
@ -58,13 +58,13 @@ typedef struct FloodType {
|
||||
* IMPORTANT: MUST be in alphabetic order!!
|
||||
*/
|
||||
FloodType floodtypes[] = {
|
||||
{ 'c', FLD_CTCP, "CTCPflood", 'C', "mM", 0, },
|
||||
{ 'j', FLD_JOIN, "joinflood", 'i', "R", 0, },
|
||||
{ 'k', FLD_KNOCK, "knockflood", 'K', "", 0, },
|
||||
{ 'm', FLD_MSG, "msg/noticeflood", 'm', "M", 0, },
|
||||
{ 'n', FLD_NICK, "nickflood", 'N', "", 0, },
|
||||
{ 't', FLD_TEXT, "msg/noticeflood", '\0', "bd", 1, },
|
||||
{ 'r', FLD_REPEAT, "repeating", '\0', "bd", 1, },
|
||||
{ 'c', CHFLD_CTCP, "CTCPflood", 'C', "mM", 0, },
|
||||
{ 'j', CHFLD_JOIN, "joinflood", 'i', "R", 0, },
|
||||
{ 'k', CHFLD_KNOCK, "knockflood", 'K', "", 0, },
|
||||
{ 'm', CHFLD_MSG, "msg/noticeflood", 'm', "M", 0, },
|
||||
{ 'n', CHFLD_NICK, "nickflood", 'N', "", 0, },
|
||||
{ 't', CHFLD_TEXT, "msg/noticeflood", '\0', "bd", 1, },
|
||||
{ 'r', CHFLD_REPEAT, "repeating", '\0', "bd", 1, },
|
||||
};
|
||||
|
||||
#define MODEF_DEFAULT_UNSETTIME cfg.modef_default_unsettime
|
||||
@ -132,7 +132,7 @@ uint64_t gen_floodprot_msghash(char *text);
|
||||
int cmodef_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
|
||||
void *cmodef_put_param(void *r_in, char *param);
|
||||
char *cmodef_get_param(void *r_in);
|
||||
char *cmodef_conv_param(char *param_in, Client *client);
|
||||
char *cmodef_conv_param(char *param_in, Client *client, Channel *channel);
|
||||
void cmodef_free_param(void *r);
|
||||
void *cmodef_dup_struct(void *r_in);
|
||||
int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx);
|
||||
@ -142,7 +142,7 @@ int cmodef_channel_destroy(Channel *channel, int *should_destroy);
|
||||
int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype);
|
||||
int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype);
|
||||
int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment);
|
||||
int floodprot_nickchange(Client *client, char *oldnick);
|
||||
int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick);
|
||||
int floodprot_chanmode_del(Channel *channel, int m);
|
||||
void memberflood_free(ModData *md);
|
||||
int floodprot_stats(Client *client, char *flag);
|
||||
@ -596,7 +596,7 @@ char *cmodef_get_param(void *r_in)
|
||||
/** Convert parameter to something proper.
|
||||
* NOTE: client may be NULL if called for e.g. set::modes-on-join
|
||||
*/
|
||||
char *cmodef_conv_param(char *param_in, Client *client)
|
||||
char *cmodef_conv_param(char *param_in, Client *client, Channel *channel)
|
||||
{
|
||||
static char retbuf[256];
|
||||
char param[256];
|
||||
@ -753,7 +753,7 @@ int floodprot_join(Client *client, Channel *channel, MessageTag *mtags, char *pa
|
||||
(client->srvptr->serv->boottime && (TStime() - client->srvptr->serv->boottime >= MODEF_BOOT_DELAY)) &&
|
||||
!IsULine(client))
|
||||
{
|
||||
do_floodprot(channel, client, FLD_JOIN);
|
||||
do_floodprot(channel, client, CHFLD_JOIN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -842,7 +842,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
|
||||
chp = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
|
||||
|
||||
if (!chp || !(chp->limit[FLD_TEXT] || chp->limit[FLD_REPEAT]))
|
||||
if (!chp || !(chp->limit[CHFLD_TEXT] || chp->limit[CHFLD_REPEAT]))
|
||||
return HOOK_CONTINUE;
|
||||
|
||||
if (moddata_membership(mb, mdflood).ptr == NULL)
|
||||
@ -859,7 +859,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
memberflood->firstmsg = TStime();
|
||||
memberflood->nmsg = 1;
|
||||
memberflood->nmsg_repeat = 1;
|
||||
if (chp->limit[FLD_REPEAT])
|
||||
if (chp->limit[CHFLD_REPEAT])
|
||||
{
|
||||
memberflood->lastmsg = gen_floodprot_msghash(*msg);
|
||||
memberflood->prevmsg = 0;
|
||||
@ -868,7 +868,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
}
|
||||
|
||||
/* Anti-repeat ('r') */
|
||||
if (chp->limit[FLD_REPEAT])
|
||||
if (chp->limit[CHFLD_REPEAT])
|
||||
{
|
||||
msghash = gen_floodprot_msghash(*msg);
|
||||
if (memberflood->lastmsg)
|
||||
@ -876,7 +876,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
if ((memberflood->lastmsg == msghash) || (memberflood->prevmsg == msghash))
|
||||
{
|
||||
memberflood->nmsg_repeat++;
|
||||
if (memberflood->nmsg_repeat > chp->limit[FLD_REPEAT])
|
||||
if (memberflood->nmsg_repeat > chp->limit[CHFLD_REPEAT])
|
||||
is_flooding_repeat = 1;
|
||||
}
|
||||
memberflood->prevmsg = memberflood->lastmsg;
|
||||
@ -884,11 +884,11 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
memberflood->lastmsg = msghash;
|
||||
}
|
||||
|
||||
if (chp->limit[FLD_TEXT])
|
||||
if (chp->limit[CHFLD_TEXT])
|
||||
{
|
||||
/* increase msgs */
|
||||
memberflood->nmsg++;
|
||||
if (memberflood->nmsg > chp->limit[FLD_TEXT])
|
||||
if (memberflood->nmsg > chp->limit[CHFLD_TEXT])
|
||||
is_flooding_text = 1;
|
||||
}
|
||||
|
||||
@ -903,11 +903,11 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
|
||||
if (is_flooding_repeat)
|
||||
{
|
||||
snprintf(errbuf, sizeof(errbuf), "Flooding (Your last message is too similar to previous ones)");
|
||||
flood_type = FLD_REPEAT;
|
||||
flood_type = CHFLD_REPEAT;
|
||||
} else
|
||||
{
|
||||
snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", chp->limit[FLD_TEXT], chp->per);
|
||||
flood_type = FLD_TEXT;
|
||||
snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", chp->limit[CHFLD_TEXT], chp->per);
|
||||
flood_type = CHFLD_TEXT;
|
||||
}
|
||||
|
||||
if (chp->action[flood_type] == 'd')
|
||||
@ -952,10 +952,10 @@ int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int
|
||||
|
||||
/* HINT: don't be so stupid to reorder the items in the if's below.. you'll break things -- Syzop. */
|
||||
|
||||
do_floodprot(channel, client, FLD_MSG);
|
||||
do_floodprot(channel, client, CHFLD_MSG);
|
||||
|
||||
if ((text[0] == '\001') && strncmp(text+1, "ACTION ", 7))
|
||||
do_floodprot(channel, client, FLD_CTCP);
|
||||
do_floodprot(channel, client, CHFLD_CTCP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -963,11 +963,11 @@ int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int
|
||||
int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment)
|
||||
{
|
||||
if (IsFloodLimit(channel) && !IsULine(client))
|
||||
do_floodprot(channel, client, FLD_KNOCK);
|
||||
do_floodprot(channel, client, CHFLD_KNOCK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int floodprot_nickchange(Client *client, char *oldnick)
|
||||
int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick)
|
||||
{
|
||||
Membership *mp;
|
||||
|
||||
@ -980,7 +980,7 @@ int floodprot_nickchange(Client *client, char *oldnick)
|
||||
if (channel && IsFloodLimit(channel) &&
|
||||
!(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANADMIN)))
|
||||
{
|
||||
do_floodprot(channel, client, FLD_NICK);
|
||||
do_floodprot(channel, client, CHFLD_NICK);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -1001,27 +1001,27 @@ int floodprot_chanmode_del(Channel *channel, int modechar)
|
||||
switch(modechar)
|
||||
{
|
||||
case 'C':
|
||||
chp->counter[FLD_CTCP] = 0;
|
||||
chp->counter[CHFLD_CTCP] = 0;
|
||||
break;
|
||||
case 'N':
|
||||
chp->counter[FLD_NICK] = 0;
|
||||
chp->counter[CHFLD_NICK] = 0;
|
||||
break;
|
||||
case 'm':
|
||||
chp->counter[FLD_MSG] = 0;
|
||||
chp->counter[FLD_CTCP] = 0;
|
||||
chp->counter[CHFLD_MSG] = 0;
|
||||
chp->counter[CHFLD_CTCP] = 0;
|
||||
break;
|
||||
case 'K':
|
||||
chp->counter[FLD_KNOCK] = 0;
|
||||
chp->counter[CHFLD_KNOCK] = 0;
|
||||
break;
|
||||
case 'i':
|
||||
chp->counter[FLD_JOIN] = 0;
|
||||
chp->counter[CHFLD_JOIN] = 0;
|
||||
break;
|
||||
case 'M':
|
||||
chp->counter[FLD_MSG] = 0;
|
||||
chp->counter[FLD_CTCP] = 0;
|
||||
chp->counter[CHFLD_MSG] = 0;
|
||||
chp->counter[CHFLD_CTCP] = 0;
|
||||
break;
|
||||
case 'R':
|
||||
chp->counter[FLD_JOIN] = 0;
|
||||
chp->counter[CHFLD_JOIN] = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -19,10 +19,12 @@ struct ConfigHistoryExt {
|
||||
int lines; /**< number of lines */
|
||||
long time; /**< seconds */
|
||||
};
|
||||
struct {
|
||||
typedef struct cfgstruct cfgstruct;
|
||||
struct cfgstruct {
|
||||
ConfigHistoryExt playback_on_join; /**< Maximum number of lines & time to playback on-join */
|
||||
ConfigHistoryExt max_storage_per_channel; /**< Maximum number of lines & time to record */
|
||||
} cfg;
|
||||
ConfigHistoryExt max_storage_per_channel_registered; /**< Maximum number of lines & time to record for +r channels*/
|
||||
ConfigHistoryExt max_storage_per_channel_unregistered; /**< Maximum number of lines & time to record for -r channels */
|
||||
};
|
||||
|
||||
typedef struct HistoryChanMode HistoryChanMode;
|
||||
struct HistoryChanMode {
|
||||
@ -30,29 +32,38 @@ struct HistoryChanMode {
|
||||
unsigned long max_time; /**< Maximum number of time (in seconds) to record */
|
||||
};
|
||||
|
||||
/* Global variables */
|
||||
Cmode_t EXTMODE_HISTORY = 0L;
|
||||
static cfgstruct cfg;
|
||||
static cfgstruct test;
|
||||
|
||||
#define HistoryEnabled(channel) (channel->mode.extmode & EXTMODE_HISTORY)
|
||||
|
||||
/* Forward declarations */
|
||||
static void init_config(void);
|
||||
static void init_config(cfgstruct *cfg);
|
||||
int history_config_test(ConfigFile *, ConfigEntry *, int, int *);
|
||||
int history_config_posttest(int *);
|
||||
int history_config_run(ConfigFile *, ConfigEntry *, int);
|
||||
int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode);
|
||||
static int compare_history_modes(HistoryChanMode *a, HistoryChanMode *b);
|
||||
int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
|
||||
void *history_chanmode_put_param(void *r_in, char *param);
|
||||
char *history_chanmode_get_param(void *r_in);
|
||||
char *history_chanmode_conv_param(char *param, Client *client);
|
||||
char *history_chanmode_conv_param(char *param, Client *client, Channel *channel);
|
||||
void history_chanmode_free_param(void *r);
|
||||
void *history_chanmode_dup_struct(void *r_in);
|
||||
int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx);
|
||||
int history_channel_destroy(Channel *channel, int *should_destroy);
|
||||
int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype);
|
||||
int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]);
|
||||
CMD_OVERRIDE_FUNC(override_mode);
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
init_config(&test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, history_config_test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, history_config_posttest);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -75,7 +86,7 @@ MOD_INIT()
|
||||
creq.sjoin_check = history_chanmode_sjoin_check;
|
||||
CmodeAdd(modinfo->handle, creq, &EXTMODE_HISTORY);
|
||||
|
||||
init_config();
|
||||
init_config(&cfg);
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, history_config_run);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CHANMODE, 0, history_chanmode_change);
|
||||
@ -88,6 +99,11 @@ MOD_INIT()
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
CommandOverrideAdd(modinfo->handle, "MODE", override_mode);
|
||||
CommandOverrideAdd(modinfo->handle, "SVSMODE", override_mode);
|
||||
CommandOverrideAdd(modinfo->handle, "SVS2MODE", override_mode);
|
||||
CommandOverrideAdd(modinfo->handle, "SAMODE", override_mode);
|
||||
CommandOverrideAdd(modinfo->handle, "SJOIN", override_mode);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -96,14 +112,16 @@ MOD_UNLOAD()
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
static void init_config(void)
|
||||
static void init_config(cfgstruct *cfg)
|
||||
{
|
||||
/* Set default values */
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.playback_on_join.lines = 15;
|
||||
cfg.playback_on_join.time = 86400;
|
||||
cfg.max_storage_per_channel.lines = 200;
|
||||
cfg.max_storage_per_channel.time = 86400*7;
|
||||
memset(cfg, 0, sizeof(cfgstruct));
|
||||
cfg->playback_on_join.lines = 15;
|
||||
cfg->playback_on_join.time = 86400;
|
||||
cfg->max_storage_per_channel_unregistered.lines = 200;
|
||||
cfg->max_storage_per_channel_unregistered.time = 86400*31;
|
||||
cfg->max_storage_per_channel_registered.lines = 5000;
|
||||
cfg->max_storage_per_channel_registered.time = 86400*31;
|
||||
}
|
||||
|
||||
#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; }
|
||||
@ -111,9 +129,9 @@ static void init_config(void)
|
||||
int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
ConfigEntry *cep, *cepp, *cep4;
|
||||
int on_join_lines=0, maximum_storage_lines=0;
|
||||
long on_join_time=0L, maximum_storage_time=0L;
|
||||
ConfigEntry *cep, *cepp, *cep4, *cep5;
|
||||
int on_join_lines=0, maximum_storage_lines_registered=0, maximum_storage_lines_unregistered=0;
|
||||
long on_join_time=0L, maximum_storage_time_registered=0L, maximum_storage_time_unregistered=0L;
|
||||
|
||||
/* We only care about set::history */
|
||||
if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history"))
|
||||
@ -134,29 +152,29 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
int v;
|
||||
CheckNull(cep4);
|
||||
v = atoi(cep4->ce_vardata);
|
||||
if ((v < 1) || (v > 1000000000))
|
||||
if ((v < 0) || (v > 1000))
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::playback-on-join::lines must be between 1 and 1000. "
|
||||
config_error("%s:%i: set::history::channel::playback-on-join::lines must be between 0 and 1000. "
|
||||
"Recommended values are 10-50. Got: %d.",
|
||||
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, v);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
on_join_lines = v;
|
||||
test.playback_on_join.lines = v;
|
||||
} else
|
||||
if (!strcmp(cep4->ce_varname, "time"))
|
||||
{
|
||||
long v;
|
||||
CheckNull(cep4);
|
||||
v = config_checkval(cep4->ce_vardata, CFG_TIME);
|
||||
if (v < 1)
|
||||
if (v < 0)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::playback-on-join::time must be a positive number.",
|
||||
config_error("%s:%i: set::history::channel::playback-on-join::time must be zero or more.",
|
||||
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
on_join_time = v;
|
||||
test.playback_on_join.time = v;
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cep4->ce_fileptr->cf_filename,
|
||||
@ -169,45 +187,127 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next)
|
||||
{
|
||||
if (!strcmp(cep4->ce_varname, "lines"))
|
||||
if (!strcmp(cep4->ce_varname, "registered"))
|
||||
{
|
||||
int v;
|
||||
CheckNull(cep4);
|
||||
v = atoi(cep4->ce_vardata);
|
||||
if (v < 1)
|
||||
for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::lines must be a positive number.",
|
||||
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
if (!strcmp(cep5->ce_varname, "lines"))
|
||||
{
|
||||
int v;
|
||||
CheckNull(cep5);
|
||||
v = atoi(cep5->ce_vardata);
|
||||
if (v < 1)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::lines must be a positive number.",
|
||||
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
test.max_storage_per_channel_registered.lines = v;
|
||||
} else
|
||||
if (!strcmp(cep5->ce_varname, "time"))
|
||||
{
|
||||
long v;
|
||||
CheckNull(cep5);
|
||||
v = config_checkval(cep5->ce_vardata, CFG_TIME);
|
||||
if (v < 1)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::time must be a positive number.",
|
||||
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
test.max_storage_per_channel_registered.time = v;
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cep5->ce_fileptr->cf_filename,
|
||||
cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::registered", cep5->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
maximum_storage_lines = v;
|
||||
} else
|
||||
if (!strcmp(cep4->ce_varname, "time"))
|
||||
if (!strcmp(cep4->ce_varname, "unregistered"))
|
||||
{
|
||||
long v;
|
||||
CheckNull(cep4);
|
||||
v = config_checkval(cep4->ce_vardata, CFG_TIME);
|
||||
if (v < 1)
|
||||
for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::time must be a positive number.",
|
||||
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
if (!strcmp(cep5->ce_varname, "lines"))
|
||||
{
|
||||
int v;
|
||||
CheckNull(cep5);
|
||||
v = atoi(cep5->ce_vardata);
|
||||
if (v < 1)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::lines must be a positive number.",
|
||||
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
test.max_storage_per_channel_unregistered.lines = v;
|
||||
} else
|
||||
if (!strcmp(cep5->ce_varname, "time"))
|
||||
{
|
||||
long v;
|
||||
CheckNull(cep5);
|
||||
v = config_checkval(cep5->ce_vardata, CFG_TIME);
|
||||
if (v < 1)
|
||||
{
|
||||
config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::time must be a positive number.",
|
||||
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
test.max_storage_per_channel_unregistered.time = v;
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cep5->ce_fileptr->cf_filename,
|
||||
cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::unregistered", cep5->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
maximum_storage_time = v;
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cep4->ce_fileptr->cf_filename,
|
||||
cep4->ce_varlinenum, "set::history::channel::max-storage-per-channel", cep4->ce_varname);
|
||||
config_error_unknown(cep->ce_fileptr->cf_filename,
|
||||
cep->ce_varlinenum, "set::history::max-storage-per-channel", cep->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
config_error_unknown(cepp->ce_fileptr->cf_filename,
|
||||
cepp->ce_varlinenum, "set::history::channel", cepp->ce_varname);
|
||||
errors++;
|
||||
/* hmm.. I don't like this method. but I just quickly copied it from CONFIG_ALLOW for now... */
|
||||
int used = 0;
|
||||
Hook *h;
|
||||
for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
|
||||
{
|
||||
int value, errs = 0;
|
||||
if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
|
||||
&& !(h->owner->options & MOD_OPT_PERM))
|
||||
continue;
|
||||
value = (*(h->func.intfunc))(cf, cepp, CONFIG_SET_HISTORY_CHANNEL, &errs);
|
||||
if (value == 2)
|
||||
used = 1;
|
||||
if (value == 1)
|
||||
{
|
||||
used = 1;
|
||||
break;
|
||||
}
|
||||
if (value == -1)
|
||||
{
|
||||
used = 1;
|
||||
errors += errs;
|
||||
break;
|
||||
}
|
||||
if (value == -2)
|
||||
{
|
||||
used = 1;
|
||||
errors += errs;
|
||||
}
|
||||
}
|
||||
if (!used)
|
||||
{
|
||||
config_error_unknown(cepp->ce_fileptr->cf_filename,
|
||||
cepp->ce_varlinenum, "set::history::channel", cepp->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -217,23 +317,25 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
}
|
||||
}
|
||||
|
||||
if ((on_join_time && maximum_storage_time) && (on_join_time > maximum_storage_time))
|
||||
{
|
||||
config_error("set::history::channel::playback-on-join::time cannot be higher than set::history::channel::max-storage-per-channel::time. Either set the playback-on-join::time lower or the maximum::time higher.");
|
||||
errors++;
|
||||
}
|
||||
if ((on_join_lines && maximum_storage_lines) && (on_join_lines > maximum_storage_lines))
|
||||
{
|
||||
config_error("set::history::channel::playback-on-join::lines cannot be higher than set::history::channel::max-storage-per-channel::lines. Either set the playback-on-join::lines lower or the maximum::lines higher.");
|
||||
errors++;
|
||||
}
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int history_config_posttest(int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
/* We could check here for on join lines / on join time being bigger than max storage but..
|
||||
* not really important.
|
||||
*/
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
ConfigEntry *cep, *cepp, *cep4;
|
||||
ConfigEntry *cep, *cepp, *cep4, *cep5;
|
||||
|
||||
if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history"))
|
||||
return 0;
|
||||
@ -262,15 +364,44 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next)
|
||||
{
|
||||
if (!strcmp(cep4->ce_varname, "lines"))
|
||||
if (!strcmp(cep4->ce_varname, "registered"))
|
||||
{
|
||||
cfg.max_storage_per_channel.lines = atoi(cep4->ce_vardata);
|
||||
for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
|
||||
{
|
||||
if (!strcmp(cep5->ce_varname, "lines"))
|
||||
{
|
||||
cfg.max_storage_per_channel_registered.lines = atoi(cep5->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep5->ce_varname, "time"))
|
||||
{
|
||||
cfg.max_storage_per_channel_registered.time = config_checkval(cep5->ce_vardata, CFG_TIME);
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (!strcmp(cep4->ce_varname, "time"))
|
||||
if (!strcmp(cep4->ce_varname, "unregistered"))
|
||||
{
|
||||
cfg.max_storage_per_channel.time = config_checkval(cep4->ce_vardata, CFG_TIME);
|
||||
for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
|
||||
{
|
||||
if (!strcmp(cep5->ce_varname, "lines"))
|
||||
{
|
||||
cfg.max_storage_per_channel_unregistered.lines = atoi(cep5->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep5->ce_varname, "time"))
|
||||
{
|
||||
cfg.max_storage_per_channel_unregistered.time = config_checkval(cep5->ce_vardata, CFG_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
Hook *h;
|
||||
for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
|
||||
{
|
||||
int value = (*(h->func.intfunc))(cf, cepp, CONFIG_SET_HISTORY_CHANNEL);
|
||||
if (value == 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +415,7 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
* @param lines: The number of lines (the X in +H X:Y)
|
||||
* @param t: The time value (the Y in +H X:Y)
|
||||
*/
|
||||
int history_parse_chanmode(char *param, int *lines, long *t)
|
||||
int history_parse_chanmode(Channel *channel, char *param, int *lines, long *t)
|
||||
{
|
||||
char buf[64], *p, *q;
|
||||
char contains_non_digit = 0;
|
||||
@ -327,12 +458,20 @@ int history_parse_chanmode(char *param, int *lines, long *t)
|
||||
return 0;
|
||||
|
||||
/* Check imposed configuration limits... */
|
||||
if (*lines > cfg.max_storage_per_channel.lines)
|
||||
*lines = cfg.max_storage_per_channel.lines;
|
||||
if (!channel || has_channel_mode(channel, 'r'))
|
||||
{
|
||||
if (*lines > cfg.max_storage_per_channel_registered.lines)
|
||||
*lines = cfg.max_storage_per_channel_registered.lines;
|
||||
|
||||
if (*t > cfg.max_storage_per_channel.time)
|
||||
*t = cfg.max_storage_per_channel.time;
|
||||
if (*t > cfg.max_storage_per_channel_registered.time)
|
||||
*t = cfg.max_storage_per_channel_registered.time;
|
||||
} else {
|
||||
if (*lines > cfg.max_storage_per_channel_unregistered.lines)
|
||||
*lines = cfg.max_storage_per_channel_unregistered.lines;
|
||||
|
||||
if (*t > cfg.max_storage_per_channel_unregistered.time)
|
||||
*t = cfg.max_storage_per_channel_unregistered.time;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -355,7 +494,7 @@ int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *pa
|
||||
int lines = 0;
|
||||
long t = 0L;
|
||||
|
||||
if (!history_parse_chanmode(param, &lines, &t))
|
||||
if (!history_parse_chanmode(channel, param, &lines, &t))
|
||||
{
|
||||
sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'H', "Invalid syntax for MODE +H. Use +H lines:period. The period must be in minutes (eg: 10) or a time value (eg: 1h).");
|
||||
return EX_DENY;
|
||||
@ -369,19 +508,37 @@ int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *pa
|
||||
return EX_DENY;
|
||||
}
|
||||
|
||||
static void history_chanmode_helper(char *buf, size_t bufsize, int lines, long t)
|
||||
{
|
||||
if ((t % 86400) == 0)
|
||||
{
|
||||
/* Can be represented in full days, eg "1d" */
|
||||
snprintf(buf, bufsize, "%d:%ldd", lines, t / 86400);
|
||||
} else
|
||||
if ((t % 3600) == 0)
|
||||
{
|
||||
/* Can be represented in hours, eg "8h" */
|
||||
snprintf(buf, bufsize, "%d:%ldh", lines, t / 3600);
|
||||
} else
|
||||
{
|
||||
/* Otherwise, stick to minutes */
|
||||
snprintf(buf, bufsize, "%d:%ldm", lines, t / 60);
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert channel parameter to something proper.
|
||||
* NOTE: client may be NULL if called for e.g. set::modes-playback-on-join
|
||||
*/
|
||||
char *history_chanmode_conv_param(char *param, Client *client)
|
||||
char *history_chanmode_conv_param(char *param, Client *client, Channel *channel)
|
||||
{
|
||||
static char buf[64];
|
||||
int lines = 0;
|
||||
long t = 0L;
|
||||
|
||||
if (!history_parse_chanmode(param, &lines, &t))
|
||||
if (!history_parse_chanmode(channel, param, &lines, &t))
|
||||
return NULL;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d:%ldm", lines, t / 60);
|
||||
history_chanmode_helper(buf, sizeof(buf), lines, t);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -392,7 +549,7 @@ void *history_chanmode_put_param(void *mode_in, char *param)
|
||||
int lines = 0;
|
||||
long t = 0L;
|
||||
|
||||
if (!history_parse_chanmode(param, &lines, &t))
|
||||
if (!history_parse_chanmode(NULL, param, &lines, &t))
|
||||
return NULL;
|
||||
|
||||
if (!h)
|
||||
@ -416,13 +573,7 @@ char *history_chanmode_get_param(void *h_in)
|
||||
if (!h_in)
|
||||
return NULL;
|
||||
|
||||
/* For now we convert the time to minutes for displaying purposes
|
||||
* and show it as eg 5:10m.
|
||||
* In a later release we can have a go at converting to '1h', '1d'
|
||||
* and such, but not before most people run 5.0.2+ as otherwise you
|
||||
* get desyncs in channel history retention times.
|
||||
*/
|
||||
snprintf(buf, sizeof(buf), "%d:%ldm", h->max_lines, h->max_time / 60);
|
||||
history_chanmode_helper(buf, sizeof(buf), h->max_lines, h->max_time);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -531,17 +682,117 @@ int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix,
|
||||
|
||||
int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[])
|
||||
{
|
||||
if (!HistoryEnabled(channel))
|
||||
/* Only for +H channels */
|
||||
if (!HistoryEnabled(channel) || !cfg.playback_on_join.lines || !cfg.playback_on_join.time)
|
||||
return 0;
|
||||
|
||||
if (MyUser(client))
|
||||
/* No history-on-join for clients that implement CHATHISTORY,
|
||||
* they will pull history themselves if they need it.
|
||||
*/
|
||||
if (HasCapability(client, "draft/chathistory") || HasCapability(client, "chathistory"))
|
||||
return 0;
|
||||
|
||||
if (MyUser(client) && can_receive_history(client))
|
||||
{
|
||||
HistoryFilter filter;
|
||||
HistoryResult *r;
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
filter.cmd = HFC_SIMPLE;
|
||||
filter.last_lines = cfg.playback_on_join.lines;
|
||||
filter.last_seconds = cfg.playback_on_join.time;
|
||||
history_request(client, channel->chname, &filter);
|
||||
r = history_request(channel->chname, &filter);
|
||||
if (r)
|
||||
{
|
||||
history_send_result(client, r);
|
||||
free_history_result(r);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Check if a channel went from +r to -r and adjust +H if needed.
|
||||
* This does not only override "MODE" but also "SAMODE", "SJOIN" and more.
|
||||
*/
|
||||
CMD_OVERRIDE_FUNC(override_mode)
|
||||
{
|
||||
Channel *channel;
|
||||
int had_r = 0;
|
||||
|
||||
/* We only bother checking for this corner case if the -r
|
||||
* comes from a server directly linked to us, this normally
|
||||
* means: we are the server that services are linked to.
|
||||
*/
|
||||
if ((IsServer(client) && client->local) ||
|
||||
(IsUser(client) && client->srvptr && client->srvptr->local))
|
||||
{
|
||||
/* Now check if the channel is currently +r */
|
||||
if ((parc >= 2) && !BadPtr(parv[1]) && ((channel = find_channel(parv[1], NULL))) &&
|
||||
has_channel_mode(channel, 'r'))
|
||||
{
|
||||
had_r = 1;
|
||||
}
|
||||
}
|
||||
CallCommandOverride(ovr, client, recv_mtags, parc, parv);
|
||||
|
||||
/* If..
|
||||
* - channel was +r
|
||||
* - re-lookup the channel and check that it still
|
||||
* exists (as it may have been destroyed)
|
||||
* - and is now -r
|
||||
* - and has +H set
|
||||
* then...
|
||||
*/
|
||||
if (had_r &&
|
||||
((channel = find_channel(parv[1], NULL))) &&
|
||||
!has_channel_mode(channel, 'r') &&
|
||||
HistoryEnabled(channel))
|
||||
{
|
||||
/* Check if limit is higher than allowed for unregistered channels */
|
||||
HistoryChanMode *settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H');
|
||||
int changed = 0;
|
||||
|
||||
if (!settings)
|
||||
return; /* Weird */
|
||||
|
||||
if (settings->max_lines > cfg.max_storage_per_channel_unregistered.lines)
|
||||
{
|
||||
settings->max_lines = cfg.max_storage_per_channel_unregistered.lines;
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
if (settings->max_time > cfg.max_storage_per_channel_unregistered.time)
|
||||
{
|
||||
settings->max_time = cfg.max_storage_per_channel_unregistered.time;
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
MessageTag *mtags = NULL;
|
||||
char *params = history_chanmode_get_param(settings);
|
||||
|
||||
if (!params)
|
||||
return; /* Weird */
|
||||
|
||||
strlcpy(modebuf, "+H", sizeof(modebuf));
|
||||
strlcpy(parabuf, params, sizeof(modebuf));
|
||||
|
||||
new_message(&me, NULL, &mtags);
|
||||
|
||||
sendto_channel(channel, &me, &me, 0, 0, SEND_LOCAL, mtags,
|
||||
":%s MODE %s %s %s",
|
||||
me.name, channel->chname, modebuf, parabuf);
|
||||
sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s %lld",
|
||||
me.id, channel->chname, modebuf, parabuf,
|
||||
(long long)channel->creationtime);
|
||||
|
||||
/* Activate this hook just like cmd_mode.c */
|
||||
RunHook7(HOOKTYPE_REMOTE_CHANMODE, &me, channel, mtags, modebuf, parabuf, 0, 0);
|
||||
|
||||
free_message_tags(mtags);
|
||||
|
||||
*modebuf = *parabuf = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ typedef enum {
|
||||
int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
|
||||
void *cmodeL_put_param(void *r_in, char *param);
|
||||
char *cmodeL_get_param(void *r_in);
|
||||
char *cmodeL_conv_param(char *param_in, Client *client);
|
||||
char *cmodeL_conv_param(char *param_in, Client *client, Channel *channel);
|
||||
void cmodeL_free_param(void *r);
|
||||
void *cmodeL_dup_struct(void *r_in);
|
||||
int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx);
|
||||
@ -171,7 +171,7 @@ char *cmodeL_get_param(void *r_in)
|
||||
/** Convert parameter to something proper.
|
||||
* NOTE: client may be NULL
|
||||
*/
|
||||
char *cmodeL_conv_param(char *param, Client *client)
|
||||
char *cmodeL_conv_param(char *param, Client *client, Channel *channel)
|
||||
{
|
||||
char *p;
|
||||
|
||||
|
@ -35,14 +35,14 @@ ModuleHeader MOD_HEADER = {
|
||||
do { \
|
||||
sendto_realops_and_log("[channeldb] Error writing to temporary database file " \
|
||||
"'%s': %s (DATABASE NOT SAVED)", \
|
||||
fname, strerror(errno)); \
|
||||
fname, unrealdb_get_error_string()); \
|
||||
} while(0)
|
||||
|
||||
#define W_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
WARN_WRITE_ERROR(tmpfname); \
|
||||
fclose(fd); \
|
||||
unrealdb_close(db); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
@ -55,43 +55,53 @@ ModuleHeader MOD_HEADER = {
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Structs */
|
||||
struct cfgstruct {
|
||||
char *database;
|
||||
char *db_secret;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
void channeldb_moddata_free(ModData *md);
|
||||
void setcfg(void);
|
||||
void freecfg(void);
|
||||
int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
||||
int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
void setcfg(struct cfgstruct *cfg);
|
||||
void freecfg(struct cfgstruct *cfg);
|
||||
int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
||||
int channeldb_config_posttest(int *errs);
|
||||
int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
EVENT(write_channeldb_evt);
|
||||
int write_channeldb(void);
|
||||
int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel);
|
||||
int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel);
|
||||
int read_channeldb(void);
|
||||
static void set_channel_mode(Channel *channel, char *modes, char *parameters);
|
||||
|
||||
/* Global variables */
|
||||
static uint32_t channeldb_version = CHANNELDB_VERSION;
|
||||
struct cfgstruct {
|
||||
char *database;
|
||||
};
|
||||
static struct cfgstruct cfg;
|
||||
static struct cfgstruct test;
|
||||
|
||||
static long channeldb_next_event = 0;
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_configtest);
|
||||
memset(&test, 0, sizeof(test));
|
||||
setcfg(&test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_config_test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, channeldb_config_posttest);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
/* We must unload early, when all channel modes and such are still in place: */
|
||||
ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, -99999999);
|
||||
|
||||
LoadPersistentLong(modinfo, channeldb_next_event);
|
||||
|
||||
setcfg();
|
||||
setcfg(&cfg);
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_configrun);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_config_run);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -122,7 +132,10 @@ MOD_LOAD()
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
freecfg();
|
||||
if (loop.ircd_terminating)
|
||||
write_channeldb();
|
||||
freecfg(&test);
|
||||
freecfg(&cfg);
|
||||
SavePersistentLong(modinfo, channeldb_next_event);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
@ -133,19 +146,20 @@ void channeldb_moddata_free(ModData *md)
|
||||
md->i = 0;
|
||||
}
|
||||
|
||||
void setcfg(void)
|
||||
void setcfg(struct cfgstruct *cfg)
|
||||
{
|
||||
// Default: data/channel.db
|
||||
safe_strdup(cfg.database, "channel.db");
|
||||
convert_to_absolute_path(&cfg.database, PERMDATADIR);
|
||||
safe_strdup(cfg->database, "channel.db");
|
||||
convert_to_absolute_path(&cfg->database, PERMDATADIR);
|
||||
}
|
||||
|
||||
void freecfg(void)
|
||||
void freecfg(struct cfgstruct *cfg)
|
||||
{
|
||||
safe_free(cfg.database);
|
||||
safe_free(cfg->database);
|
||||
safe_free(cfg->db_secret);
|
||||
}
|
||||
|
||||
int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
ConfigEntry *cep;
|
||||
@ -159,16 +173,45 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!cep->ce_vardata) {
|
||||
if (!cep->ce_vardata)
|
||||
{
|
||||
config_error("%s:%i: blank set::channeldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(cep->ce_varname, "database")) {
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
{
|
||||
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
|
||||
continue;
|
||||
safe_strdup(test.database, cep->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
{
|
||||
char *err;
|
||||
if ((err = unrealdb_test_secret(cep->ce_vardata)))
|
||||
{
|
||||
config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
safe_strdup(test.db_secret, cep->ce_vardata);
|
||||
} else
|
||||
{
|
||||
config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int channeldb_config_posttest(int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
char *errstr;
|
||||
|
||||
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
|
||||
{
|
||||
config_error("[channeldb] %s", errstr);
|
||||
errors++;
|
||||
}
|
||||
|
||||
@ -176,7 +219,7 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
ConfigEntry *cep;
|
||||
|
||||
@ -191,6 +234,8 @@ int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
safe_strdup(cfg.database, cep->ce_vardata);
|
||||
else if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
safe_strdup(cfg.db_secret, cep->ce_vardata);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -206,7 +251,7 @@ EVENT(write_channeldb_evt)
|
||||
int write_channeldb(void)
|
||||
{
|
||||
char tmpfname[512];
|
||||
FILE *fd;
|
||||
UnrealDB *db;
|
||||
Channel *channel;
|
||||
int cnt = 0;
|
||||
#ifdef BENCHMARK
|
||||
@ -217,33 +262,33 @@ int write_channeldb(void)
|
||||
|
||||
// Write to a tempfile first, then rename it if everything succeeded
|
||||
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
|
||||
fd = fopen(tmpfname, "wb");
|
||||
if (!fd)
|
||||
db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
W_SAFE(write_data(fd, &channeldb_version, sizeof(channeldb_version)));
|
||||
W_SAFE(unrealdb_write_int32(db, channeldb_version));
|
||||
|
||||
/* First, count +P channels and write the count to the database */
|
||||
for (channel = channels; channel; channel=channel->nextch)
|
||||
if (has_channel_mode(channel, 'P'))
|
||||
cnt++;
|
||||
W_SAFE(write_int64(fd, cnt));
|
||||
W_SAFE(unrealdb_write_int64(db, cnt));
|
||||
|
||||
for (channel = channels; channel; channel=channel->nextch)
|
||||
{
|
||||
/* We only care about +P (persistent) channels */
|
||||
if (has_channel_mode(channel, 'P'))
|
||||
{
|
||||
if (!write_channel_entry(fd, tmpfname, channel))
|
||||
if (!write_channel_entry(db, tmpfname, channel))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything seems to have gone well, attempt to close and rename the tempfile
|
||||
if (fclose(fd) != 0)
|
||||
if (!unrealdb_close(db))
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
@ -266,7 +311,7 @@ int write_channeldb(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_listmode(FILE *fd, const char *tmpfname, Ban *lst)
|
||||
int write_listmode(UnrealDB *db, const char *tmpfname, Ban *lst)
|
||||
{
|
||||
Ban *l;
|
||||
int cnt = 0;
|
||||
@ -274,50 +319,50 @@ int write_listmode(FILE *fd, const char *tmpfname, Ban *lst)
|
||||
/* First count and write the list count */
|
||||
for (l = lst; l; l = l->next)
|
||||
cnt++;
|
||||
W_SAFE(write_int32(fd, cnt));
|
||||
W_SAFE(unrealdb_write_int32(db, cnt));
|
||||
|
||||
for (l = lst; l; l = l->next)
|
||||
{
|
||||
/* The entry, setby, seton */
|
||||
W_SAFE(write_str(fd, l->banstr));
|
||||
W_SAFE(write_str(fd, l->who));
|
||||
W_SAFE(write_int64(fd, l->when));
|
||||
W_SAFE(unrealdb_write_str(db, l->banstr));
|
||||
W_SAFE(unrealdb_write_str(db, l->who));
|
||||
W_SAFE(unrealdb_write_int64(db, l->when));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel)
|
||||
int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel)
|
||||
{
|
||||
W_SAFE(write_int32(fd, MAGIC_CHANNEL_START));
|
||||
W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_START));
|
||||
/* Channel name */
|
||||
W_SAFE(write_str(fd, channel->chname));
|
||||
W_SAFE(unrealdb_write_str(db, channel->chname));
|
||||
/* Channel creation time */
|
||||
W_SAFE(write_int64(fd, channel->creationtime));
|
||||
W_SAFE(unrealdb_write_int64(db, channel->creationtime));
|
||||
/* Topic (topic, setby, seton) */
|
||||
W_SAFE(write_str(fd, channel->topic));
|
||||
W_SAFE(write_str(fd, channel->topic_nick));
|
||||
W_SAFE(write_int64(fd, channel->topic_time));
|
||||
W_SAFE(unrealdb_write_str(db, channel->topic));
|
||||
W_SAFE(unrealdb_write_str(db, channel->topic_nick));
|
||||
W_SAFE(unrealdb_write_int64(db, channel->topic_time));
|
||||
/* Basic channel modes (eg: +sntkl key 55) */
|
||||
channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel);
|
||||
W_SAFE(write_str(fd, modebuf));
|
||||
W_SAFE(write_str(fd, parabuf));
|
||||
W_SAFE(unrealdb_write_str(db, modebuf));
|
||||
W_SAFE(unrealdb_write_str(db, parabuf));
|
||||
/* Mode lock */
|
||||
W_SAFE(write_str(fd, channel->mode_lock));
|
||||
W_SAFE(unrealdb_write_str(db, channel->mode_lock));
|
||||
/* List modes (bans, exempts, invex) */
|
||||
if (!write_listmode(fd, tmpfname, channel->banlist))
|
||||
if (!write_listmode(db, tmpfname, channel->banlist))
|
||||
return 0;
|
||||
if (!write_listmode(fd, tmpfname, channel->exlist))
|
||||
if (!write_listmode(db, tmpfname, channel->exlist))
|
||||
return 0;
|
||||
if (!write_listmode(fd, tmpfname, channel->invexlist))
|
||||
if (!write_listmode(db, tmpfname, channel->invexlist))
|
||||
return 0;
|
||||
W_SAFE(write_int32(fd, MAGIC_CHANNEL_END));
|
||||
W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_END));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define R_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \
|
||||
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
|
||||
if (e) \
|
||||
{ \
|
||||
safe_free(e->banstr); \
|
||||
@ -328,21 +373,21 @@ int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel)
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int read_listmode(FILE *fd, Ban **lst)
|
||||
int read_listmode(UnrealDB *db, Ban **lst)
|
||||
{
|
||||
uint32_t total;
|
||||
uint64_t when;
|
||||
int i;
|
||||
Ban *e = NULL;
|
||||
|
||||
R_SAFE(read_data(fd, &total, sizeof(total)));
|
||||
R_SAFE(unrealdb_read_int32(db, &total));
|
||||
|
||||
for (i = 0; i < total; i++)
|
||||
{
|
||||
e = safe_alloc(sizeof(Ban));
|
||||
R_SAFE(read_str(fd, &e->banstr));
|
||||
R_SAFE(read_str(fd, &e->who));
|
||||
R_SAFE(read_data(fd, &when, sizeof(when)));
|
||||
R_SAFE(unrealdb_read_str(db, &e->banstr));
|
||||
R_SAFE(unrealdb_read_str(db, &e->who));
|
||||
R_SAFE(unrealdb_read_int64(db, &when));
|
||||
e->when = when;
|
||||
e->next = *lst;
|
||||
*lst = e;
|
||||
@ -366,8 +411,8 @@ int read_listmode(FILE *fd, Ban **lst)
|
||||
#define R_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \
|
||||
fclose(fd); \
|
||||
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
|
||||
unrealdb_close(db); \
|
||||
FreeChannelEntry(); \
|
||||
return 0; \
|
||||
} \
|
||||
@ -375,7 +420,7 @@ int read_listmode(FILE *fd, Ban **lst)
|
||||
|
||||
int read_channeldb(void)
|
||||
{
|
||||
FILE *fd;
|
||||
UnrealDB *db;
|
||||
uint32_t version;
|
||||
int added = 0;
|
||||
int i;
|
||||
@ -397,29 +442,41 @@ int read_channeldb(void)
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
fd = fopen(cfg.database, "rb");
|
||||
if (!fd)
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
|
||||
{
|
||||
/* Database does not exist. Could be first boot */
|
||||
config_warn("[channeldb] No database present at '%s', will start a new one", cfg.database);
|
||||
return 1;
|
||||
} else {
|
||||
config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, strerror(errno));
|
||||
} else
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
|
||||
{
|
||||
/* Re-open as unencrypted */
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
|
||||
if (!db)
|
||||
{
|
||||
/* This should actually never happen, unless some weird I/O error */
|
||||
config_warn("[channeldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
{
|
||||
config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
R_SAFE(read_data(fd, &version, sizeof(version)));
|
||||
R_SAFE(unrealdb_read_int32(db, &version));
|
||||
if (version > channeldb_version)
|
||||
{
|
||||
config_warn("[channeldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, channeldb_version, version);
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
R_SAFE(read_data(fd, &count, sizeof(count)));
|
||||
R_SAFE(unrealdb_read_int64(db, &count));
|
||||
|
||||
for (i=1; i <= count; i++)
|
||||
{
|
||||
@ -434,20 +491,20 @@ int read_channeldb(void)
|
||||
mode_lock = NULL;
|
||||
|
||||
Channel *channel;
|
||||
R_SAFE(read_data(fd, &magic, sizeof(magic)));
|
||||
R_SAFE(unrealdb_read_int32(db, &magic));
|
||||
if (magic != MAGIC_CHANNEL_START)
|
||||
{
|
||||
config_error("[channeldb] Corrupt database (%s) - channel magic start is 0x%x. Further reading aborted.", cfg.database, magic);
|
||||
break;
|
||||
}
|
||||
R_SAFE(read_str(fd, &chname));
|
||||
R_SAFE(read_data(fd, &creationtime, sizeof(creationtime)));
|
||||
R_SAFE(read_str(fd, &topic));
|
||||
R_SAFE(read_str(fd, &topic_nick));
|
||||
R_SAFE(read_data(fd, &topic_time, sizeof(topic_time)));
|
||||
R_SAFE(read_str(fd, &modes1));
|
||||
R_SAFE(read_str(fd, &modes2));
|
||||
R_SAFE(read_str(fd, &mode_lock));
|
||||
R_SAFE(unrealdb_read_str(db, &chname));
|
||||
R_SAFE(unrealdb_read_int64(db, &creationtime));
|
||||
R_SAFE(unrealdb_read_str(db, &topic));
|
||||
R_SAFE(unrealdb_read_str(db, &topic_nick));
|
||||
R_SAFE(unrealdb_read_int64(db, &topic_time));
|
||||
R_SAFE(unrealdb_read_str(db, &modes1));
|
||||
R_SAFE(unrealdb_read_str(db, &modes2));
|
||||
R_SAFE(unrealdb_read_str(db, &mode_lock));
|
||||
/* If we got this far, we can create/initialize the channel with the above */
|
||||
channel = get_channel(&me, chname, CREATE);
|
||||
channel->creationtime = creationtime;
|
||||
@ -456,10 +513,10 @@ int read_channeldb(void)
|
||||
channel->topic_time = topic_time;
|
||||
safe_strdup(channel->mode_lock, mode_lock);
|
||||
set_channel_mode(channel, modes1, modes2);
|
||||
R_SAFE(read_listmode(fd, &channel->banlist));
|
||||
R_SAFE(read_listmode(fd, &channel->exlist));
|
||||
R_SAFE(read_listmode(fd, &channel->invexlist));
|
||||
R_SAFE(read_data(fd, &magic, sizeof(magic)));
|
||||
R_SAFE(read_listmode(db, &channel->banlist));
|
||||
R_SAFE(read_listmode(db, &channel->exlist));
|
||||
R_SAFE(read_listmode(db, &channel->invexlist));
|
||||
R_SAFE(unrealdb_read_int32(db, &magic));
|
||||
FreeChannelEntry();
|
||||
added++;
|
||||
if (magic != MAGIC_CHANNEL_END)
|
||||
@ -469,7 +526,7 @@ int read_channeldb(void)
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
|
||||
if (added)
|
||||
sendto_realops_and_log("[channeldb] Added %d persistent channels (+P)", added);
|
||||
|
294
src/modules/chathistory.c
Normal file
294
src/modules/chathistory.c
Normal file
@ -0,0 +1,294 @@
|
||||
/* src/modules/chathistory.c - IRCv3 CHATHISTORY command.
|
||||
* (C) Copyright 2021 Bram Matthys (Syzop) and the UnrealIRCd team
|
||||
* License: GPLv2
|
||||
*
|
||||
* This implements the "CHATHISTORY" command, the CAP and 005 token.
|
||||
* https://ircv3.net/specs/extensions/chathistory
|
||||
*/
|
||||
#include "unrealircd.h"
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"chathistory",
|
||||
"1.0",
|
||||
"IRCv3 CHATHISTORY command",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
CMD_FUNC(cmd_chathistory);
|
||||
|
||||
/* Global variables */
|
||||
long CAP_CHATHISTORY = 0L;
|
||||
|
||||
/* TODO: consider moving to config file */
|
||||
#define CHATHISTORY_LIMIT 50
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
ClientCapabilityInfo c;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
CommandAdd(modinfo->handle, "CHATHISTORY", cmd_chathistory, MAXPARA, CMD_USER);
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.name = "draft/chathistory";
|
||||
ClientCapabilityAdd(modinfo->handle, &c, &CAP_CHATHISTORY);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
ISupportSetFmt(modinfo->handle, "CHATHISTORY", "%d", CHATHISTORY_LIMIT);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
int chathistory_token(char *str, char *token, char **store)
|
||||
{
|
||||
char *p = strchr(str, '=');
|
||||
if (!p)
|
||||
return 0;
|
||||
*p = '\0'; // frag
|
||||
if (!strcmp(str, token))
|
||||
{
|
||||
*p = '='; // restore
|
||||
*store = strdup(p + 1); // can be \0
|
||||
return 1;
|
||||
}
|
||||
*p = '='; // restore
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chathistory_targets_send_line(Client *client, HistoryResult *r, char *batchid)
|
||||
{
|
||||
MessageTag *mtags = NULL;
|
||||
MessageTag *m;
|
||||
char *ts;
|
||||
|
||||
if (!r->log || !((m = find_mtag(r->log->mtags, "time"))) || !m->value)
|
||||
return 0;
|
||||
ts = m->value;
|
||||
|
||||
if (!BadPtr(batchid))
|
||||
{
|
||||
mtags = safe_alloc(sizeof(MessageTag));
|
||||
mtags->name = strdup("batch");
|
||||
mtags->value = strdup(batchid);
|
||||
}
|
||||
|
||||
sendto_one(client, mtags, ":%s CHATHISTORY TARGETS %s %s",
|
||||
me.name, r->object, ts);
|
||||
|
||||
if (mtags)
|
||||
free_message_tags(mtags);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void chathistory_targets(Client *client, HistoryFilter *filter, int limit)
|
||||
{
|
||||
Membership *mp;
|
||||
HistoryResult *r;
|
||||
char batch[BATCHLEN+1];
|
||||
int sent = 0;
|
||||
|
||||
batch[0] = '\0';
|
||||
if (HasCapability(client, "batch"))
|
||||
{
|
||||
/* Start a new batch */
|
||||
generate_batch_id(batch);
|
||||
sendto_one(client, NULL, ":%s BATCH +%s draft/chathistory-targets", me.name, batch);
|
||||
}
|
||||
|
||||
filter->cmd = HFC_BEFORE;
|
||||
if (strcmp(filter->timestamp_a, filter->timestamp_b) < 0)
|
||||
{
|
||||
/* Swap if needed */
|
||||
char *swap = filter->timestamp_a;
|
||||
filter->timestamp_a = filter->timestamp_b;
|
||||
filter->timestamp_b = swap;
|
||||
}
|
||||
filter->limit = 1;
|
||||
|
||||
for (mp = client->user->channel; mp; mp = mp->next)
|
||||
{
|
||||
Channel *channel = mp->channel;
|
||||
r = history_request(channel->chname, filter);
|
||||
if (r->log && chathistory_targets_send_line(client, r, batch))
|
||||
{
|
||||
if (++sent >= limit)
|
||||
break; /* We are done */
|
||||
}
|
||||
free_history_result(r);
|
||||
r = NULL;
|
||||
}
|
||||
|
||||
/* End of batch */
|
||||
if (*batch)
|
||||
sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
|
||||
}
|
||||
|
||||
CMD_FUNC(cmd_chathistory)
|
||||
{
|
||||
HistoryFilter *filter = NULL;
|
||||
HistoryResult *r = NULL;
|
||||
Channel *channel;
|
||||
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
|
||||
/* This command is only for local users */
|
||||
if (!MyUser(client))
|
||||
return;
|
||||
|
||||
if ((parc < 5) || BadPtr(parv[4]))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS :Insufficient parameters", me.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasCapability(client, "server-time"))
|
||||
{
|
||||
sendnotice(client, "Your IRC client does not support the 'server-time' capability");
|
||||
sendnotice(client, "https://ircv3.net/specs/extensions/server-time");
|
||||
sendnotice(client, "History request refused.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(parv[1], "TARGETS"))
|
||||
{
|
||||
Membership *mp;
|
||||
int limit;
|
||||
|
||||
filter = safe_alloc(sizeof(HistoryFilter));
|
||||
/* Below this point, instead of 'return', use 'goto end' */
|
||||
|
||||
if (!chathistory_token(parv[2], "timestamp", &filter->timestamp_a))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_b))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx",
|
||||
me.name, parv[1], parv[4]);
|
||||
goto end;
|
||||
}
|
||||
limit = atoi(parv[4]);
|
||||
chathistory_targets(client, filter, limit);
|
||||
goto end;
|
||||
}
|
||||
|
||||
channel = find_channel(parv[2], NULL);
|
||||
if (!channel || !IsMember(client, channel) || !has_channel_mode(channel, 'H'))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_TARGET %s %s :Messages could not be retrieved",
|
||||
me.name, parv[1], parv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
filter = safe_alloc(sizeof(HistoryFilter));
|
||||
/* Below this point, instead of 'return', use 'goto end', which takes care of the freeing of 'filter' and 'history' */
|
||||
|
||||
if (!strcmp(parv[1], "BEFORE"))
|
||||
{
|
||||
filter->cmd = HFC_BEFORE;
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
|
||||
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
filter->limit = atoi(parv[4]);
|
||||
} else
|
||||
if (!strcmp(parv[1], "AFTER"))
|
||||
{
|
||||
filter->cmd = HFC_AFTER;
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
|
||||
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
filter->limit = atoi(parv[4]);
|
||||
} else
|
||||
if (!strcmp(parv[1], "LATEST"))
|
||||
{
|
||||
filter->cmd = HFC_LATEST;
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
|
||||
!chathistory_token(parv[3], "msgid", &filter->msgid_a) &&
|
||||
strcmp(parv[3], "*"))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx or *",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
filter->limit = atoi(parv[4]);
|
||||
} else
|
||||
if (!strcmp(parv[1], "AROUND"))
|
||||
{
|
||||
filter->cmd = HFC_AROUND;
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
|
||||
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
filter->limit = atoi(parv[4]);
|
||||
} else
|
||||
if (!strcmp(parv[1], "BETWEEN"))
|
||||
{
|
||||
filter->cmd = HFC_BETWEEN;
|
||||
if (BadPtr(parv[5]))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s :Insufficient parameters", parv[1], me.name);
|
||||
goto end;
|
||||
}
|
||||
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
|
||||
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
|
||||
me.name, parv[1], parv[3]);
|
||||
goto end;
|
||||
}
|
||||
if (!chathistory_token(parv[4], "timestamp", &filter->timestamp_b) &&
|
||||
!chathistory_token(parv[4], "msgid", &filter->msgid_b))
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
|
||||
me.name, parv[1], parv[4]);
|
||||
goto end;
|
||||
}
|
||||
filter->limit = atoi(parv[5]);
|
||||
} else {
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s :Invalid subcommand", me.name, parv[1]);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (filter->limit <= 0)
|
||||
{
|
||||
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %d :Specified limit is =<0",
|
||||
me.name, parv[1], filter->limit);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (filter->limit > CHATHISTORY_LIMIT)
|
||||
filter->limit = CHATHISTORY_LIMIT;
|
||||
|
||||
if ((r = history_request(channel->chname, filter)))
|
||||
history_send_result(client, r);
|
||||
|
||||
end:
|
||||
if (filter)
|
||||
free_history_filter(filter);
|
||||
if (r)
|
||||
free_history_result(r);
|
||||
}
|
@ -639,7 +639,7 @@ static int can_dcc(Client *client, char *target, Client *targetcli, char *filena
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (match_spamfilter(client, filename, SPAMF_DCC, target, 0, NULL))
|
||||
if (match_spamfilter(client, filename, SPAMF_DCC, "PRIVMSG", target, 0, NULL))
|
||||
return 0;
|
||||
|
||||
if ((fl = dcc_isforbidden(client, filename)))
|
||||
|
@ -72,6 +72,7 @@ void history_usage(Client *client)
|
||||
CMD_FUNC(cmd_history)
|
||||
{
|
||||
HistoryFilter filter;
|
||||
HistoryResult *r;
|
||||
Channel *channel;
|
||||
int lines = HISTORY_LINES_DEFAULT;
|
||||
|
||||
@ -115,12 +116,18 @@ CMD_FUNC(cmd_history)
|
||||
if (!HasCapability(client, "server-time"))
|
||||
{
|
||||
sendnotice(client, "Your IRC client does not support the 'server-time' capability");
|
||||
sendnotice(client, "https://ircv3.net/specs/extensions/server-time-3.2.html");
|
||||
sendnotice(client, "https://ircv3.net/specs/extensions/server-time");
|
||||
sendnotice(client, "History request refused.");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
filter.cmd = HFC_SIMPLE;
|
||||
filter.last_lines = lines;
|
||||
history_request(client, channel->chname, &filter);
|
||||
|
||||
if ((r = history_request(channel->chname, &filter)))
|
||||
{
|
||||
history_send_result(client, r);
|
||||
free_history_result(r);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@ ModuleHeader MOD_HEADER
|
||||
/* Forward declarations */
|
||||
int hbn_history_set_limit(char *object, int max_lines, long max_time);
|
||||
int hbn_history_add(char *object, MessageTag *mtags, char *line);
|
||||
int hbn_history_request(Client *client, char *object, HistoryFilter *filter);
|
||||
HistoryResult *hbn_history_request(char *object, HistoryFilter *filter);
|
||||
int hbn_history_destroy(char *object);
|
||||
|
||||
MOD_INIT()
|
||||
@ -30,7 +30,6 @@ MOD_INIT()
|
||||
HistoryBackendInfo hbi;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1);
|
||||
|
||||
memset(&hbi, 0, sizeof(hbi));
|
||||
hbi.name = "mem";
|
||||
@ -59,9 +58,9 @@ int hbn_history_add(char *object, MessageTag *mtags, char *line)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hbn_history_request(Client *client, char *object, HistoryFilter *filter)
|
||||
HistoryResult *hbn_history_request(char *object, HistoryFilter *filter)
|
||||
{
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hbn_history_set_limit(char *object, int max_lines, long max_time)
|
||||
|
@ -190,16 +190,6 @@ static void ident_lookup_receive(int fd, int revents, void *userdata)
|
||||
return;
|
||||
}
|
||||
|
||||
void skip_whitespace(char **p)
|
||||
{
|
||||
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
|
||||
}
|
||||
|
||||
void read_until(char **p, char *stopchars)
|
||||
{
|
||||
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
|
||||
}
|
||||
|
||||
static char *ident_lookup_parse(Client *client, char *buf)
|
||||
{
|
||||
/* <port> , <port> : USERID : <OSTYPE>: <username>
|
||||
|
@ -166,25 +166,16 @@ CMD_FUNC(cmd_invite)
|
||||
return;
|
||||
}
|
||||
|
||||
if (MyConnect(client))
|
||||
if (MyUser(client))
|
||||
{
|
||||
if (target_limit_exceeded(client, target, target->name))
|
||||
return;
|
||||
|
||||
if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL))
|
||||
if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL) &&
|
||||
flood_limit_exceeded(client, FLD_INVITE))
|
||||
{
|
||||
if ((client->user->flood.invite_t + INVITE_PERIOD) <= timeofday)
|
||||
{
|
||||
client->user->flood.invite_c = 0;
|
||||
client->user->flood.invite_t = timeofday;
|
||||
}
|
||||
if (client->user->flood.invite_c <= INVITE_COUNT)
|
||||
client->user->flood.invite_c++;
|
||||
if (client->user->flood.invite_c > INVITE_COUNT)
|
||||
{
|
||||
sendnumeric(client, RPL_TRYAGAIN, "INVITE");
|
||||
return;
|
||||
}
|
||||
sendnumeric(client, RPL_TRYAGAIN, "INVITE");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!override)
|
||||
|
@ -23,10 +23,6 @@
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
/* Default settings for set::anti-flood::join-flood block: */
|
||||
#define JOINTHROTTLE_DEFAULT_COUNT 3
|
||||
#define JOINTHROTTLE_DEFAULT_TIME 90
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"jointhrottle",
|
||||
@ -40,11 +36,6 @@ ModuleInfo *ModInfo = NULL;
|
||||
|
||||
ModDataInfo *jointhrottle_md; /* Module Data structure which we acquire */
|
||||
|
||||
struct {
|
||||
unsigned short num;
|
||||
unsigned short t;
|
||||
} cfg;
|
||||
|
||||
typedef struct JoinFlood JoinFlood;
|
||||
|
||||
struct JoinFlood {
|
||||
@ -55,8 +46,6 @@ struct JoinFlood {
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
int jointhrottle_config_test(ConfigFile *, ConfigEntry *, int, int *);
|
||||
int jointhrottle_config_run(ConfigFile *, ConfigEntry *, int);
|
||||
void jointhrottle_md_free(ModData *m);
|
||||
int jointhrottle_can_join(Client *client, Channel *channel, char *key, char *parv[]);
|
||||
int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]);
|
||||
@ -67,7 +56,6 @@ JoinFlood *jointhrottle_addentry(Client *client, Channel *channel);
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, jointhrottle_config_test);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -89,12 +77,9 @@ MOD_INIT()
|
||||
if (!jointhrottle_md)
|
||||
abort();
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, jointhrottle_config_run);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, jointhrottle_can_join);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, jointhrottle_local_join);
|
||||
|
||||
cfg.t = JOINTHROTTLE_DEFAULT_TIME;
|
||||
cfg.num = JOINTHROTTLE_DEFAULT_COUNT;
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -109,53 +94,10 @@ MOD_UNLOAD()
|
||||
return MOD_FAILED;
|
||||
}
|
||||
|
||||
int jointhrottle_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
int cnt=0, period=0;
|
||||
|
||||
if (type != CONFIG_SET_ANTI_FLOOD)
|
||||
return 0;
|
||||
|
||||
if (strcmp(ce->ce_varname, "join-flood"))
|
||||
return 0; /* otherwise not interested */
|
||||
|
||||
if (!ce->ce_vardata || !config_parse_flood(ce->ce_vardata, &cnt, &period) ||
|
||||
(cnt < 1) || (cnt > 255) || (period < 5))
|
||||
{
|
||||
config_error("%s:%i: set::anti-flood::join-flood. Syntax is '<count>:<period>' (eg 3:90), "
|
||||
"count should be 1-255, period should be greater than 4",
|
||||
ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
|
||||
errors++;
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int jointhrottle_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
int cnt=0, period=0;
|
||||
|
||||
if (type != CONFIG_SET_ANTI_FLOOD)
|
||||
return 0;
|
||||
|
||||
if (strcmp(ce->ce_varname, "join-flood"))
|
||||
return 0; /* otherwise not interested */
|
||||
|
||||
config_parse_flood(ce->ce_vardata, &cnt, &period);
|
||||
|
||||
cfg.t = period;
|
||||
cfg.num = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isjthrottled(Client *client, Channel *channel)
|
||||
{
|
||||
JoinFlood *e;
|
||||
int num = cfg.num;
|
||||
int t = cfg.t;
|
||||
FloodSettings *settings = get_floodsettings_for_user(client, FLD_JOIN);
|
||||
|
||||
if (!MyUser(client))
|
||||
return 0;
|
||||
@ -171,7 +113,8 @@ static int isjthrottled(Client *client, Channel *channel)
|
||||
/* Ok... now the actual check:
|
||||
* if ([timer valid] && [one more join would exceed num])
|
||||
*/
|
||||
if (((TStime() - e->firstjoin) < t) && (e->numjoins == num))
|
||||
if (((TStime() - e->firstjoin) < settings->period[FLD_JOIN]) &&
|
||||
(e->numjoins >= settings->limit[FLD_JOIN]))
|
||||
return 1; /* Throttled */
|
||||
|
||||
return 0;
|
||||
@ -196,7 +139,7 @@ static void jointhrottle_increase_usercounter(Client *client, Channel *channel)
|
||||
e->firstjoin = TStime();
|
||||
e->numjoins = 1;
|
||||
} else
|
||||
if ((TStime() - e->firstjoin) < cfg.t) /* still valid? */
|
||||
if ((TStime() - e->firstjoin) < iConf.floodsettings->period[FLD_JOIN]) /* still valid? */
|
||||
{
|
||||
e->numjoins++;
|
||||
} else {
|
||||
@ -266,11 +209,12 @@ EVENT(jointhrottle_cleanup_structs)
|
||||
{
|
||||
jf_next = jf->next;
|
||||
|
||||
if (jf->firstjoin + cfg.t > TStime())
|
||||
if (jf->firstjoin + iConf.floodsettings->period[FLD_JOIN] > TStime())
|
||||
continue; /* still valid entry */
|
||||
#ifdef DEBUGMODE
|
||||
ircd_log(LOG_ERROR, "jointhrottle_cleanup_structs(): freeing %s/%s (%ld[%ld], %d)",
|
||||
client->name, jf->chname, jf->firstjoin, (long)(TStime() - jf->firstjoin), cfg.t);
|
||||
ircd_log(LOG_ERROR, "jointhrottle_cleanup_structs(): freeing %s/%s (%ld[%ld], %ld)",
|
||||
client->name, jf->chname, jf->firstjoin, (long)(TStime() - jf->firstjoin),
|
||||
iConf.floodsettings->period[FLD_JOIN]);
|
||||
#endif
|
||||
if (moddata_local_client(client, jointhrottle_md).ptr == jf)
|
||||
{
|
||||
|
@ -132,21 +132,12 @@ CMD_FUNC(cmd_knock)
|
||||
if (i == HOOK_DENY)
|
||||
return;
|
||||
|
||||
if (MyUser(client) && !ValidatePermissionsForPath("immune:knock-flood",client,NULL,NULL,NULL))
|
||||
if (MyUser(client) &&
|
||||
!ValidatePermissionsForPath("immune:knock-flood",client,NULL,NULL,NULL) &&
|
||||
flood_limit_exceeded(client, FLD_KNOCK))
|
||||
{
|
||||
if ((client->user->flood.knock_t + KNOCK_PERIOD) <= timeofday)
|
||||
{
|
||||
client->user->flood.knock_c = 0;
|
||||
client->user->flood.knock_t = timeofday;
|
||||
}
|
||||
if (client->user->flood.knock_c <= KNOCK_COUNT)
|
||||
client->user->flood.knock_c++;
|
||||
if (client->user->flood.knock_c > KNOCK_COUNT)
|
||||
{
|
||||
sendnumeric(client, ERR_CANNOTKNOCK, parv[1],
|
||||
"You are KNOCK flooding");
|
||||
return;
|
||||
}
|
||||
sendnumeric(client, ERR_CANNOTKNOCK, parv[1], "You are KNOCK flooding");
|
||||
return;
|
||||
}
|
||||
|
||||
new_message(&me, NULL, &mtags);
|
||||
|
@ -31,14 +31,6 @@ ModuleHeader MOD_HEADER
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
/** The length of a standard 'msgid' tag (note that special
|
||||
* msgid tags will be longer).
|
||||
* The 22 alphanumeric characters provide slightly more
|
||||
* than 128 bits of randomness (62^22 > 2^128).
|
||||
* See mtag_add_or_inherit_msgid() for more information.
|
||||
*/
|
||||
#define MSGIDLEN 22
|
||||
|
||||
/* Variables */
|
||||
long CAP_ACCOUNT_TAG = 0L;
|
||||
|
||||
|
@ -117,8 +117,14 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm
|
||||
}
|
||||
|
||||
// Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric?
|
||||
if (MyUser(client) && match_spamfilter(client, *msgtext, (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG), target->name, 0, NULL))
|
||||
return 0;
|
||||
if (MyUser(client))
|
||||
{
|
||||
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG);
|
||||
char *cmd = sendtype_to_cmd(sendtype);
|
||||
|
||||
if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL))
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = HOOK_CONTINUE;
|
||||
for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next)
|
||||
@ -379,8 +385,13 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[],
|
||||
if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7))
|
||||
sendflags |= SKIP_CTCP;
|
||||
|
||||
if (MyUser(client) && match_spamfilter(client, text, (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG), channel->chname, 0, NULL))
|
||||
return;
|
||||
if (MyUser(client))
|
||||
{
|
||||
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG);
|
||||
|
||||
if (match_spamfilter(client, text, spamtype, cmd, channel->chname, 0, NULL))
|
||||
return;
|
||||
}
|
||||
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
|
||||
|
@ -1293,7 +1293,7 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what,
|
||||
if (handler->is_ok(client, channel, mode, param, EXCHK_PARAM, what) == FALSE)
|
||||
return paracnt; /* rejected by is_ok */
|
||||
|
||||
morphed = handler->conv_param(param, client);
|
||||
morphed = handler->conv_param(param, client, channel);
|
||||
if (!morphed)
|
||||
return paracnt; /* rejected by conv_param */
|
||||
|
||||
@ -1303,12 +1303,12 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what,
|
||||
char *now, *requested;
|
||||
char flag = handler->flag;
|
||||
now = cm_getparameter(channel, flag);
|
||||
requested = handler->conv_param(param, client);
|
||||
requested = handler->conv_param(param, client, channel);
|
||||
if (now && requested && !strcmp(now, requested))
|
||||
return paracnt; /* ignore... */
|
||||
}
|
||||
ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, "+%c%s",
|
||||
handler->flag, handler->conv_param(param, client));
|
||||
handler->flag, handler->conv_param(param, client, channel));
|
||||
(*pcount)++;
|
||||
param = morphed; /* set param to converted parameter. */
|
||||
}
|
||||
|
@ -214,11 +214,10 @@ CMD_FUNC(cmd_nick_remote)
|
||||
sendto_snomask(SNO_FNICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s",
|
||||
client->name, client->user->username, client->user->realhost, nick);
|
||||
|
||||
RunHook2(HOOKTYPE_REMOTE_NICKCHANGE, client, nick);
|
||||
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, client, mtags, nick);
|
||||
client->lastnick = lastnick ? lastnick : TStime();
|
||||
add_history(client, 1);
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld",
|
||||
client->id, nick, (long long)client->lastnick);
|
||||
sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick);
|
||||
@ -275,19 +274,6 @@ CMD_FUNC(cmd_nick_local)
|
||||
return;
|
||||
}
|
||||
|
||||
/* set::anti-flood::nick-flood */
|
||||
if (client->user && !ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL))
|
||||
{
|
||||
if ((client->user->flood.nick_c >= NICK_COUNT) &&
|
||||
(TStime() - client->user->flood.nick_t < NICK_PERIOD))
|
||||
{
|
||||
/* Throttle... */
|
||||
sendnumeric(client, ERR_NCHANGETOOFAST, nick,
|
||||
(int)(NICK_PERIOD - (TStime() - client->user->flood.nick_t)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for collisions / in use */
|
||||
if (!strcasecmp("ircd", nick) || !strcasecmp("irc", nick))
|
||||
{
|
||||
@ -299,7 +285,7 @@ CMD_FUNC(cmd_nick_local)
|
||||
{
|
||||
/* Local client changing nick: check spamfilter */
|
||||
spamfilter_build_user_string(spamfilter_user, nick, client);
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL))
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "NICK", NULL, 0, NULL))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -322,6 +308,16 @@ CMD_FUNC(cmd_nick_local)
|
||||
/* fallthrough for ircops that have sufficient privileges */
|
||||
}
|
||||
|
||||
/* set::anti-flood::nick-flood */
|
||||
if (client->user &&
|
||||
!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL) &&
|
||||
flood_limit_exceeded(client, FLD_NICK))
|
||||
{
|
||||
/* Throttle... */
|
||||
sendnumeric(client, ERR_NCHANGETOOFAST, nick);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL))
|
||||
cptr->local->since += 3; /* Nick-flood prot. -Donwulff */
|
||||
|
||||
@ -440,20 +436,13 @@ CMD_FUNC(cmd_nick_local)
|
||||
}
|
||||
}
|
||||
|
||||
if (TStime() - client->user->flood.nick_t >= NICK_PERIOD)
|
||||
{
|
||||
client->user->flood.nick_t = TStime();
|
||||
client->user->flood.nick_c = 1;
|
||||
} else
|
||||
client->user->flood.nick_c++;
|
||||
|
||||
sendto_snomask(SNO_NICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s",
|
||||
client->name, client->user->username, client->user->realhost, nick);
|
||||
|
||||
RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, client, nick);
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, client, mtags, nick);
|
||||
client->lastnick = TStime();
|
||||
add_history(client, 1);
|
||||
new_message(client, recv_mtags, &mtags);
|
||||
sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld",
|
||||
client->id, nick, (long long)client->lastnick);
|
||||
sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick);
|
||||
@ -681,7 +670,12 @@ nickkill2done:
|
||||
return;
|
||||
|
||||
if (client->user->svid[0] != '0')
|
||||
{
|
||||
user_account_login(recv_mtags, client);
|
||||
/* no need to check for kill upon user_account_login() here
|
||||
* since that can only happen for local users.
|
||||
*/
|
||||
}
|
||||
|
||||
RunHook(HOOKTYPE_REMOTE_CONNECT, client);
|
||||
|
||||
@ -721,7 +715,7 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
|
||||
userbad[USERLEN * 2 + 1], *ubad = userbad, noident = 0;
|
||||
int i, xx;
|
||||
Hook *h;
|
||||
ClientUser *user = client->user;
|
||||
User *user = client->user;
|
||||
char *tkllayer[9] = {
|
||||
me.name, /*0 server.name */
|
||||
"+", /*1 +|- */
|
||||
@ -879,7 +873,7 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
|
||||
find_shun(client);
|
||||
|
||||
spamfilter_build_user_string(spamfilter_user, client->name, client);
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, &savetkl))
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl))
|
||||
{
|
||||
if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) ||
|
||||
(savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN)))
|
||||
|
@ -89,7 +89,7 @@ CMD_FUNC(cmd_part)
|
||||
}
|
||||
if (commentx)
|
||||
{
|
||||
if (match_spamfilter(client, commentx, SPAMF_PART, parv[1], 0, NULL))
|
||||
if (match_spamfilter(client, commentx, SPAMF_PART, "PART", parv[1], 0, NULL))
|
||||
commentx = NULL;
|
||||
if (IsDead(client))
|
||||
return;
|
||||
|
@ -82,7 +82,7 @@ CMD_FUNC(cmd_quit)
|
||||
return;
|
||||
}
|
||||
|
||||
if (match_spamfilter(client, comment, SPAMF_QUIT, NULL, 0, NULL))
|
||||
if (match_spamfilter(client, comment, SPAMF_QUIT, "QUIT", NULL, 0, NULL))
|
||||
{
|
||||
comment = client->name;
|
||||
if (IsDead(client))
|
||||
|
116
src/modules/reply-tag.c
Normal file
116
src/modules/reply-tag.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* IRC - Internet Relay Chat, src/modules/reply-tag.c
|
||||
* (C) 2021 Syzop & The UnrealIRCd Team
|
||||
*
|
||||
* See file AUTHORS in IRC package for additional names of
|
||||
* the programmers.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* This implements https://ircv3.net/specs/client-tags/reply */
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"reply-tag",
|
||||
"5.0",
|
||||
"+reply client tag",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-5",
|
||||
};
|
||||
|
||||
int replytag_mtag_is_ok(Client *client, char *name, char *value);
|
||||
void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature);
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MessageTagHandlerInfo mtag;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
#if 0
|
||||
memset(&mtag, 0, sizeof(mtag));
|
||||
mtag.name = "+reply";
|
||||
mtag.is_ok = replytag_mtag_is_ok;
|
||||
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
|
||||
MessageTagHandlerAdd(modinfo->handle, &mtag);
|
||||
#endif
|
||||
|
||||
memset(&mtag, 0, sizeof(mtag));
|
||||
mtag.name = "+draft/reply";
|
||||
mtag.is_ok = replytag_mtag_is_ok;
|
||||
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
|
||||
MessageTagHandlerAdd(modinfo->handle, &mtag);
|
||||
|
||||
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_replytag);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
/** This function verifies if the client sending the mtag is permitted to do so.
|
||||
*/
|
||||
int replytag_mtag_is_ok(Client *client, char *name, char *value)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* Require a non-empty parameter */
|
||||
if (BadPtr(value))
|
||||
return 0;
|
||||
|
||||
/* All our PRIVMSG/NOTICE msgid's are of this size: */
|
||||
if (strlen(value) != MSGIDLEN)
|
||||
return 0;
|
||||
|
||||
for (p = value; *p; p++)
|
||||
if (!isalnum(*p))
|
||||
return 0; /* non-alphanumeric */
|
||||
|
||||
return 1; /* OK */
|
||||
}
|
||||
|
||||
void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature)
|
||||
{
|
||||
MessageTag *m;
|
||||
|
||||
if (IsUser(client))
|
||||
{
|
||||
#if 0
|
||||
m = find_mtag(recv_mtags, "+reply");
|
||||
if (m)
|
||||
{
|
||||
m = duplicate_mtag(m);
|
||||
AddListItem(m, *mtag_list);
|
||||
}
|
||||
#endif
|
||||
m = find_mtag(recv_mtags, "+draft/reply");
|
||||
if (m)
|
||||
{
|
||||
m = duplicate_mtag(m);
|
||||
AddListItem(m, *mtag_list);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,9 +19,12 @@
|
||||
|
||||
#define REPUTATION_VERSION "1.2"
|
||||
|
||||
/* Change to #define to benchmark. Note that this will add random
|
||||
* reputation entries so should never be used on production servers!!!
|
||||
*/
|
||||
#undef BENCHMARK
|
||||
#undef TEST
|
||||
|
||||
#undef BENCHMARK
|
||||
/* Benchmark results (2GHz Xeon Skylake, compiled with -O2, Linux):
|
||||
* 10k random IP's with various expire times:
|
||||
* - load db: 23 ms
|
||||
@ -76,12 +79,30 @@ ModuleHeader MOD_HEADER
|
||||
|
||||
#define Reputation(client) moddata_client(client, reputation_md).l
|
||||
|
||||
#define WARN_WRITE_ERROR(fname) \
|
||||
do { \
|
||||
sendto_realops_and_log("[reputation] Error writing to temporary database file " \
|
||||
"'%s': %s (DATABASE NOT SAVED)", \
|
||||
fname, unrealdb_get_error_string()); \
|
||||
} while(0)
|
||||
|
||||
#define W_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
WARN_WRITE_ERROR(tmpfname); \
|
||||
unrealdb_close(db); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Definitions (structs, etc.) */
|
||||
|
||||
struct cfgstruct {
|
||||
int expire_score[MAXEXPIRES];
|
||||
long expire_time[MAXEXPIRES];
|
||||
char *database;
|
||||
char *db_secret;
|
||||
};
|
||||
|
||||
typedef struct ReputationEntry ReputationEntry;
|
||||
@ -97,6 +118,7 @@ struct ReputationEntry {
|
||||
/* Global variables */
|
||||
|
||||
static struct cfgstruct cfg; /**< Current configuration */
|
||||
static struct cfgstruct test; /**< Testing configuration (not active yet) */
|
||||
long reputation_starttime = 0;
|
||||
long reputation_writtentime = 0;
|
||||
|
||||
@ -111,7 +133,8 @@ ModDataInfo *reputation_md; /* Module Data structure which we acquire */
|
||||
void reputation_md_free(ModData *m);
|
||||
char *reputation_md_serialize(ModData *m);
|
||||
void reputation_md_unserialize(char *str, ModData *m);
|
||||
void config_setdefaults(void);
|
||||
void reputation_config_setdefaults(struct cfgstruct *cfg);
|
||||
void reputation_free_config(struct cfgstruct *cfg);
|
||||
CMD_FUNC(reputation_cmd);
|
||||
CMD_FUNC(reputationunperm);
|
||||
int reputation_whois(Client *client, Client *target);
|
||||
@ -122,18 +145,21 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
int reputation_config_posttest(int *errs);
|
||||
static uint64_t hash_reputation_entry(char *ip);
|
||||
ReputationEntry *find_reputation_entry(char *ip);
|
||||
void add_reputation_entry(ReputationEntry *e);
|
||||
EVENT(delete_old_records);
|
||||
EVENT(add_scores);
|
||||
EVENT(save_db_evt);
|
||||
void load_db(void);
|
||||
void save_db(void);
|
||||
EVENT(reputation_save_db_evt);
|
||||
int reputation_load_db(void);
|
||||
int reputation_save_db(void);
|
||||
int reputation_starttime_callback(void);
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
memcpy(&ModInf, modinfo, modinfo->size);
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
memset(&test, 0, sizeof(cfg));
|
||||
reputation_config_setdefaults(&test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, reputation_config_test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, reputation_config_posttest);
|
||||
CallbackAddEx(modinfo->handle, CALLBACKTYPE_REPUTATION_STARTTIME, reputation_starttime_callback);
|
||||
@ -146,6 +172,7 @@ MOD_INIT()
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1);
|
||||
|
||||
memset(&ReputationHashTable, 0, sizeof(ReputationHashTable));
|
||||
siphash_generate_key(siphashkey_reputation);
|
||||
|
||||
@ -160,7 +187,7 @@ MOD_INIT()
|
||||
if (!reputation_md)
|
||||
abort();
|
||||
|
||||
config_setdefaults();
|
||||
reputation_config_setdefaults(&cfg);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, reputation_config_run);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, reputation_whois);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, reputation_set_on_connect);
|
||||
@ -172,43 +199,79 @@ MOD_INIT()
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef BENCHMARK
|
||||
void reputation_benchmark(int entries)
|
||||
{
|
||||
char ip[64];
|
||||
int i;
|
||||
ReputationEntry *e;
|
||||
|
||||
srand(1234); // fixed seed
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
{
|
||||
ReputationEntry *e = safe_alloc(sizeof(ReputationEntry) + 64);
|
||||
snprintf(e->ip, 63, "%d.%d.%d.%d", rand()%255, rand()%255, rand()%255, rand()%255);
|
||||
e->score = rand()%255 + 1;
|
||||
e->last_seen = TStime();
|
||||
if (find_reputation_entry(e->ip))
|
||||
{
|
||||
safe_free(e);
|
||||
continue;
|
||||
}
|
||||
add_reputation_entry(e);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MOD_LOAD()
|
||||
{
|
||||
load_db();
|
||||
reputation_load_db();
|
||||
if (reputation_starttime == 0)
|
||||
reputation_starttime = TStime();
|
||||
EventAdd(ModInf.handle, "delete_old_records", delete_old_records, NULL, DELETE_OLD_EVERY*1000, 0);
|
||||
EventAdd(ModInf.handle, "add_scores", add_scores, NULL, BUMP_SCORE_EVERY*1000, 0);
|
||||
EventAdd(ModInf.handle, "save_db", save_db_evt, NULL, SAVE_DB_EVERY*1000, 0);
|
||||
EventAdd(ModInf.handle, "reputation_save_db", reputation_save_db_evt, NULL, SAVE_DB_EVERY*1000, 0);
|
||||
#ifdef BENCHMARK
|
||||
reputation_benchmark(10000);
|
||||
#endif
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
save_db();
|
||||
if (loop.ircd_terminating)
|
||||
reputation_save_db();
|
||||
reputation_free_config(&test);
|
||||
reputation_free_config(&cfg);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
void config_setdefaults(void)
|
||||
void reputation_config_setdefaults(struct cfgstruct *cfg)
|
||||
{
|
||||
/* data/reputation.db */
|
||||
safe_strdup(cfg.database, "reputation.db");
|
||||
convert_to_absolute_path(&cfg.database, PERMDATADIR);
|
||||
safe_strdup(cfg->database, "reputation.db");
|
||||
convert_to_absolute_path(&cfg->database, PERMDATADIR);
|
||||
|
||||
/* EXPIRES the following entries if the IP does appear for some time: */
|
||||
/* <=2 points after 1 hour */
|
||||
cfg.expire_score[0] = 2;
|
||||
cfg->expire_score[0] = 2;
|
||||
#ifndef TEST
|
||||
cfg.expire_time[0] = 3600;
|
||||
cfg->expire_time[0] = 3600;
|
||||
#else
|
||||
cfg.expire_time[0] = 36;
|
||||
cfg->expire_time[0] = 36;
|
||||
#endif
|
||||
/* <=6 points after 7 days */
|
||||
cfg.expire_score[1] = 6;
|
||||
cfg.expire_time[1] = 86400*7;
|
||||
cfg->expire_score[1] = 6;
|
||||
cfg->expire_time[1] = 86400*7;
|
||||
/* ANY result that has not been seen for 30 days */
|
||||
cfg.expire_score[2] = -1;
|
||||
cfg.expire_time[2] = 86400*30;
|
||||
cfg->expire_score[2] = -1;
|
||||
cfg->expire_time[2] = 86400*30;
|
||||
}
|
||||
|
||||
void reputation_free_config(struct cfgstruct *cfg)
|
||||
{
|
||||
safe_free(cfg->database);
|
||||
safe_free(cfg->db_secret);
|
||||
}
|
||||
|
||||
int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
@ -218,11 +281,11 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
|
||||
if (type != CONFIG_SET)
|
||||
return 0;
|
||||
|
||||
|
||||
/* We are only interrested in set::reputation.. */
|
||||
if (!ce || strcmp(ce->ce_varname, "reputation"))
|
||||
return 0;
|
||||
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!cep->ce_vardata)
|
||||
@ -235,6 +298,18 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
{
|
||||
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
|
||||
safe_strdup(test.database, cep->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
{
|
||||
char *err;
|
||||
if ((err = unrealdb_test_secret(cep->ce_vardata)))
|
||||
{
|
||||
config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
safe_strdup(test.db_secret, cep->ce_vardata);
|
||||
} else
|
||||
{
|
||||
config_error("%s:%i: unknown directive set::reputation::%s",
|
||||
@ -243,7 +318,7 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
@ -254,16 +329,20 @@ int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
|
||||
if (type != CONFIG_SET)
|
||||
return 0;
|
||||
|
||||
|
||||
/* We are only interrested in set::reputation.. */
|
||||
if (!ce || strcmp(ce->ce_varname, "reputation"))
|
||||
return 0;
|
||||
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
{
|
||||
safe_strdup(cfg.database, cep->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
{
|
||||
safe_strdup(cfg.db_secret, cep->ce_vardata);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@ -272,13 +351,20 @@ int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
int reputation_config_posttest(int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
char *errstr;
|
||||
|
||||
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
|
||||
{
|
||||
config_error("[reputation] %s", errstr);
|
||||
errors++;
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
/** Parse database header and set variables appropriately */
|
||||
int parse_db_header(char *buf)
|
||||
int parse_db_header_old(char *buf)
|
||||
{
|
||||
char *header=NULL, *version=NULL, *starttime=NULL, *writtentime=NULL;
|
||||
char *p=NULL;
|
||||
@ -308,7 +394,7 @@ int parse_db_header(char *buf)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void load_db(void)
|
||||
void reputation_load_db_old(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char buf[512], *p;
|
||||
@ -324,7 +410,7 @@ void load_db(void)
|
||||
config_warn("WARNING: Could not open/read database '%s': %s", cfg.database, strerror(ERRNO));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (fgets(buf, 512, fd) == NULL)
|
||||
{
|
||||
@ -332,7 +418,7 @@ void load_db(void)
|
||||
fclose(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Header contains: REPDB <version> <starttime> <writtentime>
|
||||
* Where:
|
||||
* REPDB: Literally the string "REPDB".
|
||||
@ -341,7 +427,7 @@ void load_db(void)
|
||||
* in other words: when this module was first loaded, ever.
|
||||
* <writtentime> Time that the database was last written.
|
||||
*/
|
||||
if (!parse_db_header(buf))
|
||||
if (!parse_db_header_old(buf))
|
||||
{
|
||||
config_error("WARNING: Cannot load database %s. Error reading header. "
|
||||
"Database corrupt? Or are you downgrading from a newer "
|
||||
@ -355,7 +441,7 @@ void load_db(void)
|
||||
{
|
||||
char *ip = NULL, *score = NULL, *last_seen = NULL;
|
||||
ReputationEntry *e;
|
||||
|
||||
|
||||
stripcrlf(buf);
|
||||
/* Format: <ip> <score> <last seen> */
|
||||
ip = strtoken(&p, buf, " ");
|
||||
@ -367,12 +453,12 @@ void load_db(void)
|
||||
last_seen = strtoken(&p, NULL, " ");
|
||||
if (!last_seen)
|
||||
continue;
|
||||
|
||||
|
||||
e = safe_alloc(sizeof(ReputationEntry)+strlen(ip));
|
||||
strcpy(e->ip, ip); /* safe, see alloc above */
|
||||
e->score = atoi(score);
|
||||
e->last_seen = atol(last_seen);
|
||||
|
||||
|
||||
add_reputation_entry(e);
|
||||
}
|
||||
fclose(fd);
|
||||
@ -384,7 +470,130 @@ void load_db(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void save_db(void)
|
||||
#define R_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_warn("[reputation] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
|
||||
unrealdb_close(db); \
|
||||
safe_free(ip); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int reputation_load_db_new(UnrealDB *db)
|
||||
{
|
||||
uint64_t l_db_version = 0;
|
||||
uint64_t l_starttime = 0;
|
||||
uint64_t l_writtentime = 0;
|
||||
uint64_t count = 0;
|
||||
uint64_t i;
|
||||
char *ip = NULL;
|
||||
uint16_t score;
|
||||
uint64_t last_seen;
|
||||
ReputationEntry *e;
|
||||
#ifdef BENCHMARK
|
||||
struct timeval tv_alpha, tv_beta;
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
R_SAFE(unrealdb_read_int64(db, &l_db_version)); /* reputation db version */
|
||||
if (l_db_version > 2)
|
||||
{
|
||||
config_error("[reputation] Reputation DB is of a newer version (%ld) than supported by us (%ld). "
|
||||
"Did you perhaps downgrade your UnrealIRCd?",
|
||||
(long)l_db_version, (long)2);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
R_SAFE(unrealdb_read_int64(db, &l_starttime)); /* starttime of data gathering */
|
||||
R_SAFE(unrealdb_read_int64(db, &l_writtentime)); /* current time */
|
||||
R_SAFE(unrealdb_read_int64(db, &count)); /* number of entries */
|
||||
|
||||
reputation_starttime = l_starttime;
|
||||
reputation_writtentime = l_writtentime;
|
||||
|
||||
for (i=0; i < count; i++)
|
||||
{
|
||||
R_SAFE(unrealdb_read_str(db, &ip));
|
||||
R_SAFE(unrealdb_read_int16(db, &score));
|
||||
R_SAFE(unrealdb_read_int64(db, &last_seen));
|
||||
|
||||
e = safe_alloc(sizeof(ReputationEntry)+strlen(ip));
|
||||
strcpy(e->ip, ip); /* safe, see alloc above */
|
||||
e->score = score;
|
||||
e->last_seen = last_seen;
|
||||
add_reputation_entry(e);
|
||||
safe_free(ip);
|
||||
}
|
||||
unrealdb_close(db);
|
||||
#ifdef BENCHMARK
|
||||
gettimeofday(&tv_beta, NULL);
|
||||
ircd_log(LOG_ERROR, "Reputation benchmark: LOAD DB: %lld microseconds",
|
||||
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Load the reputation DB.
|
||||
* Strategy is:
|
||||
* 1) Check for the old header "REPDB 1", if so then call reputation_load_db_old().
|
||||
* 2) Otherwise, open with unrealdb routine
|
||||
* 3) If that fails due to a password provided but the file is unrealdb without password
|
||||
* then fallback to open without a password (so users can easily upgrade to encrypted)
|
||||
*/
|
||||
int reputation_load_db(void)
|
||||
{
|
||||
FILE *fd;
|
||||
UnrealDB *db;
|
||||
char buf[512];
|
||||
|
||||
fd = fopen(cfg.database, "r");
|
||||
if (!fd)
|
||||
{
|
||||
/* Database does not exist. Could be first boot */
|
||||
config_warn("[reputation] No database present at '%s', will start a new one", cfg.database);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*buf = '\0';
|
||||
if (fgets(buf, sizeof(buf), fd) == NULL)
|
||||
{
|
||||
fclose(fd);
|
||||
config_warn("[reputation] Database at '%s' is 0 bytes", cfg.database);
|
||||
return 1;
|
||||
}
|
||||
fclose(fd);
|
||||
if (!strncmp(buf, "REPDB 1 ", 8))
|
||||
{
|
||||
reputation_load_db_old();
|
||||
return 1; /* not so good to always pretend succes */
|
||||
}
|
||||
|
||||
/* Otherwise, it is an unrealdb, crypted or not */
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
|
||||
{
|
||||
/* Database does not exist. Could be first boot */
|
||||
config_warn("[reputation] No database present at '%s', will start a new one", cfg.database);
|
||||
return 1;
|
||||
} else
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
|
||||
{
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
|
||||
}
|
||||
if (!db)
|
||||
{
|
||||
config_error("[reputation] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return reputation_load_db_new(db);
|
||||
}
|
||||
|
||||
int reputation_save_db_old(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char tmpfname[512];
|
||||
@ -395,19 +604,15 @@ void save_db(void)
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
sendto_realops("REPUTATION IS RUNNING IN TEST MODE. SAVING DB'S...");
|
||||
#endif
|
||||
|
||||
/* We write to a temporary file. Only to rename it later if everything was ok */
|
||||
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
|
||||
|
||||
|
||||
fd = fopen(tmpfname, "w");
|
||||
if (!fd)
|
||||
{
|
||||
config_error("ERROR: Could not open/write database '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fprintf(fd, "REPDB 1 %lld %lld\n", (long long)reputation_starttime, (long long)TStime()) < 0)
|
||||
@ -422,7 +627,7 @@ void save_db(void)
|
||||
write_fail:
|
||||
config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
|
||||
fclose(fd);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,9 +635,9 @@ write_fail:
|
||||
if (fclose(fd) < 0)
|
||||
{
|
||||
config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Everything went fine. We rename our temporary file to the existing
|
||||
* DB file (will overwrite), which is more or less an atomic operation.
|
||||
*/
|
||||
@ -444,7 +649,7 @@ write_fail:
|
||||
{
|
||||
config_error("ERROR renaming '%s' to '%s': %s -- DATABASE *NOT* SAVED!!!",
|
||||
tmpfname, cfg.database, strerror(ERRNO));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
reputation_writtentime = TStime();
|
||||
@ -455,7 +660,91 @@ write_fail:
|
||||
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
|
||||
#endif
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int reputation_save_db(void)
|
||||
{
|
||||
UnrealDB *db;
|
||||
char tmpfname[512];
|
||||
int i;
|
||||
uint64_t count;
|
||||
ReputationEntry *e;
|
||||
#ifdef BENCHMARK
|
||||
struct timeval tv_alpha, tv_beta;
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
sendto_realops("REPUTATION IS RUNNING IN TEST MODE. SAVING DB'S...");
|
||||
#endif
|
||||
|
||||
/* Comment this out after one or more releases (means you cannot downgrade to <=5.0.9.1 anymore) */
|
||||
if (cfg.db_secret == NULL)
|
||||
return reputation_save_db_old();
|
||||
|
||||
/* We write to a temporary file. Only to rename it later if everything was ok */
|
||||
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
|
||||
|
||||
db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write header */
|
||||
W_SAFE(unrealdb_write_int64(db, 2)); /* reputation db version */
|
||||
W_SAFE(unrealdb_write_int64(db, reputation_starttime)); /* starttime of data gathering */
|
||||
W_SAFE(unrealdb_write_int64(db, TStime())); /* current time */
|
||||
|
||||
/* Count entries */
|
||||
count = 0;
|
||||
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
|
||||
for (e = ReputationHashTable[i]; e; e = e->next)
|
||||
count++;
|
||||
W_SAFE(unrealdb_write_int64(db, count)); /* Number of DB entries */
|
||||
|
||||
/* Now write the actual individual entries: */
|
||||
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
|
||||
{
|
||||
for (e = ReputationHashTable[i]; e; e = e->next)
|
||||
{
|
||||
W_SAFE(unrealdb_write_str(db, e->ip));
|
||||
W_SAFE(unrealdb_write_int16(db, e->score));
|
||||
W_SAFE(unrealdb_write_int64(db, e->last_seen));
|
||||
}
|
||||
}
|
||||
|
||||
if (!unrealdb_close(db))
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Everything went fine. We rename our temporary file to the existing
|
||||
* DB file (will overwrite), which is more or less an atomic operation.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
/* The rename operation cannot be atomic on Windows as it will cause a "file exists" error */
|
||||
unlink(cfg.database);
|
||||
#endif
|
||||
if (rename(tmpfname, cfg.database) < 0)
|
||||
{
|
||||
config_error("ERROR renaming '%s' to '%s': %s -- DATABASE *NOT* SAVED!!!",
|
||||
tmpfname, cfg.database, strerror(ERRNO));
|
||||
return 0;
|
||||
}
|
||||
|
||||
reputation_writtentime = TStime();
|
||||
|
||||
#ifdef BENCHMARK
|
||||
gettimeofday(&tv_beta, NULL);
|
||||
ircd_log(LOG_ERROR, "Reputation benchmark: SAVE DB: %lld microseconds",
|
||||
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint64_t hash_reputation_entry(char *ip)
|
||||
@ -548,7 +837,7 @@ EVENT(add_scores)
|
||||
*/
|
||||
#define MARKER_UNREGISTERED_USER (marker)
|
||||
#define MARKER_REGISTERED_USER (marker+1)
|
||||
|
||||
|
||||
list_for_each_entry(client, &client_list, client_node)
|
||||
{
|
||||
if (!IsUser(client))
|
||||
@ -625,13 +914,13 @@ EVENT(delete_old_records)
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
|
||||
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
|
||||
{
|
||||
for (e = ReputationHashTable[i]; e; e = e_next)
|
||||
{
|
||||
e_next = e->next;
|
||||
|
||||
|
||||
if (is_reputation_expired(e))
|
||||
{
|
||||
#ifdef DEBUGMODE
|
||||
@ -651,9 +940,9 @@ EVENT(delete_old_records)
|
||||
#endif
|
||||
}
|
||||
|
||||
EVENT(save_db_evt)
|
||||
EVENT(reputation_save_db_evt)
|
||||
{
|
||||
save_db();
|
||||
reputation_save_db();
|
||||
}
|
||||
|
||||
CMD_FUNC(reputationunperm)
|
||||
@ -801,7 +1090,7 @@ CMD_FUNC(reputation_user_cmd)
|
||||
sendnumeric(client, ERR_NOPRIVILEGES);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ((parc < 2) || BadPtr(parv[1]))
|
||||
{
|
||||
sendnotice(client, "Reputation module statistics:");
|
||||
@ -825,7 +1114,7 @@ CMD_FUNC(reputation_user_cmd)
|
||||
sendnotice(client, "/REPUTATION <NN List users with reputation score below value NN");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (strchr(parv[1], '.') || strchr(parv[1], ':'))
|
||||
{
|
||||
ip = parv[1];
|
||||
@ -871,7 +1160,7 @@ CMD_FUNC(reputation_user_cmd)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
e = find_reputation_entry(ip);
|
||||
if (!e)
|
||||
{
|
||||
@ -927,7 +1216,7 @@ CMD_FUNC(reputation_server_cmd)
|
||||
sendnumeric(client, ERR_NEEDMOREPARAMS, "REPUTATION");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ip = parv[1];
|
||||
|
||||
if (parv[2][0] == '*')
|
||||
@ -993,7 +1282,7 @@ CMD_FUNC(reputation_cmd)
|
||||
{
|
||||
if (MyUser(client))
|
||||
reputation_user_cmd(client, recv_mtags, parc, parv);
|
||||
else if (IsServer(client))
|
||||
else if (IsServer(client) || IsMe(client))
|
||||
reputation_server_cmd(client, recv_mtags, parc, parv);
|
||||
}
|
||||
|
||||
@ -1003,7 +1292,7 @@ int reputation_whois(Client *client, Client *target)
|
||||
|
||||
if (!IsOper(client))
|
||||
return 0; /* only opers can see this.. */
|
||||
|
||||
|
||||
if (reputation > 0)
|
||||
{
|
||||
sendto_one(client, NULL, ":%s %d %s %s :is using an IP with a reputation score of %d",
|
||||
|
@ -36,6 +36,7 @@ struct RestrictedCommand {
|
||||
int exempt_identified;
|
||||
int exempt_reputation_score;
|
||||
int exempt_webirc;
|
||||
int exempt_tls;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -179,9 +180,9 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
if (!strcmp(cep2->ce_varname, "connect-delay"))
|
||||
{
|
||||
long v = config_checkval(cep2->ce_vardata, CFG_TIME);
|
||||
if ((v < 10) || (v > 3600))
|
||||
if ((v < 1) || (v > 3600))
|
||||
{
|
||||
config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 10-3600", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname);
|
||||
config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 1-3600", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
continue;
|
||||
@ -189,10 +190,13 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-identified"))
|
||||
continue;
|
||||
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-webirc"))
|
||||
continue;
|
||||
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-tls"))
|
||||
continue;
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-reputation-score"))
|
||||
{
|
||||
int v = atoi(cep2->ce_vardata);
|
||||
@ -279,6 +283,12 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-tls"))
|
||||
{
|
||||
rcmd->exempt_tls = config_checkval(cep2->ce_vardata, CFG_YESNO);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cep2->ce_varname, "exempt-reputation-score"))
|
||||
{
|
||||
rcmd->exempt_reputation_score = atoi(cep2->ce_vardata);
|
||||
@ -299,6 +309,8 @@ int rcmd_canbypass(Client *client, RestrictedCommand *rcmd)
|
||||
return 1;
|
||||
if (rcmd->exempt_webirc && moddata_client_get(client, "webirc"))
|
||||
return 1;
|
||||
if (rcmd->exempt_tls && IsSecureConnect(client))
|
||||
return 1;
|
||||
if (rcmd->exempt_reputation_score > 0 && (GetReputation(client) >= rcmd->exempt_reputation_score))
|
||||
return 1;
|
||||
if (rcmd->connect_delay && client->local && (TStime() - client->local->firsttime >= rcmd->connect_delay))
|
||||
|
@ -275,24 +275,5 @@ CMD_FUNC(cmd_sajoin)
|
||||
strlcat(jbuf, ",", sizeof jbuf);
|
||||
strlcat(jbuf, name, sizeof jbuf);
|
||||
}
|
||||
|
||||
if (did_anything)
|
||||
{
|
||||
if (!sjmode)
|
||||
{
|
||||
//sendnotice(target, "*** You were forced to join %s", jbuf);
|
||||
sendto_umode_global(UMODE_OPER, "%s used SAJOIN to make %s join %s", client->name, target->name, jbuf);
|
||||
/* Logging function added by XeRXeS */
|
||||
ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %s",
|
||||
client->name, target->name, jbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
//sendnotice(target, "*** You were forced to join %s with '%c'", jbuf, sjmode);
|
||||
sendto_umode_global(UMODE_OPER, "%s used SAJOIN to make %s join %c%s", client->name, target->name, sjmode, jbuf);
|
||||
ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %c%s",
|
||||
client->name, target->name, sjmode, jbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,22 +159,6 @@ CMD_FUNC(cmd_sapart)
|
||||
parv[0] = target->name; // nick
|
||||
parv[1] = parv[2]; // chan
|
||||
parv[2] = comment ? commentx : NULL; // comment
|
||||
if (comment)
|
||||
{
|
||||
//sendnotice(target, "*** You were forced to part %s (%s)", parv[1], commentx);
|
||||
sendto_umode_global(UMODE_OPER, "%s used SAPART to make %s part %s (%s)",
|
||||
client->name, target->name, parv[1], comment);
|
||||
ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s (%s)",
|
||||
client->name, target->name, parv[1], comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
//sendnotice(target, "*** You were forced to part %s", parv[1]);
|
||||
sendto_umode_global(UMODE_OPER, "%s used SAPART to make %s part %s",
|
||||
client->name, target->name, parv[1]);
|
||||
ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s",
|
||||
client->name, target->name, parv[1]);
|
||||
}
|
||||
do_cmd(target, NULL, "PART", comment ? 3 : 2, parv);
|
||||
/* target may be killed now due to the part reason @ spamfilter */
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ CMD_FUNC(cmd_svslogin)
|
||||
|
||||
strlcpy(target->user->svid, parv[3], sizeof(target->user->svid));
|
||||
user_account_login(recv_mtags, target);
|
||||
if (MyConnect(target) && IsDead(target))
|
||||
return; /* was killed due to *LINE on ~a probably */
|
||||
} else {
|
||||
/* It is perfectly normal for target to be NULL as this
|
||||
* happens during registration phase (pre-connect).
|
||||
|
@ -179,6 +179,7 @@ void _send_protoctl_servers(Client *client, int response)
|
||||
{
|
||||
char buf[512];
|
||||
Client *acptr;
|
||||
int sendit = 1;
|
||||
|
||||
sendto_one(client, NULL, "PROTOCTL EAUTH=%s,%d,%s%s,%s",
|
||||
me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", version);
|
||||
@ -188,15 +189,24 @@ void _send_protoctl_servers(Client *client, int response)
|
||||
list_for_each_entry(acptr, &global_server_list, client_node)
|
||||
{
|
||||
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s,", acptr->id);
|
||||
sendit = 1;
|
||||
if (strlen(buf) > sizeof(buf)-12)
|
||||
break; /* prevent overflow/cutoff if you have a network with more than 90 servers or something. */
|
||||
{
|
||||
if (buf[strlen(buf)-1] == ',')
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
sendto_one(client, NULL, "%s", buf);
|
||||
/* We use the asterisk here too for continuation lines */
|
||||
ircsnprintf(buf, sizeof(buf), "PROTOCTL SERVERS=*");
|
||||
sendit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove final comma (if any) */
|
||||
if (buf[strlen(buf)-1] == ',')
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
|
||||
sendto_one(client, NULL, "%s", buf);
|
||||
if (sendit)
|
||||
sendto_one(client, NULL, "%s", buf);
|
||||
}
|
||||
|
||||
void _send_server_message(Client *client)
|
||||
|
@ -89,7 +89,7 @@ CMD_FUNC(cmd_setname)
|
||||
/* set the new name before we check, but don't send to servers unless it is ok */
|
||||
strcpy(client->info, parv[1]);
|
||||
spamfilter_build_user_string(spamfilter_user, client->name, client);
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL))
|
||||
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "SETNAME", NULL, 0, NULL))
|
||||
{
|
||||
/* Was rejected by spamfilter, restore the realname */
|
||||
strcpy(client->info, tmpinfo);
|
||||
|
@ -144,15 +144,6 @@ CMD_FUNC(cmd_squit)
|
||||
sendto_umode_global(UMODE_OPER, "Received SQUIT %s from %s (%s)",
|
||||
target->name, get_client_name(client, FALSE), comment);
|
||||
}
|
||||
if (IsOper(client))
|
||||
{
|
||||
/*
|
||||
* It was manually /squit'ed by a human being(we hope),
|
||||
* there is a very good chance they don't want us to
|
||||
* reconnect right away. -Cabal95
|
||||
*/
|
||||
SetSQuit(target);
|
||||
}
|
||||
|
||||
exit_client(target, recv_mtags, comment);
|
||||
exit_client_ex(target, client->direction, recv_mtags, comment);
|
||||
}
|
||||
|
@ -376,8 +376,14 @@ CMD_FUNC(cmd_stats)
|
||||
else
|
||||
stat->func(client, NULL);
|
||||
|
||||
/* Modules can append data: */
|
||||
RunHook2(HOOKTYPE_STATS, client, flags);
|
||||
/* Modules can append data:
|
||||
* ('STATS S' already has special code for this that
|
||||
* maintains certain ordering, so not included here)
|
||||
*/
|
||||
if (stat->flag != 'S')
|
||||
{
|
||||
RunHook2(HOOKTYPE_STATS, client, flags);
|
||||
}
|
||||
|
||||
sendnumeric(client, RPL_ENDOFSTATS, stat->flag);
|
||||
|
||||
@ -772,9 +778,33 @@ int stats_officialchannels(Client *client, char *para)
|
||||
|
||||
#define SafePrint(x) ((x) ? (x) : "")
|
||||
|
||||
/** Helper for stats_set() */
|
||||
static void stats_set_anti_flood(Client *client, FloodSettings *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; floodoption_names[i]; i++)
|
||||
{
|
||||
if (i == FLD_CONVERSATIONS)
|
||||
{
|
||||
sendtxtnumeric(client, "anti-flood::%s::%s: %d users, new user every %s",
|
||||
f->name, floodoption_names[i],
|
||||
(int)f->limit[i], pretty_time_val(f->period[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
sendtxtnumeric(client, "anti-flood::%s::%s: %d per %s",
|
||||
f->name, floodoption_names[i],
|
||||
(int)f->limit[i], pretty_time_val(f->period[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int stats_set(Client *client, char *para)
|
||||
{
|
||||
char *uhallow;
|
||||
SecurityGroup *s;
|
||||
FloodSettings *f;
|
||||
|
||||
if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL))
|
||||
{
|
||||
@ -875,9 +905,17 @@ int stats_set(Client *client, char *para)
|
||||
sendtxtnumeric(client, "anti-flood::handshake-data-flood::amount: %ld bytes", iConf.handshake_data_flood_amount);
|
||||
sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-action: %s", banact_valtostring(iConf.handshake_data_flood_ban_action));
|
||||
sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-time: %s", pretty_time_val(iConf.handshake_data_flood_ban_time));
|
||||
if (AWAY_PERIOD)
|
||||
sendtxtnumeric(client, "anti-flood::away-flood: %d per %s", AWAY_COUNT, pretty_time_val(AWAY_PERIOD));
|
||||
sendtxtnumeric(client, "anti-flood::nick-flood: %d per %s", NICK_COUNT, pretty_time_val(NICK_PERIOD));
|
||||
|
||||
/* set::anti-flood */
|
||||
for (s = securitygroups; s; s = s->next)
|
||||
if ((f = find_floodsettings_block(s->name)))
|
||||
stats_set_anti_flood(client, f);
|
||||
f = find_floodsettings_block("unknown-users");
|
||||
stats_set_anti_flood(client, f);
|
||||
|
||||
//if (AWAY_PERIOD)
|
||||
// sendtxtnumeric(client, "anti-flood::away-flood: %d per %s", AWAY_COUNT, pretty_time_val(AWAY_PERIOD));
|
||||
//sendtxtnumeric(client, "anti-flood::nick-flood: %d per %s", NICK_COUNT, pretty_time_val(NICK_PERIOD));
|
||||
sendtxtnumeric(client, "handshake-timeout: %s", pretty_time_val(iConf.handshake_timeout));
|
||||
sendtxtnumeric(client, "sasl-timeout: %s", pretty_time_val(iConf.sasl_timeout));
|
||||
sendtxtnumeric(client, "ident::connect-timeout: %s", pretty_time_val(IDENT_CONNECT_TIMEOUT));
|
||||
@ -895,6 +933,10 @@ int stats_set(Client *client, char *para)
|
||||
sendtxtnumeric(client, "outdated-tls-policy::oper: %s", policy_valtostr(iConf.outdated_tls_policy_oper));
|
||||
sendtxtnumeric(client, "outdated-tls-policy::server: %s", policy_valtostr(iConf.outdated_tls_policy_server));
|
||||
RunHook2(HOOKTYPE_STATS, client, "S");
|
||||
#ifndef _WIN32
|
||||
sendtxtnumeric(client, "This server can handle %d concurrent sockets (%d clients + %d reserve)",
|
||||
maxclients+CLIENTS_RESERVE, maxclients, CLIENTS_RESERVE);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -408,6 +408,8 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[],
|
||||
{
|
||||
strlcpy(target->user->svid, parv[3], sizeof(target->user->svid));
|
||||
user_account_login(recv_mtags, target);
|
||||
if (MyConnect(target) && IsDead(target))
|
||||
return; /* was killed due to *LINE on ~a probably */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -95,6 +95,7 @@ CMD_FUNC(cmd_svsnick)
|
||||
|
||||
/* no 'recv_mtags' here, we do not inherit from SVSNICK but generate a new NICK event */
|
||||
new_message(acptr, NULL, &mtags);
|
||||
RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, acptr, mtags, parv[2]);
|
||||
sendto_local_common_channels(acptr, acptr, 0, mtags, ":%s NICK :%s", acptr->name, parv[2]);
|
||||
sendto_one(acptr, mtags, ":%s NICK :%s", acptr->name, parv[2]);
|
||||
sendto_server(NULL, 0, 0, mtags, ":%s NICK %s :%ld", acptr->id, parv[2], atol(parv[3]));
|
||||
@ -107,7 +108,6 @@ CMD_FUNC(cmd_svsnick)
|
||||
sendto_snomask(SNO_NICKCHANGE,
|
||||
"*** %s (%s@%s) has been forced to change their nickname to %s",
|
||||
acptr->name, acptr->user->username, acptr->user->realhost, parv[2]);
|
||||
RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, acptr, parv[2]);
|
||||
|
||||
strlcpy(acptr->name, parv[2], sizeof acptr->name);
|
||||
add_to_client_hash_table(parv[2], acptr);
|
||||
|
@ -234,8 +234,8 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member
|
||||
if (!MyUser(client))
|
||||
return HOOK_CONTINUE;
|
||||
|
||||
/* IRCOps and U-Lines override */
|
||||
if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL)))
|
||||
/* U-Lines, servers and IRCOps override */
|
||||
if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL)))
|
||||
return HOOK_CONTINUE;
|
||||
|
||||
what = sendtypetowhat(sendtype);
|
||||
@ -259,6 +259,7 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member
|
||||
if (flood->cnt[what] >= channelcfg->cnt[what])
|
||||
{
|
||||
/* Flood detected */
|
||||
flood_limit_exceeded_log(client, "target-flood-channel");
|
||||
snprintf(errbuf, sizeof(errbuf), "Channel is being flooded. Message not delivered.");
|
||||
*errmsg = errbuf;
|
||||
return HOOK_DENY;
|
||||
@ -280,8 +281,8 @@ int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text
|
||||
if (!MyUser(target))
|
||||
return HOOK_CONTINUE;
|
||||
|
||||
/* IRCOps and U-Lines override */
|
||||
if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL)))
|
||||
/* U-Lines, servers and IRCOps override */
|
||||
if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL)))
|
||||
return HOOK_CONTINUE;
|
||||
|
||||
what = sendtypetowhat(sendtype);
|
||||
@ -305,6 +306,7 @@ int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text
|
||||
if (flood->cnt[what] >= privatecfg->cnt[what])
|
||||
{
|
||||
/* Flood detected */
|
||||
flood_limit_exceeded_log(client, "target-flood-user");
|
||||
snprintf(errbuf, sizeof(errbuf), "User is being flooded. Message not delivered.");
|
||||
*errmsg = errbuf;
|
||||
return HOOK_DENY;
|
||||
|
@ -82,7 +82,9 @@ 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);
|
||||
int _match_spamfilter(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk);
|
||||
int _match_spamfilter(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
|
||||
int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd);
|
||||
int check_mtag_spamfilters_present(void);
|
||||
int _join_viruschan(Client *client, TKL *tk, int type);
|
||||
void _spamfilter_build_user_string(char *buf, char *nick, Client *client);
|
||||
int _match_user(char *rmask, Client *client, int options);
|
||||
@ -109,6 +111,7 @@ struct TKLTypeTable
|
||||
char *log_name; /**< Used for logging and server notices */
|
||||
unsigned tkltype:1; /**< Is a type available in cmd_tkl() and friends */
|
||||
unsigned exceptiontype:1; /**< Is a type available for exceptions */
|
||||
unsigned needip:1; /**< When using this exempt option, only IP addresses are permitted (processed before DNS/ident lookups etc) */
|
||||
};
|
||||
|
||||
/** This table which defines all TKL types and TKL exception types.
|
||||
@ -117,35 +120,35 @@ struct TKLTypeTable
|
||||
*
|
||||
* IMPORTANT IF YOU ARE ADDING A NEW TYPE TO THIS TABLE:
|
||||
* - also update eline_syntax()
|
||||
* - also check if eline_type_requires_ip() needs to be updated
|
||||
* - update help.conf (HELPOP ELINE)
|
||||
* - more?
|
||||
*/
|
||||
TKLTypeTable tkl_types[] = {
|
||||
/* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> */
|
||||
{ "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1 },
|
||||
{ "kline", 'k', TKL_KILL, "K-Line", 1, 1 },
|
||||
{ "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1 },
|
||||
{ "zline", 'z', TKL_ZAP, "Z-Line", 1, 1 },
|
||||
{ "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1 },
|
||||
{ "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1 },
|
||||
{ "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0 },
|
||||
{ "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1 },
|
||||
{ "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0 },
|
||||
{ "local-spamfilter", 'e', TKL_EXCEPTION, "Local Exception", 1, 0 },
|
||||
{ "local-exception", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0 },
|
||||
{ "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1 },
|
||||
{ "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1 },
|
||||
{ "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1 },
|
||||
{ "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1 },
|
||||
{ "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1 },
|
||||
{ "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1 },
|
||||
{ "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1 },
|
||||
{ NULL, '\0', 0, NULL, 0, 0 },
|
||||
/* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> <need ip address?> */
|
||||
{ "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1, 0 },
|
||||
{ "kline", 'k', TKL_KILL, "K-Line", 1, 1, 0 },
|
||||
{ "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1, 1 },
|
||||
{ "zline", 'z', TKL_ZAP, "Z-Line", 1, 1, 1 },
|
||||
{ "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1, 0 },
|
||||
{ "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1, 0 },
|
||||
{ "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0 },
|
||||
{ "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0 },
|
||||
{ "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0 },
|
||||
{ "local-spamfilter", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 },
|
||||
{ "local-exception", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 },
|
||||
{ "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1 },
|
||||
{ "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 1 },
|
||||
{ "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0 },
|
||||
{ "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1, 1 },
|
||||
{ "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1, 0 },
|
||||
{ "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1, 0 },
|
||||
{ "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1, 0 },
|
||||
{ NULL, '\0', 0, NULL, 0, 0, 0 },
|
||||
};
|
||||
#define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version"
|
||||
|
||||
int max_stats_matches = 1000;
|
||||
int mtag_spamfilters_present = 0; /**< Are any spamfilters with type SPAMF_MTAG present? */
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
@ -178,8 +181,9 @@ MOD_TEST()
|
||||
EfunctionAddVoid(modinfo->handle, EFUNC_TKL_SYNCH, _tkl_sync);
|
||||
EfunctionAddVoid(modinfo->handle, EFUNC_CMD_TKL, _cmd_tkl);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_PLACE_HOST_BAN, _place_host_ban);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER, _match_spamfilter);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER_VIRUSCHAN, _join_viruschan);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER, _match_spamfilter);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER_MTAGS, _match_spamfilter_mtags);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_JOIN_VIRUSCHAN, _join_viruschan);
|
||||
EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH, _tkl_ip_hash);
|
||||
@ -213,6 +217,7 @@ MOD_INIT()
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
check_mtag_spamfilters_present();
|
||||
EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
@ -1363,6 +1368,15 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type)
|
||||
mask[3] = '\0';
|
||||
usermask = mask; /* eg ~S: */
|
||||
hostmask = mask2buf;
|
||||
|
||||
if (((*type == 'z') || (*type == 'Z')))
|
||||
{
|
||||
sendnotice(client, "ERROR: (g)zlines must be placed at *@\037IPMASK\037. "
|
||||
"Extended server bans don't work here because (g)zlines are processed"
|
||||
"BEFORE dns and ident lookups are done and before reading any client data. "
|
||||
"If you want to use extended server bans then use a KLINE/GLINE instead.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Delete: allow any attempt */
|
||||
strlcpy(mask2buf, mask+3, sizeof(mask2buf));
|
||||
@ -1543,14 +1557,14 @@ void eline_syntax(Client *client)
|
||||
* exception to be placed on *@ip rather than
|
||||
* user@host or *@host. For eg zlines.
|
||||
*/
|
||||
int eline_type_requires_ip(char *bantypes)
|
||||
TKLTypeTable *eline_type_requires_ip(char *bantypes)
|
||||
{
|
||||
if (strchr(bantypes, 'z') || strchr(bantypes, 'Z') ||
|
||||
strchr(bantypes, 'c') ||
|
||||
strchr(bantypes, 'b') ||
|
||||
strchr(bantypes, 'd'))
|
||||
return 1;
|
||||
return 0;
|
||||
int i;
|
||||
|
||||
for (i=0; tkl_types[i].config_name; i++)
|
||||
if (tkl_types[i].needip && strchr(bantypes, tkl_types[i].letter))
|
||||
return &tkl_types[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Checks a string to see if it contains invalid ban exception types */
|
||||
@ -1590,6 +1604,7 @@ CMD_FUNC(cmd_eline)
|
||||
"-", /*9 reason */
|
||||
NULL
|
||||
};
|
||||
TKLTypeTable *t;
|
||||
|
||||
if (IsServer(client))
|
||||
return;
|
||||
@ -1673,11 +1688,11 @@ CMD_FUNC(cmd_eline)
|
||||
mask[3] = '\0';
|
||||
usermask = mask; /* eg ~S: */
|
||||
hostmask = mask2buf;
|
||||
if (eline_type_requires_ip(bantypes))
|
||||
if ((t = eline_type_requires_ip(bantypes)))
|
||||
{
|
||||
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b do not work on extended server bans. "
|
||||
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE "
|
||||
"extended bans can be checked.");
|
||||
sendnotice(client, "ERROR: Ban exception with type '%c' does not work on extended server bans. "
|
||||
"This is because checking for %s takes places BEFORE "
|
||||
"extended bans can be checked.", t->letter, t->log_name);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1724,25 +1739,31 @@ CMD_FUNC(cmd_eline)
|
||||
sendnotice(client, "[error] For technical reasons you cannot start the host with a ':', sorry");
|
||||
return;
|
||||
}
|
||||
if (add && eline_type_requires_ip(bantypes))
|
||||
if (add && ((t = eline_type_requires_ip(bantypes))))
|
||||
{
|
||||
/* Trying to exempt a user from a (G)ZLINE,
|
||||
* make sure the user isn't specifying a host then.
|
||||
*/
|
||||
if (strcmp(usermask, "*"))
|
||||
{
|
||||
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b need to be placed at \037*\037@ipmask, not \037user\037@ipmask. "
|
||||
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE any dns and ident lookups.");
|
||||
sendnotice(client, "ERROR: Ban exception with type '%c' need to be placed at \037*\037@ipmask, not \037user\037@ipmask. "
|
||||
"This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.",
|
||||
t->letter,
|
||||
t->log_name);
|
||||
return;
|
||||
}
|
||||
for (p=hostmask; *p; p++)
|
||||
{
|
||||
if (isalpha(*p) && !isxdigit(*p))
|
||||
{
|
||||
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b need to be placed at *@\037ipmask\037, not *@\037hostmask\037. "
|
||||
"(so for example *@192.168.* is ok, but *@*.aol.com is not). "
|
||||
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE any dns and ident lookups.");
|
||||
sendnotice(client, "ERROR: Ban exception with type '%c' needs to be placed at *@\037ipmask\037, not *@\037hostmask\037. "
|
||||
"(so for example *@192.168.* is OK, but *@*.aol.com is not). "
|
||||
"This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.",
|
||||
t->letter,
|
||||
t->log_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -2367,6 +2388,9 @@ TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Matc
|
||||
index = tkl_hash(tkl_typetochar(type));
|
||||
AddListItem(tkl, tklines[index]);
|
||||
|
||||
if (target & SPAMF_MTAG)
|
||||
mtag_spamfilters_present = 1;
|
||||
|
||||
return tkl;
|
||||
}
|
||||
|
||||
@ -2637,6 +2661,7 @@ void _tkl_del_line(TKL *tkl)
|
||||
|
||||
/* Finally, free the entry */
|
||||
free_tkl(tkl);
|
||||
check_mtag_spamfilters_present();
|
||||
}
|
||||
|
||||
/** Add some default ban exceptions - for localhost */
|
||||
@ -3052,7 +3077,7 @@ int _find_shun(Client *client)
|
||||
if (!(tkl->type & TKL_SHUN))
|
||||
continue;
|
||||
|
||||
snprintf(uhost, sizeof(uhost), "%s@%s", tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask);
|
||||
tkl_uhost(tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX);
|
||||
|
||||
if (match_user(uhost, client, MATCH_CHECK_REAL))
|
||||
{
|
||||
@ -3114,7 +3139,7 @@ int _find_spamfilter_user(Client *client, int flags)
|
||||
return 0;
|
||||
|
||||
spamfilter_build_user_string(spamfilter_user, client->name, client);
|
||||
return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, flags, NULL);
|
||||
return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, flags, NULL);
|
||||
}
|
||||
|
||||
/** Check a spamfilter against all local users and print a message.
|
||||
@ -4654,6 +4679,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type)
|
||||
/** match_spamfilter: executes the spamfilter on the input string.
|
||||
* @param str The text (eg msg text, notice text, part text, quit text, etc
|
||||
* @param target The spamfilter target (SPAMF_*)
|
||||
* @param cmd The command (eg: "PRIVMSG")
|
||||
* @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away)
|
||||
* @param flags Any flags (SPAMFLAG_*)
|
||||
* @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_
|
||||
@ -4661,7 +4687,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type)
|
||||
* 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched.
|
||||
* In case of 1, be sure to check IsDead(client)..
|
||||
*/
|
||||
int _match_spamfilter(Client *client, char *str_in, int target, char *destination, int flags, TKL **rettkl)
|
||||
int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char *destination, int flags, TKL **rettkl)
|
||||
{
|
||||
TKL *tkl;
|
||||
TKL *winner_tkl = NULL;
|
||||
@ -4676,6 +4702,9 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
|
||||
if (rettkl)
|
||||
*rettkl = NULL; /* initialize to NULL */
|
||||
|
||||
if (!cmd)
|
||||
cmd = cmdname_by_spamftarget(target);
|
||||
|
||||
if (target == SPAMF_USER)
|
||||
str = str_in;
|
||||
else
|
||||
@ -4755,7 +4784,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
|
||||
ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]",
|
||||
client->name, client->user->username, client->user->realhost,
|
||||
tkl->ptr.spamfilter->match->str,
|
||||
cmdname_by_spamftarget(target), destinationbuf, str,
|
||||
cmd, destinationbuf, str,
|
||||
unreal_decodespace(tkl->ptr.spamfilter->tkl_reason));
|
||||
|
||||
sendto_snomask_global(SNO_SPAMF, "%s", buf);
|
||||
@ -4807,6 +4836,12 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
|
||||
me.name, client->name, destination, reason);
|
||||
break;
|
||||
}
|
||||
case SPAMF_MTAG:
|
||||
{
|
||||
sendnumericfmt(client, ERR_CANNOTDOCOMMAND, "%s :Command blocked: %s",
|
||||
cmd, reason);
|
||||
break;
|
||||
}
|
||||
case SPAMF_DCC:
|
||||
{
|
||||
char errmsg[512];
|
||||
@ -4837,7 +4872,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
|
||||
if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN))
|
||||
{
|
||||
if ((target != SPAMF_USER) && (target != SPAMF_QUIT))
|
||||
sendnumeric(client, RPL_SPAMCMDFWD, cmdname_by_spamftarget(target), reason);
|
||||
sendnumeric(client, RPL_SPAMCMDFWD, cmd, reason);
|
||||
return 0;
|
||||
} else
|
||||
if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK))
|
||||
@ -4875,6 +4910,60 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
|
||||
return 0; /* NOTREACHED */
|
||||
}
|
||||
|
||||
/** Check message-tag spamfilters.
|
||||
* @param client The client
|
||||
* @param mtags Message tags sent by client
|
||||
* @param cmd Command to be executed (can be NULL)
|
||||
* @retval Return 1 to stop processing the command (ignore it) or 0 to allow/continue as normal
|
||||
*/
|
||||
int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd)
|
||||
{
|
||||
MessageTag *m;
|
||||
char buf[4096];
|
||||
char *str;
|
||||
|
||||
/* This is a shortcut: if there are no spamfilters present
|
||||
* on message tags then we can return immediately.
|
||||
* Saves a lot of CPU and it is quite likely too!
|
||||
*/
|
||||
if (mtag_spamfilters_present == 0)
|
||||
return 0;
|
||||
|
||||
for (m = mtags; m; m = m->next)
|
||||
{
|
||||
if (m->value)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s=%s", m->name, m->value);
|
||||
str = buf;
|
||||
} else {
|
||||
str = m->name;
|
||||
}
|
||||
if (match_spamfilter(client, str, SPAMF_MTAG, cmd, NULL, 0, NULL))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Updates 'mtag_spamfilters_present' based on if any spamfilters
|
||||
* are present with the SPAMF_MTAG target.
|
||||
*/
|
||||
int check_mtag_spamfilters_present(void)
|
||||
{
|
||||
TKL *tkl;
|
||||
|
||||
for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next)
|
||||
{
|
||||
if (tkl->ptr.spamfilter->target & SPAMF_MTAG)
|
||||
{
|
||||
mtag_spamfilters_present = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
mtag_spamfilters_present = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** CIDR function to compare the first 'mask' bits.
|
||||
* @author Taken from atheme
|
||||
* @returns 1 if equal, 0 if not.
|
||||
|
@ -61,14 +61,14 @@ ModuleHeader MOD_HEADER = {
|
||||
do { \
|
||||
sendto_realops_and_log("[tkldb] Error writing to temporary database file " \
|
||||
"'%s': %s (DATABASE NOT SAVED)", \
|
||||
fname, strerror(errno)); \
|
||||
fname, unrealdb_get_error_string()); \
|
||||
} while(0)
|
||||
|
||||
#define R_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_warn("[tkldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \
|
||||
fclose(fd); \
|
||||
config_warn("[tkldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
|
||||
unrealdb_close(db); \
|
||||
FreeTKLRead(); \
|
||||
return 0; \
|
||||
} \
|
||||
@ -78,7 +78,7 @@ ModuleHeader MOD_HEADER = {
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
WARN_WRITE_ERROR(tmpfname); \
|
||||
fclose(fd); \
|
||||
unrealdb_close(db); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
@ -91,41 +91,56 @@ ModuleHeader MOD_HEADER = {
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Structs */
|
||||
struct cfgstruct {
|
||||
char *database;
|
||||
char *db_secret;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
void tkldb_moddata_free(ModData *md);
|
||||
void setcfg(void);
|
||||
void freecfg(void);
|
||||
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
||||
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
void setcfg(struct cfgstruct *cfg);
|
||||
void freecfg(struct cfgstruct *cfg);
|
||||
int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
||||
int tkldb_config_posttest(int *errs);
|
||||
int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
EVENT(write_tkldb_evt);
|
||||
int write_tkldb(void);
|
||||
int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl);
|
||||
int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl);
|
||||
int read_tkldb(void);
|
||||
|
||||
/* Globals variables */
|
||||
const uint32_t tkldb_version = TKLDB_VERSION;
|
||||
struct cfgstruct {
|
||||
char *database;
|
||||
};
|
||||
static struct cfgstruct cfg;
|
||||
static struct cfgstruct test;
|
||||
|
||||
static long tkldb_next_event = 0;
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_configtest);
|
||||
memset(&test, 0, sizeof(test));
|
||||
setcfg(&test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_config_test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, tkldb_config_posttest);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, -9999);
|
||||
|
||||
LoadPersistentLong(modinfo, tkldb_next_event);
|
||||
|
||||
setcfg();
|
||||
setcfg(&cfg);
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_config_run);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
if (!tkldb_next_event)
|
||||
{
|
||||
/* If this is the first time that our module is loaded, then
|
||||
@ -142,25 +157,16 @@ MOD_INIT()
|
||||
}
|
||||
tkldb_next_event = TStime() + TKLDB_SAVE_EVERY + TKLDB_SAVE_EVERY_DELTA;
|
||||
}
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_configrun);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
EventAdd(modinfo->handle, "tkldb_write_tkldb", write_tkldb_evt, NULL, 1000, 0);
|
||||
if (ModuleGetError(modinfo->handle) != MODERR_NOERROR)
|
||||
{
|
||||
config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
|
||||
return MOD_FAILED;
|
||||
}
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
write_tkldb();
|
||||
freecfg();
|
||||
if (loop.ircd_terminating)
|
||||
write_tkldb();
|
||||
freecfg(&test);
|
||||
freecfg(&cfg);
|
||||
SavePersistentLong(modinfo, tkldb_next_event);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
@ -171,19 +177,20 @@ void tkldb_moddata_free(ModData *md)
|
||||
md->i = 0;
|
||||
}
|
||||
|
||||
void setcfg(void)
|
||||
void setcfg(struct cfgstruct *cfg)
|
||||
{
|
||||
// Default: data/tkl.db
|
||||
safe_strdup(cfg.database, "tkl.db");
|
||||
convert_to_absolute_path(&cfg.database, PERMDATADIR);
|
||||
safe_strdup(cfg->database, "tkl.db");
|
||||
convert_to_absolute_path(&cfg->database, PERMDATADIR);
|
||||
}
|
||||
|
||||
void freecfg(void)
|
||||
void freecfg(struct cfgstruct *cfg)
|
||||
{
|
||||
safe_free(cfg.database);
|
||||
safe_free(cfg->database);
|
||||
safe_free(cfg->db_secret);
|
||||
}
|
||||
|
||||
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
ConfigEntry *cep;
|
||||
@ -197,16 +204,45 @@ int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
{
|
||||
if (!cep->ce_vardata) {
|
||||
if (!cep->ce_vardata)
|
||||
{
|
||||
config_error("%s:%i: blank set::tkldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(cep->ce_varname, "database")) {
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
{
|
||||
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
|
||||
continue;
|
||||
safe_strdup(test.database, cep->ce_vardata);
|
||||
} else
|
||||
if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
{
|
||||
char *err;
|
||||
if ((err = unrealdb_test_secret(cep->ce_vardata)))
|
||||
{
|
||||
config_error("%s:%i: set::tkldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
safe_strdup(test.db_secret, cep->ce_vardata);
|
||||
} else
|
||||
{
|
||||
config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
errors++;
|
||||
}
|
||||
config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int tkldb_config_posttest(int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
char *errstr;
|
||||
|
||||
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
|
||||
{
|
||||
config_error("[tkldb] %s", errstr);
|
||||
errors++;
|
||||
}
|
||||
|
||||
@ -214,7 +250,7 @@ int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
ConfigEntry *cep;
|
||||
|
||||
@ -229,6 +265,8 @@ int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
if (!strcmp(cep->ce_varname, "database"))
|
||||
safe_strdup(cfg.database, cep->ce_vardata);
|
||||
else if (!strcmp(cep->ce_varname, "db-secret"))
|
||||
safe_strdup(cfg.db_secret, cep->ce_vardata);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -244,7 +282,7 @@ EVENT(write_tkldb_evt)
|
||||
int write_tkldb(void)
|
||||
{
|
||||
char tmpfname[512];
|
||||
FILE *fd;
|
||||
UnrealDB *db;
|
||||
uint64_t tklcount;
|
||||
int index, index2;
|
||||
TKL *tkl;
|
||||
@ -256,15 +294,15 @@ int write_tkldb(void)
|
||||
|
||||
// Write to a tempfile first, then rename it if everything succeeded
|
||||
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
|
||||
fd = fopen(tmpfname, "wb");
|
||||
if (!fd)
|
||||
db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
W_SAFE(write_int32(fd, TKLDB_MAGIC));
|
||||
W_SAFE(write_data(fd, &tkldb_version, sizeof(tkldb_version)));
|
||||
W_SAFE(unrealdb_write_int32(db, TKLDB_MAGIC));
|
||||
W_SAFE(unrealdb_write_int32(db, tkldb_version));
|
||||
|
||||
// Count the *-Lines
|
||||
tklcount = 0;
|
||||
@ -292,7 +330,7 @@ int write_tkldb(void)
|
||||
tklcount++;
|
||||
}
|
||||
}
|
||||
W_SAFE(write_data(fd, &tklcount, sizeof(tklcount)));
|
||||
W_SAFE(unrealdb_write_int64(db, tklcount));
|
||||
|
||||
// Now write the actual *-Lines, first the ones in the hash table
|
||||
for (index = 0; index < TKLIPHASHLEN1; index++)
|
||||
@ -303,7 +341,7 @@ int write_tkldb(void)
|
||||
{
|
||||
if (tkl->flags & TKL_FLAG_CONFIG)
|
||||
continue; /* config entry */
|
||||
if (!write_tkline(fd, tmpfname, tkl)) // write_tkline() closes the fd on errors itself
|
||||
if (!write_tkline(db, tmpfname, tkl)) // write_tkline() closes the db on errors itself
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -315,13 +353,13 @@ int write_tkldb(void)
|
||||
{
|
||||
if (tkl->flags & TKL_FLAG_CONFIG)
|
||||
continue; /* config entry */
|
||||
if (!write_tkline(fd, tmpfname, tkl))
|
||||
if (!write_tkline(db, tmpfname, tkl))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything seems to have gone well, attempt to close and rename the tempfile
|
||||
if (fclose(fd) != 0)
|
||||
if (!unrealdb_close(db))
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
@ -344,18 +382,18 @@ int write_tkldb(void)
|
||||
}
|
||||
|
||||
/** Write a TKL entry */
|
||||
int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
|
||||
int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl)
|
||||
{
|
||||
char tkltype;
|
||||
char buf[256];
|
||||
|
||||
/* First, write the common attributes */
|
||||
tkltype = tkl_typetochar(tkl->type);
|
||||
W_SAFE(write_data(fd, &tkltype, sizeof(tkltype))); // TKL char
|
||||
W_SAFE(unrealdb_write_char(db, tkltype)); // TKL char
|
||||
|
||||
W_SAFE(write_str(fd, tkl->set_by));
|
||||
W_SAFE(write_int64(fd, tkl->set_at));
|
||||
W_SAFE(write_int64(fd, tkl->expire_at));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->set_by));
|
||||
W_SAFE(unrealdb_write_int64(db, tkl->set_at));
|
||||
W_SAFE(unrealdb_write_int64(db, tkl->expire_at));
|
||||
|
||||
if (TKLIsServerBan(tkl))
|
||||
{
|
||||
@ -365,9 +403,9 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
|
||||
snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask);
|
||||
usermask = buf;
|
||||
}
|
||||
W_SAFE(write_str(fd, usermask));
|
||||
W_SAFE(write_str(fd, tkl->ptr.serverban->hostmask));
|
||||
W_SAFE(write_str(fd, tkl->ptr.serverban->reason));
|
||||
W_SAFE(unrealdb_write_str(db, usermask));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->hostmask));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->reason));
|
||||
} else
|
||||
if (TKLIsBanException(tkl))
|
||||
{
|
||||
@ -377,17 +415,17 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
|
||||
snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.banexception->usermask);
|
||||
usermask = buf;
|
||||
}
|
||||
W_SAFE(write_str(fd, usermask));
|
||||
W_SAFE(write_str(fd, tkl->ptr.banexception->hostmask));
|
||||
W_SAFE(write_str(fd, tkl->ptr.banexception->bantypes));
|
||||
W_SAFE(write_str(fd, tkl->ptr.banexception->reason));
|
||||
W_SAFE(unrealdb_write_str(db, usermask));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->hostmask));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->bantypes));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->reason));
|
||||
} else
|
||||
if (TKLIsNameBan(tkl))
|
||||
{
|
||||
char *hold = tkl->ptr.nameban->hold ? "H" : "*";
|
||||
W_SAFE(write_str(fd, hold));
|
||||
W_SAFE(write_str(fd, tkl->ptr.nameban->name));
|
||||
W_SAFE(write_str(fd, tkl->ptr.nameban->reason));
|
||||
W_SAFE(unrealdb_write_str(db, hold));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->name));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->reason));
|
||||
} else
|
||||
if (TKLIsSpamfilter(tkl))
|
||||
{
|
||||
@ -395,12 +433,12 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
|
||||
char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target);
|
||||
char action = banact_valtochar(tkl->ptr.spamfilter->action);
|
||||
|
||||
W_SAFE(write_str(fd, match_type));
|
||||
W_SAFE(write_str(fd, tkl->ptr.spamfilter->match->str));
|
||||
W_SAFE(write_str(fd, target));
|
||||
W_SAFE(write_data(fd, &action, sizeof(action)));
|
||||
W_SAFE(write_str(fd, tkl->ptr.spamfilter->tkl_reason));
|
||||
W_SAFE(write_int64(fd, tkl->ptr.spamfilter->tkl_duration));
|
||||
W_SAFE(unrealdb_write_str(db, match_type));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->match->str));
|
||||
W_SAFE(unrealdb_write_str(db, target));
|
||||
W_SAFE(unrealdb_write_char(db, action));
|
||||
W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->tkl_reason));
|
||||
W_SAFE(unrealdb_write_int64(db, tkl->ptr.spamfilter->tkl_duration));
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -409,7 +447,7 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
|
||||
/** Read all entries from the TKL db */
|
||||
int read_tkldb(void)
|
||||
{
|
||||
FILE *fd;
|
||||
UnrealDB *db;
|
||||
TKL *tkl = NULL;
|
||||
uint32_t magic = 0;
|
||||
uint32_t version;
|
||||
@ -426,49 +464,61 @@ int read_tkldb(void)
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
fd = fopen(cfg.database, "rb");
|
||||
if (!fd)
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
|
||||
{
|
||||
/* Database does not exist. Could be first boot */
|
||||
config_warn("[tkldb] No database present at '%s', will start a new one", cfg.database);
|
||||
return 1;
|
||||
} else {
|
||||
config_warn("[tkldb] Unable to open database file '%s' for reading: %s", cfg.database, strerror(errno));
|
||||
} else
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
|
||||
{
|
||||
/* Re-open as unencrypted */
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
|
||||
if (!db)
|
||||
{
|
||||
/* This should actually never happen, unless some weird I/O error */
|
||||
config_warn("[tkldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
{
|
||||
config_warn("[tkldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The database starts with a "magic value" - unless it's some old version or corrupt */
|
||||
R_SAFE(read_data(fd, &magic, sizeof(magic)));
|
||||
R_SAFE(unrealdb_read_int32(db, &magic));
|
||||
if (magic != TKLDB_MAGIC)
|
||||
{
|
||||
config_warn("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database);
|
||||
config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to "
|
||||
"delete the existing database. Just keep at least 1 server linked during the upgrade "
|
||||
"process to preserve your global *LINES and Spamfilters.");
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Now do a version check */
|
||||
R_SAFE(read_data(fd, &version, sizeof(version)));
|
||||
R_SAFE(unrealdb_read_int32(db, &version));
|
||||
if (version < 4999)
|
||||
{
|
||||
config_warn("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version);
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
if (version > tkldb_version)
|
||||
{
|
||||
config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported",
|
||||
cfg.database, (unsigned long)tkldb_version, (unsigned long)version);
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
R_SAFE(read_data(fd, &tklcount, sizeof(tklcount)));
|
||||
R_SAFE(unrealdb_read_int64(db, &tklcount));
|
||||
|
||||
for (cnt = 0; cnt < tklcount; cnt++)
|
||||
{
|
||||
@ -477,7 +527,7 @@ int read_tkldb(void)
|
||||
tkl = safe_alloc(sizeof(TKL));
|
||||
|
||||
/* First, fetch the TKL type.. */
|
||||
R_SAFE(read_data(fd, &c, sizeof(c)));
|
||||
R_SAFE(unrealdb_read_char(db, &c));
|
||||
tkl->type = tkl_chartotype(c);
|
||||
if (!tkl->type)
|
||||
{
|
||||
@ -493,10 +543,10 @@ int read_tkldb(void)
|
||||
}
|
||||
|
||||
/* Read the common types (same for all TKLs) */
|
||||
R_SAFE(read_str(fd, &tkl->set_by));
|
||||
R_SAFE(read_int64(fd, &v));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->set_by));
|
||||
R_SAFE(unrealdb_read_int64(db, &v));
|
||||
tkl->set_at = v;
|
||||
R_SAFE(read_int64(fd, &v));
|
||||
R_SAFE(unrealdb_read_int64(db, &v));
|
||||
tkl->expire_at = v;
|
||||
|
||||
/* Save some CPU... if it's already expired then don't bother adding */
|
||||
@ -513,7 +563,7 @@ int read_tkldb(void)
|
||||
/* Usermask - but taking into account that the
|
||||
* %-prefix means a soft ban.
|
||||
*/
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
if (*str == '%')
|
||||
{
|
||||
softban = 1;
|
||||
@ -524,8 +574,8 @@ int read_tkldb(void)
|
||||
safe_free(str);
|
||||
|
||||
/* And the other 2 fields.. */
|
||||
R_SAFE(read_str(fd, &tkl->ptr.serverban->hostmask));
|
||||
R_SAFE(read_str(fd, &tkl->ptr.serverban->reason));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->hostmask));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->reason));
|
||||
|
||||
if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask,
|
||||
tkl->ptr.serverban->hostmask, softban))
|
||||
@ -551,7 +601,7 @@ int read_tkldb(void)
|
||||
/* Usermask - but taking into account that the
|
||||
* %-prefix means a soft ban.
|
||||
*/
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
if (*str == '%')
|
||||
{
|
||||
softban = 1;
|
||||
@ -562,9 +612,9 @@ int read_tkldb(void)
|
||||
safe_free(str);
|
||||
|
||||
/* And the other 3 fields.. */
|
||||
R_SAFE(read_str(fd, &tkl->ptr.banexception->hostmask));
|
||||
R_SAFE(read_str(fd, &tkl->ptr.banexception->bantypes));
|
||||
R_SAFE(read_str(fd, &tkl->ptr.banexception->reason));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->hostmask));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->bantypes));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->reason));
|
||||
|
||||
if (find_tkl_banexception(tkl->type, tkl->ptr.banexception->usermask,
|
||||
tkl->ptr.banexception->hostmask, softban))
|
||||
@ -587,12 +637,12 @@ int read_tkldb(void)
|
||||
{
|
||||
tkl->ptr.nameban = safe_alloc(sizeof(NameBan));
|
||||
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
if (*str == 'H')
|
||||
tkl->ptr.nameban->hold = 1;
|
||||
safe_free(str);
|
||||
R_SAFE(read_str(fd, &tkl->ptr.nameban->name));
|
||||
R_SAFE(read_str(fd, &tkl->ptr.nameban->reason));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->name));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->reason));
|
||||
|
||||
if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name,
|
||||
tkl->ptr.nameban->hold))
|
||||
@ -617,7 +667,7 @@ int read_tkldb(void)
|
||||
tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter));
|
||||
|
||||
/* Match method */
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
match_method = unreal_match_method_strtoval(str);
|
||||
if (!match_method)
|
||||
{
|
||||
@ -627,7 +677,7 @@ int read_tkldb(void)
|
||||
safe_free(str);
|
||||
|
||||
/* Match string (eg: regex) */
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err);
|
||||
if (!tkl->ptr.spamfilter->match)
|
||||
{
|
||||
@ -637,7 +687,7 @@ int read_tkldb(void)
|
||||
safe_free(str);
|
||||
|
||||
/* Target (eg: cpn) */
|
||||
R_SAFE(read_str(fd, &str));
|
||||
R_SAFE(unrealdb_read_str(db, &str));
|
||||
tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL);
|
||||
if (!tkl->ptr.spamfilter->target)
|
||||
{
|
||||
@ -648,7 +698,7 @@ int read_tkldb(void)
|
||||
safe_free(str);
|
||||
|
||||
/* Action */
|
||||
R_SAFE(read_data(fd, &c, sizeof(c)));
|
||||
R_SAFE(unrealdb_read_char(db, &c));
|
||||
tkl->ptr.spamfilter->action = banact_chartoval(c);
|
||||
if (!tkl->ptr.spamfilter->action)
|
||||
{
|
||||
@ -657,8 +707,8 @@ int read_tkldb(void)
|
||||
do_not_add = 1;
|
||||
}
|
||||
|
||||
R_SAFE(read_str(fd, &tkl->ptr.spamfilter->tkl_reason));
|
||||
R_SAFE(read_int64(fd, &v));
|
||||
R_SAFE(unrealdb_read_str(db, &tkl->ptr.spamfilter->tkl_reason));
|
||||
R_SAFE(unrealdb_read_int64(db, &v));
|
||||
tkl->ptr.spamfilter->tkl_duration = v;
|
||||
|
||||
if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str,
|
||||
@ -695,10 +745,7 @@ int read_tkldb(void)
|
||||
FreeTKLRead();
|
||||
}
|
||||
|
||||
/* If everything went fine, then reading a single byte should cause an EOF error */
|
||||
if (fread(&c, 1, 1, fd) == 1)
|
||||
config_warn("[tkldb] Database invalid. Extra data found at end of DB file.");
|
||||
fclose(fd);
|
||||
unrealdb_close(db);
|
||||
|
||||
if (added_cnt)
|
||||
sendto_realops_and_log("[tkldb] Re-added %d *-Lines", added_cnt);
|
||||
|
@ -242,7 +242,7 @@ CMD_FUNC(cmd_topic)
|
||||
Hook *tmphook;
|
||||
int n;
|
||||
|
||||
if (match_spamfilter(client, topic, SPAMF_TOPIC, channel->chname, 0, NULL))
|
||||
if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->chname, 0, NULL))
|
||||
return;
|
||||
|
||||
for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) {
|
||||
|
@ -26,6 +26,11 @@ ModuleHeader MOD_HEADER
|
||||
#define WEBSOCKET_SEND_BUFFER_SIZE 16384
|
||||
#endif
|
||||
|
||||
typedef enum WebSocketType {
|
||||
WEBSOCKET_TYPE_BINARY = 1,
|
||||
WEBSOCKET_TYPE_TEXT = 2
|
||||
} WebSocketType;
|
||||
|
||||
typedef struct WebSocketUser WebSocketUser;
|
||||
struct WebSocketUser {
|
||||
char get; /**< GET initiated */
|
||||
@ -33,14 +38,14 @@ struct WebSocketUser {
|
||||
char *handshake_key; /**< Handshake key (used during handshake) */
|
||||
char *lefttoparse; /**< Leftover buffer to parse */
|
||||
int lefttoparselen; /**< Length of lefttoparse buffer */
|
||||
WebSocketType type; /**< WEBSOCKET_TYPE_BINARY or WEBSOCKET_TYPE_TEXT */
|
||||
char *sec_websocket_protocol; /**< Only valid during parsing of the request, after that it is NULL again */
|
||||
};
|
||||
|
||||
#define WEBSOCKET_TYPE_BINARY 0x1
|
||||
#define WEBSOCKET_TYPE_TEXT 0x2
|
||||
|
||||
#define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr)
|
||||
|
||||
#define WEBSOCKET_TYPE(client) ((client->local && client->local->listener) ? client->local->listener->websocket_options : 0)
|
||||
#define WEBSOCKET_PORT(client) ((client->local && client->local->listener) ? client->local->listener->websocket_options : 0)
|
||||
#define WEBSOCKET_TYPE(client) (WSU(client)->type)
|
||||
|
||||
#define WEBSOCKET_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" /* see RFC6455 */
|
||||
|
||||
@ -59,7 +64,7 @@ int websocket_packet_in(Client *client, char *readbuf, int *length);
|
||||
void websocket_mdata_free(ModData *m);
|
||||
int websocket_handle_packet(Client *client, char *readbuf, int length);
|
||||
int websocket_handle_handshake(Client *client, char *readbuf, int *length);
|
||||
int websocket_complete_handshake(Client *client);
|
||||
int websocket_handshake_send_response(Client *client);
|
||||
int websocket_handle_packet_ping(Client *client, char *buf, int len);
|
||||
int websocket_handle_packet_pong(Client *client, char *buf, int len);
|
||||
int websocket_create_packet(int opcode, char **buf, int *len);
|
||||
@ -67,6 +72,7 @@ int websocket_send_pong(Client *client, char *buf, int len);
|
||||
|
||||
/* Global variables */
|
||||
ModDataInfo *websocket_md;
|
||||
static int ws_text_mode_available = 1;
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
@ -98,6 +104,8 @@ MOD_INIT()
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
if (non_utf8_nick_chars_in_use || (iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY))
|
||||
ws_text_mode_available = 0;
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -302,11 +310,12 @@ int websocket_handle_websocket(Client *client, char *readbuf2, int length2)
|
||||
*/
|
||||
int websocket_packet_in(Client *client, char *readbuf, int *length)
|
||||
{
|
||||
if ((client->local->receiveM == 0) && WEBSOCKET_TYPE(client) && !WSU(client) && (*length > 8) && !strncmp(readbuf, "GET ", 4))
|
||||
if ((client->local->receiveM == 0) && WEBSOCKET_PORT(client) && !WSU(client) && (*length > 8) && !strncmp(readbuf, "GET ", 4))
|
||||
{
|
||||
/* Allocate a new WebSocketUser struct for this session */
|
||||
moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser));
|
||||
WSU(client)->get = 1;
|
||||
WSU(client)->type = client->local->listener->websocket_options; /* the default, unless the client chooses otherwise */
|
||||
}
|
||||
|
||||
if (!WSU(client))
|
||||
@ -452,6 +461,58 @@ int websocket_handshake_helper(char *buffer, int len, char **key, char **value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Finally, validate the websocket request (handshake) and proceed or reject. */
|
||||
int websocket_handshake_valid(Client *client)
|
||||
{
|
||||
if (!WSU(client)->handshake_key)
|
||||
{
|
||||
if (is_module_loaded("webredir"))
|
||||
{
|
||||
char *parx[2] = { NULL, NULL };
|
||||
do_cmd(client, NULL, "GET", 1, parx);
|
||||
}
|
||||
dead_socket(client, "Invalid WebSocket request");
|
||||
return 0;
|
||||
}
|
||||
if (WSU(client)->sec_websocket_protocol)
|
||||
{
|
||||
char *p = NULL, *name;
|
||||
int negotiated = 0;
|
||||
|
||||
for (name = strtoken(&p, WSU(client)->sec_websocket_protocol, ",");
|
||||
name;
|
||||
name = strtoken(&p, NULL, ","))
|
||||
{
|
||||
skip_whitespace(&name);
|
||||
if (!strcmp(name, "binary.ircv3.net"))
|
||||
{
|
||||
negotiated = WEBSOCKET_TYPE_BINARY;
|
||||
break; /* First hit wins */
|
||||
} else
|
||||
if (!strcmp(name, "text.ircv3.net") && ws_text_mode_available)
|
||||
{
|
||||
negotiated = WEBSOCKET_TYPE_TEXT;
|
||||
break; /* First hit wins */
|
||||
}
|
||||
}
|
||||
if (negotiated == WEBSOCKET_TYPE_BINARY)
|
||||
{
|
||||
WSU(client)->type = WEBSOCKET_TYPE_BINARY;
|
||||
safe_strdup(WSU(client)->sec_websocket_protocol, "binary.ircv3.net");
|
||||
} else
|
||||
if (negotiated == WEBSOCKET_TYPE_TEXT)
|
||||
{
|
||||
WSU(client)->type = WEBSOCKET_TYPE_TEXT;
|
||||
safe_strdup(WSU(client)->sec_websocket_protocol, "text.ircv3.net");
|
||||
} else
|
||||
{
|
||||
/* Negotiation failed, fallback to the default (don't set it here) */
|
||||
safe_free(WSU(client)->sec_websocket_protocol);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Handle client GET WebSocket handshake.
|
||||
* Yes, I'm going to assume that the header fits in one packet and one packet only.
|
||||
*/
|
||||
@ -500,22 +561,19 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length)
|
||||
return -1;
|
||||
}
|
||||
safe_strdup(WSU(client)->handshake_key, value);
|
||||
} else
|
||||
if (!strcasecmp(key, "Sec-WebSocket-Protocol"))
|
||||
{
|
||||
/* Save it here, will be processed later */
|
||||
safe_strdup(WSU(client)->sec_websocket_protocol, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (end_of_request)
|
||||
{
|
||||
if (!WSU(client)->handshake_key)
|
||||
{
|
||||
if (is_module_loaded("webredir"))
|
||||
{
|
||||
char *parx[2] = { NULL, NULL };
|
||||
do_cmd(client, NULL, "GET", 1, parx);
|
||||
}
|
||||
dead_socket(client, "Invalid WebSocket request");
|
||||
if (!websocket_handshake_valid(client))
|
||||
return -1;
|
||||
}
|
||||
websocket_complete_handshake(client);
|
||||
websocket_handshake_send_response(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -528,7 +586,7 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length)
|
||||
}
|
||||
|
||||
/** Complete the handshake by sending the appropriate HTTP 101 response etc. */
|
||||
int websocket_complete_handshake(Client *client)
|
||||
int websocket_handshake_send_response(Client *client)
|
||||
{
|
||||
char buf[512], hashbuf[64];
|
||||
SHA_CTX hash;
|
||||
@ -547,10 +605,21 @@ int websocket_complete_handshake(Client *client)
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: %s\r\n"
|
||||
"\r\n",
|
||||
"Sec-WebSocket-Accept: %s\r\n",
|
||||
hashbuf);
|
||||
|
||||
if (WSU(client)->sec_websocket_protocol)
|
||||
{
|
||||
/* using strlen() is safe here since above buffer will not
|
||||
* cause it to be >=512 and thus we won't get into negatives.
|
||||
*/
|
||||
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
|
||||
"Sec-WebSocket-Protocol: %s\r\n",
|
||||
WSU(client)->sec_websocket_protocol);
|
||||
}
|
||||
|
||||
strlcat(buf, "\r\n", sizeof(buf));
|
||||
|
||||
/* Caution: we bypass sendQ flood checking by doing it this way.
|
||||
* Risk is minimal, though, as we only permit limited text only
|
||||
* once per session.
|
||||
|
@ -103,15 +103,8 @@ CMD_FUNC(cmd_whois)
|
||||
if (wilds)
|
||||
continue;
|
||||
|
||||
if ((target = find_client(nick, NULL)))
|
||||
if ((target = find_person(nick, NULL)))
|
||||
{
|
||||
if (IsServer(target))
|
||||
continue;
|
||||
/*
|
||||
* I'm always last :-) and target->next == NULL!!
|
||||
*/
|
||||
if (IsMe(target))
|
||||
break;
|
||||
/*
|
||||
* 'Rules' established for sending a WHOIS reply:
|
||||
* - only send replies about common or public channels
|
||||
|
@ -35,6 +35,7 @@ ModuleHeader MOD_HEADER
|
||||
#define FIELD_OPLEVEL 0x1000 /* meaningless and stupid, but whatever */
|
||||
#define FIELD_REALHOST 0x2000
|
||||
#define FIELD_MODES 0x4000
|
||||
#define FIELD_REPUTATION 0x8000
|
||||
|
||||
#define WMATCH_NICK 0x0001
|
||||
#define WMATCH_USER 0x0002
|
||||
@ -261,6 +262,7 @@ CMD_FUNC(cmd_whox)
|
||||
case 'a': fmt.fields |= FIELD_ACCOUNT; break;
|
||||
case 'm': fmt.fields |= FIELD_MODES; break;
|
||||
case 'o': fmt.fields |= FIELD_OPLEVEL; break;
|
||||
case 'R': fmt.fields |= FIELD_REPUTATION; break;
|
||||
case ',':
|
||||
s++;
|
||||
fmt.querytype = s;
|
||||
@ -814,6 +816,13 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f
|
||||
append_format(str, sizeof str, &pos, " %s", (!isdigit(*acptr->user->svid)) ? acptr->user->svid : "0");
|
||||
if (HasField(fmt, FIELD_OPLEVEL))
|
||||
append_format(str, sizeof str, &pos, " %s", (channel && is_skochanop(acptr, channel)) ? "999" : "n/a");
|
||||
if (HasField(fmt, FIELD_REPUTATION))
|
||||
{
|
||||
if (IsOper(client))
|
||||
append_format(str, sizeof str, &pos, " %d", GetReputation(acptr));
|
||||
else
|
||||
append_format(str, sizeof str, &pos, " %s", "*");
|
||||
}
|
||||
if (HasField(fmt, FIELD_INFO))
|
||||
append_format(str, sizeof str, &pos, " :%s", acptr->info);
|
||||
|
||||
|
@ -478,7 +478,7 @@ static char *replies[] = {
|
||||
/* 435 */ NULL, /* bahamut */
|
||||
/* 436 ERR_NICKCOLLISION */ "%s :Nickname collision KILL",
|
||||
/* 437 ERR_BANNICKCHANGE */ "%s :Cannot change nickname while banned on channel",
|
||||
/* 438 ERR_NCHANGETOOFAST */ "%s :Nick change too fast. Please wait %d seconds",
|
||||
/* 438 ERR_NCHANGETOOFAST */ "%s :Nick change too fast. Please try again later.",
|
||||
/* 439 ERR_TARGETTOOFAST */ "%s :Message target change too fast. Please wait %ld seconds",
|
||||
/* 440 ERR_SERVICESDOWN */ "%s :Services are currently down. Please try again later.",
|
||||
/* 441 ERR_USERNOTINCHANNEL */ "%s %s :They aren't on that channel",
|
||||
|
@ -485,6 +485,11 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch)
|
||||
}
|
||||
}
|
||||
para[++i] = NULL;
|
||||
|
||||
/* Check if one of the message tags are rejected by spamfilter */
|
||||
if (MyConnect(from) && !IsServer(from) && match_spamfilter_mtags(from, mtags, cmptr ? cmptr->cmd : NULL))
|
||||
return;
|
||||
|
||||
if (cmptr == NULL)
|
||||
{
|
||||
do_numeric(numeric, from, mtags, i, para);
|
||||
@ -494,6 +499,7 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch)
|
||||
if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE))
|
||||
cptr->local->last = TStime();
|
||||
|
||||
/* Now ready to execute the command */
|
||||
#ifndef DEBUGMODE
|
||||
if (cmptr->flags & CMD_ALIAS)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "unrealircd.h"
|
||||
|
||||
/*
|
||||
* ircd used to store full servernames in ClientUser as well as in the
|
||||
* ircd used to store full servernames in User as well as in the
|
||||
* whowas info. there can be some 40k such structures alive at any
|
||||
* given time, while the number of unique server names a server sees in
|
||||
* its lifetime is at most a few hundred. by tokenizing server names
|
||||
|
@ -28,6 +28,7 @@
|
||||
/* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */
|
||||
|
||||
#include "unrealircd.h"
|
||||
#include <ares.h>
|
||||
#ifndef _WIN32
|
||||
/* for uname(), is POSIX so should be OK... */
|
||||
#include <sys/utsname.h>
|
||||
@ -176,10 +177,12 @@ CMD_FUNC(cmd_version)
|
||||
if (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL))
|
||||
{
|
||||
sendnotice(client, "%s", SSLeay_version(SSLEAY_VERSION));
|
||||
sendnotice(client, "%s", pcre2_version());
|
||||
sendnotice(client, "libsodium %s", sodium_version_string());
|
||||
#ifdef USE_LIBCURL
|
||||
sendnotice(client, "%s", curl_version());
|
||||
#endif
|
||||
sendnotice(client, "c-ares %s", ares_version(NULL));
|
||||
sendnotice(client, "%s", pcre2_version());
|
||||
}
|
||||
if (MyUser(client))
|
||||
send_version(client,RPL_ISUPPORT);
|
||||
|
@ -724,6 +724,34 @@ void outofmemory(size_t bytes)
|
||||
exit(7);
|
||||
}
|
||||
|
||||
/** Allocate sensitive memory - this should only be used for HIGHLY sensitive data, since
|
||||
* it wastes 8192+ bytes even if only asked to allocate for example 32 bytes (this is by design).
|
||||
* @param size How many bytes to allocate
|
||||
* @returns A pointer to the newly allocated memory.
|
||||
* @note If out of memory then the IRCd will exit.
|
||||
*/
|
||||
void *safe_alloc_sensitive(size_t size)
|
||||
{
|
||||
void *p;
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
p = sodium_malloc(((size/32)*32)+32);
|
||||
if (!p)
|
||||
outofmemory(size);
|
||||
memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Safely duplicate a string */
|
||||
char *our_strdup_sensitive(const char *str)
|
||||
{
|
||||
char *ret = safe_alloc_sensitive(strlen(str)+1);
|
||||
if (!ret)
|
||||
outofmemory(strlen(str));
|
||||
strcpy(ret, str); /* safe, see above */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Returns a unique filename in the specified directory
|
||||
* using the specified suffix. The returned value will
|
||||
* be of the form <dir>/<random-hex>.<suffix>
|
||||
@ -1307,3 +1335,18 @@ char *unreal_strftime(char *str)
|
||||
return str;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Convert a string to lowercase */
|
||||
void strtolower_safe(char *dst, char *src, int size)
|
||||
{
|
||||
if (!size)
|
||||
return; /* size of 0 is unworkable */
|
||||
size--; /* for \0 */
|
||||
|
||||
for (; *src && size; src++)
|
||||
{
|
||||
*dst++ = tolower(*src);
|
||||
size--;
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
1163
src/unrealdb.c
Normal file
1163
src/unrealdb.c
Normal file
File diff suppressed because it is too large
Load Diff
141
src/user.c
141
src/user.c
@ -142,6 +142,8 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
|
||||
{
|
||||
u_char hash = hash_target(target);
|
||||
int i;
|
||||
int max_concurrent_conversations_users, max_concurrent_conversations_new_user_every;
|
||||
FloodSettings *settings;
|
||||
|
||||
if (ValidatePermissionsForPath("immune:max-concurrent-conversations",client,NULL,NULL,NULL))
|
||||
return 0;
|
||||
@ -149,7 +151,18 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
|
||||
if (client->local->targets[0] == hash)
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < iConf.max_concurrent_conversations_users; i++)
|
||||
settings = get_floodsettings_for_user(client, FLD_CONVERSATIONS);
|
||||
max_concurrent_conversations_users = settings->limit[FLD_CONVERSATIONS];
|
||||
max_concurrent_conversations_new_user_every = settings->period[FLD_CONVERSATIONS];
|
||||
|
||||
if (max_concurrent_conversations_users <= 0)
|
||||
return 0; /* unlimited */
|
||||
|
||||
/* Shouldn't be needed, but better check here than access out-of-bounds memory */
|
||||
if (max_concurrent_conversations_users > MAXCCUSERS)
|
||||
max_concurrent_conversations_users = MAXCCUSERS;
|
||||
|
||||
for (i = 1; i < max_concurrent_conversations_users; i++)
|
||||
{
|
||||
if (client->local->targets[i] == hash)
|
||||
{
|
||||
@ -166,6 +179,7 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
|
||||
client->local->nexttarget += 2; /* punish them some more */
|
||||
client->local->since += 2; /* lag them up as well */
|
||||
|
||||
flood_limit_exceeded_log(client, "max-concurrent-conversations");
|
||||
sendnumeric(client, ERR_TARGETTOOFAST, name, client->local->nexttarget - TStime());
|
||||
|
||||
return 1;
|
||||
@ -175,15 +189,15 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
|
||||
* This is so client->local->nexttarget=0 will become client->local->nexttarget=currenttime-...
|
||||
*/
|
||||
if (TStime() > client->local->nexttarget +
|
||||
(iConf.max_concurrent_conversations_users * iConf.max_concurrent_conversations_new_user_every))
|
||||
(max_concurrent_conversations_users * max_concurrent_conversations_new_user_every))
|
||||
{
|
||||
client->local->nexttarget = TStime() - ((iConf.max_concurrent_conversations_users-1) * iConf.max_concurrent_conversations_new_user_every);
|
||||
client->local->nexttarget = TStime() - ((max_concurrent_conversations_users-1) * max_concurrent_conversations_new_user_every);
|
||||
}
|
||||
|
||||
client->local->nexttarget += iConf.max_concurrent_conversations_new_user_every;
|
||||
client->local->nexttarget += max_concurrent_conversations_new_user_every;
|
||||
|
||||
/* Add the new target (first move the rest, then add us at position 0 */
|
||||
memmove(&client->local->targets[1], &client->local->targets[0], iConf.max_concurrent_conversations_users - 1);
|
||||
memmove(&client->local->targets[1], &client->local->targets[0], max_concurrent_conversations_users - 1);
|
||||
client->local->targets[0] = hash;
|
||||
|
||||
return 0;
|
||||
@ -698,6 +712,12 @@ void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen)
|
||||
/** Called after a user is logged in (or out) of a services account */
|
||||
void user_account_login(MessageTag *recv_mtags, Client *client)
|
||||
{
|
||||
if (MyConnect(client))
|
||||
{
|
||||
find_shun(client);
|
||||
if (find_tkline_match(client, 0) && IsDead(client))
|
||||
return;
|
||||
}
|
||||
RunHook2(HOOKTYPE_ACCOUNT_LOGIN, client, recv_mtags);
|
||||
}
|
||||
|
||||
@ -811,6 +831,10 @@ void set_security_group_defaults(void)
|
||||
}
|
||||
securitygroups = NULL;
|
||||
|
||||
/* Default group: webirc */
|
||||
s = add_security_group("webirc-users", 50);
|
||||
s->webirc = 1;
|
||||
|
||||
/* Default group: known-users */
|
||||
s = add_security_group("known-users", 100);
|
||||
s->identified = 1;
|
||||
@ -919,3 +943,110 @@ char *get_connect_extinfo(Client *client)
|
||||
|
||||
return retbuf;
|
||||
}
|
||||
|
||||
/** Log a message that flood protection kicked in for the client.
|
||||
* This sends to the +f snomask at the moment.
|
||||
* @param client The client to check flood for (local user)
|
||||
* @param opt The flood option (eg FLD_AWAY)
|
||||
*/
|
||||
void flood_limit_exceeded_log(Client *client, char *floodname)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Flood blocked (%s) from %s!%s@%s [%s]",
|
||||
floodname,
|
||||
client->name,
|
||||
client->user->username,
|
||||
client->user->realhost,
|
||||
GetIP(client));
|
||||
ircd_log(LOG_FLOOD, "%s", buf);
|
||||
sendto_snomask_global(SNO_FLOOD, "%s", buf);
|
||||
}
|
||||
|
||||
/** Is the flood limit exceeded for an option? eg for away-flood.
|
||||
* @param client The client to check flood for (local user)
|
||||
* @param opt The flood option (eg FLD_AWAY)
|
||||
* @note This increments the flood counter as well.
|
||||
* @returns 1 if exceeded, 0 if not.
|
||||
*/
|
||||
int flood_limit_exceeded(Client *client, FloodOption opt)
|
||||
{
|
||||
FloodSettings *f;
|
||||
|
||||
if (!MyUser(client))
|
||||
return 0;
|
||||
|
||||
f = get_floodsettings_for_user(client, opt);
|
||||
if (f->limit[opt] <= 0)
|
||||
return 0; /* No limit set or unlimited */
|
||||
|
||||
ircd_log(LOG_ERROR, "Checking flood_limit_exceeded() for '%s', type %d with max %d:%ld...",
|
||||
client->name, (int)opt, (int)f->limit[opt], (long)f->period[opt]);
|
||||
|
||||
/* Ok, let's do the flood check */
|
||||
if ((client->local->flood[opt].t + f->period[opt]) <= timeofday)
|
||||
{
|
||||
/* Time exceeded, reset */
|
||||
client->local->flood[opt].count = 0;
|
||||
client->local->flood[opt].t = timeofday;
|
||||
}
|
||||
if (client->local->flood[opt].count <= f->limit[opt])
|
||||
client->local->flood[opt].count++;
|
||||
if (client->local->flood[opt].count > f->limit[opt])
|
||||
{
|
||||
flood_limit_exceeded_log(client, floodoption_names[opt]);
|
||||
return 1; /* Flood limit hit! */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Get the appropriate anti-flood settings block for this user.
|
||||
* @param client The client, should be locally connected.
|
||||
* @param opt The flood option we are interested in
|
||||
* @returns The FloodSettings for this user, never returns NULL.
|
||||
*/
|
||||
FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt)
|
||||
{
|
||||
SecurityGroup *s;
|
||||
FloodSettings *f;
|
||||
|
||||
/* Go through all security groups by order of priority
|
||||
* (eg: first "known-users", then "unknown-users").
|
||||
* For each of these:
|
||||
* - Check if a set::anti-flood::xxxx block exists for this group
|
||||
* - Check if the limit is non-zero (eg there is any limit set)
|
||||
* If any of these are false then we continue with next block
|
||||
* that matches.
|
||||
*/
|
||||
|
||||
// XXX: alternatively, instead of this double loop,
|
||||
// do a post-conf thing and sort iConf.floodsettings
|
||||
// according to the security-group { } order.
|
||||
for (s = securitygroups; s; s = s->next)
|
||||
{
|
||||
if (user_allowed_by_security_group(client, s) &&
|
||||
((f = find_floodsettings_block(s->name))) &&
|
||||
f->limit[opt])
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return default settings block (which may have a zero limit set) */
|
||||
f = find_floodsettings_block("unknown-users");
|
||||
if (!f)
|
||||
abort(); /* impossible */
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
MODVAR char *floodoption_names[] = {
|
||||
"nick-flood",
|
||||
"join-flood",
|
||||
"away-flood",
|
||||
"invite-flood",
|
||||
"knock-flood",
|
||||
"max-concurrent-conversations",
|
||||
NULL
|
||||
};
|
||||
|
@ -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.9"
|
||||
id="5.2.0.1"
|
||||
echo "$id"
|
||||
|
||||
if test -r version.c
|
||||
@ -133,7 +133,8 @@ char *unrealcredits[] =
|
||||
"Underfloor Heating Systems - www.underfloorheatingsystems.co.uk,",
|
||||
"Project Free TV - newprojectfreetv.com, companyaccountscheck.com,",
|
||||
"Coyote Traffic - www.coyotetraffic.com, www.emailextractor14lite.com,",
|
||||
"bet365 Bonus Code - williamspromocodes.co.uk/bet365-bonus-code-sports/",
|
||||
"bet365 Bonus Code - williamspromocodes.co.uk/bet365-bonus-code-sports/,",
|
||||
"Valerie Amelia Pond",
|
||||
" ",
|
||||
"==========================[ Supporters ]==========================",
|
||||
"Our support staff: Cronus, Jobe, SpaceDog, Stealth",
|
||||
|
@ -3,7 +3,7 @@
|
||||
<assemblyIdentity
|
||||
processorArchitecture="amd64"
|
||||
name="UnrealIRCd.UnrealIRCd.5"
|
||||
version="5.0.9.0"
|
||||
version="5.2.0.1"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Internet Relay Chat Daemon</description>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
[Setup]
|
||||
AppName=UnrealIRCd 5
|
||||
AppVerName=UnrealIRCd 5.0.9
|
||||
AppVerName=UnrealIRCd 5.2.0.1
|
||||
AppPublisher=UnrealIRCd Team
|
||||
AppPublisherURL=https://www.unrealircd.org
|
||||
AppSupportURL=https://www.unrealircd.org
|
||||
@ -72,6 +72,7 @@ Source: "src\modules\third\*.dll"; DestDir: "{app}\modules\third"; Flags: ignore
|
||||
|
||||
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
|
||||
Source: "c:\dev\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "c:\dev\unrealircd-5-libs\c-ares\msvc\cares\dll-release\cares.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "c:\dev\unrealircd-5-libs\libressl\bin\openssl.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "c:\dev\unrealircd-5-libs\libressl\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
|
@ -53,17 +53,34 @@ if [ "$1" = "start" ] ; then
|
||||
# Now check if we need to create a crash report.
|
||||
@BINDIR@/unrealircd -R
|
||||
elif [ "$1" = "stop" ] ; then
|
||||
echo "Stopping UnrealIRCd"
|
||||
echo -n "Stopping UnrealIRCd"
|
||||
if [ ! -r $PID_FILE ] ; then
|
||||
echo
|
||||
echo "ERROR: UnrealIRCd is not running"
|
||||
exit 1
|
||||
fi
|
||||
kill -15 `cat $PID_FILE`
|
||||
if [ "$?" != 0 ]; then
|
||||
echo
|
||||
echo "ERROR: UnrealIRCd is not running"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
# Wait for UnrealIRCd to terminate, but wait 10 seconds max
|
||||
n="0"
|
||||
while [ "$n" -lt 10 ]
|
||||
do
|
||||
echo -n "."
|
||||
if [ ! -r $PID_FILE ] ; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 `cat $PID_FILE`; then
|
||||
break
|
||||
fi
|
||||
n=`expr $n + 1`
|
||||
sleep 1
|
||||
done
|
||||
echo
|
||||
# In case it is still running, kill it for good.
|
||||
if [ -r $PID_FILE ] ; then
|
||||
kill -9 `cat $PID_FILE` 1>/dev/null 2>&1
|
||||
fi
|
||||
@ -80,17 +97,7 @@ elif [ "$1" = "rehash" ] ; then
|
||||
fi
|
||||
elif [ "$1" = "restart" ] ; then
|
||||
echo "Restarting UnrealIRCd"
|
||||
if [ ! -r $PID_FILE ] ; then
|
||||
echo "WARNING: UnrealIRCd was not running"
|
||||
else
|
||||
kill -15 `cat $PID_FILE`
|
||||
if [ "$?" != 0 ]; then
|
||||
echo "WARNING: UnrealIRCd was not running"
|
||||
else
|
||||
sleep 1
|
||||
kill -9 `cat $PID_FILE` 1>/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
$0 stop
|
||||
$0 start
|
||||
elif [ "$1" = "croncheck" ] ; then
|
||||
if [ -r $PID_FILE ] ; then
|
||||
@ -212,7 +219,7 @@ __EOF__
|
||||
echo '2. If you are using 3rd party modules we might request you'
|
||||
echo ' to run without them and verify you still crash. This is'
|
||||
echo ' to eleminate any loss of time due to bugs made by others'
|
||||
echo '3. Always use the latest UnrealIRCd version, we fix (crash)bugs'
|
||||
echo '3. Use a reasonably recent UnrealIRCd version. We fix (crash)bugs'
|
||||
echo ' all the time so your bug might as well be fixed already.'
|
||||
echo ""
|
||||
echo "Thanks!"
|
||||
|
Loading…
Reference in New Issue
Block a user