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