*.swp
/.config.cache
+/.gdbargs
/.modulemanager
/BSDmakefile
/GNUmakefile
/build
/inspircd
+/inspircd.1
+/inspircd-genssl.1
+/inspircd.service
/org.inspircd.plist
/run
/bin
-/include/inspircd_config.h
-/include/inspircd_version.h
+/include/config.h
/src/modules/m_geoip.cpp
/src/modules/m_ldapauth.cpp
/src/modules/m_pgsql.cpp
/src/modules/m_regex_pcre.cpp
/src/modules/m_regex_posix.cpp
+/src/modules/m_regex_re2.cpp
/src/modules/m_regex_stdlib.cpp
/src/modules/m_regex_tre.cpp
/src/modules/m_sqlite3.cpp
+### Important Notice
+
+The `master` branch contains the latest development version. If you are running
+a server then you probably want the `insp20` branch. You can obtain this from
+the [releases](https://github.com/inspircd/inspircd/releases) page or by running
+`git checkout insp20` if you are installing via Git.
+
### About
InspIRCd is a modular Internet Relay Chat (IRC) server written in C++ for Linux,
use strict;
use warnings FATAL => qw(all);
-use Data::Dumper;
-BEGIN {
- $Data::Dumper::Sortkeys = 1;
- $Data::Dumper::Useqq = 1;
-};
-
use File::Copy ();
-use Socket;
+use File::Spec::Functions qw(rel2abs);
use Cwd;
use Getopt::Long;
-# Utility functions for our buildsystem
-use make::utilities;
use make::configure;
-use make::gnutlscert;
-use make::opensslcert;
-
-###############################################################################################
-#
-# NON-EDITABLE VARIABLES
-#
-###############################################################################################
-
-our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
- $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
- $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port,
- $opt_system, $opt_uid);
+use make::utilities;
-our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir, $opt_data_dir, $opt_log_dir);
+our ($opt_use_gnutls, $opt_use_openssl, $opt_nointeractive, $opt_socketengine,
+ $opt_system, $opt_uid, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir,
+ $opt_data_dir, $opt_log_dir);
sub list_extras ();
GetOptions (
'enable-gnutls' => \$opt_use_gnutls,
- 'rebuild' => \$opt_rebuild,
'system' => \$opt_system,
'uid=s' => \$opt_uid,
'enable-openssl' => \$opt_use_openssl,
'disable-interactive' => \$opt_nointeractive,
- 'enable-ports' => \$opt_ports,
- 'enable-epoll' => \$opt_epoll,
- 'enable-kqueue' => \$opt_kqueue,
- 'disable-ports' => \$opt_noports,
- 'disable-epoll' => \$opt_noepoll,
- 'disable-kqueue' => \$opt_nokqueue,
- 'disable-ipv6' => \$opt_noipv6,
- 'with-cc=s' => \$opt_cc,
- 'with-maxbuf=i' => \$opt_maxbuf,
- 'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
+ 'socketengine=s' => \$opt_socketengine,
'prefix=s' => \$opt_base_dir,
'config-dir=s' => \$opt_config_dir,
'module-dir=s' => \$opt_module_dir,
'binary-dir=s' => \$opt_binary_dir,
'data-dir=s' => \$opt_data_dir,
'log-dir=s' => \$opt_log_dir,
- 'disable-debuginfo' => sub { $opt_disable_debug = 1 },
- 'help' => sub { showhelp(); },
- 'update' => sub { update(); },
- 'clean' => sub { clean(); },
+ 'help' => \&cmd_help,
+ 'update' => \&cmd_update,
+ 'clean' => \&cmd_clean,
'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
'enable-extras=s@' => \@opt_enableextras, # ^
'disable-extras=s@' => \@opt_disableextras, # ^
- 'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
- 'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
);
if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
(defined $opt_data_dir) ||
(defined $opt_log_dir) ||
(defined $opt_nointeractive) ||
- (defined $opt_cc) ||
- (defined $opt_noipv6) ||
- (defined $opt_kqueue) ||
- (defined $opt_epoll) ||
- (defined $opt_ports) ||
+ (defined $opt_socketengine) ||
(defined $opt_use_openssl) ||
- (defined $opt_nokqueue) ||
- (defined $opt_noepoll) ||
- (defined $opt_noports) ||
- (defined $opt_maxbuf) ||
(defined $opt_system) ||
(defined $opt_uid) ||
- (defined $opt_use_gnutls) ||
- (defined $opt_freebsd_port)
+ (defined $opt_use_gnutls)
);
-chomp(our $topdir = getcwd());
-our $this = resolve_directory($topdir); # PWD, Regardless.
-our @modlist = (); # Declare for Module List..
-our %config = (); # Initiate Configuration Hash..
-our $cache_loaded = getcache();
-$config{ME} = resolve_directory($topdir); # Present Working Directory
+our %config = read_configure_cache();
+
+print "Checking for cache from previous configure... ";
+print %config ? "found\n" : "not found\n";
-$config{BASE_DIR} = $config{ME}."/run";
+$config{BASE_DIR} = getcwd()."/run";
if (defined $opt_base_dir) {
$config{BASE_DIR} = $opt_base_dir;
}
if (defined $opt_system) {
- $config{UID} = $opt_uid || 'ircd';
- $config{CONFIG_DIR} = '/etc/inspircd';
- $config{MODULE_DIR} = '/usr/lib/inspircd';
- $config{BINARY_DIR} = '/usr/sbin/';
- $config{BUILD_DIR} = resolve_directory($config{ME}."/build"); # Build Directory
- $config{DATA_DIR} = '/var/inspircd';
- $config{LOG_DIR} = '/var/log/inspircd';
+ $config{UID} = defined $opt_uid ? $opt_uid : 'ircd';
+ $config{CONFIG_DIR} = '/etc/inspircd';
+ $config{MODULE_DIR} = '/usr/lib/inspircd';
+ $config{BINARY_DIR} = '/usr/sbin/';
+ $config{DATA_DIR} = '/var/inspircd';
+ $config{LOG_DIR} = '/var/log/inspircd';
} else {
- $config{UID} = $opt_uid || $<;
- $config{CONFIG_DIR} = resolve_directory($config{BASE_DIR}."/conf"); # Configuration Directory
- $config{MODULE_DIR} = resolve_directory($config{BASE_DIR}."/modules"); # Modules Directory
- $config{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory
- $config{BUILD_DIR} = resolve_directory($config{ME}."/build"); # Build Directory
- $config{DATA_DIR} = resolve_directory($config{BASE_DIR}."/data"); # Data directory
- $config{LOG_DIR} = resolve_directory($config{BASE_DIR}."/logs"); # Log directory
+ $config{UID} = defined $opt_uid ? $opt_uid : $<;
+ $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
+ $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
+ $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
+ $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
+ $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
}
if (defined $opt_config_dir) {
if (defined $opt_log_dir) {
$config{LOG_DIR} = $opt_log_dir;
}
-chomp($config{HAS_GNUTLS} = `pkg-config --modversion gnutls 2>/dev/null`); # GNUTLS Version.
-
-if (defined $opt_freebsd_port)
-{
- chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
- chomp($config{HAS_OPENSSL_PORT} = `pkg-config --modversion openssl 2>/dev/null`);
- $config{USE_FREEBSD_BASE_SSL} = "n";
-}
-else
-{
- if ($^O eq "freebsd")
- {
- # default: use base ssl
- chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`); # OpenSSL version, freebsd specific
- chomp($config{HAS_OPENSSL_PORT} = `pkg-config --modversion openssl 2>/dev/null`); # Port version, may be different
- }
- else
- {
- chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`); # Openssl version, others
- $config{HAS_OPENSSL_PORT} = "";
- $config{USE_FREEBSD_BASE_SSL} = "n";
- }
-}
+chomp($config{HAS_GNUTLS} = `pkg-config --modversion gnutls 2>/dev/null`);
+chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
chomp(our $gnutls_ver = $config{HAS_GNUTLS});
chomp(our $openssl_ver = $config{HAS_OPENSSL});
-$config{USE_GNUTLS} = "n";
+$config{USE_GNUTLS} = 0;
if (defined $opt_use_gnutls)
{
$config{USE_GNUTLS} = "y"; # Use gnutls.
}
-$config{USE_OPENSSL} = "n"; # Use openssl.
+$config{USE_OPENSSL} = 0; # Use openssl.
if (defined $opt_use_openssl)
{
$config{USE_OPENSSL} = "y";
}
-if (!defined $opt_disable_debug) {
- $config{OPTIMISATI} = "-g1"; # Optimisation Flag
-} else {
- $config{OPTIMISATI} = "-O2";
+$config{CXX} = defined $ENV{CXX} && !system("$ENV{CXX} -v > /dev/null 2>&1") ? $ENV{CXX} : find_compiler();
+if ($config{CXX} eq "") {
+ print "A C++ compiler could not be detected on your system!\n";
+ print "Set the CXX environment variable to the full path if this is incorrect.\n";
+ exit 1;
}
-$config{HAS_STRLCPY} = "false"; # strlcpy Check.
-$config{HAS_STDINT} = "false"; # stdint.h check
-$config{USE_KQUEUE} = "y"; # kqueue enabled
-if (defined $opt_nokqueue) {
- $config{USE_KQUEUE} = "n";
-}
-$config{USE_POLL} = "y"; # poll enabled
-$config{USE_EPOLL} = "y"; # epoll enabled
-if (defined $opt_noepoll)
-{
- $config{USE_EPOLL} = "n";
-}
-$config{USE_PORTS} = "y"; # epoll enabled
-if (defined $opt_noports)
-{
- $config{USE_PORTS} = "n";
+our %cxx = get_compiler_info($config{CXX});
+if ($cxx{UNSUPPORTED}) {
+ print "Your C++ compiler is too old to build InspIRCd!\n";
+ print "Reason: $cxx{REASON}\n";
+ exit 1;
}
-$config{_SOMAXCONN} = SOMAXCONN; # Max connections in accept queue
-$config{OSNAME} = $^O; # Operating System Name
-$config{IS_DARWIN} = "NO"; # Is OSX?
-$config{STARTSCRIPT} = "inspircd"; # start script?
-$config{DESTINATION} = "BASE"; # Is target path.
-if ($config{OSNAME} =~ /darwin/i)
-{
- $config{IS_DARWIN} = "YES";
- $config{STARTSCRIPT} = "org.inspircd.plist"; # start script for OSX.
- $config{CC} = "xcrun clang++"; # C++ compiler for OSX.
-}
-elsif ($config{OSNAME} =~ /freebsd/i)
-{
- chomp(my $fbsd_version = `uname -r`);
- $config{CC} = $fbsd_version ge '10.0' ? 'clang++' : 'g++';
-}
-else
-{
- $config{CC} = "g++"; # C++ compiler
-}
-if (defined $opt_cc)
-{
- $config{CC} = $opt_cc;
-}
-our $exec = $config{CC} . " -dumpversion | cut -c 1";
-chomp($config{GCCVER} = `$exec`); # Major GCC Version
-$exec = $config{CC} . " -dumpversion | cut -c 3";
-chomp($config{GCCMINOR} = `$exec`);
-$config{MAXBUF} = "512"; # Max buffer size
if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
$config{HAS_OPENSSL} = $1;
$config{HAS_OPENSSL} = "";
}
-if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
- print "`$config{CC}` was not found! A C++ compiler is required to build InspIRCd!\n";
- print "You can pass a custom compiler to $0 using --with-cc=[name].\n";
- exit;
-}
-
-# Get and Set some important vars..
-getmodules();
+$config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
+$config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
-sub clean
-{
- unlink(".config.cache");
+if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
+ $config{SOCKETENGINE} ||= 'epoll';
}
-our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
-
-sub update
-{
- eval {
- chomp($topdir = getcwd());
- $this = resolve_directory($topdir); # PWD, Regardless.
- getmodules();
- # Does the cache file exist?
- if (!getcache()) {
- # No, No it doesn't.. *BASH*
- print "You have not run ./configure before. Please do this before trying to run the update script.\n";
- exit 0;
- } else {
- # We've Loaded the cache file and all our variables..
- print "Updating files...\n";
- if (defined($opt_disable_debug) && $opt_disable_debug == 1)
- {
- print "Disabling debug information (-g).\n";
- $config{OPTIMISATI} = "";
- }
- $has_epoll = $config{HAS_EPOLL};
- $has_ports = $config{HAS_PORTS};
- $has_kqueue = $config{HAS_KQUEUE};
- writefiles(1);
- makecache();
- print "Complete.\n";
- exit;
- }
- };
- if ($@)
- {
- print "Configure update failed: $@\n";
- }
- exit;
+if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
+ $config{SOCKETENGINE} ||= 'kqueue';
}
-
-sub test_compile {
- my $feature = shift;
- my $fail = 0;
- $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
- $fail ||= system "./test_$feature";
- unlink "test_$feature";
- return !$fail;
+if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
+ $config{SOCKETENGINE} ||= 'ports';
}
-print "Running non-interactive configure...\n" unless $interactive;
-print "Checking for cache from previous configure... ";
-print ($cache_loaded ? "found\n" : "not found\n");
-$config{SYSTEM} = lc $^O;
-print "Checking operating system version... $config{SYSTEM}\n";
-
-$exec = $config{CC} . " -dumpversion | cut -c 1";
-chomp($config{GCCVER} = `$exec`); # Major GCC Version
-$exec = $config{CC} . " -dumpversion | cut -c 3";
-chomp($config{GCCMINOR} = `$exec`);
-
-printf "Checking if stdint.h exists... ";
-$config{HAS_STDINT} = "true";
-our $fail = 0;
-open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
-if ($config{HAS_STDINT} eq "true") {
- close(STDINT);
+if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
+ $config{SOCKETENGINE} ||= 'poll';
}
-print "yes\n" if $config{HAS_STDINT} eq "true";
-print "no\n" if $config{HAS_STDINT} eq "false";
-printf "Checking if strlcpy exists... ";
-# Perform the strlcpy() test..
-$config{HAS_STRLCPY} = "false";
-$fail = 0;
-open(STRLCPY, "</usr/include/string.h") or $fail = 1;
-if (!$fail) {
- while (defined(my $line = <STRLCPY>)) {
- chomp($line);
- # try and find the delcaration of:
- # size_t strlcpy(...)
- if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
- $config{HAS_STRLCPY} = "true";
- }
- }
- close(STRLCPY);
-}
-print "yes\n" if $config{HAS_STRLCPY} eq "true";
-print "no\n" if $config{HAS_STRLCPY} eq "false";
+# Select is available on all platforms
+$config{HAS_SELECT} = 1;
+$config{SOCKETENGINE} ||= "select";
-printf "Checking if kqueue exists... ";
-$has_kqueue = 0;
-$fail = 0;
-open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
-if (!$fail) {
- while (defined(my $line = <KQUEUE>)) {
- chomp($line);
- # try and find the delcaration of:
- # int kqueue(void);
- if ($line =~ /int(\0x9|\s)+kqueue/) {
- $has_kqueue = 1;
+if (defined $opt_socketengine) {
+ my $cfgkey = "HAS_" . uc $opt_socketengine;
+ if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
+ $config{SOCKETENGINE} = $opt_socketengine;
+ } else {
+ print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
+ print "Available socket engines are:";
+ foreach (<src/socketengines/socketengine_*.cpp>) {
+ s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
+ print " $1" if $config{"HAS_" . uc $1};
}
+ print "\n";
+ exit 1;
}
- close(KQUEUE);
}
-print "yes\n" if $has_kqueue == 1;
-print "no\n" if $has_kqueue == 0;
-
-printf "Checking for epoll support... ";
-$has_epoll = test_compile('epoll');
-print $has_epoll ? "yes\n" : "no\n";
-
-printf "Checking for eventfd support... ";
-$config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
-print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
-printf "Checking if Solaris I/O completion ports are available... ";
-$has_ports = 0;
-our $system = `uname -s`;
-chomp ($system);
-$has_ports = 1 if ($system eq "SunOS");
-
-if ($has_ports) {
- my $kernel = `uname -r`;
- chomp($kernel);
- if (($kernel !~ /^5\.1./)) {
- $has_ports = 0;
- }
-}
-print "yes\n" if $has_ports == 1;
-print "no\n" if $has_ports == 0;
-
-$config{HAS_EPOLL} = $has_epoll;
-$config{HAS_KQUEUE} = $has_kqueue;
-
-printf "Checking for libgnutls... ";
+print "Checking for libgnutls... ";
if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
print "yes\n";
$config{HAS_GNUTLS} = "n";
}
-printf "Checking for openssl... ";
+print "Checking for openssl... ";
if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
if (defined($openssl_ver) && ($openssl_ver ne "")) {
print "yes\n";
$config{HAS_OPENSSL} = "n";
}
-printf "Checking if you are running an ancient, unsupported OS... ";
-if ($config{OSNAME} =~ /FreeBSD/i)
-{
- my $version = `uname -r`;
- if ($version =~ /^4\./)
- {
- print "yes.\n";
- print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
- print "To build, you will need to add the following to CXXFLAGS:\n";
- print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
- }
- else
- {
- print "no ($version)\n";
- }
-}
-else
-{
- print "no ($config{OSNAME})\n";
-}
-
-################################################################################
-# BEGIN INTERACTIVE PART #
-################################################################################
-
-# Clear the Screen..
if ($interactive)
{
- print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
- my $wholeos = $^O;
+ # Clear the screen.
+ system 'tput', 'clear';
+
+ my %version = get_version();
- my $rev = getrevision();
# Display Introduction Message..
print <<"STOP" ;
Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
dir, otherwise you won't have a config file!
-Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
-Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
+Your operating system is: \e[1;32m$^O\e[0m
STOP
- if ($rev eq "r0") {
- print " (Non-SVN build)";
- }
- print ".\n\n";
+ print "Your InspIRCd version is: \e[1;32m";
+ print "$version{MAJOR}.$version{MINOR}.$version{PATCH}+$version{LABEL}";
+ print "\e[0m\n\n";
+ print "The following compiler has been detected: \e[1;32m$cxx{NAME} $cxx{VERSION}\e[0m ($config{CXX})\n\n";
- $config{CHANGE_COMPILER} = "n";
- print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
+ # Check that the user actually wants this version.
+ if ($version{LABEL} ne 'release') {
+ print <<"EOW" ;
+\e[1;31mWARNING!\e[0m You are building a development version. This contains code which has
+not been tested as heavily and may contain various faults which could seriously
+affect the running of your server. It is recommended that you use a stable
+version instead.
- while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
- print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
-should NOT be used. You should probably specify a newer compiler.\n\n";
- yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
- if ($config{CHANGE_COMPILER} =~ /y/i) {
- print "What command do you want to use to invoke your compiler?\n";
- print "[\e[1;32m$config{CC}\e[0m] -> ";
- chomp($config{CC} = <STDIN>);
- if ($config{CC} eq "") {
- $config{CC} = "g++";
- }
- chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
- if ($foo ne "") {
- chomp($config{GCCVER} = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
- chomp($config{GCCMINOR} = `$config{CC} -dumpversion | cut -c 3`);
- print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
- if ($config{GCCVER} < 3) {
- print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
- }
- }
- else {
- print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
- }
- }
- }
+You can obtain the latest stable version from https://github.com/inspircd/inspircd/releases
+or by running `git checkout insp20` if you are installing from Git.
- print "\n";
+EOW
+ exit 1 unless prompt_bool(1, 'I understand this warning and want to continue anyway.', 0);
+ }
# Directory Settings..
my $tmpbase = $config{BASE_DIR};
- dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
+ $config{BASE_DIR} = prompt_dir(1, 'What directory do you wish to install the InspIRCd base?', $config{BASE_DIR});
if ($tmpbase ne $config{BASE_DIR}) {
- $config{CONFIG_DIR} = resolve_directory($config{BASE_DIR}."/conf"); # Configuration Dir
- $config{MODULE_DIR} = resolve_directory($config{BASE_DIR}."/modules"); # Modules Directory
- $config{DATA_DIR} = resolve_directory($config{BASE_DIR}."/data"); # Data Directory
- $config{LOG_DIR} = resolve_directory($config{BASE_DIR}."/logs"); # Log Directory
- $config{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory
+ $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
+ $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
+ $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
+ $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
+ $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
}
- dir_check("are the configuration files", "CONFIG_DIR");
- dir_check("are the modules to be compiled to", "MODULE_DIR");
- dir_check("is the IRCd binary to be placed", "BINARY_DIR");
- dir_check("are variable data files to be located in", "DATA_DIR");
- dir_check("are the logs to be stored in", "LOG_DIR");
- dir_check("do you want the build to take place", "BUILD_DIR");
-
+ $config{BINARY_DIR} = prompt_dir(1, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR});
+ $config{CONFIG_DIR} = prompt_dir(1, 'In what directory are the configuration files to be stored?', $config{CONFIG_DIR});
+ $config{DATA_DIR} = prompt_dir(1, 'In what directory are variable data files to be stored?', $config{DATA_DIR});
+ $config{LOG_DIR} = prompt_dir(1, 'In what directory are log files to be stored?', $config{LOG_DIR});
+ $config{MODULE_DIR} = prompt_dir(1, 'In what directory are the modules to be placed?', $config{MODULE_DIR});
+
my $chose_hiperf = 0;
- if ($has_kqueue) {
- yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
- print "\n";
- if ($config{USE_KQUEUE} eq "y") {
+ if ($config{HAS_KQUEUE}) {
+ $config{USE_KQUEUE} = prompt_bool(1, 'Your operating system has support for the high performance kqueue socket engine. Would you like to enable it?', 1);
+ if ($config{USE_KQUEUE}) {
+ $config{SOCKETENGINE} = "kqueue";
$chose_hiperf = 1;
}
}
- if ($has_epoll) {
- yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
- print "\n";
- if ($config{USE_EPOLL} eq "y") {
+ if ($config{HAS_EPOLL}) {
+ $config{USE_EPOLL} = prompt_bool(1, 'Your operating system has support for the high performance epoll socket engine. Would you like to enable it?', 1);
+ if ($config{USE_EPOLL}) {
+ $config{SOCKETENGINE} = "epoll";
$chose_hiperf = 1;
}
}
- if ($has_ports) {
- yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
- print "\n";
- if ($config{USE_PORTS} eq "y") {
+ if ($config{HAS_PORTS}) {
+ $config{USE_PORTS} = prompt_bool(1, 'Your operating system has support for the high performance IOCP socket engine. Would you like to enable it?', 1);
+ if ($config{USE_PORTS}) {
+ $config{SOCKETENGINE} = "ports";
$chose_hiperf = 1;
}
}
- if (!$chose_hiperf) {
- yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
- if ($config{USE_POLL} ne "y")
- {
- print "No high-performance socket engines are available, or you chose\n";
- print "not to enable one. Defaulting to select() engine.\n\n";
+ if (!$chose_hiperf && $config{HAS_POLL}) {
+ $config{USE_POLL} = prompt_bool(1, 'Your operating system has support for the mid performance poll socket engine. Would you like to enable it?', 1);
+ if ($config{USE_POLL}) {
+ $config{SOCKETENGINE} = "poll";
}
}
-
- $config{USE_FREEBSD_BASE_SSL} = "n";
- $config{USE_FREEBSD_PORTS_SSL} = "n";
- if ($config{HAS_OPENSSL_PORT} ne "")
+ unless ($chose_hiperf || $config{USE_POLL})
{
- $config{USE_FREEBSD_PORTS_SSL} = "y";
- print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
- print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
- yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
- print "\n";
- $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
-
- if ($config{USE_FREEBSD_BASE_SSL} eq "n")
- {
- # update to port version
- $openssl_ver = $config{HAS_OPENSSL_PORT};
- }
- }
- else
- {
- $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
+ print "No high-performance socket engines are available, or you chose not to enable one. Defaulting to select() engine.\n\n";
+ $config{SOCKETENGINE} = "select";
}
- $config{USE_SSL} = "n";
- $config{MODUPDATE} = 'n';
-
if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
{
print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
- yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
- if ($config{USE_SSL} eq "y")
+ $config{USE_SSL} = prompt_bool(1, 'One or more SSL libraries detected. Would you like to enable SSL support?', 1);
+ if ($config{USE_SSL})
{
if ($config{HAS_GNUTLS} eq "y")
{
- yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
- if ($config{USE_GNUTLS} eq "y")
+ $config{USE_GNUTLS} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_gnutls (recommended)?', 1);
+ if ($config{USE_GNUTLS})
{
- print "\nUsing GnuTLS SSL module.\n";
+ print "Using GnuTLS SSL module.\n\n";
+ unlink 'src/modules/m_ssl_gnutls.cpp' if -f 'src/modules/m_ssl_gnutls.cpp';
+ symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!\n";
}
}
if ($config{HAS_OPENSSL} eq "y")
{
- yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
- if ($config{USE_OPENSSL} eq "y")
+ $config{USE_OPENSSL} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_openssl (recommended)?', 1);
+ if ($config{USE_OPENSSL})
{
- print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
+ print "Using OpenSSL SSL module.\n\n";
+ unlink 'src/modules/m_ssl_openssl.cpp' if -f 'src/modules/m_ssl_openssl.cpp';
+ symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!\n";
}
}
}
print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
print "is in your path.\n\n";
}
-
- yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
- print "\n";
- if ($config{MODUPDATE} eq "y") {
- print "Checking for upgrades to extra and third-party modules... ";
- system "./modulemanager upgrade";
- }
}
# We are on a POSIX system, we can enable POSIX extras without asking
symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
-dumphash();
-
-if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
+if (($config{USE_GNUTLS}) && ($config{HAS_GNUTLS} ne "y"))
{
- print "Sorry, but I couldn't detect GnuTLS. Make sure gnutls-config is in your path.\n";
- exit(0);
+ print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
+ exit 1;
}
-if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
+if (($config{USE_OPENSSL}) && ($config{HAS_OPENSSL} ne "y"))
{
- print "Sorry, but I couldn't detect OpenSSL. Make sure openssl is in your path.\n";
- exit(0);
+ print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config is in your path.\n";
+ exit 1;
}
-our $failed = 0;
-$config{CERTGEN} ||= 'y';
-yesno('CERTGEN',"Would you like to generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
-
-if ($config{USE_GNUTLS} eq "y") {
- unless (-r "src/modules/m_ssl_gnutls.cpp") {
- print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
- symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
- }
- if ($interactive && $config{CERTGEN} eq 'y')
- {
- unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
- print "SSL certificates not found, generating.. \n\n
-*************************************************************
-* Generating the private key may take some time, once done, *
-* answer the questions which follow. If you are unsure, *
-* just hit enter! *
-*************************************************************\n\n";
- $failed = make_gnutls_cert();
- if ($failed) {
- print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
- } else {
+if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
+ if (my $val = prompt_bool($interactive, 'Would you like to generate SSL certificates now?', $interactive)) {
+ unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem" && -r "$config{CONFIG_DIR}/dhparams.pem") {
+ unless (system './tools/genssl auto') {
print "\nCertificate generation complete, copying to config directory... ";
File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
+ File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
print "Done.\n\n";
}
- }
- else {
- print "SSL certificates found, skipping.\n\n";
- }
- }
- else
- {
- print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
- }
-}
-
-if ($config{USE_OPENSSL} eq "y") {
- unless (-r "src/modules/m_ssl_openssl.cpp") {
- print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
- symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
- }
- $failed = 0;
- if ($interactive && $config{CERTGEN} eq 'y')
- {
- unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
- print "SSL certificates not found, generating.. \n\n
-*************************************************************
-* Generating the certificates may take some time, go grab a *
-* coffee or something. *
-*************************************************************\n\n";
- make_openssl_cert();
- print "\nCertificate generation complete, copying to config directory... ";
- File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
- File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
- File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
- print "Done.\n\n";
} else {
print "SSL certificates found, skipping.\n\n"
}
+ } else {
+ print "Skipping SSL certificate generation in non-interactive mode.\n\n";
}
- else
- {
- print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
- }
-}
-if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
- print "Skipping SSL certificate generation as SSL support is not available.\n\n";
+} else {
+ print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
}
-depcheck();
-writefiles(1);
-makecache();
+print "Writing \e[1;32m.config.cache\e[0m ...\n";
+write_configure_cache(%config);
+parse_templates(\%config, \%cxx);
+dump_hash();
-print "\n\n";
+print "\n";
print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
-if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
+if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
print "modules in your config. This configure script has added those modules to the\n";
print "build process. For more info, please refer to:\n";
print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
}
-print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
-if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
- print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but I'm letting you\ngo ahead with it anyway, just in case I'm wrong :-)\n";
-}
-
-if ($config{GCCVER} < "3") {
- print <<FOO2;
-\e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
-GCC 2.x series compilers only had partial (read as broken) C++ support, and
-your compile will most likely fail horribly! If you have any problems, do NOT
-report them to the bugtracker or forums without first upgrading your compiler
-to a newer 3.x or 4.x (or whatever is available currently) version.
-FOO2
-}
-
-################################################################################
-# HELPER FUNCTIONS #
-################################################################################
-sub getcache {
- # Retrieves the .config.cache file, and loads values into the main config hash.
- open(CACHE, ".config.cache") or return 0;
- while (<CACHE>) {
- chomp;
- # Ignore Blank lines, and comments..
- next if /^\s*$/;
- next if /^\s*#/;
- my ($key, $value) = split("=", $_, 2);
- $value =~ /^\"(.*)\"$/;
- # Do something with data here!
- $config{$key} = $1;
- }
- close(CACHE);
- return 1;
-}
-
-sub makecache {
- # Dump the contents of %config
- print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
- open(FILEHANDLE, ">.config.cache");
- foreach my $key (keys %config) {
- print FILEHANDLE "$key=\"$config{$key}\"\n";
- }
- close(FILEHANDLE);
-}
-
-sub dir_check {
- my ($desc, $hash_key) = @_;
- my $complete = 0;
- while (!$complete) {
- print "In what directory $desc?\n";
- print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
- chomp(my $var = <STDIN>);
- if ($var eq "") {
- $var = $config{$hash_key};
- }
- if ($var =~ /^\~\/(.+)$/) {
- # Convert it to a full path..
- $var = resolve_directory($ENV{HOME} . "/" . $1);
- }
- elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
- {
- # Assume relative Path was given.. fill in the rest.
- $var = $this . "/$var";
- }
-
- $var = resolve_directory($var);
- if (! -e $var) {
- print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
- chomp(my $tmp = <STDIN>);
- if (($tmp eq "") || ($tmp =~ /^y/i)) {
- # Attempt to Create the Dir..
- my $chk = eval {
- use File::Path ();
- File::Path::mkpath($var, 0, 0777);
- 1;
- };
- unless (defined($chk) && -d $var) {
- print "Unable to create directory. ($var)\n\n";
- # Restart Loop..
- next;
- }
- } else {
- # They said they don't want to create, and we can't install there.
- print "\n\n";
- next;
- }
- } else {
- if (!is_dir($var)) {
- # Target exists, but is not a directory.
- print "File $var exists, but is not a directory.\n\n";
- next;
- }
- }
- # Either Dir Exists, or was created fine.
- $config{$hash_key} = $var;
- $complete = 1;
- print "\n";
- }
-}
-
-our $SHARED = "";
-
-my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
-
-sub writefiles {
- my($writeheader) = @_;
- # First File.. inspircd_config.h
- chomp(my $incos = `uname -n -s -r`);
- chomp(my $version = `sh src/version.sh`);
- chomp(my $revision2 = getrevision());
- my $branch = "InspIRCd-0.0";
- if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
- {
- $branch = $1;
- }
- if ($writeheader == 1)
- {
- print "Writing \e[1;32minspircd_config.h\e[0m\n";
- open(FILEHANDLE, ">include/inspircd_config.h.tmp");
- print FILEHANDLE <<EOF;
-/* Auto generated by configure, do not modify! */
-#ifndef __CONFIGURATION_AUTO__
-#define __CONFIGURATION_AUTO__
-
-/* this is for windows support. */
-#define CoreExport /**/
-#define DllExport /**/
-
-#define CONFIG_PATH "$config{CONFIG_DIR}"
-#define DATA_PATH "$config{DATA_DIR}"
-#define LOG_PATH "$config{LOG_DIR}"
-#define MOD_PATH "$config{MODULE_DIR}"
-#define SOMAXCONN_S "$config{_SOMAXCONN}"
-#define ENTRYPOINT int main(int argc, char** argv)
-
-EOF
-print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
-
- if ($config{OSNAME} =~ /SunOS/i) {
- print FILEHANDLE "#define IS_SOLARIS\n";
- }
- if ($config{OSNAME} =~ /MINGW32/i) {
- print FILEHANDLE "#define IS_MINGW\n";
- }
- if ($config{GCCVER} >= 3) {
- print FILEHANDLE "#define GCC3\n";
- }
- if ($config{HAS_STRLCPY} eq "true") {
- print FILEHANDLE "#define HAS_STRLCPY\n";
- }
- if ($config{HAS_STDINT} eq "true") {
- print FILEHANDLE "#define HAS_STDINT\n";
- }
- if ($config{HAS_EVENTFD} eq 'true') {
- print FILEHANDLE "#define HAS_EVENTFD\n";
- }
- if ($config{OSNAME} !~ /DARWIN/i) {
- print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
- }
- my $use_hiperf = 0;
- if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
- print FILEHANDLE "#define USE_KQUEUE\n";
- $config{SOCKETENGINE} = "socketengine_kqueue";
- $use_hiperf = 1;
- }
- if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
- print FILEHANDLE "#define USE_EPOLL\n";
- $config{SOCKETENGINE} = "socketengine_epoll";
- $use_hiperf = 1;
- }
- if (($has_ports) && ($config{USE_PORTS} eq "y")) {
- print FILEHANDLE "#define USE_PORTS\n";
- $config{SOCKETENGINE} = "socketengine_ports";
- $use_hiperf = 1;
- }
- # user didn't choose either epoll or select for their OS.
- # default them to USE_SELECT (ewwy puke puke)
- if (!$use_hiperf) {
- print "no hi-perf, " . $config{USE_POLL};
- if ($config{USE_POLL} eq "y")
- {
- print FILEHANDLE "#define USE_POLL\n";
- $config{SOCKETENGINE} = "socketengine_poll";
- }
- else
- {
- print FILEHANDLE "#define USE_SELECT\n";
- $config{SOCKETENGINE} = "socketengine_select";
- }
- }
- print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
- close(FILEHANDLE);
-
- open(FILEHANDLE, ">include/inspircd_version.h.tmp");
- print FILEHANDLE <<EOF;
-#define BRANCH "$branch"
-#define VERSION "$version"
-#define REVISION "$revision2"
-#define SYSTEM "$incos"
-EOF
- close FILEHANDLE;
-
- for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
- my $diff = 0;
- open my $fh1, $file or $diff = 1;
- open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
- while (!$diff) {
- my $line1 = <$fh1>;
- my $line2 = <$fh2>;
- if (defined($line1) != defined($line2)) {
- $diff = 1;
- } elsif (!defined $line1) {
- last;
- } else {
- $diff = ($line1 ne $line2);
- }
- }
- if ($diff) {
- unlink $file;
- rename "$file.tmp", $file;
- } else {
- unlink "$file.tmp";
- }
- }
- }
-
- # Write all .in files.
- my $tmp = "";
- my $file = "";
- my $exe = "inspircd";
-
- # Do this once here, and cache it in the .*.inc files,
- # rather than attempting to read src/version.sh from
- # compiled code -- we might not have the source to hand.
- # Fix for bug#177 by Brain.
-
- chomp($version = `sh ./src/version.sh`);
- chomp(my $revision = getrevision());
- $version = "$version(r$revision)";
-
- # We can actually parse any file starting with . and ending with .inc,
- # but right now we only parse .inspircd.inc to form './inspircd'
- prepare_dynamic_makefile();
-
- my @dotfiles = qw(main.mk inspircd);
- push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
-
- foreach my $file (@dotfiles) {
- open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
- $_ = join '', <FILEHANDLE>;
- close(FILEHANDLE);
-
- $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
-
- for my $var (qw(
- CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
- STARTSCRIPT DESTINATION SOCKETENGINE
- )) {
- s/\@$var\@/$config{$var}/g;
- }
- s/\@EXECUTABLE\@/$exe/ if defined $exe;
- s/\@VERSION\@/$version/ if defined $version;
-
- if ($file eq 'main.mk') {
- print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
-
- my $mk_tmp = $_;
- s/\@IFDEF (\S+)/ifdef $1/g;
- s/\@IFNDEF (\S+)/ifndef $1/g;
- s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
- s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
- s/\@ELSE/else/g;
- s/\@ENDIF/endif/g;
- s/ *\@BSD_ONLY .*\n//g;
- s/\@GNU_ONLY //g;
- s/\@DO_EXPORT (.*)/export $1/g;
- open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
- print MKF $_;
- close MKF;
-
- print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
- $_ = $mk_tmp;
- s/\@IFDEF (\S+)/.if defined($1)/g;
- s/\@IFNDEF (\S+)/.if !defined($1)/g;
- s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
- s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
- s/\@ELSE/.else/g;
- s/\@ENDIF/.endif/g;
- s/\@BSD_ONLY //g;
- s/ *\@GNU_ONLY .*\n//g;
- $mk_tmp = $_;
- $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
- open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
- print MKF $mk_tmp;
- close MKF;
- } else {
- print "Writing \e[1;32m$file\e[0m ...\n";
- open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
- print FILEHANDLE $_;
- close(FILEHANDLE);
- }
- }
-
- chmod 0755, 'inspircd';
-}
-
-sub depcheck
-{
- getmodules();
- for my $mod (@modlist) {
- getcompilerflags("src/modules/m_$mod.cpp");
- getlinkerflags("src/modules/m_$mod.cpp");
- }
-}
-
-sub prepare_dynamic_makefile
-{
- my $i = 0;
-
- if (!$has_epoll)
- {
- $config{USE_EPOLL} = 0;
- }
- if (!$has_kqueue)
- {
- $config{USE_KQUEUE} = 0;
- }
- if (!$has_ports)
- {
- $config{USE_PORTS} = 0;
- }
-}
+print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n";
# Routine to list out the extra/ modules that have been enabled.
# Note: when getting any filenames out and comparing, it's important to lc it if the
for my $extra (keys(%extras)) {
next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
- my @deps = split / +/, getdependencies($abs_extra);
+ my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
for my $dep (@deps) {
if (exists($extras{$dep})) {
my $ref = \$extras{$dep}; # Take reference.
next;
}
# Get dependencies, and add them to be processed.
- my @deps = split / +/, getdependencies($extrapath);
+ my @deps = split /\s+/, get_property($extrapath, 'ModDep');
for my $dep (@deps) {
next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
- if (!-e "src/modules/$dep") {
+ if (!-e "src/modules/$dep" && !-e "include/$dep") {
if (-e "src/modules/extra/$dep") {
print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
push @extras, $dep;
}
# Check if anything needs this.
for my $file (@files) {
- my @deps = split / +/, getdependencies("src/modules/extra/$file");
+ my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
# File depends on this extra...
if (scalar(grep { $_ eq $extra } @deps) > 0) {
# And is both enabled and not about to be disabled.
#
# <keyword pattern="^blah.*?$" reason="Dont blah!" action="gline" duration="1d6h" flags="pnPq">
-# An example of excluding a channel from filtering:
-# <exemptfromfilter channel="#help">
+# You may specify specific channels that are exempt from being filtered:
+#<exemptfromfilter target="#opers">
+#<exemptfromfilter target="#help">
+
+# You can also exempt messages from being filtered if they are sent to
+# specific nicks.
+# Example that exempts all messages sent *to* NickServ:
+#<exemptfromfilter target="NickServ">
+
+# Note that messages *from* services are never subject to filtering;
+# <exemptfromfilter> tags are only for exempting messages sent *to* the
+# configured targets.
MKPASSWD VHOST TITLE SETNAME
WHOIS WHOWAS ISON USERHOST WATCH
-LIST NAMES WHO MOTD RULES
+LIST NAMES WHO MOTD
ADMIN MAP LINKS LUSERS TIME
STATS VERSION INFO MODULES COMMANDS
SSLINFO
Authenticate for a vhost using the specified username and password.">
-<helpop key="remove" value="/REMOVE <nick> <channel> [<reason>]
+<helpop key="remove" value="/REMOVE <channel> <nick> [<reason>]
Removes a user from a channel you specify. You must be at least a
channel halfoperator to remove a user. A removed user will part with
a message stating they were removed from the channel and by whom.">
+<helpop key="rmode" value="/RMODE [channel] [modeletter] {[pattern]}
+
+Removes listmodes from a channel.
+E.g. /RMODE #Chan b m:* will remove all mute extbans.">
+
<helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
-This behaves identically to /REMOVE, the only difference is that the
-<channel> and <nick> parameters are switched around to match /KICK's
-syntax. Also, /REMOVE is a builtin mIRC command which caused trouble
-for some users.">
+This behaves identically to /REMOVE. /REMOVE is a builtin mIRC command
+which caused trouble for some users.">
<helpop key="devoice" value="/DEVOICE <channel>
contain important server rules and notices and should be read prior
to using a server.">
-<helpop key="rules" value="/RULES
-
-Show the rules file for the local server. This is similar in effect to
-except that these are not sent automatically on connect.">
-
<helpop key="oper" value="/OPER <login> <password>
Attempts to authenticate a user as an IRC operator.
KILL SAQUIT GLINE ZLINE QLINE
KLINE RLINE ELINE CBAN SHUN
-FILTER OJOIN
+FILTER OJOIN CLEARCHAN
CONNECT SQUIT RCONNECT RSQUIT
1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
5 minutes and 6 seconds. All fields in this format are optional.">
-<helpop key="sajoin" value="/SAJOIN <nick> <channel>
+<helpop key="sajoin" value="/SAJOIN [<nick>] <channel>[,<channel>]
-Forces the user to join the channel.">
+Forces the user to join the channel(s).
+If no nick is given, it joins the oper doing the /SAJOIN.">
-<helpop key="sapart" value="/SAPART <nick> <channel>
+<helpop key="sapart" value="/SAPART <nick> <channel>[,<channel>]
-Forces the user to part the channel.">
+Forces the user to part the channel(s).">
<helpop key="samode" value="/SAMODE <target> (+|-)<modes> [<parameters for modes>]
Closes all unregistered connections to the local server.">
+<helpop key="clearchan" value="/CLEARCHAN <channel> [<KILL|KICK|G|Z>] [<reason>]
+
+Quits or kicks all non-opers from a channel, optionally G/Z-Lines them.
+Useful for quickly nuking bot channels.
+
+The default method, KILL, simply disconnects the victims from the server,
+while methods G and Z also add G/Z-Lines for all the targets.
+
+When used, the victims won't see each other getting kicked or quitting.">
+
######################
# User/Channel Modes #
######################
v <nickname> Gives voice to <nickname>, allowing them to speak
while the channel is +m.
- h <nickname> Gives halfop status to <nickname> (this mode can
- be disabled).
+ h <nickname> Gives halfop status to <nickname> (requires
+ customprefix module).
o <nickname> Gives op status to <nickname>.
a <nickname> Gives protected status to <nickname>, preventing
them from them from being kicked (+q only,
- requires chanprotect module).
+ requires customprefix module).
q <nickname> Gives owner status to <nickname>, preventing them
from being kicked (Services or only, requires
- chanprotect module).
+ customprefix module).
b <hostmask> Bans <hostmask> from the channel.
e <hostmask> Excepts <hostmask> from bans (requires
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ Kicks as default, blocks with ~ and bans with *
+ The last two parameters are optional.
F <changes>:<sec> Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
module).
s:<server> Matches users on a matching server (requires serverban
module).
- z:<certfp> Matches users having the given SSL certificate
- fingerprint (requires sslmodes module).
+ z:<certfp> Matches users with a matching SSL certificate fingerprint
+ (requires sslmodes module)
O:<opertype> Matches IRCops of a matching type, mostly useful as an
an invite exception (requires operchans module).
R:<account> Matches users logged into a matching account (requires
MKPASSWD VHOST TITLE SETNAME
WHOIS WHOWAS ISON USERHOST WATCH
-LIST NAMES WHO MOTD RULES
+LIST NAMES WHO MOTD
ADMIN MAP LINKS LUSERS TIME
STATS VERSION INFO MODULES COMMANDS
SSLINFO
KILL SAQUIT GLINE ZLINE QLINE
KLINE RLINE ELINE CBAN SHUN
-FILTER
+FILTER CLEARCHAN
CONNECT SQUIT RCONNECT RSQUIT
v <nickname> Gives voice to <nickname>, allowing them to speak
while the channel is +m.
- h <nickname> Gives halfop status to <nickname> (this mode can
- be disabled).
+ h <nickname> Gives halfop status to <nickname> (requires
+ customprefix module).
o <nickname> Gives op status to <nickname>.
a <nickname> Gives protected status to <nickname>, preventing
them from them from being kicked (+q only,
- requires chanprotect module).
+ requires customprefix module).
q <nickname> Gives owner status to <nickname>, preventing them
from being kicked (Services or only, requires
- chanprotect module).
+ customprefix module).
b <hostmask> Bans <hostmask> from the channel.
e <hostmask> Excepts <hostmask> from bans (requires
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ Kicks as default, blocks with ~ and bans with *
+ The last two parameters are optional.
F <changes>:<sec> Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
# #
########################################################################
+#-#-#-#-#-#-#-#-#-# CONFIGURATION FORMAT #-#-#-#-#-#-#-#-#-#-#-#-#-#-
+# #
+# In order to maintain compatibility with older configuration files, #
+# you can change the configuration parser to parse as it did in #
+# previous releases. When using the "compat" format, you need to use #
+# C++ escape sequences (e.g. \n) instead of XML ones (e.g. &nl;) and #
+# can not use <define> to create macros. #
+#<config format="compat">
+
#-#-#-#-#-#-#-#-#-# INCLUDE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#-#
# #
# This optional tag allows you to include another config file #
# #
# Variables may be redefined and may reference other variables. #
# Value expansion happens at the time the tag is read. #
-# #
-# Using variable definitions REQUIRES that the config format be #
-# changed to "xml" from the default "compat" that uses escape #
-# sequences such as "\"" and "\n", and does not support <define> #
-<config format="xml">
<define name="bindip" value="1.2.2.3">
<define name="localips" value="&bindip;/24">
# loaded for SSL to work. If you do not want the port(s) in this bind
# tag to support SSL, just remove or comment out this option.
ssl="gnutls"
+
+ # defer: When this is non-zero, connections will not be handed over to
+ # the daemon from the operating system before data is ready.
+ # In Linux, the value indicates the number of seconds we'll wait for a
+ # connection to come up with data. Don't set it too low!
+ # In BSD the value is ignored; only zero and non-zero is possible.
+ # Windows ignores this parameter completely.
+ # Note: This does not take effect on rehash.
+ # To change it on a running bind, you'll have to comment it out,
+ # rehash, comment it in and rehash again.
+ defer="0"
>
<bind address="" port="6660-6669" type="clients">
password="secret"
# maxchans: Maximum number of channels a user in this class
- # be in at one time. This overrides every other maxchans setting.
- #maxchans="30"
+ # be in at one time.
+ maxchans="20"
# timeout: How long (in seconds) the server will wait before
# disconnecting a user if they do not do anything on connect.
# maxconnwarn: Enable warnings when localmax or globalmax is hit (defaults to on)
maxconnwarn="off"
+ # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
+ # in this class. This can save a lot of resources on very busy servers.
+ resolvehostnames="yes"
+
# usednsbl: Defines whether or not users in this class are subject to DNSBL. Default is yes.
# This setting only has effect when m_dnsbl is loaded.
#usednsbl="yes"
allow="*"
# maxchans: Maximum number of channels a user in this class
- # be in at one time. This overrides every other maxchans setting.
- #maxchans="30"
+ # be in at one time.
+ maxchans="20"
# timeout: How long (in seconds) the server will wait before
# disconnecting a user if they do not do anything on connect.
# globalmax: Maximum global (network-wide) connections per IP.
globalmax="3"
+ # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
+ # in this class. This can save a lot of resources on very busy servers.
+ resolvehostnames="yes"
+
# useident: Defines if users in this class must respond to a ident query or not.
useident="no"
# This file has all the information about oper classes, types and o:lines.
# You *MUST* edit it.
-<include file="conf/examples/opers.conf.example">
+<include file="examples/opers.conf.example">
# This file has all the information about server links and ulined servers.
# You *MUST* edit it if you intend to link servers.
-<include file="conf/examples/links.conf.example">
+<include file="examples/links.conf.example">
#-#-#-#-#-#-#-#-#-#- MISCELLANEOUS CONFIGURATION -#-#-#-#-#-#-#-#-#-#
# #
# Files block - contains files whose contents are used by the ircd
#
# motd - displayed on connect and when a user executes /MOTD
-# rules - displayed when the user executes /RULES
# Modules can also define their own files
-<files motd="conf/examples/motd.txt.example" rules="conf/examples/rules.txt.example">
+<files motd="examples/motd.txt.example">
# Example of an executable file include. Note this will be read on rehash,
# not when the command is run.
-#<execfiles rules="wget -O - http://www.example.com/rules.txt">
-
-#-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# #
-
-<channels
- # users: Maximum number of channels a user can be in at once.
- users="20"
-
- # opers: Maximum number of channels an oper can be in at once.
- opers="60">
+#<execfiles motd="wget -O - http://www.example.com/motd.txt">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# If these values are not defined, InspIRCd uses the default DNS resolver
# the correct parameters are.
syntaxhints="no"
- # cyclehosts: If enabled, when a user gets a host set, it will cycle
- # them in all their channels. If not, it will simply change their host
- # without cycling them.
- cyclehosts="yes"
-
# cyclehostsfromuser: If enabled, the source of the mode change for
# cyclehosts will be the user who cycled. This can look nicer, but
# triggers anti-takeover mechanisms of some obsolete bots.
# defaultmodes: What modes are set on a empty channel when a user
# joins it and it is unregistered.
- defaultmodes="nt"
+ defaultmodes="not"
- # moronbanner: This is the text that is sent to a user when they are
+ # xlinemessage: This is the text that is sent to a user when they are
# banned from the server.
- moronbanner="You're banned! Email abuse@example.com with the ERROR line below for help."
+ xlinemessage="You're banned! Email irc@example.com with the ERROR line below for help."
# exemptchanops: exemptions for channel access restrictions based on prefix.
exemptchanops="nonick:v flood:o"
# nosnoticestack: This prevents snotices from 'stacking' and giving you
# the message saying '(last message repeated X times)'. Defaults to no.
- nosnoticestack="no"
-
- # welcomenotice: When turned on, this sends a NOTICE to connecting users
- # with the text Welcome to <networkname>! after successful registration.
- # Defaults to yes.
- welcomenotice="yes">
+ nosnoticestack="no">
#-#-#-#-#-#-#-#-#-#-#-# PERFORMANCE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#
# in the accept queue. This is *NOT* the total maximum number of
# connections per server. Some systems may only allow this to be up
# to 5, while others (such as Linux and *BSD) default to 128.
+ # Setting this above the limit imposed by your OS can have undesired
+ # effects.
somaxconn="128"
- # limitsomaxconn: By default, somaxconn (see above) is limited to a
- # safe maximum value in the 2.0 branch for compatibility reasons.
- # This setting can be used to disable this limit, forcing InspIRCd
- # to use the value specified above.
- limitsomaxconn="true"
-
# softlimit: This optional feature allows a defined softlimit for
# connections. If defined, it sets a soft max connections value.
softlimit="12800"
+ # clonesonconnect: If this is set to false, we won't check for clones
+ # on initial connection, but only after the DNS check is done.
+ # This can be useful where your main class is more restrictive
+ # than some other class a user can be assigned after DNS lookup is complete.
+ # Turning this option off will make the server spend more time on users we may
+ # potentially not want. Normally this should be neglible, though.
+ # Default value is true
+ clonesonconnect="true"
+
# quietbursts: When syncing or splitting from a network, a server
# can generate a lot of connect and quit messages to opers with
# +C and +Q snomasks. Setting this to yes squelches those messages,
# which makes it easier for opers, but degrades the functionality of
# bots like BOPM during netsplits.
- quietbursts="yes"
-
- # nouserdns: If enabled, no DNS lookups will be performed on
- # connecting users. This can save a lot of resources on very busy servers.
- nouserdns="no">
+ quietbursts="yes">
#-#-#-#-#-#-#-#-#-#-#-# SECURITY CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
# #
<security
+ # allowcoreunload: If this value is set to yes, Opers will be able to
+ # unload core modules (e.g. cmd_privmsg.so).
+ allowcoreunload="no"
# announceinvites: This option controls which members of the channel
# receive an announcement when someone is INVITEd. Available values:
# higher ranked users. This is the recommended setting.
announceinvites="dynamic"
- # hidemodes: If enabled, then the listmodes given will be hidden
- # from users below halfop. This is not recommended to be set on +b
- # as it may break some functionality in popular clients such as mIRC.
- hidemodes="eI"
-
# hideulines: If this value is set to yes, U-lined servers will
# be hidden from non-opers in /links and /map.
hideulines="no"
# (Commands like /notice, /privmsg, /kick, etc)
maxtargets="20"
- # customversion: Displays a custom string when a user /version's
- # the ircd. This may be set for security reasons or vanity reasons.
+ # customversion: A custom message to be displayed in the comments field
+ # of the VERSION command response. This does not hide the InspIRCd version.
customversion=""
# operspywhois: show opers (users/auspex) the +s channels a user is in. Values:
# maxident: Maximum length of a ident/username.
maxident="11"
+ # maxhost: Maximum length of a hostname.
+ maxhost="64"
+
# maxquit: Maximum length of a quit message.
maxquit="255"
# maxaway: Maximum length of an away message.
maxaway="200">
+#-#-#-#-#-#-#-#-#-#-#-#-# PATHS CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This configuration tag defines the location that InspIRCd stores #
+# various types of files such as configuration files, log files and #
+# modules. You will probably not need to change these from the values #
+# set when InspIRCd was built unless you are using a binary package #
+# where you do not have the ability to set build time configuration. #
+#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Logging
# to do what they want.
#
# An example log tag would be:
-# <log method="file" type="OPER" level="default" target="logs/opers.log">
+# <log method="file" type="OPER" level="default" target="opers.log">
# which would log all information on /oper (failed and successful) to
# a file called opers.log.
#
# The following log tag is highly default and uncustomised. It is recommended you
# sort out your own log tags. This is just here so you get some output.
-<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="logs/ircd.log">
+<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="ircd.log">
#-#-#-#-#-#-#-#-#-#-#-#-#- WHOWAS OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#
# #
# provide almost all the features of InspIRCd. :) #
# #
# The default does nothing -- we include it for simplicity for you. #
-<include file="conf/examples/modules.conf.example">
+<include file="examples/modules.conf.example">
# Here are some pre-built modules.conf files that closely match the
# default configurations of some popular IRCd's. You still may want to
# recommended that you make your own modules file based on modules.conf.example.
# Settings similar to UnrealIRCd defaults.
-#<include file="conf/examples/modules/unrealircd.conf.example">
+#<include file="examples/modules/unrealircd.conf.example">
# Settings similar to Charybdis IRCd defaults.
-#<include file="conf/examples/modules/charybdis.conf.example">
+#<include file="examples/modules/charybdis.conf.example">
#########################################################################
# allowmask: Range of IP addresses to allow for this link.
# Can be a CIDR (see example).
- allowmask="203.0.113.0/24"
+ allowmask="203.0.113.0/24 127.0.0.0/8 2001:db8::/32"
# timeout: If defined, this option defines how long the server
# will wait to consider the connect attempt failed and try the
ssl="gnutls"
# fingerprint: If defined, this option will force servers to be
- # authenticated using SSL Fingerprints. See http://wiki.inspircd.org/SSL
- # for more information. This will require an SSL link for both inbound
- # and outbound connections.
+ # authenticated using SSL certificate fingerprints. See
+ # http://wiki.inspircd.org/SSL for more information. This will
+ # require an SSL link for both inbound and outbound connections.
#fingerprint=""
# bind: Local IP address to bind to.
# Simple autoconnect block. This enables automatic connection of a server
# Recommended setup is to have leaves connect to the hub, and have no
# automatic connections started by the hub.
-<autoconnect period="300" server="hub.penguin.org">
+<autoconnect period="10m" server="hub.penguin.org">
# Failover autoconnect block. If you have multiple hubs, or want your network
# to automatically link even if the hub is down, you can specify multiple
# channel. e.g. +b nick!ident@host#channelbanneduserissentto
#<module name="m_banredirect.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# bcrypt module: Allows other modules to generate bcrypt hashes,
+# usually for cryptographic uses and security.
+#<module name="m_bcrypt.so">
+#
+# rounds: Defines how many rounds the bcrypt function will run when
+# generating new hashes.
+#<bcrypt rounds="10">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Block amsg module: Attempt to block all usage of /amsg and /ame.
#<module name="m_blockamsg.so">
# specify some censor tags. See also: #
# http://wiki.inspircd.org/Modules/censor #
#
-#<include file="conf/examples/censor.conf.example">
+#<include file="examples/censor.conf.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
# (http://cgiirc.sourceforge.net).
+# Adds snomask +w for monitoring CGI:IRC connections.
#<module name="m_cgiirc.so">
#
#-#-#-#-#-#-#-#-#-#-#-# CGIIRC CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
# This is the hard limit for 'X'.
# If notice is set to yes, joining users will get a NOTICE before playback
# telling them about the following lines being the pre-join history.
-#<chanhistory maxlines="20" notice="yes">
+# If bots is set to yes, it will also send to users marked with +B
+#<chanhistory maxlines="20" notice="yes" bots="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel logging module: Used to send snotice output to channels, to
# in a channel matching a ban like +b j:#channel*mask from joining.
#<module name="m_channelban.so">
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Chanprotect module: Gives +q and +a channel modes.
-#<module name="m_chanprotect.so">
-
-<chanprotect
- # noservices: With this set to yes, when a user joins an empty channel,
- # the server will set +q on them. If set to no, it will only set +o
- # on them until they register the channel.
- noservices="no"
-
- # qprefix: Prefix (symbol) to use for +q users.
- qprefix="~"
-
- # aprefix: Prefix (symbol) to use for +a users.
- aprefix="&"
-
- # deprotectself: If this value is set (true, yes or 1), it will allow
- # +a and +q users to remove the +a and +q from themselves, otherwise,
- # the status will have to be removed by services.
- deprotectself="yes"
-
- # deprotectothers: If this value is set to yes, true, or 1, then any
- # user with +q or +a may remove the +q or +a from other users.
- deprotectothers="yes">
-
-
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Check module: Adds the /CHECK command.
# Check is useful for looking up information on channels, users,
# To use, CHGNAME must be in one of your oper class blocks.
#<module name="m_chgname.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
+# all users on a channel using /CLEARCHAN.
+#<module name="m_clearchan.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Cloaking module: Adds usermode +x and cloaking support.
# Relies on the module m_md5.so being loaded.
# cloak prefix as shown below. The cloak key must be shared across #
# the network for correct cloaking. #
# #
-# There are four methods of cloaking: #
+# There are two methods of cloaking: #
# #
# half Cloak only the "unique" portion of a host; show #
# the last 2 parts of the domain, /16 subnet of IPv4 #
# full Cloak the users completely, using three slices for #
# common CIDR bans (IPv4: /16, /24; IPv6: /48, /64). #
# #
-# These methods use a single key that can be any length of text. #
+# The methods use a single key that can be any length of text. #
# An optional prefix may be specified to mark cloaked hosts. #
-# #
-# The following methods are maintained for backwards compatibility; #
-# they are slightly less secure, and always hide unresolved IPs. #
-# #
-# compat-host InspIRCd 1.2-compatible host-based cloaking. #
-# compat-ip InspIRCd 1.2-compatible ip-always cloaking. #
-# #
-# If you use a compat cloaking mode then you must specify key1, key2, #
-# key3, key4; the values must be less than 0x80000000 and should be #
-# picked at random. Prefix is mandatory, will default to network name #
-# if not specified, and will always have a "-" appended. #
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#
#<cloak mode="half"
# key="secret"
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Auto join on connect module: Allows you to force users to join one
-# or more channels automatically upon connecting to the server.
+# or more channels automatically upon connecting to the server, or
+# join them in case they aren't on any channels after being online
+# for X seconds.
#<module name="m_conn_join.so">
#
#-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# If you have m_conn_join.so loaded, you can configure it using the
# following values, or set autojoin="#chat,#help" in <connect> blocks.
#
+# Join users immediately after connection to #one #two and #three.
#<autojoin channel="#one,#two,#three">
+# Join users to #chat after 15 seconds if they aren't on any channels.
+#<autojoin channel="#chat" delay="15">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Set modes on connect module: When this module is loaded <connect>
#
# This allows for 10 connections in an hour with a 10 minute ban if
# that is exceeded.
-#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
+#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128"
+# A custom ban message may optionally be specified.
+# banmessage="Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Connection throttle module.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Custom prefixes: Allows for channel prefixes to be added.
-# This replaces m_chanprotect and m_halfop.
#<module name="m_customprefix.so">
#
# name The name of the mode, must be unique from other modes.
# #
# Your choice of regex engine must match on all servers network-wide.
#
-# You may specify specific channels that are exempt from being filtered:
-#<exemptfromfilter channel="#blah">
+# To learn more about the configuration of this module, read #
+# examples/filter.conf.example, which covers the various types of #
+# filters and shows how to add exemptions. #
#
#-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# specify below the path to the filter.conf file, or define some #
# <filter> tags. #
# #
-#<include file="conf/examples/filter.conf.example">
+#<include file="examples/filter.conf.example">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Flash Policy Daemon module: Allows Flash IRC clients (e.g. LightIRC)#
+# to connect. If no file is specified, it'll serve a default policy #
+# allowing all IPs to connect to all plaintext IRC ports #
+#<bind address="" port="8430" type="flashpolicyd"> #
+#<flashpolicyd timeout="5" file=""> #
+#<module name="m_flashpolicyd.so"> #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Gecos ban: Implements extended ban 'r', which stops anyone matching
#<module name="m_globalload.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Halfop module: Provides the +h (halfops) channel status mode.
-#<module name="m_halfop.so">
-
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# HELPOP module: Provides the /HELPOP command.
+# HELPOP module: Provides the /HELPOP command
#<module name="m_helpop.so">
#
#-#-#-#-#-#-#-#-#-#-#-#- HELPOP CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
# If you specify to use the m_helpop.so module, then specify below #
# the path to the helpop.conf file. #
-#<include file="conf/examples/inspircd.helpop-full.example">
+# #
+#<include file="examples/inspircd.helpop-full.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Hide chans module: Allows users to hide their channels list from non-
# This setting is not recommended for most mainstream networks.
#<hidechans affectsopers="false">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Hide list module: Allows for hiding the list of listmodes from users
+# who do not have sufficient channel rank.
+#<module name="m_hidelist.so">
+#
+# Each <hidelist> tag configures one listmode to hide.
+# mode: Name of the listmode to hide.
+# rank: Minimum rank required to view the list. If set to 0, all
+# members of the channel may view the list, but non-members may not.
+# The rank of the built-in op and voice mode is 30000 and 10000,
+# respectively; the rank of other prefix modes is configurable.
+# Defaults to 20000.
+#
+# Hiding the ban list is not recommended because it may break some
+# clients.
+#
+# Hide filter (+g) list:
+#<hidelist mode="filter" rank="30000">
+# Only show invite exceptions (+I) to channel members:
+#<hidelist mode="invex" rank="0">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Hide oper module: Allows opers to hide their oper status from non-
# opers by setting user mode +H on themselves.
#<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
#<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
+# hostcycle: If loaded, when a user gets a host or ident set, it will
+# cycle them in all their channels. If not loaded it will simply change
+# their host/ident without cycling them.
+#<module name="m_hostcycle.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# httpd module: Provides HTTP server support for InspIRCd.
#<module name="m_httpd.so">
# default to 5 seconds. This is a non-blocking timeout which holds #
# the user in a 'connecting' state until the lookup is complete. #
# The bind value indicates which IP to bind outbound requests to. #
+# nolookupprefix: If on, the idents of users being in a connect class #
+# with ident lookups disabled (i.e. <connect useident="off">) will be #
+# prefixed with a "~". If off, the ident of those users will not be #
+# prefixed. Default is off. #
#
-#<ident timeout="5">
+#<ident timeout="5" nolookupprefix="no">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Invite exception module: Adds support for channel invite exceptions
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Anti auto rejoin: Adds support for prevention of auto-rejoin (+J).
#<module name="m_kicknorejoin.so">
-# Set the maximum time that is accepted as a parameter for +J here.
-#<kicknorejoin maxtime="1m">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Knock module: Adds the /KNOCK command and channel mode +K.
# If set to "both" then (surprise!) both will be sent.
#<knock notify="notice">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# LDAP module: Allows other SQL modules to access a LDAP database
+# through a unified API.
+# This modules is in extras. Re-run configure with:
+# ./configure --enable-extras=m_ldap.cpp
+# and run make install, then uncomment this module to enable it.
+#
+#<module name="m_ldap.so">
+#<database module="ldap" id="ldapdb" server="ldap://localhost" binddn="cn=Manager,dc=inspircd,dc=org" bindauth="mysecretpass" searchscope="subtree">
+# The server parameter indicates the LDAP server to connect to. The #
+# ldap:// style scheme before the hostname proper is MANDATORY. #
+# #
+# The binddn and bindauth indicate the DN to bind to for searching, #
+# and the password for the distinguished name. Some LDAP servers will #
+# allow anonymous searching in which case these two values do not #
+# need defining, otherwise they should be set similar to the examples #
+# above. #
+# #
+# The searchscope value indicates the subtree to search under. On our #
+# test system this is 'subtree'. Your mileage may vary. #
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# LDAP authentication module: Adds the ability to authenticate users #
-# via LDAP. This is an extra module which must be enabled explicitly #
-# by symlinking it from modules/extra, and requires the OpenLDAP libs #
-# This module is in extras. To enable it, Re-run configure with: #
-# ./configure --enable-extras=m_ldapauth.cpp #
-# and run make install, then uncomment this module. #
+# via LDAP. #
#<module name="m_ldapauth.so">
# #
# Configuration: #
# #
-# <ldapauth baserdn="ou=People,dc=brainbox,dc=cc" #
+# <ldapauth dbid="ldapdb" #
+# baserdn="ou=People,dc=brainbox,dc=cc" #
# attribute="uid" #
-# server="ldap://brainwave.brainbox.cc" #
-# allowpattern="Guest*" #
+# allowpattern="Guest* Bot*" #
# killreason="Access denied" #
-# searchscope="subtree" #
-# binddn="cn=Manager,dc=brainbox,dc=cc" #
-# bindauth="mysecretpass" #
# verbose="yes" #
# host="$uid.$ou.inspircd.org"> #
# #
# The attribute value indicates the attribute which is used to locate #
# a user account by name. On POSIX systems this is usually 'uid'. #
# #
-# The server parameter indicates the LDAP server to connect to. The #
-# ldap:// style scheme before the hostname proper is MANDATORY. #
-# #
-# The allowpattern value allows you to specify a wildcard mask which #
-# will always be allowed to connect regardless of if they have an #
-# account, for example guest users. #
+# The allowpattern value allows you to specify a space separated list #
+# of wildcard masks which will always be allowed to connect #
+# regardless of if they have an account, for example guest and bot #
+# users. #
# #
# Killreason indicates the QUIT reason to give to users if they fail #
# to authenticate. #
# #
-# The searchscope value indicates the subtree to search under. On our #
-# test system this is 'subtree'. Your mileage may vary. #
-# #
# Setting the verbose value causes an oper notice to be sent out for #
# every failed authentication to the server, with an error string. #
# #
-# The binddn and bindauth indicate the DN to bind to for searching, #
-# and the password for the distinguished name. Some LDAP servers will #
-# allow anonymous searching in which case these two values do not #
-# need defining, otherwise they should be set similar to the examples #
-# above. #
-# #
# ldapwhitelist indicates that clients connecting from an IP in the #
# provided CIDR do not need to authenticate against LDAP. It can be #
# repeated to whitelist multiple CIDRs. #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# LDAP oper configuration module: Adds the ability to authenticate #
-# opers via LDAP. This is an extra module which must be enabled #
-# explicitly by symlinking it from modules/extra, and requires the #
-# OpenLDAP libs. Re-run configure with: #
-# ./configure --enable-extras=m_ldapoper.cpp
-# and run make install, then uncomment this module to enable it. #
+# opers via LDAP. #
#<module name="m_ldapoper.so">
# #
# Configuration: #
# #
-# <ldapoper baserdn="ou=People,dc=brainbox,dc=cc"
-# server="ldap://brainwave.brainbox.cc"
-# searchscope="subtree"
-# binddn="cn=Manager,dc=brainbox,dc=cc"
-# bindauth="mysecretpass"
+# <ldapoper dbid="ldapdb"
+# baserdn="ou=People,dc=brainbox,dc=cc"
# attribute="uid">
# #
# Available configuration items are identical to the same items in #
# would likely be immediately bounced by services.
#<module name="m_mlock.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Modenotice module: Adds the /MODENOTICE command that allows opers to
+# send notices to all users having the given user mode(s) set.
+#<module name="m_modenotice.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# MsSQL module: Allows other SQL modules to access MS SQL Server
# through a unified API.
# Read the comment above <connect:allowmotdcolors> in #
# inspircd.conf.example for details. #
# #
-#<opermotd file="conf/examples/opermotd.txt.example" onoper="yes" processcolors="false">
+#<opermotd file="examples/opermotd.txt.example" onoper="yes" processcolors="false">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Override module: Adds support for oper override.
# Generate hashes using the /MKPASSWD command on the server.
# Don't run it on a server you don't trust with your password.
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# PBKDF2 module: Allows other modules to generate PBKDF2 hashes,
+# usually for cryptographic uses and security.
+# This module relies on other hash providers (e.g. SHA256).
+#<module name="m_pbkdf2.so">
+#
+# iterations: Iterations the hashing function runs when generating new
+# hashes.
+# length: Length in bytes of the derived key.
+#<pbkdf2 iterations="12288" length="32">
+# You can override these values with specific values
+# for specific providers if you want to. Example given for SHA256.
+#<pbkdf2prov hash="sha256" iterations="24576">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Permanent channels module: Channels with the permanent channel mode
# will remain open even after everyone else has left the channel, and
#
# If 'listmodes' is true then all list modes (+b, +I, +e, +g...) will be
# saved. Defaults to false.
-#<permchanneldb filename="data/permchannels.conf" listmodes="true">
-#<include file="data/permchannels.conf">
+#<permchanneldb filename="permchannels.conf" listmodes="true">
+#<include file="permchannels.conf">
#
# You may also create channels on startup by using the <permchannels> block.
# Don't forget to set them +P in the modes, or they won't stay permanent.
# m_rline.
#<module name="m_regex_pcre.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Regular Expression Provider for RE2 Regular Expressions.
+# You need libre2 installed and in your include/library paths in order
+# to compile and load this module.
+#<module name="m_regex_re2.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular expression provider for POSIX regular expressions.
# You shouldn't need any additional libraries on a POSIX-compatible
# Remove module: Adds the /REMOVE command which is a peaceful
# alternative to /KICK.
#<module name="m_remove.so">
+#
+# supportnokicks: If true, /REMOVE is not allowed on channels where the
+# nokicks (+Q) mode is set. Defaults to false.
+# protectedrank: Members having this rank or above may not be /REMOVE'd
+# by anyone. Set to 0 to disable this feature. Defaults to 50000.
+#<remove supportnokicks="true" protectedrank="50000">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# A module to block, kick or ban upon similiar messages being uttered several times.
+# Syntax [~*][lines]:[sec]{[:difference]}{[:matchlines]}
+# ~ is to block, * is to ban, default is kick.
+# lines - In mode 1 the amount of lines that has to match consecutively - In mode 2 the size of the backlog to keep for matching
+# seconds - How old the message has to be before it's invalidated.
+# distance - Edit distance, in percent, between two strings to trigger on.
+# matchlines - When set, the function goes into mode 2. In this mode the function will trigger if this many of the last <lines> matches.
+#
+# As this module can be rather CPU-intensive, it comes with some options.
+# maxbacklog - Maximum size that can be specified for backlog. 0 disables multiline matching.
+# maxdistance - Max percentage of difference between two lines we'll allow to match. Set to 0 to disable edit-distance matching.
+# maxlines - Max lines of backlog to match against.
+# maxsecs - Maximum value of seconds a user can set. 0 to allow any.
+# size - Maximum number of characters to check for, can be used to truncate messages
+# before they are checked, resulting in less CPU usage. Increasing this beyond 512
+# doesn't have any effect, as the maximum length of a message on IRC cannot exceed that.
+#<repeat maxbacklog="20" maxlines="20" maxdistance="50" maxsecs="0" size="512">
+#<module name="m_repeat.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restricted channels module: Allows only opers to create channels.
# use glob. For this reason, is recommended to use a real regex engine
# so that at least \s or [[:space:]] is available.
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# RMODE module: Adds the /RMODE command
+# Allows channel mods to remove list modes en masse.
+# Syntax: /rmode <channel> <mode> [pattern]
+# E.g. '/rmode #Channel b m:*' will remove all mute-extbans on the channel.
+#<module name="m_rmode.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAJOIN module: Adds the /SAJOIN command which forcibly joins a user
# to the given channel.
# This module is oper-only.
# To use, SAJOIN must be in one of your oper class blocks.
+# Opers need the users/sajoin-others priv to be able to /SAJOIN users
+# other than themselves.
#<module name="m_sajoin.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# to a server matching a mask like +b s:server.mask.here from joining.
#<module name="m_serverban.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Showfile: Provides support for showing a text file to users when #
+# they enter a command. #
+# This module adds one command for each <showfile> tag that shows the #
+# given file to the user as a series of messages or numerics. #
+#<module name="m_showfile.so"> #
+# #
+#-#-#-#-#-#-#-#-#-#-# SHOWFILE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# name - The name of the command which displays this file. This is #
+# the only mandatory setting, all others are optional. #
+# file - The text file to be shown to the user. #
+# By default same as the command name. #
+# method - How should the file be shown? #
+# * numeric: Send contents using a numeric #
+# (similiar to /MOTD; the default). #
+# * notice: Send contents as a series of notices. #
+# * msg: Send contents as a series of private messages. #
+# colors - If true, color codes (\c, \b, \u, etc.) will be processed #
+# and sent as ANSI colors. If false (default) the file will #
+# be displayed as-is. #
+# #
+# When using the method "numeric", the following extra settings are #
+# available: #
+# #
+# introtext - Introductory line, "Showing <name>" by default. #
+# intronumeric - Numeric used for the introductory line. #
+# numeric - Numeric used for sending the text itself. #
+# endtext - Ending line, "End of <name>" by default. #
+# endnumeric - Numeric used for the ending line. #
+# #
+#<showfile name="RULES"
+# file="rules.txt"
+# colors="true"
+# introtext="Server rules:"
+# endtext="End of server rules.">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Show whois module: Adds the +W usermode which allows opers to see
# when they are /WHOIS'd.
#<module name="m_showwhois.so">
#
# If you wish, you may also let users set this mode. Only opers with the
-# users/auspex priv will see real hosts of people, though. This setting
-# is not reloadable via /REHASH, changing it requires /RELOADMODULE.
+# users/auspex priv will see real hosts of people, though.
#<showwhois opersonly="yes"
#
# You may also set whether or not users should receive whois notices,
# scripts to validate users. For this to work, one of m_ssl_gnutls.so
# or m_ssl_openssl.so must be loaded. This module also adds the
# "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the
+# opers to use SSL cert fingerprints to verify their identity and the
# ability to force opers to use SSL connections in order to oper up.
# It is highly recommended to load this module if you use SSL on your
# network.
# #
#<sqloper dbid="1" hash="md5">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# StartTLS module: Implements STARTTLS, which allows clients #
+# connected to non SSL enabled ports to enable SSL, if a proper SSL #
+# module is loaded (either m_ssl_gnutls or m_ssl_openssl). #
+#<module name="m_starttls.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be #
# added/removed by Services. #
#<module name="m_svshold.so">
-# If silent is true no snotices will be generated by SVSHOLD.
+# SVSHOLD does not generate server notices by default, you can turn
+# notices on by uncommenting the next line.
#<svshold silent="false">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# scripts to validate users. For this to work, one of m_ssl_gnutls.so
# or m_ssl_openssl.so must be loaded. This module also adds the
# "* <user> is using a secure connection" whois line, the ability for
-# opers to use SSL fingerprints to verify their identity and the ability
-# to force opers to use SSL connections in order to oper up.
+# opers to use SSL cert fingerprints to verify their identity and the
+# ability to force opers to use SSL connections in order to oper up.
# It is highly recommended to load this module especially if
# you use SSL on your network.
# For how to use the oper features, please see the first example <oper> tag
<module name="m_chanfilter.so">
<chanfilter hidemask="yes">
-<module name="m_chanprotect.so">
-
-<chanprotect
- noservices="no"
- qprefix="~"
- aprefix="&"
- deprotectself="yes"
- deprotectothers="yes">
-
<module name="m_check.so">
<module name="m_chghost.so">
<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
# #
# Your choice of regex engine must match on all servers network-wide.
#
-# You may specify specific channels that are exempt from being filtered:
-#<exemptfromfilter channel="#blah">
+# To learn more about the configuration of this module, read #
+# examples/filter.conf.example, which covers the various types of #
+# filters and shows how to add exemptions. #
#
#-#-#-#-#-#-#-#-#-#-#- FILTER CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# - servers/auspex: allows opers with this priv to see more detail about server information than normal users.
# ACTIONS:
# - users/mass-message: allows opers with this priv to PRIVMSG and NOTICE to a server mask (e.g. NOTICE $*)
- # - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels.
+ # - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE
# PERMISSIONS:
# - users/flood/no-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE)
# - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE)
# - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE)
#
# *NOTE: These privs are potentially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
- privs="users/auspex channels/auspex servers/auspex users/mass-message channels/high-join-limit users/flood/no-throttle users/flood/increased-buffers"
+ privs="users/auspex channels/auspex servers/auspex users/mass-message users/flood/no-throttle users/flood/increased-buffers"
# usermodes: Oper-only usermodes that opers with this class can use.
usermodes="*"
<type
# name: Name of type. Used in actual server operator accounts below.
- # Cannot contain spaces. If you would like a space, use
- # the _ character instead and it will translate to a space on whois.
name="NetAdmin"
# classes: Classes (blocks above) that this type belongs to.
# vhost: Host opers of this type get when they log in (oper up). This is optional.
vhost="netadmin.omega.example.org"
+ # maxchans: Maximum number of channels opers of this type can be in at once.
+ maxchans="60"
+
# modes: User modes besides +o that are set on an oper of this type
# when they oper up. Used for snomasks and other things.
# Requires that m_opermodes.so be loaded.
# If m_sslinfo isn't loaded, this option will be ignored.
#fingerprint="67cb9dc013248a829bb2171ed11becd4"
- # autologin: If an SSL fingerprint for this oper is specified, you can
- # have the oper block automatically log in. This moves all security of the
- # oper block to the protection of the client certificate, so be sure that
- # the private key is well-protected! Requires m_sslinfo.
+ # autologin: If an SSL certificate fingerprint for this oper is specified,
+ # you can have the oper block automatically log in. This moves all security
+ # of the oper block to the protection of the client certificate, so be sure
+ # that the private key is well-protected! Requires m_sslinfo.
#autologin="on"
# sslonly: If on, this oper can only oper up if they're using a SSL connection.
+++ /dev/null
-This is the InspIRCd rules file.
-
-Place any network or server rules here :)
+++ /dev/null
-Network Working Group P. Mockapetris
-Request for Comments: 1035 ISI
- November 1987
-Obsoletes: RFCs 882, 883, 973
-
- DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
-
-
-1. STATUS OF THIS MEMO
-
-This RFC describes the details of the domain system and protocol, and
-assumes that the reader is familiar with the concepts discussed in a
-companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034].
-
-The domain system is a mixture of functions and data types which are an
-official protocol and functions and data types which are still
-experimental. Since the domain system is intentionally extensible, new
-data types and experimental behavior should always be expected in parts
-of the system beyond the official protocol. The official protocol parts
-include standard queries, responses and the Internet class RR data
-formats (e.g., host addresses). Since the previous RFC set, several
-definitions have changed, so some previous definitions are obsolete.
-
-Experimental or obsolete features are clearly marked in these RFCs, and
-such information should be used with caution.
-
-The reader is especially cautioned not to depend on the values which
-appear in examples to be current or complete, since their purpose is
-primarily pedagogical. Distribution of this memo is unlimited.
-
- Table of Contents
-
- 1. STATUS OF THIS MEMO 1
- 2. INTRODUCTION 3
- 2.1. Overview 3
- 2.2. Common configurations 4
- 2.3. Conventions 7
- 2.3.1. Preferred name syntax 7
- 2.3.2. Data Transmission Order 8
- 2.3.3. Character Case 9
- 2.3.4. Size limits 10
- 3. DOMAIN NAME SPACE AND RR DEFINITIONS 10
- 3.1. Name space definitions 10
- 3.2. RR definitions 11
- 3.2.1. Format 11
- 3.2.2. TYPE values 12
- 3.2.3. QTYPE values 12
- 3.2.4. CLASS values 13
-
-
-
-Mockapetris [Page 1]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- 3.2.5. QCLASS values 13
- 3.3. Standard RRs 13
- 3.3.1. CNAME RDATA format 14
- 3.3.2. HINFO RDATA format 14
- 3.3.3. MB RDATA format (EXPERIMENTAL) 14
- 3.3.4. MD RDATA format (Obsolete) 15
- 3.3.5. MF RDATA format (Obsolete) 15
- 3.3.6. MG RDATA format (EXPERIMENTAL) 16
- 3.3.7. MINFO RDATA format (EXPERIMENTAL) 16
- 3.3.8. MR RDATA format (EXPERIMENTAL) 17
- 3.3.9. MX RDATA format 17
- 3.3.10. NULL RDATA format (EXPERIMENTAL) 17
- 3.3.11. NS RDATA format 18
- 3.3.12. PTR RDATA format 18
- 3.3.13. SOA RDATA format 19
- 3.3.14. TXT RDATA format 20
- 3.4. ARPA Internet specific RRs 20
- 3.4.1. A RDATA format 20
- 3.4.2. WKS RDATA format 21
- 3.5. IN-ADDR.ARPA domain 22
- 3.6. Defining new types, classes, and special namespaces 24
- 4. MESSAGES 25
- 4.1. Format 25
- 4.1.1. Header section format 26
- 4.1.2. Question section format 28
- 4.1.3. Resource record format 29
- 4.1.4. Message compression 30
- 4.2. Transport 32
- 4.2.1. UDP usage 32
- 4.2.2. TCP usage 32
- 5. MASTER FILES 33
- 5.1. Format 33
- 5.2. Use of master files to define zones 35
- 5.3. Master file example 36
- 6. NAME SERVER IMPLEMENTATION 37
- 6.1. Architecture 37
- 6.1.1. Control 37
- 6.1.2. Database 37
- 6.1.3. Time 39
- 6.2. Standard query processing 39
- 6.3. Zone refresh and reload processing 39
- 6.4. Inverse queries (Optional) 40
- 6.4.1. The contents of inverse queries and responses 40
- 6.4.2. Inverse query and response example 41
- 6.4.3. Inverse query processing 42
-
-
-
-
-
-
-Mockapetris [Page 2]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- 6.5. Completion queries and responses 42
- 7. RESOLVER IMPLEMENTATION 43
- 7.1. Transforming a user request into a query 43
- 7.2. Sending the queries 44
- 7.3. Processing responses 46
- 7.4. Using the cache 47
- 8. MAIL SUPPORT 47
- 8.1. Mail exchange binding 48
- 8.2. Mailbox binding (Experimental) 48
- 9. REFERENCES and BIBLIOGRAPHY 50
- Index 54
-
-2. INTRODUCTION
-
-2.1. Overview
-
-The goal of domain names is to provide a mechanism for naming resources
-in such a way that the names are usable in different hosts, networks,
-protocol families, internets, and administrative organizations.
-
-From the user's point of view, domain names are useful as arguments to a
-local agent, called a resolver, which retrieves information associated
-with the domain name. Thus a user might ask for the host address or
-mail information associated with a particular domain name. To enable
-the user to request a particular type of information, an appropriate
-query type is passed to the resolver with the domain name. To the user,
-the domain tree is a single information space; the resolver is
-responsible for hiding the distribution of data among name servers from
-the user.
-
-From the resolver's point of view, the database that makes up the domain
-space is distributed among various name servers. Different parts of the
-domain space are stored in different name servers, although a particular
-data item will be stored redundantly in two or more name servers. The
-resolver starts with knowledge of at least one name server. When the
-resolver processes a user query it asks a known name server for the
-information; in return, the resolver either receives the desired
-information or a referral to another name server. Using these
-referrals, resolvers learn the identities and contents of other name
-servers. Resolvers are responsible for dealing with the distribution of
-the domain space and dealing with the effects of name server failure by
-consulting redundant databases in other servers.
-
-Name servers manage two kinds of data. The first kind of data held in
-sets called zones; each zone is the complete database for a particular
-"pruned" subtree of the domain space. This data is called
-authoritative. A name server periodically checks to make sure that its
-zones are up to date, and if not, obtains a new copy of updated zones
-
-
-
-Mockapetris [Page 3]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-from master files stored locally or in another name server. The second
-kind of data is cached data which was acquired by a local resolver.
-This data may be incomplete, but improves the performance of the
-retrieval process when non-local data is repeatedly accessed. Cached
-data is eventually discarded by a timeout mechanism.
-
-This functional structure isolates the problems of user interface,
-failure recovery, and distribution in the resolvers and isolates the
-database update and refresh problems in the name servers.
-
-2.2. Common configurations
-
-A host can participate in the domain name system in a number of ways,
-depending on whether the host runs programs that retrieve information
-from the domain system, name servers that answer queries from other
-hosts, or various combinations of both functions. The simplest, and
-perhaps most typical, configuration is shown below:
-
- Local Host | Foreign
- |
- +---------+ +----------+ | +--------+
- | | user queries | |queries | | |
- | User |-------------->| |---------|->|Foreign |
- | Program | | Resolver | | | Name |
- | |<--------------| |<--------|--| Server |
- | | user responses| |responses| | |
- +---------+ +----------+ | +--------+
- | A |
- cache additions | | references |
- V | |
- +----------+ |
- | cache | |
- +----------+ |
-
-User programs interact with the domain name space through resolvers; the
-format of user queries and user responses is specific to the host and
-its operating system. User queries will typically be operating system
-calls, and the resolver and its cache will be part of the host operating
-system. Less capable hosts may choose to implement the resolver as a
-subroutine to be linked in with every program that needs its services.
-Resolvers answer user queries with information they acquire via queries
-to foreign name servers and the local cache.
-
-Note that the resolver may have to make several queries to several
-different foreign name servers to answer a particular user query, and
-hence the resolution of a user query may involve several network
-accesses and an arbitrary amount of time. The queries to foreign name
-servers and the corresponding responses have a standard format described
-
-
-
-Mockapetris [Page 4]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-in this memo, and may be datagrams.
-
-Depending on its capabilities, a name server could be a stand alone
-program on a dedicated machine or a process or processes on a large
-timeshared host. A simple configuration might be:
-
- Local Host | Foreign
- |
- +---------+ |
- / /| |
- +---------+ | +----------+ | +--------+
- | | | | |responses| | |
- | | | | Name |---------|->|Foreign |
- | Master |-------------->| Server | | |Resolver|
- | files | | | |<--------|--| |
- | |/ | | queries | +--------+
- +---------+ +----------+ |
-
-Here a primary name server acquires information about one or more zones
-by reading master files from its local file system, and answers queries
-about those zones that arrive from foreign resolvers.
-
-The DNS requires that all zones be redundantly supported by more than
-one name server. Designated secondary servers can acquire zones and
-check for updates from the primary server using the zone transfer
-protocol of the DNS. This configuration is shown below:
-
- Local Host | Foreign
- |
- +---------+ |
- / /| |
- +---------+ | +----------+ | +--------+
- | | | | |responses| | |
- | | | | Name |---------|->|Foreign |
- | Master |-------------->| Server | | |Resolver|
- | files | | | |<--------|--| |
- | |/ | | queries | +--------+
- +---------+ +----------+ |
- A |maintenance | +--------+
- | +------------|->| |
- | queries | |Foreign |
- | | | Name |
- +------------------|--| Server |
- maintenance responses | +--------+
-
-In this configuration, the name server periodically establishes a
-virtual circuit to a foreign name server to acquire a copy of a zone or
-to check that an existing copy has not changed. The messages sent for
-
-
-
-Mockapetris [Page 5]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-these maintenance activities follow the same form as queries and
-responses, but the message sequences are somewhat different.
-
-The information flow in a host that supports all aspects of the domain
-name system is shown below:
-
- Local Host | Foreign
- |
- +---------+ +----------+ | +--------+
- | | user queries | |queries | | |
- | User |-------------->| |---------|->|Foreign |
- | Program | | Resolver | | | Name |
- | |<--------------| |<--------|--| Server |
- | | user responses| |responses| | |
- +---------+ +----------+ | +--------+
- | A |
- cache additions | | references |
- V | |
- +----------+ |
- | Shared | |
- | database | |
- +----------+ |
- A | |
- +---------+ refreshes | | references |
- / /| | V |
- +---------+ | +----------+ | +--------+
- | | | | |responses| | |
- | | | | Name |---------|->|Foreign |
- | Master |-------------->| Server | | |Resolver|
- | files | | | |<--------|--| |
- | |/ | | queries | +--------+
- +---------+ +----------+ |
- A |maintenance | +--------+
- | +------------|->| |
- | queries | |Foreign |
- | | | Name |
- +------------------|--| Server |
- maintenance responses | +--------+
-
-The shared database holds domain space data for the local name server
-and resolver. The contents of the shared database will typically be a
-mixture of authoritative data maintained by the periodic refresh
-operations of the name server and cached data from previous resolver
-requests. The structure of the domain data and the necessity for
-synchronization between name servers and resolvers imply the general
-characteristics of this database, but the actual format is up to the
-local implementor.
-
-
-
-
-Mockapetris [Page 6]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-Information flow can also be tailored so that a group of hosts act
-together to optimize activities. Sometimes this is done to offload less
-capable hosts so that they do not have to implement a full resolver.
-This can be appropriate for PCs or hosts which want to minimize the
-amount of new network code which is required. This scheme can also
-allow a group of hosts can share a small number of caches rather than
-maintaining a large number of separate caches, on the premise that the
-centralized caches will have a higher hit ratio. In either case,
-resolvers are replaced with stub resolvers which act as front ends to
-resolvers located in a recursive server in one or more name servers
-known to perform that service:
-
- Local Hosts | Foreign
- |
- +---------+ |
- | | responses |
- | Stub |<--------------------+ |
- | Resolver| | |
- | |----------------+ | |
- +---------+ recursive | | |
- queries | | |
- V | |
- +---------+ recursive +----------+ | +--------+
- | | queries | |queries | | |
- | Stub |-------------->| Recursive|---------|->|Foreign |
- | Resolver| | Server | | | Name |
- | |<--------------| |<--------|--| Server |
- +---------+ responses | |responses| | |
- +----------+ | +--------+
- | Central | |
- | cache | |
- +----------+ |
-
-In any case, note that domain components are always replicated for
-reliability whenever possible.
-
-2.3. Conventions
-
-The domain system has several conventions dealing with low-level, but
-fundamental, issues. While the implementor is free to violate these
-conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
-ALL behavior observed from other hosts.
-
-2.3.1. Preferred name syntax
-
-The DNS specifications attempt to be as general as possible in the rules
-for constructing domain names. The idea is that the name of any
-existing object can be expressed as a domain name with minimal changes.
-
-
-
-Mockapetris [Page 7]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-However, when assigning a domain name for an object, the prudent user
-will select a name which satisfies both the rules of the domain system
-and any existing rules for the object, whether these rules are published
-or implied by existing programs.
-
-For example, when naming a mail domain, the user should satisfy both the
-rules of this memo and those in RFC-822. When creating a new host name,
-the old rules for HOSTS.TXT should be followed. This avoids problems
-when old software is converted to use domain names.
-
-The following syntax will result in fewer problems with many
-
-applications that use domain names (e.g., mail, TELNET).
-
-<domain> ::= <subdomain> | " "
-
-<subdomain> ::= <label> | <subdomain> "." <label>
-
-<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-
-<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-
-<let-dig-hyp> ::= <let-dig> | "-"
-
-<let-dig> ::= <letter> | <digit>
-
-<letter> ::= any one of the 52 alphabetic characters A through Z in
-upper case and a through z in lower case
-
-<digit> ::= any one of the ten digits 0 through 9
-
-Note that while upper and lower case letters are allowed in domain
-names, no significance is attached to the case. That is, two names with
-the same spelling but different case are to be treated as if identical.
-
-The labels must follow the rules for ARPANET host names. They must
-start with a letter, end with a letter or digit, and have as interior
-characters only letters, digits, and hyphen. There are also some
-restrictions on the length. Labels must be 63 characters or less.
-
-For example, the following strings identify hosts in the Internet:
-
-A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
-
-2.3.2. Data Transmission Order
-
-The order of transmission of the header and data described in this
-document is resolved to the octet level. Whenever a diagram shows a
-
-
-
-Mockapetris [Page 8]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-group of octets, the order of transmission of those octets is the normal
-order in which they are read in English. For example, in the following
-diagram, the octets are transmitted in the order they are numbered.
-
- 0 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | 1 | 2 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | 3 | 4 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | 5 | 6 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-Whenever an octet represents a numeric quantity, the left most bit in
-the diagram is the high order or most significant bit. That is, the bit
-labeled 0 is the most significant bit. For example, the following
-diagram represents the value 170 (decimal).
-
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |1 0 1 0 1 0 1 0|
- +-+-+-+-+-+-+-+-+
-
-Similarly, whenever a multi-octet field represents a numeric quantity
-the left most bit of the whole field is the most significant bit. When
-a multi-octet quantity is transmitted the most significant octet is
-transmitted first.
-
-2.3.3. Character Case
-
-For all parts of the DNS that are part of the official protocol, all
-comparisons between character strings (e.g., labels, domain names, etc.)
-are done in a case-insensitive manner. At present, this rule is in
-force throughout the domain system without exception. However, future
-additions beyond current usage may need to use the full binary octet
-capabilities in names, so attempts to store domain names in 7-bit ASCII
-or use of special bytes to terminate labels, etc., should be avoided.
-
-When data enters the domain system, its original case should be
-preserved whenever possible. In certain circumstances this cannot be
-done. For example, if two RRs are stored in a database, one at x.y and
-one at X.Y, they are actually stored at the same place in the database,
-and hence only one casing would be preserved. The basic rule is that
-case can be discarded only when data is used to define structure in a
-database, and two names are identical when compared in a case
-insensitive manner.
-
-
-
-
-Mockapetris [Page 9]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-Loss of case sensitive data must be minimized. Thus while data for x.y
-and X.Y may both be stored under a single location x.y or X.Y, data for
-a.x and B.X would never be stored under A.x, A.X, b.x, or b.X. In
-general, this preserves the case of the first label of a domain name,
-but forces standardization of interior node labels.
-
-Systems administrators who enter data into the domain database should
-take care to represent the data they supply to the domain system in a
-case-consistent manner if their system is case-sensitive. The data
-distribution system in the domain system will ensure that consistent
-representations are preserved.
-
-2.3.4. Size limits
-
-Various objects and parameters in the DNS have size limits. They are
-listed below. Some could be easily changed, others are more
-fundamental.
-
-labels 63 octets or less
-
-names 255 octets or less
-
-TTL positive values of a signed 32 bit number.
-
-UDP messages 512 octets or less
-
-3. DOMAIN NAME SPACE AND RR DEFINITIONS
-
-3.1. Name space definitions
-
-Domain names in messages are expressed in terms of a sequence of labels.
-Each label is represented as a one octet length field followed by that
-number of octets. Since every domain name ends with the null label of
-the root, a domain name is terminated by a length byte of zero. The
-high order two bits of every length octet must be zero, and the
-remaining six bits of the length field limit the label to 63 octets or
-less.
-
-To simplify implementations, the total length of a domain name (i.e.,
-label octets and label length octets) is restricted to 255 octets or
-less.
-
-Although labels can contain any 8 bit values in octets that make up a
-label, it is strongly recommended that labels follow the preferred
-syntax described elsewhere in this memo, which is compatible with
-existing host naming conventions. Name servers and resolvers must
-compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
-with zero parity. Non-alphabetic codes must match exactly.
-
-
-
-Mockapetris [Page 10]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.2. RR definitions
-
-3.2.1. Format
-
-All RRs have the same top level format shown below:
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | |
- / /
- / NAME /
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | TYPE |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | CLASS |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | TTL |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | RDLENGTH |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
- / RDATA /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-
-where:
-
-NAME an owner name, i.e., the name of the node to which this
- resource record pertains.
-
-TYPE two octets containing one of the RR TYPE codes.
-
-CLASS two octets containing one of the RR CLASS codes.
-
-TTL a 32 bit signed integer that specifies the time interval
- that the resource record may be cached before the source
- of the information should again be consulted. Zero
- values are interpreted to mean that the RR can only be
- used for the transaction in progress, and should not be
- cached. For example, SOA records are always distributed
- with a zero TTL to prohibit caching. Zero values can
- also be used for extremely volatile data.
-
-RDLENGTH an unsigned 16 bit integer that specifies the length in
- octets of the RDATA field.
-
-
-
-Mockapetris [Page 11]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-RDATA a variable length string of octets that describes the
- resource. The format of this information varies
- according to the TYPE and CLASS of the resource record.
-
-3.2.2. TYPE values
-
-TYPE fields are used in resource records. Note that these types are a
-subset of QTYPEs.
-
-TYPE value and meaning
-
-A 1 a host address
-
-NS 2 an authoritative name server
-
-MD 3 a mail destination (Obsolete - use MX)
-
-MF 4 a mail forwarder (Obsolete - use MX)
-
-CNAME 5 the canonical name for an alias
-
-SOA 6 marks the start of a zone of authority
-
-MB 7 a mailbox domain name (EXPERIMENTAL)
-
-MG 8 a mail group member (EXPERIMENTAL)
-
-MR 9 a mail rename domain name (EXPERIMENTAL)
-
-NULL 10 a null RR (EXPERIMENTAL)
-
-WKS 11 a well known service description
-
-PTR 12 a domain name pointer
-
-HINFO 13 host information
-
-MINFO 14 mailbox or mail list information
-
-MX 15 mail exchange
-
-TXT 16 text strings
-
-3.2.3. QTYPE values
-
-QTYPE fields appear in the question part of a query. QTYPES are a
-superset of TYPEs, hence all TYPEs are valid QTYPEs. In addition, the
-following QTYPEs are defined:
-
-
-
-Mockapetris [Page 12]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-AXFR 252 A request for a transfer of an entire zone
-
-MAILB 253 A request for mailbox-related records (MB, MG or MR)
-
-MAILA 254 A request for mail agent RRs (Obsolete - see MX)
-
-* 255 A request for all records
-
-3.2.4. CLASS values
-
-CLASS fields appear in resource records. The following CLASS mnemonics
-and values are defined:
-
-IN 1 the Internet
-
-CS 2 the CSNET class (Obsolete - used only for examples in
- some obsolete RFCs)
-
-CH 3 the CHAOS class
-
-HS 4 Hesiod [Dyer 87]
-
-3.2.5. QCLASS values
-
-QCLASS fields appear in the question section of a query. QCLASS values
-are a superset of CLASS values; every CLASS is a valid QCLASS. In
-addition to CLASS values, the following QCLASSes are defined:
-
-* 255 any class
-
-3.3. Standard RRs
-
-The following RR definitions are expected to occur, at least
-potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
-will be used in all classes, and have the same format in all classes.
-Because their RDATA format is known, all domain names in the RDATA
-section of these RRs may be compressed.
-
-<domain-name> is a domain name represented as a series of labels, and
-terminated by a label with zero length. <character-string> is a single
-length octet followed by that number of characters. <character-string>
-is treated as binary information, and can be up to 256 characters in
-length (including the length octet).
-
-
-
-
-
-
-
-
-Mockapetris [Page 13]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.3.1. CNAME RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / CNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-CNAME A <domain-name> which specifies the canonical or primary
- name for the owner. The owner name is an alias.
-
-CNAME RRs cause no additional section processing, but name servers may
-choose to restart the query at the canonical name in certain cases. See
-the description of name server logic in [RFC-1034] for details.
-
-3.3.2. HINFO RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / CPU /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / OS /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-CPU A <character-string> which specifies the CPU type.
-
-OS A <character-string> which specifies the operating
- system type.
-
-Standard values for CPU and OS can be found in [RFC-1010].
-
-HINFO records are used to acquire general information about a host. The
-main use is for protocols such as FTP that can use special procedures
-when talking between machines or operating systems of the same type.
-
-3.3.3. MB RDATA format (EXPERIMENTAL)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / MADNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME A <domain-name> which specifies a host which has the
- specified mailbox.
-
-
-
-Mockapetris [Page 14]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-MB records cause additional section processing which looks up an A type
-RRs corresponding to MADNAME.
-
-3.3.4. MD RDATA format (Obsolete)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / MADNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME A <domain-name> which specifies a host which has a mail
- agent for the domain which should be able to deliver
- mail for the domain.
-
-MD records cause additional section processing which looks up an A type
-record corresponding to MADNAME.
-
-MD is obsolete. See the definition of MX and [RFC-974] for details of
-the new scheme. The recommended policy for dealing with MD RRs found in
-a master file is to reject them, or to convert them to MX RRs with a
-preference of 0.
-
-3.3.5. MF RDATA format (Obsolete)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / MADNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MADNAME A <domain-name> which specifies a host which has a mail
- agent for the domain which will accept mail for
- forwarding to the domain.
-
-MF records cause additional section processing which looks up an A type
-record corresponding to MADNAME.
-
-MF is obsolete. See the definition of MX and [RFC-974] for details ofw
-the new scheme. The recommended policy for dealing with MD RRs found in
-a master file is to reject them, or to convert them to MX RRs with a
-preference of 10.
-
-
-
-
-
-
-
-Mockapetris [Page 15]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.3.6. MG RDATA format (EXPERIMENTAL)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / MGMNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MGMNAME A <domain-name> which specifies a mailbox which is a
- member of the mail group specified by the domain name.
-
-MG records cause no additional section processing.
-
-3.3.7. MINFO RDATA format (EXPERIMENTAL)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / RMAILBX /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / EMAILBX /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-RMAILBX A <domain-name> which specifies a mailbox which is
- responsible for the mailing list or mailbox. If this
- domain name names the root, the owner of the MINFO RR is
- responsible for itself. Note that many existing mailing
- lists use a mailbox X-request for the RMAILBX field of
- mailing list X, e.g., Msgroup-request for Msgroup. This
- field provides a more general mechanism.
-
-
-EMAILBX A <domain-name> which specifies a mailbox which is to
- receive error messages related to the mailing list or
- mailbox specified by the owner of the MINFO RR (similar
- to the ERRORS-TO: field which has been proposed). If
- this domain name names the root, errors should be
- returned to the sender of the message.
-
-MINFO records cause no additional section processing. Although these
-records can be associated with a simple mailbox, they are usually used
-with a mailing list.
-
-
-
-
-
-
-
-
-Mockapetris [Page 16]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.3.8. MR RDATA format (EXPERIMENTAL)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / NEWNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NEWNAME A <domain-name> which specifies a mailbox which is the
- proper rename of the specified mailbox.
-
-MR records cause no additional section processing. The main use for MR
-is as a forwarding entry for a user who has moved to a different
-mailbox.
-
-3.3.9. MX RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | PREFERENCE |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / EXCHANGE /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-PREFERENCE A 16 bit integer which specifies the preference given to
- this RR among others at the same owner. Lower values
- are preferred.
-
-EXCHANGE A <domain-name> which specifies a host willing to act as
- a mail exchange for the owner name.
-
-MX records cause type A additional section processing for the host
-specified by EXCHANGE. The use of MX RRs is explained in detail in
-[RFC-974].
-
-3.3.10. NULL RDATA format (EXPERIMENTAL)
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / <anything> /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-Anything at all may be in the RDATA field so long as it is 65535 octets
-or less.
-
-
-
-
-Mockapetris [Page 17]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-NULL records cause no additional section processing. NULL RRs are not
-allowed in master files. NULLs are used as placeholders in some
-experimental extensions of the DNS.
-
-3.3.11. NS RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / NSDNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NSDNAME A <domain-name> which specifies a host which should be
- authoritative for the specified class and domain.
-
-NS records cause both the usual additional section processing to locate
-a type A record, and, when used in a referral, a special search of the
-zone in which they reside for glue information.
-
-The NS RR states that the named host should be expected to have a zone
-starting at owner name of the specified class. Note that the class may
-not indicate the protocol family which should be used to communicate
-with the host, although it is typically a strong hint. For example,
-hosts which are name servers for either Internet (IN) or Hesiod (HS)
-class information are normally queried using IN class protocols.
-
-3.3.12. PTR RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / PTRDNAME /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-PTRDNAME A <domain-name> which points to some location in the
- domain name space.
-
-PTR records cause no additional section processing. These RRs are used
-in special domains to point to some other location in the domain space.
-These records are simple data, and don't imply any special processing
-similar to that performed by CNAME, which identifies aliases. See the
-description of the IN-ADDR.ARPA domain for an example.
-
-
-
-
-
-
-
-
-Mockapetris [Page 18]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.3.13. SOA RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / MNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / RNAME /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | SERIAL |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | REFRESH |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | RETRY |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | EXPIRE |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | MINIMUM |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-MNAME The <domain-name> of the name server that was the
- original or primary source of data for this zone.
-
-RNAME A <domain-name> which specifies the mailbox of the
- person responsible for this zone.
-
-SERIAL The unsigned 32 bit version number of the original copy
- of the zone. Zone transfers preserve this value. This
- value wraps and should be compared using sequence space
- arithmetic.
-
-REFRESH A 32 bit time interval before the zone should be
- refreshed.
-
-RETRY A 32 bit time interval that should elapse before a
- failed refresh should be retried.
-
-EXPIRE A 32 bit time value that specifies the upper limit on
- the time interval that can elapse before the zone is no
- longer authoritative.
-
-
-
-
-
-Mockapetris [Page 19]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-MINIMUM The unsigned 32 bit minimum TTL field that should be
- exported with any RR from this zone.
-
-SOA records cause no additional section processing.
-
-All times are in units of seconds.
-
-Most of these fields are pertinent only for name server maintenance
-operations. However, MINIMUM is used in all query operations that
-retrieve RRs from a zone. Whenever a RR is sent in a response to a
-query, the TTL field is set to the maximum of the TTL field from the RR
-and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
-bound on the TTL field for all RRs in a zone. Note that this use of
-MINIMUM should occur when the RRs are copied into the response and not
-when the zone is loaded from a master file or via a zone transfer. The
-reason for this provison is to allow future dynamic update facilities to
-change the SOA RR with known semantics.
-
-
-3.3.14. TXT RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- / TXT-DATA /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-TXT-DATA One or more <character-string>s.
-
-TXT RRs are used to hold descriptive text. The semantics of the text
-depends on the domain where it is found.
-
-3.4. Internet specific RRs
-
-3.4.1. A RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | ADDRESS |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ADDRESS A 32 bit Internet address.
-
-Hosts that have multiple Internet addresses will have multiple A
-records.
-
-
-
-
-
-Mockapetris [Page 20]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-A records cause no additional section processing. The RDATA section of
-an A line in a master file is an Internet address expressed as four
-decimal numbers separated by dots without any imbedded spaces (e.g.,
-"10.2.0.52" or "192.0.5.6").
-
-3.4.2. WKS RDATA format
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | ADDRESS |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | PROTOCOL | |
- +--+--+--+--+--+--+--+--+ |
- | |
- / <BIT MAP> /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ADDRESS An 32 bit Internet address
-
-PROTOCOL An 8 bit IP protocol number
-
-<BIT MAP> A variable length bit map. The bit map must be a
- multiple of 8 bits long.
-
-The WKS record is used to describe the well known services supported by
-a particular protocol on a particular internet address. The PROTOCOL
-field specifies an IP protocol number, and the bit map has one bit per
-port of the specified protocol. The first bit corresponds to port 0,
-the second to port 1, etc. If the bit map does not include a bit for a
-protocol of interest, that bit is assumed zero. The appropriate values
-and mnemonics for ports and protocols are specified in [RFC-1010].
-
-For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
-25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
-port 25; if zero, SMTP service is not supported on the specified
-address.
-
-The purpose of WKS RRs is to provide availability information for
-servers for TCP and UDP. If a server supports both TCP and UDP, or has
-multiple Internet addresses, then multiple WKS RRs are used.
-
-WKS RRs cause no additional section processing.
-
-In master files, both ports and protocols are expressed using mnemonics
-or decimal numbers.
-
-
-
-
-Mockapetris [Page 21]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.5. IN-ADDR.ARPA domain
-
-The Internet uses a special domain to support gateway location and
-Internet address to host mapping. Other classes may employ a similar
-strategy in other domains. The intent of this domain is to provide a
-guaranteed method to perform host address to host name mapping, and to
-facilitate queries to locate all gateways on a particular network in the
-Internet.
-
-Note that both of these services are similar to functions that could be
-performed by inverse queries; the difference is that this part of the
-domain name space is structured according to address, and hence can
-guarantee that the appropriate data can be located without an exhaustive
-search of the domain space.
-
-The domain begins at IN-ADDR.ARPA and has a substructure which follows
-the Internet addressing structure.
-
-Domain names in the IN-ADDR.ARPA domain are defined to have up to four
-labels in addition to the IN-ADDR.ARPA suffix. Each label represents
-one octet of an Internet address, and is expressed as a character string
-for a decimal value in the range 0-255 (with leading zeros omitted
-except in the case of a zero octet which is represented by a single
-zero).
-
-Host addresses are represented by domain names that have all four labels
-specified. Thus data for Internet address 10.2.0.52 is located at
-domain name 52.0.2.10.IN-ADDR.ARPA. The reversal, though awkward to
-read, allows zones to be delegated which are exactly one network of
-address space. For example, 10.IN-ADDR.ARPA can be a zone containing
-data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
-MILNET. Address nodes are used to hold pointers to primary host names
-in the normal domain space.
-
-Network numbers correspond to some non-terminal nodes at various depths
-in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
-2, or 3 octets. Network nodes are used to hold pointers to the primary
-host names of gateways attached to that network. Since a gateway is, by
-definition, on more than one network, it will typically have two or more
-network nodes which point at it. Gateways will also have host level
-pointers at their fully qualified addresses.
-
-Both the gateway pointers at network nodes and the normal host pointers
-at full address nodes use the PTR RR to point back to the primary domain
-names of the corresponding hosts.
-
-For example, the IN-ADDR.ARPA domain will contain information about the
-ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
-
-
-
-Mockapetris [Page 22]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU. Assuming that ISI
-gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
-GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
-and a name GW.LCS.MIT.EDU, the domain database would contain:
-
- 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
- 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
- 18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
- 26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
- 22.0.2.10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
- 103.0.0.26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
- 77.0.0.10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
- 4.0.10.18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
- 103.0.3.26.IN-ADDR.ARPA. PTR A.ISI.EDU.
- 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
-
-Thus a program which wanted to locate gateways on net 10 would originate
-a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA. It
-would receive two RRs in response:
-
- 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
- 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
-
-The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
-GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
-these gateways.
-
-A resolver which wanted to find the host name corresponding to Internet
-host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
-QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
-
- 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
-
-Several cautions apply to the use of these services:
- - Since the IN-ADDR.ARPA special domain and the normal domain
- for a particular host or gateway will be in different zones,
- the possibility exists that that the data may be inconsistent.
-
- - Gateways will often have two names in separate domains, only
- one of which can be primary.
-
- - Systems that use the domain database to initialize their
- routing tables must start with enough gateway information to
- guarantee that they can access the appropriate name server.
-
- - The gateway data only reflects the existence of a gateway in a
- manner equivalent to the current HOSTS.TXT file. It doesn't
- replace the dynamic availability information from GGP or EGP.
-
-
-
-Mockapetris [Page 23]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-3.6. Defining new types, classes, and special namespaces
-
-The previously defined types and classes are the ones in use as of the
-date of this memo. New definitions should be expected. This section
-makes some recommendations to designers considering additions to the
-existing facilities. The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
-forum where general discussion of design issues takes place.
-
-In general, a new type is appropriate when new information is to be
-added to the database about an existing object, or we need new data
-formats for some totally new object. Designers should attempt to define
-types and their RDATA formats that are generally applicable to all
-classes, and which avoid duplication of information. New classes are
-appropriate when the DNS is to be used for a new protocol, etc which
-requires new class-specific data formats, or when a copy of the existing
-name space is desired, but a separate management domain is necessary.
-
-New types and classes need mnemonics for master files; the format of the
-master files requires that the mnemonics for type and class be disjoint.
-
-TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
-respectively.
-
-The present system uses multiple RRs to represent multiple values of a
-type rather than storing multiple values in the RDATA section of a
-single RR. This is less efficient for most applications, but does keep
-RRs shorter. The multiple RRs assumption is incorporated in some
-experimental work on dynamic update methods.
-
-The present system attempts to minimize the duplication of data in the
-database in order to insure consistency. Thus, in order to find the
-address of the host for a mail exchange, you map the mail domain name to
-a host name, then the host name to addresses, rather than a direct
-mapping to host address. This approach is preferred because it avoids
-the opportunity for inconsistency.
-
-In defining a new type of data, multiple RR types should not be used to
-create an ordering between entries or express different formats for
-equivalent bindings, instead this information should be carried in the
-body of the RR and a single type used. This policy avoids problems with
-caching multiple types and defining QTYPEs to match multiple types.
-
-For example, the original form of mail exchange binding used two RR
-types one to represent a "closer" exchange (MD) and one to represent a
-"less close" exchange (MF). The difficulty is that the presence of one
-RR type in a cache doesn't convey any information about the other
-because the query which acquired the cached information might have used
-a QTYPE of MF, MD, or MAILA (which matched both). The redesigned
-
-
-
-Mockapetris [Page 24]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-service used a single type (MX) with a "preference" value in the RDATA
-section which can order different RRs. However, if any MX RRs are found
-in the cache, then all should be there.
-
-4. MESSAGES
-
-4.1. Format
-
-All communications inside of the domain protocol are carried in a single
-format called a message. The top level format of message is divided
-into 5 sections (some of which are empty in certain cases) shown below:
-
- +---------------------+
- | Header |
- +---------------------+
- | Question | the question for the name server
- +---------------------+
- | Answer | RRs answering the question
- +---------------------+
- | Authority | RRs pointing toward an authority
- +---------------------+
- | Additional | RRs holding additional information
- +---------------------+
-
-The header section is always present. The header includes fields that
-specify which of the remaining sections are present, and also specify
-whether the message is a query or a response, a standard query or some
-other opcode, etc.
-
-The names of the sections after the header are derived from their use in
-standard queries. The question section contains fields that describe a
-question to a name server. These fields are a query type (QTYPE), a
-query class (QCLASS), and a query domain name (QNAME). The last three
-sections have the same format: a possibly empty list of concatenated
-resource records (RRs). The answer section contains RRs that answer the
-question; the authority section contains RRs that point toward an
-authoritative name server; the additional records section contains RRs
-which relate to the query, but are not strictly answers for the
-question.
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris [Page 25]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-4.1.1. Header section format
-
-The header contains the following fields:
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | ID |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | QDCOUNT |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | ANCOUNT |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | NSCOUNT |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | ARCOUNT |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-ID A 16 bit identifier assigned by the program that
- generates any kind of query. This identifier is copied
- the corresponding reply and can be used by the requester
- to match up replies to outstanding queries.
-
-QR A one bit field that specifies whether this message is a
- query (0), or a response (1).
-
-OPCODE A four bit field that specifies kind of query in this
- message. This value is set by the originator of a query
- and copied into the response. The values are:
-
- 0 a standard query (QUERY)
-
- 1 an inverse query (IQUERY)
-
- 2 a server status request (STATUS)
-
- 3-15 reserved for future use
-
-AA Authoritative Answer - this bit is valid in responses,
- and specifies that the responding name server is an
- authority for the domain name in question section.
-
- Note that the contents of the answer section may have
- multiple owner names because of aliases. The AA bit
-
-
-
-Mockapetris [Page 26]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- corresponds to the name which matches the query name, or
- the first owner name in the answer section.
-
-TC TrunCation - specifies that this message was truncated
- due to length greater than that permitted on the
- transmission channel.
-
-RD Recursion Desired - this bit may be set in a query and
- is copied into the response. If RD is set, it directs
- the name server to pursue the query recursively.
- Recursive query support is optional.
-
-RA Recursion Available - this be is set or cleared in a
- response, and denotes whether recursive query support is
- available in the name server.
-
-Z Reserved for future use. Must be zero in all queries
- and responses.
-
-RCODE Response code - this 4 bit field is set as part of
- responses. The values have the following
- interpretation:
-
- 0 No error condition
-
- 1 Format error - The name server was
- unable to interpret the query.
-
- 2 Server failure - The name server was
- unable to process this query due to a
- problem with the name server.
-
- 3 Name Error - Meaningful only for
- responses from an authoritative name
- server, this code signifies that the
- domain name referenced in the query does
- not exist.
-
- 4 Not Implemented - The name server does
- not support the requested kind of query.
-
- 5 Refused - The name server refuses to
- perform the specified operation for
- policy reasons. For example, a name
- server may not wish to provide the
- information to the particular requester,
- or a name server may not wish to perform
- a particular operation (e.g., zone
-
-
-
-Mockapetris [Page 27]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- transfer) for particular data.
-
- 6-15 Reserved for future use.
-
-QDCOUNT an unsigned 16 bit integer specifying the number of
- entries in the question section.
-
-ANCOUNT an unsigned 16 bit integer specifying the number of
- resource records in the answer section.
-
-NSCOUNT an unsigned 16 bit integer specifying the number of name
- server resource records in the authority records
- section.
-
-ARCOUNT an unsigned 16 bit integer specifying the number of
- resource records in the additional records section.
-
-4.1.2. Question section format
-
-The question section is used to carry the "question" in most queries,
-i.e., the parameters that define what is being asked. The section
-contains QDCOUNT (usually 1) entries, each of the following format:
-
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | |
- / QNAME /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | QTYPE |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | QCLASS |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-QNAME a domain name represented as a sequence of labels, where
- each label consists of a length octet followed by that
- number of octets. The domain name terminates with the
- zero length octet for the null label of the root. Note
- that this field may be an odd number of octets; no
- padding is used.
-
-QTYPE a two octet code which specifies the type of the query.
- The values for this field include all codes valid for a
- TYPE field, together with some more general codes which
- can match more than one type of RR.
-
-
-
-Mockapetris [Page 28]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-QCLASS a two octet code that specifies the class of the query.
- For example, the QCLASS field is IN for the Internet.
-
-4.1.3. Resource record format
-
-The answer, authority, and additional sections all share the same
-format: a variable number of resource records, where the number of
-records is specified in the corresponding count field in the header.
-Each resource record has the following format:
- 1 1 1 1 1 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | |
- / /
- / NAME /
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | TYPE |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | CLASS |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | TTL |
- | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | RDLENGTH |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
- / RDATA /
- / /
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-where:
-
-NAME a domain name to which this resource record pertains.
-
-TYPE two octets containing one of the RR type codes. This
- field specifies the meaning of the data in the RDATA
- field.
-
-CLASS two octets which specify the class of the data in the
- RDATA field.
-
-TTL a 32 bit unsigned integer that specifies the time
- interval (in seconds) that the resource record may be
- cached before it should be discarded. Zero values are
- interpreted to mean that the RR can only be used for the
- transaction in progress, and should not be cached.
-
-
-
-
-
-Mockapetris [Page 29]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-RDLENGTH an unsigned 16 bit integer that specifies the length in
- octets of the RDATA field.
-
-RDATA a variable length string of octets that describes the
- resource. The format of this information varies
- according to the TYPE and CLASS of the resource record.
- For example, the if the TYPE is A and the CLASS is IN,
- the RDATA field is a 4 octet ARPA Internet address.
-
-4.1.4. Message compression
-
-In order to reduce the size of messages, the domain system utilizes a
-compression scheme which eliminates the repetition of domain names in a
-message. In this scheme, an entire domain name or a list of labels at
-the end of a domain name is replaced with a pointer to a prior occurance
-of the same name.
-
-The pointer takes the form of a two octet sequence:
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- | 1 1| OFFSET |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-The first two bits are ones. This allows a pointer to be distinguished
-from a label, since the label must begin with two zero bits because
-labels are restricted to 63 octets or less. (The 10 and 01 combinations
-are reserved for future use.) The OFFSET field specifies an offset from
-the start of the message (i.e., the first octet of the ID field in the
-domain header). A zero offset specifies the first byte of the ID field,
-etc.
-
-The compression scheme allows a domain name in a message to be
-represented as either:
-
- - a sequence of labels ending in a zero octet
-
- - a pointer
-
- - a sequence of labels ending with a pointer
-
-Pointers can only be used for occurances of a domain name where the
-format is not class specific. If this were not the case, a name server
-or resolver would be required to know the format of all RRs it handled.
-As yet, there are no such cases, but they may occur in future RDATA
-formats.
-
-If a domain name is contained in a part of the message subject to a
-length field (such as the RDATA section of an RR), and compression is
-
-
-
-Mockapetris [Page 30]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-used, the length of the compressed name is used in the length
-calculation, rather than the length of the expanded name.
-
-Programs are free to avoid using pointers in messages they generate,
-although this will reduce datagram capacity, and may cause truncation.
-However all programs are required to understand arriving messages that
-contain pointers.
-
-For example, a datagram might need to use the domain names F.ISI.ARPA,
-FOO.F.ISI.ARPA, ARPA, and the root. Ignoring the other fields of the
-message, these domain names might be represented as:
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 20 | 1 | F |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 22 | 3 | I |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 24 | S | I |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 26 | 4 | A |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 28 | R | P |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 30 | A | 0 |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 40 | 3 | F |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 42 | O | O |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 44 | 1 1| 20 |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 64 | 1 1| 26 |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- 92 | 0 | |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-The domain name for F.ISI.ARPA is shown at offset 20. The domain name
-FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
-concatenate a label for FOO to the previously defined F.ISI.ARPA. The
-domain name ARPA is defined at offset 64 using a pointer to the ARPA
-component of the name F.ISI.ARPA at 20; note that this pointer relies on
-ARPA being the last label in the string at 20. The root domain name is
-
-
-
-Mockapetris [Page 31]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-defined by a single octet of zeros at 92; the root domain name has no
-labels.
-
-4.2. Transport
-
-The DNS assumes that messages will be transmitted as datagrams or in a
-byte stream carried by a virtual circuit. While virtual circuits can be
-used for any DNS activity, datagrams are preferred for queries due to
-their lower overhead and better performance. Zone refresh activities
-must use virtual circuits because of the need for reliable transfer.
-
-The Internet supports name server access using TCP [RFC-793] on server
-port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
-port 53 (decimal).
-
-4.2.1. UDP usage
-
-Messages sent using UDP user server port 53 (decimal).
-
-Messages carried by UDP are restricted to 512 bytes (not counting the IP
-or UDP headers). Longer messages are truncated and the TC bit is set in
-the header.
-
-UDP is not acceptable for zone transfers, but is the recommended method
-for standard queries in the Internet. Queries sent using UDP may be
-lost, and hence a retransmission strategy is required. Queries or their
-responses may be reordered by the network, or by processing in name
-servers, so resolvers should not depend on them being returned in order.
-
-The optimal UDP retransmission policy will vary with performance of the
-Internet and the needs of the client, but the following are recommended:
-
- - The client should try other servers and server addresses
- before repeating a query to a specific address of a server.
-
- - The retransmission interval should be based on prior
- statistics if possible. Too aggressive retransmission can
- easily slow responses for the community at large. Depending
- on how well connected the client is to its expected servers,
- the minimum retransmission interval should be 2-5 seconds.
-
-More suggestions on server selection and retransmission policy can be
-found in the resolver section of this memo.
-
-4.2.2. TCP usage
-
-Messages sent over TCP connections use server port 53 (decimal). The
-message is prefixed with a two byte length field which gives the message
-
-
-
-Mockapetris [Page 32]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-length, excluding the two byte length field. This length field allows
-the low-level processing to assemble a complete message before beginning
-to parse it.
-
-Several connection management policies are recommended:
-
- - The server should not block other activities waiting for TCP
- data.
-
- - The server should support multiple connections.
-
- - The server should assume that the client will initiate
- connection closing, and should delay closing its end of the
- connection until all outstanding client requests have been
- satisfied.
-
- - If the server needs to close a dormant connection to reclaim
- resources, it should wait until the connection has been idle
- for a period on the order of two minutes. In particular, the
- server should allow the SOA and AXFR request sequence (which
- begins a refresh operation) to be made on a single connection.
- Since the server would be unable to answer queries anyway, a
- unilateral close or reset may be used instead of a graceful
- close.
-
-5. MASTER FILES
-
-Master files are text files that contain RRs in text form. Since the
-contents of a zone can be expressed in the form of a list of RRs a
-master file is most often used to define a zone, though it can be used
-to list a cache's contents. Hence, this section first discusses the
-format of RRs in a master file, and then the special considerations when
-a master file is used to create a zone in some name server.
-
-5.1. Format
-
-The format of these files is a sequence of entries. Entries are
-predominantly line-oriented, though parentheses can be used to continue
-a list of items across a line boundary, and text literals can contain
-CRLF within the text. Any combination of tabs and spaces act as a
-delimiter between the separate items that make up an entry. The end of
-any line in the master file can end with a comment. The comment starts
-with a ";" (semicolon).
-
-The following entries are defined:
-
- <blank>[<comment>]
-
-
-
-
-Mockapetris [Page 33]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- $ORIGIN <domain-name> [<comment>]
-
- $INCLUDE <file-name> [<domain-name>] [<comment>]
-
- <domain-name><rr> [<comment>]
-
- <blank><rr> [<comment>]
-
-Blank lines, with or without comments, are allowed anywhere in the file.
-
-Two control entries are defined: $ORIGIN and $INCLUDE. $ORIGIN is
-followed by a domain name, and resets the current origin for relative
-domain names to the stated name. $INCLUDE inserts the named file into
-the current file, and may optionally specify a domain name that sets the
-relative domain name origin for the included file. $INCLUDE may also
-have a comment. Note that a $INCLUDE entry never changes the relative
-origin of the parent file, regardless of changes to the relative origin
-made within the included file.
-
-The last two forms represent RRs. If an entry for an RR begins with a
-blank, then the RR is assumed to be owned by the last stated owner. If
-an RR entry begins with a <domain-name>, then the owner name is reset.
-
-<rr> contents take one of the following forms:
-
- [<TTL>] [<class>] <type> <RDATA>
-
- [<class>] [<TTL>] <type> <RDATA>
-
-The RR begins with optional TTL and class fields, followed by a type and
-RDATA field appropriate to the type and class. Class and type use the
-standard mnemonics, TTL is a decimal integer. Omitted class and TTL
-values are default to the last explicitly stated values. Since type and
-class mnemonics are disjoint, the parse is unique. (Note that this
-order is different from the order used in examples and the order used in
-the actual RRs; the given order allows easier parsing and defaulting.)
-
-<domain-name>s make up a large share of the data in the master file.
-The labels in the domain name are expressed as character strings and
-separated by dots. Quoting conventions allow arbitrary characters to be
-stored in domain names. Domain names that end in a dot are called
-absolute, and are taken as complete. Domain names which do not end in a
-dot are called relative; the actual domain name is the concatenation of
-the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
-an argument to the master file loading routine. A relative name is an
-error when no origin is available.
-
-
-
-
-
-Mockapetris [Page 34]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-<character-string> is expressed in one or two ways: as a contiguous set
-of characters without interior spaces, or as a string beginning with a "
-and ending with a ". Inside a " delimited string any character can
-occur, except for a " itself, which must be quoted using \ (back slash).
-
-Because these files are text files several special encodings are
-necessary to allow arbitrary data to be loaded. In particular:
-
- of the root.
-
-@ A free standing @ is used to denote the current origin.
-
-\X where X is any character other than a digit (0-9), is
- used to quote that character so that its special meaning
- does not apply. For example, "\." can be used to place
- a dot character in a label.
-
-\DDD where each D is a digit is the octet corresponding to
- the decimal number described by DDD. The resulting
- octet is assumed to be text and is not checked for
- special meaning.
-
-( ) Parentheses are used to group data that crosses a line
- boundary. In effect, line terminations are not
- recognized within parentheses.
-
-; Semicolon is used to start a comment; the remainder of
- the line is ignored.
-
-5.2. Use of master files to define zones
-
-When a master file is used to load a zone, the operation should be
-suppressed if any errors are encountered in the master file. The
-rationale for this is that a single error can have widespread
-consequences. For example, suppose that the RRs defining a delegation
-have syntax errors; then the server will return authoritative name
-errors for all names in the subzone (except in the case where the
-subzone is also present on the server).
-
-Several other validity checks that should be performed in addition to
-insuring that the file is syntactically correct:
-
- 1. All RRs in the file should have the same class.
-
- 2. Exactly one SOA RR should be present at the top of the zone.
-
- 3. If delegations are present and glue information is required,
- it should be present.
-
-
-
-Mockapetris [Page 35]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- 4. Information present outside of the authoritative nodes in the
- zone should be glue information, rather than the result of an
- origin or similar error.
-
-5.3. Master file example
-
-The following is an example file which might be used to define the
-ISI.EDU zone.and is loaded with an origin of ISI.EDU:
-
-@ IN SOA VENERA Action\.domains (
- 20 ; SERIAL
- 7200 ; REFRESH
- 600 ; RETRY
- 3600000; EXPIRE
- 60) ; MINIMUM
-
- NS A.ISI.EDU.
- NS VENERA
- NS VAXA
- MX 10 VENERA
- MX 20 VAXA
-
-A A 26.3.0.103
-
-VENERA A 10.1.0.52
- A 128.9.0.32
-
-VAXA A 10.2.0.27
- A 128.9.0.33
-
-
-$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
-
-Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
-
- MOE MB A.ISI.EDU.
- LARRY MB A.ISI.EDU.
- CURLEY MB A.ISI.EDU.
- STOOGES MG MOE
- MG LARRY
- MG CURLEY
-
-Note the use of the \ character in the SOA RR to specify the responsible
-person mailbox "Action.domains@E.ISI.EDU".
-
-
-
-
-
-
-
-Mockapetris [Page 36]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-6. NAME SERVER IMPLEMENTATION
-
-6.1. Architecture
-
-The optimal structure for the name server will depend on the host
-operating system and whether the name server is integrated with resolver
-operations, either by supporting recursive service, or by sharing its
-database with a resolver. This section discusses implementation
-considerations for a name server which shares a database with a
-resolver, but most of these concerns are present in any name server.
-
-6.1.1. Control
-
-A name server must employ multiple concurrent activities, whether they
-are implemented as separate tasks in the host's OS or multiplexing
-inside a single name server program. It is simply not acceptable for a
-name server to block the service of UDP requests while it waits for TCP
-data for refreshing or query activities. Similarly, a name server
-should not attempt to provide recursive service without processing such
-requests in parallel, though it may choose to serialize requests from a
-single client, or to regard identical requests from the same client as
-duplicates. A name server should not substantially delay requests while
-it reloads a zone from master files or while it incorporates a newly
-refreshed zone into its database.
-
-6.1.2. Database
-
-While name server implementations are free to use any internal data
-structures they choose, the suggested structure consists of three major
-parts:
-
- - A "catalog" data structure which lists the zones available to
- this server, and a "pointer" to the zone data structure. The
- main purpose of this structure is to find the nearest ancestor
- zone, if any, for arriving standard queries.
-
- - Separate data structures for each of the zones held by the
- name server.
-
- - A data structure for cached data. (or perhaps separate caches
- for different classes)
-
-All of these data structures can be implemented an identical tree
-structure format, with different data chained off the nodes in different
-parts: in the catalog the data is pointers to zones, while in the zone
-and cache data structures, the data will be RRs. In designing the tree
-framework the designer should recognize that query processing will need
-to traverse the tree using case-insensitive label comparisons; and that
-
-
-
-Mockapetris [Page 37]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-in real data, a few nodes have a very high branching factor (100-1000 or
-more), but the vast majority have a very low branching factor (0-1).
-
-One way to solve the case problem is to store the labels for each node
-in two pieces: a standardized-case representation of the label where all
-ASCII characters are in a single case, together with a bit mask that
-denotes which characters are actually of a different case. The
-branching factor diversity can be handled using a simple linked list for
-a node until the branching factor exceeds some threshold, and
-transitioning to a hash structure after the threshold is exceeded. In
-any case, hash structures used to store tree sections must insure that
-hash functions and procedures preserve the casing conventions of the
-DNS.
-
-The use of separate structures for the different parts of the database
-is motivated by several factors:
-
- - The catalog structure can be an almost static structure that
- need change only when the system administrator changes the
- zones supported by the server. This structure can also be
- used to store parameters used to control refreshing
- activities.
-
- - The individual data structures for zones allow a zone to be
- replaced simply by changing a pointer in the catalog. Zone
- refresh operations can build a new structure and, when
- complete, splice it into the database via a simple pointer
- replacement. It is very important that when a zone is
- refreshed, queries should not use old and new data
- simultaneously.
-
- - With the proper search procedures, authoritative data in zones
- will always "hide", and hence take precedence over, cached
- data.
-
- - Errors in zone definitions that cause overlapping zones, etc.,
- may cause erroneous responses to queries, but problem
- determination is simplified, and the contents of one "bad"
- zone can't corrupt another.
-
- - Since the cache is most frequently updated, it is most
- vulnerable to corruption during system restarts. It can also
- become full of expired RR data. In either case, it can easily
- be discarded without disturbing zone data.
-
-A major aspect of database design is selecting a structure which allows
-the name server to deal with crashes of the name server's host. State
-information which a name server should save across system crashes
-
-
-
-Mockapetris [Page 38]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-includes the catalog structure (including the state of refreshing for
-each zone) and the zone data itself.
-
-6.1.3. Time
-
-Both the TTL data for RRs and the timing data for refreshing activities
-depends on 32 bit timers in units of seconds. Inside the database,
-refresh timers and TTLs for cached data conceptually "count down", while
-data in the zone stays with constant TTLs.
-
-A recommended implementation strategy is to store time in two ways: as
-a relative increment and as an absolute time. One way to do this is to
-use positive 32 bit numbers for one type and negative numbers for the
-other. The RRs in zones use relative times; the refresh timers and
-cache data use absolute times. Absolute numbers are taken with respect
-to some known origin and converted to relative values when placed in the
-response to a query. When an absolute TTL is negative after conversion
-to relative, then the data is expired and should be ignored.
-
-6.2. Standard query processing
-
-The major algorithm for standard query processing is presented in
-[RFC-1034].
-
-When processing queries with QCLASS=*, or some other QCLASS which
-matches multiple classes, the response should never be authoritative
-unless the server can guarantee that the response covers all classes.
-
-When composing a response, RRs which are to be inserted in the
-additional section, but duplicate RRs in the answer or authority
-sections, may be omitted from the additional section.
-
-When a response is so long that truncation is required, the truncation
-should start at the end of the response and work forward in the
-datagram. Thus if there is any data for the authority section, the
-answer section is guaranteed to be unique.
-
-The MINIMUM value in the SOA should be used to set a floor on the TTL of
-data distributed from a zone. This floor function should be done when
-the data is copied into a response. This will allow future dynamic
-update protocols to change the SOA MINIMUM field without ambiguous
-semantics.
-
-6.3. Zone refresh and reload processing
-
-In spite of a server's best efforts, it may be unable to load zone data
-from a master file due to syntax errors, etc., or be unable to refresh a
-zone within the its expiration parameter. In this case, the name server
-
-
-
-Mockapetris [Page 39]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-should answer queries as if it were not supposed to possess the zone.
-
-If a master is sending a zone out via AXFR, and a new version is created
-during the transfer, the master should continue to send the old version
-if possible. In any case, it should never send part of one version and
-part of another. If completion is not possible, the master should reset
-the connection on which the zone transfer is taking place.
-
-6.4. Inverse queries (Optional)
-
-Inverse queries are an optional part of the DNS. Name servers are not
-required to support any form of inverse queries. If a name server
-receives an inverse query that it does not support, it returns an error
-response with the "Not Implemented" error set in the header. While
-inverse query support is optional, all name servers must be at least
-able to return the error response.
-
-6.4.1. The contents of inverse queries and responses Inverse
-queries reverse the mappings performed by standard query operations;
-while a standard query maps a domain name to a resource, an inverse
-query maps a resource to a domain name. For example, a standard query
-might bind a domain name to a host address; the corresponding inverse
-query binds the host address to a domain name.
-
-Inverse queries take the form of a single RR in the answer section of
-the message, with an empty question section. The owner name of the
-query RR and its TTL are not significant. The response carries
-questions in the question section which identify all names possessing
-the query RR WHICH THE NAME SERVER KNOWS. Since no name server knows
-about all of the domain name space, the response can never be assumed to
-be complete. Thus inverse queries are primarily useful for database
-management and debugging activities. Inverse queries are NOT an
-acceptable method of mapping host addresses to host names; use the IN-
-ADDR.ARPA domain instead.
-
-Where possible, name servers should provide case-insensitive comparisons
-for inverse queries. Thus an inverse query asking for an MX RR of
-"Venera.isi.edu" should get the same response as a query for
-"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
-produce the same result as an inverse query for "IBM-pc unix". However,
-this cannot be guaranteed because name servers may possess RRs that
-contain character strings but the name server does not know that the
-data is character.
-
-When a name server processes an inverse query, it either returns:
-
- 1. zero, one, or multiple domain names for the specified
- resource as QNAMEs in the question section
-
-
-
-Mockapetris [Page 40]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- 2. an error code indicating that the name server doesn't support
- inverse mapping of the specified resource type.
-
-When the response to an inverse query contains one or more QNAMEs, the
-owner name and TTL of the RR in the answer section which defines the
-inverse query is modified to exactly match an RR found at the first
-QNAME.
-
-RRs returned in the inverse queries cannot be cached using the same
-mechanism as is used for the replies to standard queries. One reason
-for this is that a name might have multiple RRs of the same type, and
-only one would appear. For example, an inverse query for a single
-address of a multiply homed host might create the impression that only
-one address existed.
-
-6.4.2. Inverse query and response example The overall structure
-of an inverse query for retrieving the domain name that corresponds to
-Internet address 10.1.0.52 is shown below:
-
- +-----------------------------------------+
- Header | OPCODE=IQUERY, ID=997 |
- +-----------------------------------------+
- Question | <empty> |
- +-----------------------------------------+
- Answer | <anyname> A IN 10.1.0.52 |
- +-----------------------------------------+
- Authority | <empty> |
- +-----------------------------------------+
- Additional | <empty> |
- +-----------------------------------------+
-
-This query asks for a question whose answer is the Internet style
-address 10.1.0.52. Since the owner name is not known, any domain name
-can be used as a placeholder (and is ignored). A single octet of zero,
-signifying the root, is usually used because it minimizes the length of
-the message. The TTL of the RR is not significant. The response to
-this query might be:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris [Page 41]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- +-----------------------------------------+
- Header | OPCODE=RESPONSE, ID=997 |
- +-----------------------------------------+
- Question |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
- +-----------------------------------------+
- Answer | VENERA.ISI.EDU A IN 10.1.0.52 |
- +-----------------------------------------+
- Authority | <empty> |
- +-----------------------------------------+
- Additional | <empty> |
- +-----------------------------------------+
-
-Note that the QTYPE in a response to an inverse query is the same as the
-TYPE field in the answer section of the inverse query. Responses to
-inverse queries may contain multiple questions when the inverse is not
-unique. If the question section in the response is not empty, then the
-RR in the answer section is modified to correspond to be an exact copy
-of an RR at the first QNAME.
-
-6.4.3. Inverse query processing
-
-Name servers that support inverse queries can support these operations
-through exhaustive searches of their databases, but this becomes
-impractical as the size of the database increases. An alternative
-approach is to invert the database according to the search key.
-
-For name servers that support multiple zones and a large amount of data,
-the recommended approach is separate inversions for each zone. When a
-particular zone is changed during a refresh, only its inversions need to
-be redone.
-
-Support for transfer of this type of inversion may be included in future
-versions of the domain system, but is not supported in this version.
-
-6.5. Completion queries and responses
-
-The optional completion services described in RFC-882 and RFC-883 have
-been deleted. Redesigned services may become available in the future.
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris [Page 42]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-7. RESOLVER IMPLEMENTATION
-
-The top levels of the recommended resolver algorithm are discussed in
-[RFC-1034]. This section discusses implementation details assuming the
-database structure suggested in the name server implementation section
-of this memo.
-
-7.1. Transforming a user request into a query
-
-The first step a resolver takes is to transform the client's request,
-stated in a format suitable to the local OS, into a search specification
-for RRs at a specific name which match a specific QTYPE and QCLASS.
-Where possible, the QTYPE and QCLASS should correspond to a single type
-and a single class, because this makes the use of cached data much
-simpler. The reason for this is that the presence of data of one type
-in a cache doesn't confirm the existence or non-existence of data of
-other types, hence the only way to be sure is to consult an
-authoritative source. If QCLASS=* is used, then authoritative answers
-won't be available.
-
-Since a resolver must be able to multiplex multiple requests if it is to
-perform its function efficiently, each pending request is usually
-represented in some block of state information. This state block will
-typically contain:
-
- - A timestamp indicating the time the request began.
- The timestamp is used to decide whether RRs in the database
- can be used or are out of date. This timestamp uses the
- absolute time format previously discussed for RR storage in
- zones and caches. Note that when an RRs TTL indicates a
- relative time, the RR must be timely, since it is part of a
- zone. When the RR has an absolute time, it is part of a
- cache, and the TTL of the RR is compared against the timestamp
- for the start of the request.
-
- Note that using the timestamp is superior to using a current
- time, since it allows RRs with TTLs of zero to be entered in
- the cache in the usual manner, but still used by the current
- request, even after intervals of many seconds due to system
- load, query retransmission timeouts, etc.
-
- - Some sort of parameters to limit the amount of work which will
- be performed for this request.
-
- The amount of work which a resolver will do in response to a
- client request must be limited to guard against errors in the
- database, such as circular CNAME references, and operational
- problems, such as network partition which prevents the
-
-
-
-Mockapetris [Page 43]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- resolver from accessing the name servers it needs. While
- local limits on the number of times a resolver will retransmit
- a particular query to a particular name server address are
- essential, the resolver should have a global per-request
- counter to limit work on a single request. The counter should
- be set to some initial value and decremented whenever the
- resolver performs any action (retransmission timeout,
- retransmission, etc.) If the counter passes zero, the request
- is terminated with a temporary error.
-
- Note that if the resolver structure allows one request to
- start others in parallel, such as when the need to access a
- name server for one request causes a parallel resolve for the
- name server's addresses, the spawned request should be started
- with a lower counter. This prevents circular references in
- the database from starting a chain reaction of resolver
- activity.
-
- - The SLIST data structure discussed in [RFC-1034].
-
- This structure keeps track of the state of a request if it
- must wait for answers from foreign name servers.
-
-7.2. Sending the queries
-
-As described in [RFC-1034], the basic task of the resolver is to
-formulate a query which will answer the client's request and direct that
-query to name servers which can provide the information. The resolver
-will usually only have very strong hints about which servers to ask, in
-the form of NS RRs, and may have to revise the query, in response to
-CNAMEs, or revise the set of name servers the resolver is asking, in
-response to delegation responses which point the resolver to name
-servers closer to the desired information. In addition to the
-information requested by the client, the resolver may have to call upon
-its own services to determine the address of name servers it wishes to
-contact.
-
-In any case, the model used in this memo assumes that the resolver is
-multiplexing attention between multiple requests, some from the client,
-and some internally generated. Each request is represented by some
-state information, and the desired behavior is that the resolver
-transmit queries to name servers in a way that maximizes the probability
-that the request is answered, minimizes the time that the request takes,
-and avoids excessive transmissions. The key algorithm uses the state
-information of the request to select the next name server address to
-query, and also computes a timeout which will cause the next action
-should a response not arrive. The next action will usually be a
-transmission to some other server, but may be a temporary error to the
-
-
-
-Mockapetris [Page 44]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-client.
-
-The resolver always starts with a list of server names to query (SLIST).
-This list will be all NS RRs which correspond to the nearest ancestor
-zone that the resolver knows about. To avoid startup problems, the
-resolver should have a set of default servers which it will ask should
-it have no current NS RRs which are appropriate. The resolver then adds
-to SLIST all of the known addresses for the name servers, and may start
-parallel requests to acquire the addresses of the servers when the
-resolver has the name, but no addresses, for the name servers.
-
-To complete initialization of SLIST, the resolver attaches whatever
-history information it has to the each address in SLIST. This will
-usually consist of some sort of weighted averages for the response time
-of the address, and the batting average of the address (i.e., how often
-the address responded at all to the request). Note that this
-information should be kept on a per address basis, rather than on a per
-name server basis, because the response time and batting average of a
-particular server may vary considerably from address to address. Note
-also that this information is actually specific to a resolver address /
-server address pair, so a resolver with multiple addresses may wish to
-keep separate histories for each of its addresses. Part of this step
-must deal with addresses which have no such history; in this case an
-expected round trip time of 5-10 seconds should be the worst case, with
-lower estimates for the same local network, etc.
-
-Note that whenever a delegation is followed, the resolver algorithm
-reinitializes SLIST.
-
-The information establishes a partial ranking of the available name
-server addresses. Each time an address is chosen and the state should
-be altered to prevent its selection again until all other addresses have
-been tried. The timeout for each transmission should be 50-100% greater
-than the average predicted value to allow for variance in response.
-
-Some fine points:
-
- - The resolver may encounter a situation where no addresses are
- available for any of the name servers named in SLIST, and
- where the servers in the list are precisely those which would
- normally be used to look up their own addresses. This
- situation typically occurs when the glue address RRs have a
- smaller TTL than the NS RRs marking delegation, or when the
- resolver caches the result of a NS search. The resolver
- should detect this condition and restart the search at the
- next ancestor zone, or alternatively at the root.
-
-
-
-
-
-Mockapetris [Page 45]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- - If a resolver gets a server error or other bizarre response
- from a name server, it should remove it from SLIST, and may
- wish to schedule an immediate transmission to the next
- candidate server address.
-
-7.3. Processing responses
-
-The first step in processing arriving response datagrams is to parse the
-response. This procedure should include:
-
- - Check the header for reasonableness. Discard datagrams which
- are queries when responses are expected.
-
- - Parse the sections of the message, and insure that all RRs are
- correctly formatted.
-
- - As an optional step, check the TTLs of arriving data looking
- for RRs with excessively long TTLs. If a RR has an
- excessively long TTL, say greater than 1 week, either discard
- the whole response, or limit all TTLs in the response to 1
- week.
-
-The next step is to match the response to a current resolver request.
-The recommended strategy is to do a preliminary matching using the ID
-field in the domain header, and then to verify that the question section
-corresponds to the information currently desired. This requires that
-the transmission algorithm devote several bits of the domain ID field to
-a request identifier of some sort. This step has several fine points:
-
- - Some name servers send their responses from different
- addresses than the one used to receive the query. That is, a
- resolver cannot rely that a response will come from the same
- address which it sent the corresponding query to. This name
- server bug is typically encountered in UNIX systems.
-
- - If the resolver retransmits a particular request to a name
- server it should be able to use a response from any of the
- transmissions. However, if it is using the response to sample
- the round trip time to access the name server, it must be able
- to determine which transmission matches the response (and keep
- transmission times for each outgoing message), or only
- calculate round trip times based on initial transmissions.
-
- - A name server will occasionally not have a current copy of a
- zone which it should have according to some NS RRs. The
- resolver should simply remove the name server from the current
- SLIST, and continue.
-
-
-
-
-Mockapetris [Page 46]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-7.4. Using the cache
-
-In general, we expect a resolver to cache all data which it receives in
-responses since it may be useful in answering future client requests.
-However, there are several types of data which should not be cached:
-
- - When several RRs of the same type are available for a
- particular owner name, the resolver should either cache them
- all or none at all. When a response is truncated, and a
- resolver doesn't know whether it has a complete set, it should
- not cache a possibly partial set of RRs.
-
- - Cached data should never be used in preference to
- authoritative data, so if caching would cause this to happen
- the data should not be cached.
-
- - The results of an inverse query should not be cached.
-
- - The results of standard queries where the QNAME contains "*"
- labels if the data might be used to construct wildcards. The
- reason is that the cache does not necessarily contain existing
- RRs or zone boundary information which is necessary to
- restrict the application of the wildcard RRs.
-
- - RR data in responses of dubious reliability. When a resolver
- receives unsolicited responses or RR data other than that
- requested, it should discard it without caching it. The basic
- implication is that all sanity checks on a packet should be
- performed before any of it is cached.
-
-In a similar vein, when a resolver has a set of RRs for some name in a
-response, and wants to cache the RRs, it should check its cache for
-already existing RRs. Depending on the circumstances, either the data
-in the response or the cache is preferred, but the two should never be
-combined. If the data in the response is from authoritative data in the
-answer section, it is always preferred.
-
-8. MAIL SUPPORT
-
-The domain system defines a standard for mapping mailboxes into domain
-names, and two methods for using the mailbox information to derive mail
-routing information. The first method is called mail exchange binding
-and the other method is mailbox binding. The mailbox encoding standard
-and mail exchange binding are part of the DNS official protocol, and are
-the recommended method for mail routing in the Internet. Mailbox
-binding is an experimental feature which is still under development and
-subject to change.
-
-
-
-
-Mockapetris [Page 47]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-The mailbox encoding standard assumes a mailbox name of the form
-"<local-part>@<mail-domain>". While the syntax allowed in each of these
-sections varies substantially between the various mail internets, the
-preferred syntax for the ARPA Internet is given in [RFC-822].
-
-The DNS encodes the <local-part> as a single label, and encodes the
-<mail-domain> as a domain name. The single label from the <local-part>
-is prefaced to the domain name from <mail-domain> to form the domain
-name corresponding to the mailbox. Thus the mailbox HOSTMASTER@SRI-
-NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA. If the
-<local-part> contains dots or other special characters, its
-representation in a master file will require the use of backslash
-quoting to ensure that the domain name is properly encoded. For
-example, the mailbox Action.domains@ISI.EDU would be represented as
-Action\.domains.ISI.EDU.
-
-8.1. Mail exchange binding
-
-Mail exchange binding uses the <mail-domain> part of a mailbox
-specification to determine where mail should be sent. The <local-part>
-is not even consulted. [RFC-974] specifies this method in detail, and
-should be consulted before attempting to use mail exchange support.
-
-One of the advantages of this method is that it decouples mail
-destination naming from the hosts used to support mail service, at the
-cost of another layer of indirection in the lookup function. However,
-the addition layer should eliminate the need for complicated "%", "!",
-etc encodings in <local-part>.
-
-The essence of the method is that the <mail-domain> is used as a domain
-name to locate type MX RRs which list hosts willing to accept mail for
-<mail-domain>, together with preference values which rank the hosts
-according to an order specified by the administrators for <mail-domain>.
-
-In this memo, the <mail-domain> ISI.EDU is used in examples, together
-with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
-ISI.EDU. If a mailer had a message for Mockapetris@ISI.EDU, it would
-route it by looking up MX RRs for ISI.EDU. The MX RRs at ISI.EDU name
-VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
-addresses.
-
-8.2. Mailbox binding (Experimental)
-
-In mailbox binding, the mailer uses the entire mail destination
-specification to construct a domain name. The encoded domain name for
-the mailbox is used as the QNAME field in a QTYPE=MAILB query.
-
-Several outcomes are possible for this query:
-
-
-
-Mockapetris [Page 48]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- 1. The query can return a name error indicating that the mailbox
- does not exist as a domain name.
-
- In the long term, this would indicate that the specified
- mailbox doesn't exist. However, until the use of mailbox
- binding is universal, this error condition should be
- interpreted to mean that the organization identified by the
- global part does not support mailbox binding. The
- appropriate procedure is to revert to exchange binding at
- this point.
-
- 2. The query can return a Mail Rename (MR) RR.
-
- The MR RR carries new mailbox specification in its RDATA
- field. The mailer should replace the old mailbox with the
- new one and retry the operation.
-
- 3. The query can return a MB RR.
-
- The MB RR carries a domain name for a host in its RDATA
- field. The mailer should deliver the message to that host
- via whatever protocol is applicable, e.g., b,SMTP.
-
- 4. The query can return one or more Mail Group (MG) RRs.
-
- This condition means that the mailbox was actually a mailing
- list or mail group, rather than a single mailbox. Each MG RR
- has a RDATA field that identifies a mailbox that is a member
- of the group. The mailer should deliver a copy of the
- message to each member.
-
- 5. The query can return a MB RR as well as one or more MG RRs.
-
- This condition means the the mailbox was actually a mailing
- list. The mailer can either deliver the message to the host
- specified by the MB RR, which will in turn do the delivery to
- all members, or the mailer can use the MG RRs to do the
- expansion itself.
-
-In any of these cases, the response may include a Mail Information
-(MINFO) RR. This RR is usually associated with a mail group, but is
-legal with a MB. The MINFO RR identifies two mailboxes. One of these
-identifies a responsible person for the original mailbox name. This
-mailbox should be used for requests to be added to a mail group, etc.
-The second mailbox name in the MINFO RR identifies a mailbox that should
-receive error messages for mail failures. This is particularly
-appropriate for mailing lists when errors in member names should be
-reported to a person other than the one who sends a message to the list.
-
-
-
-Mockapetris [Page 49]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-New fields may be added to this RR in the future.
-
-
-9. REFERENCES and BIBLIOGRAPHY
-
-[Dyer 87] S. Dyer, F. Hsu, "Hesiod", Project Athena
- Technical Plan - Name Service, April 1987, version 1.9.
-
- Describes the fundamentals of the Hesiod name service.
-
-[IEN-116] J. Postel, "Internet Name Server", IEN-116,
- USC/Information Sciences Institute, August 1979.
-
- A name service obsoleted by the Domain Name System, but
- still in use.
-
-[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
- Communications of the ACM, October 1986, volume 29, number
- 10.
-
-[RFC-742] K. Harrenstien, "NAME/FINGER", RFC-742, Network
- Information Center, SRI International, December 1977.
-
-[RFC-768] J. Postel, "User Datagram Protocol", RFC-768,
- USC/Information Sciences Institute, August 1980.
-
-[RFC-793] J. Postel, "Transmission Control Protocol", RFC-793,
- USC/Information Sciences Institute, September 1981.
-
-[RFC-799] D. Mills, "Internet Name Domains", RFC-799, COMSAT,
- September 1981.
-
- Suggests introduction of a hierarchy in place of a flat
- name space for the Internet.
-
-[RFC-805] J. Postel, "Computer Mail Meeting Notes", RFC-805,
- USC/Information Sciences Institute, February 1982.
-
-[RFC-810] E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
- Internet Host Table Specification", RFC-810, Network
- Information Center, SRI International, March 1982.
-
- Obsolete. See RFC-952.
-
-[RFC-811] K. Harrenstien, V. White, and E. Feinler, "Hostnames
- Server", RFC-811, Network Information Center, SRI
- International, March 1982.
-
-
-
-
-Mockapetris [Page 50]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- Obsolete. See RFC-953.
-
-[RFC-812] K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
- Network Information Center, SRI International, March
- 1982.
-
-[RFC-819] Z. Su, and J. Postel, "The Domain Naming Convention for
- Internet User Applications", RFC-819, Network
- Information Center, SRI International, August 1982.
-
- Early thoughts on the design of the domain system.
- Current implementation is completely different.
-
-[RFC-821] J. Postel, "Simple Mail Transfer Protocol", RFC-821,
- USC/Information Sciences Institute, August 1980.
-
-[RFC-830] Z. Su, "A Distributed System for Internet Name Service",
- RFC-830, Network Information Center, SRI International,
- October 1982.
-
- Early thoughts on the design of the domain system.
- Current implementation is completely different.
-
-[RFC-882] P. Mockapetris, "Domain names - Concepts and
- Facilities," RFC-882, USC/Information Sciences
- Institute, November 1983.
-
- Superceeded by this memo.
-
-[RFC-883] P. Mockapetris, "Domain names - Implementation and
- Specification," RFC-883, USC/Information Sciences
- Institute, November 1983.
-
- Superceeded by this memo.
-
-[RFC-920] J. Postel and J. Reynolds, "Domain Requirements",
- RFC-920, USC/Information Sciences Institute,
- October 1984.
-
- Explains the naming scheme for top level domains.
-
-[RFC-952] K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
- Table Specification", RFC-952, SRI, October 1985.
-
- Specifies the format of HOSTS.TXT, the host/address
- table replaced by the DNS.
-
-
-
-
-
-Mockapetris [Page 51]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-[RFC-953] K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
- RFC-953, SRI, October 1985.
-
- This RFC contains the official specification of the
- hostname server protocol, which is obsoleted by the DNS.
- This TCP based protocol accesses information stored in
- the RFC-952 format, and is used to obtain copies of the
- host table.
-
-[RFC-973] P. Mockapetris, "Domain System Changes and
- Observations", RFC-973, USC/Information Sciences
- Institute, January 1986.
-
- Describes changes to RFC-882 and RFC-883 and reasons for
- them.
-
-[RFC-974] C. Partridge, "Mail routing and the domain system",
- RFC-974, CSNET CIC BBN Labs, January 1986.
-
- Describes the transition from HOSTS.TXT based mail
- addressing to the more powerful MX system used with the
- domain system.
-
-[RFC-1001] NetBIOS Working Group, "Protocol standard for a NetBIOS
- service on a TCP/UDP transport: Concepts and Methods",
- RFC-1001, March 1987.
-
- This RFC and RFC-1002 are a preliminary design for
- NETBIOS on top of TCP/IP which proposes to base NetBIOS
- name service on top of the DNS.
-
-[RFC-1002] NetBIOS Working Group, "Protocol standard for a NetBIOS
- service on a TCP/UDP transport: Detailed
- Specifications", RFC-1002, March 1987.
-
-[RFC-1010] J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
- USC/Information Sciences Institute, May 1987.
-
- Contains socket numbers and mnemonics for host names,
- operating systems, etc.
-
-[RFC-1031] W. Lazear, "MILNET Name Domain Transition", RFC-1031,
- November 1987.
-
- Describes a plan for converting the MILNET to the DNS.
-
-[RFC-1032] M. Stahl, "Establishing a Domain - Guidelines for
- Administrators", RFC-1032, November 1987.
-
-
-
-Mockapetris [Page 52]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- Describes the registration policies used by the NIC to
- administer the top level domains and delegate subzones.
-
-[RFC-1033] M. Lottor, "Domain Administrators Operations Guide",
- RFC-1033, November 1987.
-
- A cookbook for domain administrators.
-
-[Solomon 82] M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
- Name Server", Computer Networks, vol 6, nr 3, July 1982.
-
- Describes a name service for CSNET which is independent
- from the DNS and DNS use in the CSNET.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris [Page 53]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
-Index
-
- * 13
-
- ; 33, 35
-
- <character-string> 35
- <domain-name> 34
-
- @ 35
-
- \ 35
-
- A 12
-
- Byte order 8
-
- CH 13
- Character case 9
- CLASS 11
- CNAME 12
- Completion 42
- CS 13
-
- Hesiod 13
- HINFO 12
- HS 13
-
- IN 13
- IN-ADDR.ARPA domain 22
- Inverse queries 40
-
- Mailbox names 47
- MB 12
- MD 12
- MF 12
- MG 12
- MINFO 12
- MINIMUM 20
- MR 12
- MX 12
-
- NS 12
- NULL 12
-
- Port numbers 32
- Primary server 5
- PTR 12, 18
-
-
-
-Mockapetris [Page 54]
-\f
-RFC 1035 Domain Implementation and Specification November 1987
-
-
- QCLASS 13
- QTYPE 12
-
- RDATA 12
- RDLENGTH 11
-
- Secondary server 5
- SOA 12
- Stub resolvers 7
-
- TCP 32
- TXT 12
- TYPE 11
-
- UDP 32
-
- WKS 12
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Mockapetris [Page 55]
-\f
+++ /dev/null
-
-
-
-
-
-
-Network Working Group M. St. Johns
-Request for Comments: 1413 US Department of Defense
-Obsoletes: 931 February 1993
-
-
- Identification Protocol
-
-Status of this Memo
-
- This RFC specifies an IAB standards track protocol for the Internet
- community, and requests discussion and suggestions for improvements.
- Please refer to the current edition of the "IAB Official Protocol
- Standards" for the standardization state and status of this protocol.
- Distribution of this memo is unlimited.
-
-1. INTRODUCTION
-
- The Identification Protocol (a.k.a., "ident", a.k.a., "the Ident
- Protocol") provides a means to determine the identity of a user of a
- particular TCP connection. Given a TCP port number pair, it returns
- a character string which identifies the owner of that connection on
- the server's system.
-
- The Identification Protocol was formerly called the Authentication
- Server Protocol. It has been renamed to better reflect its function.
- This document is a product of the TCP Client Identity Protocol
- Working Group of the Internet Engineering Task Force (IETF).
-
-2. OVERVIEW
-
- This is a connection based application on TCP. A server listens for
- TCP connections on TCP port 113 (decimal). Once a connection is
- established, the server reads a line of data which specifies the
- connection of interest. If it exists, the system dependent user
- identifier of the connection of interest is sent as the reply. The
- server may then either shut the connection down or it may continue to
- read/respond to multiple queries.
-
- The server should close the connection down after a configurable
- amount of time with no queries - a 60-180 second idle timeout is
- recommended. The client may close the connection down at any time;
- however to allow for network delays the client should wait at least
- 30 seconds (or longer) after a query before abandoning the query and
- closing the connection.
-
-
-
-
-
-
-
-St. Johns [Page 1]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
-3. RESTRICTIONS
-
- Queries are permitted only for fully specified connections. The
- query contains the local/foreign port pair -- the local/foreign
- address pair used to fully specify the connection is taken from the
- local and foreign address of query connection. This means a user on
- address A may only query the server on address B about connections
- between A and B.
-
-4. QUERY/RESPONSE FORMAT
-
- The server accepts simple text query requests of the form:
-
- <port-on-server> , <port-on-client>
-
- where <port-on-server> is the TCP port (decimal) on the target (where
- the "ident" server is running) system, and <port-on-client> is the
- TCP port (decimal) on the source (client) system.
-
- N.B - If a client on host A wants to ask a server on host B about a
- connection specified locally (on the client's machine) as 23, 6191
- (an inbound TELNET connection), the client must actually ask about
- 6191, 23 - which is how the connection would be specified on host B.
-
- For example:
-
- 6191, 23
-
- The response is of the form
-
- <port-on-server> , <port-on-client> : <resp-type> : <add-info>
-
- where <port-on-server>,<port-on-client> are the same pair as the
- query, <resp-type> is a keyword identifying the type of response, and
- <add-info> is context dependent.
-
- The information returned is that associated with the fully specified
- TCP connection identified by <server-address>, <client-address>,
- <port-on-server>, <port-on-client>, where <server-address> and
- <client-address> are the local and foreign IP addresses of the
- querying connection -- i.e., the TCP connection to the Identification
- Protocol Server. (<port-on-server> and <port-on-client> are taken
- from the query.)
-
- For example:
-
- 6193, 23 : USERID : UNIX : stjohns
- 6195, 23 : ERROR : NO-USER
-
-
-
-St. Johns [Page 2]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
-5. RESPONSE TYPES
-
-A response can be one of two types:
-
-USERID
-
- In this case, <add-info> is a string consisting of an
- operating system name (with an optional character set
- identifier), followed by ":", followed by an
- identification string.
-
- The character set (if present) is separated from the
- operating system name by ",". The character set
- identifier is used to indicate the character set of the
- identification string. The character set identifier,
- if omitted, defaults to "US-ASCII" (see below).
-
- Permitted operating system names and character set
- names are specified in RFC 1340, "Assigned Numbers" or
- its successors.
-
- In addition to those operating system and character set
- names specified in "Assigned Numbers" there is one
- special case operating system identifier - "OTHER".
-
- Unless "OTHER" is specified as the operating system
- type, the server is expected to return the "normal"
- user identification of the owner of this connection.
- "Normal" in this context may be taken to mean a string
- of characters which uniquely identifies the connection
- owner such as a user identifier assigned by the system
- administrator and used by such user as a mail
- identifier, or as the "user" part of a user/password
- pair used to gain access to system resources. When an
- operating system is specified (e.g., anything but
- "OTHER"), the user identifier is expected to be in a
- more or less immediately useful form - e.g., something
- that could be used as an argument to "finger" or as a
- mail address.
-
- "OTHER" indicates the identifier is an unformatted
- character string consisting of printable characters in
- the specified character set. "OTHER" should be
- specified if the user identifier does not meet the
- constraints of the previous paragraph. Sending an
- encrypted audit token, or returning other non-userid
- information about a user (such as the real name and
- phone number of a user from a UNIX passwd file) are
-
-
-
-St. Johns [Page 3]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
- both examples of when "OTHER" should be used.
-
- Returned user identifiers are expected to be printable
- in the character set indicated.
-
- The identifier is an unformatted octet string - - all
- octets are permissible EXCEPT octal 000 (NUL), 012 (LF)
- and 015 (CR). N.B. - space characters (040) following the
- colon separator ARE part of the identifier string and
- may not be ignored. A response string is still
- terminated normally by a CR/LF. N.B. A string may be
- printable, but is not *necessarily* printable.
-
-ERROR
-
- For some reason the port owner could not be determined, <add-info>
- tells why. The following are the permitted values of <add-info> and
- their meanings:
-
- INVALID-PORT
-
- Either the local or foreign port was improperly
- specified. This should be returned if either or
- both of the port ids were out of range (TCP port
- numbers are from 1-65535), negative integers, reals or
- in any fashion not recognized as a non-negative
- integer.
-
- NO-USER
-
- The connection specified by the port pair is not
- currently in use or currently not owned by an
- identifiable entity.
-
- HIDDEN-USER
-
- The server was able to identify the user of this
- port, but the information was not returned at the
- request of the user.
-
- UNKNOWN-ERROR
-
- Can't determine connection owner; reason unknown.
- Any error not covered above should return this
- error code value. Optionally, this code MAY be
- returned in lieu of any other specific error code
- if, for example, the server desires to hide
- information implied by the return of that error
-
-
-
-St. Johns [Page 4]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
- code, or for any other reason. If a server
- implements such a feature, it MUST be configurable
- and it MUST default to returning the proper error
- message.
-
- Other values may eventually be specified and defined in future
- revisions to this document. If an implementer has a need to specify
- a non-standard error code, that code must begin with "X".
-
- In addition, the server is allowed to drop the query connection
- without responding. Any premature close (i.e., one where the client
- does not receive the EOL, whether graceful or an abort should be
- considered to have the same meaning as "ERROR : UNKNOWN-ERROR".
-
-FORMAL SYNTAX
-
- <request> ::= <port-pair> <EOL>
-
- <port-pair> ::= <integer> "," <integer>
-
- <reply> ::= <reply-text> <EOL>
-
- <EOL> ::= "015 012" ; CR-LF End of Line Indicator
-
- <reply-text> ::= <error-reply> | <ident-reply>
-
- <error-reply> ::= <port-pair> ":" "ERROR" ":" <error-type>
-
- <ident-reply> ::= <port-pair> ":" "USERID" ":" <opsys-field>
- ":" <user-id>
-
- <error-type> ::= "INVALID-PORT" | "NO-USER" | "UNKNOWN-ERROR"
- | "HIDDEN-USER" | <error-token>
-
- <opsys-field> ::= <opsys> [ "," <charset>]
-
- <opsys> ::= "OTHER" | "UNIX" | <token> ...etc.
- ; (See "Assigned Numbers")
-
- <charset> ::= "US-ASCII" | ...etc.
- ; (See "Assigned Numbers")
-
- <user-id> ::= <octet-string>
-
- <token> ::= 1*64<token-characters> ; 1-64 characters
-
- <error-token> ::= "X"1*63<token-characters>
- ; 2-64 chars beginning w/X
-
-
-
-St. Johns [Page 5]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
- <integer> ::= 1*5<digit> ; 1-5 digits.
-
- <digit> ::= "0" | "1" ... "8" | "9" ; 0-9
-
- <token-characters> ::=
- <Any of these ASCII characters: a-z, A-Z,
- - (dash), .!@#$%^&*()_=+.,<>/?"'~`{}[]; >
- ; upper and lowercase a-z plus
- ; printables minus the colon ":"
- ; character.
-
- <octet-string> ::= 1*512<octet-characters>
-
- <octet-characters> ::=
- <any octet from 00 to 377 (octal) except for
- ASCII NUL (000), CR (015) and LF (012)>
-
-Notes on Syntax:
-
- 1) To promote interoperability among variant
- implementations, with respect to white space the above
- syntax is understood to embody the "be conservative in
- what you send and be liberal in what you accept"
- philosophy. Clients and servers should not generate
- unnecessary white space (space and tab characters) but
- should accept white space anywhere except within a
- token. In parsing responses, white space may occur
- anywhere, except within a token. Specifically, any
- amount of white space is permitted at the beginning or
- end of a line both for queries and responses. This
- does not apply for responses that contain a user ID
- because everything after the colon after the operating
- system type until the terminating CR/LF is taken as
- part of the user ID. The terminating CR/LF is NOT
- considered part of the user ID.
-
- 2) The above notwithstanding, servers should restrict the
- amount of inter-token white space they send to the
- smallest amount reasonable or useful. Clients should
- feel free to abort a connection if they receive 1000
- characters without receiving an <EOL>.
-
- 3) The 512 character limit on user IDs and the 64
- character limit on tokens should be understood to mean
- as follows: a) No new token (i.e., OPSYS or ERROR-TYPE)
- token will be defined that has a length greater than 64
- and b) a server SHOULD NOT send more than 512 octets of
- user ID and a client MUST accept at least 512 octets of
-
-
-
-St. Johns [Page 6]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
- user ID. Because of this limitation, a server MUST
- return the most significant portion of the user ID in
- the first 512 octets.
-
- 4) The character sets and character set identifiers should
- map directly to those defined in or referenced by RFC 1340,
- "Assigned Numbers" or its successors. Character set
- identifiers only apply to the user identification field
- - all other fields will be defined in and must be sent
- as US-ASCII.
-
- 5) Although <user-id> is defined as an <octet-string>
- above, it must follow the format and character set
- constraints implied by the <opsys-field>; see the
- discussion above.
-
- 6) The character set provides context for the client to
- print or store the returned user identification string.
- If the client does not recognize or implement the
- returned character set, it should handle the returned
- identification string as OCTET, but should in addition
- store or report the character set. An OCTET string
- should be printed, stored or handled in hex notation
- (0-9a-f) in addition to any other representation the
- client implements - this provides a standard
- representation among differing implementations.
-
-6. Security Considerations
-
- The information returned by this protocol is at most as trustworthy
- as the host providing it OR the organization operating the host. For
- example, a PC in an open lab has few if any controls on it to prevent
- a user from having this protocol return any identifier the user
- wants. Likewise, if the host has been compromised the information
- returned may be completely erroneous and misleading.
-
- The Identification Protocol is not intended as an authorization or
- access control protocol. At best, it provides some additional
- auditing information with respect to TCP connections. At worst, it
- can provide misleading, incorrect, or maliciously incorrect
- information.
-
- The use of the information returned by this protocol for other than
- auditing is strongly discouraged. Specifically, using Identification
- Protocol information to make access control decisions - either as the
- primary method (i.e., no other checks) or as an adjunct to other
- methods may result in a weakening of normal host security.
-
-
-
-
-St. Johns [Page 7]
-\f
-RFC 1413 Identification Protocol February 1993
-
-
- An Identification server may reveal information about users,
- entities, objects or processes which might normally be considered
- private. An Identification server provides service which is a rough
- analog of the CallerID services provided by some phone companies and
- many of the same privacy considerations and arguments that apply to
- the CallerID service apply to Identification. If you wouldn't run a
- "finger" server due to privacy considerations you may not want to run
- this protocol.
-
-7. ACKNOWLEDGEMENTS
-
- Acknowledgement is given to Dan Bernstein who is primarily
- responsible for renewing interest in this protocol and for pointing
- out some annoying errors in RFC 931.
-
-References
-
- [1] St. Johns, M., "Authentication Server", RFC 931, TPSC, January
- 1985.
-
- [2] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1340,
- USC/Information Sciences Institute, July 1992.
-
-Author's Address
-
- Michael C. St. Johns
- DARPA/CSTO
- 3701 N. Fairfax Dr
- Arlington, VA 22203
-
- Phone: (703) 696-2271
- EMail: stjohns@DARPA.MIL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-St. Johns [Page 8]
-\f
\ No newline at end of file
+++ /dev/null
-
-
-
-
-
-
-Network Working Group J. Oikarinen
-Request for Comments: 1459 D. Reed
- May 1993
-
-
- Internet Relay Chat Protocol
-
-Status of This Memo
-
- This memo defines an Experimental Protocol for the Internet
- community. Discussion and suggestions for improvement are requested.
- Please refer to the current edition of the "IAB Official Protocol
- Standards" for the standardization state and status of this protocol.
- Distribution of this memo is unlimited.
-
-Abstract
-
- The IRC protocol was developed over the last 4 years since it was
- first implemented as a means for users on a BBS to chat amongst
- themselves. Now it supports a world-wide network of servers and
- clients, and is stringing to cope with growth. Over the past 2 years,
- the average number of users connected to the main IRC network has
- grown by a factor of 10.
-
- The IRC protocol is a text-based protocol, with the simplest client
- being any socket program capable of connecting to the server.
-
-Table of Contents
-
- 1. INTRODUCTION ............................................... 4
- 1.1 Servers ................................................ 4
- 1.2 Clients ................................................ 5
- 1.2.1 Operators .......................................... 5
- 1.3 Channels ................................................ 5
- 1.3.1 Channel Operators .................................... 6
- 2. THE IRC SPECIFICATION ....................................... 7
- 2.1 Overview ................................................ 7
- 2.2 Character codes ......................................... 7
- 2.3 Messages ................................................ 7
- 2.3.1 Message format in 'pseudo' BNF .................... 8
- 2.4 Numeric replies ......................................... 10
- 3. IRC Concepts ................................................ 10
- 3.1 One-to-one communication ................................ 10
- 3.2 One-to-many ............................................. 11
- 3.2.1 To a list .......................................... 11
- 3.2.2 To a group (channel) ............................... 11
- 3.2.3 To a host/server mask .............................. 12
- 3.3 One to all .............................................. 12
-
-
-
-Oikarinen & Reed [Page 1]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 3.3.1 Client to Client ................................... 12
- 3.3.2 Clients to Server .................................. 12
- 3.3.3 Server to Server ................................... 12
- 4. MESSAGE DETAILS ............................................. 13
- 4.1 Connection Registration ................................. 13
- 4.1.1 Password message ................................... 14
- 4.1.2 Nickname message ................................... 14
- 4.1.3 User message ....................................... 15
- 4.1.4 Server message ..................................... 16
- 4.1.5 Operator message ................................... 17
- 4.1.6 Quit message ....................................... 17
- 4.1.7 Server Quit message ................................ 18
- 4.2 Channel operations ...................................... 19
- 4.2.1 Join message ....................................... 19
- 4.2.2 Part message ....................................... 20
- 4.2.3 Mode message ....................................... 21
- 4.2.3.1 Channel modes ................................. 21
- 4.2.3.2 User modes .................................... 22
- 4.2.4 Topic message ...................................... 23
- 4.2.5 Names message ...................................... 24
- 4.2.6 List message ....................................... 24
- 4.2.7 Invite message ..................................... 25
- 4.2.8 Kick message ....................................... 25
- 4.3 Server queries and commands ............................. 26
- 4.3.1 Version message .................................... 26
- 4.3.2 Stats message ...................................... 27
- 4.3.3 Links message ...................................... 28
- 4.3.4 Time message ....................................... 29
- 4.3.5 Connect message .................................... 29
- 4.3.6 Trace message ...................................... 30
- 4.3.7 Admin message ...................................... 31
- 4.3.8 Info message ....................................... 31
- 4.4 Sending messages ........................................ 32
- 4.4.1 Private messages ................................... 32
- 4.4.2 Notice messages .................................... 33
- 4.5 User-based queries ...................................... 33
- 4.5.1 Who query .......................................... 33
- 4.5.2 Whois query ........................................ 34
- 4.5.3 Whowas message ..................................... 35
- 4.6 Miscellaneous messages .................................. 35
- 4.6.1 Kill message ....................................... 36
- 4.6.2 Ping message ....................................... 37
- 4.6.3 Pong message ....................................... 37
- 4.6.4 Error message ...................................... 38
- 5. OPTIONAL MESSAGES ........................................... 38
- 5.1 Away message ............................................ 38
- 5.2 Rehash command .......................................... 39
- 5.3 Restart command ......................................... 39
-
-
-
-Oikarinen & Reed [Page 2]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 5.4 Summon message .......................................... 40
- 5.5 Users message ........................................... 40
- 5.6 Operwall command ........................................ 41
- 5.7 Userhost message ........................................ 42
- 5.8 Ison message ............................................ 42
- 6. REPLIES ..................................................... 43
- 6.1 Error Replies ........................................... 43
- 6.2 Command responses ....................................... 48
- 6.3 Reserved numerics ....................................... 56
- 7. Client and server authentication ............................ 56
- 8. Current Implementations Details ............................. 56
- 8.1 Network protocol: TCP ................................... 57
- 8.1.1 Support of Unix sockets ............................ 57
- 8.2 Command Parsing ......................................... 57
- 8.3 Message delivery ........................................ 57
- 8.4 Connection 'Liveness' ................................... 58
- 8.5 Establishing a server-client connection ................. 58
- 8.6 Establishing a server-server connection ................. 58
- 8.6.1 State information exchange when connecting ......... 59
- 8.7 Terminating server-client connections ................... 59
- 8.8 Terminating server-server connections ................... 59
- 8.9 Tracking nickname changes ............................... 60
- 8.10 Flood control of clients ............................... 60
- 8.11 Non-blocking lookups ................................... 61
- 8.11.1 Hostname (DNS) lookups ............................ 61
- 8.11.2 Username (Ident) lookups .......................... 61
- 8.12 Configuration file ..................................... 61
- 8.12.1 Allowing clients to connect ....................... 62
- 8.12.2 Operators ......................................... 62
- 8.12.3 Allowing servers to connect ....................... 62
- 8.12.4 Administrivia ..................................... 63
- 8.13 Channel membership ..................................... 63
- 9. Current problems ............................................ 63
- 9.1 Scalability ............................................. 63
- 9.2 Labels .................................................. 63
- 9.2.1 Nicknames .......................................... 63
- 9.2.2 Channels ........................................... 64
- 9.2.3 Servers ............................................ 64
- 9.3 Algorithms .............................................. 64
- 10. Support and availability ................................... 64
- 11. Security Considerations .................................... 65
- 12. Authors' Addresses ......................................... 65
-
-
-
-
-
-
-
-
-
-Oikarinen & Reed [Page 3]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-1. INTRODUCTION
-
- The IRC (Internet Relay Chat) protocol has been designed over a
- number of years for use with text based conferencing. This document
- describes the current IRC protocol.
-
- The IRC protocol has been developed on systems using the TCP/IP
- network protocol, although there is no requirement that this remain
- the only sphere in which it operates.
-
- IRC itself is a teleconferencing system, which (through the use of
- the client-server model) is well-suited to running on many machines
- in a distributed fashion. A typical setup involves a single process
- (the server) forming a central point for clients (or other servers)
- to connect to, performing the required message delivery/multiplexing
- and other functions.
-
-1.1 Servers
-
- The server forms the backbone of IRC, providing a point to which
- clients may connect to to talk to each other, and a point for other
- servers to connect to, forming an IRC network. The only network
- configuration allowed for IRC servers is that of a spanning tree [see
- Fig. 1] where each server acts as a central node for the rest of the
- net it sees.
-
-
- [ Server 15 ] [ Server 13 ] [ Server 14]
- / \ /
- / \ /
- [ Server 11 ] ------ [ Server 1 ] [ Server 12]
- / \ /
- / \ /
- [ Server 2 ] [ Server 3 ]
- / \ \
- / \ \
- [ Server 4 ] [ Server 5 ] [ Server 6 ]
- / | \ /
- / | \ /
- / | \____ /
- / | \ /
- [ Server 7 ] [ Server 8 ] [ Server 9 ] [ Server 10 ]
-
- :
- [ etc. ]
- :
-
- [ Fig. 1. Format of IRC server network ]
-
-
-
-Oikarinen & Reed [Page 4]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-1.2 Clients
-
- A client is anything connecting to a server that is not another
- server. Each client is distinguished from other clients by a unique
- nickname having a maximum length of nine (9) characters. See the
- protocol grammar rules for what may and may not be used in a
- nickname. In addition to the nickname, all servers must have the
- following information about all clients: the real name of the host
- that the client is running on, the username of the client on that
- host, and the server to which the client is connected.
-
-1.2.1 Operators
-
- To allow a reasonable amount of order to be kept within the IRC
- network, a special class of clients (operators) is allowed to perform
- general maintenance functions on the network. Although the powers
- granted to an operator can be considered as 'dangerous', they are
- nonetheless required. Operators should be able to perform basic
- network tasks such as disconnecting and reconnecting servers as
- needed to prevent long-term use of bad network routing. In
- recognition of this need, the protocol discussed herein provides for
- operators only to be able to perform such functions. See sections
- 4.1.7 (SQUIT) and 4.3.5 (CONNECT).
-
- A more controversial power of operators is the ability to remove a
- user from the connected network by 'force', i.e. operators are able
- to close the connection between any client and server. The
- justification for this is delicate since its abuse is both
- destructive and annoying. For further details on this type of
- action, see section 4.6.1 (KILL).
-
-1.3 Channels
-
- A channel is a named group of one or more clients which will all
- receive messages addressed to that channel. The channel is created
- implicitly when the first client joins it, and the channel ceases to
- exist when the last client leaves it. While channel exists, any
- client can reference the channel using the name of the channel.
-
- Channels names are strings (beginning with a '&' or '#' character) of
- length up to 200 characters. Apart from the the requirement that the
- first character being either '&' or '#'; the only restriction on a
- channel name is that it may not contain any spaces (' '), a control G
- (^G or ASCII 7), or a comma (',' which is used as a list item
- separator by the protocol).
-
- There are two types of channels allowed by this protocol. One is a
- distributed channel which is known to all the servers that are
-
-
-
-Oikarinen & Reed [Page 5]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- connected to the network. These channels are marked by the first
- character being a only clients on the server where it exists may join
- it. These are distinguished by a leading '&' character. On top of
- these two types, there are the various channel modes available to
- alter the characteristics of individual channels. See section 4.2.3
- (MODE command) for more details on this.
-
- To create a new channel or become part of an existing channel, a user
- is required to JOIN the channel. If the channel doesn't exist prior
- to joining, the channel is created and the creating user becomes a
- channel operator. If the channel already exists, whether or not your
- request to JOIN that channel is honoured depends on the current modes
- of the channel. For example, if the channel is invite-only, (+i),
- then you may only join if invited. As part of the protocol, a user
- may be a part of several channels at once, but a limit of ten (10)
- channels is recommended as being ample for both experienced and
- novice users. See section 8.13 for more information on this.
-
- If the IRC network becomes disjoint because of a split between two
- servers, the channel on each side is only composed of those clients
- which are connected to servers on the respective sides of the split,
- possibly ceasing to exist on one side of the split. When the split
- is healed, the connecting servers announce to each other who they
- think is in each channel and the mode of that channel. If the
- channel exists on both sides, the JOINs and MODEs are interpreted in
- an inclusive manner so that both sides of the new connection will
- agree about which clients are in the channel and what modes the
- channel has.
-
-1.3.1 Channel Operators
-
- The channel operator (also referred to as a "chop" or "chanop") on a
- given channel is considered to 'own' that channel. In recognition of
- this status, channel operators are endowed with certain powers which
- enable them to keep control and some sort of sanity in their channel.
- As an owner of a channel, a channel operator is not required to have
- reasons for their actions, although if their actions are generally
- antisocial or otherwise abusive, it might be reasonable to ask an IRC
- operator to intervene, or for the usersjust leave and go elsewhere
- and form their own channel.
-
- The commands which may only be used by channel operators are:
-
- KICK - Eject a client from the channel
- MODE - Change the channel's mode
- INVITE - Invite a client to an invite-only channel (mode +i)
- TOPIC - Change the channel topic in a mode +t channel
-
-
-
-
-Oikarinen & Reed [Page 6]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- A channel operator is identified by the '@' symbol next to their
- nickname whenever it is associated with a channel (ie replies to the
- NAMES, WHO and WHOIS commands).
-
-2. The IRC Specification
-
-2.1 Overview
-
- The protocol as described herein is for use both with server to
- server and client to server connections. There are, however, more
- restrictions on client connections (which are considered to be
- untrustworthy) than on server connections.
-
-2.2 Character codes
-
- No specific character set is specified. The protocol is based on a a
- set of codes which are composed of eight (8) bits, making up an
- octet. Each message may be composed of any number of these octets;
- however, some octet values are used for control codes which act as
- message delimiters.
-
- Regardless of being an 8-bit protocol, the delimiters and keywords
- are such that protocol is mostly usable from USASCII terminal and a
- telnet connection.
-
- Because of IRC's scandanavian origin, the characters {}| are
- considered to be the lower case equivalents of the characters []\,
- respectively. This is a critical issue when determining the
- equivalence of two nicknames.
-
-2.3 Messages
-
- Servers and clients send eachother messages which may or may not
- generate a reply. If the message contains a valid command, as
- described in later sections, the client should expect a reply as
- specified but it is not advised to wait forever for the reply; client
- to server and server to server communication is essentially
- asynchronous in nature.
-
- Each IRC message may consist of up to three main parts: the prefix
- (optional), the command, and the command parameters (of which there
- may be up to 15). The prefix, command, and all parameters are
- separated by one (or more) ASCII space character(s) (0x20).
-
- The presence of a prefix is indicated with a single leading ASCII
- colon character (':', 0x3b), which must be the first character of the
- message itself. There must be no gap (whitespace) between the colon
- and the prefix. The prefix is used by servers to indicate the true
-
-
-
-Oikarinen & Reed [Page 7]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- origin of the message. If the prefix is missing from the message, it
- is assumed to have originated from the connection from which it was
- received. Clients should not use prefix when sending a message from
- themselves; if they use a prefix, the only valid prefix is the
- registered nickname associated with the client. If the source
- identified by the prefix cannot be found from the server's internal
- database, or if the source is registered from a different link than
- from which the message arrived, the server must ignore the message
- silently.
-
- The command must either be a valid IRC command or a three (3) digit
- number represented in ASCII text.
-
- IRC messages are always lines of characters terminated with a CR-LF
- (Carriage Return - Line Feed) pair, and these messages shall not
- exceed 512 characters in length, counting all characters including
- the trailing CR-LF. Thus, there are 510 characters maximum allowed
- for the command and its parameters. There is no provision for
- continuation message lines. See section 7 for more details about
- current implementations.
-
-2.3.1 Message format in 'pseudo' BNF
-
- The protocol messages must be extracted from the contiguous stream of
- octets. The current solution is to designate two characters, CR and
- LF, as message separators. Empty messages are silently ignored,
- which permits use of the sequence CR-LF between messages
- without extra problems.
-
- The extracted message is parsed into the components <prefix>,
- <command> and list of parameters matched either by <middle> or
- <trailing> components.
-
- The BNF representation for this is:
-
-
-<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
-<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
-<command> ::= <letter> { <letter> } | <number> <number> <number>
-<SPACE> ::= ' ' { ' ' }
-<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
-
-<middle> ::= <Any *non-empty* sequence of octets not including SPACE
- or NUL or CR or LF, the first of which may not be ':'>
-<trailing> ::= <Any, possibly *empty*, sequence of octets not including
- NUL or CR or LF>
-
-<crlf> ::= CR LF
-
-
-
-Oikarinen & Reed [Page 8]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-NOTES:
-
- 1) <SPACE> is consists only of SPACE character(s) (0x20).
- Specially notice that TABULATION, and all other control
- characters are considered NON-WHITE-SPACE.
-
- 2) After extracting the parameter list, all parameters are equal,
- whether matched by <middle> or <trailing>. <Trailing> is just
- a syntactic trick to allow SPACE within parameter.
-
- 3) The fact that CR and LF cannot appear in parameter strings is
- just artifact of the message framing. This might change later.
-
- 4) The NUL character is not special in message framing, and
- basically could end up inside a parameter, but as it would
- cause extra complexities in normal C string handling. Therefore
- NUL is not allowed within messages.
-
- 5) The last parameter may be an empty string.
-
- 6) Use of the extended prefix (['!' <user> ] ['@' <host> ]) must
- not be used in server to server communications and is only
- intended for server to client messages in order to provide
- clients with more useful information about who a message is
- from without the need for additional queries.
-
- Most protocol messages specify additional semantics and syntax for
- the extracted parameter strings dictated by their position in the
- list. For example, many server commands will assume that the first
- parameter after the command is the list of targets, which can be
- described with:
-
- <target> ::= <to> [ "," <target> ]
- <to> ::= <channel> | <user> '@' <servername> | <nick> | <mask>
- <channel> ::= ('#' | '&') <chstring>
- <servername> ::= <host>
- <host> ::= see RFC 952 [DNS:4] for details on allowed hostnames
- <nick> ::= <letter> { <letter> | <number> | <special> }
- <mask> ::= ('#' | '$') <chstring>
- <chstring> ::= <any 8bit code except SPACE, BELL, NUL, CR, LF and
- comma (',')>
-
- Other parameter syntaxes are:
-
- <user> ::= <nonwhite> { <nonwhite> }
- <letter> ::= 'a' ... 'z' | 'A' ... 'Z'
- <number> ::= '0' ... '9'
- <special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
-
-
-
-Oikarinen & Reed [Page 9]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- <nonwhite> ::= <any 8bit code except SPACE (0x20), NUL (0x0), CR
- (0xd), and LF (0xa)>
-
-2.4 Numeric replies
-
- Most of the messages sent to the server generate a reply of some
- sort. The most common reply is the numeric reply, used for both
- errors and normal replies. The numeric reply must be sent as one
- message consisting of the sender prefix, the three digit numeric, and
- the target of the reply. A numeric reply is not allowed to originate
- from a client; any such messages received by a server are silently
- dropped. In all other respects, a numeric reply is just like a normal
- message, except that the keyword is made up of 3 numeric digits
- rather than a string of letters. A list of different replies is
- supplied in section 6.
-
-3. IRC Concepts.
-
- This section is devoted to describing the actual concepts behind the
- organization of the IRC protocol and how the current
- implementations deliver different classes of messages.
-
-
-
- 1--\
- A D---4
- 2--/ \ /
- B----C
- / \
- 3 E
-
- Servers: A, B, C, D, E Clients: 1, 2, 3, 4
-
- [ Fig. 2. Sample small IRC network ]
-
-3.1 One-to-one communication
-
- Communication on a one-to-one basis is usually only performed by
- clients, since most server-server traffic is not a result of servers
- talking only to each other. To provide a secure means for clients to
- talk to each other, it is required that all servers be able to send a
- message in exactly one direction along the spanning tree in order to
- reach any client. The path of a message being delivered is the
- shortest path between any two points on the spanning tree.
-
- The following examples all refer to Figure 2 above.
-
-
-
-
-
-Oikarinen & Reed [Page 10]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-Example 1:
- A message between clients 1 and 2 is only seen by server A, which
- sends it straight to client 2.
-
-Example 2:
- A message between clients 1 and 3 is seen by servers A & B, and
- client 3. No other clients or servers are allowed see the message.
-
-Example 3:
- A message between clients 2 and 4 is seen by servers A, B, C & D
- and client 4 only.
-
-3.2 One-to-many
-
- The main goal of IRC is to provide a forum which allows easy and
- efficient conferencing (one to many conversations). IRC offers
- several means to achieve this, each serving its own purpose.
-
-3.2.1 To a list
-
- The least efficient style of one-to-many conversation is through
- clients talking to a 'list' of users. How this is done is almost
- self explanatory: the client gives a list of destinations to which
- the message is to be delivered and the server breaks it up and
- dispatches a separate copy of the message to each given destination.
- This isn't as efficient as using a group since the destination list
- is broken up and the dispatch sent without checking to make sure
- duplicates aren't sent down each path.
-
-3.2.2 To a group (channel)
-
- In IRC the channel has a role equivalent to that of the multicast
- group; their existence is dynamic (coming and going as people join
- and leave channels) and the actual conversation carried out on a
- channel is only sent to servers which are supporting users on a given
- channel. If there are multiple users on a server in the same
- channel, the message text is sent only once to that server and then
- sent to each client on the channel. This action is then repeated for
- each client-server combination until the original message has fanned
- out and reached each member of the channel.
-
- The following examples all refer to Figure 2.
-
-Example 4:
- Any channel with 1 client in it. Messages to the channel go to the
- server and then nowhere else.
-
-
-
-
-
-Oikarinen & Reed [Page 11]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-Example 5:
- 2 clients in a channel. All messages traverse a path as if they
- were private messages between the two clients outside a channel.
-
-Example 6:
- Clients 1, 2 and 3 in a channel. All messages to the channel are
- sent to all clients and only those servers which must be traversed
- by the message if it were a private message to a single client. If
- client 1 sends a message, it goes back to client 2 and then via
- server B to client 3.
-
-3.2.3 To a host/server mask
-
- To provide IRC operators with some mechanism to send messages to a
- large body of related users, host and server mask messages are
- provided. These messages are sent to users whose host or server
- information match that of the mask. The messages are only sent to
- locations where users are, in a fashion similar to that of channels.
-
-3.3 One-to-all
-
- The one-to-all type of message is better described as a broadcast
- message, sent to all clients or servers or both. On a large network
- of users and servers, a single message can result in a lot of traffic
- being sent over the network in an effort to reach all of the desired
- destinations.
-
- For some messages, there is no option but to broadcast it to all
- servers so that the state information held by each server is
- reasonably consistent between servers.
-
-3.3.1 Client-to-Client
-
- There is no class of message which, from a single message, results in
- a message being sent to every other client.
-
-3.3.2 Client-to-Server
-
- Most of the commands which result in a change of state information
- (such as channel membership, channel mode, user status, etc) must be
- sent to all servers by default, and this distribution may not be
- changed by the client.
-
-3.3.3 Server-to-Server.
-
- While most messages between servers are distributed to all 'other'
- servers, this is only required for any message that affects either a
- user, channel or server. Since these are the basic items found in
-
-
-
-Oikarinen & Reed [Page 12]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- IRC, nearly all messages originating from a server are broadcast to
- all other connected servers.
-
-4. Message details
-
- On the following pages are descriptions of each message recognized by
- the IRC server and client. All commands described in this section
- must be implemented by any server for this protocol.
-
- Where the reply ERR_NOSUCHSERVER is listed, it means that the
- <server> parameter could not be found. The server must not send any
- other replies after this for that command.
-
- The server to which a client is connected is required to parse the
- complete message, returning any appropriate errors. If the server
- encounters a fatal error while parsing a message, an error must be
- sent back to the client and the parsing terminated. A fatal error
- may be considered to be incorrect command, a destination which is
- otherwise unknown to the server (server, nick or channel names fit
- this category), not enough parameters or incorrect privileges.
-
- If a full set of parameters is presented, then each must be checked
- for validity and appropriate responses sent back to the client. In
- the case of messages which use parameter lists using the comma as an
- item separator, a reply must be sent for each item.
-
- In the examples below, some messages appear using the full format:
-
- :Name COMMAND parameter list
-
- Such examples represent a message from "Name" in transit between
- servers, where it is essential to include the name of the original
- sender of the message so remote servers may send back a reply along
- the correct path.
-
-4.1 Connection Registration
-
- The commands described here are used to register a connection with an
- IRC server as either a user or a server as well as correctly
- disconnect.
-
- A "PASS" command is not required for either client or server
- connection to be registered, but it must precede the server message
- or the latter of the NICK/USER combination. It is strongly
- recommended that all server connections have a password in order to
- give some level of security to the actual connections. The
- recommended order for a client to register is as follows:
-
-
-
-
-Oikarinen & Reed [Page 13]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 1. Pass message
- 2. Nick message
- 3. User message
-
-4.1.1 Password message
-
-
- Command: PASS
- Parameters: <password>
-
- The PASS command is used to set a 'connection password'. The
- password can and must be set before any attempt to register the
- connection is made. Currently this requires that clients send a PASS
- command before sending the NICK/USER combination and servers *must*
- send a PASS command before any SERVER command. The password supplied
- must match the one contained in the C/N lines (for servers) or I
- lines (for clients). It is possible to send multiple PASS commands
- before registering but only the last one sent is used for
- verification and it may not be changed once registered. Numeric
- Replies:
-
- ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
-
- Example:
-
- PASS secretpasswordhere
-
-4.1.2 Nick message
-
- Command: NICK
- Parameters: <nickname> [ <hopcount> ]
-
- NICK message is used to give user a nickname or change the previous
- one. The <hopcount> parameter is only used by servers to indicate
- how far away a nick is from its home server. A local connection has
- a hopcount of 0. If supplied by a client, it must be ignored.
-
- If a NICK message arrives at a server which already knows about an
- identical nickname for another client, a nickname collision occurs.
- As a result of a nickname collision, all instances of the nickname
- are removed from the server's database, and a KILL command is issued
- to remove the nickname from all other server's database. If the NICK
- message causing the collision was a nickname change, then the
- original (old) nick must be removed as well.
-
- If the server recieves an identical NICK from a client which is
- directly connected, it may issue an ERR_NICKCOLLISION to the local
- client, drop the NICK command, and not generate any kills.
-
-
-
-Oikarinen & Reed [Page 14]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- Numeric Replies:
-
- ERR_NONICKNAMEGIVEN ERR_ERRONEUSNICKNAME
- ERR_NICKNAMEINUSE ERR_NICKCOLLISION
-
- Example:
-
- NICK Wiz ; Introducing new nick "Wiz".
-
- :WiZ NICK Kilroy ; WiZ changed his nickname to Kilroy.
-
-4.1.3 User message
-
- Command: USER
- Parameters: <username> <hostname> <servername> <realname>
-
- The USER message is used at the beginning of connection to specify
- the username, hostname, servername and realname of s new user. It is
- also used in communication between servers to indicate new user
- arriving on IRC, since only after both USER and NICK have been
- received from a client does a user become registered.
-
- Between servers USER must to be prefixed with client's NICKname.
- Note that hostname and servername are normally ignored by the IRC
- server when the USER command comes from a directly connected client
- (for security reasons), but they are used in server to server
- communication. This means that a NICK must always be sent to a
- remote server when a new user is being introduced to the rest of the
- network before the accompanying USER is sent.
-
- It must be noted that realname parameter must be the last parameter,
- because it may contain space characters and must be prefixed with a
- colon (':') to make sure this is recognised as such.
-
- Since it is easy for a client to lie about its username by relying
- solely on the USER message, the use of an "Identity Server" is
- recommended. If the host which a user connects from has such a
- server enabled the username is set to that as in the reply from the
- "Identity Server".
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
-
- Examples:
-
-
- USER guest tolmoon tolsun :Ronnie Reagan
-
-
-
-Oikarinen & Reed [Page 15]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- ; User registering themselves with a
- username of "guest" and real name
- "Ronnie Reagan".
-
-
- :testnick USER guest tolmoon tolsun :Ronnie Reagan
- ; message between servers with the
- nickname for which the USER command
- belongs to
-
-4.1.4 Server message
-
- Command: SERVER
- Parameters: <servername> <hopcount> <info>
-
- The server message is used to tell a server that the other end of a
- new connection is a server. This message is also used to pass server
- data over whole net. When a new server is connected to net,
- information about it be broadcast to the whole network. <hopcount>
- is used to give all servers some internal information on how far away
- all servers are. With a full server list, it would be possible to
- construct a map of the entire server tree, but hostmasks prevent this
- from being done.
-
- The SERVER message must only be accepted from either (a) a connection
- which is yet to be registered and is attempting to register as a
- server, or (b) an existing connection to another server, in which
- case the SERVER message is introducing a new server behind that
- server.
-
- Most errors that occur with the receipt of a SERVER command result in
- the connection being terminated by the destination host (target
- SERVER). Error replies are usually sent using the "ERROR" command
- rather than the numeric since the ERROR command has several useful
- properties which make it useful here.
-
- If a SERVER message is parsed and attempts to introduce a server
- which is already known to the receiving server, the connection from
- which that message must be closed (following the correct procedures),
- since a duplicate route to a server has formed and the acyclic nature
- of the IRC tree broken.
-
- Numeric Replies:
-
- ERR_ALREADYREGISTRED
-
- Example:
-
-
-
-
-Oikarinen & Reed [Page 16]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-SERVER test.oulu.fi 1 :[tolsun.oulu.fi] Experimental server
- ; New server test.oulu.fi introducing
- itself and attempting to register. The
- name in []'s is the hostname for the
- host running test.oulu.fi.
-
-
-:tolsun.oulu.fi SERVER csd.bu.edu 5 :BU Central Server
- ; Server tolsun.oulu.fi is our uplink
- for csd.bu.edu which is 5 hops away.
-
-4.1.5 Oper
-
- Command: OPER
- Parameters: <user> <password>
-
- OPER message is used by a normal user to obtain operator privileges.
- The combination of <user> and <password> are required to gain
- Operator privileges.
-
- If the client sending the OPER command supplies the correct password
- for the given user, the server then informs the rest of the network
- of the new operator by issuing a "MODE +o" for the clients nickname.
-
- The OPER message is client-server only.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS RPL_YOUREOPER
- ERR_NOOPERHOST ERR_PASSWDMISMATCH
-
- Example:
-
- OPER foo bar ; Attempt to register as an operator
- using a username of "foo" and "bar" as
- the password.
-
-4.1.6 Quit
-
- Command: QUIT
- Parameters: [<Quit message>]
-
- A client session is ended with a quit message. The server must close
- the connection to a client which sends a QUIT message. If a "Quit
- Message" is given, this will be sent instead of the default message,
- the nickname.
-
- When netsplits (disconnecting of two servers) occur, the quit message
-
-
-
-Oikarinen & Reed [Page 17]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- is composed of the names of two servers involved, separated by a
- space. The first name is that of the server which is still connected
- and the second name is that of the server that has become
- disconnected.
-
- If, for some other reason, a client connection is closed without the
- client issuing a QUIT command (e.g. client dies and EOF occurs
- on socket), the server is required to fill in the quit message with
- some sort of message reflecting the nature of the event which
- caused it to happen.
-
- Numeric Replies:
-
- None.
-
- Examples:
-
- QUIT :Gone to have lunch ; Preferred message format.
-
-4.1.7 Server quit message
-
- Command: SQUIT
- Parameters: <server> <comment>
-
- The SQUIT message is needed to tell about quitting or dead servers.
- If a server wishes to break the connection to another server it must
- send a SQUIT message to the other server, using the the name of the
- other server as the server parameter, which then closes its
- connection to the quitting server.
-
- This command is also available operators to help keep a network of
- IRC servers connected in an orderly fashion. Operators may also
- issue an SQUIT message for a remote server connection. In this case,
- the SQUIT must be parsed by each server inbetween the operator and
- the remote server, updating the view of the network held by each
- server as explained below.
-
- The <comment> should be supplied by all operators who execute a SQUIT
- for a remote server (that is not connected to the server they are
- currently on) so that other operators are aware for the reason of
- this action. The <comment> is also filled in by servers which may
- place an error or similar message here.
-
- Both of the servers which are on either side of the connection being
- closed are required to to send out a SQUIT message (to all its other
- server connections) for all other servers which are considered to be
- behind that link.
-
-
-
-
-Oikarinen & Reed [Page 18]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- Similarly, a QUIT message must be sent to the other connected servers
- rest of the network on behalf of all clients behind that link. In
- addition to this, all channel members of a channel which lost a
- member due to the split must be sent a QUIT message.
-
- If a server connection is terminated prematurely (e.g. the server on
- the other end of the link died), the server which detects
- this disconnection is required to inform the rest of the network
- that the connection has closed and fill in the comment field
- with something appropriate.
-
- Numeric replies:
-
- ERR_NOPRIVILEGES ERR_NOSUCHSERVER
-
- Example:
-
- SQUIT tolsun.oulu.fi :Bad Link ? ; the server link tolson.oulu.fi has
- been terminated because of "Bad Link".
-
- :Trillian SQUIT cm22.eng.umd.edu :Server out of control
- ; message from Trillian to disconnect
- "cm22.eng.umd.edu" from the net
- because "Server out of control".
-
-4.2 Channel operations
-
- This group of messages is concerned with manipulating channels, their
- properties (channel modes), and their contents (typically clients).
- In implementing these, a number of race conditions are inevitable
- when clients at opposing ends of a network send commands which will
- ultimately clash. It is also required that servers keep a nickname
- history to ensure that wherever a <nick> parameter is given, the
- server check its history in case it has recently been changed.
-
-4.2.1 Join message
-
- Command: JOIN
- Parameters: <channel>{,<channel>} [<key>{,<key>}]
-
- The JOIN command is used by client to start listening a specific
- channel. Whether or not a client is allowed to join a channel is
- checked only by the server the client is connected to; all other
- servers automatically add the user to the channel when it is received
- from other servers. The conditions which affect this are as follows:
-
- 1. the user must be invited if the channel is invite-only;
-
-
-
-
-Oikarinen & Reed [Page 19]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 2. the user's nick/username/hostname must not match any
- active bans;
-
- 3. the correct key (password) must be given if it is set.
-
- These are discussed in more detail under the MODE command (see
- section 4.2.3 for more details).
-
- Once a user has joined a channel, they receive notice about all
- commands their server receives which affect the channel. This
- includes MODE, KICK, PART, QUIT and of course PRIVMSG/NOTICE. The
- JOIN command needs to be broadcast to all servers so that each server
- knows where to find the users who are on the channel. This allows
- optimal delivery of PRIVMSG/NOTICE messages to the channel.
-
- If a JOIN is successful, the user is then sent the channel's topic
- (using RPL_TOPIC) and the list of users who are on the channel (using
- RPL_NAMREPLY), which must include the user joining.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_BANNEDFROMCHAN
- ERR_INVITEONLYCHAN ERR_BADCHANNELKEY
- ERR_CHANNELISFULL ERR_BADCHANMASK
- ERR_NOSUCHCHANNEL ERR_TOOMANYCHANNELS
- RPL_TOPIC
-
- Examples:
-
- JOIN #foobar ; join channel #foobar.
-
- JOIN &foo fubar ; join channel &foo using key "fubar".
-
- JOIN #foo,&bar fubar ; join channel #foo using key "fubar"
- and &bar using no key.
-
- JOIN #foo,#bar fubar,foobar ; join channel #foo using key "fubar".
- and channel #bar using key "foobar".
-
- JOIN #foo,#bar ; join channels #foo and #bar.
-
- :WiZ JOIN #Twilight_zone ; JOIN message from WiZ
-
-4.2.2 Part message
-
- Command: PART
- Parameters: <channel>{,<channel>}
-
-
-
-
-Oikarinen & Reed [Page 20]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- The PART message causes the client sending the message to be removed
- from the list of active users for all given channels listed in the
- parameter string.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL
- ERR_NOTONCHANNEL
-
- Examples:
-
- PART #twilight_zone ; leave channel "#twilight_zone"
-
- PART #oz-ops,&group5 ; leave both channels "&group5" and
- "#oz-ops".
-
-4.2.3 Mode message
-
- Command: MODE
-
- The MODE command is a dual-purpose command in IRC. It allows both
- usernames and channels to have their mode changed. The rationale for
- this choice is that one day nicknames will be obsolete and the
- equivalent property will be the channel.
-
- When parsing MODE messages, it is recommended that the entire message
- be parsed first and then the changes which resulted then passed on.
-
-4.2.3.1 Channel modes
-
- Parameters: <channel> {[+|-]|o|p|s|i|t|n|b|v} [<limit>] [<user>]
- [<ban mask>]
-
- The MODE command is provided so that channel operators may change the
- characteristics of `their' channel. It is also required that servers
- be able to change channel modes so that channel operators may be
- created.
-
- The various modes available for channels are as follows:
-
- o - give/take channel operator privileges;
- p - private channel flag;
- s - secret channel flag;
- i - invite-only channel flag;
- t - topic settable by channel operator only flag;
- n - no messages to channel from clients on the outside;
- m - moderated channel;
- l - set the user limit to channel;
-
-
-
-Oikarinen & Reed [Page 21]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- b - set a ban mask to keep users out;
- v - give/take the ability to speak on a moderated channel;
- k - set a channel key (password).
-
- When using the 'o' and 'b' options, a restriction on a total of three
- per mode command has been imposed. That is, any combination of 'o'
- and
-
-4.2.3.2 User modes
-
- Parameters: <nickname> {[+|-]|i|w|s|o}
-
- The user MODEs are typically changes which affect either how the
- client is seen by others or what 'extra' messages the client is sent.
- A user MODE command may only be accepted if both the sender of the
- message and the nickname given as a parameter are both the same.
-
- The available modes are as follows:
-
- i - marks a users as invisible;
- s - marks a user for receipt of server notices;
- w - user receives wallops;
- o - operator flag.
-
- Additional modes may be available later on.
-
- If a user attempts to make themselves an operator using the "+o"
- flag, the attempt should be ignored. There is no restriction,
- however, on anyone `deopping' themselves (using "-o"). Numeric
- Replies:
-
- ERR_NEEDMOREPARAMS RPL_CHANNELMODEIS
- ERR_CHANOPRIVSNEEDED ERR_NOSUCHNICK
- ERR_NOTONCHANNEL ERR_KEYSET
- RPL_BANLIST RPL_ENDOFBANLIST
- ERR_UNKNOWNMODE ERR_NOSUCHCHANNEL
-
- ERR_USERSDONTMATCH RPL_UMODEIS
- ERR_UMODEUNKNOWNFLAG
-
- Examples:
-
- Use of Channel Modes:
-
-MODE #Finnish +im ; Makes #Finnish channel moderated and
- 'invite-only'.
-
-MODE #Finnish +o Kilroy ; Gives 'chanop' privileges to Kilroy on
-
-
-
-Oikarinen & Reed [Page 22]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- channel #Finnish.
-
-MODE #Finnish +v Wiz ; Allow WiZ to speak on #Finnish.
-
-MODE #Fins -s ; Removes 'secret' flag from channel
- #Fins.
-
-MODE #42 +k oulu ; Set the channel key to "oulu".
-
-MODE #eu-opers +l 10 ; Set the limit for the number of users
- on channel to 10.
-
-MODE &oulu +b ; list ban masks set for channel.
-
-MODE &oulu +b *!*@* ; prevent all users from joining.
-
-MODE &oulu +b *!*@*.edu ; prevent any user from a hostname
- matching *.edu from joining.
-
- Use of user Modes:
-
-:MODE WiZ -w ; turns reception of WALLOPS messages
- off for WiZ.
-
-:Angel MODE Angel +i ; Message from Angel to make themselves
- invisible.
-
-MODE WiZ -o ; WiZ 'deopping' (removing operator
- status). The plain reverse of this
- command ("MODE WiZ +o") must not be
- allowed from users since would bypass
- the OPER command.
-
-4.2.4 Topic message
-
- Command: TOPIC
- Parameters: <channel> [<topic>]
-
- The TOPIC message is used to change or view the topic of a channel.
- The topic for channel <channel> is returned if there is no <topic>
- given. If the <topic> parameter is present, the topic for that
- channel will be changed, if the channel modes permit this action.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_NOTONCHANNEL
- RPL_NOTOPIC RPL_TOPIC
- ERR_CHANOPRIVSNEEDED
-
-
-
-Oikarinen & Reed [Page 23]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- Examples:
-
- :Wiz TOPIC #test :New topic ;User Wiz setting the topic.
-
- TOPIC #test :another topic ;set the topic on #test to "another
- topic".
-
- TOPIC #test ; check the topic for #test.
-
-4.2.5 Names message
-
- Command: NAMES
- Parameters: [<channel>{,<channel>}]
-
- By using the NAMES command, a user can list all nicknames that are
- visible to them on any channel that they can see. Channel names
- which they can see are those which aren't private (+p) or secret (+s)
- or those which they are actually on. The <channel> parameter
- specifies which channel(s) to return information about if valid.
- There is no error reply for bad channel names.
-
- If no <channel> parameter is given, a list of all channels and their
- occupants is returned. At the end of this list, a list of users who
- are visible but either not on any channel or not on a visible channel
- are listed as being on `channel' "*".
-
- Numerics:
-
- RPL_NAMREPLY RPL_ENDOFNAMES
-
- Examples:
-
- NAMES #twilight_zone,#42 ; list visible users on #twilight_zone
- and #42 if the channels are visible to
- you.
-
- NAMES ; list all visible channels and users
-
-4.2.6 List message
-
- Command: LIST
- Parameters: [<channel>{,<channel>} [<server>]]
-
- The list message is used to list channels and their topics. If the
- <channel> parameter is used, only the status of that channel
- is displayed. Private channels are listed (without their
- topics) as channel "Prv" unless the client generating the query is
- actually on that channel. Likewise, secret channels are not listed
-
-
-
-Oikarinen & Reed [Page 24]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- at all unless the client is a member of the channel in question.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER RPL_LISTSTART
- RPL_LIST RPL_LISTEND
-
- Examples:
-
- LIST ; List all channels.
-
- LIST #twilight_zone,#42 ; List channels #twilight_zone and #42
-
-4.2.7 Invite message
-
- Command: INVITE
- Parameters: <nickname> <channel>
-
- The INVITE message is used to invite users to a channel. The
- parameter <nickname> is the nickname of the person to be invited to
- the target channel <channel>. There is no requirement that the
- channel the target user is being invited to must exist or be a valid
- channel. To invite a user to a channel which is invite only (MODE
- +i), the client sending the invite must be recognised as being a
- channel operator on the given channel.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_NOSUCHNICK
- ERR_NOTONCHANNEL ERR_USERONCHANNEL
- ERR_CHANOPRIVSNEEDED
- RPL_INVITING RPL_AWAY
-
- Examples:
-
- :Angel INVITE Wiz #Dust ; User Angel inviting WiZ to channel
- #Dust
-
- INVITE Wiz #Twilight_Zone ; Command to invite WiZ to
- #Twilight_zone
-
-4.2.8 Kick command
-
- Command: KICK
- Parameters: <channel> <user> [<comment>]
-
- The KICK command can be used to forcibly remove a user from a
- channel. It 'kicks them out' of the channel (forced PART).
-
-
-
-Oikarinen & Reed [Page 25]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- Only a channel operator may kick another user out of a channel.
- Each server that receives a KICK message checks that it is valid
- (ie the sender is actually a channel operator) before removing
- the victim from the channel.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL
- ERR_BADCHANMASK ERR_CHANOPRIVSNEEDED
- ERR_NOTONCHANNEL
-
- Examples:
-
-KICK &Melbourne Matthew ; Kick Matthew from &Melbourne
-
-KICK #Finnish John :Speaking English
- ; Kick John from #Finnish using
- "Speaking English" as the reason
- (comment).
-
-:WiZ KICK #Finnish John ; KICK message from WiZ to remove John
- from channel #Finnish
-
-NOTE:
- It is possible to extend the KICK command parameters to the
-following:
-
-<channel>{,<channel>} <user>{,<user>} [<comment>]
-
-4.3 Server queries and commands
-
- The server query group of commands has been designed to return
- information about any server which is connected to the network. All
- servers connected must respond to these queries and respond
- correctly. Any invalid response (or lack thereof) must be considered
- a sign of a broken server and it must be disconnected/disabled as
- soon as possible until the situation is remedied.
-
- In these queries, where a parameter appears as "<server>", it will
- usually mean it can be a nickname or a server or a wildcard name of
- some sort. For each parameter, however, only one query and set of
- replies is to be generated.
-
-4.3.1 Version message
-
- Command: VERSION
- Parameters: [<server>]
-
-
-
-
-Oikarinen & Reed [Page 26]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- The VERSION message is used to query the version of the server
- program. An optional parameter <server> is used to query the version
- of the server program which a client is not directly connected to.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER RPL_VERSION
-
- Examples:
-
- :Wiz VERSION *.se ; message from Wiz to check the version
- of a server matching "*.se"
-
- VERSION tolsun.oulu.fi ; check the version of server
- "tolsun.oulu.fi".
-
-4.3.2 Stats message
-
- Command: STATS
- Parameters: [<query> [<server>]]
-
- The stats message is used to query statistics of certain server. If
- <server> parameter is omitted, only the end of stats reply is sent
- back. The implementation of this command is highly dependent on the
- server which replies, although the server must be able to supply
- information as described by the queries below (or similar).
-
- A query may be given by any single letter which is only checked by
- the destination server (if given as the <server> parameter) and is
- otherwise passed on by intermediate servers, ignored and unaltered.
- The following queries are those found in the current IRC
- implementation and provide a large portion of the setup information
- for that server. Although these may not be supported in the same way
- by other versions, all servers should be able to supply a valid reply
- to a STATS query which is consistent with the reply formats currently
- used and the purpose of the query.
-
- The currently supported queries are:
-
- c - returns a list of servers which the server may connect
- to or allow connections from;
- h - returns a list of servers which are either forced to be
- treated as leaves or allowed to act as hubs;
- i - returns a list of hosts which the server allows a client
- to connect from;
- k - returns a list of banned username/hostname combinations
- for that server;
- l - returns a list of the server's connections, showing how
-
-
-
-Oikarinen & Reed [Page 27]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- long each connection has been established and the traffic
- over that connection in bytes and messages for each
- direction;
- m - returns a list of commands supported by the server and
- the usage count for each if the usage count is non zero;
- o - returns a list of hosts from which normal clients may
- become operators;
- y - show Y (Class) lines from server's configuration file;
- u - returns a string showing how long the server has been up.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
- RPL_STATSCLINE RPL_STATSNLINE
- RPL_STATSILINE RPL_STATSKLINE
- RPL_STATSQLINE RPL_STATSLLINE
- RPL_STATSLINKINFO RPL_STATSUPTIME
- RPL_STATSCOMMANDS RPL_STATSOLINE
- RPL_STATSHLINE RPL_ENDOFSTATS
-
- Examples:
-
-STATS m ; check the command usage for the server
- you are connected to
-
-:Wiz STATS c eff.org ; request by WiZ for C/N line
- information from server eff.org
-
-4.3.3 Links message
-
- Command: LINKS
- Parameters: [[<remote server>] <server mask>]
-
- With LINKS, a user can list all servers which are known by the server
- answering the query. The returned list of servers must match the
- mask, or if no mask is given, the full list is returned.
-
- If <remote server> is given in addition to <server mask>, the LINKS
- command is forwarded to the first server found that matches that name
- (if any), and that server is then required to answer the query.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
- RPL_LINKS RPL_ENDOFLINKS
-
- Examples:
-
-
-
-
-Oikarinen & Reed [Page 28]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-LINKS *.au ; list all servers which have a name
- that matches *.au;
-
-:WiZ LINKS *.bu.edu *.edu ; LINKS message from WiZ to the first
- server matching *.edu for a list of
- servers matching *.bu.edu.
-
-4.3.4 Time message
-
- Command: TIME
- Parameters: [<server>]
-
- The time message is used to query local time from the specified
- server. If the server parameter is not given, the server handling the
- command must reply to the query.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER RPL_TIME
-
- Examples:
-
- TIME tolsun.oulu.fi ; check the time on the server
- "tolson.oulu.fi"
-
- Angel TIME *.au ; user angel checking the time on a
- server matching "*.au"
-
-4.3.5 Connect message
-
- Command: CONNECT
- Parameters: <target server> [<port> [<remote server>]]
-
- The CONNECT command can be used to force a server to try to establish
- a new connection to another server immediately. CONNECT is a
- privileged command and is to be available only to IRC Operators. If
- a remote server is given then the CONNECT attempt is made by that
- server to <target server> and <port>.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER ERR_NOPRIVILEGES
- ERR_NEEDMOREPARAMS
-
- Examples:
-
-CONNECT tolsun.oulu.fi ; Attempt to connect a server to
- tolsun.oulu.fi
-
-
-
-Oikarinen & Reed [Page 29]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-:WiZ CONNECT eff.org 6667 csd.bu.edu
- ; CONNECT attempt by WiZ to get servers
- eff.org and csd.bu.edu connected on port
- 6667.
-
-4.3.6 Trace message
-
- Command: TRACE
- Parameters: [<server>]
-
- TRACE command is used to find the route to specific server. Each
- server that processes this message must tell the sender about it by
- sending a reply indicating it is a pass-through link, forming a chain
- of replies similar to that gained from using "traceroute". After
- sending this reply back, it must then send the TRACE message to the
- next server until given server is reached. If the <server> parameter
- is omitted, it is recommended that TRACE command send a message to
- the sender telling which servers the current server has direct
- connection to.
-
- If the destination given by "<server>" is an actual server, then the
- destination server is required to report all servers and users which
- are connected to it, although only operators are permitted to see
- users present. If the destination given by <server> is a nickname,
- they only a reply for that nickname is given.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
-
- If the TRACE message is destined for another server, all intermediate
- servers must return a RPL_TRACELINK reply to indicate that the TRACE
- passed through it and where its going next.
-
- RPL_TRACELINK
- A TRACE reply may be composed of any number of the following numeric
- replies.
-
- RPL_TRACECONNECTING RPL_TRACEHANDSHAKE
- RPL_TRACEUNKNOWN RPL_TRACEOPERATOR
- RPL_TRACEUSER RPL_TRACESERVER
- RPL_TRACESERVICE RPL_TRACENEWTYPE
- RPL_TRACECLASS
-
- Examples:
-
-TRACE *.oulu.fi ; TRACE to a server matching *.oulu.fi
-
-
-
-
-Oikarinen & Reed [Page 30]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-:WiZ TRACE AngelDust ; TRACE issued by WiZ to nick AngelDust
-
-4.3.7 Admin command
-
- Command: ADMIN
- Parameters: [<server>]
-
- The admin message is used to find the name of the administrator of
- the given server, or current server if <server> parameter is omitted.
- Each server must have the ability to forward ADMIN messages to other
- servers.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
- RPL_ADMINME RPL_ADMINLOC1
- RPL_ADMINLOC2 RPL_ADMINEMAIL
-
- Examples:
-
- ADMIN tolsun.oulu.fi ; request an ADMIN reply from
- tolsun.oulu.fi
-
- :WiZ ADMIN *.edu ; ADMIN request from WiZ for first
- server found to match *.edu.
-
-4.3.8 Info command
-
- Command: INFO
- Parameters: [<server>]
-
- The INFO command is required to return information which describes
- the server: its version, when it was compiled, the patchlevel, when
- it was started, and any other miscellaneous information which may be
- considered to be relevant.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
- RPL_INFO RPL_ENDOFINFO
-
- Examples:
-
- INFO csd.bu.edu ; request an INFO reply from
- csd.bu.edu
-
- :Avalon INFO *.fi ; INFO request from Avalon for first
- server found to match *.fi.
-
-
-
-Oikarinen & Reed [Page 31]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- INFO Angel ; request info from the server that
- Angel is connected to.
-
-4.4 Sending messages
-
- The main purpose of the IRC protocol is to provide a base for clients
- to communicate with each other. PRIVMSG and NOTICE are the only
- messages available which actually perform delivery of a text message
- from one client to another - the rest just make it possible and try
- to ensure it happens in a reliable and structured manner.
-
-4.4.1 Private messages
-
- Command: PRIVMSG
- Parameters: <receiver>{,<receiver>} <text to be sent>
-
- PRIVMSG is used to send private messages between users. <receiver>
- is the nickname of the receiver of the message. <receiver> can also
- be a list of names or channels separated with commas.
-
- The <receiver> parameter may also me a host mask (#mask) or server
- mask ($mask). In both cases the server will only send the PRIVMSG
- to those who have a server or host matching the mask. The mask must
- have at least 1 (one) "." in it and no wildcards following the
- last ".". This requirement exists to prevent people sending messages
- to "#*" or "$*", which would broadcast to all users; from
- experience, this is abused more than used responsibly and properly.
- Wildcards are the '*' and '?' characters. This extension to
- the PRIVMSG command is only available to Operators.
-
- Numeric Replies:
-
- ERR_NORECIPIENT ERR_NOTEXTTOSEND
- ERR_CANNOTSENDTOCHAN ERR_NOTOPLEVEL
- ERR_WILDTOPLEVEL ERR_TOOMANYTARGETS
- ERR_NOSUCHNICK
- RPL_AWAY
-
- Examples:
-
-:Angel PRIVMSG Wiz :Hello are you receiving this message ?
- ; Message from Angel to Wiz.
-
-PRIVMSG Angel :yes I'm receiving it !receiving it !'u>(768u+1n) .br ;
- Message to Angel.
-
-PRIVMSG jto@tolsun.oulu.fi :Hello !
- ; Message to a client on server
-
-
-
-Oikarinen & Reed [Page 32]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- tolsun.oulu.fi with username of "jto".
-
-PRIVMSG $*.fi :Server tolsun.oulu.fi rebooting.
- ; Message to everyone on a server which
- has a name matching *.fi.
-
-PRIVMSG #*.edu :NSFNet is undergoing work, expect interruptions
- ; Message to all users who come from a
- host which has a name matching *.edu.
-
-4.4.2 Notice
-
- Command: NOTICE
- Parameters: <nickname> <text>
-
- The NOTICE message is used similarly to PRIVMSG. The difference
- between NOTICE and PRIVMSG is that automatic replies must never be
- sent in response to a NOTICE message. This rule applies to servers
- too - they must not send any error reply back to the client on
- receipt of a notice. The object of this rule is to avoid loops
- between a client automatically sending something in response to
- something it received. This is typically used by automatons (clients
- with either an AI or other interactive program controlling their
- actions) which are always seen to be replying lest they end up in a
- loop with another automaton.
-
- See PRIVMSG for more details on replies and examples.
-
-4.5 User based queries
-
- User queries are a group of commands which are primarily concerned
- with finding details on a particular user or group users. When using
- wildcards with any of these commands, if they match, they will only
- return information on users who are 'visible' to you. The visibility
- of a user is determined as a combination of the user's mode and the
- common set of channels you are both on.
-
-4.5.1 Who query
-
- Command: WHO
- Parameters: [<name> [<o>]]
-
- The WHO message is used by a client to generate a query which returns
- a list of information which 'matches' the <name> parameter given by
- the client. In the absence of the <name> parameter, all visible
- (users who aren't invisible (user mode +i) and who don't have a
- common channel with the requesting client) are listed. The same
- result can be achieved by using a <name> of "0" or any wildcard which
-
-
-
-Oikarinen & Reed [Page 33]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- will end up matching every entry possible.
-
- The <name> passed to WHO is matched against users' host, server, real
- name and nickname if the channel <name> cannot be found.
-
- If the "o" parameter is passed only operators are returned according
- to the name mask supplied.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER
- RPL_WHOREPLY RPL_ENDOFWHO
-
- Examples:
-
- WHO *.fi ; List all users who match against
- "*.fi".
-
- WHO jto* o ; List all users with a match against
- "jto*" if they are an operator.
-
-4.5.2 Whois query
-
- Command: WHOIS
- Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
-
- This message is used to query information about particular user. The
- server will answer this message with several numeric messages
- indicating different statuses of each user which matches the nickmask
- (if you are entitled to see them). If no wildcard is present in the
- <nickmask>, any information about that nick which you are allowed to
- see is presented. A comma (',') separated list of nicknames may be
- given.
-
- The latter version sends the query to a specific server. It is
- useful if you want to know how long the user in question has been
- idle as only local server (ie. the server the user is directly
- connected to) knows that information, while everything else is
- globally known.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER ERR_NONICKNAMEGIVEN
- RPL_WHOISUSER RPL_WHOISCHANNELS
- RPL_WHOISCHANNELS RPL_WHOISSERVER
- RPL_AWAY RPL_WHOISOPERATOR
- RPL_WHOISIDLE ERR_NOSUCHNICK
- RPL_ENDOFWHOIS
-
-
-
-Oikarinen & Reed [Page 34]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- Examples:
-
- WHOIS wiz ; return available user information
- about nick WiZ
-
- WHOIS eff.org trillian ; ask server eff.org for user
- information about trillian
-
-4.5.3 Whowas
-
- Command: WHOWAS
- Parameters: <nickname> [<count> [<server>]]
-
- Whowas asks for information about a nickname which no longer exists.
- This may either be due to a nickname change or the user leaving IRC.
- In response to this query, the server searches through its nickname
- history, looking for any nicks which are lexically the same (no wild
- card matching here). The history is searched backward, returning the
- most recent entry first. If there are multiple entries, up to
- <count> replies will be returned (or all of them if no <count>
- parameter is given). If a non-positive number is passed as being
- <count>, then a full search is done.
-
- Numeric Replies:
-
- ERR_NONICKNAMEGIVEN ERR_WASNOSUCHNICK
- RPL_WHOWASUSER RPL_WHOISSERVER
- RPL_ENDOFWHOWAS
-
- Examples:
-
- WHOWAS Wiz ; return all information in the nick
- history about nick "WiZ";
-
- WHOWAS Mermaid 9 ; return at most, the 9 most recent
- entries in the nick history for
- "Mermaid";
-
- WHOWAS Trillian 1 *.edu ; return the most recent history for
- "Trillian" from the first server found
- to match "*.edu".
-
-4.6 Miscellaneous messages
-
- Messages in this category do not fit into any of the above categories
- but are nonetheless still a part of and required by the protocol.
-
-
-
-
-
-Oikarinen & Reed [Page 35]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-4.6.1 Kill message
-
- Command: KILL
- Parameters: <nickname> <comment>
-
- The KILL message is used to cause a client-server connection to be
- closed by the server which has the actual connection. KILL is used
- by servers when they encounter a duplicate entry in the list of valid
- nicknames and is used to remove both entries. It is also available
- to operators.
-
- Clients which have automatic reconnect algorithms effectively make
- this command useless since the disconnection is only brief. It does
- however break the flow of data and can be used to stop large amounts
- of being abused, any user may elect to receive KILL messages
- generated for others to keep an 'eye' on would be trouble spots.
-
- In an arena where nicknames are required to be globally unique at all
- times, KILL messages are sent whenever 'duplicates' are detected
- (that is an attempt to register two users with the same nickname) in
- the hope that both of them will disappear and only 1 reappear.
-
- The comment given must reflect the actual reason for the KILL. For
- server-generated KILLs it usually is made up of details concerning
- the origins of the two conflicting nicknames. For users it is left
- up to them to provide an adequate reason to satisfy others who see
- it. To prevent/discourage fake KILLs from being generated to hide
- the identify of the KILLer, the comment also shows a 'kill-path'
- which is updated by each server it passes through, each prepending
- its name to the path.
-
- Numeric Replies:
-
- ERR_NOPRIVILEGES ERR_NEEDMOREPARAMS
- ERR_NOSUCHNICK ERR_CANTKILLSERVER
-
-
- KILL David (csd.bu.edu <- tolsun.oulu.fi)
- ; Nickname collision between csd.bu.edu
- and tolson.oulu.fi
-
-
- NOTE:
- It is recommended that only Operators be allowed to kill other users
- with KILL message. In an ideal world not even operators would need
- to do this and it would be left to servers to deal with.
-
-
-
-
-
-Oikarinen & Reed [Page 36]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-4.6.2 Ping message
-
- Command: PING
- Parameters: <server1> [<server2>]
-
- The PING message is used to test the presence of an active client at
- the other end of the connection. A PING message is sent at regular
- intervals if no other activity detected coming from a connection. If
- a connection fails to respond to a PING command within a set amount
- of time, that connection is closed.
-
- Any client which receives a PING message must respond to <server1>
- (server which sent the PING message out) as quickly as possible with
- an appropriate PONG message to indicate it is still there and alive.
- Servers should not respond to PING commands but rely on PINGs from
- the other end of the connection to indicate the connection is alive.
- If the <server2> parameter is specified, the PING message gets
- forwarded there.
-
- Numeric Replies:
-
- ERR_NOORIGIN ERR_NOSUCHSERVER
-
- Examples:
-
- PING tolsun.oulu.fi ; server sending a PING message to
- another server to indicate it is still
- alive.
-
- PING WiZ ; PING message being sent to nick WiZ
-
-4.6.3 Pong message
-
- Command: PONG
- Parameters: <daemon> [<daemon2>]
-
- PONG message is a reply to ping message. If parameter <daemon2> is
- given this message must be forwarded to given daemon. The <daemon>
- parameter is the name of the daemon who has responded to PING message
- and generated this message.
-
- Numeric Replies:
-
- ERR_NOORIGIN ERR_NOSUCHSERVER
-
- Examples:
-
- PONG csd.bu.edu tolsun.oulu.fi ; PONG message from csd.bu.edu to
-
-
-
-Oikarinen & Reed [Page 37]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- tolsun.oulu.fi
-
-4.6.4 Error
-
- Command: ERROR
- Parameters: <error message>
-
- The ERROR command is for use by servers when reporting a serious or
- fatal error to its operators. It may also be sent from one server to
- another but must not be accepted from any normal unknown clients.
-
- An ERROR message is for use for reporting errors which occur with a
- server-to-server link only. An ERROR message is sent to the server
- at the other end (which sends it to all of its connected operators)
- and to all operators currently connected. It is not to be passed
- onto any other servers by a server if it is received from a server.
-
- When a server sends a received ERROR message to its operators, the
- message should be encapsulated inside a NOTICE message, indicating
- that the client was not responsible for the error.
-
- Numerics:
-
- None.
-
- Examples:
-
- ERROR :Server *.fi already exists; ERROR message to the other server
- which caused this error.
-
- NOTICE WiZ :ERROR from csd.bu.edu -- Server *.fi already exists
- ; Same ERROR message as above but sent
- to user WiZ on the other server.
-
-5. OPTIONALS
-
- This section describes OPTIONAL messages. They are not required in a
- working server implementation of the protocol described herein. In
- the absence of the option, an error reply message must be generated
- or an unknown command error. If the message is destined for another
- server to answer then it must be passed on (elementary parsing
- required) The allocated numerics for this are listed with the
- messages below.
-
-5.1 Away
-
- Command: AWAY
- Parameters: [message]
-
-
-
-Oikarinen & Reed [Page 38]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- With the AWAY message, clients can set an automatic reply string for
- any PRIVMSG commands directed at them (not to a channel they are on).
- The automatic reply is sent by the server to client sending the
- PRIVMSG command. The only replying server is the one to which the
- sending client is connected to.
-
- The AWAY message is used either with one parameter (to set an AWAY
- message) or with no parameters (to remove the AWAY message).
-
- Numeric Replies:
-
- RPL_UNAWAY RPL_NOWAWAY
-
- Examples:
-
- AWAY :Gone to lunch. Back in 5 ; set away message to "Gone to lunch.
- Back in 5".
-
- :WiZ AWAY ; unmark WiZ as being away.
-
-
-5.2 Rehash message
-
- Command: REHASH
- Parameters: None
-
- The rehash message can be used by the operator to force the server to
- re-read and process its configuration file.
-
- Numeric Replies:
-
- RPL_REHASHING ERR_NOPRIVILEGES
-
-Examples:
-
-REHASH ; message from client with operator
- status to server asking it to reread its
- configuration file.
-
-5.3 Restart message
-
- Command: RESTART
- Parameters: None
-
- The restart message can only be used by an operator to force a server
- restart itself. This message is optional since it may be viewed as a
- risk to allow arbitrary people to connect to a server as an operator
- and execute this command, causing (at least) a disruption to service.
-
-
-
-Oikarinen & Reed [Page 39]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- The RESTART command must always be fully processed by the server to
- which the sending client is connected and not be passed onto other
- connected servers.
-
- Numeric Replies:
-
- ERR_NOPRIVILEGES
-
- Examples:
-
- RESTART ; no parameters required.
-
-5.4 Summon message
-
- Command: SUMMON
- Parameters: <user> [<server>]
-
- The SUMMON command can be used to give users who are on a host
- running an IRC server a message asking them to please join IRC. This
- message is only sent if the target server (a) has SUMMON enabled, (b)
- the user is logged in and (c) the server process can write to the
- user's tty (or similar).
-
- If no <server> parameter is given it tries to summon <user> from the
- server the client is connected to is assumed as the target.
-
- If summon is not enabled in a server, it must return the
- ERR_SUMMONDISABLED numeric and pass the summon message onwards.
-
- Numeric Replies:
-
- ERR_NORECIPIENT ERR_FILEERROR
- ERR_NOLOGIN ERR_NOSUCHSERVER
- RPL_SUMMONING
-
- Examples:
-
- SUMMON jto ; summon user jto on the server's host
-
- SUMMON jto tolsun.oulu.fi ; summon user jto on the host which a
- server named "tolsun.oulu.fi" is
- running.
-
-
-5.5 Users
-
- Command: USERS
- Parameters: [<server>]
-
-
-
-Oikarinen & Reed [Page 40]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- The USERS command returns a list of users logged into the server in a
- similar format to who(1), rusers(1) and finger(1). Some people
- may disable this command on their server for security related
- reasons. If disabled, the correct numeric must be returned to
- indicate this.
-
- Numeric Replies:
-
- ERR_NOSUCHSERVER ERR_FILEERROR
- RPL_USERSSTART RPL_USERS
- RPL_NOUSERS RPL_ENDOFUSERS
- ERR_USERSDISABLED
-
- Disabled Reply:
-
- ERR_USERSDISABLED
-
- Examples:
-
-USERS eff.org ; request a list of users logged in on
- server eff.org
-
-:John USERS tolsun.oulu.fi ; request from John for a list of users
- logged in on server tolsun.oulu.fi
-
-5.6 Operwall message
-
- Command: WALLOPS
- Parameters: Text to be sent to all operators currently online
-
- Sends a message to all operators currently online. After
- implementing WALLOPS as a user command it was found that it was
- often and commonly abused as a means of sending a message to a lot
- of people (much similar to WALL). Due to this it is recommended
- that the current implementation of WALLOPS be used as an
- example by allowing and recognising only servers as the senders of
- WALLOPS.
-
- Numeric Replies:
-
- ERR_NEEDMOREPARAMS
-
- Examples:
-
- :csd.bu.edu WALLOPS :Connect '*.uiuc.edu 6667' from Joshua; WALLOPS
- message from csd.bu.edu announcing a
- CONNECT message it received and acted
- upon from Joshua.
-
-
-
-Oikarinen & Reed [Page 41]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-5.7 Userhost message
-
- Command: USERHOST
- Parameters: <nickname>{<space><nickname>}
-
- The USERHOST command takes a list of up to 5 nicknames, each
- separated by a space character and returns a list of information
- about each nickname that it found. The returned list has each reply
- separated by a space.
-
- Numeric Replies:
-
- RPL_USERHOST ERR_NEEDMOREPARAMS
-
- Examples:
-
- USERHOST Wiz Michael Marty p ;USERHOST request for information on
- nicks "Wiz", "Michael", "Marty" and "p"
-
-5.8 Ison message
-
- Command: ISON
- Parameters: <nickname>{<space><nickname>}
-
- The ISON command was implemented to provide a quick and efficient
- means to get a response about whether a given nickname was currently
- on IRC. ISON only takes one (1) parameter: a space-separated list of
- nicks. For each nickname in the list that is present, the server
- adds that to its reply string. Thus the reply string may return
- empty (none of the given nicks are present), an exact copy of the
- parameter string (all of them present) or as any other subset of the
- set of nicks given in the parameter. The only limit on the number
- of nicks that may be checked is that the combined length must not be
- too large as to cause the server to chop it off so it fits in 512
- characters.
-
- ISON is only be processed by the server local to the client sending
- the command and thus not passed onto other servers for further
- processing.
-
- Numeric Replies:
-
- RPL_ISON ERR_NEEDMOREPARAMS
-
- Examples:
-
- ISON phone trillian WiZ jarlek Avalon Angel Monstah
- ; Sample ISON request for 7 nicks.
-
-
-
-Oikarinen & Reed [Page 42]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-6. REPLIES
-
- The following is a list of numeric replies which are generated in
- response to the commands given above. Each numeric is given with its
- number, name and reply string.
-
-6.1 Error Replies.
-
- 401 ERR_NOSUCHNICK
- "<nickname> :No such nick/channel"
-
- - Used to indicate the nickname parameter supplied to a
- command is currently unused.
-
- 402 ERR_NOSUCHSERVER
- "<server name> :No such server"
-
- - Used to indicate the server name given currently
- doesn't exist.
-
- 403 ERR_NOSUCHCHANNEL
- "<channel name> :No such channel"
-
- - Used to indicate the given channel name is invalid.
-
- 404 ERR_CANNOTSENDTOCHAN
- "<channel name> :Cannot send to channel"
-
- - Sent to a user who is either (a) not on a channel
- which is mode +n or (b) not a chanop (or mode +v) on
- a channel which has mode +m set and is trying to send
- a PRIVMSG message to that channel.
-
- 405 ERR_TOOMANYCHANNELS
- "<channel name> :You have joined too many \
- channels"
- - Sent to a user when they have joined the maximum
- number of allowed channels and they try to join
- another channel.
-
- 406 ERR_WASNOSUCHNICK
- "<nickname> :There was no such nickname"
-
- - Returned by WHOWAS to indicate there is no history
- information for that nickname.
-
- 407 ERR_TOOMANYTARGETS
- "<target> :Duplicate recipients. No message \
-
-
-
-Oikarinen & Reed [Page 43]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- delivered"
-
- - Returned to a client which is attempting to send a
- PRIVMSG/NOTICE using the user@host destination format
- and for a user@host which has several occurrences.
-
- 409 ERR_NOORIGIN
- ":No origin specified"
-
- - PING or PONG message missing the originator parameter
- which is required since these commands must work
- without valid prefixes.
-
- 411 ERR_NORECIPIENT
- ":No recipient given (<command>)"
- 412 ERR_NOTEXTTOSEND
- ":No text to send"
- 413 ERR_NOTOPLEVEL
- "<mask> :No toplevel domain specified"
- 414 ERR_WILDTOPLEVEL
- "<mask> :Wildcard in toplevel domain"
-
- - 412 - 414 are returned by PRIVMSG to indicate that
- the message wasn't delivered for some reason.
- ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
- are returned when an invalid use of
- "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
-
- 421 ERR_UNKNOWNCOMMAND
- "<command> :Unknown command"
-
- - Returned to a registered client to indicate that the
- command sent is unknown by the server.
-
- 422 ERR_NOMOTD
- ":MOTD File is missing"
-
- - Server's MOTD file could not be opened by the server.
-
- 423 ERR_NOADMININFO
- "<server> :No administrative info available"
-
- - Returned by a server in response to an ADMIN message
- when there is an error in finding the appropriate
- information.
-
- 424 ERR_FILEERROR
- ":File error doing <file op> on <file>"
-
-
-
-Oikarinen & Reed [Page 44]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- - Generic error message used to report a failed file
- operation during the processing of a message.
-
- 431 ERR_NONICKNAMEGIVEN
- ":No nickname given"
-
- - Returned when a nickname parameter expected for a
- command and isn't found.
-
- 432 ERR_ERRONEUSNICKNAME
- "<nick> :Erroneus nickname"
-
- - Returned after receiving a NICK message which contains
- characters which do not fall in the defined set. See
- section x.x.x for details on valid nicknames.
-
- 433 ERR_NICKNAMEINUSE
- "<nick> :Nickname is already in use"
-
- - Returned when a NICK message is processed that results
- in an attempt to change to a currently existing
- nickname.
-
- 436 ERR_NICKCOLLISION
- "<nick> :Nickname collision KILL"
-
- - Returned by a server to a client when it detects a
- nickname collision (registered of a NICK that
- already exists by another server).
-
- 441 ERR_USERNOTINCHANNEL
- "<nick> <channel> :They aren't on that channel"
-
- - Returned by the server to indicate that the target
- user of the command is not on the given channel.
-
- 442 ERR_NOTONCHANNEL
- "<channel> :You're not on that channel"
-
- - Returned by the server whenever a client tries to
- perform a channel effecting command for which the
- client isn't a member.
-
- 443 ERR_USERONCHANNEL
- "<user> <channel> :is already on channel"
-
- - Returned when a client tries to invite a user to a
- channel they are already on.
-
-
-
-Oikarinen & Reed [Page 45]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 444 ERR_NOLOGIN
- "<user> :User not logged in"
-
- - Returned by the summon after a SUMMON command for a
- user was unable to be performed since they were not
- logged in.
-
- 445 ERR_SUMMONDISABLED
- ":SUMMON has been disabled"
-
- - Returned as a response to the SUMMON command. Must be
- returned by any server which does not implement it.
-
- 446 ERR_USERSDISABLED
- ":USERS has been disabled"
-
- - Returned as a response to the USERS command. Must be
- returned by any server which does not implement it.
-
- 451 ERR_NOTREGISTERED
- ":You have not registered"
-
- - Returned by the server to indicate that the client
- must be registered before the server will allow it
- to be parsed in detail.
-
- 461 ERR_NEEDMOREPARAMS
- "<command> :Not enough parameters"
-
- - Returned by the server by numerous commands to
- indicate to the client that it didn't supply enough
- parameters.
-
- 462 ERR_ALREADYREGISTRED
- ":You may not reregister"
-
- - Returned by the server to any link which tries to
- change part of the registered details (such as
- password or user details from second USER message).
-
-
- 463 ERR_NOPERMFORHOST
- ":Your host isn't among the privileged"
-
- - Returned to a client which attempts to register with
- a server which does not been setup to allow
- connections from the host the attempted connection
- is tried.
-
-
-
-Oikarinen & Reed [Page 46]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 464 ERR_PASSWDMISMATCH
- ":Password incorrect"
-
- - Returned to indicate a failed attempt at registering
- a connection for which a password was required and
- was either not given or incorrect.
-
- 465 ERR_YOUREBANNEDCREEP
- ":You are banned from this server"
-
- - Returned after an attempt to connect and register
- yourself with a server which has been setup to
- explicitly deny connections to you.
-
- 467 ERR_KEYSET
- "<channel> :Channel key already set"
- 471 ERR_CHANNELISFULL
- "<channel> :Cannot join channel (+l)"
- 472 ERR_UNKNOWNMODE
- "<char> :is unknown mode char to me"
- 473 ERR_INVITEONLYCHAN
- "<channel> :Cannot join channel (+i)"
- 474 ERR_BANNEDFROMCHAN
- "<channel> :Cannot join channel (+b)"
- 475 ERR_BADCHANNELKEY
- "<channel> :Cannot join channel (+k)"
- 481 ERR_NOPRIVILEGES
- ":Permission Denied- You're not an IRC operator"
-
- - Any command requiring operator privileges to operate
- must return this error to indicate the attempt was
- unsuccessful.
-
- 482 ERR_CHANOPRIVSNEEDED
- "<channel> :You're not channel operator"
-
- - Any command requiring 'chanop' privileges (such as
- MODE messages) must return this error if the client
- making the attempt is not a chanop on the specified
- channel.
-
- 483 ERR_CANTKILLSERVER
- ":You cant kill a server!"
-
- - Any attempts to use the KILL command on a server
- are to be refused and this error returned directly
- to the client.
-
-
-
-
-Oikarinen & Reed [Page 47]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 491 ERR_NOOPERHOST
- ":No O-lines for your host"
-
- - If a client sends an OPER message and the server has
- not been configured to allow connections from the
- client's host as an operator, this error must be
- returned.
-
- 501 ERR_UMODEUNKNOWNFLAG
- ":Unknown MODE flag"
-
- - Returned by the server to indicate that a MODE
- message was sent with a nickname parameter and that
- the a mode flag sent was not recognized.
-
- 502 ERR_USERSDONTMATCH
- ":Cant change mode for other users"
-
- - Error sent to any user trying to view or change the
- user mode for a user other than themselves.
-
-6.2 Command responses.
-
- 300 RPL_NONE
- Dummy reply number. Not used.
-
- 302 RPL_USERHOST
- ":[<reply>{<space><reply>}]"
-
- - Reply format used by USERHOST to list replies to
- the query list. The reply string is composed as
- follows:
-
- <reply> ::= <nick>['*'] '=' <'+'|'-'><hostname>
-
- The '*' indicates whether the client has registered
- as an Operator. The '-' or '+' characters represent
- whether the client has set an AWAY message or not
- respectively.
-
- 303 RPL_ISON
- ":[<nick> {<space><nick>}]"
-
- - Reply format used by ISON to list replies to the
- query list.
-
- 301 RPL_AWAY
- "<nick> :<away message>"
-
-
-
-Oikarinen & Reed [Page 48]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- 305 RPL_UNAWAY
- ":You are no longer marked as being away"
- 306 RPL_NOWAWAY
- ":You have been marked as being away"
-
- - These replies are used with the AWAY command (if
- allowed). RPL_AWAY is sent to any client sending a
- PRIVMSG to a client which is away. RPL_AWAY is only
- sent by the server to which the client is connected.
- Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
- client removes and sets an AWAY message.
-
- 311 RPL_WHOISUSER
- "<nick> <user> <host> * :<real name>"
- 312 RPL_WHOISSERVER
- "<nick> <server> :<server info>"
- 313 RPL_WHOISOPERATOR
- "<nick> :is an IRC operator"
- 317 RPL_WHOISIDLE
- "<nick> <integer> :seconds idle"
- 318 RPL_ENDOFWHOIS
- "<nick> :End of /WHOIS list"
- 319 RPL_WHOISCHANNELS
- "<nick> :{[@|+]<channel><space>}"
-
- - Replies 311 - 313, 317 - 319 are all replies
- generated in response to a WHOIS message. Given that
- there are enough parameters present, the answering
- server must either formulate a reply out of the above
- numerics (if the query nick is found) or return an
- error reply. The '*' in RPL_WHOISUSER is there as
- the literal character and not as a wild card. For
- each reply set, only RPL_WHOISCHANNELS may appear
- more than once (for long lists of channel names).
- The '@' and '+' characters next to the channel name
- indicate whether a client is a channel operator or
- has been granted permission to speak on a moderated
- channel. The RPL_ENDOFWHOIS reply is used to mark
- the end of processing a WHOIS message.
-
- 314 RPL_WHOWASUSER
- "<nick> <user> <host> * :<real name>"
- 369 RPL_ENDOFWHOWAS
- "<nick> :End of WHOWAS"
-
- - When replying to a WHOWAS message, a server must use
- the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
- ERR_WASNOSUCHNICK for each nickname in the presented
-
-
-
-Oikarinen & Reed [Page 49]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- list. At the end of all reply batches, there must
- be RPL_ENDOFWHOWAS (even if there was only one reply
- and it was an error).
-
- 321 RPL_LISTSTART
- "Channel :Users Name"
- 322 RPL_LIST
- "<channel> <# visible> :<topic>"
- 323 RPL_LISTEND
- ":End of /LIST"
-
- - Replies RPL_LISTSTART, RPL_LIST, RPL_LISTEND mark
- the start, actual replies with data and end of the
- server's response to a LIST command. If there are
- no channels available to return, only the start
- and end reply must be sent.
-
- 324 RPL_CHANNELMODEIS
- "<channel> <mode> <mode params>"
-
- 331 RPL_NOTOPIC
- "<channel> :No topic is set"
- 332 RPL_TOPIC
- "<channel> :<topic>"
-
- - When sending a TOPIC message to determine the
- channel topic, one of two replies is sent. If
- the topic is set, RPL_TOPIC is sent back else
- RPL_NOTOPIC.
-
- 341 RPL_INVITING
- "<channel> <nick>"
-
- - Returned by the server to indicate that the
- attempted INVITE message was successful and is
- being passed onto the end client.
-
- 342 RPL_SUMMONING
- "<user> :Summoning user to IRC"
-
- - Returned by a server answering a SUMMON message to
- indicate that it is summoning that user.
-
- 351 RPL_VERSION
- "<version>.<debuglevel> <server> :<comments>"
-
- - Reply by the server showing its version details.
- The <version> is the version of the software being
-
-
-
-Oikarinen & Reed [Page 50]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- used (including any patchlevel revisions) and the
- <debuglevel> is used to indicate if the server is
- running in "debug mode".
-
- The "comments" field may contain any comments about
- the version or further version details.
-
- 352 RPL_WHOREPLY
- "<channel> <user> <host> <server> <nick> \
- <H|G>[*][@|+] :<hopcount> <real name>"
- 315 RPL_ENDOFWHO
- "<name> :End of /WHO list"
-
- - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
- to answer a WHO message. The RPL_WHOREPLY is only
- sent if there is an appropriate match to the WHO
- query. If there is a list of parameters supplied
- with a WHO message, a RPL_ENDOFWHO must be sent
- after processing each list item with <name> being
- the item.
-
- 353 RPL_NAMREPLY
- "<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]"
- 366 RPL_ENDOFNAMES
- "<channel> :End of /NAMES list"
-
- - To reply to a NAMES message, a reply pair consisting
- of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
- server back to the client. If there is no channel
- found as in the query, then only RPL_ENDOFNAMES is
- returned. The exception to this is when a NAMES
- message is sent with no parameters and all visible
- channels and contents are sent back in a series of
- RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
- the end.
-
- 364 RPL_LINKS
- "<mask> <server> :<hopcount> <server info>"
- 365 RPL_ENDOFLINKS
- "<mask> :End of /LINKS list"
-
- - In replying to the LINKS message, a server must send
- replies back using the RPL_LINKS numeric and mark the
- end of the list using an RPL_ENDOFLINKS reply.
-
- 367 RPL_BANLIST
- "<channel> <banid>"
- 368 RPL_ENDOFBANLIST
-
-
-
-Oikarinen & Reed [Page 51]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- "<channel> :End of channel ban list"
-
- - When listing the active 'bans' for a given channel,
- a server is required to send the list back using the
- RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate
- RPL_BANLIST is sent for each active banid. After the
- banids have been listed (or if none present) a
- RPL_ENDOFBANLIST must be sent.
-
- 371 RPL_INFO
- ":<string>"
- 374 RPL_ENDOFINFO
- ":End of /INFO list"
-
- - A server responding to an INFO message is required to
- send all its 'info' in a series of RPL_INFO messages
- with a RPL_ENDOFINFO reply to indicate the end of the
- replies.
-
- 375 RPL_MOTDSTART
- ":- <server> Message of the day - "
- 372 RPL_MOTD
- ":- <text>"
- 376 RPL_ENDOFMOTD
- ":End of /MOTD command"
-
- - When responding to the MOTD message and the MOTD file
- is found, the file is displayed line by line, with
- each line no longer than 80 characters, using
- RPL_MOTD format replies. These should be surrounded
- by a RPL_MOTDSTART (before the RPL_MOTDs) and an
- RPL_ENDOFMOTD (after).
-
- 381 RPL_YOUREOPER
- ":You are now an IRC operator"
-
- - RPL_YOUREOPER is sent back to a client which has
- just successfully issued an OPER message and gained
- operator status.
-
- 382 RPL_REHASHING
- "<config file> :Rehashing"
-
- - If the REHASH option is used and an operator sends
- a REHASH message, an RPL_REHASHING is sent back to
- the operator.
-
- 391 RPL_TIME
-
-
-
-Oikarinen & Reed [Page 52]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- "<server> :<string showing server's local time>"
-
- - When replying to the TIME message, a server must send
- the reply using the RPL_TIME format above. The string
- showing the time need only contain the correct day and
- time there. There is no further requirement for the
- time string.
-
- 392 RPL_USERSSTART
- ":UserID Terminal Host"
- 393 RPL_USERS
- ":%-8s %-9s %-8s"
- 394 RPL_ENDOFUSERS
- ":End of users"
- 395 RPL_NOUSERS
- ":Nobody logged in"
-
- - If the USERS message is handled by a server, the
- replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
- RPL_NOUSERS are used. RPL_USERSSTART must be sent
- first, following by either a sequence of RPL_USERS
- or a single RPL_NOUSER. Following this is
- RPL_ENDOFUSERS.
-
- 200 RPL_TRACELINK
- "Link <version & debug level> <destination> \
- <next server>"
- 201 RPL_TRACECONNECTING
- "Try. <class> <server>"
- 202 RPL_TRACEHANDSHAKE
- "H.S. <class> <server>"
- 203 RPL_TRACEUNKNOWN
- "???? <class> [<client IP address in dot form>]"
- 204 RPL_TRACEOPERATOR
- "Oper <class> <nick>"
- 205 RPL_TRACEUSER
- "User <class> <nick>"
- 206 RPL_TRACESERVER
- "Serv <class> <int>S <int>C <server> \
- <nick!user|*!*>@<host|server>"
- 208 RPL_TRACENEWTYPE
- "<newtype> 0 <client name>"
- 261 RPL_TRACELOG
- "File <logfile> <debug level>"
-
- - The RPL_TRACE* are all returned by the server in
- response to the TRACE message. How many are
- returned is dependent on the the TRACE message and
-
-
-
-Oikarinen & Reed [Page 53]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- whether it was sent by an operator or not. There
- is no predefined order for which occurs first.
- Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and
- RPL_TRACEHANDSHAKE are all used for connections
- which have not been fully established and are either
- unknown, still attempting to connect or in the
- process of completing the 'server handshake'.
- RPL_TRACELINK is sent by any server which handles
- a TRACE message and has to pass it on to another
- server. The list of RPL_TRACELINKs sent in
- response to a TRACE command traversing the IRC
- network should reflect the actual connectivity of
- the servers themselves along that path.
- RPL_TRACENEWTYPE is to be used for any connection
- which does not fit in the other categories but is
- being displayed anyway.
-
- 211 RPL_STATSLINKINFO
- "<linkname> <sendq> <sent messages> \
- <sent bytes> <received messages> \
- <received bytes> <time open>"
- 212 RPL_STATSCOMMANDS
- "<command> <count>"
- 213 RPL_STATSCLINE
- "C <host> * <name> <port> <class>"
- 214 RPL_STATSNLINE
- "N <host> * <name> <port> <class>"
- 215 RPL_STATSILINE
- "I <host> * <host> <port> <class>"
- 216 RPL_STATSKLINE
- "K <host> * <username> <port> <class>"
- 218 RPL_STATSYLINE
- "Y <class> <ping frequency> <connect \
- frequency> <max sendq>"
- 219 RPL_ENDOFSTATS
- "<stats letter> :End of /STATS report"
- 241 RPL_STATSLLINE
- "L <hostmask> * <servername> <maxdepth>"
- 242 RPL_STATSUPTIME
- ":Server Up %d days %d:%02d:%02d"
- 243 RPL_STATSOLINE
- "O <hostmask> * <name>"
- 244 RPL_STATSHLINE
- "H <hostmask> * <servername>"
-
- 221 RPL_UMODEIS
- "<user mode string>"
-
-
-
-
-Oikarinen & Reed [Page 54]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- - To answer a query about a client's own mode,
- RPL_UMODEIS is sent back.
-
- 251 RPL_LUSERCLIENT
- ":There are <integer> users and <integer> \
- invisible on <integer> servers"
- 252 RPL_LUSEROP
- "<integer> :operator(s) online"
- 253 RPL_LUSERUNKNOWN
- "<integer> :unknown connection(s)"
- 254 RPL_LUSERCHANNELS
- "<integer> :channels formed"
- 255 RPL_LUSERME
- ":I have <integer> clients and <integer> \
- servers"
-
- - In processing an LUSERS message, the server
- sends a set of replies from RPL_LUSERCLIENT,
- RPL_LUSEROP, RPL_USERUNKNOWN,
- RPL_LUSERCHANNELS and RPL_LUSERME. When
- replying, a server must send back
- RPL_LUSERCLIENT and RPL_LUSERME. The other
- replies are only sent back if a non-zero count
- is found for them.
-
- 256 RPL_ADMINME
- "<server> :Administrative info"
- 257 RPL_ADMINLOC1
- ":<admin info>"
- 258 RPL_ADMINLOC2
- ":<admin info>"
- 259 RPL_ADMINEMAIL
- ":<admin info>"
-
- - When replying to an ADMIN message, a server
- is expected to use replies RLP_ADMINME
- through to RPL_ADMINEMAIL and provide a text
- message with each. For RPL_ADMINLOC1 a
- description of what city, state and country
- the server is in is expected, followed by
- details of the university and department
- (RPL_ADMINLOC2) and finally the administrative
- contact for the server (an email address here
- is required) in RPL_ADMINEMAIL.
-
-
-
-
-
-
-
-Oikarinen & Reed [Page 55]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-6.3 Reserved numerics.
-
- These numerics are not described above since they fall into one of
- the following categories:
-
- 1. no longer in use;
-
- 2. reserved for future planned use;
-
- 3. in current use but are part of a non-generic 'feature' of
- the current IRC server.
-
- 209 RPL_TRACECLASS 217 RPL_STATSQLINE
- 231 RPL_SERVICEINFO 232 RPL_ENDOFSERVICES
- 233 RPL_SERVICE 234 RPL_SERVLIST
- 235 RPL_SERVLISTEND
- 316 RPL_WHOISCHANOP 361 RPL_KILLDONE
- 362 RPL_CLOSING 363 RPL_CLOSEEND
- 373 RPL_INFOSTART 384 RPL_MYPORTIS
- 466 ERR_YOUWILLBEBANNED 476 ERR_BADCHANMASK
- 492 ERR_NOSERVICEHOST
-
-7. Client and server authentication
-
- Clients and servers are both subject to the same level of
- authentication. For both, an IP number to hostname lookup (and
- reverse check on this) is performed for all connections made to the
- server. Both connections are then subject to a password check (if
- there is a password set for that connection). These checks are
- possible on all connections although the password check is only
- commonly used with servers.
-
- An additional check that is becoming of more and more common is that
- of the username responsible for making the connection. Finding the
- username of the other end of the connection typically involves
- connecting to an authentication server such as IDENT as described in
- RFC 1413.
-
- Given that without passwords it is not easy to reliably determine who
- is on the other end of a network connection, use of passwords is
- strongly recommended on inter-server connections in addition to any
- other measures such as using an ident server.
-
-8. Current implementations
-
- The only current implementation of this protocol is the IRC server,
- version 2.8. Earlier versions may implement some or all of the
- commands described by this document with NOTICE messages replacing
-
-
-
-Oikarinen & Reed [Page 56]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- many of the numeric replies. Unfortunately, due to backward
- compatibility requirements, the implementation of some parts of this
- document varies with what is laid out. On notable difference is:
-
- * recognition that any LF or CR anywhere in a message marks the
- end of that message (instead of requiring CR-LF);
-
- The rest of this section deals with issues that are mostly of
- importance to those who wish to implement a server but some parts
- also apply directly to clients as well.
-
-8.1 Network protocol: TCP - why it is best used here.
-
- IRC has been implemented on top of TCP since TCP supplies a reliable
- network protocol which is well suited to this scale of conferencing.
- The use of multicast IP is an alternative, but it is not widely
- available or supported at the present time.
-
-8.1.1 Support of Unix sockets
-
- Given that Unix domain sockets allow listen/connect operations, the
- current implementation can be configured to listen and accept both
- client and server connections on a Unix domain socket. These are
- recognized as sockets where the hostname starts with a '/'.
-
- When providing any information about the connections on a Unix domain
- socket, the server is required to supplant the actual hostname in
- place of the pathname unless the actual socket name is being asked
- for.
-
-8.2 Command Parsing
-
- To provide useful 'non-buffered' network IO for clients and servers,
- each connection is given its own private 'input buffer' in which the
- results of the most recent read and parsing are kept. A buffer size
- of 512 bytes is used so as to hold 1 full message, although, this
- will usually hold several commands. The private buffer is parsed
- after every read operation for valid messages. When dealing with
- multiple messages from one client in the buffer, care should be taken
- in case one happens to cause the client to be 'removed'.
-
-8.3 Message delivery
-
- It is common to find network links saturated or hosts to which you
- are sending data unable to send data. Although Unix typically
- handles this through the TCP window and internal buffers, the server
- often has large amounts of data to send (especially when a new
- server-server link forms) and the small buffers provided in the
-
-
-
-Oikarinen & Reed [Page 57]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- kernel are not enough for the outgoing queue. To alleviate this
- problem, a "send queue" is used as a FIFO queue for data to be sent.
- A typical "send queue" may grow to 200 Kbytes on a large IRC network
- with a slow network connection when a new server connects.
-
- When polling its connections, a server will first read and parse all
- incoming data, queuing any data to be sent out. When all available
- input is processed, the queued data is sent. This reduces the number
- of write() system calls and helps TCP make bigger packets.
-
-8.4 Connection 'Liveness'
-
- To detect when a connection has died or become unresponsive, the
- server must ping each of its connections that it doesn't get a
- response from in a given amount of time.
-
- If a connection doesn't respond in time, its connection is closed
- using the appropriate procedures. A connection is also dropped if
- its sendq grows beyond the maximum allowed, because it is better to
- close a slow connection than have a server process block.
-
-8.5 Establishing a server to client connection
-
- Upon connecting to an IRC server, a client is sent the MOTD (if
- present) as well as the current user/server count (as per the LUSER
- command). The server is also required to give an unambiguous message
- to the client which states its name and version as well as any other
- introductory messages which may be deemed appropriate.
-
- After dealing with this, the server must then send out the new user's
- nickname and other information as supplied by itself (USER command)
- and as the server could discover (from DNS/authentication servers).
- The server must send this information out with NICK first followed by
- USER.
-
-8.6 Establishing a server-server connection.
-
- The process of establishing of a server-to-server connection is
- fraught with danger since there are many possible areas where
- problems can occur - the least of which are race conditions.
-
- After a server has received a connection following by a PASS/SERVER
- pair which were recognised as being valid, the server should then
- reply with its own PASS/SERVER information for that connection as
- well as all of the other state information it knows about as
- described below.
-
- When the initiating server receives a PASS/SERVER pair, it too then
-
-
-
-Oikarinen & Reed [Page 58]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- checks that the server responding is authenticated properly before
- accepting the connection to be that server.
-
-8.6.1 Server exchange of state information when connecting
-
- The order of state information being exchanged between servers is
- essential. The required order is as follows:
-
- * all known other servers;
-
- * all known user information;
-
- * all known channel information.
-
- Information regarding servers is sent via extra SERVER messages, user
- information with NICK/USER/MODE/JOIN messages and channels with MODE
- messages.
-
- NOTE: channel topics are *NOT* exchanged here because the TOPIC
- command overwrites any old topic information, so at best, the two
- sides of the connection would exchange topics.
-
- By passing the state information about servers first, any collisions
- with servers that already exist occur before nickname collisions due
- to a second server introducing a particular nickname. Due to the IRC
- network only being able to exist as an acyclic graph, it may be
- possible that the network has already reconnected in another
- location, the place where the collision occurs indicating where the
- net needs to split.
-
-8.7 Terminating server-client connections
-
- When a client connection closes, a QUIT message is generated on
- behalf of the client by the server to which the client connected. No
- other message is to be generated or used.
-
-8.8 Terminating server-server connections
-
- If a server-server connection is closed, either via a remotely
- generated SQUIT or 'natural' causes, the rest of the connected IRC
- network must have its information updated with by the server which
- detected the closure. The server then sends a list of SQUITs (one
- for each server behind that connection) and a list of QUITs (again,
- one for each client behind that connection).
-
-
-
-
-
-
-
-Oikarinen & Reed [Page 59]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-8.9 Tracking nickname changes
-
- All IRC servers are required to keep a history of recent nickname
- changes. This is required to allow the server to have a chance of
- keeping in touch of things when nick-change race conditions occur
- with commands which manipulate them. Commands which must trace nick
- changes are:
-
- * KILL (the nick being killed)
-
- * MODE (+/- o,v)
-
- * KICK (the nick being kicked)
-
- No other commands are to have nick changes checked for.
-
- In the above cases, the server is required to first check for the
- existence of the nickname, then check its history to see who that
- nick currently belongs to (if anyone!). This reduces the chances of
- race conditions but they can still occur with the server ending up
- affecting the wrong client. When performing a change trace for an
- above command it is recommended that a time range be given and
- entries which are too old ignored.
-
- For a reasonable history, a server should be able to keep previous
- nickname for every client it knows about if they all decided to
- change. This size is limited by other factors (such as memory, etc).
-
-8.10 Flood control of clients
-
- With a large network of interconnected IRC servers, it is quite easy
- for any single client attached to the network to supply a continuous
- stream of messages that result in not only flooding the network, but
- also degrading the level of service provided to others. Rather than
- require every 'victim' to be provide their own protection, flood
- protection was written into the server and is applied to all clients
- except services. The current algorithm is as follows:
-
- * check to see if client's `message timer' is less than
- current time (set to be equal if it is);
-
- * read any data present from the client;
-
- * while the timer is less than ten seconds ahead of the current
- time, parse any present messages and penalize the client by
- 2 seconds for each message;
-
- which in essence means that the client may send 1 message every 2
-
-
-
-Oikarinen & Reed [Page 60]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- seconds without being adversely affected.
-
-8.11 Non-blocking lookups
-
- In a real-time environment, it is essential that a server process do
- as little waiting as possible so that all the clients are serviced
- fairly. Obviously this requires non-blocking IO on all network
- read/write operations. For normal server connections, this was not
- difficult, but there are other support operations that may cause the
- server to block (such as disk reads). Where possible, such activity
- should be performed with a short timeout.
-
-8.11.1 Hostname (DNS) lookups
-
- Using the standard resolver libraries from Berkeley and others has
- meant large delays in some cases where replies have timed out. To
- avoid this, a separate set of DNS routines were written which were
- setup for non-blocking IO operations and then polled from within the
- main server IO loop.
-
-8.11.2 Username (Ident) lookups
-
- Although there are numerous ident libraries for use and inclusion
- into other programs, these caused problems since they operated in a
- synchronous manner and resulted in frequent delays. Again the
- solution was to write a set of routines which would cooperate with
- the rest of the server and work using non-blocking IO.
-
-8.12 Configuration File
-
- To provide a flexible way of setting up and running the server, it is
- recommended that a configuration file be used which contains
- instructions to the server on the following:
-
- * which hosts to accept client connections from;
-
- * which hosts to allow to connect as servers;
-
- * which hosts to connect to (both actively and
- passively);
-
- * information about where the server is (university,
- city/state, company are examples of this);
-
- * who is responsible for the server and an email address
- at which they can be contacted;
-
- * hostnames and passwords for clients which wish to be given
-
-
-
-Oikarinen & Reed [Page 61]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- access to restricted operator commands.
-
- In specifying hostnames, both domain names and use of the 'dot'
- notation (127.0.0.1) should both be accepted. It must be possible to
- specify the password to be used/accepted for all outgoing and
- incoming connections (although the only outgoing connections are
- those to other servers).
-
- The above list is the minimum requirement for any server which wishes
- to make a connection with another server. Other items which may be
- of use are:
-
- * specifying which servers other server may introduce;
-
- * how deep a server branch is allowed to become;
-
- * hours during which clients may connect.
-
-8.12.1 Allowing clients to connect
-
- A server should use some sort of 'access control list' (either in the
- configuration file or elsewhere) that is read at startup and used to
- decide what hosts clients may use to connect to it.
-
- Both 'deny' and 'allow' should be implemented to provide the required
- flexibility for host access control.
-
-8.12.2 Operators
-
- The granting of operator privileges to a disruptive person can have
- dire consequences for the well-being of the IRC net in general due to
- the powers given to them. Thus, the acquisition of such powers
- should not be very easy. The current setup requires two 'passwords'
- to be used although one of them is usually easy guessed. Storage of
- oper passwords in configuration files is preferable to hard coding
- them in and should be stored in a crypted format (ie using crypt(3)
- from Unix) to prevent easy theft.
-
-8.12.3 Allowing servers to connect
-
- The interconnection of server is not a trivial matter: a bad
- connection can have a large impact on the usefulness of IRC. Thus,
- each server should have a list of servers to which it may connect and
- which servers may connect to it. Under no circumstances should a
- server allow an arbitrary host to connect as a server. In addition
- to which servers may and may not connect, the configuration file
- should also store the password and other characteristics of that
- link.
-
-
-
-Oikarinen & Reed [Page 62]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-8.12.4 Administrivia
-
- To provide accurate and valid replies to the ADMIN command (see
- section 4.3.7), the server should find the relevant details in the
- configuration.
-
-8.13 Channel membership
-
- The current server allows any registered local user to join upto 10
- different channels. There is no limit imposed on non-local users so
- that the server remains (reasonably) consistant with all others on a
- channel membership basis
-
-9. Current problems
-
- There are a number of recognized problems with this protocol, all of
- which hope to be solved sometime in the near future during its
- rewrite. Currently, work is underway to find working solutions to
- these problems.
-
-9.1 Scalability
-
- It is widely recognized that this protocol does not scale
- sufficiently well when used in a large arena. The main problem comes
- from the requirement that all servers know about all other servers
- and users and that information regarding them be updated as soon as
- it changes. It is also desirable to keep the number of servers low
- so that the path length between any two points is kept minimal and
- the spanning tree as strongly branched as possible.
-
-9.2 Labels
-
- The current IRC protocol has 3 types of labels: the nickname, the
- channel name and the server name. Each of the three types has its
- own domain and no duplicates are allowed inside that domain.
- Currently, it is possible for users to pick the label for any of the
- three, resulting in collisions. It is widely recognized that this
- needs reworking, with a plan for unique names for channels and nicks
- that don't collide being desirable as well as a solution allowing a
- cyclic tree.
-
-9.2.1 Nicknames
-
- The idea of the nickname on IRC is very convenient for users to use
- when talking to each other outside of a channel, but there is only a
- finite nickname space and being what they are, its not uncommon for
- several people to want to use the same nick. If a nickname is chosen
- by two people using this protocol, either one will not succeed or
-
-
-
-Oikarinen & Reed [Page 63]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
- both will removed by use of KILL (4.6.1).
-
-9.2.2 Channels
-
- The current channel layout requires that all servers know about all
- channels, their inhabitants and properties. Besides not scaling
- well, the issue of privacy is also a concern. A collision of
- channels is treated as an inclusive event (both people who create the
- new channel are considered to be members of it) rather than an
- exclusive one such as used to solve nickname collisions.
-
-9.2.3 Servers
-
- Although the number of servers is usually small relative to the
- number of users and channels, they two currently required to be known
- globally, either each one separately or hidden behind a mask.
-
-9.3 Algorithms
-
- In some places within the server code, it has not been possible to
- avoid N^2 algorithms such as checking the channel list of a set
- of clients.
-
- In current server versions, there are no database consistency checks,
- each server assumes that a neighbouring server is correct. This
- opens the door to large problems if a connecting server is buggy or
- otherwise tries to introduce contradictions to the existing net.
-
- Currently, because of the lack of unique internal and global labels,
- there are a multitude of race conditions that exist. These race
- conditions generally arise from the problem of it taking time for
- messages to traverse and effect the IRC network. Even by changing to
- unique labels, there are problems with channel-related commands being
- disrupted.
-
-10. Current support and availability
-
- Mailing lists for IRC related discussion:
- Future protocol: ircd-three-request@eff.org
- General discussion: operlist-request@eff.org
-
- Software implemenations
- cs.bu.edu:/irc
- nic.funet.fi:/pub/irc
- coombs.anu.edu.au:/pub/irc
-
- Newsgroup: alt.irc
-
-
-
-
-Oikarinen & Reed [Page 64]
-\f
-RFC 1459 Internet Relay Chat Protocol May 1993
-
-
-Security Considerations
-
- Security issues are discussed in sections 4.1, 4.1.1, 4.1.3, 5.5, and
- 7.
-
-12. Authors' Addresses
-
- Jarkko Oikarinen
- Tuirantie 17 as 9
- 90500 OULU
- FINLAND
-
- Email: jto@tolsun.oulu.fi
-
-
- Darren Reed
- 4 Pateman Street
- Watsonia, Victoria 3087
- Australia
-
- Email: avalon@coombs.anu.edu.au
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Oikarinen & Reed [Page 65]
-\f
\ No newline at end of file
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace insp
+{
+ template <typename T> class aligned_storage;
+}
+
+template <typename T>
+class insp::aligned_storage
+{
+ mutable typename TR1NS::aligned_storage<sizeof(T), TR1NS::alignment_of<T>::value>::type data;
+
+ public:
+ aligned_storage()
+ {
+ }
+
+ aligned_storage(const aligned_storage& other)
+ {
+ }
+
+ T* operator->() const
+ {
+ return static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ operator T*() const
+ {
+ return operator->();
+ }
+};
*/
-#ifndef BANCACHE_H
-#define BANCACHE_H
+#pragma once
/** Stores a cached ban entry.
* Each ban has one of these hashed in a hash_map to make for faster removal
/** Reason, shown as quit message
*/
std::string Reason;
- /** IP to match against, no wildcards here (of course)
- */
- std::string IP;
/** Time that the ban expires at
*/
time_t Expiry;
- BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason)
- {
- this->Type = type;
- this->Reason = reason;
- this->IP = ip;
- this->Expiry = ServerInstance->Time() + 86400; // a day. this might seem long, but entries will be removed as glines/etc expire.
- }
+ BanCacheHit(const std::string& type, const std::string& reason, time_t seconds);
- // overridden to allow custom time
- BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
- {
- this->Type = type;
- this->Reason = reason;
- this->IP = ip;
- this->Expiry = ServerInstance->Time() + seconds;
- }
+ bool IsPositive() const { return (!Reason.empty()); }
};
-/* A container of ban cache items.
- * must be defined after class BanCacheHit.
- */
-typedef nspace::hash_map<std::string, BanCacheHit*, nspace::hash<std::string> > BanCacheHash;
-
/** A manager for ban cache, which allocates and deallocates and checks cached bans.
*/
class CoreExport BanCacheManager
{
- private:
- BanCacheHash* BanHash;
+ /** A container of ban cache items.
+ */
+ typedef TR1NS::unordered_map<std::string, BanCacheHit*, TR1NS::hash<std::string> > BanCacheHash;
+
+ BanCacheHash BanHash;
+ bool RemoveIfExpired(BanCacheHash::iterator& it);
+
public:
/** Creates and adds a Ban Cache item.
* @param ip The IP the item is for.
* @param type The type of ban cache item. std::string. .empty() means it's a negative match (user is allowed freely).
* @param reason The reason for the ban. Left .empty() if it's a negative match.
+ * @param seconds Number of seconds before nuking the bancache entry, the default is a day. This might seem long, but entries will be removed as glines/etc expire.
*/
- BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason);
-
- // Overridden to allow an optional number of seconds before expiry
- BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds);
+ BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds = 0);
BanCacheHit *GetHit(const std::string &ip);
- bool RemoveHit(BanCacheHit *b);
/** Removes all entries of a given type, either positive or negative. Returns the number of hits removed.
* @param type The type of bancache entries to remove (e.g. 'G')
* @param positive Remove either positive (true) or negative (false) hits.
*/
- unsigned int RemoveEntries(const std::string &type, bool positive);
+ void RemoveEntries(const std::string& type, bool positive);
- BanCacheManager()
- {
- this->BanHash = new BanCacheHash();
- }
~BanCacheManager();
- void RehashCache();
};
-
-#endif
*/
-#ifndef BASE_H
-#define BASE_H
+#pragma once
#include <map>
#include <deque>
*/
class CoreExport CoreException : public std::exception
{
- public:
+ protected:
/** Holds the error message to be displayed
*/
const std::string err;
/** Source of the exception
*/
const std::string source;
- /** Default constructor, just uses the error mesage 'Core threw an exception'.
- */
- CoreException() : err("Core threw an exception"), source("The core") {}
+
+ public:
/** This constructor can be used to specify an error message before throwing.
+ * @param message Human readable error message
*/
CoreException(const std::string &message) : err(message), source("The core") {}
/** This constructor can be used to specify an error message before throwing,
* and to specify the source of the exception.
+ * @param message Human readable error message
+ * @param src Source of the exception
*/
CoreException(const std::string &message, const std::string &src) : err(message), source(src) {}
/** This destructor solves world hunger, cancels the world debt, and causes the world to end.
*/
virtual ~CoreException() throw() {};
/** Returns the reason for the exception.
- * The module should probably put something informative here as the user will see this upon failure.
+ * @return Human readable description of the error
*/
- virtual const char* GetReason()
- {
- return err.c_str();
- }
+ const std::string& GetReason() const { return err; }
- virtual const char* GetSource()
- {
- return source.c_str();
- }
+ /** Returns the source of the exception
+ * @return Source of the exception
+ */
+ const std::string& GetSource() const { return source; }
};
class Module;
const std::string name;
/** Type of service (must match object type) */
const ServiceType service;
- ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
- : creator(Creator), name(Name), service(Type) {}
+ ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type);
virtual ~ServiceProvider();
-};
-
-#endif
+ /** If called, this ServiceProvider won't be registered automatically
+ */
+ void DisableAutoRegister();
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mode.h"
+#include "channels.h"
+#include "listmode.h"
+
+/** Channel mode +b
+ */
+class ModeChannelBan : public ListModeBase
+{
+ public:
+ ModeChannelBan()
+ : ListModeBase(NULL, "ban", 'b', "End of channel ban list", 367, 368, true, "maxbans")
+ {
+ }
+};
+
+/** Channel mode +k
+ */
+class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
+{
+ public:
+ ModeChannelKey();
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
+ void SerializeParam(Channel* chan, const std::string* key, std::string& out);
+ ModeAction OnSet(User* source, Channel* chan, std::string& param);
+};
+
+/** Channel mode +l
+ */
+class ModeChannelLimit : public ParamMode<ModeChannelLimit, LocalIntExt>
+{
+ public:
+ ModeChannelLimit();
+ bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
+ void SerializeParam(Channel* chan, intptr_t n, std::string& out);
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter);
+};
+
+/** Channel mode +o
+ */
+class ModeChannelOp : public PrefixMode
+{
+ public:
+ ModeChannelOp()
+ : PrefixMode(NULL, "op", 'o', OP_VALUE, '@')
+ {
+ levelrequired = OP_VALUE;
+ }
+};
+
+/** Channel mode +v
+ */
+class ModeChannelVoice : public PrefixMode
+{
+ public:
+ ModeChannelVoice()
+ : PrefixMode(NULL, "voice", 'v', VOICE_VALUE, '+')
+ {
+ levelrequired = HALFOP_VALUE;
+ }
+};
+
+/** User mode +s
+ */
+class ModeUserServerNoticeMask : public ModeHandler
+{
+ /** Process a snomask modifier string, e.g. +abc-de
+ * @param user The target user
+ * @param input A sequence of notice mask characters
+ * @return The cleaned mode sequence which can be output,
+ * e.g. in the above example if masks c and e are not
+ * valid, this function will return +ab-d
+ */
+ std::string ProcessNoticeMasks(User* user, const std::string& input);
+
+ public:
+ ModeUserServerNoticeMask();
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
+ void OnParameterMissing(User* user, User* dest, Channel* channel);
+
+ /** Create a displayable mode string of the snomasks set on a given user
+ * @param user The user whose notice masks to format
+ * @return The notice mask character sequence
+ */
+ std::string GetUserParameter(User* user);
+};
+
+/** User mode +o
+ */
+class ModeUserOperator : public ModeHandler
+{
+ public:
+ ModeUserOperator();
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
+};
*
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2012 Adam <Adam@anope.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
*/
-#ifndef CALLER_H
-#define CALLER_H
+#pragma once
+
+#if defined HAS_CXX11_VARIADIC_TEMPLATES
+
+template<typename ReturnType, typename... Args> class CoreExport Handler : public classbase
+{
+ public:
+ virtual ~Handler() { }
+ virtual ReturnType Call(Args...) = 0;
+};
+
+template<typename ReturnType, typename... Args> class CoreExport Caller
+{
+ public:
+ Handler<ReturnType, Args...>* target;
+
+ Caller(Handler<ReturnType, Args...>* initial) : target(initial) { }
+ virtual ~Caller() { }
+
+ virtual ReturnType operator()(const Args&... params)
+ {
+ return this->target->Call(params...);
+ }
+};
+
+/* Below here is compat with the old API */
+#define HandlerBase0 Handler
+#define HandlerBase1 Handler
+#define HandlerBase2 Handler
+#define HandlerBase3 Handler
+#define HandlerBase4 Handler
+#define HandlerBase5 Handler
+#define HandlerBase6 Handler
+#define HandlerBase7 Handler
+#define HandlerBase8 Handler
+
+#define caller1 Caller
+#define caller2 Caller
+#define caller3 Caller
+#define caller4 Caller
+#define caller5 Caller
+#define caller6 Caller
+#define caller7 Caller
+#define caller8 Caller
+
+#define DEFINE_HANDLER0(NAME, RETURN) \
+ class CoreExport NAME : public Handler<RETURN> { public: NAME() { } virtual RETURN Call(); }
+
+#define DEFINE_HANDLER1(NAME, RETURN, V1) \
+ class CoreExport NAME : public Handler<RETURN, V1> { public: NAME() { } virtual RETURN Call(V1); }
+
+#define DEFINE_HANDLER2(NAME, RETURN, V1, V2) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2> { public: NAME() { } virtual RETURN Call(V1, V2); }
+
+#define DEFINE_HANDLER3(NAME, RETURN, V1, V2, V3) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3> { public: NAME() { } virtual RETURN Call(V1, V2, V3); }
+
+#define DEFINE_HANDLER4(NAME, RETURN, V1, V2, V3, V4) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4); }
+
+#define DEFINE_HANDLER5(NAME, RETURN, V1, V2, V3, V4, V5) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5); }
+
+#define DEFINE_HANDLER6(NAME, RETURN, V1, V2, V3, V4, V5, V6) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5, V6> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6); }
+
+#define DEFINE_HANDLER7(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5, V6, V7> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7); }
+
+#define DEFINE_HANDLER8(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7, V8) \
+ class CoreExport NAME : public Handler<RETURN, V1, V2, V3, V4, V5, V6, V7, V8> { public: NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7, V8); }
+
+#else
/** The templates below can be auto generated by tools/create_templates.pl.
* They are used to represent a functor with a given number of parameters and
*/
-#ifndef CHANNELS_H
-#define CHANNELS_H
+#pragma once
#include "membership.h"
#include "mode.h"
+#include "parammode.h"
/** Holds an entry for a ban list, exemption list, or invite list.
* This class contains a single element in a channel list, such as a banlist.
*/
-class HostItem
-{
- public:
- /** Time the item was added
- */
- time_t set_time;
- /** Who added the item
- */
- std::string set_by;
- /** The actual item data
- */
- std::string data;
-
- HostItem() { /* stub */ }
- virtual ~HostItem() { /* stub */ }
-};
-
-/** A subclass of HostItem designed to hold channel bans (+b)
- */
-class BanItem : public HostItem
-{
-};
/** Holds all relevent information for a channel.
* This class represents a channel, and contains its name, modes, topic, topic set time,
* etc, and an instance of the BanList type.
*/
-class CoreExport Channel : public Extensible, public InviteBase
+class CoreExport Channel : public Extensible, public InviteBase<Channel>
{
- /** Connect a Channel to a User
+ public:
+ /** A map of Memberships on a channel keyed by User pointers
*/
- static Channel* ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created);
+ typedef std::map<User*, insp::aligned_storage<Membership> > MemberMap;
+ private:
/** Set default modes for the channel on creation
*/
void SetDefaultModes();
- /** Maximum number of bans (cached)
- */
- int maxbans;
-
/** Modes for the channel.
- * This is not a null terminated string! It is a bitset where
- * each item in it represents if a mode is set. For example
- * for mode +A, index 0. Use modechar-65 to calculate which
- * field to check.
+ * It is a bitset where each item in it represents if a mode is set.
+ * To see if a mode is set, inspect modes[mh->modeid]
*/
- std::bitset<64> modes;
+ std::bitset<ModeParser::MODEID_MAX> modes;
- /** Parameters for custom modes.
- * One for each custom mode letter.
+ /** Remove the given membership from the channel's internal map of
+ * memberships and destroy the Membership object.
+ * This function does not remove the channel from User::chanlist.
+ * Since the parameter is an iterator to the target, the complexity
+ * of this function is constant.
+ * @param membiter The MemberMap iterator to remove, must be valid
*/
- CustomModeList custom_mode_params;
+ void DelUser(const MemberMap::iterator& membiter);
public:
/** Creates a channel record and initialises it with default values
- * @throw Nothing at present.
+ * @param name The name of the channel
+ * @param ts The creation time of the channel
+ * @throw CoreException if this channel name is in use
*/
Channel(const std::string &name, time_t ts);
+ /** Checks whether the channel should be destroyed, and if yes, begins
+ * the teardown procedure.
+ *
+ * If there are users on the channel or a module vetoes the deletion
+ * (OnPreChannelDelete hook) then nothing else happens.
+ * Otherwise, first the OnChannelDelete event is fired, then the channel is
+ * removed from the channel list. All pending invites are destroyed and
+ * finally the channel is added to the cull list.
+ */
+ void CheckDestroy();
+
/** The channel's name.
*/
std::string name;
/** User list.
*/
- UserMembList userlist;
+ MemberMap userlist;
/** Channel topic.
* If this is an empty string, no channel topic is set.
*/
std::string setby; /* 128 */
- /** The list of all bans set on the channel.
- */
- BanList bans;
-
/** Sets or unsets a custom mode in the channels info
* @param mode The mode character to set or unset
* @param value True if you want to set the mode or false if you want to remove it
*/
void SetMode(ModeHandler* mode, bool value);
- void SetMode(char mode,bool mode_on);
-
- /** Sets or unsets a custom mode in the channels info
- * @param mode The mode character to set or unset
- * @param parameter The parameter string to associate with this mode character.
- * If it is empty, the mode is unset; if it is nonempty, the mode is set.
- */
- void SetModeParam(ModeHandler* mode, const std::string& parameter);
- void SetModeParam(char mode, const std::string& parameter);
/** Returns true if a mode is set on a channel
* @param mode The mode character you wish to query
* @return True if the custom mode is set, false if otherwise
*/
- inline bool IsModeSet(char mode) { return modes[mode-'A']; }
- inline bool IsModeSet(ModeHandler* mode) { return modes[mode->GetModeChar()-'A']; }
-
+ bool IsModeSet(ModeHandler* mode) { return ((mode->GetId() != ModeParser::MODEID_MAX) && (modes[mode->GetId()])); }
+ bool IsModeSet(ModeHandler& mode) { return IsModeSet(&mode); }
+ bool IsModeSet(ChanModeReference& mode);
/** Returns the parameter for a custom mode on a channel.
* @param mode The mode character you wish to query
*
* @return The parameter for this mode is returned, or an empty string
*/
- std::string GetModeParameter(char mode);
std::string GetModeParameter(ModeHandler* mode);
+ std::string GetModeParameter(ChanModeReference& mode);
+ std::string GetModeParameter(ParamModeBase* pm);
/** Sets the channel topic.
- * @param u The user setting the topic
- * @param t The topic to set it to. Non-const, as it may be modified by a hook.
- * @param forceset If set to true then all access checks will be bypassed.
+ * @param user The user setting the topic.
+ * @param topic The topic to set it to.
*/
- int SetTopic(User *u, std::string &t, bool forceset = false);
+ void SetTopic(User* user, const std::string& topic);
/** Obtain the channel "user counter"
- * This returns the channel reference counter, which is initialized
- * to 0 when the channel is created and incremented/decremented
- * upon joins, parts quits and kicks.
+ * This returns the number of users on this channel
*
* @return The number of users on this channel
*/
- long GetUserCounter();
+ long GetUserCounter() const { return userlist.size(); }
/** Add a user pointer to the internal reference list
* @param user The user to add
*
* @return This function returns pointer to a map of User pointers (CUList*).
*/
- const UserMembList* GetUsers();
+ const MemberMap& GetUsers() const { return userlist; }
/** Returns true if the user given is on the given channel.
* @param user The user to look for
/** Make src kick user from this channel with the given reason.
* @param src The source of the kick
- * @param user The user being kicked (must be on this channel)
+ * @param victimiter Iterator to the user being kicked, must be valid
* @param reason The reason for the kick
*/
- void KickUser(User *src, User *user, const char* reason);
+ void KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason);
+
+ /** Make src kick user from this channel with the given reason.
+ * @param src The source of the kick
+ * @param user The user being kicked
+ * @param reason The reason for the kick
+ */
+ void KickUser(User* src, User* user, const std::string& reason)
+ {
+ MemberMap::iterator it = userlist.find(user);
+ if (it != userlist.end())
+ KickUser(src, it, reason);
+ }
/** Part a user from this channel with the given reason.
* If the reason field is NULL, no reason will be sent.
*/
void PartUser(User *user, std::string &reason);
- /* Join a user to a channel. May be a channel that doesnt exist yet.
+ /** Join a local user to a channel, with or without permission checks. May be a channel that doesn't exist yet.
* @param user The user to join to the channel.
- * @param cn The channel name to join to. Does not have to exist.
+ * @param channame The channel name to join to. Does not have to exist.
* @param key The key of the channel, if given
* @param override If true, override all join restrictions such as +bkil
* @return A pointer to the Channel the user was joined to. A new Channel may have
* been created if the channel did not exist before the user was joined to it.
- * If the user could not be joined to a channel, the return value may be NULL.
+ * If the user could not be joined to a channel, the return value is NULL.
+ */
+ static Channel* JoinUser(LocalUser* user, std::string channame, bool override = false, const std::string& key = "");
+
+ /** Join a user to an existing channel, without doing any permission checks
+ * @param user The user to join to the channel
+ * @param privs Priviliges (prefix mode letters) to give to this user, may be NULL
+ * @param bursting True if this join is the result of a netburst (passed to modules in the OnUserJoin hook)
+ * @param created_by_local True if this channel was just created by a local user (passed to modules in the OnUserJoin hook)
+ * @return A newly created Membership object, or NULL if the user was already inside the channel or if the user is a server user
*/
- static Channel* JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS = 0);
+ Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
/** Write to a channel, from a user, using va_args for text
* @param user User whos details to prefix the line with
/** Write a line of text that already includes the source */
void RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string& text);
- /** Returns the maximum number of bans allowed to be set on this channel
- * @return The maximum number of bans allowed
- */
- long GetMaxBans();
-
/** Return the channel's modes with parameters.
* @param showkey If this is set to true, the actual key is shown,
* otherwise it is replaced with '<KEY>'
* @return The channel mode string
*/
- char* ChanModes(bool showkey);
+ const char* ChanModes(bool showkey);
/** Spool the NAMES list for this channel to the given user
* @param user The user to spool the NAMES list to
+ * @param isinside If true, the user is inside the channel, if not then false
*/
- void UserList(User *user);
-
- /** Get the number of invisible users on this channel
- * @return Number of invisible users
- */
- int CountInvisible();
-
- /** Get a users prefix on this channel in a string.
- * @param user The user to look up
- * @return A character array containing the prefix string.
- * Unlike GetStatus and GetStatusFlags which will only return the
- * core specified modes @, % and + (op, halfop and voice), GetPrefixChar
- * will also return module-defined prefixes. If the user has to prefix,
- * an empty but non-null string is returned. If the user has multiple
- * prefixes, the highest is returned. If you do not recognise the prefix
- * character you can get, you can deal with it in a 'proprtional' manner
- * compared to known prefixes, using GetPrefixValue().
- */
- const char* GetPrefixChar(User *user);
-
- /** Return all of a users mode prefixes into a char* string.
- * @param user The user to look up
- * @return A list of all prefix characters. The prefixes will always
- * be in rank order, greatest first, as certain IRC clients require
- * this when multiple prefixes are used names lists.
- */
- const char* GetAllPrefixChars(User* user);
+ void UserList(User* user, bool isinside = true);
/** Get the value of a users prefix on this channel.
* @param user The user to look up
*/
unsigned int GetPrefixValue(User* user);
- /** This method removes all prefix characters from a user.
- * It will not inform the user or the channel of the removal of prefixes,
- * and should be used when the user parts or quits.
- * @param user The user to remove all prefixes from
- */
- void RemoveAllPrefixes(User* user);
-
- /** Add a prefix character to a user.
- * Only the core should call this method, usually from
- * within the mode parser or when the first user joins
- * the channel (to grant ops to them)
- * @param user The user to associate the privilage with
- * @param prefix The prefix character to associate
- * @param adding True if adding the prefix, false when removing
- * @return True if a change was made
- */
- bool SetPrefix(User* user, char prefix, bool adding);
-
/** Check if a user is banned on this channel
* @param user A user to check against the banlist
* @returns True if the user given is banned
/** Get the status of an "action" type extban
*/
ModResult GetExtBanStatus(User *u, char type);
-
- /** Clears the cached max bans value
- */
- void ResetMaxBans();
};
-#endif
+inline bool Channel::HasUser(User* user)
+{
+ return (userlist.find(user) != userlist.end());
+}
+
+inline std::string Channel::GetModeParameter(ChanModeReference& mode)
+{
+ if (!mode)
+ return "";
+ return GetModeParameter(*mode);
+}
+
+inline std::string Channel::GetModeParameter(ModeHandler* mh)
+{
+ std::string out;
+ ParamModeBase* pm = mh->IsParameterMode();
+ if (pm && this->IsModeSet(pm))
+ pm->GetParameter(this, out);
+ return out;
+}
+
+inline std::string Channel::GetModeParameter(ParamModeBase* pm)
+{
+ std::string out;
+ if (this->IsModeSet(pm))
+ pm->GetParameter(this, out);
+ return out;
+}
+
+inline bool Channel::IsModeSet(ChanModeReference& mode)
+{
+ if (!mode)
+ return false;
+ return IsModeSet(*mode);
+}
*/
-#ifndef COMMAND_PARSE_H
-#define COMMAND_PARSE_H
+#pragma once
/** This class handles command management and parsing.
* It allows you to add and remove commands from the map,
*/
class CoreExport CommandParser
{
- private:
- /** Process a parameter string into a list of items
- * @param command_p The output list of items
- * @param parameters The input string
- * @return The number of parameters parsed into command_p
- */
- int ProcessParameters(std::vector<std::string>& command_p, char* parameters);
+ public:
+ typedef TR1NS::unordered_map<std::string, Command*> CommandMap;
+ private:
/** Process a command from a user.
* @param user The user to parse the command for
* @param cmd The command string to process
*/
- bool ProcessCommand(LocalUser *user, std::string &cmd);
+ void ProcessCommand(LocalUser* user, std::string& cmd);
- public:
/** Command list, a hash_map of command names to Command*
*/
- Commandtable cmdlist;
+ CommandMap cmdlist;
+ public:
/** Default constructor.
*/
CommandParser();
+ /** Get a command name -> Command* map containing all client to server commands
+ * @return A map of command handlers keyed by command names
+ */
+ const CommandMap& GetCommands() const { return cmdlist; }
+
/** Calls the handler for a given command.
* @param commandname The command to find. This should be in uppercase.
* @param parameters Parameter list
* @param user The user to call the handler on behalf of
+ * @param cmd If non-NULL and the command was executed it is set to the command handler,
+ * otherwise it isn't written to.
* @return This method will return CMD_SUCCESS if the command handler was found and called,
* and the command completeld successfully. It will return CMD_FAILURE if the command handler was found
* and called, but the command did not complete successfully, and it will return CMD_INVALID if the
* command simply did not exist at all or the wrong number of parameters were given, or the user
* was not privilaged enough to execute the command.
*/
- CmdResult CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user);
+ CmdResult CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd = NULL);
/** Get the handler function for a command.
* @param commandname The command required. Always use uppercase for this parameter.
*/
Command* GetHandler(const std::string &commandname);
- /** This function returns true if a command is valid with the given number of parameters and user.
- * @param commandname The command name to check
- * @param pcnt The parameter count
- * @param user The user to check against
- * @return If the user given has permission to execute the command, and the parameter count is
- * equal to or greater than the minimum number of parameters to the given command, then this
- * function will return true, otherwise it will return false.
- */
- bool IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user);
-
- /** LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
- * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
- * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
+ /** LoopCall is used to call a command handler repeatedly based on the contents of a comma seperated list.
+ * There are two ways to call this method, either with one potential list or with two potential lists.
+ * We need to handle two potential lists for JOIN, because a JOIN may contain two lists of items at once:
* the channel names and their keys as follows:
*
* JOIN \#chan1,\#chan2,\#chan3 key1,,key3
*
- * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
- * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
- * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
- * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
+ * Therefore, we need to deal with both lists concurrently. If there are two lists then the method reads
+ * them both together until the first runs out of tokens.
+ * With one list it is much simpler, and is used in NAMES, WHOIS, PRIVMSG etc.
+ *
+ * If there is only one list and there are duplicates in it, then the command handler is only called for
+ * unique items. Entries are compared using "irc comparision" (see irc::string).
+ * If the usemax parameter is true (the default) the function only parses until it reaches
+ * ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
+ *
+ * The OnPostCommand hook is executed for each item after it has been processed by the handler, with the
+ * original line parameter being empty (to indicate that the command in that form was created by this function).
+ * This only applies if the user executing the command is local.
+ *
+ * If there are two lists and the second list runs out of tokens before the first list then parameters[extra]
+ * will be an EMPTY string when Handle() is called for the remaining tokens in the first list, even if it is
+ * in the middle of parameters[]! Moreover, empty tokens in the second list are allowed, and those will also
+ * result in the appropiate entry being empty in parameters[].
+ * This is different than what command handlers usually expect; the command parser only allows an empty param
+ * as the last item in the vector.
*
* @param user The user who sent the command
- * @param CommandObj the command object to call for each parameter in the list
- * @param parameters Parameter list as an array of array of char (that's not a typo).
+ * @param handler The command handler to call for each parameter in the list
+ * @param parameters Parameter list as a vector of strings
* @param splithere The first parameter index to split as a comma seperated list
- * @param extra The second parameter index to split as a comma seperated list
- * @param usemax Limit the command to MaxTargets targets
- * @return This function will return 1 when there are no more parameters to process. When this occurs, its
- * caller should return without doing anything, otherwise it should continue into its main section of code.
+ * @param extra The second parameter index to split as a comma seperated list, or -1 (the default) if there is only one list
+ * @param usemax True to limit the command to MaxTargets targets (default), or false to process all tokens
+ * @return This function returns true when it identified a list in the given parameter and finished calling the
+ * command handler for each entry on the list. When this occurs, the caller should return without doing anything,
+ * otherwise it should continue into its main section of code.
*/
- int LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
+ static bool LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
/** Take a raw input buffer from a recvq, and process it on behalf of a user.
* @param buffer The buffer line to process
* @param user The user to whom this line belongs
*/
- bool ProcessBuffer(std::string &buffer,LocalUser *user);
+ void ProcessBuffer(std::string &buffer,LocalUser *user);
/** Add a new command to the commands hash
* @param f The new Command to add to the list
*/
void RemoveCommand(Command* x);
- /** Translate nicknames in a string into UIDs, based on the TranslationType given.
- * @param to The translation type to use for the process.
- * @param source The input string
- * @param dest The output string, it is safe to pass source and dest as the same variable only for translation type TR_TEXT.
- * @return returns the number of substitutions made. Will always be 0 or 1
+ /** Translate a single item based on the TranslationType given.
+ * @param to The translation type to use for the process
+ * @param item The input string
+ * @param dest The output string. The translation result will be appended to this string
+ * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
+ * @param paramnumber The index of the parameter we are translating.
*/
- int TranslateUIDs(TranslateType to, const std::string &source, std::string &dest);
+ static void TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator = NULL, unsigned int paramnumber = 0);
/** Translate nicknames in a list of strings into UIDs, based on the TranslateTypes given.
* @param to The translation types to use for the process. If this list is too short, TR_TEXT is assumed for the rest.
* @param source The strings to translate
- * @param dest The output string
* @param prefix_final True if the final source argument should have a colon prepended (if it could contain a space)
- * @param custom_translator Used to translate the parameter if the TR_CUSTOM type is found in to
- * @return returns the number of substitutions made.
+ * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
+ * @return dest The output string
*/
- int TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final = false, Command* custom_translator = NULL);
+ static std::string TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final = false, CommandBase* custom_translator = NULL);
};
/** A lookup table of values for multiplier characters used by
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
-
-#endif
*/
-#ifndef CMD_WHOWAS_H
-#define CMD_WHOWAS_H
+#pragma once
+
#include "modules.h"
-struct WhowasRequest : public Request
+namespace WhoWas
{
- /* list of available internal commands */
- enum Internals
+ /** One entry for a nick. There may be multiple entries for a nick.
+ */
+ struct Entry
{
- WHOWAS_ADD = 1,
- WHOWAS_STATS = 2,
- WHOWAS_PRUNE = 3,
- WHOWAS_MAINTAIN = 4
- };
+ /** Real host
+ */
+ const std::string host;
- const Internals type;
- std::string value;
- User* user;
+ /** Displayed host
+ */
+ const std::string dhost;
- WhowasRequest(Module* src, Module* whowas, Internals Type) : Request(src, whowas, "WHOWAS"), type(Type)
- {}
-};
+ /** Ident
+ */
+ const std::string ident;
-/* Forward ref for timer */
-class WhoWasMaintainTimer;
+ /** Server name
+ */
+ const std::string server;
-/* Forward ref for typedefs */
-class WhoWasGroup;
+ /** Full name (GECOS)
+ */
+ const std::string gecos;
-/** Timer that is used to maintain the whowas list, called once an hour
- */
-extern WhoWasMaintainTimer* timer;
+ /** Signon time
+ */
+ const time_t signon;
-/** A group of users related by nickname
- */
-typedef std::deque<WhoWasGroup*> whowas_set;
+ /** Initialize this Entry with a user
+ */
+ Entry(User* user);
+ };
-/** Sets of users in the whowas system
- */
-typedef std::map<irc::string,whowas_set*> whowas_users;
+ /** Everything known about one nick
+ */
+ struct Nick : public insp::intrusive_list_node<Nick>
+ {
+ /** A group of users related by nickname
+ */
+ typedef std::deque<Entry*> List;
-/** Sets of time and users in whowas list
- */
-typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo;
+ /** Container where each element has information about one occurrence of this nick
+ */
+ List entries;
+
+ /** Time this nick was added to the database
+ */
+ const time_t addtime;
+
+ /** Nickname whose information is stored in this class
+ */
+ const std::string nick;
+
+ /** Constructor to initialize fields
+ */
+ Nick(const std::string& nickname);
+
+ /** Destructor, deallocates all elements in the entries container
+ */
+ ~Nick();
+ };
+
+ class Manager
+ {
+ public:
+ struct Stats
+ {
+ /** Number of currently existing WhoWas::Entry objects
+ */
+ size_t entrycount;
+ };
+
+ /** Add a user to the whowas database. Called when a user quits.
+ * @param user The user to add to the database
+ */
+ void Add(User* user);
+
+ /** Retrieves statistics about the whowas database
+ * @return Whowas statistics as a WhoWas::Manager::Stats struct
+ */
+ Stats GetStats() const;
+
+ /** Expires old entries
+ */
+ void Maintain();
+
+ /** Updates the current configuration which may result in the database being pruned if the
+ * new values are lower than the current ones.
+ * @param NewGroupSize Maximum number of nicks allowed in the database. In case there are this many nicks
+ * in the database and one more is added, the oldest one is removed (FIFO).
+ * @param NewMaxGroups Maximum number of entries per nick
+ * @param NewMaxKeep Seconds how long each nick should be kept
+ */
+ void UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep);
+
+ /** Retrieves all data known about a given nick
+ * @param nick Nickname to find, case insensitive (IRC casemapping)
+ * @return A pointer to a WhoWas::Nick if the nick was found, NULL otherwise
+ */
+ const Nick* FindNick(const std::string& nick) const;
+
+ /** Returns true if WHOWAS is enabled according to the current configuration
+ * @return True if WHOWAS is enabled according to the configuration, false if WHOWAS is disabled
+ */
+ bool IsEnabled() const;
+
+ /** Constructor
+ */
+ Manager();
+
+ /** Destructor
+ */
+ ~Manager();
+
+ private:
+ /** Order in which the users were added into the map, used to remove oldest nick
+ */
+ typedef insp::intrusive_list_tail<Nick> FIFO;
+
+ /** Sets of users in the whowas system
+ */
+ typedef TR1NS::unordered_map<std::string, WhoWas::Nick*, irc::insensitive, irc::StrHashComp> whowas_users;
+
+ /** Primary container, links nicknames tracked by WHOWAS to a list of records
+ */
+ whowas_users whowas;
+
+ /** List of nicknames in the order they were inserted into the map
+ */
+ FIFO whowas_fifo;
+
+ /** Max number of WhoWas entries per user.
+ */
+ unsigned int GroupSize;
+
+ /** Max number of cumulative user-entries in WhoWas.
+ * When max reached and added to, push out oldest entry FIFO style.
+ */
+ unsigned int MaxGroups;
+
+ /** Max seconds a user is kept in WhoWas before being pruned.
+ */
+ unsigned int MaxKeep;
+
+ /** Shrink all data structures to honor the current settings
+ */
+ void Prune();
+ };
+}
/** Handle /WHOWAS. These command handlers can be reloaded by the core,
* and handle basic RFC1459 commands. Commands within modules work
*/
class CommandWhowas : public Command
{
- private:
- /** Whowas container, contains a map of vectors of users tracked by WHOWAS
- */
- whowas_users whowas;
-
- /** Whowas container, contains a map of time_t to users tracked by WHOWAS
+ public:
+ /** Manager handling all whowas database related tasks
*/
- whowas_users_fifo whowas_fifo;
+ WhoWas::Manager manager;
- public:
CommandWhowas(Module* parent);
/** Handle command.
* @param parameters The parameters to the comamnd
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- void AddToWhoWas(User* user);
- std::string GetStats();
- void PruneWhoWas(time_t t);
- void MaintainWhoWas(time_t t);
- ~CommandWhowas();
};
-
-/** Used to hold WHOWAS information
- */
-class WhoWasGroup
-{
- public:
- /** Real host
- */
- std::string host;
- /** Displayed host
- */
- std::string dhost;
- /** Ident
- */
- std::string ident;
- /** Server name
- */
- std::string server;
- /** Fullname (GECOS)
- */
- std::string gecos;
- /** Signon time
- */
- time_t signon;
-
- /** Initialize this WhoWasFroup with a user
- */
- WhoWasGroup(User* user);
- /** Destructor
- */
- ~WhoWasGroup();
-};
-
-class WhoWasMaintainTimer : public Timer
-{
- public:
- WhoWasMaintainTimer(long interval)
- : Timer(interval, ServerInstance->Time(), true)
- {
- }
- virtual void Tick(time_t TIME);
-};
-
-#endif
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/**
+ * Some implementations of the C++11 standard library are incomplete so we use
+ * the implementation of the same types from C++ Technical Report 1 instead.
+ */
+#if defined _LIBCPP_VERSION || defined _WIN32
+# define TR1NS std
+# include <unordered_map>
+# include <type_traits>
+#else
+# define TR1NS std::tr1
+# include <tr1/unordered_map>
+# include <tr1/type_traits>
+#endif
+
+/**
+ * This macro enables the compile-time checking of printf format strings. This
+ * makes the compiler show a warning if the format of a printf arguments are
+ * incorrect.
+ */
+#if defined __clang__ || defined __GNUC__
+# define CUSTOM_PRINTF(stringpos, firstpos) __attribute__((format(printf, stringpos, firstpos)))
+#else
+# define CUSTOM_PRINTF(stringpos, firstpos)
+#endif
+
+/**
+ * These macros enable the use of the C++11 override control keywords in
+ * compilers which support them.
+ */
+#if __cplusplus >= 201103L
+# define HAS_CXX11_FINAL_OVERRIDE
+#elif defined __clang__
+# if __has_feature(cxx_override_control)
+# define HAS_CXX11_FINAL_OVERRIDE
+# endif
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+# if defined __GXX_EXPERIMENTAL_CXX0X__
+# define HAS_CXX11_FINAL_OVERRIDE
+# endif
+#elif _MSC_VER >= 1700
+# define HAS_CXX11_FINAL_OVERRIDE
+#endif
+
+#if defined HAS_CXX11_FINAL_OVERRIDE
+# define CXX11_FINAL final
+# define CXX11_OVERRIDE override
+#else
+# define CXX11_FINAL
+# define CXX11_OVERRIDE
+#endif
+
+/**
+ * These macros enable the detection of the C++11 variadic templates in
+ * compilers which support them.
+ */
+#if __cplusplus >= 201103L
+# define HAS_CXX11_VARIADIC_TEMPLATES
+#elif defined __clang__
+# if __has_feature(cxx_variadic_templates)
+# define HAS_CXX11_VARIADIC_TEMPLATES
+# endif
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+# if defined __GXX_EXPERIMENTAL_CXX0X__
+# define HAS_CXX11_VARIADIC_TEMPLATES
+# endif
+#elif _MSC_FULL_VER >= 170051025
+# define HAS_CXX11_VARIADIC_TEMPLATES
+#endif
+
+/**
+ * This macro allows methods to be marked as deprecated. To use this, wrap the
+ * method declaration in the header file with the macro.
+ */
+#if defined __clang__ || defined __GNUC__
+# define DEPRECATED_METHOD(function) function __attribute__((deprecated))
+#elif defined _MSC_VER
+# define DEPRECATED_METHOD(function) __declspec(deprecated) function
+#else
+# define DEPRECATED_METHOD(function) function
+#endif
+
+/**
+ * Windows is very different to UNIX so we have to wrap certain features in
+ * order to build on Windows correctly.
+ */
+#if defined _WIN32
+# include "inspircd_win32wrapper.h"
+# include "threadengines/threadengine_win32.h"
+#else
+# define ENTRYPOINT int main(int argc, char** argv)
+# define DllExport __attribute__ ((visibility ("default")))
+# define CoreExport __attribute__ ((visibility ("default")))
+# include <unistd.h>
+# include "threadengines/threadengine_pthread.h"
+#endif
*/
+#pragma once
+
struct fpos
{
std::string filename;
enum ParseFlags
{
- FLAG_USE_XML = 1,
+ FLAG_USE_COMPAT = 1,
FLAG_NO_EXEC = 2,
FLAG_NO_INC = 4
};
vars["quot"] = "\"";
vars["newline"] = vars["nl"] = "\n";
}
- bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = "");
- bool ParseExec(const std::string& name, int flags, const std::string& mandatory_tag = "");
+ bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = std::string(), bool isexec = false);
void DoInclude(ConfigTag* includeTag, int flags);
void DoReadFile(const std::string& key, const std::string& file, int flags, bool exec);
};
}
}
};
-
-
*/
-#ifndef INSPIRCD_CONFIGREADER
-#define INSPIRCD_CONFIGREADER
+#pragma once
#include <sstream>
#include <string>
/** Get the value of an option, using def if it does not exist */
std::string getString(const std::string& key, const std::string& def = "");
/** Get the value of an option, using def if it does not exist */
- long getInt(const std::string& key, long def = 0);
+ long getInt(const std::string& key, long def = 0, long min = LONG_MIN, long max = LONG_MAX);
/** Get the value of an option, using def if it does not exist */
double getFloat(const std::string& key, double def = 0);
/** Get the value of an option, using def if it does not exist */
bool getBool(const std::string& key, bool def = false);
+ /** Get the value in seconds of a duration that is in the user-friendly "1h2m3s" format,
+ * using a default value if it does not exist or is out of bounds.
+ * @param key The config key name
+ * @param def Default value (optional)
+ * @param min Minimum acceptable value (optional)
+ * @param max Maximum acceptable value (optional)
+ * @return The duration in seconds
+ */
+ long getDuration(const std::string& key, long def = 0, long min = LONG_MIN, long max = LONG_MAX);
+
/** Get the value of an option
* @param key The option to get
* @param value The location to store the value (unmodified if does not exist)
*/
bool readString(const std::string& key, std::string& value, bool allow_newline = false);
+ /** Check for an out of range value. If the value falls outside the boundaries a warning is
+ * logged and the value is corrected (set to def).
+ * @param key The key name, used in the warning message
+ * @param res The value to verify and modify if needed
+ * @param def The default value, res will be set to this if (min <= res <= max) doesn't hold true
+ * @param min Minimum accepted value for res
+ * @param max Maximum accepted value for res
+ */
+ void CheckRange(const std::string& key, long& res, long def, long min, long max);
+
std::string getTagLocation();
inline const std::vector<KeyVal>& getItems() const { return items; }
size_t MaxGecos;
/** Maximum away message length */
size_t MaxAway;
+ /** Maximum line length */
+ size_t MaxLine;
+ /** Maximum hostname length */
+ size_t MaxHost;
/** Creating the class initialises it to the defaults
* as in 1.1's ./configure script. Reading other values
* from the config will change these values.
*/
- ServerLimits() : NickMax(31), ChanMax(64), MaxModes(20), IdentMax(12), MaxQuit(255), MaxTopic(307), MaxKick(255), MaxGecos(128), MaxAway(200)
- {
- }
+ ServerLimits() : NickMax(31), ChanMax(64), MaxModes(20), IdentMax(12),
+ MaxQuit(255), MaxTopic(307), MaxKick(255), MaxGecos(128), MaxAway(200),
+ MaxLine(512), MaxHost(64) { }
};
struct CommandLineConf
*/
bool writelog;
- /** True if we have been told to run the testsuite from the commandline,
- * rather than entering the mainloop.
- */
- bool TestSuite;
-
/** Saved argc from startup
*/
int argc;
/** Saved argv from startup
*/
char** argv;
-
- std::string startup_log;
};
class CoreExport OperInfo : public refcountbase
/** Get a configuration item, searching in the oper, type, and class blocks (in that order) */
std::string getConfig(const std::string& key);
void init();
-
- inline const char* NameStr()
- {
- return irc::Spacify(name.c_str());
- }
};
/** This class holds the bulk of the runtime configuration for the ircd.
void CrossCheckConnectBlocks(ServerConfig* current);
public:
+ class ServerPaths
+ {
+ public:
+ /** Config path */
+ std::string Config;
+
+ /** Data path */
+ std::string Data;
+
+ /** Log path */
+ std::string Log;
+
+ /** Module path */
+ std::string Module;
+
+ ServerPaths()
+ : Config(INSPIRCD_CONFIG_PATH)
+ , Data(INSPIRCD_DATA_PATH)
+ , Log(INSPIRCD_LOG_PATH)
+ , Module(INSPIRCD_MODULE_PATH) { }
+
+ std::string PrependConfig(const std::string& fn) const { return FileSystem::ExpandPath(Config, fn); }
+ std::string PrependData(const std::string& fn) const { return FileSystem::ExpandPath(Data, fn); }
+ std::string PrependLog(const std::string& fn) const { return FileSystem::ExpandPath(Log, fn); }
+ std::string PrependModule(const std::string& fn) const { return FileSystem::ExpandPath(Module, fn); }
+ };
+
+ /** Holds a complete list of all connect blocks
+ */
+ typedef std::vector<reference<ConnectClass> > ClassVector;
+
+ /** Index of valid oper blocks and types
+ */
+ typedef std::map<std::string, reference<OperInfo> > OperIndex;
/** Get a configuration tag
* @param tag The name of the tag to get
*/
ServerLimits Limits;
+ /** Locations of various types of file (config, module, etc). */
+ ServerPaths Paths;
+
/** Configuration parsed from the command line.
*/
CommandLineConf cmdline;
*/
int c_ipv6_range;
- /** Max number of WhoWas entries per user.
- */
- int WhoWasGroupSize;
-
- /** Max number of cumulative user-entries in WhoWas.
- * When max reached and added to, push out oldest entry FIFO style.
- */
- int WhoWasMaxGroups;
-
- /** Max seconds a user is kept in WhoWas before being pruned.
- */
- int WhoWasMaxKeep;
-
/** Holds the server name of the local server
* as defined by the administrator.
*/
std::string ServerName;
- /** Notice to give to users when they are Xlined
+ /** Notice to give to users when they are banned by an XLine
*/
- std::string MoronBanner;
+ std::string XLineMessage;
/* Holds the network name the local server
* belongs to. This is an arbitary field defined
*/
std::string ServerDesc;
- /** Holds the admin's name, for output in
- * the /ADMIN command.
- */
- std::string AdminName;
-
- /** Holds the email address of the admin,
- * for output in the /ADMIN command.
- */
- std::string AdminEmail;
-
- /** Holds the admin's nickname, for output
- * in the /ADMIN command
- */
- std::string AdminNick;
-
- /** The admin-configured /DIE password
- */
- std::string diepass;
-
- /** The admin-configured /RESTART password
- */
- std::string restartpass;
-
- /** The hash method for *BOTH* the die and restart passwords.
- */
- std::string powerhash;
-
- /** The pathname and filename of the message of the
- * day file, as defined by the administrator.
- */
- std::string motd;
-
- /** The pathname and filename of the rules file,
- * as defined by the administrator.
- */
- std::string rules;
-
- /** The quit prefix in use, or an empty string
- */
- std::string PrefixQuit;
-
- /** The quit suffix in use, or an empty string
- */
- std::string SuffixQuit;
-
- /** The fixed quit message in use, or an empty string
- */
- std::string FixedQuit;
-
- /** The part prefix in use, or an empty string
- */
- std::string PrefixPart;
-
- /** The part suffix in use, or an empty string
- */
- std::string SuffixPart;
-
- /** The fixed part message in use, or an empty string
- */
- std::string FixedPart;
-
- /** The DNS server to use for DNS queries
- */
- std::string DNSServer;
-
/** Pretend disabled commands don't exist.
*/
bool DisabledDontExist;
*/
char DisabledCModes[64];
- /** The full path to the modules directory.
- * This is either set at compile time, or
- * overridden in the configuration file via
- * the \<path> tag.
- */
- std::string ModPath;
-
/** If set to true, then all opers on this server are
* shown with a generic 'is an IRC operator' line rather
* than the oper type. Oper types are still used internally.
*/
bool RestrictBannedUsers;
- /** If this is set to true, then mode lists (e.g
- * MODE \#chan b) are hidden from unprivileged
- * users.
- */
- bool HideModeLists[256];
-
/** The number of seconds the DNS subsystem
* will wait before timing out any request.
*/
*/
int MaxConn;
+ /** If we should check for clones during CheckClass() in AddUser()
+ * Setting this to false allows to not trigger on maxclones for users
+ * that may belong to another class after DNS-lookup is complete.
+ * It does, however, make the server spend more time on users we may potentially not want.
+ */
+ bool CCOnConnect;
+
/** The soft limit value assigned to the irc server.
* The IRC server will not allow more than this
* number of local users.
*/
ClassVector Classes;
- /** The 005 tokens of this server (ISUPPORT)
- * populated/repopulated upon loading or unloading
- * modules.
- */
- std::string data005;
-
- /** isupport strings
- */
- std::vector<std::string> isupport;
-
/** STATS characters in this list are available
* only to operators.
*/
*/
std::string CustomVersion;
- /** List of u-lined servers
- */
- std::map<irc::string, bool> ulines;
-
- /** Max banlist sizes for channels (the std::string is a glob)
- */
- std::map<std::string, int> maxbans;
-
- /** If set to true, no user DNS lookups are to be performed
- */
- bool NoUserDns;
-
/** If set to true, provide syntax hints for unknown commands
*/
bool SyntaxHints;
- /** If set to true, users appear to quit then rejoin when their hosts change.
- * This keeps clients synchronized properly.
- */
- bool CycleHosts;
-
/** If set to true, the CycleHosts mode change will be sourced from the user,
* rather than the server
*/
*/
bool FullHostInTopic;
- /** Oper block and type index.
- * For anonymous oper blocks (type only), prefix with a space.
+ /** Oper blocks keyed by their name
*/
OperIndex oper_blocks;
- /** Max channels per user
+ /** Oper types keyed by their name
+ */
+ OperIndex OperTypes;
+
+ /** Default value for <connect:maxchans>, deprecated in 2.2
*/
unsigned int MaxChans;
- /** Oper max channels per user
+ /** Default value for <oper:maxchans>, deprecated in 2.2
*/
unsigned int OperMaxChans;
/** Get server ID as string with required leading zeroes
*/
- const std::string& GetSID();
-
- /** Update the 005 vector
- */
- void Update005();
-
- /** Send the 005 numerics (ISUPPORT) to a user
- */
- void Send005(User* user);
+ const std::string& GetSID() const { return sid; }
/** Read the entire configuration into memory
* and initialize this class. All other methods
void Fill();
- /** Returns true if the given string starts with a windows drive letter
- */
- bool StartsWithWindowsDriveLetter(const std::string &path);
-
bool ApplyDisabledCommands(const std::string& data);
- /** Clean a filename, stripping the directories (and drives) from string.
- * @param name Directory to tidy
- * @return The cleaned filename
- */
- static const char* CleanFilename(const char* name);
-
- /** Check if a file exists.
- * @param file The full path to a file
- * @return True if the file exists and is readable.
+ /** Escapes a value for storage in a configuration key.
+ * @param str The string to escape.
+ * @param xml Are we using the XML config format?
*/
- static bool FileExists(const char* file);
+ static std::string Escape(const std::string& str, bool xml = true);
/** If this value is true, invites will bypass more than just +i
*/
/** If this value is true, snotices will not stack when repeats are sent
*/
bool NoSnoticeStack;
-
- /** If true, a "Welcome to <networkname>!" NOTICE will be sent to
- * connecting users
- */
- bool WelcomeNotice;
};
/** The background thread for config reading, so that reading from executable includes
bool IsDone() { return done; }
};
-#endif
+class CoreExport ConfigStatus
+{
+ public:
+ User* const srcuser;
+
+ ConfigStatus(User* user = NULL)
+ : srcuser(user)
+ {
+ }
+};
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CONSOLECOLORS_H
-#define CONSOLECOLORS_H
+
+#pragma once
#include <ostream>
}
#endif
-
-#endif
*/
-#ifndef CTABLES_H
-#define CTABLES_H
+#pragma once
/** Used to indicate command success codes
*/
*/
enum TranslateType
{
- TR_END, /* End of known parameters, everything after this is TR_TEXT */
TR_TEXT, /* Raw text, leave as-is */
TR_NICK, /* Nickname, translate to UUID for server->server */
TR_CUSTOM /* Custom translation handled by EncodeParameter/DecodeParameter */
*/
std::string serverdest;
+ /** For unicast, the destination Server
+ */
+ Server* server;
+
/** Create a RouteDescriptor
*/
RouteDescriptor(RouteType t, const std::string &d)
- : type(t), serverdest(d) { }
+ : type(t), serverdest(d), server(NULL) { }
+
+ RouteDescriptor(RouteType t, Server* srv)
+ : type(t), server(srv) { }
};
/** Do not route this command */
/** A structure that defines a command. Every command available
* in InspIRCd must be defined as derived from Command.
*/
-class CoreExport Command : public ServiceProvider
+class CoreExport CommandBase : public ServiceProvider
{
public:
/** User flags needed to execute the command or 0
*/
unsigned long use_count;
- /** used by /stats m
- */
- unsigned long total_bytes;
-
/** True if the command is disabled to non-opers
*/
bool disabled;
* @param maxpara Maximum number of parameters this command may have - extra parameters
* will be tossed into one last space-seperated param.
*/
- Command(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) :
+ CommandBase(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) :
ServiceProvider(me, cmd, SERVICE_COMMAND), flags_needed(0), min_params(minpara), max_params(maxpara),
- use_count(0), total_bytes(0), disabled(false), works_before_reg(false), allow_empty_last_param(true),
+ use_count(0), disabled(false), works_before_reg(false), allow_empty_last_param(true),
Penalty(1)
{
}
- /** Handle the command from a user.
- * @param parameters The parameters for the command.
- * @param user The user who issued the command.
- * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
- */
- virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0;
-
virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
return ROUTE_LOCALONLY;
return works_before_reg;
}
- virtual ~Command();
+ virtual ~CommandBase();
+};
+
+class CoreExport Command : public CommandBase
+{
+ public:
+ /** If true, the command will not be forwarded by the linking module even if it comes via ENCAP.
+ * Can be used to forward commands before their effects.
+ */
+ bool force_manual_route;
+
+ Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0)
+ : CommandBase(me, cmd, minpara, maxpara)
+ , force_manual_route(false)
+ {
+ }
+
+ /** Handle the command from a user.
+ * @param parameters The parameters for the command.
+ * @param user The user who issued the command.
+ * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure.
+ */
+ virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0;
+
+ /** Destructor
+ * Removes this command from the command parser
+ */
+ ~Command();
};
class CoreExport SplitCommand : public Command
translation.push_back(x5);translation.push_back(x6);translation.push_back(x7);
#define TRANSLATE8(x1,x2,x3,x4,x5,x6,x7,x8) translation.push_back(x1);translation.push_back(x2);translation.push_back(x3);translation.push_back(x4);\
translation.push_back(x5);translation.push_back(x6);translation.push_back(x7);translation.push_back(x8);
-
-#endif
*/
-#ifndef CULL_LIST_H
-#define CULL_LIST_H
+#pragma once
/**
* The CullList class is used to delete objects at the end of the main loop to
void Run();
};
-
-#endif
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-/*
-dns.h - dns library very very loosely based on
-firedns, Copyright (C) 2002 Ian Gulliver
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of version 2 of the GNU General Public License as
-published by the Free Software Foundation.
-
-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#ifndef DNS_H
-#define DNS_H
-
-#include "socket.h"
-#include "hashcomp.h"
-
-/**
- * Query and resource record types
- */
-enum QueryType
-{
- /** Uninitialized Query */
- DNS_QUERY_NONE = 0,
- /** 'A' record: an ipv4 address */
- DNS_QUERY_A = 1,
- /** 'CNAME' record: An alias */
- DNS_QUERY_CNAME = 5,
- /** 'PTR' record: a hostname */
- DNS_QUERY_PTR = 12,
- /** 'AAAA' record: an ipv6 address */
- DNS_QUERY_AAAA = 28,
-
- /** Force 'PTR' to use IPV4 scemantics */
- DNS_QUERY_PTR4 = 0xFFFD,
- /** Force 'PTR' to use IPV6 scemantics */
- DNS_QUERY_PTR6 = 0xFFFE
-};
-
-/**
- * Result status, used internally
- */
-class CoreExport DNSResult
-{
- public:
- /** Result ID
- */
- int id;
- /** Result body, a hostname or IP address
- */
- std::string result;
- /** Time-to-live value of the result
- */
- unsigned long ttl;
- /** The original request, a hostname or IP address
- */
- std::string original;
- /** The type of the request
- */
- QueryType type;
-
- /** Build a DNS result.
- * @param i The request ID
- * @param res The request result, a hostname or IP
- * @param timetolive The request time-to-live
- * @param orig The original request, a hostname or IP
- */
- DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig, QueryType qt = DNS_QUERY_NONE) : id(i), result(res), ttl(timetolive), original(orig), type(qt) { }
-};
-
-/**
- * Information on a completed lookup, used internally
- */
-typedef std::pair<unsigned char*, std::string> DNSInfo;
-
-/** Cached item stored in the query cache.
- */
-class CoreExport CachedQuery
-{
- public:
- /** The cached result data, an IP or hostname
- */
- std::string data;
- /** The type of result this is
- */
- QueryType type;
- /** The time when the item is due to expire
- */
- time_t expires;
-
- /** Build a cached query
- * @param res The result data, an IP or hostname
- * @param ttl The time-to-live value of the query result
- */
- CachedQuery(const std::string &res, QueryType qt, unsigned int ttl);
-
- /** Returns the number of seconds remaining before this
- * cache item has expired and should be removed.
- */
- int CalcTTLRemaining();
-};
-
-/** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs.
- */
-typedef nspace::hash_map<irc::string, CachedQuery, irc::hash> dnscache;
-
-/**
- * Error types that class Resolver can emit to its error method.
- */
-enum ResolverError
-{
- RESOLVER_NOERROR = 0,
- RESOLVER_NSDOWN = 1,
- RESOLVER_NXDOMAIN = 2,
- RESOLVER_BADIP = 3,
- RESOLVER_TIMEOUT = 4,
- RESOLVER_FORCEUNLOAD = 5
-};
-
-/**
- * Used internally to force PTR lookups to use a certain protocol scemantics,
- * e.g. x.x.x.x.in-addr.arpa for v4, and *.ip6.arpa for v6.
- */
-enum ForceProtocol
-{
- /** Forced to use ipv4 */
- PROTOCOL_IPV4 = 0,
- /** Forced to use ipv6 */
- PROTOCOL_IPV6 = 1
-};
-
-/**
- * The Resolver class is a high-level abstraction for resolving DNS entries.
- * It can do forward and reverse IPv4 lookups, and where IPv6 is supported, will
- * also be able to do those, transparent of protocols. Module developers must
- * extend this class via inheritence, and then insert a pointer to their derived
- * class into the core using Server::AddResolver(). Once you have done this,
- * the class will be able to receive callbacks. There are two callbacks which
- * can occur by calling virtual methods, one is a success situation, and the other
- * an error situation.
- */
-class CoreExport Resolver
-{
- protected:
- /**
- * Pointer to creator module (if any, or NULL)
- */
- ModuleRef Creator;
- /**
- * The input data, either a host or an IP address
- */
- std::string input;
- /**
- * True if a forward lookup is being performed, false if otherwise
- */
- QueryType querytype;
- /**
- * The DNS erver being used for lookups. If this is an empty string,
- * the value of ServerConfig::DNSServer is used instead.
- */
- std::string server;
- /**
- * The ID allocated to your lookup. This is a pseudo-random number
- * between 0 and 65535, a value of -1 indicating a failure.
- * The core uses this to route results to the correct objects.
- */
- int myid;
-
- /**
- * Cached result, if there is one
- */
- CachedQuery *CQ;
-
- /**
- * Time left before cache expiry
- */
- int time_left;
-
- public:
- /**
- * Initiate DNS lookup. Your class should not attempt to delete or free these
- * objects, as the core will do this for you. They must always be created upon
- * the heap using new, as you cannot be sure at what time they will be deleted.
- * Allocating them on the stack or attempting to delete them yourself could cause
- * the object to go 'out of scope' and cause a segfault in the core if the result
- * arrives at a later time.
- * @param source The IP or hostname to resolve
- * @param qt The query type to perform. Resolution of 'A', 'AAAA', 'PTR' and 'CNAME' records
- * is supported. Use one of the QueryType enum values to initiate this type of
- * lookup. Resolution of 'AAAA' ipv6 records is always supported, regardless of
- * wether InspIRCd is built with ipv6 support.
- * To look up reverse records, specify one of DNS_QUERY_PTR4 or DNS_QUERY_PTR6 depending
- * on the type of address you are looking up.
- * @param cached The constructor will set this boolean to true or false depending
- * on whether the DNS lookup you are attempting is cached (and not expired) or not.
- * If the value is cached, upon return this will be set to true, otherwise it will
- * be set to false. You should pass this value to InspIRCd::AddResolver(), which
- * will then influence the behaviour of the method and determine whether a cached
- * or non-cached result is obtained. The value in this variable is always correct
- * for the given request when the constructor exits.
- * @param creator See the note below.
- * @throw ModuleException This class may throw an instance of ModuleException, in the
- * event a lookup could not be allocated, or a similar hard error occurs such as
- * the network being down. This will also be thrown if an invalid IP address is
- * passed when resolving a 'PTR' record.
- *
- * NOTE: If you are instantiating your DNS lookup from a module, you should set the
- * value of creator to point at your Module class. This way if your module is unloaded
- * whilst lookups are in progress, they can be safely removed and your module will not
- * crash the server.
- */
- Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator);
-
- /**
- * The default destructor does nothing.
- */
- virtual ~Resolver();
-
- /**
- * When your lookup completes, this method will be called.
- * @param result The resulting DNS lookup, either an IP address or a hostname.
- * @param ttl The time-to-live value of the result, in the instance of a cached
- * result, this is the number of seconds remaining before refresh/expiry.
- * @param cached True if the result is a cached result, false if it was requested
- * from the DNS server.
- */
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) = 0;
-
- /**
- * If an error occurs (such as NXDOMAIN, no domain name found) then this method
- * will be called.
- * @param e A ResolverError enum containing the error type which has occured.
- * @param errormessage The error text of the error that occured.
- */
- virtual void OnError(ResolverError e, const std::string &errormessage);
-
- /**
- * Returns the id value of this class. This is primarily used by the core
- * to determine where in various tables to place a pointer to your class, but it
- * is safe to call and use this method.
- * As specified in RFC1035, each dns request has a 16 bit ID value, ranging
- * from 0 to 65535. If there is an issue and the core cannot send your request,
- * this method will return -1.
- */
- int GetId();
-
- /**
- * Returns the creator module, or NULL
- */
- Module* GetCreator();
-
- /**
- * If the result is a cached result, this triggers the objects
- * OnLookupComplete. This is done because it is not safe to call
- * the abstract virtual method from the constructor.
- */
- void TriggerCachedResult();
-};
-
-/** DNS is a singleton class used by the core to dispatch dns
- * requests to the dns server, and route incoming dns replies
- * back to Resolver objects, based upon the request ID. You
- * should never use this class yourself.
- */
-class CoreExport DNS : public EventHandler
-{
- private:
-
- /**
- * The maximum value of a dns request id,
- * 16 bits wide, 0xFFFF.
- */
- static const int MAX_REQUEST_ID = 0xFFFF;
-
- /**
- * Currently cached items
- */
- dnscache* cache;
-
- /** A timer which ticks every hour to remove expired
- * items from the DNS cache.
- */
- class CacheTimer* PruneTimer;
-
- /**
- * Build a dns packet payload
- */
- int MakePayload(const char* name, const QueryType rr, const unsigned short rr_class, unsigned char* payload);
-
- public:
-
- irc::sockets::sockaddrs myserver;
-
- /**
- * Currently active Resolver classes
- */
- Resolver* Classes[MAX_REQUEST_ID];
-
- /**
- * Requests that are currently 'in flight'
- */
- DNSRequest* requests[MAX_REQUEST_ID];
-
- /**
- * The port number DNS requests are made on,
- * and replies have as a source-port number.
- */
- static const int QUERY_PORT = 53;
-
- /**
- * Fill an rr (resource record) with data from input
- */
- static void FillResourceRecord(ResourceRecord* rr, const unsigned char* input);
-
- /**
- * Fill a header with data from input limited by a length
- */
- static void FillHeader(DNSHeader *header, const unsigned char *input, const int length);
-
- /**
- * Empty out a header into a data stream ready for transmission "on the wire"
- */
- static void EmptyHeader(unsigned char *output, const DNSHeader *header, const int length);
-
- /**
- * Start the lookup of an ipv4 from a hostname
- */
- int GetIP(const char* name);
-
- /**
- * Start lookup of a hostname from an ip, but
- * force a specific protocol to be used for the lookup
- * for example to perform an ipv6 reverse lookup.
- */
- int GetNameForce(const char *ip, ForceProtocol fp);
-
- /**
- * Start lookup of an ipv6 from a hostname
- */
- int GetIP6(const char *name);
-
- /**
- * Start lookup of a CNAME from another hostname
- */
- int GetCName(const char* alias);
-
- /**
- * Fetch the result string (an ip or host)
- * and/or an error message to go with it.
- */
- DNSResult GetResult();
-
- /**
- * Handle a SocketEngine read event
- * Inherited from EventHandler
- */
- void HandleEvent(EventType et, int errornum = 0);
-
- /**
- * Add a Resolver* to the list of active classes
- */
- bool AddResolverClass(Resolver* r);
-
- /**
- * Add a query to the list to be sent
- */
- DNSRequest* AddQuery(DNSHeader *header, int &id, const char* original);
-
- /**
- * The constructor initialises the dns socket,
- * and clears the request lists.
- */
- DNS();
-
- /**
- * Re-initialize the DNS subsystem.
- */
- void Rehash();
-
- /**
- * Destructor
- */
- ~DNS();
-
- /**
- * Turn an in6_addr into a .ip6.arpa domain
- */
- static void MakeIP6Int(char* query, const in6_addr *ip);
-
- /**
- * Clean out all dns resolvers owned by a particular
- * module, to make unloading a module safe if there
- * are dns requests currently in progress.
- */
- void CleanResolvers(Module* module);
-
- /** Return the cached value of an IP or hostname
- * @param source An IP or hostname to find in the cache.
- * @return A pointer to a CachedQuery if the item exists,
- * otherwise NULL.
- */
- CachedQuery* GetCache(const std::string &source);
-
- /** Delete a cached item from the DNS cache.
- * @param source An IP or hostname to remove
- */
- void DelCache(const std::string &source);
-
- /** Clear all items from the DNS cache immediately.
- */
- int ClearCache();
-
- /** Prune the DNS cache, e.g. remove all expired
- * items and rehash the cache buckets, but leave
- * items in the hash which are still valid.
- */
- int PruneCache();
-};
-
-#endif
-
*/
-#ifndef DLL_H
-#define DLL_H
+#pragma once
/** The DLLManager class is able to load a module file by filename,
* and locate its init_module symbol.
/** Get detailed version information from the module file */
std::string GetVersion();
};
-
-#endif
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "base.h"
+
+class CoreExport dynamic_reference_base : public interfacebase, public insp::intrusive_list_node<dynamic_reference_base>
+{
+ private:
+ std::string name;
+ void resolve();
+ protected:
+ ServiceProvider* value;
+ public:
+ ModuleRef creator;
+ dynamic_reference_base(Module* Creator, const std::string& Name);
+ ~dynamic_reference_base();
+ inline const std::string& GetProvider() { return name; }
+ void SetProvider(const std::string& newname);
+ void check();
+ operator bool() { return (value != NULL); }
+ static void reset_all();
+};
+
+inline void dynamic_reference_base::check()
+{
+ if (!value)
+ throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
+}
+
+template<typename T>
+class dynamic_reference : public dynamic_reference_base
+{
+ public:
+ dynamic_reference(Module* Creator, const std::string& Name)
+ : dynamic_reference_base(Creator, Name) {}
+
+ inline T* operator->()
+ {
+ check();
+ return static_cast<T*>(value);
+ }
+
+ T* operator*()
+ {
+ return operator->();
+ }
+};
+
+template<typename T>
+class dynamic_reference_nocheck : public dynamic_reference_base
+{
+ public:
+ dynamic_reference_nocheck(Module* Creator, const std::string& Name)
+ : dynamic_reference_base(Creator, Name) {}
+
+ T* operator->()
+ {
+ return static_cast<T*>(value);
+ }
+
+ T* operator*()
+ {
+ return operator->();
+ }
+};
+
+class ModeHandler;
+class ChanModeReference : public dynamic_reference_nocheck<ModeHandler>
+{
+ public:
+ ChanModeReference(Module* mod, const std::string& modename)
+ : dynamic_reference_nocheck<ModeHandler>(mod, "mode/" + modename) {}
+};
+
+class UserModeReference : public dynamic_reference_nocheck<ModeHandler>
+{
+ public:
+ UserModeReference(Module* mod, const std::string& modename)
+ : dynamic_reference_nocheck<ModeHandler>(mod, "umode/" + modename) {}
+};
*/
-#ifndef EXITCODE_H
-#define EXITCODE_H
+#pragma once
/** Valid exit codes to be used with InspIRCd::Exit()
*/
{
EXIT_STATUS_NOERROR = 0, /* No error */
EXIT_STATUS_DIE = 1, /* Operator issued DIE */
- EXIT_STATUS_FAILED_EXEC = 2, /* execv() failed */
- EXIT_STATUS_INTERNAL = 3, /* Internal error */
- EXIT_STATUS_CONFIG = 4, /* Config error */
- EXIT_STATUS_LOG = 5, /* Log file error */
- EXIT_STATUS_FORK = 6, /* fork() failed */
- EXIT_STATUS_ARGV = 7, /* Invalid program arguments */
- EXIT_STATUS_BIND = 8, /* Port binding failed on all ports */
- EXIT_STATUS_PID = 9, /* Couldn't write PID file */
- EXIT_STATUS_SOCKETENGINE = 10, /* Couldn't start socket engine */
- EXIT_STATUS_ROOT = 11, /* Refusing to start as root */
- EXIT_STATUS_DIETAG = 12, /* Found a die tag in the config file */
- EXIT_STATUS_MODULE = 13, /* Couldn't load a required module */
- EXIT_STATUS_CREATEPROCESS = 14, /* CreateProcess failed (windows) */
- EXIT_STATUS_SIGTERM = 15, /* Note: dont move this value. It corresponds with the value of #define SIGTERM. */
- EXIT_STATUS_BADHANDLER = 16, /* Bad command handler loaded */
- EXIT_STATUS_RSCH_FAILED = 17, /* Windows service specific failure, will name these later */
- EXIT_STATUS_UPDATESCM_FAILED = 18, /* Windows service specific failure, will name these later */
- EXIT_STATUS_CREATE_EVENT_FAILED = 19 /* Windows service specific failure, will name these later */
+ EXIT_STATUS_CONFIG = 2, /* Config error */
+ EXIT_STATUS_LOG = 3, /* Log file error */
+ EXIT_STATUS_FORK = 4, /* fork() failed */
+ EXIT_STATUS_ARGV = 5, /* Invalid program arguments */
+ EXIT_STATUS_PID = 6, /* Couldn't write PID file */
+ EXIT_STATUS_SOCKETENGINE = 7, /* Couldn't start socket engine */
+ EXIT_STATUS_ROOT = 8, /* Refusing to start as root */
+ EXIT_STATUS_MODULE = 9, /* Couldn't load a required module */
+ EXIT_STATUS_SIGTERM = 10 /* Received SIGTERM */
};
/** Array that maps exit codes (ExitStatus types) to
* human-readable strings to be shown on shutdown.
*/
extern const char * ExitCodes[];
-
-#endif
-
*/
-#ifndef EXTENSIBLE_H
-#define EXTENSIBLE_H
+#pragma once
#include <stdint.h>
* Holds all extensible metadata for the class.
*/
ExtensibleStore extensions;
+
+ /** True if this Extensible has been culled.
+ * A warning is generated if false on destruction.
+ */
+ unsigned int culled:1;
public:
/**
* Get the extension items for iteraton (i.e. for metadata sync during netburst)
virtual CullResult cull();
virtual ~Extensible();
void doUnhookExtensions(const std::vector<reference<ExtensionItem> >& toRemove);
+
+ /**
+ * Free all extension items attached to this Extensible
+ */
+ void FreeAllExtItems();
};
class CoreExport ExtensionManager
virtual void free(void* item) = 0;
};
-template<typename T>
+template <typename T, typename Del = stdalgo::defaultdeleter<T> >
class SimpleExtItem : public LocalExtItem
{
public:
{
T* ptr = new T(value);
T* old = static_cast<T*>(set_raw(container, ptr));
- delete old;
+ Del del;
+ del(old);
}
inline void set(Extensible* container, T* value)
{
T* old = static_cast<T*>(set_raw(container, value));
- delete old;
+ Del del;
+ del(old);
}
inline void unset(Extensible* container)
{
T* old = static_cast<T*>(unset_raw(container));
- delete old;
+ Del del;
+ del(old);
}
virtual void free(void* item)
{
- delete static_cast<T*>(item);
+ Del del;
+ del(static_cast<T*>(item));
}
};
std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
intptr_t get(const Extensible* container) const;
intptr_t set(Extensible* container, intptr_t value);
+ void unset(Extensible* container) { set(container, 0); }
void free(void* item);
};
void unset(Extensible* container);
void free(void* item);
};
-
-#endif
*/
-#ifndef FILELOGGER_H
-#define FILELOGGER_H
+#pragma once
#include "logger.h"
-/** Debug levels for use with InspIRCd::Log()
- * */
-enum DebugLevel
-{
- RAWIO = 5,
- DEBUG = 10,
- VERBOSE = 20,
- DEFAULT = 30,
- SPARSE = 40,
- NONE = 50
-};
-
-
-/* Forward declaration -- required */
-class InspIRCd;
-
/** A logging class which logs to a streamed file.
*/
class CoreExport FileLogStream : public LogStream
private:
FileWriter *f;
public:
- FileLogStream(int loglevel, FileWriter *fw);
+ FileLogStream(LogLevel loglevel, FileWriter *fw);
virtual ~FileLogStream();
- virtual void OnLog(int loglevel, const std::string &type, const std::string &msg);
+ virtual void OnLog(LogLevel loglevel, const std::string &type, const std::string &msg);
};
-
-#endif
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** Provides an easy method of reading a text file into memory. */
+class CoreExport FileReader
+{
+ /** The lines of text in the file. */
+ std::vector<std::string> lines;
+
+ /** File size in bytes. */
+ unsigned long totalSize;
+
+ public:
+ /** Initializes a new file reader. */
+ FileReader() : totalSize(0) { }
+
+ /** Initializes a new file reader and reads the specified file.
+ * @param filename The file to read into memory.
+ */
+ FileReader(const std::string& filename);
+
+ /** Loads a text file from disk.
+ * @param filename The file to read into memory.
+ * @throw CoreException The file can not be loaded.
+ */
+ void Load(const std::string& filename);
+
+ /** Retrieves the entire contents of the file cache as a single string. */
+ std::string GetString() const;
+
+ /** Retrieves the entire contents of the file cache as a vector of strings. */
+ const std::vector<std::string>& GetVector() const { return lines; }
+
+ /** Retrieves the total size in bytes of the file. */
+ unsigned long TotalSize() const { return totalSize; }
+};
+
+/** Implements methods for file system access */
+class CoreExport FileSystem
+{
+private:
+ FileSystem() { }
+
+public:
+ /** Expands a path fragment to a full path.
+ * @param base The base path to expand from
+ * @param fragment The path fragment to expand on top of base.
+ */
+ static std::string ExpandPath(const std::string& base, const std::string& fragment);
+
+ /**
+ * Checks whether a file with the specified name exists on the filesystem.
+ * @param path The path to a file.
+ * @return True if the file exists; otherwise, false.
+ */
+ static bool FileExists(const std::string& path);
+
+ /** Gets the file name segment of a path.
+ * @param path The path to extract the file name from.
+ * @return The file name segment of a path.
+ */
+ static std::string GetFileName(const std::string& path);
+
+ /** Determines whether the given path starts with a Windows drive letter.
+ * @param path The path to validate.
+ * @returns True if the path begins with a Windows drive letter; otherwise, false.
+ */
+ static bool StartsWithWindowsDriveLetter(const std::string& path);
+};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef INSPIRCD_HASHMAP_H
-#define INSPIRCD_HASHMAP_H
-
- /** Where hash_map is varies from compiler to compiler
- * as it is not standard unless we have tr1.
- *
- * TODO: in 2.2 if we drop support for libstdc++ older than 3.4.7 and GCC older
- * than 4.1 this can be cleaned up massively.
- */
- #if !defined _LIBCPP_VERSION && !defined _WIN32
- #if !defined __GLIBCXX__ || __GLIBCXX__ > 20060309
- // GCC4+ has deprecated hash_map and uses tr1. But of course, uses a different include to MSVC. FOR FUCKS SAKE.
- #include <tr1/unordered_map>
- #define HAS_TR1_UNORDERED
- #define HASHMAP_DEPRECATED
- #define hash_map unordered_map
- #define nspace std::tr1
- #define BEGIN_HASHMAP_NAMESPACE namespace std { namespace tr1 {
- #define END_HASHMAP_NAMESPACE } }
- #else
- #include <ext/hash_map>
- /** Oddball linux namespace for hash_map */
- #define nspace __gnu_cxx
- #define BEGIN_HASHMAP_NAMESPACE namespace nspace {
- #define END_HASHMAP_NAMESPACE }
- #endif
- #else
- #include <unordered_map>
- #define HAS_TR1_UNORDERED
- #define HASHMAP_DEPRECATED
- #define hash_map unordered_map
- #define nspace std
- #define BEGIN_HASHMAP_NAMESPACE namespace std {
- #define END_HASHMAP_NAMESPACE }
- #endif
-
-#endif
*/
-#ifndef HASHCOMP_H
-#define HASHCOMP_H
+#pragma once
#include <cstring>
#include <string>
#include <deque>
#include <map>
#include <set>
-#include "hash_map.h"
+#include "inspircd.h"
/*******************************************************
* This file contains classes and templates that deal
bool operator()(const std::string& s1, const std::string& s2) const;
};
+ struct insensitive
+ {
+ size_t CoreExport operator()(const std::string &s) const;
+ };
+
+ struct insensitive_swo
+ {
+ bool CoreExport operator()(const std::string& a, const std::string& b) const;
+ };
+
/** The irc_char_traits class is used for RFC-style comparison of strings.
* This class is used to implement irc::string, a case-insensitive, RFC-
* comparing string class.
*/
- struct irc_char_traits : std::char_traits<char> {
-
+ struct CoreExport irc_char_traits : public std::char_traits<char>
+ {
/** Check if two chars match.
* @param c1st First character
* @param c2nd Second character
* @return similar to strcmp, zero for equal, less than zero for str1
* being less and greater than zero for str1 being greater than str2.
*/
- static CoreExport int compare(const char* str1, const char* str2, size_t n);
+ static int compare(const char* str1, const char* str2, size_t n);
/** Find a char within a string up to position n.
* @param s1 String to find in
* @param c Character to search for
* @return Pointer to the first occurance of c in s1
*/
- static CoreExport const char* find(const char* s1, int n, char c);
+ static const char* find(const char* s1, int n, char c);
};
- /** Compose a hex string from raw data.
- * @param raw The raw data to compose hex from
- * @param rawsz The size of the raw data buffer
- * @return The hex string.
- */
- CoreExport std::string hex(const unsigned char *raw, size_t rawsz);
-
/** This typedef declares irc::string based upon irc_char_traits.
*/
typedef std::basic_string<char, irc_char_traits, std::allocator<char> > string;
- /** irc::stringjoiner joins string lists into a string, using
- * the given seperator string.
- * This class can join a vector of std::string, a deque of
- * std::string, or a const char* const* array, using overloaded
- * constructors.
+ /** Joins the contents of a vector to a string.
+ * @param sequence Zero or more items to join.
+ * @separator The character to place between the items.
*/
- class CoreExport stringjoiner
- {
- private:
+ std::string CoreExport stringjoiner(const std::vector<std::string>& sequence, char separator = ' ');
- /** Output string
+ /** irc::sepstream allows for splitting token seperated lists.
+ * Each successive call to sepstream::GetToken() returns
+ * the next token, until none remain, at which point the method returns
+ * an empty string.
+ */
+ class CoreExport sepstream
+ {
+ protected:
+ /** Original string.
*/
- std::string joined;
-
+ std::string tokens;
+ /** Separator value
+ */
+ char sep;
+ /** Current string position
+ */
+ size_t pos;
+ /** If set then GetToken() can return an empty string
+ */
+ bool allow_empty;
public:
-
- /** Join elements of a vector, between (and including) begin and end
- * @param seperator The string to seperate values with
- * @param sequence One or more items to seperate
- * @param begin The starting element in the sequence to be joined
- * @param end The ending element in the sequence to be joined
+ /** Create a sepstream and fill it with the provided data
*/
- stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end);
+ sepstream(const std::string &source, char separator, bool allowempty = false);
- /** Join elements of a deque, between (and including) begin and end
- * @param seperator The string to seperate values with
- * @param sequence One or more items to seperate
- * @param begin The starting element in the sequence to be joined
- * @param end The ending element in the sequence to be joined
+ /** Fetch the next token from the stream
+ * @param token The next token from the stream is placed here
+ * @return True if tokens still remain, false if there are none left
*/
- stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end);
+ bool GetToken(std::string& token);
- /** Join elements of an array of char arrays, between (and including) begin and end
- * @param seperator The string to seperate values with
- * @param sequence One or more items to seperate
- * @param begin The starting element in the sequence to be joined
- * @param end The ending element in the sequence to be joined
+ /** Fetch the entire remaining stream, without tokenizing
+ * @return The remaining part of the stream
*/
- stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end);
+ const std::string GetRemaining();
- /** Get the joined sequence
- * @return A reference to the joined string
+ /** Returns true if the end of the stream has been reached
+ * @return True if the end of the stream has been reached, otherwise false
*/
- std::string& GetJoined();
+ bool StreamEnd();
};
- /** irc::modestacker stacks mode sequences into a list.
- * It can then reproduce this list, clamped to a maximum of MAXMODES
- * values per line.
+ /** A derived form of sepstream, which seperates on commas
*/
- class CoreExport modestacker
+ class CoreExport commasepstream : public sepstream
{
- private:
- /** The mode sequence and its parameters
- */
- std::deque<std::string> sequence;
-
- /** True if the mode sequence is initially adding
- * characters, false if it is initially removing
- * them
- */
- bool adding;
public:
-
- /** Construct a new modestacker.
- * @param add True if the stack is adding modes,
- * false if it is removing them
- */
- modestacker(bool add);
-
- /** Push a modeletter and its parameter onto the stack.
- * No checking is performed as to if this mode actually
- * requires a parameter. If you stack invalid mode
- * sequences, they will be tidied if and when they are
- * passed to a mode parser.
- * @param modeletter The mode letter to insert
- * @param parameter The parameter for the mode
+ /** Initialize with comma separator
*/
- void Push(char modeletter, const std::string ¶meter);
-
- /** Push a modeletter without parameter onto the stack.
- * No checking is performed as to if this mode actually
- * requires a parameter. If you stack invalid mode
- * sequences, they will be tidied if and when they are
- * passed to a mode parser.
- * @param modeletter The mode letter to insert
- */
- void Push(char modeletter);
-
- /** Push a '+' symbol onto the stack.
- */
- void PushPlus();
+ commasepstream(const std::string &source, bool allowempty = false) : sepstream(source, ',', allowempty)
+ {
+ }
+ };
- /** Push a '-' symbol onto the stack.
- */
- void PushMinus();
-
- /** Return zero or more elements which form the
- * mode line. This will be clamped to a max of
- * MAXMODES items (MAXMODES-1 mode parameters and
- * one mode sequence string), and max_line_size
- * characters. As specified below, this function
- * should be called in a loop until it returns zero,
- * indicating there are no more modes to return.
- * @param result The vector to populate. This will not
- * be cleared before it is used.
- * @param max_line_size The maximum size of the line
- * to build, in characters, seperate to MAXMODES.
- * @return The number of elements in the deque.
- * The function should be called repeatedly until it
- * returns 0, in case there are multiple lines of
- * mode changes to be obtained.
+ /** A derived form of sepstream, which seperates on spaces
+ */
+ class CoreExport spacesepstream : public sepstream
+ {
+ public:
+ /** Initialize with space separator
*/
- int GetStackedLine(std::vector<std::string> &result, int max_line_size = 360);
-
- /** deprecated compatability interface - TODO remove */
- int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360) {
- std::vector<std::string> r;
- int n = GetStackedLine(r, max_line_size);
- result.clear();
- result.insert(result.end(), r.begin(), r.end());
- return n;
+ spacesepstream(const std::string &source, bool allowempty = false) : sepstream(source, ' ', allowempty)
+ {
}
};
* list will be ":item". This is to allow for parsing 'source' fields
* from data.
*/
- class CoreExport tokenstream
+ class CoreExport tokenstream : private spacesepstream
{
- private:
-
- /** Original string
- */
- std::string tokens;
-
- /** Last position of a seperator token
- */
- std::string::iterator last_starting_position;
-
- /** Current string position
- */
- std::string::iterator n;
-
- /** True if the last value was an ending value
- */
- bool last_pushed;
public:
-
/** Create a tokenstream and fill it with the provided data
*/
tokenstream(const std::string &source);
- /** Destructor
- */
- ~tokenstream();
-
/** Fetch the next token from the stream as a std::string
* @param token The next token available, or an empty string if none remain
* @return True if tokens are left to be read, false if the last token was just retrieved.
bool GetToken(long &token);
};
- /** irc::sepstream allows for splitting token seperated lists.
- * Each successive call to sepstream::GetToken() returns
- * the next token, until none remain, at which point the method returns
- * an empty string.
- */
- class CoreExport sepstream
- {
- private:
- /** Original string.
- */
- std::string tokens;
- /** Last position of a seperator token
- */
- std::string::iterator last_starting_position;
- /** Current string position
- */
- std::string::iterator n;
- /** Seperator value
- */
- char sep;
- public:
- /** Create a sepstream and fill it with the provided data
- */
- sepstream(const std::string &source, char seperator);
-
- /** Destructor
- */
- virtual ~sepstream();
-
- /** Fetch the next token from the stream
- * @param token The next token from the stream is placed here
- * @return True if tokens still remain, false if there are none left
- */
- virtual bool GetToken(std::string &token);
-
- /** Fetch the entire remaining stream, without tokenizing
- * @return The remaining part of the stream
- */
- virtual const std::string GetRemaining();
-
- /** Returns true if the end of the stream has been reached
- * @return True if the end of the stream has been reached, otherwise false
- */
- virtual bool StreamEnd();
- };
-
- /** A derived form of sepstream, which seperates on commas
- */
- class CoreExport commasepstream : public sepstream
- {
- public:
- /** Initialize with comma seperator
- */
- commasepstream(const std::string &source) : sepstream(source, ',')
- {
- }
- };
-
- /** A derived form of sepstream, which seperates on spaces
- */
- class CoreExport spacesepstream : public sepstream
- {
- public:
- /** Initialize with space seperator
- */
- spacesepstream(const std::string &source) : sepstream(source, ' ')
- {
- }
- };
-
/** The portparser class seperates out a port range into integers.
* A port range may be specified in the input string in the form
* "6660,6661,6662-6669,7020". The end of the stream is indicated by
long GetToken();
};
- /** Turn _ characters in a string into spaces
- * @param n String to translate
- * @return The new value with _ translated to space.
- */
- CoreExport const char* Spacify(const char* n);
-
struct hash
{
/** Hash an irc::string using RFC1459 case sensitivity rules
return str;
}
-
-/** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of
- * \#ifdefs we'll just do it all at once. Except, of course, with TR1, when it's the same as GCC.
- */
-BEGIN_HASHMAP_NAMESPACE
-
- /** Hashing function to hash irc::string
- */
-#if defined(_WIN32) && !defined(HAS_TR1_UNORDERED)
- template<> class CoreExport hash_compare<irc::string, std::less<irc::string> >
- {
- public:
- enum { bucket_size = 4, min_buckets = 8 }; /* Got these numbers from the CRT source, if anyone wants to change them feel free. */
-
- /** Compare two irc::string values for hashing in hash_map
- */
- bool operator()(const irc::string & s1, const irc::string & s2) const
- {
- if(s1.length() != s2.length()) return true;
- return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
- }
-
- /** Hash an irc::string value for hash_map
- */
- size_t operator()(const irc::string & s) const;
- };
-
- template<> class CoreExport hash_compare<std::string, std::less<std::string> >
- {
- public:
- enum { bucket_size = 4, min_buckets = 8 }; /* Again, from the CRT source */
-
- /** Compare two std::string values for hashing in hash_map
- */
- bool operator()(const std::string & s1, const std::string & s2) const
- {
- if(s1.length() != s2.length()) return true;
- return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0);
- }
-
- /** Hash a std::string using RFC1459 case sensitivity rules
- * @param s A string to hash
- * @return The hash value
- */
- size_t operator()(const std::string & s) const;
- };
-#else
-
- /* XXX FIXME: Implement a hash function overriding std::string's that works with TR1! */
-
-#ifdef HASHMAP_DEPRECATED
- struct insensitive
-#else
- CoreExport template<> struct hash<std::string>
-#endif
- {
- size_t CoreExport operator()(const std::string &s) const;
- };
-
-#endif
-
- /** Convert a string to lower case respecting RFC1459
- * @param n A string to lowercase
- */
- void strlower(char *n);
-
-END_HASHMAP_NAMESPACE
-
-#endif
*/
-#ifndef INSPIRCD_H
-#define INSPIRCD_H
+#pragma once
-#define _FILE_OFFSET_BITS 64
-#ifndef _LARGEFILE_SOURCE
-#define _LARGEFILE_SOURCE
-#endif
-
-#ifndef _WIN32
-#define DllExport
-#define CoreExport
-#else
-#include "inspircd_win32wrapper.h"
-/** Windows defines these already */
-#undef ERROR
-#endif
-
-#ifdef __GNUC__
-#define CUSTOM_PRINTF(STRING, FIRST) __attribute__((format(printf, STRING, FIRST)))
-#else
-#define CUSTOM_PRINTF(STRING, FIRST)
-#endif
-
-// Required system headers.
+#include <climits>
+#include <cmath>
#include <csignal>
-#include <ctime>
#include <cstdarg>
-#include <algorithm>
-#include <cmath>
-#include <cstring>
-#include <climits>
#include <cstdio>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
+#include <cstring>
+#include <ctime>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <list>
+#include <algorithm>
+#include <bitset>
#include <deque>
+#include <list>
#include <map>
-#include <bitset>
#include <set>
-#include <time.h>
-#include "inspircd_config.h"
-#include "inspircd_version.h"
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "intrusive_list.h"
+#include "compat.h"
+#include "aligned_storage.h"
#include "typedefs.h"
-#include "consolecolors.h"
+#include "stdalgo.h"
CoreExport extern InspIRCd* ServerInstance;
+/** Base class for manager classes that are still accessed using -> but are no longer pointers
+ */
+template <typename T>
+struct fakederef
+{
+ T* operator->()
+ {
+ return static_cast<T*>(this);
+ }
+};
+
+#include "config.h"
+#include "dynref.h"
+#include "consolecolors.h"
#include "caller.h"
#include "cull_list.h"
#include "extensible.h"
+#include "fileutils.h"
#include "numerics.h"
#include "uid.h"
+#include "server.h"
#include "users.h"
#include "channels.h"
#include "timer.h"
#include "configreader.h"
#include "inspstring.h"
#include "protocol.h"
-
-#ifndef PATH_MAX
-#warning Potentially broken system, PATH_MAX undefined
-#define PATH_MAX 4096
-#endif
-
-/**
- * Used to define the maximum number of parameters a command may have.
- */
-#define MAXPARAMETERS 127
-
-/** Returned by some functions to indicate failure.
- */
-#define ERROR -1
-
-/** Support for librodent -
- * see http://www.chatspike.net/index.php?z=64
- */
-#define ETIREDHAMSTERS EAGAIN
+#include "bancache.h"
+#include "isupportmanager.h"
/** Template function to convert any input type to std::string
*/
template<typename T> inline std::string ConvNumeric(const T &in)
{
- if (in == 0) return "0";
- char res[MAXBUF];
- char* out = res;
+ if (in == 0)
+ return "0";
T quotient = in;
- while (quotient) {
- *out = "0123456789"[ std::abs( (long)quotient % 10 ) ];
- ++out;
+ std::string out;
+ while (quotient)
+ {
+ out += "0123456789"[ std::abs( (long)quotient % 10 ) ];
quotient /= 10;
}
if (in < 0)
- *out++ = '-';
- *out = 0;
- std::reverse(res,out);
- return res;
+ out += '-';
+ std::reverse(out.begin(), out.end());
+ return out;
}
/** Template function to convert any input type to std::string
return atol(tmp.str().c_str());
}
+inline uint64_t ConvToUInt64(const std::string& in)
+{
+ uint64_t ret;
+ std::istringstream tmp(in);
+ if (!(tmp >> ret))
+ return 0;
+ return ret;
+}
+
/** This class contains various STATS counters
* It is used by the InspIRCd class, which internally
* has an instance of it.
public:
/** Number of accepted connections
*/
- unsigned long statsAccept;
+ unsigned long Accept;
/** Number of failed accepts
*/
- unsigned long statsRefused;
+ unsigned long Refused;
/** Number of unknown commands seen
*/
- unsigned long statsUnknown;
+ unsigned long Unknown;
/** Number of nickname collisions handled
*/
- unsigned long statsCollisions;
+ unsigned long Collisions;
/** Number of DNS queries sent out
*/
- unsigned long statsDns;
+ unsigned long Dns;
/** Number of good DNS replies received
* NOTE: This may not tally to the number sent out,
* due to timeouts and other latency issues.
*/
- unsigned long statsDnsGood;
+ unsigned long DnsGood;
/** Number of bad (negative) DNS replies received
* NOTE: This may not tally to the number sent out,
* due to timeouts and other latency issues.
*/
- unsigned long statsDnsBad;
+ unsigned long DnsBad;
/** Number of inbound connections seen
*/
- unsigned long statsConnects;
+ unsigned long Connects;
/** Total bytes of data transmitted
*/
- unsigned long statsSent;
+ unsigned long Sent;
/** Total bytes of data received
*/
- unsigned long statsRecv;
+ unsigned long Recv;
#ifdef _WIN32
/** Cpu usage at last sample
*/
/** The constructor initializes all the counts to zero
*/
serverstats()
- : statsAccept(0), statsRefused(0), statsUnknown(0), statsCollisions(0), statsDns(0),
- statsDnsGood(0), statsDnsBad(0), statsConnects(0), statsSent(0), statsRecv(0)
+ : Accept(0), Refused(0), Unknown(0), Collisions(0), Dns(0),
+ DnsGood(0), DnsBad(0), Connects(0), Sent(0), Recv(0)
{
}
};
-DEFINE_HANDLER2(IsNickHandler, bool, const char*, size_t);
+DEFINE_HANDLER1(IsNickHandler, bool, const std::string&);
DEFINE_HANDLER2(GenRandomHandler, void, char*, size_t);
-DEFINE_HANDLER1(IsIdentHandler, bool, const char*);
-DEFINE_HANDLER1(FloodQuitUserHandler, void, User*);
-DEFINE_HANDLER2(IsChannelHandler, bool, const char*, size_t);
-DEFINE_HANDLER1(IsSIDHandler, bool, const std::string&);
-DEFINE_HANDLER1(RehashHandler, void, const std::string&);
+DEFINE_HANDLER1(IsIdentHandler, bool, const std::string&);
+DEFINE_HANDLER1(IsChannelHandler, bool, const std::string&);
DEFINE_HANDLER3(OnCheckExemptionHandler, ModResult, User*, Channel*, const std::string&);
-class TestSuite;
-
/** The main class of the irc server.
* This class contains instances of all the other classes in this software.
* Amongst other things, it contains a ModeParser, a DNS object, a CommandParser
class CoreExport InspIRCd
{
private:
- /** Holds the current UID. Used to generate the next one.
- */
- char current_uid[UUID_LENGTH];
-
/** Set up the signal handlers
*/
void SetSignals();
*/
bool DaemonSeed();
- /** Iterate the list of BufferedSocket objects, removing ones which have timed out
- * @param TIME the current time
- */
- void DoSocketTimeouts(time_t TIME);
-
- /** Increments the current UID by one.
- */
- void IncrementUID(int pos);
-
- /** Perform background user events such as PING checks
- */
- void DoBackgroundUserStuff();
-
- /** Returns true when all modules have done pre-registration checks on a user
- * @param user The user to verify
- * @return True if all modules have finished checking this user
- */
- bool AllModulesReportReady(LocalUser* user);
-
/** The current time, updated in the mainloop
*/
struct timespec TIME;
*/
char ReadBuffer[65535];
+ /** Check we aren't running as root, and exit if we are
+ * with exit code EXIT_STATUS_ROOT.
+ */
+ void CheckRoot();
+
public:
+ UIDGenerator UIDGen;
+
/** Global cull list, will be processed on next iteration
*/
CullList GlobalCulls;
IsNickHandler HandleIsNick;
IsIdentHandler HandleIsIdent;
- FloodQuitUserHandler HandleFloodQuitUser;
OnCheckExemptionHandler HandleOnCheckExemption;
IsChannelHandler HandleIsChannel;
- IsSIDHandler HandleIsSID;
- RehashHandler HandleRehash;
GenRandomHandler HandleGenRandom;
/** Globally accessible fake user record. This is used to force mode changes etc across s2s, etc.. bit ugly, but.. better than how this was done in 1.1
*/
FakeUser* FakeClient;
- /** Returns the next available UID for this server.
- */
- std::string GetUID();
-
- static const char LogHeader[];
-
/** Find a user in the UUID hash
* @param uid The UUID to find
* @return A pointer to the user, or NULL if the user does not exist
*/
User* FindUUID(const std::string &uid);
- /** Find a user in the UUID hash
- * @param uid The UUID to find
- * @return A pointer to the user, or NULL if the user does not exist
- */
- User* FindUUID(const char *uid);
-
- /** Build the ISUPPORT string by triggering all modules On005Numeric events
- */
- void BuildISupport();
-
/** Time this ircd was booted
*/
time_t startup_time;
/** Mode handler, handles mode setting and removal
*/
- ModeParser* Modes;
+ ModeParser Modes;
/** Command parser, handles client to server commands
*/
- CommandParser* Parser;
-
- /** Socket engine, handles socket activity events
- */
- SocketEngine* SE;
+ CommandParser Parser;
/** Thread engine, Handles threading where required
*/
- ThreadEngine* Threads;
+ ThreadEngine Threads;
/** The thread/class used to read config files in REHASH and on startup
*/
/** LogManager handles logging.
*/
- LogManager *Logs;
+ LogManager Logs;
/** ModuleManager contains everything related to loading/unloading
* modules.
*/
- ModuleManager* Modules;
+ ModuleManager Modules;
/** BanCacheManager is used to speed up checking of restrictions on connection
* to the IRCd.
*/
- BanCacheManager *BanCache;
+ BanCacheManager BanCache;
/** Stats class, holds miscellaneous stats counters
*/
- serverstats* stats;
+ serverstats stats;
/** Server Config class, holds configuration file data
*/
/** Snomask manager - handles routing of snomask messages
* to opers.
*/
- SnomaskManager* SNO;
-
- /** DNS class, provides resolver facilities to the core and modules
- */
- DNS* Res;
+ SnomaskManager SNO;
/** Timer manager class, triggers Timer timer events
*/
- TimerManager* Timers;
+ TimerManager Timers;
/** X-Line manager. Handles G/K/Q/E line setting, removal and matching
*/
/** User manager. Various methods and data associated with users.
*/
- UserManager *Users;
+ UserManager Users;
/** Channel list, a hash_map containing all channels XXX move to channel manager class
*/
- chan_hash* chanlist;
+ chan_hash chanlist;
/** List of the open ports
*/
*/
ProtocolInterface* PI;
- /** Holds extensible for user nickforced
+ /** Default implementation of the ProtocolInterface, does nothing
*/
- LocalIntExt NICKForced;
+ ProtocolInterface DefaultProtocolInterface;
/** Holds extensible for user operquit
*/
- LocalStringExt OperQuit;
+ StringExtItem OperQuit;
+
+ /** Manages the generation and transmission of ISUPPORT. */
+ ISupportManager ISupport;
/** Get the current time
* Because this only calls time() once every time around the mainloop,
*/
int BindPorts(FailedPortList &failed_ports);
- /** Binds a socket on an already open file descriptor
- * @param sockfd A valid file descriptor of an open socket
- * @param port The port number to bind to
- * @param addr The address to bind to (IP only)
- * @param dolisten Should this port be listened on?
- * @return True if the port was bound successfully
- */
- bool BindSocket(int sockfd, int port, const char* addr, bool dolisten = true);
-
- /** Gets the GECOS (description) field of the given server.
- * If the servername is not that of the local server, the name
- * is passed to handling modules which will attempt to determine
- * the GECOS that bleongs to the given servername.
- * @param servername The servername to find the description of
- * @return The description of this server, or of the local server
- */
- std::string GetServerDescription(const std::string& servername);
-
/** Find a user in the nick hash.
* If the user cant be found in the nick hash check the uuid hash
* @param nick The nickname to find
*/
User* FindNick(const std::string &nick);
- /** Find a user in the nick hash.
- * If the user cant be found in the nick hash check the uuid hash
- * @param nick The nickname to find
- * @return A pointer to the user, or NULL if the user does not exist
- */
- User* FindNick(const char* nick);
-
- /** Find a user in the nick hash ONLY
- */
- User* FindNickOnly(const char* nick);
-
/** Find a user in the nick hash ONLY
*/
User* FindNickOnly(const std::string &nick);
*/
Channel* FindChan(const std::string &chan);
- /** Find a channel in the channels hash
- * @param chan The channel to find
- * @return A pointer to the channel, or NULL if the channel does not exist
- */
- Channel* FindChan(const char* chan);
-
- /** Check we aren't running as root, and exit if we are
- * @return Depending on the configuration, this function may never return
- */
- void CheckRoot();
-
- /** Determine the right path for, and open, the logfile
- * @param argv The argv passed to main() initially, used to calculate program path
- * @param argc The argc passed to main() initially, used to calculate program path
- * @return True if the log could be opened, false if otherwise
+ /** Get a hash map containing all channels, keyed by their name
+ * @return A hash map mapping channel names to Channel pointers
*/
- bool OpenLog(char** argv, int argc);
+ chan_hash& GetChans() { return chanlist; }
/** Return true if a channel name is valid
* @param chname A channel name to verify
* @return True if the name is valid
*/
- caller2<bool, const char*, size_t> IsChannel;
+ caller1<bool, const std::string&> IsChannel;
/** Return true if str looks like a server ID
- * @param string to check against
+ * @param sid string to check against
*/
- caller1<bool, const std::string&> IsSID;
-
- /** Rehash the local server
- */
- caller1<void, const std::string&> Rehash;
+ static bool IsSID(const std::string& sid);
/** Handles incoming signals after being set
* @param signal the signal recieved
*/
static void QuickExit(int status);
- /** Return a count of channels on the network
- * @return The number of channels
- */
- long ChannelCount();
+ /** Formats the input string with the specified arguments.
+ * @param formatString The string to format
+ * @param ... A variable number of format arguments.
+ * @return The formatted string
+ */
+ static const char* Format(const char* formatString, ...) CUSTOM_PRINTF(1, 2);
+ static const char* Format(va_list &vaList, const char* formatString) CUSTOM_PRINTF(2, 0);
/** Send an error notice to all local users, opered and unopered
* @param s The error string to send
* @param n A nickname to verify
* @return True if the nick is valid
*/
- caller2<bool, const char*, size_t> IsNick;
+ caller1<bool, const std::string&> IsNick;
/** Return true if an ident is valid
* @param An ident to verify
* @return True if the ident is valid
*/
- caller1<bool, const char*> IsIdent;
-
- /** Add a dns Resolver class to this server's active set
- * @param r The resolver to add
- * @param cached If this value is true, then the cache will
- * be searched for the DNS result, immediately. If the value is
- * false, then a request will be sent to the nameserver, and the
- * result will not be immediately available. You should usually
- * use the boolean value which you passed to the Resolver
- * constructor, which Resolver will set appropriately depending
- * on if cached results are available and haven't expired. It is
- * however safe to force this value to false, forcing a remote DNS
- * lookup, but not an update of the cache.
- * @return True if the operation completed successfully. Note that
- * if this method returns true, you should not attempt to access
- * the resolver class you pass it after this call, as depending upon
- * the request given, the object may be deleted!
- */
- bool AddResolver(Resolver* r, bool cached);
-
- /** Add a command to this server's command parser
- * @param f A Command command handler object to add
- * @throw ModuleException Will throw ModuleExcption if the command already exists
- */
- inline void AddCommand(Command *f)
- {
- Modules->AddService(*f);
- }
-
- /** Send a modechange.
- * The parameters provided are identical to that sent to the
- * handler for class cmd_mode.
- * @param parameters The mode parameters
- * @param user The user to send error messages to
- */
- void SendMode(const std::vector<std::string>& parameters, User *user);
-
- /** Send a modechange and route it to the network.
- * The parameters provided are identical to that sent to the
- * handler for class cmd_mode.
- * @param parameters The mode parameters
- * @param user The user to send error messages to
- */
- void SendGlobalMode(const std::vector<std::string>& parameters, User *user);
+ caller1<bool, const std::string&> IsIdent;
/** Match two strings using pattern matching, optionally, with a map
* to check case against (may be NULL). If map is null, match will be case insensitive.
* @param mask The glob pattern to match against.
* @param map The character map to use when matching.
*/
- static bool Match(const std::string &str, const std::string &mask, unsigned const char *map = NULL);
- static bool Match(const char *str, const char *mask, unsigned const char *map = NULL);
+ static bool Match(const std::string& str, const std::string& mask, unsigned const char* map = NULL);
+ static bool Match(const char* str, const char* mask, unsigned const char* map = NULL);
/** Match two strings using pattern matching, optionally, with a map
* to check case against (may be NULL). If map is null, match will be case insensitive.
* @param mask The glob or CIDR pattern to match against.
* @param map The character map to use when matching.
*/
- static bool MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map = NULL);
- static bool MatchCIDR(const char *str, const char *mask, unsigned const char *map = NULL);
+ static bool MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map = NULL);
+ static bool MatchCIDR(const char* str, const char* mask, unsigned const char* map = NULL);
- /** Call the handler for a given command.
- * @param commandname The command whos handler you wish to call
- * @param parameters The mode parameters
- * @param user The user to execute the command as
- * @return True if the command handler was called successfully
+ /** Matches a hostname and IP against a space delimited list of hostmasks.
+ * @param masks The space delimited masks to match against.
+ * @param hostname The hostname to try and match.
+ * @param ipaddr The IP address to try and match.
*/
- CmdResult CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user);
-
- /** Return true if the command is a module-implemented command and the given parameters are valid for it
- * @param commandname The command name to check
- * @param pcnt The parameter count
- * @param user The user to test-execute the command as
- * @return True if the command handler is a module command, and there are enough parameters and the user has permission to the command
- */
- bool IsValidModuleCommand(const std::string &commandname, int pcnt, User* user);
+ static bool MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr);
/** Return true if the given parameter is a valid nick!user\@host mask
* @param mask A nick!user\@host masak to match against
* @return True i the mask is valid
*/
- bool IsValidMask(const std::string &mask);
+ static bool IsValidMask(const std::string& mask);
/** Strips all color codes from the given string
* @param sentence The string to strip from
static void ProcessColors(file_cache& input);
/** Rehash the local server
+ * @param uuid The uuid of the user who started the rehash, can be empty
*/
- void RehashServer();
-
- /** Check if the given nickmask matches too many users, send errors to the given user
- * @param nick A nickmask to match against
- * @param user A user to send error text to
- * @return True if the nick matches too many users
- */
- bool NickMatchesEveryone(const std::string &nick, User* user);
-
- /** Check if the given IP mask matches too many users, send errors to the given user
- * @param ip An ipmask to match against
- * @param user A user to send error text to
- * @return True if the ip matches too many users
- */
- bool IPMatchesEveryone(const std::string &ip, User* user);
-
- /** Check if the given hostmask matches too many users, send errors to the given user
- * @param mask A hostmask to match against
- * @param user A user to send error text to
- * @return True if the host matches too many users
- */
- bool HostMatchesEveryone(const std::string &mask, User* user);
+ void Rehash(const std::string& uuid = "");
/** Calculate a duration in seconds from a string in the form 1y2w3d4h6m5s
* @param str A string containing a time in the form 1y2w3d4h6m5s
* (one year, two weeks, three days, four hours, six minutes and five seconds)
* @return The total number of seconds
*/
- long Duration(const std::string &str);
+ static unsigned long Duration(const std::string& str);
/** Attempt to compare a password to a string from the config file.
* This will be passed to handling modules which will compare the data
* @param data The data from the config file
* @param input The data input by the oper
* @param hashtype The hash from the config file
- * @return 0 if the strings match, 1 or -1 if they do not
+ * @return True if the strings match, false if they do not
*/
- int PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype);
-
- /** Check if a given server is a uline.
- * An empty string returns true, this is by design.
- * @param server The server to check for uline status
- * @return True if the server is a uline OR the string is empty
- */
- bool ULine(const std::string& server);
-
- /** Returns true if the uline is 'silent' (doesnt generate
- * remote connect notices etc).
- */
- bool SilentULine(const std::string& server);
+ bool PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype);
/** Returns the full version string of this ircd
* @return The version string
*/
- std::string GetVersionString(bool rawversion = false);
+ std::string GetVersionString(bool getFullVersion = false);
/** Attempt to write the process id to a given file
* @param filename The PID file to attempt to write to
*/
void SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...) CUSTOM_PRINTF(5, 6);
- /** Handle /WHOIS
- */
- void DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick);
-
- /** Quit a user for excess flood, and if they are not
- * fully registered yet, temporarily zline their IP.
- * @param current user to quit
- */
- caller1<void, User*> FloodQuitUser;
-
/** Called to check whether a channel restriction mode applies to a user
* @param User that is attempting some action
* @param Channel that the action is being performed on
*/
caller3<ModResult, User*, Channel*, const std::string&> OnCheckExemption;
- /** Restart the server.
- * This function will not return. If an error occurs,
- * it will throw an instance of CoreException.
- * @param reason The restart reason to show to all clients
- * @throw CoreException An instance of CoreException indicating the error from execv().
- */
- void Restart(const std::string &reason);
-
/** Prepare the ircd for restart or shutdown.
* This function unloads all modules which can be unloaded,
* closes all open sockets, and closes the logfile.
*/
void Cleanup();
- /** This copies the user and channel hash_maps into new hash maps.
- * This frees memory used by the hash_map allocator (which it neglects
- * to free, most of the time, using tons of ram)
- */
- void RehashUsersAndChans();
-
- /** Resets the cached max bans value on all channels.
- * Called by rehash.
+ /** Return a time_t as a human-readable string.
+ * @param format The format to retrieve the date/time in. See `man 3 strftime`
+ * for more information. If NULL, "%a %b %d %T %Y" is assumed.
+ * @param utc True to convert the time to string as-is, false to convert it to local time first.
+ * @return A string representing the given date/time.
*/
- void ResetMaxBans();
+ static std::string TimeString(time_t curtime, const char* format = NULL, bool utc = false);
- /** Return a time_t as a human-readable string.
+ /** Compare two strings in a timing-safe way. If the lengths of the strings differ, the function
+ * returns false immediately (leaking information about the length), otherwise it compares each
+ * character and only returns after all characters have been compared.
+ * @param one First string
+ * @param two Second string
+ * @return True if the strings match, false if they don't
*/
- std::string TimeString(time_t curtime);
+ static bool TimingSafeCompare(const std::string& one, const std::string& two);
/** Begin execution of the server.
* NOTE: this function NEVER returns. Internally,
* it will repeatedly loop.
- * @return The return value for this function is undefined.
*/
- int Run();
-
- /** Adds an extban char to the 005 token.
- */
- void AddExtBanChar(char c);
+ void Run();
char* GetReadBuffer()
{
return this->ReadBuffer;
}
-
- friend class TestSuite;
};
ENTRYPOINT;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
Version GetVersion()
{
return Version(cmd.name, VF_VENDOR|VF_CORE);
}
};
-#endif
+inline void stdalgo::culldeleter::operator()(classbase* item)
+{
+ if (item)
+ ServerInstance->GlobalCulls.AddItem(item);
+}
*/
-#ifndef INSPSOCKET_H
-#define INSPSOCKET_H
+#pragma once
#include "timer.h"
+class IOHook;
+
/**
* States which a socket may be in
*/
* @param secs_from_now Seconds from now to time out
* @param now The current time
*/
- SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now, time_t now) : Timer(secs_from_now, now), sock(thesock), sfd(fd) { }
+ SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now) : Timer(secs_from_now), sock(thesock), sfd(fd) { }
/** Handle tick event
*/
- virtual void Tick(time_t now);
+ virtual bool Tick(time_t now);
};
/**
*/
class CoreExport StreamSocket : public EventHandler
{
- /** Module that handles raw I/O for this socket, or NULL */
- reference<Module> IOHook;
+ /** The IOHook that handles raw I/O for this socket, or NULL */
+ IOHook* iohook;
+
/** Private send queue. Note that individual strings may be shared
*/
std::deque<std::string> sendq;
protected:
std::string recvq;
public:
- StreamSocket() : sendq_len(0) {}
- inline Module* GetIOHook();
- inline void AddIOHook(Module* m);
- inline void DelIOHook();
+ StreamSocket() : iohook(NULL), sendq_len(0) {}
+ IOHook* GetIOHook() const;
+ void AddIOHook(IOHook* hook);
+ void DelIOHook();
/** Handle event from socket engine.
* This will call OnDataReady if there is *new* data in recvq
*/
BufferedSocketError BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);
};
-#include "modules.h"
-
-inline Module* StreamSocket::GetIOHook() { return IOHook; }
-inline void StreamSocket::AddIOHook(Module* m) { IOHook = m; }
-inline void StreamSocket::DelIOHook() { IOHook = NULL; }
-#endif
+inline IOHook* StreamSocket::GetIOHook() const { return iohook; }
+inline void StreamSocket::AddIOHook(IOHook* hook) { iohook = hook; }
+inline void StreamSocket::DelIOHook() { iohook = NULL; }
*/
-#ifndef INSPSTRING_H
-#define INSPSTRING_H
+#pragma once
-// This (inspircd_config) is needed as inspstring doesn't pull in the central header
-#include "inspircd_config.h"
+// This (config) is needed as inspstring doesn't pull in the central header
+#include "config.h"
#include <cstring>
-//#include <cstddef>
-#ifndef HAS_STRLCPY
-/** strlcpy() implementation for systems that don't have it (linux) */
-CoreExport size_t strlcpy(char *dst, const char *src, size_t siz);
-/** strlcat() implementation for systems that don't have it (linux) */
-CoreExport size_t strlcat(char *dst, const char *src, size_t siz);
-#endif
-
-/** charlcat() will append one character to a string using the same
- * safety scemantics as strlcat().
- * @param x The string to operate on
- * @param y the character to append to the end of x
- * @param z The maximum allowed length for z including null terminator
- */
-CoreExport int charlcat(char* x,char y,int z);
-/** charremove() will remove all instances of a character from a string
- * @param mp The string to operate on
- * @param remove The character to remove
+/** Sets ret to the formated string. last is the last parameter before ..., and format is the format in printf-style */
+#define VAFORMAT(ret, last, format) \
+ do { \
+ va_list _vaList; \
+ va_start(_vaList, last); \
+ ret = InspIRCd::Format(_vaList, format); \
+ va_end(_vaList); \
+ } while (false);
+
+/** Compose a hex string from raw data.
+ * @param raw The raw data to compose hex from (can be NULL if rawsize is 0)
+ * @param rawsize The size of the raw data buffer
+ * @return The hex string
*/
-CoreExport bool charremove(char* mp, char remove);
+CoreExport std::string BinToHex(const void* raw, size_t rawsize);
-/** Binary to hexadecimal conversion */
-CoreExport std::string BinToHex(const std::string& data);
/** Base64 encode */
CoreExport std::string BinToBase64(const std::string& data, const char* table = NULL, char pad = 0);
/** Base64 decode */
CoreExport std::string Base64ToBin(const std::string& data, const char* table = NULL);
-#endif
-
+/** Compose a hex string from the data in a std::string.
+ * @param data The data to compose hex from
+ * @return The hex string.
+ */
+inline std::string BinToHex(const std::string& data)
+{
+ return BinToHex(data.data(), data.size());
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include <iterator>
+
+namespace insp
+{
+
+struct intrusive_list_def_tag { };
+
+template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list;
+template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list_tail;
+
+template <typename T, typename Tag = intrusive_list_def_tag>
+class intrusive_list_node
+{
+ T* ptr_next;
+ T* ptr_prev;
+
+ void unlink()
+ {
+ if (ptr_next)
+ ptr_next->intrusive_list_node<T, Tag>::ptr_prev = this->ptr_prev;
+ if (ptr_prev)
+ ptr_prev->intrusive_list_node<T, Tag>::ptr_next = this->ptr_next;
+ ptr_next = ptr_prev = NULL;
+ }
+
+ public:
+ intrusive_list_node()
+ : ptr_next(NULL)
+ , ptr_prev(NULL)
+ {
+ }
+
+ friend class intrusive_list<T, Tag>;
+ friend class intrusive_list_tail<T, Tag>;
+};
+
+} // namespace insp
+
+// Intrusive list where the list only has a pointer to the head element
+#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list
+#include "intrusive_list_impl.h"
+#undef INSPIRCD_INTRUSIVE_LIST_NAME
+
+// Intrusive list where the list maintains a pointer to both the head and the tail elements.
+// Additional methods: back(), push_back(), pop_back()
+#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list_tail
+#define INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+#include "intrusive_list_impl.h"
+#undef INSPIRCD_INTRUSIVE_LIST_NAME
+#undef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+namespace insp
+{
+
+template <typename T, typename Tag>
+class INSPIRCD_INTRUSIVE_LIST_NAME
+{
+ public:
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, T*>
+ {
+ T* curr;
+
+ public:
+ iterator(T* i = NULL)
+ : curr(i)
+ {
+ }
+
+ iterator& operator++()
+ {
+ curr = curr->intrusive_list_node<T, Tag>::ptr_next;
+ return *this;
+ }
+
+ iterator operator++(int)
+ {
+ iterator ret(*this);
+ operator++();
+ return ret;
+ }
+
+ iterator& operator--()
+ {
+ curr = curr->intrusive_list_node<T, Tag>::ptr_prev;
+ return *this;
+ }
+
+ iterator operator--(int)
+ {
+ iterator ret(*this);
+ operator--();
+ return ret;
+ }
+
+ bool operator==(const iterator& other) const { return (curr == other.curr); }
+ bool operator!=(const iterator& other) const { return (curr != other.curr); }
+ T* operator*() const { return curr; }
+ };
+
+ typedef iterator const_iterator;
+
+ INSPIRCD_INTRUSIVE_LIST_NAME()
+ : listhead(NULL)
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ , listtail(NULL)
+#endif
+ , listsize(0)
+ {
+ }
+
+ bool empty() const
+ {
+ return (size() == 0);
+ }
+
+ size_t size() const
+ {
+ return listsize;
+ }
+
+ iterator begin() const
+ {
+ return iterator(listhead);
+ }
+
+ iterator end() const
+ {
+ return iterator();
+ }
+
+ void pop_front()
+ {
+ erase(listhead);
+ }
+
+ T* front() const
+ {
+ return listhead;
+ }
+
+ void push_front(T* x)
+ {
+ if (listsize++)
+ {
+ x->intrusive_list_node<T, Tag>::ptr_next = listhead;
+ listhead->intrusive_list_node<T, Tag>::ptr_prev = x;
+ }
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ else
+ listtail = x;
+#endif
+ listhead = x;
+ }
+
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ T* back() const
+ {
+ return listtail;
+ }
+
+ void push_back(T* x)
+ {
+ if (listsize++)
+ {
+ x->intrusive_list_node<T, Tag>::ptr_prev = listtail;
+ listtail->intrusive_list_node<T, Tag>::ptr_next = x;
+ }
+ else
+ listhead = x;
+ listtail = x;
+ }
+
+ void pop_back()
+ {
+ erase(listtail);
+ }
+#endif
+
+ void erase(const iterator& it)
+ {
+ erase(*it);
+ }
+
+ void erase(T* x)
+ {
+ if (listhead == x)
+ listhead = x->intrusive_list_node<T, Tag>::ptr_next;
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ if (listtail == x)
+ listtail = x->intrusive_list_node<T, Tag>::ptr_prev;
+#endif
+ x->intrusive_list_node<T, Tag>::unlink();
+ listsize--;
+ }
+
+ private:
+ T* listhead;
+#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL
+ T* listtail;
+#endif
+ size_t listsize;
+};
+
+} // namespace insp
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class StreamSocket;
+
+class IOHookProvider : public ServiceProvider
+{
+ public:
+ enum Type
+ {
+ IOH_UNKNOWN,
+ IOH_SSL
+ };
+
+ const Type type;
+
+ IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN)
+ : ServiceProvider(mod, Name, SERVICE_IOHOOK), type(hooktype) { }
+
+ /** Called immediately after a connection is accepted. This is intended for raw socket
+ * processing (e.g. modules which wrap the tcp connection within another library) and provides
+ * no information relating to a user record as the connection has not been assigned yet.
+ * @param sock The socket in question
+ * @param client The client IP address and port
+ * @param server The server IP address and port
+ */
+ virtual void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) = 0;
+
+ /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
+ * by a module.
+ * @param sock The socket in question
+ */
+ virtual void OnConnect(StreamSocket* sock) = 0;
+};
+
+class IOHook : public classbase
+{
+ public:
+ /** The IOHookProvider for this hook, contains information about the hook,
+ * such as the module providing it and the hook type.
+ */
+ IOHookProvider* const prov;
+
+ IOHook(IOHookProvider* provider)
+ : prov(provider) { }
+
+ /**
+ * Called when a hooked stream has data to write, or when the socket
+ * engine returns it as writable
+ * @param sock The socket in question
+ * @param sendq Data to send to the socket
+ * @return 1 if the sendq has been completely emptied, 0 if there is
+ * still data to send, and -1 if there was an error
+ */
+ virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq) = 0;
+
+ /** Called immediately before any socket is closed. When this event is called, shutdown()
+ * has not yet been called on the socket.
+ * @param sock The socket in question
+ */
+ virtual void OnStreamSocketClose(StreamSocket* sock) = 0;
+
+ /**
+ * Called when the stream socket has data to read
+ * @param sock The socket that is ready
+ * @param recvq The receive queue that new data should be appended to
+ * @return 1 if new data has been read, 0 if no new data is ready (but the
+ * socket is still connected), -1 if there was an error or close
+ */
+ virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) = 0;
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** This class manages the generation and transmission of ISUPPORT. */
+class CoreExport ISupportManager
+{
+ private:
+ /** The generated lines which are sent to clients. */
+ std::vector<std::string> cachedlines;
+
+ public:
+ /** (Re)build the ISUPPORT vector.
+ * Called by the core on boot after all modules have been loaded, and every time when a module is loaded
+ * or unloaded. Calls the On005Numeric hook, letting modules manipulate the ISUPPORT tokens.
+ */
+ void Build();
+
+ /** Returns the cached std::vector of ISUPPORT lines.
+ * @return A list of strings prepared for sending to users
+ */
+ const std::vector<std::string>& GetLines() const { return cachedlines; }
+
+ /** Send the 005 numerics (ISUPPORT) to a user.
+ * @param user The user to send the ISUPPORT numerics to
+ */
+ void SendTo(LocalUser* user);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** The base class for list modes, should be inherited.
+ */
+class CoreExport ListModeBase : public ModeHandler
+{
+ public:
+ /** An item in a listmode's list
+ */
+ struct ListItem
+ {
+ std::string setter;
+ std::string mask;
+ time_t time;
+ ListItem(const std::string& Mask, const std::string& Setter, time_t Time)
+ : setter(Setter), mask(Mask), time(Time) { }
+ };
+
+ /** Items stored in the channel's list
+ */
+ typedef std::list<ListItem> ModeList;
+
+ private:
+ class ChanData
+ {
+ public:
+ ModeList list;
+ int maxitems;
+
+ ChanData() : maxitems(-1) { }
+ };
+
+ /** The number of items a listmode's list may contain
+ */
+ struct ListLimit
+ {
+ std::string mask;
+ unsigned int limit;
+ ListLimit(const std::string& Mask, unsigned int Limit) : mask(Mask), limit(Limit) { }
+ bool operator==(const ListLimit& other) const { return (this->mask == other.mask && this->limit == other.limit); }
+ };
+
+ /** Max items per channel by name
+ */
+ typedef std::vector<ListLimit> limitlist;
+
+ /** Finds the limit of modes that can be placed on the given channel name according to the config
+ * @param channame The channel name to find the limit for
+ * @return The maximum number of modes of this type that we allow to be set on the given channel name
+ */
+ unsigned int FindLimit(const std::string& channame);
+
+ /** Returns the limit on the given channel for this mode.
+ * If the limit is cached then the cached value is returned,
+ * otherwise the limit is determined using FindLimit() and cached
+ * for later queries before it is returned
+ * @param channame The channel name to find the limit for
+ * @param cd The ChanData associated with channel channame
+ * @return The maximum number of modes of this type that we allow to be set on the given channel
+ */
+ unsigned int GetLimitInternal(const std::string& channame, ChanData* cd);
+
+ protected:
+ /** Numeric to use when outputting the list
+ */
+ unsigned int listnumeric;
+ /** Numeric to indicate end of list
+ */
+ unsigned int endoflistnumeric;
+ /** String to send for end of list
+ */
+ std::string endofliststring;
+ /** Automatically tidy up entries
+ */
+ bool tidy;
+ /** Config tag to check for max items per channel
+ */
+ std::string configtag;
+ /** Limits on a per-channel basis read from the tag
+ * specified in ListModeBase::configtag
+ */
+ limitlist chanlimits;
+
+ /** Storage key
+ */
+ SimpleExtItem<ChanData> extItem;
+
+ public:
+ /** Constructor.
+ * @param Creator The creator of this class
+ * @param Name Mode name
+ * @param modechar Mode character
+ * @param eolstr End of list string
+ * @param lnum List numeric
+ * @param eolnum End of list numeric
+ * @param autotidy Automatically tidy list entries on add
+ * @param ctag Configuration tag to get limits from
+ */
+ ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist");
+
+ /** Get limit of this mode on a channel
+ * @param channel The channel to inspect
+ * @return Maximum number of modes of this type that can be placed on the given channel
+ */
+ unsigned int GetLimit(Channel* channel);
+
+ /** Retrieves the list of all modes set on the given channel
+ * @param channel Channel to get the list from
+ * @return A list with all modes of this type set on the given channel, can be NULL
+ */
+ ModeList* GetList(Channel* channel);
+
+ /** Display the list for this mode
+ * See mode.h
+ * @param user The user to send the list to
+ * @param channel The channel the user is requesting the list for
+ */
+ virtual void DisplayList(User* user, Channel* channel);
+
+ /** Tell a user that a list contains no elements.
+ * Sends 'eolnum' numeric with text 'eolstr', unless overridden (see constructor)
+ * @param user The user issuing the command
+ * @param channel The channel that has the empty list
+ * See mode.h
+ */
+ virtual void DisplayEmptyList(User* user, Channel* channel);
+
+ /** Remove all instances of the mode from a channel.
+ * Populates the given modestack with modes that remove every instance of
+ * this mode from the channel.
+ * See mode.h for more details.
+ * @param channel The channel to remove all instances of the mode from
+ * @param changelist Mode change list to populate with the removal of this mode
+ */
+ virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
+
+ /** Perform a rehash of this mode's configuration data
+ */
+ void DoRehash();
+
+ /** Handle the list mode.
+ * See mode.h
+ */
+ virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding);
+
+ /** Validate parameters.
+ * Overridden by implementing module.
+ * @param user Source user adding the parameter
+ * @param channel Channel the parameter is being added to
+ * @param parameter The actual parameter being added
+ * @return true if the parameter is valid
+ */
+ virtual bool ValidateParam(User* user, Channel* channel, std::string& parameter);
+
+ /** Tell the user the list is too long.
+ * Overridden by implementing module.
+ * @param source Source user adding the parameter
+ * @param channel Channel the parameter is being added to
+ * @param parameter The actual parameter being added
+ */
+ virtual void TellListTooLong(User* source, Channel* channel, std::string& parameter);
+
+ /** Tell the user an item is already on the list.
+ * Overridden by implementing module.
+ * @param source Source user adding the parameter
+ * @param channel Channel the parameter is being added to
+ * @param parameter The actual parameter being added
+ */
+ virtual void TellAlreadyOnList(User* source, Channel* channel, std::string& parameter);
+
+ /** Tell the user that the parameter is not in the list.
+ * Overridden by implementing module.
+ * @param source Source user removing the parameter
+ * @param channel Channel the parameter is being removed from
+ * @param parameter The actual parameter being removed
+ */
+ virtual void TellNotSet(User* source, Channel* channel, std::string& parameter);
+};
+
+inline ListModeBase::ModeList* ListModeBase::GetList(Channel* channel)
+{
+ ChanData* cd = extItem.get(channel);
+ if (!cd)
+ return NULL;
+
+ return &cd->list;
+}
*/
-#ifndef LOGGER_H
-#define LOGGER_H
+#pragma once
+
+/** Levels at which messages can be logged. */
+enum LogLevel
+{
+ LOG_RAWIO = 5,
+ LOG_DEBUG = 10,
+ LOG_VERBOSE = 20,
+ LOG_DEFAULT = 30,
+ LOG_SPARSE = 40,
+ LOG_NONE = 50
+};
/** Simple wrapper providing periodic flushing to a disk-backed file.
*/
class CoreExport LogStream : public classbase
{
protected:
- int loglvl;
+ LogLevel loglvl;
public:
- LogStream(int loglevel) : loglvl(loglevel)
+ static const char LogHeader[];
+
+ LogStream(LogLevel loglevel) : loglvl(loglevel)
{
}
/** Changes the loglevel for this LogStream on-the-fly.
* This is needed for -nofork. But other LogStreams could use it to change loglevels.
*/
- void ChangeLevel(int lvl) { this->loglvl = lvl; }
+ void ChangeLevel(LogLevel lvl) { this->loglvl = lvl; }
/** Called when there is stuff to log for this particular logstream. The derived class may take no action with it, or do what it
* wants with the output, basically. loglevel and type are primarily for informational purposes (the level and type of the event triggered)
* and msg is, of course, the actual message to log.
*/
- virtual void OnLog(int loglevel, const std::string &type, const std::string &msg) = 0;
+ virtual void OnLog(LogLevel loglevel, const std::string &type, const std::string &msg) = 0;
};
typedef std::map<FileWriter*, int> FileLogMap;
-class CoreExport LogManager
+class CoreExport LogManager : public fakederef<LogManager>
{
private:
/** Lock variable, set to true when a log is in progress, which prevents further loggging from happening and creating a loop.
FileLogMap FileLogs;
public:
-
LogManager();
~LogManager();
/** Logs an event, sending it to all LogStreams registered for the type.
* @param type Log message type (ex: "USERINPUT", "MODULE", ...)
- * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE)
+ * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE)
* @param msg The message to be logged (literal).
*/
- void Log(const std::string &type, int loglevel, const std::string &msg);
+ void Log(const std::string &type, LogLevel loglevel, const std::string &msg);
/** Logs an event, sending it to all LogStreams registered for the type.
* @param type Log message type (ex: "USERINPUT", "MODULE", ...)
- * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE)
+ * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE)
* @param fmt The format of the message to be logged. See your C manual on printf() for details.
*/
- void Log(const std::string &type, int loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5);
+ void Log(const std::string &type, LogLevel loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5);
};
-
-#endif
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
*/
-#ifndef MEMBERSHIP_H
-#define MEMBERSHIP_H
+#pragma once
-class CoreExport Membership : public Extensible
+uint64_t ConvToUInt64(const std::string& in);
+
+/**
+ * Represents a member of a channel.
+ * A Membership object is created when a user joins a channel, and destroyed when a user leaves
+ * (via kick, part or quit) a channel.
+ * All prefix modes a member has is tracked by this object. Moreover, Memberships are Extensibles
+ * meaning modules can add arbitrary data to them using extensions (see m_delaymsg for an example).
+ */
+class CoreExport Membership : public Extensible, public insp::intrusive_list_node<Membership>
{
public:
+ /** Type of the Membership id
+ */
+ typedef uint64_t Id;
+
+ /** User on the channel
+ */
User* const user;
+
+ /** Channel the user is on
+ */
Channel* const chan;
- // mode list, sorted by prefix rank, higest first
+
+ /** List of prefix mode letters this member has,
+ * sorted by prefix rank, highest first
+ */
std::string modes;
+
+ /** Id of this Membership, set by the protocol module, other components should never read or
+ * write this field.
+ */
+ Id id;
+
+ /** Converts a string to a Membership::Id
+ * @param str The string to convert
+ * @return Raw value of type Membership::Id
+ */
+ static Id IdFromString(const std::string& str)
+ {
+ return ConvToUInt64(str);
+ }
+
+ /** Constructor, sets the user and chan fields to the parameters, does NOT update any bookkeeping
+ * information in the User or the Channel.
+ * Call Channel::JoinUser() or ForceJoin() to make a user join a channel instead of constructing
+ * Membership objects directly.
+ */
Membership(User* u, Channel* c) : user(u), chan(c) {}
+
+ /** Returns true if this member has a given prefix mode set
+ * @param m The prefix mode letter to check
+ * @return True if the member has the prefix mode set, false otherwise
+ */
inline bool hasMode(char m) const
{
return modes.find(m) != std::string::npos;
}
+
+ /** Returns the rank of this member.
+ * The rank of a member is defined as the rank given by the 'strongest' prefix mode a
+ * member has. See the PrefixMode class description for more info.
+ * @return The rank of the member
+ */
unsigned int getRank();
+
+ /** Add a prefix character to a user.
+ * Only the core should call this method, usually from
+ * within the mode parser or when the first user joins
+ * the channel (to grant the default privs to them)
+ * @param mh The mode handler of the prefix mode to associate
+ * @param adding True if adding the prefix, false when removing
+ * @return True if a change was made
+ */
+ bool SetPrefix(PrefixMode* mh, bool adding);
+
+ /** Get the highest prefix this user has on the channel
+ * @return A character containing the highest prefix.
+ * If the user has no prefix, 0 is returned. If the user has multiple prefixes,
+ * the highest is returned. If you do not recognise the prefix character you
+ * can get, you can deal with it in a 'proportional' manner compared to known
+ * prefixes, using GetPrefixValue().
+ */
+ char GetPrefixChar() const;
+
+ /** Return all prefix chars this member has.
+ * @return A list of all prefix characters. The prefixes will always
+ * be in rank order, greatest first, as certain IRC clients require
+ * this when multiple prefixes are used names lists.
+ */
+ const char* GetAllPrefixChars() const;
};
-class CoreExport InviteBase
+template <typename T>
+class InviteBase
{
protected:
- InviteList invites;
+ /** List of pending Invitations
+ */
+ insp::intrusive_list<Invitation, T> invites;
public:
+ /** Remove and destruct all pending invitations this user or channel has.
+ * Must be called before the object is destroyed, also called when the TS of the channel is lowered.
+ */
void ClearInvites();
friend class Invitation;
};
-class Invitation : public classbase
+/**
+ * The Invitation class contains all data about a pending invitation.
+ * Invitation objects are referenced from the user and the channel they belong to.
+ */
+class CoreExport Invitation : public insp::intrusive_list_node<Invitation, Channel>, public insp::intrusive_list_node<Invitation, LocalUser>
{
+ /** Constructs an Invitation, only called by Create()
+ * @param c Channel the user is invited to
+ * @param u User being invited
+ * @param timeout Expiration time for this Invitation
+ */
Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {}
public:
+ /** User the invitation is for
+ */
LocalUser* const user;
+
+ /** Channel where the user is invited to
+ */
Channel* const chan;
+
+ /** Timestamp when this Invitation expires or 0 if it doesn't expire.
+ * Invitation::Create() can update this field; see that for more info.
+ */
time_t expiry;
+ /** Destructor
+ * Removes references to this Invitation from the associated user and channel.
+ */
~Invitation();
+
+ /** Create or extend an Invitation.
+ * When a user is invited to join a channel either a new Invitation object is created or
+ * or the expiration timestamp is updated if there is already a pending Invitation for
+ * the given (user, channel) pair and the new expiration time is further than the current.
+ * @param c Target channel
+ * @param u Target user
+ * @param timeout Timestamp when the invite should expire, 0 for no expiration
+ */
static void Create(Channel* c, LocalUser* u, time_t timeout);
+
+ /** Finds the Invitation object for the given channel/user pair.
+ * @param c Target channel, can be NULL to remove expired entries
+ * @param u Target user, cannot be NULL
+ * @param check_expired Pass true to remove all expired invites found while searching, false
+ * to return with an Invitation even if it's expired
+ * @return Invitation object for the given (channel, user) pair if it exists, NULL otherwise
+ */
static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true);
};
-#endif
+typedef insp::intrusive_list<Invitation, LocalUser> InviteList;
+
+template<typename T>
+inline void InviteBase<T>::ClearInvites()
+{
+ for (typename insp::intrusive_list<Invitation, T>::iterator i = invites.begin(); i != invites.end(); )
+ {
+ Invitation* inv = *i;
+ // Destructing the Invitation invalidates the iterator, so move it now
+ ++i;
+ delete inv;
+ }
+}
*/
-#ifndef MODE_H
-#define MODE_H
+#pragma once
#include "ctables.h"
+#include "modechange.h"
/**
* Holds the values for different type of modes
MODEACTION_ALLOW = 1 /* Allow the mode */
};
-/**
- * Used to mask off the mode types in the mode handler
- * array. Used in a simple two instruction hashing function
- * "(modeletter - 65) OR mask"
- */
-enum ModeMasks
-{
- MASK_USER = 128, /* A user mode */
- MASK_CHANNEL = 0 /* A channel mode */
-};
-
/**
* These fixed values can be used to proportionally compare module-defined prefixes to known values.
* For example, if your module queries a Channel, and is told that user 'joebloggs' has the prefix
PARAM_ALWAYS
};
+class PrefixMode;
+class ListModeBase;
+class ParamModeBase;
+
/** Each mode is implemented by ONE ModeHandler class.
* You must derive ModeHandler and add the child class to
* the list of modes handled by the ircd, using
*/
class CoreExport ModeHandler : public ServiceProvider
{
- protected:
- /**
- * The mode parameter translation type
+ public:
+ typedef size_t Id;
+
+ enum Class
+ {
+ MC_PREFIX,
+ MC_LIST,
+ MC_PARAM,
+ MC_OTHER
+ };
+
+ private:
+ /** The opaque id of this mode assigned by the mode parser
*/
- TranslateType m_paramtype;
+ Id modeid;
+ protected:
/** What kind of parameters does the mode take?
*/
ParamSpec parameters_taken;
*/
char mode;
- /** Mode prefix, or 0
- */
- char prefix;
-
/**
* True if the mode requires oper status
* to set.
*/
ModeType m_type;
+ /** The object type of this mode handler
+ */
+ const Class type_id;
+
/** The prefix char needed on channel to use this mode,
* only checked for channel modes
*/
* @param modeletter The mode letter you wish to handle
* @param params Parameters taken by the mode
* @param type Type of the mode (MODETYPE_USER or MODETYPE_CHANNEL)
+ * @param mclass The object type of this mode handler, one of ModeHandler::Class
*/
- ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type);
+ ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type, Class mclass = MC_OTHER);
virtual CullResult cull();
virtual ~ModeHandler();
/**
* Returns true if the mode is a list mode
*/
- bool IsListMode();
+ bool IsListMode() const { return list; }
+
/**
- * Mode prefix or 0. If this is defined, you should
- * also implement GetPrefixRank() to return an integer
- * value for this mode prefix.
+ * Check whether this mode is a prefix mode
+ * @return non-NULL if this mode is a prefix mode, NULL otherwise
*/
- inline char GetPrefix() const { return prefix; }
+ PrefixMode* IsPrefixMode();
+
/**
- * Get the 'value' of this modes prefix.
- * determines which to display when there are multiple.
- * The mode with the highest value is ranked first. See the
- * PrefixModeValue enum and Channel::GetPrefixValue() for
- * more information.
+ * Check whether this mode handler inherits from ListModeBase
+ * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise
*/
- virtual unsigned int GetPrefixRank();
+ ListModeBase* IsListModeBase();
+
/**
- * Returns the mode's type
+ * Check whether this mode handler inherits from ListModeBase
+ * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise
*/
- inline ModeType GetModeType() const { return m_type; }
+ ParamModeBase* IsParameterMode();
+
/**
- * Returns the mode's parameter translation type
+ * Returns the mode's type
*/
- inline TranslateType GetTranslateType() const { return m_paramtype; }
+ inline ModeType GetModeType() const { return m_type; }
/**
* Returns true if the mode can only be set/unset by an oper
*/
*/
inline char GetModeChar() { return mode; }
+ /** Return the id of this mode which is used in User::modes and
+ * Channel::modes as the index to determine whether a mode is set.
+ */
+ Id GetId() const { return modeid; }
+
/** For user modes, return the current parameter, if any
*/
virtual std::string GetUserParameter(User* useor);
virtual bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
/**
- * When a MODETYPE_USER mode handler is being removed, the server will call this method for every user on the server.
- * Your mode handler should remove its user mode from the user by sending the appropriate server modes using
- * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
- * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
- * your mode properly from each user.
+ * When a MODETYPE_USER mode handler is being removed, the core will call this method for every user on the server.
+ * The usermode will be removed using the appropiate server mode using InspIRCd::SendMode().
* @param user The user which the server wants to remove your mode from
- * @param stack The mode stack to add the mode change to
*/
- virtual void RemoveMode(User* user, irc::modestacker* stack = NULL);
+ void RemoveMode(User* user);
/**
* When a MODETYPE_CHANNEL mode handler is being removed, the server will call this method for every channel on the server.
- * Your mode handler should remove its user mode from the channel by sending the appropriate server modes using
- * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters,
- * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove
- * your mode properly from each channel. Note that in the case of listmodes, you should remove the entire list of items.
+ * The mode handler has to populate the given modestacker with mode changes that remove the mode from the channel.
+ * The default implementation of this method can remove all kinds of channel modes except listmodes.
+ * In the case of listmodes, the entire list of items must be added to the modestacker (which is handled by ListModeBase,
+ * so if you inherit from it or your mode can be removed by the default implementation then you do not have to implement
+ * this function).
* @param channel The channel which the server wants to remove your mode from
- * @param stack The mode stack to add the mode change to
+ * @param changelist Mode change list to populate with the removal of this mode
*/
- virtual void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
+ virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
inline unsigned int GetLevelRequired() const { return levelrequired; }
+
+ friend class ModeParser;
+};
+
+/**
+ * Prefix modes are channel modes that grant a specific rank to members having prefix mode set.
+ * They require a parameter when setting and unsetting; the parameter is always a member of the channel.
+ * A prefix mode may be set on any number of members on a channel, but for a given member a given prefix
+ * mode is either set or not set, in other words members cannot have the same prefix mode set more than once.
+ *
+ * A rank of a member is defined as the rank given by the 'strongest' prefix mode that member has.
+ * Other parts of the IRCd use this rank to determine whether a channel action is allowable for a user or not.
+ * The rank of a prefix mode is constant, i.e. the same rank value is given to all users having that prefix mode set.
+ *
+ * Note that it is possible that the same action requires a different rank on a different channel;
+ * for example changing the topic on a channel having +t set requires a rank that is >= than the rank of a halfop,
+ * but there is no such restriction when +t isn't set.
+ */
+class CoreExport PrefixMode : public ModeHandler
+{
+ protected:
+ /** The prefix character granted by this mode. '@' for op, '+' for voice, etc.
+ * If 0, this mode does not have a visible prefix character.
+ */
+ char prefix;
+
+ /** The prefix rank of this mode, used to compare prefix
+ * modes
+ */
+ unsigned int prefixrank;
+
+ public:
+ /**
+ * Constructor
+ * @param Creator The module creating this mode
+ * @param Name The user-friendly one word name of the prefix mode, e.g.: "op", "voice"
+ * @param ModeLetter The mode letter of this mode
+ * @param Rank Rank given by this prefix mode, see explanation above
+ * @param PrefixChar Prefix character, or 0 if the mode has no prefix character
+ */
+ PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank = 0, char PrefixChar = 0);
+
+ /**
+ * Handles setting and unsetting the prefix mode.
+ * Finds the given member of the given channel, if it's not found an error message is sent to 'source'
+ * and MODEACTION_DENY is returned. Otherwise the mode change is attempted.
+ * @param source Source of the mode change, an error message is sent to this user if the target is not found
+ * @param dest Unused
+ * @param channel The channel the mode change is happening on
+ * @param param The nickname or uuid of the target user
+ * @param adding True when the mode is being set, false when it is being unset
+ * @return MODEACTION_ALLOW if the change happened, MODEACTION_DENY if no change happened
+ * The latter occurs either when the member cannot be found or when the member already has this prefix set
+ * (when setting) or doesn't have this prefix set (when unsetting).
+ */
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding);
+
+ /**
+ * Removes this prefix mode from all users on the given channel
+ * @param chan The channel which the server wants to remove your mode from
+ * @param changelist Mode change list to populate with the removal of this mode
+ */
+ void RemoveMode(Channel* channel, Modes::ChangeList& changelist);
+
+ /**
+ * Mode prefix or 0. If this is defined, you should
+ * also implement GetPrefixRank() to return an integer
+ * value for this mode prefix.
+ */
+ char GetPrefix() const { return prefix; }
+
+ /**
+ * Get the 'value' of this modes prefix.
+ * determines which to display when there are multiple.
+ * The mode with the highest value is ranked first. See the
+ * PrefixModeValue enum and Channel::GetPrefixValue() for
+ * more information.
+ */
+ unsigned int GetPrefixRank() const { return prefixrank; }
};
/** A prebuilt mode handler which handles a simple user mode, e.g. no parameters, usable by any user, with no extra
public:
SimpleUserModeHandler(Module* Creator, const std::string& Name, char modeletter)
: ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_USER) {}
- virtual ~SimpleUserModeHandler() {}
virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
};
public:
SimpleChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
: ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_CHANNEL) {}
- virtual ~SimpleChannelModeHandler() {}
virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
};
-class CoreExport ParamChannelModeHandler : public ModeHandler
-{
- public:
- ParamChannelModeHandler(Module* Creator, const std::string& Name, char modeletter)
- : ModeHandler(Creator, Name, modeletter, PARAM_SETONLY, MODETYPE_CHANNEL) {}
- virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- /** Validate the parameter - you may change the value to normalize it. Return true if it is valid. */
- virtual bool ParamValidate(std::string& parameter);
-};
-
/**
* The ModeWatcher class can be used to alter the behaviour of a mode implemented
* by the core or by another module. To use ModeWatcher, derive a class from it,
*/
class CoreExport ModeWatcher : public classbase
{
- protected:
+ private:
/**
- * The mode letter this class is watching
+ * The mode name this class is watching
*/
- char mode;
+ const std::string mode;
+
/**
* The mode type being watched (user or channel)
*/
/**
* The constructor initializes the mode and the mode type
*/
- ModeWatcher(Module* creator, char modeletter, ModeType type);
+ ModeWatcher(Module* creator, const std::string& modename, ModeType type);
/**
* The default destructor does nothing.
*/
virtual ~ModeWatcher();
/**
- * Get the mode character being watched
- * @return The mode character being watched
+ * Get the mode name being watched
+ * @return The mode name being watched
*/
- char GetModeChar();
+ const std::string& GetModeName() const { return mode; }
+
/**
* Get the mode type being watched
* @return The mode type being watched (user or channel)
* If you alter the parameter you are given, the mode handler will see your atered version
* when it handles the mode.
* @param adding True if the mode is being added and false if it is being removed
- * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
* @return True to allow the mode change to go ahead, false to abort it. If you abort the
* change, the mode handler (and ModeWatcher::AfterMode()) will never see the mode change.
*/
- virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding, ModeType type);
+ virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string& parameter, bool adding);
/**
* After the mode character has been processed by the ModeHandler, this method will be called.
* @param source The sender of the mode
* @param parameter The parameter of the mode, if the mode is supposed to have a parameter.
* You cannot alter the parameter here, as the mode handler has already processed it.
* @param adding True if the mode is being added and false if it is being removed
- * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL
*/
- virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string ¶meter, bool adding, ModeType type);
+ virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding);
};
-typedef std::vector<ModeWatcher*>::iterator ModeWatchIter;
+typedef std::multimap<std::string, ModeWatcher*>::iterator ModeWatchIter;
/** The mode parser handles routing of modes and handling of mode strings.
* It marshalls, controls and maintains both ModeWatcher and ModeHandler classes,
* parses client to server MODE strings for user and channel modes, and performs
* processing for the 004 mode list numeric, amongst other things.
*/
-class CoreExport ModeParser
+class CoreExport ModeParser : public fakederef<ModeParser>
{
+ public:
+ static const ModeHandler::Id MODEID_MAX = 64;
+
+ /** Type of the container that maps mode names to ModeHandlers
+ */
+ typedef TR1NS::unordered_map<std::string, ModeHandler*, irc::insensitive, irc::StrHashComp> ModeHandlerMap;
+
private:
+ /** Last item in the ModeType enum
+ */
+ static const unsigned int MODETYPE_LAST = 2;
+
/** Mode handlers for each mode, to access a handler subtract
* 65 from the ascii value of the mode letter.
* The upper bit of the value indicates if its a usermode
* or a channel mode, so we have 256 of them not 64.
*/
- ModeHandler* modehandlers[256];
- /** Mode watcher classes arranged in the same way as the
- * mode handlers, except for instead of having 256 of them
- * we have 256 lists of them.
+ ModeHandler* modehandlers[MODETYPE_LAST][128];
+
+ /** An array of mode handlers indexed by the mode id
+ */
+ ModeHandler* modehandlersbyid[MODETYPE_LAST][MODEID_MAX];
+
+ /** A map of mode handlers keyed by their name
+ */
+ ModeHandlerMap modehandlersbyname[MODETYPE_LAST];
+
+ /** Lists of mode handlers by type
*/
- std::vector<ModeWatcher*> modewatchers[256];
- /** Displays the current modes of a channel or user.
- * Used by ModeParser::Process.
+ struct
+ {
+ /** List of mode handlers that inherit from ListModeBase
+ */
+ std::vector<ListModeBase*> list;
+
+ /** List of mode handlers that inherit from PrefixMode
+ */
+ std::vector<PrefixMode*> prefix;
+ } mhlist;
+
+ /** Mode watcher classes
*/
- void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text);
- /** Displays the value of a list mode
- * Used by ModeParser::Process.
+ std::multimap<std::string, ModeWatcher*> modewatchermap;
+
+ /** Last processed mode change
*/
- void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence);
+ Modes::ChangeList LastChangeList;
/**
* Attempts to apply a mode change to a user or channel
*/
- ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string ¶m, bool SkipACL);
+ ModeAction TryMode(User* user, User* targu, Channel* targc, Modes::Change& mcitem, bool SkipACL);
+
+ /** Returns a list of user or channel mode characters.
+ * Used for constructing the parts of the mode list in the 004 numeric.
+ * @param mt Controls whether to list user modes or channel modes
+ * @param needparam Return modes only if they require a parameter to be set
+ * @return The available mode letters that satisfy the given conditions
+ */
+ std::string CreateModeList(ModeType mt, bool needparam = false);
+
+ /** Recreate the cached mode list that is displayed in the 004 numeric
+ * in Cached004ModeList.
+ * Called when a mode handler is added or removed.
+ */
+ void RecreateModeListFor004Numeric();
+
+ /** Allocates an unused id for the given mode type, throws a ModuleException if out of ids.
+ * @param mt The type of the mode to allocate the id for
+ * @return The id
+ */
+ ModeHandler::Id AllocateModeId(ModeType mt);
/** The string representing the last set of modes to be parsed.
* Use GetLastParse() to get this value, to be used for display purposes.
*/
std::string LastParse;
- std::vector<std::string> LastParseParams;
- std::vector<TranslateType> LastParseTranslate;
- unsigned int sent[256];
-
- unsigned int seq;
+ /** Cached mode list for use in 004 numeric
+ */
+ std::string Cached004ModeList;
public:
+ typedef std::vector<ListModeBase*> ListModeList;
+ typedef std::vector<PrefixMode*> PrefixModeList;
+
+ typedef unsigned int ModeProcessFlag;
+ enum ModeProcessFlags
+ {
+ /** If only this flag is specified, the mode change will be global
+ * and parameter modes will have their parameters explicitly set
+ * (not merged). This is the default.
+ */
+ MODE_NONE = 0,
+
+ /** If this flag is set then the parameters of non-listmodes will be
+ * merged according to their conflict resolution rules.
+ * Does not affect user modes, channel modes without a parameter and
+ * listmodes.
+ */
+ MODE_MERGE = 1,
+
+ /** If this flag is set then the linking module will ignore the mode change
+ * and not send it to other servers. The mode change will be processed
+ * locally and sent to local user(s) as usual.
+ */
+ MODE_LOCALONLY = 2,
+
+ /** If this flag is set then the mode change will be subject to access checks.
+ * For more information see the documentation of the PrefixMode class,
+ * ModeHandler::levelrequired and ModeHandler::AccessCheck().
+ * Modules may explicitly allow a mode change regardless of this flag by returning
+ * MOD_RES_ALLOW from the OnPreMode hook. Only affects channel mode changes.
+ */
+ MODE_CHECKACCESS = 4
+ };
- /** The constructor initializes all the RFC basic modes by using ModeParserAddMode().
- */
ModeParser();
~ModeParser();
- /** Used to check if user 'd' should be allowed to do operation 'MASK' on channel 'chan'.
- * for example, should 'user A' be able to 'op' on 'channel B'.
+ /** Initialize all built-in modes
*/
- User* SanityChecks(User *user,const char *dest,Channel *chan,int status);
+ static void InitBuiltinModes();
+
/** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out.
* E.g.
*
* may be different to what you sent after it has been 'cleaned up' by the parser.
* @return Last parsed string, as seen by users.
*/
- const std::string& GetLastParse();
- const std::vector<std::string>& GetLastParseParams() { return LastParseParams; }
- const std::vector<TranslateType>& GetLastParseTranslate() { return LastParseTranslate; }
+ const std::string& GetLastParse() const { return LastParse; }
+
/** Add a mode to the mode parser.
- * @return True if the mode was successfully added.
+ * Throws a ModuleException if the mode cannot be added.
*/
- bool AddMode(ModeHandler* mh);
+ void AddMode(ModeHandler* mh);
+
/** Delete a mode from the mode parser.
* When a mode is deleted, the mode handler will be called
* for every user (if it is a user mode) or for every channel
* triggered. See the documentation of class ModeWatcher for more
* information.
* @param mw The ModeWatcher you want to add
- * @return True if the ModeWatcher was added correctly
*/
- bool AddModeWatcher(ModeWatcher* mw);
+ void AddModeWatcher(ModeWatcher* mw);
+
/** Delete a mode watcher.
* A mode watcher is triggered before and after a mode handler is
* triggered. See the documentation of class ModeWatcher for more
* @return True if the ModeWatcher was deleted correctly
*/
bool DelModeWatcher(ModeWatcher* mw);
- /** Process a set of mode changes from a server or user.
- * @param parameters The parameters of the mode change, in the format
- * they would be from a MODE command.
- * @param user The user setting or removing the modes. When the modes are set
- * by a server, an 'uninitialized' User is used, where *user\::nick == NULL
- * and *user->server == NULL.
- * @param merge Should the mode parameters be merged?
- */
- void Process(const std::vector<std::string>& parameters, User *user, bool merge = false);
+
+ /** Process a list of mode changes entirely. If the mode changes do not fit into one MODE line
+ * then multiple MODE lines are generated.
+ * @param user The source of the mode change, can be a server user.
+ * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel.
+ * @param targetuser User to apply the mode change on. NULL if changing modes on a user.
+ * @param changelist Modes to change in form of a Modes::ChangeList.
+ * @param flags Optional flags controlling how the mode change is processed,
+ * defaults to MODE_NONE.
+ */
+ void Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE);
+
+ /** Process a single MODE line's worth of mode changes, taking max modes and line length limits
+ * into consideration. Return value indicates how many modes were processed.
+ * @param user The source of the mode change, can be a server user.
+ * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel.
+ * @param targetuser User to apply the mode change on. NULL if changing modes on a user.
+ * @param changelist Modes to change in form of a Modes::ChangeList. May not process
+ * the entire list due to MODE line length and max modes limitations.
+ * @param flags Optional flags controlling how the mode change is processed,
+ * defaults to MODE_NONE.
+ * @param beginindex Index of the first element in changelist to process. Mode changes before
+ * the element with this index are ignored.
+ * @return Number of mode changes processed from changelist.
+ */
+ unsigned int ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE, unsigned int beginindex = 0);
+
+ /** Turn a list of parameters compatible with the format of the MODE command into
+ * Modes::ChangeList form. All modes are processed, regardless of max modes. Unknown modes
+ * are skipped.
+ * @param user The source of the mode change, can be a server user. Error numerics are sent to
+ * this user.
+ * @param type MODETYPE_USER if this is a user mode change or MODETYPE_CHANNEL if this
+ * is a channel mode change.
+ * @param parameters List of strings describing the mode change to convert to a ChangeList.
+ * Must be using the same format as the parameters of a MODE command.
+ * @param changelist ChangeList object to populate.
+ * @param beginindex Index of the first element that is part of the MODE list in the parameters
+ * container. Defaults to 1.
+ * @param endindex Index of the first element that is not part of the MODE list. By default,
+ * the entire container is considered part of the MODE list.
+ */
+ void ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex = 1, unsigned int endindex = UINT_MAX);
+
+ /** Find the mode handler for a given mode name and type.
+ * @param modename The mode name to search for.
+ * @param mt Type of mode to search for, user or channel.
+ * @return A pointer to a ModeHandler class, or NULL of there isn't a handler for the given mode name.
+ */
+ ModeHandler* FindMode(const std::string& modename, ModeType mt);
/** Find the mode handler for a given mode and type.
* @param modeletter mode letter to search for
*/
ModeHandler* FindMode(unsigned const char modeletter, ModeType mt);
+ /** Find the mode handler for the given prefix mode
+ * @param modeletter The mode letter to search for
+ * @return A pointer to the PrefixMode or NULL if the mode wasn't found or it isn't a prefix mode
+ */
+ PrefixMode* FindPrefixMode(unsigned char modeletter);
+
/** Find a mode handler by its prefix.
* If there is no mode handler with the given prefix, NULL will be returned.
* @param pfxletter The prefix to find, e.g. '@'
* @return The mode handler which handles this prefix, or NULL if there is none.
*/
- ModeHandler* FindPrefix(unsigned const char pfxletter);
-
- /** Returns a list of mode characters which are usermodes.
- * This is used in the 004 numeric when users connect.
- */
- std::string UserModeList();
+ PrefixMode* FindPrefix(unsigned const char pfxletter);
- /** Returns a list of channel mode characters which are listmodes.
- * This is used in the 004 numeric when users connect.
+ /** Returns a list of modes, space seperated by type:
+ * 1. User modes
+ * 2. Channel modes
+ * 3. Channel modes that require a parameter when set
+ * This is sent to users as the last part of the 004 numeric
*/
- std::string ChannelModeList();
-
- /** Returns a list of channel mode characters which take parameters.
- * This is used in the 004 numeric when users connect.
- */
- std::string ParaModeList();
+ const std::string& GetModeListFor004Numeric();
/** Generates a list of modes, comma seperated by type:
* 1; Listmodes EXCEPT those with a prefix
* 3; Modes that only take a param when adding
* 4; Modes that dont take a param
*/
- std::string GiveModeList(ModeMasks m);
-
- static bool PrefixComparison(ModeHandler* one, ModeHandler* two);
+ std::string GiveModeList(ModeType mt);
/** This returns the PREFIX=(ohv)@%+ section of the 005 numeric, or
* just the "@%+" part if the parameter false
*/
std::string BuildPrefixes(bool lettersAndModes = true);
+
+ /** Get a list of all mode handlers that inherit from ListModeBase
+ * @return A list containing ListModeBase modes
+ */
+ const ListModeList& GetListModes() const { return mhlist.list; }
+
+ /** Get a list of all prefix modes
+ * @return A list containing all prefix modes
+ */
+ const PrefixModeList& GetPrefixModes() const { return mhlist.prefix; }
+
+ /** Get a mode name -> ModeHandler* map containing all modes of the given type
+ * @param mt Type of modes to return, MODETYPE_USER or MODETYPE_CHANNEL
+ * @return A map of mode handlers of the given type
+ */
+ const ModeHandlerMap& GetModes(ModeType mt) const { return modehandlersbyname[mt]; }
+
+ /** Show the list of a list mode to a user. Modules can deny the listing.
+ * @param user User to show the list to.
+ * @param chan Channel to show the list of.
+ * @param mh List mode to show the list of.
+ */
+ void ShowListModeList(User* user, Channel* chan, ModeHandler* mh);
};
-#endif
+inline const std::string& ModeParser::GetModeListFor004Numeric()
+{
+ return Cached004ModeList;
+}
+
+inline PrefixMode* ModeHandler::IsPrefixMode()
+{
+ return (this->type_id == MC_PREFIX ? static_cast<PrefixMode*>(this) : NULL);
+}
+
+inline ListModeBase* ModeHandler::IsListModeBase()
+{
+ return (this->type_id == MC_LIST ? reinterpret_cast<ListModeBase*>(this) : NULL);
+}
+
+inline ParamModeBase* ModeHandler::IsParameterMode()
+{
+ return (this->type_id == MC_PARAM ? reinterpret_cast<ParamModeBase*>(this) : NULL);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Modes
+{
+ struct Change;
+ class ChangeList;
+}
+
+/** A single mode to be changed
+ */
+struct Modes::Change
+{
+ bool adding;
+ ModeHandler* mh;
+ std::string param;
+
+ /**
+ * @param handler Mode handler
+ * @param add True if this mode is being set, false if removed
+ * @param parameter Mode parameter
+ */
+ Change(ModeHandler* handler, bool add, const std::string& parameter)
+ : adding(add)
+ , mh(handler)
+ , param(parameter)
+ {
+ }
+};
+
+/** A list of mode changes that can be applied on a Channel or User
+ */
+class Modes::ChangeList
+{
+ public:
+ typedef std::vector<Change> List;
+
+ /** Add a new mode to be changed to this ChangeList
+ * @param handler Mode handler
+ * @param add True if this mode is being set, false if removed
+ * @param parameter Mode parameter
+ */
+ void push(ModeHandler* mh, bool adding, const std::string& param = std::string())
+ {
+ items.push_back(Change(mh, adding, param));
+ }
+
+ /** Add a new mode to this ChangeList which will be set on the target
+ * @param handler Mode handler
+ * @param parameter Mode parameter
+ */
+ void push_add(ModeHandler* mh, const std::string& param = std::string())
+ {
+ push(mh, true, param);
+ }
+
+ /** Add a new mode to this ChangeList which will be unset from the target
+ * @param handler Mode handler
+ * @param parameter Mode parameter
+ */
+ void push_remove(ModeHandler* mh, const std::string& param = std::string())
+ {
+ push(mh, false, param);
+ }
+
+ /** Remove all mode changes from this stack
+ */
+ void clear() { items.clear(); }
+
+ /** Checks whether the ChangeList is empty, equivalent to (size() != 0).
+ * @return True if the ChangeList is empty, false otherwise.
+ */
+ bool empty() const { return items.empty(); }
+
+ /** Get number of mode changes in this ChangeList
+ * @return Number of mode changes in this ChangeList
+ */
+ List::size_type size() const { return items.size(); }
+
+ /** Get the list of mode changes in this ChangeList
+ * @return List of modes added to this ChangeList
+ */
+ const List& getlist() const { return items; }
+
+ /** Get the list of mode changes in this ChangeList
+ * @return List of modes added to this ChangeList
+ */
+ List& getlist() { return items; }
+
+ private:
+ List items;
+};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +b
- */
-class ModeChannelBan : public ModeHandler
-{
- private:
- BanItem b;
- public:
- ModeChannelBan();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- std::string& AddBan(User *user,std::string& dest,Channel *chan,int status);
- std::string& DelBan(User *user,std::string& dest,Channel *chan,int status);
- void DisplayList(User* user, Channel* channel);
- void DisplayEmptyList(User* user, Channel* channel);
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-};
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** Channel mode +k
- */
-class ModeChannelKey : public ModeHandler
-{
- public:
- ModeChannelKey();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
-};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-
-/** Channel mode +l
- */
-class ModeChannelLimit : public ParamChannelModeHandler
-{
- public:
- ModeChannelLimit();
- bool ParamValidate(std::string& parameter);
- bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel);
-};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +o
- */
-class ModeChannelOp : public ModeHandler
-{
- private:
- public:
- ModeChannelOp();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- unsigned int GetPrefixRank();
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
-};
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-#include "channels.h"
-
-class InspIRCd;
-
-/** Channel mode +v
- */
-class ModeChannelVoice : public ModeHandler
-{
- private:
- public:
- ModeChannelVoice();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- unsigned int GetPrefixRank();
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
-};
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mode.h"
-
-/** Channel mode +i
- */
-class ModeChannelInviteOnly : public SimpleChannelModeHandler
-{
- public:
- ModeChannelInviteOnly() : SimpleChannelModeHandler(NULL, "inviteonly", 'i')
- {
- }
-};
-
-/** Channel mode +m
- */
-class ModeChannelModerated : public SimpleChannelModeHandler
-{
- public:
- ModeChannelModerated() : SimpleChannelModeHandler(NULL, "moderated", 'm')
- {
- }
-};
-
-/** Channel mode +n
- */
-class ModeChannelNoExternal : public SimpleChannelModeHandler
-{
- public:
- ModeChannelNoExternal() : SimpleChannelModeHandler(NULL, "noextmsg", 'n')
- {
- }
-};
-
-/** Channel mode +p
- */
-class ModeChannelPrivate : public SimpleChannelModeHandler
-{
- public:
- ModeChannelPrivate() : SimpleChannelModeHandler(NULL, "private", 'p')
- {
- }
-};
-
-/** Channel mode +s
- */
-class ModeChannelSecret : public SimpleChannelModeHandler
-{
- public:
- ModeChannelSecret() : SimpleChannelModeHandler(NULL, "secret", 's')
- {
- }
-};
-
-/** Channel mode +t
- */
-class ModeChannelTopicOps : public SimpleChannelModeHandler
-{
- public:
- ModeChannelTopicOps() : SimpleChannelModeHandler(NULL, "topiclock", 't')
- {
- }
-};
-
-/** User mode +i
- */
-class ModeUserInvisible : public SimpleUserModeHandler
-{
- public:
- ModeUserInvisible() : SimpleUserModeHandler(NULL, "invisible", 'i')
- {
- }
-};
-
-/** User mode +w
- */
-class ModeUserWallops : public SimpleUserModeHandler
-{
- public:
- ModeUserWallops() : SimpleUserModeHandler(NULL, "wallops", 'w')
- {
- }
-};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** User mode +o
- */
-class ModeUserOperator : public ModeHandler
-{
- public:
- ModeUserOperator();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
-};
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "mode.h"
-
-class InspIRCd;
-
-/** User mode +n
- */
-class ModeUserServerNoticeMask : public ModeHandler
-{
- public:
- ModeUserServerNoticeMask();
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- void OnParameterMissing(User* user, User* dest, Channel* channel);
- std::string GetUserParameter(User* user);
-};
*/
-#ifndef MODULES_H
-#define MODULES_H
+#pragma once
#include "dynamic.h"
#include "base.h"
#include <sstream>
#include "timer.h"
#include "mode.h"
-#include "dns.h"
/** Used to define a set of behavior bits for a module
*/
/** InspIRCd major version.
* 1.2 -> 102; 2.1 -> 201; 2.12 -> 212
*/
-#define INSPIRCD_VERSION_MAJ 200
+#define INSPIRCD_VERSION_MAJ 202
/** InspIRCd API version.
* If you change any API elements, increment this value. This counter should be
* reset whenever the major version is changed. Modules can use these two values
* and numerical comparisons in preprocessor macros if they wish to support
* multiple versions of InspIRCd in one file.
*/
-#define INSPIRCD_VERSION_API 9
+#define INSPIRCD_VERSION_API 1
/**
* This #define allows us to call a method in all
* loaded modules in a readable simple way, e.g.:
- * 'FOREACH_MOD(I_OnConnect,OnConnect(user));'
+ * 'FOREACH_MOD(OnConnect,(user));'
*/
#define FOREACH_MOD(y,x) do { \
- EventHandlerIter safei; \
- for (EventHandlerIter _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \
+ const IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## y]; \
+ for (IntModuleList::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
{ \
- safei = _i; \
- ++safei; \
+ _next = _i+1; \
try \
{ \
- (*_i)->x ; \
+ (*_i)->y x ; \
} \
catch (CoreException& modexcept) \
{ \
- ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modexcept.GetReason()); \
} \
- _i = safei; \
} \
} while (0);
*/
#define DO_EACH_HOOK(n,v,args) \
do { \
- EventHandlerIter iter_ ## n = ServerInstance->Modules->EventHandlers[I_ ## n].begin(); \
- while (iter_ ## n != ServerInstance->Modules->EventHandlers[I_ ## n].end()) \
+ const IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## n]; \
+ for (IntModuleList::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
{ \
- Module* mod_ ## n = *iter_ ## n; \
- iter_ ## n ++; \
+ _next = _i+1; \
try \
{ \
- v = (mod_ ## n)->n args;
+ v = (*_i)->n args;
#define WHILE_EACH_HOOK(n) \
} \
catch (CoreException& except_ ## n) \
{ \
- ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s", (except_ ## n).GetReason()); \
- (void) mod_ ## n; /* catch mismatched pairs */ \
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + (except_ ## n).GetReason()); \
} \
} \
} while(0)
virtual ~Version() {}
};
-/** The Request class is a unicast message directed at a given module.
- * When this class is properly instantiated it may be sent to a module
- * using the Send() method, which will call the given module's OnRequest
- * method with this class as its parameter.
- */
-class CoreExport Request : public classbase
-{
- public:
- /** This should be a null-terminated string identifying the type of request,
- * all modules should define this and use it to determine the nature of the
- * request before they attempt to cast the Request in any way.
- */
- const char* const id;
- /** This is a pointer to the sender of the message, which can be used to
- * directly trigger events, or to create a reply.
- */
- ModuleRef source;
- /** The single destination of the Request
- */
- ModuleRef dest;
-
- /** Create a new Request
- * This is for the 'new' way of defining a subclass
- * of Request and defining it in a common header,
- * passing an object of your Request subclass through
- * as a Request* and using the ID string to determine
- * what to cast it back to and the other end.
- */
- Request(Module* src, Module* dst, const char* idstr);
- /** Send the Request.
- */
- void Send();
-};
-
-
/** The Event class is a unicast message directed at all modules.
* When the class is properly instantiated it may be sent to all modules
* using the Send() method, which will trigger the OnEvent method in
: ServiceProvider(Creator, Name, SERVICE_DATA) {}
};
-class CoreExport dynamic_reference_base : public interfacebase
-{
- private:
- std::string name;
- protected:
- DataProvider* value;
- public:
- ModuleRef creator;
- dynamic_reference_base(Module* Creator, const std::string& Name);
- ~dynamic_reference_base();
- inline void ClearCache() { value = NULL; }
- inline const std::string& GetProvider() { return name; }
- void SetProvider(const std::string& newname);
- void lookup();
- operator bool();
- static void reset_all();
-};
-
-template<typename T>
-class dynamic_reference : public dynamic_reference_base
-{
- public:
- dynamic_reference(Module* Creator, const std::string& Name)
- : dynamic_reference_base(Creator, Name) {}
- inline T* operator->()
- {
- if (!value)
- lookup();
- return static_cast<T*>(value);
- }
-};
-
/** Priority types which can be used by Module::Prioritize()
*/
enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
enum Implementation
{
I_BEGIN,
- I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, I_OnRehash,
+ I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart,
I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois,
- I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreNick,
- I_OnUserMessage, I_OnUserNotice, I_OnMode, I_OnGetServerDescription, I_OnSyncUser,
- I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops, I_OnAcceptConnection, I_OnUserInit,
+ I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick,
+ I_OnUserMessage, I_OnMode, I_OnSyncUser,
+ I_OnSyncChannel, I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit,
I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
- I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule,
+ I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnLoadModule,
I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite,
I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck,
I_OnStats, I_OnChangeLocalUserHost, I_OnPreTopicChange,
- I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
- I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
+ I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect,
+ I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin,
I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
- I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO,
+ I_OnText, I_OnPassCompare, I_OnNamesListItem, I_OnNumeric,
I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
I_END
};
*/
class CoreExport Module : public classbase, public usecountbase
{
+ /** Detach an event from this module
+ * @param i Event type to detach
+ */
+ void DetachEvent(Implementation i);
+
public:
/** File that this module was loaded from
*/
{
}
+ /** This method is called when you should reload module specific configuration:
+ * on boot, on a /REHASH and on module load.
+ * @param status The current status, can be inspected for more information;
+ * also used for reporting configuration errors and warnings.
+ */
+ virtual void ReadConfig(ConfigStatus& status);
+
/** Returns the version number of a Module.
* The method should return a Version object with its version information assigned via
* Version::Version
*/
virtual void OnModuleRehash(User* user, const std::string ¶meter);
- /** Called on rehash.
- * This method is called after a rehash has completed. You should use it to reload any module
- * configuration from the main configuration file.
- * @param user The user that performed the rehash, if it was initiated by a user and that user
- * is still connected.
- */
- virtual void OnRehash(User* user);
-
/** Called whenever a snotice is about to be sent to a snomask.
* snomask and type may both be modified; the message may not.
* @param snomask The snomask the message is going to (e.g. 'A')
* @param keygiven The key given to join the channel, or an empty string if none was provided
* @return 1 To prevent the join, 0 to allow it.
*/
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven);
+ virtual ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven);
/** Called whenever a user is about to be kicked.
* Returning a value of 1 from this function stops the process immediately, causing no
* @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
* @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
* It will be ignored for private messages.
+ * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
* @return 1 to deny the message, 0 to allow it
*/
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
-
- /** Called whenever a user is about to NOTICE A user or a channel, before any processing is done.
- * Returning any nonzero value from this function stops the process immediately, causing no
- * output to be sent to the user by the core. If you do this you must produce your own numerics,
- * notices etc. This is useful for modules which may want to filter or redirect messages.
- * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user,
- * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details
- * of where the message is destined to be sent.
- * You may alter the message text as you wish before relinquishing control to the next module
- * in the chain, and if no other modules block the text this altered form of the text will be sent out
- * to the user and possibly to other servers.
- * @param user The user sending the message
- * @param dest The target of the message (Channel* or User*)
- * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
- * @param text Changeable text being sent by the user
- * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
- * @param exempt_list A list of users not to send to. For channel notices, this will usually contain just the sender.
- * It will be ignored for private notices.
- * @return 1 to deny the NOTICE, 0 to allow it
- */
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
+ virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list, MessageType msgtype);
/** Called when sending a message to all "neighbors" of a given user -
* that is, all users that share a common channel. This is used in
*
* Set exceptions[user] = true to include, exceptions[user] = false to exclude
*/
- virtual void OnBuildNeighborList(User* source, UserChanList &include_c, std::map<User*,bool> &exceptions);
+ virtual void OnBuildNeighborList(User* source, IncludeChanList& include_c, std::map<User*, bool>& exceptions);
- /** Called before any nickchange, local or remote. This can be used to implement Q-lines etc.
- * Please note that although you can see remote nickchanges through this function, you should
- * NOT make any changes to the User if the user is a remote user as this may cause a desnyc.
- * check user->server before taking any action (including returning nonzero from the method).
+ /** Called before local nickname changes. This can be used to implement Q-lines etc.
* If your method returns nonzero, the nickchange is silently forbidden, and it is down to your
* module to generate some meaninful output.
* @param user The username changing their nick
* @param newnick Their new nickname
* @return 1 to deny the change, 0 to allow
*/
- virtual ModResult OnUserPreNick(User* user, const std::string &newnick);
+ virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick);
/** Called after any PRIVMSG sent from a user.
* The dest variable contains a User* if target_type is TYPE_USER and a Channel*
* @param text the text being sent by the user
* @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
* @param exempt_list A list of users to not send to.
+ * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
*/
- virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
-
- /** Called after any NOTICE sent from a user.
- * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
- * if target_type is TYPE_CHANNEL.
- * @param user The user sending the message
- * @param dest The target of the message
- * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
- * @param text the text being sent by the user
- * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
- * @param exempt_list A list of users to not send to.
- */
- virtual void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
+ virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list, MessageType msgtype);
/** Called immediately before any NOTICE or PRIVMSG sent from a user, local or remote.
* The dest variable contains a User* if target_type is TYPE_USER and a Channel*
* if target_type is TYPE_CHANNEL.
- * The difference between this event and OnUserPreNotice/OnUserPreMessage is that delivery is gauranteed,
+ * The difference between this event and OnUserPreMessage is that delivery is gauranteed,
* the message has already been vetted. In the case of the other two methods, a later module may stop your
* message. This also differs from OnUserMessage which occurs AFTER the message has been sent.
* @param user The user sending the message
virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
/** Called after every MODE command sent from a user
- * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
- * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the
- * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3".
+ * Either the usertarget or the chantarget variable contains the target of the modes,
+ * the actual target will have a non-NULL pointer.
+ * All changed modes are available in the changelist object.
* @param user The user sending the MODEs
- * @param dest The target of the modes (User* or Channel*)
- * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
- * @param text The actual modes and their parameters if any
- * @param translate The translation types of the mode parameters
- */
- virtual void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
-
- /** Allows modules to alter or create server descriptions
- * Whenever a module requires a server description, for example for display in
- * WHOIS, this function is called in all modules. You may change or define the
- * description given in std::string &description. If you do, this description
- * will be shown in the WHOIS fields.
- * @param servername The servername being searched for
- * @param description Alterable server description for this server
+ * @param usertarget The target user of the modes, NULL if the target is a channel
+ * @param chantarget The target channel of the modes, NULL if the target is a user
+ * @param changelist The changed modes.
+ * @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags
+ * for the possible flags.
+ * @param output_mode Changed modes, including '+' and '-' characters, not including any parameters
*/
- virtual void OnGetServerDescription(const std::string &servername,std::string &description);
+ virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags, const std::string& output_mode);
/** Allows modules to synchronize data which relates to users during a netburst.
* When this function is called, it will be called from the module which implements
- * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
- * is given in Module* proto, so that you may call its methods such as ProtoSendMode
- * (see below). This function will be called for every user visible on your side
- * of the burst, allowing you to for example set modes, etc. Do not use this call to
- * synchronize data which you have stored using class Extensible -- There is a specialist
- * function OnSyncUserMetaData and OnSyncChannelMetaData for this!
+ * the linking protocol. This currently is m_spanningtree.so.
+ * This function will be called for every user visible on your side
+ * of the burst, allowing you to for example set modes, etc.
* @param user The user being syncronized
- * @param proto A pointer to the module handling network protocol
- * @param opaque An opaque pointer set by the protocol module, should not be modified!
+ * @param server The target of the burst
*/
- virtual void OnSyncUser(User* user, Module* proto, void* opaque);
+ virtual void OnSyncUser(User* user, ProtocolServer& server);
/** Allows modules to synchronize data which relates to channels during a netburst.
* When this function is called, it will be called from the module which implements
- * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
- * is given in Module* proto, so that you may call its methods such as ProtoSendMode
- * (see below). This function will be called for every user visible on your side
- * of the burst, allowing you to for example set modes, etc.
- *
- * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp
+ * the linking protocol. This currently is m_spanningtree.so.
+ * This function will be called for every channel visible on your side of the burst,
+ * allowing you to for example set modes, etc.
*
* @param chan The channel being syncronized
- * @param proto A pointer to the module handling network protocol
- * @param opaque An opaque pointer set by the protocol module, should not be modified!
+ * @param server The target of the burst
*/
- virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque);
+ virtual void OnSyncChannel(Channel* chan, ProtocolServer& server);
- /* Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
- * Whenever the linking module wants to send out data, but doesnt know what the data
- * represents (e.g. it is Extensible metadata, added to a User or Channel by a module) then
- * this method is called. You should use the ProtoSendMetaData function after you've
- * correctly decided how the data should be represented, to send the metadata on its way if
- * if it belongs to your module.
- * @param proto A pointer to the module handling network protocol
- * @param opaque An opaque pointer set by the protocol module, should not be modified!
- * @param displayable If this value is true, the data is going to be displayed to a user,
- * and not sent across the network. Use this to determine wether or not to show sensitive data.
+ /** Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
+ * When the linking module has finished sending all data it wanted to send during a netburst, then
+ * this method is called. You should use the SendMetaData() function after you've
+ * correctly decided how the data should be represented, to send the data.
+ * @param server The target of the burst
*/
- virtual void OnSyncNetwork(Module* proto, void* opaque);
+ virtual void OnSyncNetwork(ProtocolServer& server);
/** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module.
* Please see src/modules/m_swhois.cpp for a working example of how to use this method call.
*/
virtual void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
- /** Implemented by modules which provide the ability to link servers.
- * These modules will implement this method, which allows transparent sending of servermodes
- * down the network link as a broadcast, without a module calling it having to know the format
- * of the MODE command before the actual mode string.
- *
- * More documentation to follow soon. Please see src/modules/m_chanprotect.cpp for examples
- * of how to use this function.
- *
- * @param opaque An opaque pointer set by the protocol module, should not be modified!
- * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
- * @param target The Channel* or User* that modes should be sent for
- * @param modeline The modes and parameters to be sent
- * @param translate The translation types of the mode parameters
- */
- virtual void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
-
- /** Implemented by modules which provide the ability to link servers.
- * These modules will implement this method, which allows metadata (extra data added to
- * user and channel records using class Extensible, Extensible::Extend, etc) to be sent
- * to other servers on a netburst and decoded at the other end by the same module on a
- * different server.
- *
- * More documentation to follow soon. Please see src/modules/m_swhois.cpp for example of
- * how to use this function.
- * @param opaque An opaque pointer set by the protocol module, should not be modified!
- * @param target The Channel* or User* that metadata should be sent for
- * @param extname The extension name to send metadata for
- * @param extdata Encoded data for this extension name, which will be encoded at the oppsite end by an identical module using OnDecodeMetaData
- */
- virtual void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
-
- /** Called after every WALLOPS command.
- * @param user The user sending the WALLOPS
- * @param text The content of the WALLOPS message
- */
- virtual void OnWallops(User* user, const std::string &text);
-
/** Called whenever a user's hostname is changed.
* This event triggers after the host has been set.
* @param user The user whos host is being changed
*/
virtual void OnUserPostNick(User* user, const std::string &oldnick);
- /** Called before any mode change, to allow a single access check for
+ /** Called before a mode change via the MODE command, to allow a single access check for
* a full mode change (use OnRawMode to check individual modes)
*
* Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by
* @param source the user making the mode change
* @param dest the user destination of the umode change (NULL if a channel mode)
* @param channel the channel destination of the mode change
- * @param parameters raw mode parameters; parameters[0] is the user/channel being changed
+ * @param modes Modes being changed, can be edited
*/
- virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters);
+ virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes);
/** Called when a 005 numeric is about to be output.
* The module should modify the 005 numeric if needed to indicate its features.
- * @param output The 005 string to be modified if neccessary.
- */
- virtual void On005Numeric(std::string &output);
+ * @param tokens The 005 map to be modified if neccessary.
+ */
+ virtual void On005Numeric(std::map<std::string, std::string>& tokens);
/** Called when a client is disconnected by KILL.
* If a client is killed by a server, e.g. a nickname collision or protocol error,
*/
virtual ModResult OnKill(User* source, User* dest, const std::string &reason);
- /** Called when an oper wants to disconnect a remote user via KILL
- * @param source The user sending the KILL
- * @param dest The user being killed
- * @param reason The kill reason
- * @param operreason The oper kill reason
- */
- virtual void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
-
/** Called whenever a module is loaded.
* mod will contain a pointer to the module, and string will contain its name,
* for example m_widgets.so. This function is primary for dependency checking,
* @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE
* @param original_line The entire original line as passed to the parser from the user
*/
- virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
+ virtual void OnPostCommand(Command* command, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line);
/** Called when a user is first connecting, prior to starting DNS lookups, checking initial
* connect class, or accepting any commands.
* Return 1 from this function to block the mode character from being processed entirely.
* @param user The user who is sending the mode
* @param chan The channel the mode is being sent to (or NULL if a usermode)
- * @param mode The mode character being set
+ * @param mh The mode handler for the mode being changed
* @param param The parameter for the mode or an empty string
* @param adding true of the mode is being added, false if it is being removed
- * @param pcnt The parameter count for the mode (0 or 1)
* @return ACR_DENY to deny the mode, ACR_DEFAULT to do standard mode checking, and ACR_ALLOW
* to skip all permission checking. Please note that for remote mode changes, your return value
* will be ignored!
*/
- virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt);
+ virtual ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding);
/** Called whenever a user joins a channel, to determine if key checks should go ahead or not.
* This method will always be called for each join, wether or not the channel is actually +k, and
*/
virtual void OnEvent(Event& event);
- /** Called whenever a Request class is sent to your module by another module.
- * The value of Request::id should be used to determine the type of request.
- * @param request The Request class being received
- */
- virtual void OnRequest(Request& request);
-
/** Called whenever a password check is to be made. Replaces the old OldOperCompare API.
* The password field (from the config file) is in 'password' and is to be compared against
* 'input'. This method allows for encryption of passwords (oper, connect:allow, die/restart, etc).
*/
virtual void OnPostConnect(User* user);
- /** Called whenever a ban is added to a channel's list.
- * Return a non-zero value to 'eat' the mode change and prevent the ban from being added.
- * @param source The user adding the ban
- * @param channel The channel the ban is being added to
- * @param banmask The ban mask being added
- * @return 1 to block the ban, 0 to continue as normal
- */
- virtual ModResult OnAddBan(User* source, Channel* channel,const std::string &banmask);
-
- /** Called whenever a ban is removed from a channel's list.
- * Return a non-zero value to 'eat' the mode change and prevent the ban from being removed.
- * @param source The user deleting the ban
- * @param channel The channel the ban is being deleted from
- * @param banmask The ban mask being deleted
- * @return 1 to block the unban, 0 to continue as normal
- */
- virtual ModResult OnDelBan(User* source, Channel* channel,const std::string &banmask);
-
- /** Called to install an I/O hook on an event handler
- * @param user The socket to possibly install the I/O hook on
- * @param via The port that the user connected on
- */
- virtual void OnHookIO(StreamSocket* user, ListenSocket* via);
-
/** Called when a port accepts a connection
* Return MOD_RES_ACCEPT if you have used the file descriptor.
* @param fd The file descriptor returned from accept()
*/
virtual ModResult OnAcceptConnection(int fd, ListenSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
- /** Called immediately after any connection is accepted. This is intended for raw socket
- * processing (e.g. modules which wrap the tcp connection within another library) and provides
- * no information relating to a user record as the connection has not been assigned yet.
- * There are no return values from this call as all modules get an opportunity if required to
- * process the connection.
- * @param sock The socket in question
- * @param client The client IP address and port
- * @param server The server IP address and port
- */
- virtual void OnStreamSocketAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
-
- /**
- * Called when a hooked stream has data to write, or when the socket
- * engine returns it as writable
- * @param sock The socket in question
- * @param sendq Data to send to the socket
- * @return 1 if the sendq has been completely emptied, 0 if there is
- * still data to send, and -1 if there was an error
- */
- virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq);
-
- /** Called immediately before any socket is closed. When this event is called, shutdown()
- * has not yet been called on the socket.
- * @param sock The socket in question
- */
- virtual void OnStreamSocketClose(StreamSocket* sock);
-
- /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
- * by a module.
- * @param sock The socket in question
- */
- virtual void OnStreamSocketConnect(StreamSocket* sock);
-
- /**
- * Called when the stream socket has data to read
- * @param sock The socket that is ready
- * @param recvq The receive queue that new data should be appended to
- * @return 1 if new data has been read, 0 if no new data is ready (but the
- * socket is still connected), -1 if there was an error or close
- */
- virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq);
-
/** Called whenever a user sets away or returns from being away.
* The away message is available as a parameter, but should not be modified.
* At this stage, it has already been copied into the user record.
*/
virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass);
+#ifdef INSPIRCD_ENABLE_TESTSUITE
/** Add test suite hooks here. These are used for testing functionality of a module
* via the --testsuite debugging parameter.
*/
virtual void OnRunTestSuite();
+#endif
/** Called for every item in a NAMES list, so that modules may reformat portions of it as they see fit.
- * For example NAMESX, channel mode +u and +I, and UHNAMES. If the nick is set to an empty string by any
- * module, then this will cause the nickname not to be displayed at all.
+ * For example NAMESX, channel mode +u and +I, and UHNAMES.
+ * @param issuer The user who is going to receive the NAMES list being built
+ * @param item The channel member being considered for inclusion
+ * @param prefixes The prefix character(s) to display, initially set to the prefix char of the most powerful
+ * prefix mode the member has, can be changed
+ * @param nick The nick to display, initially set to the member's nick, can be changed
+ * @return Return MOD_RES_PASSTHRU to allow the member to be displayed, MOD_RES_DENY to cause them to be
+ * excluded from this NAMES list
*/
- virtual void OnNamesListItem(User* issuer, Membership* item, std::string &prefixes, std::string &nick);
+ virtual ModResult OnNamesListItem(User* issuer, Membership* item, std::string& prefixes, std::string& nick);
virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text);
* @param source The user running the /WHO query
* @param params The parameters to the /WHO query
* @param user The user that this line of the query is about
+ * @param memb The member shown in this line, NULL if no channel is in this line
* @param line The raw line to send; modifiable, if empty no line will be returned.
*/
- virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
+ virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line);
/** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to
* a module like m_cgiirc changing it.
virtual void OnSetUserIP(LocalUser* user);
};
-
-#define CONF_NO_ERROR 0x000000
-#define CONF_NOT_A_NUMBER 0x000010
-#define CONF_INT_NEGATIVE 0x000080
-#define CONF_VALUE_NOT_FOUND 0x000100
-#define CONF_FILE_NOT_FOUND 0x000200
-
-
-/** Allows reading of values from configuration files
- * This class allows a module to read from either the main configuration file (inspircd.conf) or from
- * a module-specified configuration file. It may either be instantiated with one parameter or none.
- * Constructing the class using one parameter allows you to specify a path to your own configuration
- * file, otherwise, inspircd.conf is read.
- */
-class CoreExport ConfigReader : public interfacebase
-{
- protected:
- /** Error code
- */
- long error;
-
- public:
- /** Default constructor.
- * This constructor initialises the ConfigReader class to read the inspircd.conf file
- * as specified when running ./configure.
- */
- ConfigReader();
- /** Default destructor.
- * This method destroys the ConfigReader class.
- */
- ~ConfigReader();
-
- /** Retrieves a value from the config file.
- * This method retrieves a value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve.
- */
- std::string ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds = false);
- /** Retrieves a value from the config file.
- * This method retrieves a value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve. If the
- * tag is not found the default value is returned instead.
- */
- std::string ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds = false);
-
- /** Retrieves a boolean value from the config file.
- * This method retrieves a boolean value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
- * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
- */
- bool ReadFlag(const std::string &tag, const std::string &name, int index);
- /** Retrieves a boolean value from the config file.
- * This method retrieves a boolean value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
- * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
- * If the tag is not found, the default value is used instead.
- */
- bool ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index);
-
- /** Retrieves an integer value from the config file.
- * This method retrieves an integer value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
- * values in the tag will cause the objects error value to be set, and any call to GetError() will
- * return CONF_INVALID_NUMBER to be returned. need_positive is set if the number must be non-negative.
- * If a negative number is placed into a tag which is specified positive, 0 will be returned and GetError()
- * will return CONF_INT_NEGATIVE. Note that need_positive is not suitable to get an unsigned int - you
- * should cast the result to achieve that effect.
- */
- int ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive);
- /** Retrieves an integer value from the config file.
- * This method retrieves an integer value from the config file. Where multiple copies of the tag
- * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
- * values in the tag will cause the objects error value to be set, and any call to GetError() will
- * return CONF_INVALID_NUMBER to be returned. needs_unsigned is set if the number must be unsigned.
- * If a signed number is placed into a tag which is specified unsigned, 0 will be returned and GetError()
- * will return CONF_NOT_UNSIGNED. If the tag is not found, the default value is used instead.
- */
- int ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive);
-
- /** Returns the last error to occur.
- * Valid errors can be found by looking in modules.h. Any nonzero value indicates an error condition.
- * A call to GetError() resets the error flag back to 0.
- */
- long GetError();
-
- /** Counts the number of times a given tag appears in the config file.
- * This method counts the number of times a tag appears in a config file, for use where
- * there are several tags of the same kind, e.g. with opers and connect types. It can be
- * used with the index value of ConfigReader::ReadValue to loop through all copies of a
- * multiple instance tag.
- */
- int Enumerate(const std::string &tag);
-};
-
-
-
-/** Caches a text file into memory and can be used to retrieve lines from it.
- * This class contains methods for read-only manipulation of a text file in memory.
- * Either use the constructor type with one parameter to load a file into memory
- * at construction, or use the LoadFile method to load a file.
- */
-class CoreExport FileReader : public classbase
-{
- /** The file contents
- */
- std::vector<std::string> fc;
-
- /** Content size in bytes
- */
- unsigned long contentsize;
-
- /** Calculate content size in bytes
- */
- void CalcSize();
-
- public:
- /** Default constructor.
- * This method does not load any file into memory, you must use the LoadFile method
- * after constructing the class this way.
- */
- FileReader();
-
- /** Secondary constructor.
- * This method initialises the class with a file loaded into it ready for GetLine and
- * and other methods to be called. If the file could not be loaded, FileReader::FileSize
- * returns 0.
- */
- FileReader(const std::string &filename);
-
- /** Default destructor.
- * This deletes the memory allocated to the file.
- */
- ~FileReader();
-
- /** Used to load a file.
- * This method loads a file into the class ready for GetLine and
- * and other methods to be called. If the file could not be loaded, FileReader::FileSize
- * returns 0.
- */
- void LoadFile(const std::string &filename);
-
- /** Returns the whole content of the file as std::string
- */
- std::string Contents();
-
- /** Returns the entire size of the file as std::string
- */
- unsigned long ContentSize();
-
- /** Returns true if the file exists
- * This function will return false if the file could not be opened.
- */
- bool Exists();
-
- /** Retrieve one line from the file.
- * This method retrieves one line from the text file. If an empty non-NULL string is returned,
- * the index was out of bounds, or the line had no data on it.
- */
- std::string GetLine(int x);
-
- /** Returns the size of the file in lines.
- * This method returns the number of lines in the read file. If it is 0, no lines have been
- * read into memory, either because the file is empty or it does not exist, or cannot be
- * opened due to permission problems.
- */
- int FileSize();
-};
-
/** A list of modules
*/
typedef std::vector<Module*> IntModuleList;
/** ModuleManager takes care of all things module-related
* in the core.
*/
-class CoreExport ModuleManager
+class CoreExport ModuleManager : public fakederef<ModuleManager>
{
+ public:
+ typedef std::vector<ServiceProvider*> ServiceList;
+
private:
/** Holds a string describing the last module error to occur
*/
std::string LastModuleError;
- /** Total number of modules loaded into the ircd
- */
- int ModCount;
-
/** List of loaded modules and shared object/dll handles
* keyed by module name
*/
/** Internal unload module hook */
bool CanUnload(Module*);
+
+ /** Loads all core modules (cmd_*)
+ */
+ void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
+
+ /** Calls the Prioritize() method in all loaded modules
+ * @return True if all went well, false if a dependency loop was detected
+ */
+ bool PrioritizeHooks();
+
public:
+ typedef std::map<std::string, Module*> ModuleMap;
/** Event handler hooks.
* This needs to be public to be used by FOREACH_MOD and friends.
/** List of data services keyed by name */
std::multimap<std::string, ServiceProvider*> DataProviders;
+ /** A list of ServiceProviders waiting to be registered.
+ * Non-NULL when constructing a Module, NULL otherwise.
+ * When non-NULL ServiceProviders add themselves to this list on creation and the core
+ * automatically registers them (that is, call AddService()) after the Module is constructed,
+ * and before Module::init() is called.
+ * If a service is created after the construction of the Module (for example in init()) it
+ * has to be registered manually.
+ */
+ ServiceList* NewServices;
+
/** Simple, bog-standard, boring constructor.
*/
ModuleManager();
*/
void DetachAll(Module* mod);
+ /** Attach all events to a module (used on module load)
+ * @param mod Module to attach to all events
+ */
+ void AttachAll(Module* mod);
+
/** Returns text describing the last module error
* @return The last error message to occur
*/
void UnloadAll();
void DoSafeUnload(Module*);
- /** Get the total number of currently loaded modules
- * @return The number of loaded modules
- */
- int GetCount()
- {
- return this->ModCount;
- }
-
/** Find a module by name, and return a Module* to it.
* This is preferred over iterating the module lists yourself.
* @param name The module name to look up
/** Unregister a service provided by a module */
void DelService(ServiceProvider&);
+ /** Register all services in a given ServiceList
+ * @param list The list containing the services to register
+ */
+ void AddServices(const ServiceList& list);
+
inline void AddServices(ServiceProvider** list, int count)
{
for(int i=0; i < count; i++)
return static_cast<T*>(FindService(SERVICE_DATA, name));
}
- /** Return a list of all modules matching the given filter
- * @param filter This int is a bitmask of flags set in Module::Flags,
- * such as VF_VENDOR or VF_STATIC. If you wish to receive a list of
- * all modules with no filtering, set this to 0.
- * @return The list of module names
+ /** Get a map of all loaded modules keyed by their name
+ * @return A ModuleMap containing all loaded modules
*/
- const std::vector<std::string> GetAllModuleNames(int filter);
+ const ModuleMap& GetModules() const { return Modules; }
};
/** Do not mess with these functions unless you know the C preprocessor
};
#define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \
- static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAMESTR);
-
-#define MODNAMESTR MODNAMESTR_FN_2(MODNAME)
-#define MODNAMESTR_FN_2(x) MODNAMESTR_FN_1(x)
-#define MODNAMESTR_FN_1(x) #x
+ static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAME ".so");
#else
{ \
return new y; \
} \
- extern "C" const char inspircd_src_version[] = VERSION " r" REVISION;
+ extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION " " INSPIRCD_REVISION;
#endif
#define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
#endif
-
-#endif
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include <map>
+#include <string>
+
+class AccountEvent : public Event
+{
+ public:
+ User* const user;
+ const std::string account;
+ AccountEvent(Module* me, User* u, const std::string& name)
+ : Event(me, "account_login"), user(u), account(name)
+ {
+ }
+};
+
+typedef StringExtItem AccountExtItem;
+
+inline AccountExtItem* GetAccountExtItem()
+{
+ return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname"));
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class CapEvent : public Event
+{
+ public:
+ enum CapEventType
+ {
+ CAPEVENT_REQ,
+ CAPEVENT_LS,
+ CAPEVENT_LIST,
+ CAPEVENT_CLEAR
+ };
+
+ CapEventType type;
+ std::vector<std::string> wanted;
+ std::vector<std::string> ack;
+ User* user;
+ CapEvent(Module* sender, User* u, CapEventType capevtype) : Event(sender, "cap_request"), type(capevtype), user(u) {}
+};
+
+class GenericCap
+{
+ public:
+ LocalIntExt ext;
+ const std::string cap;
+ GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap)
+ {
+ }
+
+ void HandleEvent(Event& ev)
+ {
+ if (ev.id != "cap_request")
+ return;
+
+ CapEvent *data = static_cast<CapEvent*>(&ev);
+ if (data->type == CapEvent::CAPEVENT_REQ)
+ {
+ for (std::vector<std::string>::iterator it = data->wanted.begin(); it != data->wanted.end(); ++it)
+ {
+ if (it->empty())
+ continue;
+ bool enablecap = ((*it)[0] != '-');
+ if (((enablecap) && (*it == cap)) || (*it == "-" + cap))
+ {
+ // we can handle this, so ACK it, and remove it from the wanted list
+ data->ack.push_back(*it);
+ data->wanted.erase(it);
+ ext.set(data->user, enablecap ? 1 : 0);
+ break;
+ }
+ }
+ }
+ else if (data->type == CapEvent::CAPEVENT_LS)
+ {
+ data->wanted.push_back(cap);
+ }
+ else if (data->type == CapEvent::CAPEVENT_LIST)
+ {
+ if (ext.get(data->user))
+ data->wanted.push_back(cap);
+ }
+ else if (data->type == CapEvent::CAPEVENT_CLEAR)
+ {
+ data->ack.push_back("-" + cap);
+ ext.set(data->user, 0);
+ }
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+namespace DNS
+{
+ /** Valid query types
+ */
+ enum QueryType
+ {
+ /* Nothing */
+ QUERY_NONE,
+ /* A simple A lookup */
+ QUERY_A = 1,
+ /* A CNAME lookup */
+ QUERY_CNAME = 5,
+ /* Reverse DNS lookup */
+ QUERY_PTR = 12,
+ /* IPv6 AAAA lookup */
+ QUERY_AAAA = 28
+ };
+
+ /** Flags that can be AND'd into DNSPacket::flags to receive certain values
+ */
+ enum
+ {
+ QUERYFLAGS_QR = 0x8000,
+ QUERYFLAGS_OPCODE = 0x7800,
+ QUERYFLAGS_AA = 0x400,
+ QUERYFLAGS_TC = 0x200,
+ QUERYFLAGS_RD = 0x100,
+ QUERYFLAGS_RA = 0x80,
+ QUERYFLAGS_Z = 0x70,
+ QUERYFLAGS_RCODE = 0xF
+ };
+
+ enum Error
+ {
+ ERROR_NONE,
+ ERROR_UNKNOWN,
+ ERROR_UNLOADED,
+ ERROR_TIMEDOUT,
+ ERROR_NOT_AN_ANSWER,
+ ERROR_NONSTANDARD_QUERY,
+ ERROR_FORMAT_ERROR,
+ ERROR_SERVER_FAILURE,
+ ERROR_DOMAIN_NOT_FOUND,
+ ERROR_NOT_IMPLEMENTED,
+ ERROR_REFUSED,
+ ERROR_NO_RECORDS,
+ ERROR_INVALIDTYPE
+ };
+
+ const int PORT = 53;
+
+ /**
+ * The maximum value of a dns request id,
+ * 16 bits wide, 0xFFFF.
+ */
+ const int MAX_REQUEST_ID = 0xFFFF;
+
+ class Exception : public ModuleException
+ {
+ public:
+ Exception(const std::string& message) : ModuleException(message) { }
+ };
+
+ struct Question
+ {
+ std::string name;
+ QueryType type;
+ unsigned short qclass;
+
+ Question() : type(QUERY_NONE), qclass(0) { }
+ Question(const std::string& n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { }
+ inline bool operator==(const Question& other) const { return name == other.name && type == other.type && qclass == other.qclass; }
+
+ struct hash
+ {
+ size_t operator()(const Question& question) const
+ {
+ return irc::insensitive()(question.name);
+ }
+ };
+ };
+
+ struct ResourceRecord : Question
+ {
+ unsigned int ttl;
+ std::string rdata;
+ time_t created;
+
+ ResourceRecord(const std::string& n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(ServerInstance->Time()) { }
+ ResourceRecord(const Question& question) : Question(question), ttl(0), created(ServerInstance->Time()) { }
+ };
+
+ struct Query
+ {
+ std::vector<Question> questions;
+ std::vector<ResourceRecord> answers;
+ Error error;
+ bool cached;
+
+ Query() : error(ERROR_NONE), cached(false) { }
+ Query(const Question& question) : error(ERROR_NONE), cached(false) { questions.push_back(question); }
+ };
+
+ class ReplySocket;
+ class Request;
+
+ /** DNS manager
+ */
+ class Manager : public DataProvider
+ {
+ public:
+ Manager(Module* mod) : DataProvider(mod, "DNS") { }
+
+ virtual void Process(Request* req) = 0;
+ virtual void RemoveRequest(Request* req) = 0;
+ virtual std::string GetErrorStr(Error) = 0;
+ };
+
+ /** A DNS query.
+ */
+ class Request : public Timer, public Question
+ {
+ protected:
+ Manager* const manager;
+ public:
+ /* Use result cache if available */
+ bool use_cache;
+ /* Request id */
+ unsigned short id;
+ /* Creator of this request */
+ Module* const creator;
+
+ Request(Manager* mgr, Module* mod, const std::string& addr, QueryType qt, bool usecache = true)
+ : Timer((ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5))
+ , Question(addr, qt)
+ , manager(mgr)
+ , use_cache(usecache)
+ , id(0)
+ , creator(mod)
+ {
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ virtual ~Request()
+ {
+ manager->RemoveRequest(this);
+ }
+
+ /** Called when this request succeeds
+ * @param r The query sent back from the nameserver
+ */
+ virtual void OnLookupComplete(const Query* req) = 0;
+
+ /** Called when this request fails or times out.
+ * @param r The query sent back from the nameserver, check the error code.
+ */
+ virtual void OnError(const Query* req) { }
+
+ /** Used to time out the query, calls OnError and asks the TimerManager
+ * to delete this request
+ */
+ bool Tick(time_t now)
+ {
+ Query rr(*this);
+ rr.error = ERROR_TIMEDOUT;
+ this->OnError(&rr);
+ delete this;
+ return false;
+ }
+ };
+
+} // namespace DNS
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "modules.h"
+
+class HashProvider : public DataProvider
+{
+ public:
+ const unsigned int out_size;
+ const unsigned int block_size;
+ HashProvider(Module* mod, const std::string& Name, unsigned int osiz = 0, unsigned int bsiz = 0)
+ : DataProvider(mod, "hash/" + Name), out_size(osiz), block_size(bsiz)
+ {
+ }
+
+ virtual std::string GenerateRaw(const std::string& data) = 0;
+
+ virtual std::string ToPrintable(const std::string& raw)
+ {
+ return BinToHex(raw);
+ }
+
+ virtual bool Compare(const std::string& input, const std::string& hash)
+ {
+ return InspIRCd::TimingSafeCompare(Generate(input), hash);
+ }
+
+ std::string Generate(const std::string& data)
+ {
+ return ToPrintable(GenerateRaw(data));
+ }
+
+ /** HMAC algorithm, RFC 2104 */
+ std::string hmac(const std::string& key, const std::string& msg)
+ {
+ std::string hmac1, hmac2;
+ std::string kbuf = key.length() > block_size ? GenerateRaw(key) : key;
+ kbuf.resize(block_size);
+
+ for (size_t n = 0; n < block_size; n++)
+ {
+ hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C));
+ hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
+ }
+ hmac2.append(msg);
+ hmac1.append(GenerateRaw(hmac2));
+ return GenerateRaw(hmac1);
+ }
+
+ bool IsKDF() const
+ {
+ return (!block_size);
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "base.h"
+
+#include <string>
+#include <sstream>
+#include <map>
+
+/** A modifyable list of HTTP header fields
+ */
+class HTTPHeaders
+{
+ protected:
+ std::map<std::string,std::string> headers;
+ public:
+
+ /** Set the value of a header
+ * Sets the value of the named header. If the header is already present, it will be replaced
+ */
+ void SetHeader(const std::string &name, const std::string &data)
+ {
+ headers[name] = data;
+ }
+
+ /** Set the value of a header, only if it doesn't exist already
+ * Sets the value of the named header. If the header is already present, it will NOT be updated
+ */
+ void CreateHeader(const std::string &name, const std::string &data)
+ {
+ if (!IsSet(name))
+ SetHeader(name, data);
+ }
+
+ /** Remove the named header
+ */
+ void RemoveHeader(const std::string &name)
+ {
+ headers.erase(name);
+ }
+
+ /** Remove all headers
+ */
+ void Clear()
+ {
+ headers.clear();
+ }
+
+ /** Get the value of a header
+ * @return The value of the header, or an empty string
+ */
+ std::string GetHeader(const std::string &name)
+ {
+ std::map<std::string,std::string>::iterator it = headers.find(name);
+ if (it == headers.end())
+ return std::string();
+
+ return it->second;
+ }
+
+ /** Check if the given header is specified
+ * @return true if the header is specified
+ */
+ bool IsSet(const std::string &name)
+ {
+ std::map<std::string,std::string>::iterator it = headers.find(name);
+ return (it != headers.end());
+ }
+
+ /** Get all headers, formatted by the HTTP protocol
+ * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end
+ */
+ std::string GetFormattedHeaders()
+ {
+ std::string re;
+
+ for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++)
+ re += i->first + ": " + i->second + "\r\n";
+
+ return re;
+ }
+};
+
+class HttpServerSocket;
+
+/** This class represents a HTTP request.
+ */
+class HTTPRequest : public Event
+{
+ protected:
+ std::string type;
+ std::string document;
+ std::string ipaddr;
+ std::string postdata;
+
+ public:
+
+ HTTPHeaders *headers;
+ int errorcode;
+
+ /** A socket pointer, which you must return in your HTTPDocument class
+ * if you reply to this request.
+ */
+ HttpServerSocket* sock;
+
+ /** Initialize HTTPRequest.
+ * This constructor is called by m_httpd.so to initialize the class.
+ * @param request_type The request type, e.g. GET, POST, HEAD
+ * @param uri The URI, e.g. /page
+ * @param hdr The headers sent with the request
+ * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
+ * @param ip The IP address making the web request.
+ * @param pdata The post data (content after headers) received with the request, up to Content-Length in size
+ */
+ HTTPRequest(Module* me, const std::string &eventid, const std::string &request_type, const std::string &uri,
+ HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
+ : Event(me, eventid), type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket)
+ {
+ }
+
+ /** Get the post data (request content).
+ * All post data will be returned, including carriage returns and linefeeds.
+ * @return The postdata
+ */
+ std::string& GetPostData()
+ {
+ return postdata;
+ }
+
+ /** Get the request type.
+ * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
+ * @return The request type, e.g. GET, POST, HEAD
+ */
+ std::string& GetType()
+ {
+ return type;
+ }
+
+ /** Get URI.
+ * The URI string (URL minus hostname and scheme) will be provided by this function.
+ * @return The URI being requested
+ */
+ std::string& GetURI()
+ {
+ return document;
+ }
+
+ /** Get IP address of requester.
+ * The requesting system's ip address will be returned.
+ * @return The IP address as a string
+ */
+ std::string& GetIP()
+ {
+ return ipaddr;
+ }
+};
+
+/** If you want to reply to HTTP requests, you must return a HTTPDocumentResponse to
+ * the httpd module via the HTTPdAPI.
+ * When you initialize this class you initialize it with all components required to
+ * form a valid HTTP response: the document data and a response code.
+ * You can add additional HTTP headers, if you want.
+ */
+class HTTPDocumentResponse
+{
+ public:
+ /** Module that generated this reply
+ */
+ Module* const module;
+
+ std::stringstream* document;
+ unsigned int responsecode;
+
+ /** Any extra headers to include with the defaults
+ */
+ HTTPHeaders headers;
+
+ HTTPRequest& src;
+
+ /** Initialize a HTTPDocumentResponse ready for sending to the httpd module.
+ * @param mod A pointer to the module who responded to the request
+ * @param req The request you obtained from the HTTPRequest at an earlier time
+ * @param doc A stringstream containing the document body
+ * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
+ * based upon the response code.
+ */
+ HTTPDocumentResponse(Module* mod, HTTPRequest& req, std::stringstream* doc, unsigned int response)
+ : module(mod), document(doc), responsecode(response), src(req)
+ {
+ }
+};
+
+class HTTPdAPIBase : public DataProvider
+{
+ public:
+ HTTPdAPIBase(Module* parent)
+ : DataProvider(parent, "m_httpd_api")
+ {
+ }
+
+ /** Answer an incoming HTTP request with the provided document
+ * @param response The response created by your module that will be sent to the client
+ */
+ virtual void SendResponse(HTTPDocumentResponse& response) = 0;
+};
+
+/** The API provided by the httpd module that allows other modules to respond to incoming
+ * HTTP requests
+ */
+class HTTPdAPI : public dynamic_reference<HTTPdAPIBase>
+{
+ public:
+ HTTPdAPI(Module* parent)
+ : dynamic_reference<HTTPdAPIBase>(parent, "m_httpd_api")
+ {
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+typedef int LDAPQuery;
+
+class LDAPException : public ModuleException
+{
+ public:
+ LDAPException(const std::string& reason) : ModuleException(reason) { }
+
+ virtual ~LDAPException() throw() { }
+};
+
+struct LDAPModification
+{
+ enum LDAPOperation
+ {
+ LDAP_ADD,
+ LDAP_DEL,
+ LDAP_REPLACE
+ };
+
+ LDAPOperation op;
+ std::string name;
+ std::vector<std::string> values;
+};
+
+typedef std::vector<LDAPModification> LDAPMods;
+
+struct LDAPAttributes : public std::map<std::string, std::vector<std::string> >
+{
+ size_t size(const std::string& attr) const
+ {
+ const std::vector<std::string>& array = this->getArray(attr);
+ return array.size();
+ }
+
+ const std::vector<std::string> keys() const
+ {
+ std::vector<std::string> k;
+ for (const_iterator it = this->begin(), it_end = this->end(); it != it_end; ++it)
+ k.push_back(it->first);
+ return k;
+ }
+
+ const std::string& get(const std::string& attr) const
+ {
+ const std::vector<std::string>& array = this->getArray(attr);
+ if (array.empty())
+ throw LDAPException("Empty attribute " + attr + " in LDAPResult::get");
+ return array[0];
+ }
+
+ const std::vector<std::string>& getArray(const std::string& attr) const
+ {
+ const_iterator it = this->find(attr);
+ if (it == this->end())
+ throw LDAPException("Unknown attribute " + attr + " in LDAPResult::getArray");
+ return it->second;
+ }
+};
+
+struct LDAPResult
+{
+ std::vector<LDAPAttributes> messages;
+ std::string error;
+
+ enum QueryType
+ {
+ QUERY_UNKNOWN,
+ QUERY_BIND,
+ QUERY_SEARCH,
+ QUERY_ADD,
+ QUERY_DELETE,
+ QUERY_MODIFY,
+ QUERY_COMPARE
+ };
+
+ QueryType type;
+ LDAPQuery id;
+
+ LDAPResult()
+ : type(QUERY_UNKNOWN), id(-1)
+ {
+ }
+
+ size_t size() const
+ {
+ return this->messages.size();
+ }
+
+ bool empty() const
+ {
+ return this->messages.empty();
+ }
+
+ const LDAPAttributes& get(size_t sz) const
+ {
+ if (sz >= this->messages.size())
+ throw LDAPException("Index out of range");
+ return this->messages[sz];
+ }
+
+ const std::string& getError() const
+ {
+ return this->error;
+ }
+};
+
+class LDAPInterface
+{
+ public:
+ ModuleRef creator;
+
+ LDAPInterface(Module* m) : creator(m) { }
+ virtual ~LDAPInterface() { }
+
+ virtual void OnResult(const LDAPResult& r) = 0;
+ virtual void OnError(const LDAPResult& err) = 0;
+};
+
+class LDAPProvider : public DataProvider
+{
+ public:
+ LDAPProvider(Module* Creator, const std::string& Name)
+ : DataProvider(Creator, Name) { }
+
+ /** Attempt to bind to the LDAP server as a manager
+ * @param i The LDAPInterface the result is sent to
+ * @return The query ID
+ */
+ virtual LDAPQuery BindAsManager(LDAPInterface *i) = 0;
+
+ /** Bind to LDAP
+ * @param i The LDAPInterface the result is sent to
+ * @param who The binddn
+ * @param pass The password
+ * @return The query ID
+ */
+ virtual LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) = 0;
+
+ /** Search ldap for the specified filter
+ * @param i The LDAPInterface the result is sent to
+ * @param base The base DN to search
+ * @param filter The filter to apply
+ * @return The query ID
+ */
+ virtual LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) = 0;
+
+ /** Add an entry to LDAP
+ * @param i The LDAPInterface the result is sent to
+ * @param dn The dn of the entry to add
+ * @param attributes The attributes
+ * @return The query ID
+ */
+ virtual LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) = 0;
+
+ /** Delete an entry from LDAP
+ * @param i The LDAPInterface the result is sent to
+ * @param dn The dn of the entry to delete
+ * @return The query ID
+ */
+ virtual LDAPQuery Del(LDAPInterface* i, const std::string& dn) = 0;
+
+ /** Modify an existing entry in LDAP
+ * @param i The LDAPInterface the result is sent to
+ * @param base The base DN to modify
+ * @param attributes The attributes to modify
+ * @return The query ID
+ */
+ virtual LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) = 0;
+
+ /** Compare an attribute in LDAP with our value
+ * @param i The LDAPInterface the result is sent to
+ * @param dn DN to use for comparing
+ * @param attr Attr of DN to compare with
+ * @param val value to compare attr of dn
+ * @return the query ID
+ */
+ virtual LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0;
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+class Regex : public classbase
+{
+protected:
+ /** The uncompiled regex string. */
+ std::string regex_string;
+
+ // Constructor may as well be protected, as this class is abstract.
+ Regex(const std::string& rx) : regex_string(rx) { }
+
+public:
+
+ virtual ~Regex() { }
+
+ virtual bool Matches(const std::string& text) = 0;
+
+ const std::string& GetRegexString() const
+ {
+ return regex_string;
+ }
+};
+
+class RegexFactory : public DataProvider
+{
+ public:
+ RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) { }
+
+ virtual Regex* Create(const std::string& expr) = 0;
+};
+
+class RegexException : public ModuleException
+{
+ public:
+ RegexException(const std::string& regex, const std::string& error)
+ : ModuleException("Error in regex '" + regex + "': " + error) { }
+
+ RegexException(const std::string& regex, const std::string& error, int offset)
+ : ModuleException("Error in regex '" + regex + "' at offset " + ConvToStr(offset) + ": " + error) { }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class SASLFallback : public Event
+{
+ public:
+ const parameterlist& params;
+ SASLFallback(Module* me, const parameterlist& p)
+ : Event(me, "sasl_fallback"), params(p)
+ {
+ Send();
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+struct AddServerEvent : public Event
+{
+ const std::string servername;
+ AddServerEvent(Module* me, const std::string& name)
+ : Event(me, "new_server"), servername(name)
+ {
+ Send();
+ }
+};
+
+struct DelServerEvent : public Event
+{
+ const std::string servername;
+ DelServerEvent(Module* me, const std::string& name)
+ : Event(me, "lost_server"), servername(name)
+ {
+ Send();
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** Defines the error types which SQLerror may be set to
+ */
+enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
+
+/** A list of format parameters for an SQLquery object.
+ */
+typedef std::vector<std::string> ParamL;
+
+typedef std::map<std::string, std::string> ParamM;
+
+class SQLEntry
+{
+ public:
+ std::string value;
+ bool nul;
+ SQLEntry() : nul(true) {}
+ SQLEntry(const std::string& v) : value(v), nul(false) {}
+ inline operator std::string&() { return value; }
+};
+
+typedef std::vector<SQLEntry> SQLEntries;
+
+/**
+ * Result of an SQL query. Only valid inside OnResult
+ */
+class SQLResult : public classbase
+{
+ public:
+ /**
+ * Return the number of rows in the result.
+ *
+ * Note that if you have perfomed an INSERT or UPDATE query or other
+ * query which will not return rows, this will return the number of
+ * affected rows. In this case you SHOULD NEVER access any of the result
+ * set rows, as there aren't any!
+ * @returns Number of rows in the result set.
+ */
+ virtual int Rows() = 0;
+
+ /**
+ * Return a single row (result of the query). The internal row counter
+ * is incremented by one.
+ *
+ * @param result Storage for the result data.
+ * @returns true if there was a row, false if no row exists (end of
+ * iteration)
+ */
+ virtual bool GetRow(SQLEntries& result) = 0;
+
+ /** Returns column names for the items in this row
+ */
+ virtual void GetCols(std::vector<std::string>& result) = 0;
+};
+
+/** SQLerror holds the error state of a request.
+ * The error string varies from database software to database software
+ * and should be used to display informational error messages to users.
+ */
+class SQLerror
+{
+ public:
+ /** The error id
+ */
+ SQLerrorNum id;
+
+ /** The error string
+ */
+ std::string str;
+
+ /** Initialize an SQLerror
+ * @param i The error ID to set
+ * @param s The (optional) error string to set
+ */
+ SQLerror(SQLerrorNum i, const std::string &s = "")
+ : id(i), str(s)
+ {
+ }
+
+ /** Return the error string for an error
+ */
+ const char* Str()
+ {
+ if(str.length())
+ return str.c_str();
+
+ switch(id)
+ {
+ case SQL_BAD_DBID:
+ return "Invalid database ID";
+ case SQL_BAD_CONN:
+ return "Invalid connection";
+ case SQL_QSEND_FAIL:
+ return "Sending query failed";
+ case SQL_QREPLY_FAIL:
+ return "Getting query result failed";
+ default:
+ return "Unknown error";
+ }
+ }
+};
+
+/**
+ * Object representing an SQL query. This should be allocated on the heap and
+ * passed to an SQLProvider, which will free it when the query is complete or
+ * when the querying module is unloaded.
+ *
+ * You should store whatever information is needed to have the callbacks work in
+ * this object (UID of user, channel name, etc).
+ */
+class SQLQuery : public classbase
+{
+ public:
+ ModuleRef creator;
+
+ SQLQuery(Module* Creator) : creator(Creator) {}
+ virtual ~SQLQuery() {}
+
+ virtual void OnResult(SQLResult& result) = 0;
+ /**
+ * Called when the query fails
+ */
+ virtual void OnError(SQLerror& error) { }
+};
+
+/**
+ * Provider object for SQL servers
+ */
+class SQLProvider : public DataProvider
+{
+ public:
+ SQLProvider(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
+ /** Submit an asynchronous SQL request
+ * @param callback The result reporting point
+ * @param query The hardcoded query string. If you have parameters to substitute, see below.
+ */
+ virtual void submit(SQLQuery* callback, const std::string& query) = 0;
+
+ /** Submit an asynchronous SQL request
+ * @param callback The result reporting point
+ * @param format The simple parameterized query string ('?' parameters)
+ * @param p Parameters to fill in for the '?' entries
+ */
+ virtual void submit(SQLQuery* callback, const std::string& format, const ParamL& p) = 0;
+
+ /** Submit an asynchronous SQL request.
+ * @param callback The result reporting point
+ * @param format The parameterized query string ('$name' parameters)
+ * @param p Parameters to fill in for the '$name' entries
+ */
+ virtual void submit(SQLQuery* callback, const std::string& format, const ParamM& p) = 0;
+
+ /** Convenience function to prepare a map from a User* */
+ void PopulateUserInfo(User* user, ParamM& userinfo)
+ {
+ userinfo["nick"] = user->nick;
+ userinfo["host"] = user->host;
+ userinfo["ip"] = user->GetIPString();
+ userinfo["gecos"] = user->fullname;
+ userinfo["ident"] = user->ident;
+ userinfo["server"] = user->server->GetName();
+ userinfo["uuid"] = user->uuid;
+ }
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include <string>
+#include "iohook.h"
+
+/** ssl_cert is a class which abstracts SSL certificate
+ * and key information.
+ *
+ * Because gnutls and openssl represent key information in
+ * wildly different ways, this class allows it to be accessed
+ * in a unified manner. These classes are attached to ssl-
+ * connected local users using SSLCertExt
+ */
+class ssl_cert : public refcountbase
+{
+ public:
+ std::string dn;
+ std::string issuer;
+ std::string error;
+ std::string fingerprint;
+ bool trusted, invalid, unknownsigner, revoked;
+
+ ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
+
+ /** Get certificate distinguished name
+ * @return Certificate DN
+ */
+ const std::string& GetDN()
+ {
+ return dn;
+ }
+
+ /** Get Certificate issuer
+ * @return Certificate issuer
+ */
+ const std::string& GetIssuer()
+ {
+ return issuer;
+ }
+
+ /** Get error string if an error has occured
+ * @return The error associated with this users certificate,
+ * or an empty string if there is no error.
+ */
+ const std::string& GetError()
+ {
+ return error;
+ }
+
+ /** Get key fingerprint.
+ * @return The key fingerprint as a hex string.
+ */
+ const std::string& GetFingerprint()
+ {
+ return fingerprint;
+ }
+
+ /** Get trust status
+ * @return True if this is a trusted certificate
+ * (the certificate chain validates)
+ */
+ bool IsTrusted()
+ {
+ return trusted;
+ }
+
+ /** Get validity status
+ * @return True if the certificate itself is
+ * correctly formed.
+ */
+ bool IsInvalid()
+ {
+ return invalid;
+ }
+
+ /** Get signer status
+ * @return True if the certificate appears to be
+ * self-signed.
+ */
+ bool IsUnknownSigner()
+ {
+ return unknownsigner;
+ }
+
+ /** Get revokation status.
+ * @return True if the certificate is revoked.
+ * Note that this only works properly for GnuTLS
+ * right now.
+ */
+ bool IsRevoked()
+ {
+ return revoked;
+ }
+
+ bool IsCAVerified()
+ {
+ return trusted && !invalid && !revoked && !unknownsigner && error.empty();
+ }
+
+ std::string GetMetaLine()
+ {
+ std::stringstream value;
+ bool hasError = !error.empty();
+ value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
+ << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
+ if (hasError)
+ value << GetError();
+ else
+ value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
+ return value.str();
+ }
+};
+
+class SSLIOHook : public IOHook
+{
+ protected:
+ /** Peer SSL certificate, set by the SSL module
+ */
+ reference<ssl_cert> certificate;
+
+ public:
+ SSLIOHook(IOHookProvider* hookprov)
+ : IOHook(hookprov)
+ {
+ }
+
+ /**
+ * Get the certificate sent by this peer
+ * @return The SSL certificate sent by the peer, NULL if no cert was sent
+ */
+ ssl_cert* GetCertificate() const
+ {
+ return certificate;
+ }
+
+ /**
+ * Get the fingerprint of the peer's certificate
+ * @return The fingerprint of the SSL client certificate sent by the peer,
+ * empty if no cert was sent
+ */
+ std::string GetFingerprint() const
+ {
+ ssl_cert* cert = GetCertificate();
+ if (cert)
+ return cert->GetFingerprint();
+ return "";
+ }
+};
+
+/** Helper functions for obtaining SSL client certificates and key fingerprints
+ * from StreamSockets
+ */
+class SSLClientCert
+{
+ public:
+ /**
+ * Get the client certificate from a socket
+ * @param sock The socket to get the certificate from, the socket does not have to use SSL
+ * @return The SSL client certificate information, NULL if the peer is not using SSL
+ */
+ static ssl_cert* GetCertificate(StreamSocket* sock)
+ {
+ IOHook* iohook = sock->GetIOHook();
+ if ((!iohook) || (iohook->prov->type != IOHookProvider::IOH_SSL))
+ return NULL;
+
+ SSLIOHook* ssliohook = static_cast<SSLIOHook*>(iohook);
+ return ssliohook->GetCertificate();
+ }
+
+ /**
+ * Get the fingerprint of a client certificate from a socket
+ * @param sock The socket to get the certificate fingerprint from, the
+ * socket does not have to use SSL
+ * @return The key fingerprint from the SSL certificate sent by the peer,
+ * empty if no cert was sent or the peer is not using SSL
+ */
+ static std::string GetFingerprint(StreamSocket* sock)
+ {
+ ssl_cert* cert = SSLClientCert::GetCertificate(sock);
+ if (cert)
+ return cert->GetFingerprint();
+ return "";
+ }
+};
+
+class UserCertificateAPIBase : public DataProvider
+{
+ public:
+ UserCertificateAPIBase(Module* parent)
+ : DataProvider(parent, "m_sslinfo_api")
+ {
+ }
+
+ /** Get the SSL certificate of a user
+ * @param user The user whose certificate to get, user may be remote
+ * @return The SSL certificate of the user or NULL if the user is not using SSL
+ */
+ virtual ssl_cert* GetCertificate(User* user) = 0;
+
+ /** Get the key fingerprint from a user's certificate
+ * @param user The user whose key fingerprint to get, user may be remote
+ * @return The key fingerprint from the user's SSL certificate or an empty string
+ * if the user is not using SSL or did not provide a client certificate
+ */
+ std::string GetFingerprint(User* user)
+ {
+ ssl_cert* cert = GetCertificate(user);
+ if (cert)
+ return cert->GetFingerprint();
+ return "";
+ }
+};
+
+/** API implemented by m_sslinfo that allows modules to retrive the SSL certificate
+ * information of local and remote users. It can also be used to find out whether a
+ * user is using SSL or not.
+ */
+class UserCertificateAPI : public dynamic_reference<UserCertificateAPIBase>
+{
+ public:
+ UserCertificateAPI(Module* parent)
+ : dynamic_reference<UserCertificateAPIBase>(parent, "m_sslinfo_api")
+ {
+ }
+};
*/
-#ifndef NUMERICS_H
-#define NUMERICS_H
+#pragma once
/*
- * This file is aimed providing a string that is easier to use than using the numeric
- * directly.
- *
* Module authors, please note!
* While you are free to use any numerics on this list, like the rest of the core, you
* *should not* be editing it!
/*
* Reply range of numerics.
*/
- RPL_WELCOME = 1, // 2812, not 1459
- RPL_YOURHOSTIS = 2, // 2812, not 1459
- RPL_SERVERCREATED = 3, // 2812, not 1459
- RPL_SERVERVERSION = 4, // 2812, not 1459
- RPL_ISUPPORT = 5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored)
-
- RPL_MAP = 6, // unrealircd
- RPL_ENDMAP = 7, // unrealircd
- RPL_SNOMASKIS = 8, // unrealircd
-
- RPL_YOURUUID = 42, // taken from ircnet
-
- RPL_UMODEIS = 221,
- RPL_RULES = 232, // unrealircd
- RPL_ADMINME = 256,
- RPL_ADMINLOC1 = 257,
- RPL_ADMINLOC2 = 258,
- RPL_ADMINEMAIL = 259,
- RPL_MAPUSERS = 270, // insp-specific
-
- RPL_SYNTAX = 304, // insp-specific
-
- RPL_UNAWAY = 305,
- RPL_NOWAWAY = 306,
-
- RPL_RULESTART = 308, // unrealircd
- RPL_RULESEND = 309, // unrealircd
- RPL_CHANNELMODEIS = 324,
- RPL_CHANNELCREATED = 329, // ???
- RPL_NOTOPICSET = 331,
- RPL_TOPIC = 332,
- RPL_TOPICTIME = 333, // not RFC, extremely common though
-
- RPL_INVITING = 341,
- RPL_INVITELIST = 346, // insp-specific (stolen from ircu)
- RPL_ENDOFINVITELIST = 347, // insp-specific (stolen from ircu)
- RPL_VERSION = 351,
- RPL_NAMREPLY = 353,
- RPL_ENDOFNAMES = 366,
-
- RPL_INFO = 371,
- RPL_ENDOFINFO = 374,
- RPL_MOTD = 372,
- RPL_MOTDSTART = 375,
- RPL_ENDOFMOTD = 376,
-
- RPL_YOUAREOPER = 381,
- RPL_REHASHING = 382,
- RPL_TIME = 391,
- RPL_YOURDISPLAYEDHOST = 396, // from charybdis/etc, common convention
+ RPL_WELCOME = 1, // 2812, not 1459
+ RPL_YOURHOSTIS = 2, // 2812, not 1459
+ RPL_SERVERCREATED = 3, // 2812, not 1459
+ RPL_SERVERVERSION = 4, // 2812, not 1459
+ RPL_ISUPPORT = 5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored)
+
+ RPL_MAP = 6, // unrealircd
+ RPL_ENDMAP = 7, // unrealircd
+ RPL_SNOMASKIS = 8, // unrealircd
+ RPL_REDIR = 10,
+
+ RPL_YOURUUID = 42, // taken from ircnet
+
+ RPL_UMODEIS = 221,
+ RPL_RULES = 232, // unrealircd
+
+ RPL_LUSERCLIENT = 251,
+ RPL_LUSEROP = 252,
+ RPL_LUSERUNKNOWN = 253,
+ RPL_LUSERCHANNELS = 254,
+ RPL_LUSERME = 255,
+
+ RPL_ADMINME = 256,
+ RPL_ADMINLOC1 = 257,
+ RPL_ADMINLOC2 = 258,
+ RPL_ADMINEMAIL = 259,
+
+ RPL_LOCALUSERS = 265,
+ RPL_GLOBALUSERS = 266,
+
+ RPL_MAPUSERS = 270, // insp-specific
+
+ RPL_AWAY = 301,
+
+ RPL_SYNTAX = 304, // insp-specific
+
+ RPL_UNAWAY = 305,
+ RPL_NOWAWAY = 306,
+
+ RPL_RULESTART = 308, // unrealircd
+ RPL_RULESEND = 309, // unrealircd
+
+ RPL_WHOISSERVER = 312,
+ RPL_WHOWASUSER = 314,
+
+ RPL_ENDOFWHO = 315,
+ RPL_ENDOFWHOIS = 318,
+
+ RPL_LISTSTART = 321,
+ RPL_LIST = 322,
+ RPL_LISTEND = 323,
+
+ RPL_CHANNELMODEIS = 324,
+ RPL_CHANNELCREATED = 329, // ???
+ RPL_NOTOPICSET = 331,
+ RPL_TOPIC = 332,
+ RPL_TOPICTIME = 333, // not RFC, extremely common though
+
+ RPL_INVITING = 341,
+ RPL_INVITELIST = 346, // insp-specific (stolen from ircu)
+ RPL_ENDOFINVITELIST = 347, // insp-specific (stolen from ircu)
+ RPL_VERSION = 351,
+ RPL_NAMREPLY = 353,
+ RPL_LINKS = 364,
+ RPL_ENDOFLINKS = 365,
+ RPL_ENDOFNAMES = 366,
+ RPL_ENDOFWHOWAS = 369,
+
+ RPL_INFO = 371,
+ RPL_ENDOFINFO = 374,
+ RPL_MOTD = 372,
+ RPL_MOTDSTART = 375,
+ RPL_ENDOFMOTD = 376,
+
+ RPL_WHOWASIP = 379,
+
+ RPL_YOUAREOPER = 381,
+ RPL_REHASHING = 382,
+ RPL_TIME = 391,
+ RPL_YOURDISPLAYEDHOST = 396, // from charybdis/etc, common convention
/*
* Error range of numerics.
*/
- ERR_NOSUCHNICK = 401,
- ERR_NOSUCHSERVER = 402,
- ERR_NOSUCHCHANNEL = 403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!)
- ERR_CANNOTSENDTOCHAN = 404,
- ERR_TOOMANYCHANNELS = 405,
- ERR_INVALIDCAPSUBCOMMAND = 410, // ratbox/charybdis(?)
- ERR_UNKNOWNCOMMAND = 421,
- ERR_NOMOTD = 422,
- ERR_NORULES = 434, // unrealircd
- ERR_USERNOTINCHANNEL = 441,
- ERR_NOTONCHANNEL = 442,
- ERR_USERONCHANNEL = 443,
- ERR_CANTCHANGENICK = 447, // unrealircd, probably
- ERR_NOTREGISTERED = 451,
- ERR_NEEDMOREPARAMS = 461,
- ERR_ALREADYREGISTERED = 462,
+ ERR_NOSUCHNICK = 401,
+ ERR_NOSUCHSERVER = 402,
+ ERR_NOSUCHCHANNEL = 403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!)
+ ERR_CANNOTSENDTOCHAN = 404,
+ ERR_TOOMANYCHANNELS = 405,
+ ERR_WASNOSUCHNICK = 406,
+ ERR_INVALIDCAPSUBCOMMAND = 410, // ratbox/charybdis(?)
+ ERR_NOTEXTTOSEND = 412,
+ ERR_UNKNOWNCOMMAND = 421,
+ ERR_NOMOTD = 422,
+ ERR_ERRONEUSNICKNAME = 432,
+ ERR_NICKNAMEINUSE = 433,
+ ERR_NORULES = 434, // unrealircd
+ ERR_USERNOTINCHANNEL = 441,
+ ERR_NOTONCHANNEL = 442,
+ ERR_USERONCHANNEL = 443,
+ ERR_CANTCHANGENICK = 447, // unrealircd, probably
+ ERR_NOTREGISTERED = 451,
+ ERR_NEEDMOREPARAMS = 461,
+ ERR_ALREADYREGISTERED = 462,
+ ERR_UNKNOWNMODE = 472,
/*
* A quick side-rant about the next group of numerics..
*
* -- A message from the IRC group for coder sanity, and w00t
*/
- ERR_BADCHANNELKEY = 475,
- ERR_INVITEONLYCHAN = 473,
- ERR_CHANNELISFULL = 471,
- ERR_BANNEDFROMCHAN = 474,
-
- ERR_NOPRIVILEGES = 481, // rfc, beware though, we use this for other things opers may not do also
- ERR_CHANOPRIVSNEEDED = 482, // rfc, beware though, we use this for other things like trying to kick a uline
-
- ERR_ALLMUSTSSL = 490, // unrealircd
- ERR_NOCTCPALLOWED = 492, // XXX: bzzzz. 1459 defines this as ERR_NOSERVICEHOST, research it more and perhaps change this! (ERR_CANNOTSENDTOCHAN?)
- // wtf, we also use this for m_noinvite. UGLY!
- ERR_DELAYREJOIN = 495, // insp-specific, XXX: we should use 'resource temporarily unavailable' from ircnet/ratbox or whatever
- ERR_UNKNOWNSNOMASK = 501, // insp-specific
- ERR_USERSDONTMATCH = 502,
- ERR_CANTJOINOPERSONLY = 520, // unrealircd, but crap to have so many numerics for cant join..
- ERR_CANTSENDTOUSER = 531, // ???
-
- RPL_COMMANDS = 702, // insp-specific
- RPL_COMMANDSEND = 703, // insp-specific
-
- ERR_WORDFILTERED = 936, // insp-specific, would be nice if we could get rid of this..
- ERR_CANTUNLOADMODULE = 972, // insp-specific
- RPL_UNLOADEDMODULE = 973, // insp-specific
- ERR_CANTLOADMODULE = 974, // insp-specific
- RPL_LOADEDMODULE = 975 // insp-specific
+ ERR_BADCHANNELKEY = 475,
+ ERR_INVITEONLYCHAN = 473,
+ ERR_CHANNELISFULL = 471,
+ ERR_BANNEDFROMCHAN = 474,
+
+ ERR_BANLISTFULL = 478,
+
+ ERR_NOPRIVILEGES = 481, // rfc, beware though, we use this for other things opers may not do also
+ ERR_CHANOPRIVSNEEDED = 482, // rfc, beware though, we use this for other things like trying to kick a uline
+
+ ERR_RESTRICTED = 484,
+
+ ERR_ALLMUSTSSL = 490, // unrealircd
+ ERR_NOOPERHOST = 491,
+ ERR_NOCTCPALLOWED = 492, // XXX: bzzzz. 1459 defines this as ERR_NOSERVICEHOST, research it more and perhaps change this! (ERR_CANNOTSENDTOCHAN?)
+ // wtf, we also use this for m_noinvite. UGLY!
+ ERR_DELAYREJOIN = 495, // insp-specific, XXX: we should use 'resource temporarily unavailable' from ircnet/ratbox or whatever
+ ERR_UNKNOWNSNOMASK = 501, // insp-specific
+ ERR_USERSDONTMATCH = 502,
+ ERR_CANTJOINOPERSONLY = 520, // unrealircd, but crap to have so many numerics for cant join..
+ ERR_CANTSENDTOUSER = 531, // ???
+
+ RPL_COMMANDS = 702, // insp-specific
+ RPL_COMMANDSEND = 703, // insp-specific
+
+ ERR_CHANOPEN = 713,
+ ERR_KNOCKONCHAN = 714,
+
+ ERR_WORDFILTERED = 936, // insp-specific, would be nice if we could get rid of this..
+ ERR_CANTUNLOADMODULE = 972, // insp-specific
+ RPL_UNLOADEDMODULE = 973, // insp-specific
+ ERR_CANTLOADMODULE = 974, // insp-specific
+ RPL_LOADEDMODULE = 975 // insp-specific
};
-
-#endif
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class CoreExport ParamModeBase : public ModeHandler
+{
+ private:
+ virtual void OnUnsetInternal(User* source, Channel* chan) = 0;
+
+ public:
+ ParamModeBase(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps)
+ : ModeHandler(Creator, Name, modeletter, ps, MODETYPE_CHANNEL, MC_PARAM) { }
+
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE;
+
+ // Does nothing by default
+ void OnUnset(User* source, Channel* chan) { }
+ virtual ModeAction OnSet(User* source, Channel* chan, std::string& param) = 0;
+ virtual void GetParameter(Channel* chan, std::string& out) = 0;
+};
+
+/** Defines a parameter mode
+ * T = Child class
+ * ExtItemT = Type of the extension item used to store the parameter
+ *
+ * When unsetting the mode, the extension is automatically unset.
+ */
+template <typename T, typename ExtItemT>
+class ParamMode : public ParamModeBase
+{
+ public:
+ ExtItemT ext;
+
+ /**
+ * @param Creator Module handling this mode
+ * @param Name The internal name of this mode
+ * @param modeletter The mode letter of this mode
+ * @param ps The parameter type of this mode, one of ParamSpec
+ */
+ ParamMode(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps = PARAM_SETONLY)
+ : ParamModeBase(Creator, Name, modeletter, ps)
+ , ext("parammode_" + Name, Creator)
+ {
+ }
+
+ void OnUnsetInternal(User* source, Channel* chan) CXX11_OVERRIDE
+ {
+ T* mh = static_cast<T*>(this);
+ mh->OnUnset(source, chan);
+ ext.unset(chan);
+ }
+
+ void GetParameter(Channel* chan, std::string& out) CXX11_OVERRIDE
+ {
+ T* mh = static_cast<T*>(this);
+ mh->SerializeParam(chan, ext.get(chan), out);
+ }
+};
*/
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
+#pragma once
#include "hashcomp.h"
typedef std::vector<std::string> parameterlist;
-class ProtoServer
+class ProtocolServer
{
public:
- std::string servername;
- std::string parentname;
- std::string gecos;
- unsigned int usercount;
- unsigned int opercount;
- unsigned int latencyms;
+ /** Send metadata related to this server to the target server
+ * @param key The 'key' of the data
+ * @param data The string representation of the data
+ */
+ virtual void SendMetaData(const std::string& key, const std::string& data) = 0;
};
-typedef std::list<ProtoServer> ProtoServerList;
-
-class ProtocolInterface
+class CoreExport ProtocolInterface
{
public:
- ProtocolInterface() { }
+ typedef ProtocolServer Server;
+
+ class ServerInfo
+ {
+ public:
+ std::string servername;
+ std::string parentname;
+ std::string gecos;
+ unsigned int usercount;
+ unsigned int opercount;
+ unsigned int latencyms;
+ };
+
+ typedef std::vector<ServerInfo> ServerList;
+
virtual ~ProtocolInterface() { }
- /** Send an ENCAP message to one or more linked servers.
+ /** Send an ENCAP message to all servers matching a wildcard string.
* See the protocol documentation for the purpose of ENCAP.
- * @param encap This is a list of string parameters, the first of which must be a server ID or glob matching servernames.
- * The second must be a subcommand. All subsequent parameters are dependant on the subcommand.
+ * @param targetmask The target server mask (can contain wildcards)
+ * @param cmd The ENCAP subcommand
+ * @param params List of string parameters which are dependant on the subcommand
+ * @param source The source of the message (prefix), must be a local user or NULL which means use local server
+ * @return Always true if the target mask contains wildcards; otherwise true if the server name was found,
+ * and the message was sent, false if it was not found.
* ENCAP (should) be used instead of creating new protocol messages for easier third party application support.
- * @return True if the message was sent out (target exists)
*/
- virtual bool SendEncapsulatedData(const parameterlist &encap) { return false; }
+ virtual bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const parameterlist& params, User* source = NULL) { return false; }
+
+ /** Send an ENCAP message to all servers.
+ * See the protocol documentation for the purpose of ENCAP.
+ * @param cmd The ENCAP subcommand
+ * @param params List of string parameters which are dependant on the subcommand
+ * @param source The source of the message (prefix), must be a local user or a user behind 'omit'
+ * or NULL which is equivalent to the local server
+ * @param omit If non-NULL the message won't be sent in the direction of this server, useful for forwarding messages
+ */
+ virtual void BroadcastEncap(const std::string& cmd, const parameterlist& params, User* source = NULL, User* omit = NULL) { }
- /** Send metadata for an object to other linked servers.
- * @param target The object to send metadata for.
+ /** Send metadata for a channel to other linked servers.
+ * @param chan The channel to send metadata for
* @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user
* @param data The string representation of the data
*/
- virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data) { }
+ virtual void SendMetaData(Channel* chan, const std::string& key, const std::string& data) { }
+
+ /** Send metadata for a user to other linked servers.
+ * @param user The user to send metadata for
+ * @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user
+ * @param data The string representation of the data
+ */
+ virtual void SendMetaData(User* user, const std::string& key, const std::string& data) { }
+
+ /** Send metadata related to the server to other linked servers.
+ * @param key The 'key' of the data
+ * @param data The string representation of the data
+ */
+ virtual void SendMetaData(const std::string& key, const std::string& data) { }
/** Send a topic change for a channel
* @param channel The channel to change the topic for.
*/
virtual void SendTopic(Channel* channel, std::string &topic) { }
- /** Send mode changes for an object.
- * @param target The channel name or user to send mode changes for.
- * @param modedata The mode changes to send.
- * @param translate A list of translation types
- */
- virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate) { }
-
- /** Convenience function, string wrapper around the above.
- */
- virtual void SendModeStr(const std::string &target, const std::string &modeline)
- {
- irc::spacesepstream x(modeline);
- parameterlist n;
- std::vector<TranslateType> types;
- std::string v;
- while (x.GetToken(v))
- {
- n.push_back(v);
- types.push_back(TR_TEXT);
- }
- SendMode(target, n, types);
- }
-
/** Send a notice to users with a given snomask.
* @param snomask The snomask required for the message to be sent.
* @param text The message to send.
*/
- virtual void SendSNONotice(const std::string &snomask, const std::string &text) { }
+ virtual void SendSNONotice(char snomask, const std::string& text) { }
/** Send raw data to a remote client.
* @param target The user to push data to.
* @param target The channel to message.
* @param status The status character (e.g. %) required to recieve.
* @param text The message to send.
+ * @param type The message type (MSG_PRIVMSG or MSG_NOTICE)
*/
- virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text) { }
+ virtual void SendMessage(Channel* target, char status, const std::string& text, MessageType type = MSG_PRIVMSG) { }
- /** Send a notice to a channel.
- * @param target The channel to message.
- * @param status The status character (e.g. %) required to recieve.
+ /** Send a message to a user.
+ * @param target The user to message.
* @param text The message to send.
+ * @param type The message type (MSG_PRIVMSG or MSG_NOTICE)
*/
- virtual void SendChannelNotice(Channel* target, char status, const std::string &text) { }
+ virtual void SendMessage(User* target, const std::string& text, MessageType type = MSG_PRIVMSG) { }
- /** Send a message to a user.
- * @param target The user to message.
+ /** Send a notice to a channel.
+ * @param target The channel to message.
+ * @param status The status character (e.g. %) required to recieve.
* @param text The message to send.
*/
- virtual void SendUserPrivmsg(User* target, const std::string &text) { }
+ void SendChannelNotice(Channel* target, char status, const std::string &text)
+ {
+ SendMessage(target, status, text, MSG_NOTICE);
+ }
/** Send a notice to a user.
* @param target The user to message.
* @param text The message to send.
*/
- virtual void SendUserNotice(User* target, const std::string &text) { }
+ void SendUserNotice(User* target, const std::string &text)
+ {
+ SendMessage(target, text, MSG_NOTICE);
+ }
/** Fill a list of servers and information about them.
* @param sl The list of servers to fill.
* XXX: document me properly, this is shit.
*/
- virtual void GetServerList(ProtoServerList &sl) { }
+ virtual void GetServerList(ServerList& sl) { }
};
-
-#endif
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class CoreExport Server : public classbase
+{
+ protected:
+ /** The name of this server
+ */
+ const std::string name;
+
+ /** The description of this server.
+ * This can be updated by the protocol module (for remote servers) or by a rehash (for the local server).
+ */
+ std::string description;
+
+ /** True if this server is ulined
+ */
+ bool uline;
+
+ /** True if this server is a silent uline, i.e. silent="true" in the uline block
+ */
+ bool silentuline;
+
+ /** Allow ConfigReaderThread to update the description on a rehash
+ */
+ friend class ConfigReaderThread;
+
+ public:
+ Server(const std::string& srvname, const std::string& srvdesc)
+ : name(srvname), description(srvdesc), uline(false), silentuline(false) { }
+
+ /**
+ * Returns the name of this server
+ * @return The name of this server, for example "irc.inspircd.org".
+ */
+ const std::string& GetName() const { return name; }
+
+ /** Returns the description (GECOS) of this server
+ * @return The description of this server
+ */
+ const std::string& GetDesc() const { return description; }
+
+ /**
+ * Checks whether this server is ulined
+ * @return True if this server is ulined, false otherwise.
+ */
+ bool IsULine() const { return uline; }
+
+ /**
+ * Checks whether this server is a silent uline
+ * Silent uline servers introduce, quit and oper up users without a snotice being generated.
+ * @return True if this server is a silent uline, false otherwise.
+ */
+ bool IsSilentULine() const { return silentuline; }
+};
*/
-#ifndef SNOMASKS_H
-#define SNOMASKS_H
+#pragma once
+class SnomaskManager;
class Snomask
{
- public:
+ /** Description of this snomask, e.g.: OPER, ANNOUNCEMENT, XLINE
+ */
std::string Description;
+
+ /** Information about the last sent message,
+ * used for sending "last message repeated X times" messages
+ */
std::string LastMessage;
- int Count;
- bool LastBlocked;
char LastLetter;
+ unsigned int Count;
+ /** Log and send a message to all opers who have the given snomask set
+ * @param letter The target users of this message
+ * @param desc The description of this snomask, will be prepended to the message
+ * @param msg The message to send
+ */
+ static void Send(char letter, const std::string& desc, const std::string& msg);
+
+ public:
/** Create a new Snomask
*/
- Snomask() : Count(0), LastBlocked(false), LastLetter(0)
- {
- }
+ Snomask();
/** Sends a message to all opers with this snomask.
+ * @param message The message to send
+ * @param remote If true the message will go to the uppercase variant of this snomask
*/
- void SendMessage(const std::string &message, char letter);
+ void SendMessage(const std::string& message, char letter);
/** Sends out the (last message repeated N times) message
*/
void Flush();
+
+ /** Returns the description of this snomask
+ * @param letter The letter of this snomask. If uppercase, the description of the remote
+ * variant of this snomask will be returned (i.e.: "REMOTE" will be prepended to the description).
+ * @return The description of this snomask
+ */
+ std::string GetDescription(char letter) const;
+
+ friend class SnomaskManager;
};
/** Snomask manager handles routing of SNOMASK (usermode +s) messages to opers.
* Modules and the core can enable and disable snomask characters. If they do,
* then sending snomasks using these characters becomes possible.
*/
-class CoreExport SnomaskManager
+class CoreExport SnomaskManager : public fakederef<SnomaskManager>
{
- public:
Snomask masks[26];
+ public:
/** Create a new SnomaskManager
*/
SnomaskManager();
*/
void WriteGlobalSno(char letter, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
/** Called once per 5 seconds from the mainloop, this flushes any cached
* snotices. The way the caching works is as follows:
* Calls to WriteToSnoMask write to a cache, if the call is the same as it was
* is not particularly significant, in order to keep notices going out.
*/
void FlushSnotices();
-};
-#endif
+ /** Check whether a given character is an enabled (initialized) snomask.
+ * Valid snomask chars are lower- or uppercase letters and have a description.
+ * Snomasks are initialized with EnableSnomask().
+ * @param ch The character to check
+ * @return True if the given char is allowed to be set via +s.
+ */
+ bool IsSnomaskUsable(char ch) const;
+};
*/
-#ifndef INSPIRCD_SOCKET_H
-#define INSPIRCD_SOCKET_H
+#pragma once
#ifndef _WIN32
*/
CoreExport bool MatchCIDR(const std::string &address, const std::string &cidr_mask, bool match_with_username);
- /** Return the size of the structure for syscall passing */
- inline int sa_size(const irc::sockets::sockaddrs& sa) { return sa.sa_size(); }
-
/** Convert an address-port pair into a binary sockaddr
* @param addr The IP address, IPv4 or IPv6
* @param port The port, 0 for unspecified
* @return true if the conversion was successful, false if unknown address family
*/
CoreExport bool satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port);
-
- /** Convert a binary sockaddr to a user-readable string.
- * This means IPv6 addresses are written as [::1]:6667, and *:6668 is used for 0.0.0.0:6668
- * @param sa The structure to convert
- * @return The string; "<unknown>" if not a valid address
- */
- inline std::string satouser(const irc::sockets::sockaddrs& sa) { return sa.str(); }
}
}
+#include "iohook.h"
#include "socketengine.h"
/** This class handles incoming connections on client ports.
* It will create a new User for every valid connection
int bind_port;
/** Human-readable bind description */
std::string bind_desc;
+
+ /** The IOHook provider which handles connections on this socket,
+ * NULL if there is none.
+ */
+ dynamic_reference_nocheck<IOHookProvider> iohookprov;
+
/** Create a new listening socket
*/
ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to);
/** Handles sockets internals crap of a connection, convenience wrapper really
*/
void AcceptInternal();
-};
-
-#endif
+ /** Inspects the bind block belonging to this socket to set the name of the IO hook
+ * provider which this socket will use for incoming connections.
+ * @return True if the IO hook provider was found or none was given, false otherwise.
+ */
+ bool ResetIOHookProvider();
+};
*/
-#ifndef SOCKETENGINE_H
-#define SOCKETENGINE_H
+#pragma once
#include <vector>
#include <string>
#include <map>
-#include "inspircd_config.h"
+#include "config.h"
#include "socket.h"
#include "base.h"
/** Add a trial write. During the next DispatchEvents invocation, this
* will call HandleEvent with EVENT_WRITE unless writes are known to be
* blocking.
- *
+ *
* This could be used to group several writes together into a single
* send() syscall, or to ensure that writes are blocking when attempting
* to use FD_WANT_FAST_WRITE.
/** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
* Reset by SE before running EVENT_WRITE
*/
- FD_WRITE_WILL_BLOCK = 0x8000,
+ FD_WRITE_WILL_BLOCK = 0x8000,
/** Mask for trial read/trial write */
FD_TRIAL_NOTE_MASK = 0x5000
private:
/** Private state maintained by socket engine */
int event_mask;
+
+ void SetEventMask(int mask) { event_mask = mask; }
+
protected:
/** File descriptor.
* All events which can be handled must have a file descriptor. This
*/
class CoreExport SocketEngine
{
+ public:
+ /** Socket engine statistics: count of various events, bandwidth usage
+ */
+ class Statistics
+ {
+ mutable size_t indata;
+ mutable size_t outdata;
+ mutable time_t lastempty;
+
+ /** Reset the byte counters and lastempty if there wasn't a reset in this second.
+ */
+ void CheckFlush() const;
+
+ public:
+ /** Constructor, initializes member vars except indata and outdata because those are set to 0
+ * in CheckFlush() the first time Update() or GetBandwidth() is called.
+ */
+ Statistics() : lastempty(0), TotalEvents(0), ReadEvents(0), WriteEvents(0), ErrorEvents(0) { }
+
+ /** Increase the counters for bytes sent/received in this second.
+ * @param len_in Bytes received, 0 if updating number of bytes written.
+ * @param len_out Bytes sent, 0 if updating number of bytes read.
+ */
+ void Update(size_t len_in, size_t len_out);
+
+ /** Get data transfer statistics.
+ * @param kbitspersec_in Filled with incoming traffic in this second in kbit/s.
+ * @param kbitspersec_out Filled with outgoing traffic in this second in kbit/s.
+ * @param kbitspersec_total Filled with total traffic in this second in kbit/s.
+ */
+ void CoreExport GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const;
+
+ unsigned long TotalEvents;
+ unsigned long ReadEvents;
+ unsigned long WriteEvents;
+ unsigned long ErrorEvents;
+ };
+
+ private:
+ /** Reference table, contains all current handlers
+ **/
+ static std::vector<EventHandler*> ref;
+
protected:
/** Current number of descriptors in the engine
*/
- int CurrentSetSize;
- /** Reference table, contains all current handlers
- */
- EventHandler** ref;
+ static size_t CurrentSetSize;
/** List of handlers that want a trial read/write
*/
- std::set<int> trials;
+ static std::set<int> trials;
- int MAX_DESCRIPTORS;
+ static int MAX_DESCRIPTORS;
- size_t indata;
- size_t outdata;
- time_t lastempty;
+ /** Socket engine statistics: count of various events, bandwidth usage
+ */
+ static Statistics stats;
- void UpdateStats(size_t len_in, size_t len_out);
+ static void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask) = 0;
- void SetEventMask(EventHandler* eh, int value);
-public:
+ /** Add an event handler to the base socket engine. AddFd(EventHandler*, int) should call this.
+ */
+ static bool AddFdRef(EventHandler* eh);
+
+ static void DelFdRef(EventHandler* eh);
- unsigned long TotalEvents;
- unsigned long ReadEvents;
- unsigned long WriteEvents;
- unsigned long ErrorEvents;
+ template <typename T>
+ static void ResizeDouble(std::vector<T>& vect)
+ {
+ if (SocketEngine::CurrentSetSize > vect.size())
+ vect.resize(vect.size() * 2);
+ }
+public:
/** Constructor.
* The constructor transparently initializes
* the socket engine which the ircd is using.
* failure (for example, you try and enable
* epoll on a 2.4 linux kernel) then this
* function may bail back to the shell.
+ * @return void, but it is acceptable for this function to bail back to
+ * the shell or operating system on fatal error.
*/
- SocketEngine();
+ static void Init();
/** Destructor.
* The destructor transparently tidies up
* any resources used by the socket engine.
*/
- virtual ~SocketEngine();
+ static void Deinit();
/** Add an EventHandler object to the engine. Use AddFd to add a file
* descriptor to the engine and have the socket engine monitor it. You
* @param eh An event handling object to add
* @param event_mask The initial event mask for the object
*/
- virtual bool AddFd(EventHandler* eh, int event_mask) = 0;
+ static bool AddFd(EventHandler* eh, int event_mask);
/** If you call this function and pass it an
* event handler, that event handler will
* @param eh The event handler to change
* @param event_mask The changes to make to the wait state
*/
- void ChangeEventMask(EventHandler* eh, int event_mask);
+ static void ChangeEventMask(EventHandler* eh, int event_mask);
- /** Returns the highest file descriptor you may store in the socket engine
- * @return The maximum fd value
+ /** Returns the number of file descriptors reported by the system this program may use
+ * when it was started.
+ * @return If positive, the number of file descriptors that the system reported that we
+ * may use. Otherwise (<= 0) this number could not be determined.
*/
- inline int GetMaxFds() const { return MAX_DESCRIPTORS; }
+ static int GetMaxFds() { return MAX_DESCRIPTORS; }
/** Returns the number of file descriptors being queried
* @return The set size
*/
- inline int GetUsedFds() const { return CurrentSetSize; }
+ static size_t GetUsedFds() { return CurrentSetSize; }
/** Delete an event handler from the engine.
* This function call deletes an EventHandler
* required you must do this yourself.
* @param eh The event handler object to remove
*/
- virtual void DelFd(EventHandler* eh) = 0;
+ static void DelFd(EventHandler* eh);
/** Returns true if a file descriptor exists in
* the socket engine's list.
* @param fd The event handler to look for
* @return True if this fd has an event handler
*/
- virtual bool HasFd(int fd);
+ static bool HasFd(int fd);
/** Returns the EventHandler attached to a specific fd.
* If the fd isnt in the socketengine, returns NULL.
* @param fd The event handler to look for
* @return A pointer to the event handler, or NULL
*/
- virtual EventHandler* GetRef(int fd);
+ static EventHandler* GetRef(int fd);
/** Waits for events and dispatches them to handlers. Please note that
* this doesn't wait long, only a couple of milliseconds. It returns the
* value.
* @return The number of events which have occured.
*/
- virtual int DispatchEvents() = 0;
+ static int DispatchEvents();
/** Dispatch trial reads and writes. This causes the actual socket I/O
* to happen when writes have been pre-buffered.
*/
- virtual void DispatchTrialWrites();
-
- /** Returns the socket engines name. This returns the name of the
- * engine for use in /VERSION responses.
- * @return The socket engine name
- */
- virtual std::string GetName() = 0;
+ static void DispatchTrialWrites();
/** Returns true if the file descriptors in the given event handler are
* within sensible ranges which can be handled by the socket engine.
*/
- virtual bool BoundsCheckFd(EventHandler* eh);
+ static bool BoundsCheckFd(EventHandler* eh);
/** Abstraction for BSD sockets accept(2).
* This function should emulate its namesake system call exactly.
* @param addrlen The size of the sockaddr parameter.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen);
+ static int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen);
- /** Abstraction for BSD sockets close(2).
- * This function should emulate its namesake system call exactly.
- * @param fd This version of the call takes an EventHandler instead of a bare file descriptor.
- * @return This method should return exactly the same values as the system call it emulates.
+ /** Close the underlying fd of an event handler, remove it from the socket engine and set the fd to -1.
+ * @param eh The EventHandler to close.
+ * @return 0 on success, a negative value on error
*/
- int Close(EventHandler* fd);
+ static int Close(EventHandler* eh);
/** Abstraction for BSD sockets close(2).
* This function should emulate its namesake system call exactly.
* This function should emulate its namesake system call exactly.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Close(int fd);
+ static int Close(int fd);
/** Abstraction for BSD sockets send(2).
* This function should emulate its namesake system call exactly.
* @param flags A flag value that controls the sending of the data.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Send(EventHandler* fd, const void *buf, size_t len, int flags);
+ static int Send(EventHandler* fd, const void *buf, size_t len, int flags);
/** Abstraction for BSD sockets recv(2).
* This function should emulate its namesake system call exactly.
* @param flags A flag value that controls the reception of the data.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Recv(EventHandler* fd, void *buf, size_t len, int flags);
+ static int Recv(EventHandler* fd, void *buf, size_t len, int flags);
/** Abstraction for BSD sockets recvfrom(2).
* This function should emulate its namesake system call exactly.
* @param fromlen The size of the from parameter.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen);
+ static int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen);
/** Abstraction for BSD sockets sendto(2).
* This function should emulate its namesake system call exactly.
* @param buf The buffer in which the data that is sent is stored.
* @param len The size of the buffer.
* @param flags A flag value that controls the sending of the data.
- * @param to The remote IP address and port.
+ * @param to The remote IP address and port.
* @param tolen The size of the to parameter.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen);
+ static int SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen);
/** Abstraction for BSD sockets connect(2).
* This function should emulate its namesake system call exactly.
* @param addrlen The size of the sockaddr parameter.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen);
+ static int Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen);
/** Make a file descriptor blocking.
* @param fd a file descriptor to set to blocking mode
* @return 0 on success, -1 on failure, errno is set appropriately.
*/
- int Blocking(int fd);
+ static int Blocking(int fd);
/** Make a file descriptor nonblocking.
* @param fd A file descriptor to set to nonblocking mode
* @return 0 on success, -1 on failure, errno is set appropriately.
*/
- int NonBlocking(int fd);
+ static int NonBlocking(int fd);
/** Abstraction for BSD sockets shutdown(2).
* This function should emulate its namesake system call exactly.
* @param how What part of the socket to shut down
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Shutdown(EventHandler* fd, int how);
+ static int Shutdown(EventHandler* fd, int how);
/** Abstraction for BSD sockets shutdown(2).
* This function should emulate its namesake system call exactly.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Shutdown(int fd, int how);
+ static int Shutdown(int fd, int how);
/** Abstraction for BSD sockets bind(2).
* This function should emulate its namesake system call exactly.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Bind(int fd, const irc::sockets::sockaddrs& addr);
+ static int Bind(int fd, const irc::sockets::sockaddrs& addr);
/** Abstraction for BSD sockets listen(2).
* This function should emulate its namesake system call exactly.
* @return This method should return exactly the same values as the system call it emulates.
*/
- int Listen(int sockfd, int backlog);
+ static int Listen(int sockfd, int backlog);
/** Set SO_REUSEADDR and SO_LINGER on this file descriptor
*/
- void SetReuse(int sockfd);
+ static void SetReuse(int sockfd);
/** This function is called immediately after fork().
* Some socket engines (notably kqueue) cannot have their
* @return void, but it is acceptable for this function to bail back to
* the shell or operating system on fatal error.
*/
- virtual void RecoverFromFork();
+ static void RecoverFromFork();
- /** Get data transfer statistics, kilobits per second in and out and total.
+ /** Get data transfer and event statistics
*/
- void GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total);
+ static const Statistics& GetStats() { return stats; }
/** Should we ignore the error in errno?
* Checks EAGAIN and WSAEWOULDBLOCK
return false;
}
-
-SocketEngine* CreateSocketEngine();
-
-#endif
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace stdalgo
+{
+ namespace vector
+ {
+ /**
+ * Erase a single element from a vector by overwriting it with a copy of the last element,
+ * which is then removed. This, in contrast to vector::erase(), does not result in all
+ * elements after the erased element being moved.
+ * @param vect Vector to remove the element from
+ * @param it Iterator to the element to remove
+ * @return Nothing, but all iterators, references and pointers to the erased element and the
+ * last element are invalidated
+ */
+ template <typename T>
+ inline void swaperase(typename std::vector<T>& vect, const typename std::vector<T>::iterator& it)
+ {
+ *it = vect.back();
+ vect.pop_back();
+ }
+
+ /**
+ * Find and if exists, erase a single element from a vector by overwriting it with a
+ * copy of the last element, which is then removed. This, in contrast to vector::erase(),
+ * does not result in all elements after the erased element being moved.
+ * If the given value occurs multiple times, the one with the lowest index is removed.
+ * Individual elements are compared to the given value using operator==().
+ * @param vect Vector to remove the element from
+ * @param val Value of the element to look for and remove
+ * @return True if the element was found and removed, false if it wasn't found.
+ * If true, all iterators, references and pointers pointing to either the first element that
+ * is equal to val or to the last element are invalidated.
+ */
+ template <typename T>
+ inline bool swaperase(typename std::vector<T>& vect, const T& val)
+ {
+ const typename std::vector<T>::iterator it = std::find(vect.begin(), vect.end(), val);
+ if (it != vect.end())
+ {
+ swaperase(vect, it);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Deleter that uses operator delete to delete the item
+ */
+ template <typename T>
+ struct defaultdeleter
+ {
+ void operator()(T* o)
+ {
+ delete o;
+ }
+ };
+
+ /**
+ * Deleter that adds the item to the cull list, that is, queues it for
+ * deletion at the end of the current mainloop iteration
+ */
+ struct culldeleter
+ {
+ void operator()(classbase* item);
+ };
+
+ /**
+ * Deletes all elements in a container using operator delete
+ * @param cont The container containing the elements to delete
+ */
+ template <template<typename, typename> class Cont, typename T, typename Alloc>
+ inline void delete_all(const Cont<T*, Alloc>& cont)
+ {
+ std::for_each(cont.begin(), cont.end(), defaultdeleter<T>());
+ }
+}
*/
-#ifndef TESTSUITE_H
-#define TESTSUITE_H
+#pragma once
+
+#ifdef INSPIRCD_ENABLE_TESTSUITE
class TestSuite
{
- bool RealGenerateUIDTests();
public:
TestSuite();
~TestSuite();
*/
-#ifndef THREADENGINE_H
-#define THREADENGINE_H
+#pragma once
#include <vector>
#include <string>
#include <map>
-#include "inspircd_config.h"
+#include "config.h"
#include "base.h"
-class ThreadData;
-
/** Derive from this class to implement your own threaded sections of
* code. Be sure to keep your code thread-safe and not prone to deadlocks
* and race conditions if you MUST use threading!
/** Set to true when the thread is to exit
*/
bool ExitFlag;
+
+ /** Opaque thread state managed by the ThreadEngine
+ */
+ ThreadEngine::ThreadState state;
+
+ /** ThreadEngine manages Thread::state
+ */
+ friend class ThreadEngine;
+
protected:
/** Get thread's current exit status.
* (are we being asked to exit?)
return ExitFlag;
}
public:
- /** Opaque thread state managed by threading engine
- */
- ThreadData* state;
-
/** Set Creator to NULL at this point
*/
- Thread() : ExitFlag(false), state(NULL)
+ Thread() : ExitFlag(false)
{
}
- /* If the thread is running, you MUST join BEFORE deletion */
- virtual ~Thread();
-
/** Override this method to put your actual
* threaded code here.
*/
*/
virtual void OnNotify() = 0;
};
-
-#endif
-
*/
-#ifndef THREADENGINE_PTHREAD_H
-#define THREADENGINE_PTHREAD_H
+#pragma once
#include <pthread.h>
#include "typedefs.h"
class CoreExport ThreadEngine
{
public:
-
- /** Constructor.
- */
- ThreadEngine();
-
- /** Destructor
+ /** Per-thread state, present in each Thread object, managed by the ThreadEngine
*/
- virtual ~ThreadEngine();
+ struct ThreadState
+ {
+ pthread_t pthread_id;
+ };
/** Create a new thread. This takes an already allocated
* Thread* pointer and initializes it to use this threading
*/
void Start(Thread* thread_to_init);
- /** Returns the thread engine's name for display purposes
- * @return The thread engine name
+ /** Stop a thread gracefully.
+ * First, this function asks the thread to terminate by calling Thread::SetExitFlag().
+ * Next, it waits until the thread terminates (on the operating system level). Finally,
+ * all OS-level resources associated with the thread are released. The Thread instance
+ * passed to the function is NOT freed.
+ * When this function returns, the thread is stopped and you can destroy it or restart it
+ * at a later point.
+ * Stopping a thread that is not running is a bug.
+ * @param thread The thread to stop.
*/
- const std::string GetName()
- {
- return "posix-thread";
- }
-};
-
-class CoreExport ThreadData
-{
- public:
- pthread_t pthread_id;
- void FreeThread(Thread* toFree);
+ void Stop(Thread* thread);
};
/** The Mutex class represents a mutex, which can be used to keep threads
*/
class CoreExport Mutex
{
- private:
+ protected:
pthread_mutex_t putex;
public:
/** Constructor.
}
};
-class ThreadQueueData
+class ThreadQueueData : public Mutex
{
- pthread_mutex_t mutex;
pthread_cond_t cond;
public:
ThreadQueueData()
{
- pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
}
~ThreadQueueData()
{
- pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
- void Lock()
- {
- pthread_mutex_lock(&mutex);
- }
-
- void Unlock()
- {
- pthread_mutex_unlock(&mutex);
- }
-
void Wakeup()
{
pthread_cond_signal(&cond);
void Wait()
{
- pthread_cond_wait(&cond, &mutex);
+ pthread_cond_wait(&cond, &putex);
}
};
public:
ThreadSignalSocket* sock;
};
-
-
-#endif
*/
-#ifndef THREADENGINE_WIN32_H
-#define THREADENGINE_WIN32_H
+#pragma once
-#include "inspircd_config.h"
+#include "config.h"
#include "base.h"
class Thread;
class CoreExport ThreadEngine
{
public:
-
- ThreadEngine();
-
- virtual ~ThreadEngine();
+ /** Per-thread state, present in each Thread object, managed by the ThreadEngine
+ */
+ struct ThreadState
+ {
+ HANDLE handle;
+ };
static DWORD WINAPI Entry(void* parameter);
*/
void Start(Thread* thread_to_init);
- /** Returns the thread engine's name for display purposes
- * @return The thread engine name
+ /** Stop a thread gracefully.
+ * First, this function asks the thread to terminate by calling Thread::SetExitFlag().
+ * Next, it waits until the thread terminates (on the operating system level). Finally,
+ * all OS-level resources associated with the thread are released. The Thread instance
+ * passed to the function is NOT freed.
+ * When this function returns, the thread is stopped and you can destroy it or restart it
+ * at a later point.
+ * Stopping a thread that is not running is a bug.
+ * @param thread The thread to stop.
*/
- const std::string GetName()
- {
- return "windows-thread";
- }
-};
-
-class CoreExport ThreadData
-{
- public:
- HANDLE handle;
- void FreeThread(Thread* toFree);
+ void Stop(Thread* thread);
};
/** The Mutex class represents a mutex, which can be used to keep threads
}
};
-class ThreadQueueData
+class ThreadQueueData : public Mutex
{
- CRITICAL_SECTION mutex;
HANDLE event;
public:
ThreadQueueData()
event = CreateEvent(NULL, false, false, NULL);
if (event == NULL)
throw CoreException("CreateEvent() failed in ThreadQueueData::ThreadQueueData()!");
- InitializeCriticalSection(&mutex);
}
~ThreadQueueData()
{
CloseHandle(event);
- DeleteCriticalSection(&mutex);
- }
-
- void Lock()
- {
- EnterCriticalSection(&mutex);
- }
-
- void Unlock()
- {
- LeaveCriticalSection(&mutex);
}
void Wakeup()
void Wait()
{
- LeaveCriticalSection(&mutex);
+ Unlock();
WaitForSingleObject(event, INFINITE);
- EnterCriticalSection(&mutex);
+ Lock();
}
};
connFD = -1;
}
};
-
-#endif
-
*/
-#ifndef INSPIRCD_TIMER_H
-#define INSPIRCD_TIMER_H
+#pragma once
+
+class Module;
/** Timer class for one-second resolution timers
* Timer provides a facility which allows module
* resolution. To use Timer, inherit a class from
* Timer, then insert your inherited class into the
* queue using Server::AddTimer(). The Tick() method of
- * your object (which you should override) will be called
+ * your object (which you have to override) will be called
* at the given time.
*/
class CoreExport Timer
{
- private:
/** The triggering time
*/
time_t trigger;
+
/** Number of seconds between triggers
*/
- long secs;
+ unsigned int secs;
+
/** True if this is a repeating timer
*/
bool repeat;
+
public:
/** Default constructor, initializes the triggering time
* @param secs_from_now The number of seconds from now to trigger the timer
- * @param now The time now
* @param repeating Repeat this timer every secs_from_now seconds if set to true
*/
- Timer(long secs_from_now, time_t now, bool repeating = false)
- {
- trigger = now + secs_from_now;
- secs = secs_from_now;
- repeat = repeating;
- }
+ Timer(unsigned int secs_from_now, bool repeating = false);
- /** Default destructor, does nothing.
+ /** Default destructor, removes the timer from the timer manager
*/
- virtual ~Timer() { }
+ virtual ~Timer();
/** Retrieve the current triggering time
*/
- virtual time_t GetTimer()
+ time_t GetTrigger() const
{
return trigger;
}
/** Sets the trigger timeout to a new value
+ * This does not update the bookkeeping in TimerManager, use SetInterval()
+ * to change the interval between ticks while keeping TimerManager updated
*/
- virtual void SetTimer(time_t t)
+ void SetTrigger(time_t nexttrigger)
{
- trigger = t;
+ trigger = nexttrigger;
}
+ /** Sets the interval between two ticks.
+ */
+ void SetInterval(time_t interval);
+
/** Called when the timer ticks.
* You should override this method with some useful code to
* handle the tick event.
+ * @param TIME The current time.
+ * @return True if the Timer object is still valid, false if it was destructed.
*/
- virtual void Tick(time_t TIME) = 0;
+ virtual bool Tick(time_t TIME) = 0;
/** Returns true if this timer is set to repeat
*/
- bool GetRepeat()
+ bool GetRepeat() const
{
return repeat;
}
/** Returns the interval (number of seconds between ticks)
* of this timer object.
*/
- long GetSecs()
+ unsigned int GetInterval() const
{
return secs;
}
/** Cancels the repeat state of a repeating timer.
* If you call this method, then the next time your
* timer ticks, it will be removed immediately after.
- * You should use this method call to remove a recurring
- * timer if you wish to do so within the timer's Tick
- * event, as calling TimerManager::DelTimer() from within
- * the Timer::Tick() method is dangerous and may
- * cause a segmentation fault. Calling CancelRepeat()
- * is safe in this case.
*/
void CancelRepeat()
{
}
};
-
/** This class manages sets of Timers, and triggers them at their defined times.
* This will ensure timers are not missed, as well as removing timers that have
* expired and allowing the addition of new ones.
*/
class CoreExport TimerManager
{
- protected:
+ typedef std::multimap<time_t, Timer*> TimerMap;
+
/** A list of all pending timers
*/
- std::vector<Timer *> Timers;
+ TimerMap Timers;
public:
- /** Constructor
- */
- TimerManager();
- ~TimerManager();
-
/** Tick all pending Timers
* @param TIME the current system time
*/
*/
void AddTimer(Timer *T);
- /** Delete an Timer
- * @param T an Timer derived class to delete
+ /** Remove a Timer
+ * @param T an Timer derived class to remove
*/
void DelTimer(Timer* T);
-
- /** Compares two timers
- */
- static bool TimerComparison( Timer *one, Timer*two);
};
-
-#endif
-
*/
-#ifndef TYPEDEFS_H
-#define TYPEDEFS_H
+#pragma once
class BanCacheManager;
-class BanItem;
class BufferedSocket;
class Channel;
class Command;
-class ConfigReader;
+class ConfigStatus;
class ConfigTag;
-class DNSHeader;
-class DNSRequest;
class Extensible;
class FakeUser;
class InspIRCd;
class Invitation;
-class InviteBase;
class LocalUser;
class Membership;
class Module;
class OperInfo;
+class ProtocolServer;
class RemoteUser;
+class Server;
class ServerConfig;
class ServerLimits;
class Thread;
class User;
-class UserResolver;
class XLine;
class XLineManager;
class XLineFactory;
struct ConnectClass;
struct ModResult;
-struct ResourceRecord;
#include "hashcomp.h"
#include "base.h"
-#ifdef HASHMAP_DEPRECATED
- typedef nspace::hash_map<std::string, User*, nspace::insensitive, irc::StrHashComp> user_hash;
- typedef nspace::hash_map<std::string, Channel*, nspace::insensitive, irc::StrHashComp> chan_hash;
-#else
- typedef nspace::hash_map<std::string, User*, nspace::hash<std::string>, irc::StrHashComp> user_hash;
- typedef nspace::hash_map<std::string, Channel*, nspace::hash<std::string>, irc::StrHashComp> chan_hash;
-#endif
-
-/** A list holding local users, this is the type of UserManager::local_users
- */
-typedef std::list<LocalUser*> LocalUserList;
+typedef TR1NS::unordered_map<std::string, User*, irc::insensitive, irc::StrHashComp> user_hash;
+typedef TR1NS::unordered_map<std::string, Channel*, irc::insensitive, irc::StrHashComp> chan_hash;
/** A list of failed port bindings, used for informational purposes on startup */
typedef std::vector<std::pair<std::string, std::string> > FailedPortList;
-/** Holds a complete list of all channels to which a user has been invited and has not yet joined, and the time at which they'll expire.
- */
-typedef std::vector<Invitation*> InviteList;
-
-/** Holds a complete list of all allow and deny tags from the configuration file (connection classes)
- */
-typedef std::vector<reference<ConnectClass> > ClassVector;
-
-/** Typedef for the list of user-channel records for a user
- */
-typedef std::set<Channel*> UserChanList;
-
-/** Shorthand for an iterator into a UserChanList
- */
-typedef UserChanList::iterator UCListIter;
-
-/** Holds a complete ban list
+/** List of channels to consider when building the neighbor list of a user
*/
-typedef std::vector<BanItem> BanList;
-
-/** A list of custom modes parameters on a channel
- */
-typedef std::map<char,std::string> CustomModeList;
+typedef std::vector<Membership*> IncludeChanList;
/** A cached text file stored with its contents as lines
*/
/** Iterator pair, used for tag-name ranges */
typedef std::pair<ConfigIter,ConfigIter> ConfigTagList;
-/** Index of valid oper blocks and types */
-typedef std::map<std::string, reference<OperInfo> > OperIndex;
-
/** Files read by the configuration */
typedef std::map<std::string, file_cache> ConfigFileCache;
-/** A hash of commands used by the core
- */
-typedef nspace::hash_map<std::string,Command*> Commandtable;
-
-/** Membership list of a channel */
-typedef std::map<User*, Membership*> UserMembList;
-/** Iterator of UserMembList */
-typedef UserMembList::iterator UserMembIter;
-/** const Iterator of UserMembList */
-typedef UserMembList::const_iterator UserMembCIter;
-
/** Generic user list, used for exceptions */
typedef std::set<User*> CUList;
/** An interator in an XLineLookup
*/
typedef XLineLookup::iterator LookupIter;
-
-
-#endif
-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
-/**
- * This is the maximum length of a UUID (unique user identifier).
- * This length is set in compliance with TS6 protocol, and really should not be changed. Ever.
- * It allows for a lot of clients as-is. -- w00t.
- */
-#define UUID_LENGTH 10
+class TestSuite;
+
+class CoreExport UIDGenerator
+{
+ friend class TestSuite;
+
+ /** Holds the current UID. Used to generate the next one.
+ */
+ std::string current_uid;
+
+ /** Increments the current UID by one.
+ */
+ void IncrementUID(unsigned int pos);
+
+ public:
+ /**
+ * This is the maximum length of a UUID (unique user identifier).
+ * This length is set in compliance with TS6 protocol, and really should not be changed. Ever.
+ * It allows for a lot of clients as-is. -- w00t.
+ */
+ static const unsigned int UUID_LENGTH = 9;
+
+ /** Initializes this UID generator with the given SID
+ * @param sid SID that conforms to InspIRCd::IsSID()
+ */
+ void init(const std::string& sid);
+ /** Returns the next available UID for this server.
+ */
+ std::string GetUID();
+ /** Generates a pseudorandom SID based on a servername and a description
+ * Guaranteed to return the same if invoked with the same parameters
+ * @param servername The server name to use as seed
+ * @param serverdesc The server description to use as seed
+ * @return A valid SID
+ */
+ static std::string GenerateSID(const std::string& servername, const std::string& serverdesc);
+};
*/
-#ifndef USERMANAGER_H
-#define USERMANAGER_H
+#pragma once
#include <list>
-/** A list of ip addresses cross referenced against clone counts */
-typedef std::map<irc::sockets::cidr_mask, unsigned int> clonemap;
-
-class CoreExport UserManager
+class CoreExport UserManager : public fakederef<UserManager>
{
+ public:
+ struct CloneCounts
+ {
+ unsigned int global;
+ unsigned int local;
+ CloneCounts() : global(0), local(0) { }
+ };
+
+ /** Container that maps IP addresses to clone counts
+ */
+ typedef std::map<irc::sockets::cidr_mask, CloneCounts> CloneMap;
+
+ /** Sequence container in which each element is a User*
+ */
+ typedef std::vector<User*> OperList;
+
+ /** A list holding local users
+ */
+ typedef insp::intrusive_list<LocalUser> LocalList;
+
private:
- /** Map of local ip addresses for clone counting
+ /** Map of IP addresses for clone counting
*/
- clonemap local_clones;
+ CloneMap clonemap;
+
+ /** A CloneCounts that contains zero for both local and global
+ */
+ const CloneCounts zeroclonecounts;
+
+ /** Local client list, a list containing only local clients
+ */
+ LocalList local_users;
+
public:
+ /** Constructor, initializes variables
+ */
UserManager();
- ~UserManager()
- {
- for (user_hash::iterator i = clientlist->begin();i != clientlist->end();i++)
- {
- delete i->second;
- }
- clientlist->clear();
- delete clientlist;
- delete uuidlist;
- }
+ /** Destructor, destroys all users in clientlist
+ */
+ ~UserManager();
/** Client list, a hash_map containing all clients, local and remote
*/
- user_hash* clientlist;
+ user_hash clientlist;
/** Client list stored by UUID. Contains all clients, and is updated
* automatically by the constructor and destructor of User.
*/
- user_hash* uuidlist;
-
- /** Local client list, a list containing only local clients
- */
- LocalUserList local_users;
+ user_hash uuidlist;
/** Oper list, a vector containing all local and remote opered users
*/
- std::list<User*> all_opers;
+ OperList all_opers;
/** Number of unregistered users online right now.
* (Unregistered means before USER/NICK/dns)
*/
unsigned int unregistered_count;
- /** Number of elements in local_users
+ /**
+ * Reset the already_sent IDs so we don't wrap it around and drop a message
+ * Also removes all expired invites
+ */
+ void GarbageCollect();
+
+ /** Perform background user events such as PING checks
*/
- unsigned int local_count;
+ void DoBackgroundUserStuff();
- /** Map of global ip addresses for clone counting
- * XXX - this should be private, but m_clones depends on it currently.
+ /** Returns true when all modules have done pre-registration checks on a user
+ * @param user The user to verify
+ * @return True if all modules have finished checking this user
*/
- clonemap global_clones;
+ bool AllModulesReportReady(LocalUser* user);
/** Add a client to the system.
* This will create a new User, insert it into the user_hash,
/** Disconnect a user gracefully
* @param user The user to remove
* @param quitreason The quit reason to show to normal users
- * @param operreason The quit reason to show to opers
+ * @param operreason The quit reason to show to opers, can be NULL if same as quitreason
* @return Although this function has no return type, on exit the user provided will no longer exist.
*/
- void QuitUser(User *user, const std::string &quitreason, const char* operreason = "");
-
- /** Add a user to the local clone map
- * @param user The user to add
- */
- void AddLocalClone(User *user);
+ void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
- /** Add a user to the global clone map
+ /** Add a user to the clone map
* @param user The user to add
*/
- void AddGlobalClone(User *user);
+ void AddClone(User* user);
/** Remove all clone counts from the user, you should
* use this if you change the user's IP address
*/
void RemoveCloneCounts(User *user);
- /** Return the number of global clones of this user
- * @param user The user to get a count for
- * @return The global clone count of this user
+ /** Return the number of local and global clones of this user
+ * @param user The user to get the clone counts for
+ * @return The clone counts of this user. The returned reference is volatile - you
+ * must assume that it becomes invalid as soon as you call any function other than
+ * your own.
*/
- unsigned long GlobalCloneCount(User *user);
+ const CloneCounts& GetCloneCounts(User* user) const;
- /** Return the number of local clones of this user
- * @param user The user to get a count for
- * @return The local clone count of this user
+ /** Return a map containg IP addresses and their clone counts
+ * @return The clone count map
*/
- unsigned long LocalCloneCount(User *user);
+ const CloneMap& GetCloneMap() const { return clonemap; }
- /** Return a count of users, unknown and known connections
- * @return The number of users
+ /** Return a count of all global users, unknown and known connections
+ * @return The number of users on the network, including local unregistered users
*/
- unsigned int UserCount();
+ unsigned int UserCount() const { return this->clientlist.size(); }
- /** Return a count of fully registered connections only
- * @return The number of registered users
+ /** Return a count of fully registered connections on the network
+ * @return The number of registered users on the network
*/
- unsigned int RegisteredUserCount();
+ unsigned int RegisteredUserCount() { return this->clientlist.size() - this->UnregisteredUserCount(); }
- /** Return a count of opered (umode +o) users only
- * @return The number of opers
+ /** Return a count of opered (umode +o) users on the network
+ * @return The number of opers on the network
*/
- unsigned int OperCount();
+ unsigned int OperCount() const { return this->all_opers.size(); }
- /** Return a count of unregistered (before NICK/USER) users only
- * @return The number of unregistered (unknown) connections
+ /** Return a count of local unregistered (before NICK/USER) users
+ * @return The number of local unregistered (unknown) connections
*/
- unsigned int UnregisteredUserCount();
+ unsigned int UnregisteredUserCount() const { return this->unregistered_count; }
- /** Return a count of local users on this server only
- * @return The number of local users
+ /** Return a count of local registered users
+ * @return The number of registered local users
*/
- unsigned int LocalUserCount();
-
-
+ unsigned int LocalUserCount() const { return (this->local_users.size() - this->UnregisteredUserCount()); }
+ /** Get a hash map containing all users, keyed by their nickname
+ * @return A hash map mapping nicknames to User pointers
+ */
+ user_hash& GetUsers() { return clientlist; }
- /** Number of users with a certain mode set on them
+ /** Get a list containing all local users
+ * @return A const list of local users
*/
- int ModeCount(const char mode);
+ const LocalList& GetLocalUsers() const { return local_users; }
/** Send a server notice to all local users
* @param text The text format string to send
* @param ... The format arguments
*/
void ServerNoticeAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
-
- /** Send a server message (PRIVMSG) to all local users
- * @param text The text format string to send
- * @param ... The format arguments
- */
- void ServerPrivmsgAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
};
-
-#endif
*/
-#ifndef USERS_H
-#define USERS_H
+#pragma once
#include "socket.h"
#include "inspsocket.h"
-#include "dns.h"
#include "mode.h"
#include "membership.h"
CC_NAMED = 2
};
-/** RFC1459 channel modes
- */
-enum UserModes {
- /** +s: Server notice mask */
- UM_SNOMASK = 's' - 65,
- /** +w: WALLOPS */
- UM_WALLOPS = 'w' - 65,
- /** +i: Invisible */
- UM_INVISIBLE = 'i' - 65,
- /** +o: Operator */
- UM_OPERATOR = 'o' - 65
-};
-
/** Registration state of a user, e.g.
* have they sent USER, NICK, PASS yet?
*/
*/
unsigned long limit;
+ /** If set to true, no user DNS lookups are to be performed
+ */
+ bool resolvehostnames;
+
/** Create a new connect class with no settings.
*/
ConnectClass(ConfigTag* tag, char type, const std::string& mask);
*/
std::string cachedip;
+ /** The user's mode list.
+ * Much love to the STL for giving us an easy to use bitset, saving us RAM.
+ * if (modes[modeid]) is set, then the mode is set.
+ * For example, to work out if mode +i is set, we check the field
+ * User::modes[invisiblemode->modeid] == true.
+ */
+ std::bitset<ModeParser::MODEID_MAX> modes;
+
public:
+ /** List of Memberships for this user
+ */
+ typedef insp::intrusive_list<Membership> ChanList;
/** Hostname of connection.
* This should be valid as per RFC1035.
*/
time_t signon;
- /** Time that the connection last sent a message, used to calculate idle time
- */
- time_t idle_lastmsg;
-
/** Client address that the user is connected from.
* Do not modify this value directly, use SetClientIP() to change it.
* Port is not valid for remote users.
*/
std::string fullname;
- /** The user's mode list.
- * NOT a null terminated string.
- * Also NOT an array.
- * Much love to the STL for giving us an easy to use bitset, saving us RAM.
- * if (modes[modeletter-65]) is set, then the mode is
- * set, for example, to work out if mode +s is set, we check the field
- * User::modes['s'-65] != 0.
- * The following RFC characters o, w, s, i have constants defined via an
- * enum, such as UM_SERVERNOTICE and UM_OPETATOR.
- */
- std::bitset<64> modes;
-
/** What snomasks are set on this user.
* This functions the same as the above modes.
*/
/** Channels this user is on
*/
- UserChanList chans;
+ ChanList chans;
/** The server the user is connected to.
*/
- const std::string server;
+ Server* server;
/** The user's away message.
* If this string is empty, the user is not marked as away.
std::string awaymsg;
/** Time the user last went away.
- * This is ONLY RELIABLE if user IS_AWAY()!
+ * This is ONLY RELIABLE if user IsAway()!
*/
time_t awaytime;
*/
unsigned int registered:3;
- /** True when DNS lookups are completed.
- * The UserResolver classes res_forward and res_reverse will
- * set this value once they complete.
- */
- unsigned int dns_done:1;
-
- /** Whether or not to send an snotice about this user's quitting
- */
- unsigned int quietquit:1;
-
/** If this is set to true, then all socket operations for the user
* are dropped into the bit-bucket.
* This value is set by QuitUser, and is not needed seperately from that call.
*/
unsigned int quitting:1;
- /** Recursion fix: user is out of SendQ and will be quit as soon as possible.
- * This can't be handled normally because QuitUser itself calls Write on other
- * users, which could trigger their SendQ to overrun.
- */
- unsigned int quitting_sendq:1;
-
- /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks.
- */
- unsigned int exempt:1;
-
- /** has the user responded to their previous ping?
- */
- unsigned int lastping:1;
-
/** What type of user is this? */
const unsigned int usertype:2;
/** Get client IP string from sockaddr, using static internal buffer
* @return The IP string
*/
- const char* GetIPString();
+ const std::string& GetIPString();
/** Get CIDR mask, using default range, for this user
*/
/** Constructor
* @throw CoreException if the UID allocated to the user already exists
*/
- User(const std::string &uid, const std::string& srv, int objtype);
-
- /** Check if the user matches a G or K line, and disconnect them if they do.
- * @param doZline True if ZLines should be checked (if IP has changed since initial connect)
- * Returns true if the user matched a ban, false else.
- */
- bool CheckLines(bool doZline = false);
+ User(const std::string& uid, Server* srv, int objtype);
/** Returns the full displayed host of the user
* This member function returns the hostname of the user as seen by other users
*/
void InvalidateCache();
- /** Create a displayable mode string for this users snomasks
- * @return The notice mask character sequence
+ /** Returns whether this user is currently away or not. If true,
+ * further information can be found in User::awaymsg and User::awaytime
+ * @return True if the user is away, false otherwise
*/
- const char* FormatNoticeMasks();
+ bool IsAway() const { return (!awaymsg.empty()); }
- /** Process a snomask modifier string, e.g. +abc-de
- * @param sm A sequence of notice mask characters
- * @return The cleaned mode sequence which can be output,
- * e.g. in the above example if masks c and e are not
- * valid, this function will return +ab-d
+ /** Returns whether this user is an oper or not. If true,
+ * oper information can be obtained from User::oper
+ * @return True if the user is an oper, false otherwise
*/
- std::string ProcessNoticeMasks(const char *sm);
+ bool IsOper() const { return oper; }
/** Returns true if a notice mask is set
* @param sm A notice mask character to check
*/
bool IsNoticeMaskSet(unsigned char sm);
- /** Changed a specific notice mask value
- * @param sm The server notice mask to change
- * @param value An on/off value for this mask
- */
- void SetNoticeMask(unsigned char sm, bool value);
-
/** Create a displayable mode string for this users umodes
* @param showparameters The mode string
*/
* @return True if the mode is set
*/
bool IsModeSet(unsigned char m);
+ bool IsModeSet(ModeHandler* mh);
+ bool IsModeSet(ModeHandler& mh) { return IsModeSet(&mh); }
+ bool IsModeSet(UserModeReference& moderef);
/** Set a specific usermode to on or off
* @param m The user mode
* @param value On or off setting of the mode
*/
- void SetMode(unsigned char m, bool value);
+ void SetMode(ModeHandler* mh, bool value);
+ void SetMode(ModeHandler& mh, bool value) { SetMode(&mh, value); }
/** Returns true or false for if a user can execute a privilaged oper command.
* This is done by looking up their oper type from User::oper, then referencing
*/
virtual bool HasModePermission(unsigned char mode, ModeType type);
- /** Creates a wildcard host.
- * Takes a buffer to use and fills the given buffer with the host in the format *!*\@hostname
- * @return The wildcarded hostname in *!*\@host form
- */
- char* MakeWildHost();
-
/** Creates a usermask with real host.
* Takes a buffer to use and fills the given buffer with the hostmask in the format user\@host
* @return the usermask in the format user\@host
*/
const std::string& MakeHostIP();
- /** Add the user to WHOWAS system
- */
- void AddToWhoWas();
-
/** Oper up the user using the given opertype.
* This will also give the +o usermode.
*/
void Oper(OperInfo* info);
- /** Force a nickname change.
- * If the nickname change fails (for example, because the nick in question
- * already exists) this function will return false, and you must then either
- * output an error message, or quit the user for nickname collision.
- * @param newnick The nickname to change to
- * @return True if the nickchange was successful.
- */
- inline bool ForceNickChange(const char* newnick) { return ChangeNick(newnick, true); }
-
/** Oper down.
* This will clear the +o usermode and unset the user's oper type
*/
*/
void WriteServ(const char* text, ...) CUSTOM_PRINTF(2, 3);
+ /** Sends a command to this user.
+ * @param command The command to be sent.
+ * @param text The message to send.
+ */
+ void WriteCommand(const char* command, const std::string& text);
+
+ /** Sends a server notice to this user.
+ * @param text The contents of the message to send.
+ */
+ void WriteNotice(const std::string& text) { this->WriteCommand("NOTICE", ":" + text); }
+
void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4);
void WriteNumeric(unsigned int numeric, const std::string &text);
*/
void WriteFrom(User *user, const char* text, ...) CUSTOM_PRINTF(3, 4);
- /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host.
- * @param dest The user to route the message to
- * @param data A std::string to send to the user
- */
- void WriteTo(User *dest, const std::string &data);
-
- /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host.
- * @param dest The user to route the message to
- * @param data The format string for text to send to the user
- * @param ... POD-type format arguments
- */
- void WriteTo(User *dest, const char *data, ...) CUSTOM_PRINTF(3, 4);
-
/** Write to all users that can see this user (including this user in the list if include_self is true), appending CR/LF
* @param line A std::string to send to the users
* @param include_self Should the message be sent back to the author?
*/
void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3);
- /** Write to all users that can see this user (not including this user in the list), appending CR/LF
- * @param text The format string for text to send to the users
- * @param ... POD-type format arguments
- */
- void WriteCommonExcept(const char* text, ...) CUSTOM_PRINTF(2, 3);
-
/** Write a quit message to all common users, as in User::WriteCommonExcept but with a specific
* quit message for opers only.
* @param normal_text Normal user quit message
void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
/** Dump text to a user target, splitting it appropriately to fit
- * @param LinePrefix text to prefix each complete line with
- * @param TextStream the text to send to the user
+ * @param linePrefix text to prefix each complete line with
+ * @param textStream the text to send to the user
*/
- void SendText(const std::string &LinePrefix, std::stringstream &TextStream);
+ void SendText(const std::string& linePrefix, std::stringstream& textStream);
/** Write to the user, routing the line if the user is remote.
*/
*/
bool SharesChannelWith(User *other);
- /** Send fake quit/join messages for host or ident cycle.
- * Run this after the item in question has changed.
- * You should not need to use this function, call ChangeDisplayedHost instead
- *
- * @param quitline The entire QUIT line, including the source using the old value
- */
- void DoHostCycle(const std::string &quitline);
-
/** Change the displayed host of a user.
* ALWAYS use this function, rather than writing User::dhost directly,
* as this triggers module events allowing the change to be syncronized to
- * remote servers. This will also emulate a QUIT and rejoin (where configured)
- * before setting their host field.
+ * remote servers.
* @param host The new hostname to set
* @return True if the change succeeded, false if it didn't
+ * (a module vetoed the change).
*/
- bool ChangeDisplayedHost(const char* host);
+ bool ChangeDisplayedHost(const std::string& host);
/** Change the ident (username) of a user.
* ALWAYS use this function, rather than writing User::ident directly,
- * as this correctly causes the user to seem to quit (where configured)
- * before setting their ident field.
+ * as this triggers module events allowing the change to be syncronized to
+ * remote servers.
* @param newident The new ident to set
* @return True if the change succeeded, false if it didn't
*/
- bool ChangeIdent(const char* newident);
+ bool ChangeIdent(const std::string& newident);
/** Change a users realname field.
* ALWAYS use this function, rather than writing User::fullname directly,
* @param gecos The user's new realname
* @return True if the change succeeded, false if otherwise
*/
- bool ChangeName(const char* gecos);
+ bool ChangeName(const std::string& gecos);
/** Change a user's nick
- * @param newnick The new nick
- * @param force True if the change is being forced (should not be blocked by modes like +N)
+ * @param newnick The new nick. If equal to the users uuid, the nick change always succeeds.
* @return True if the change succeeded
*/
- bool ChangeNick(const std::string& newnick, bool force = false);
-
- /** Send a command to all local users from this user
- * The command given must be able to send text with the
- * first parameter as a servermask (e.g. $*), so basically
- * you should use PRIVMSG or NOTICE.
- * @param command the command to send
- * @param text The text format string to send
- * @param ... Format arguments
- */
- void SendAll(const char* command, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
- /** Compile a channel list for this user. Used internally by WHOIS
- * @param source The user to prepare the channel list for
- * @param spy Whether to return the spy channel list rather than the normal one
- * @return This user's channel list
- */
- std::string ChannelList(User* source, bool spy);
-
- /** Split the channel list in cl which came from dest, and spool it to this user
- * Used internally by WHOIS
- * @param dest The user the original channel list came from
- * @param cl The channel list as a string obtained from User::ChannelList()
- */
- void SplitChanList(User* dest, const std::string &cl);
+ bool ChangeNick(const std::string& newnick, time_t newts = 0);
/** Remove this user from all channels they are on, and delete any that are now empty.
* This is used by QUIT, and will not send part messages!
*/
void PurgeEmptyChannels();
- /** Get the connect class which this user belongs to. NULL for remote users.
- * @return A pointer to this user's connect class.
- */
- virtual ConnectClass* GetClass();
-
/** Default destructor
*/
virtual ~User();
typedef unsigned int already_sent_t;
-class CoreExport LocalUser : public User, public InviteBase
+class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public insp::intrusive_list_node<LocalUser>
{
public:
LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
UserIOHandler eh;
- /** Position in UserManager::local_users
- */
- LocalUserList::iterator localuseriter;
-
/** Stats counter for bytes inbound
*/
unsigned int bytes_in;
*/
reference<ConnectClass> MyClass;
- ConnectClass* GetClass();
+ /** Get the connect class which this user belongs to.
+ * @return A pointer to this user's connect class.
+ */
+ ConnectClass* GetClass() const { return MyClass; }
/** Call this method to find the matching \<connect> for a user, and to check them against it.
*/
- void CheckClass();
+ void CheckClass(bool clone_count = true);
/** Server address and port that this user is connected to.
*/
*/
int GetServerPort();
+ /** Recursion fix: user is out of SendQ and will be quit as soon as possible.
+ * This can't be handled normally because QuitUser itself calls Write on other
+ * users, which could trigger their SendQ to overrun.
+ */
+ unsigned int quitting_sendq:1;
+
+ /** has the user responded to their previous ping?
+ */
+ unsigned int lastping:1;
+
+ /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks.
+ */
+ unsigned int exempt:1;
+
/** Used by PING checking code
*/
time_t nping;
+ /** Time that the connection last sent a message, used to calculate idle time
+ */
+ time_t idle_lastmsg;
+
/** This value contains how far into the penalty threshold the user is.
* This is used either to enable fake lag or for excess flood quits
*/
static already_sent_t already_sent_id;
already_sent_t already_sent;
- /** Stored reverse lookup from res_forward. Should not be used after resolution.
- */
- std::string stored_host;
-
- /** Starts a DNS lookup of the user's IP.
- * This will cause two UserResolver classes to be instantiated.
- * When complete, these objects set User::dns_done to true.
+ /** Check if the user matches a G or K line, and disconnect them if they do.
+ * @param doZline True if ZLines should be checked (if IP has changed since initial connect)
+ * Returns true if the user matched a ban, false else.
*/
- void StartDNSLookup();
+ bool CheckLines(bool doZline = false);
/** Use this method to fully connect a user.
* This will send the message of the day, check G/K/E lines, etc.
InviteList& GetInviteList();
/** Returns true if a user is invited to a channel.
- * @param channel A channel name to look up
+ * @param chan A channel to look up
* @return True if the user is invited to the given channel
*/
- bool IsInvited(const irc::string &channel);
-
- /** Adds a channel to a users invite list (invites them to a channel)
- * @param channel A channel name to add
- * @param timeout When the invite should expire (0 == never)
- */
- void InviteTo(const irc::string &channel, time_t timeout);
+ bool IsInvited(Channel* chan) { return (Invitation::Find(chan, this) != NULL); }
/** Removes a channel from a users invite list.
* This member function is called on successfully joining an invite only channel
* to which the user has previously been invited, to clear the invitation.
- * @param channel The channel to remove the invite to
+ * @param chan The channel to remove the invite to
+ * @return True if the user was invited to the channel and the invite was erased, false if the user wasn't invited
*/
- void RemoveInvite(const irc::string &channel);
+ bool RemoveInvite(Channel* chan);
void RemoveExpiredInvites();
class CoreExport RemoteUser : public User
{
public:
- RemoteUser(const std::string& uid, const std::string& srv) : User(uid, srv, USERTYPE_REMOTE)
+ RemoteUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_REMOTE)
{
}
virtual void SendText(const std::string& line);
class CoreExport FakeUser : public User
{
public:
- FakeUser(const std::string &uid, const std::string& srv) : User(uid, srv, USERTYPE_SERVER)
+ FakeUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_SERVER)
+ {
+ nick = srv->GetName();
+ }
+
+ FakeUser(const std::string& uid, const std::string& sname, const std::string& sdesc)
+ : User(uid, new Server(sname, sdesc), USERTYPE_SERVER)
{
- nick = srv;
+ nick = sname;
}
virtual CullResult cull();
{
return u->usertype == USERTYPE_SERVER ? static_cast<FakeUser*>(u) : NULL;
}
-/** Is an oper */
-#define IS_OPER(x) (x->oper)
-/** Is away */
-#define IS_AWAY(x) (!x->awaymsg.empty())
-/** Derived from Resolver, and performs user forward/reverse lookups.
- */
-class CoreExport UserResolver : public Resolver
+inline bool User::IsModeSet(ModeHandler* mh)
{
- private:
- /** UUID we are looking up */
- std::string uuid;
- /** True if the lookup is forward, false if is a reverse lookup
- */
- bool fwd;
- public:
- /** Create a resolver.
- * @param user The user to begin lookup on
- * @param to_resolve The IP or host to resolve
- * @param qt The query type
- * @param cache Modified by the constructor if the result was cached
- */
- UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache);
-
- /** Called on successful lookup
- * @param result Result string
- * @param ttl Time to live for result
- * @param cached True if the result was found in the cache
- */
- void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
+ return (modes[mh->GetId()]);
+}
- /** Called on failed lookup
- * @param e Error code
- * @param errormessage Error message string
- */
- void OnError(ResolverError e, const std::string &errormessage);
-};
+inline bool User::IsModeSet(UserModeReference& moderef)
+{
+ if (!moderef)
+ return false;
+ return IsModeSet(*moderef);
+}
-#endif
+inline void User::SetMode(ModeHandler* mh, bool value)
+{
+ modes[mh->GetId()] = value;
+}
*/
-#ifndef XLINE_H
-#define XLINE_H
+#pragma once
/** XLine is the base class for ban lines such as G lines and K lines.
* Modules may derive from this, and their xlines will automatically be
* line. Usually a line in the form 'expiring Xline blah, set by...'
* see the DisplayExpiry methods of GLine, ELine etc.
*/
- virtual void DisplayExpiry() = 0;
+ virtual void DisplayExpiry();
/** Returns the displayable form of the pattern for this xline,
* e.g. '*\@foo' or '*baz*'. This must always return the full pattern
* in a form which can be used to construct an entire derived xline,
* even if it is stored differently internally (e.g. GLine stores the
* ident and host parts seperately but will still return ident\@host
- * for its Displayable() method)
+ * for its Displayable() method).
*/
- virtual const char* Displayable() = 0;
+ virtual const std::string& Displayable() = 0;
/** Called when the xline has just been added.
*/
virtual void Apply(User* u);
- virtual void DisplayExpiry();
-
- virtual const char* Displayable();
+ virtual const std::string& Displayable();
virtual bool IsBurstable();
virtual void Apply(User* u);
- virtual void DisplayExpiry();
-
- virtual const char* Displayable();
+ virtual const std::string& Displayable();
/** Ident mask (ident part only)
*/
virtual void Unset();
- virtual void DisplayExpiry();
-
virtual void OnAdd();
- virtual const char* Displayable();
+ virtual const std::string& Displayable();
/** Ident mask (ident part only)
*/
virtual void Apply(User* u);
- virtual void DisplayExpiry();
-
- virtual const char* Displayable();
+ virtual const std::string& Displayable();
/** IP mask (no ident part)
*/
virtual void Apply(User* u);
- virtual void DisplayExpiry();
-
- virtual const char* Displayable();
+ virtual const std::string& Displayable();
/** Nickname mask
*/
*/
void InvokeStats(const std::string &type, int numeric, User* user, string_list &results);
};
-
-#endif
sub gendep($);
sub dep_cpp($$$);
sub dep_so($);
-sub dep_dir($);
+sub dep_dir($$);
sub run();
my %f2dep;
mkdir $_ for qw/bin modules obj/;
# BSD make has a horribly annoying bug resulting in an extra chdir of the make process
# Create symlinks to work around it
- symlink "../$_", "obj/$_" for qw/bin modules obj/;
+ symlink "../$_", "obj/$_" for qw/bin coremods modules obj/;
$build = getcwd();
open MAKE, '>real.mk' or die "Could not write real.mk: $!";
\@echo "in order to set the correct environment variables"
\@exit 1
-all: inspircd commands modules
+all: inspircd coremods modules
END
- my(@core_deps, @cmdlist, @modlist);
+ my(@core_deps, @cmodlist, @modlist);
for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, "threadengines/threadengine_pthread.cpp") {
my $out = find_output $file;
dep_cpp $file, $out, 'gen-o';
- next if $file =~ m#^socketengines/# && $file ne "socketengines/$ENV{SOCKETENGINE}.cpp";
+ next if $file =~ m#^socketengines/# && $file ne "socketengines/socketengine_$ENV{SOCKETENGINE}.cpp";
push @core_deps, $out;
}
- for my $file (<commands/*.cpp>) {
- my $out = dep_so $file;
- push @cmdlist, $out;
+ opendir my $coremoddir, 'coremods';
+ for my $file (sort readdir $coremoddir) {
+ next if $file =~ /^\./;
+ if ($file =~ /^core_/ && -d "coremods/$file" && dep_dir "coremods/$file", "modules/$file") {
+ mkdir "$build/obj/$file";
+ push @cmodlist, "modules/$file.so";
+ }
+ if ($file =~ /^core_.*\.cpp$/) {
+ my $out = dep_so "coremods/$file";
+ push @cmodlist, $out;
+ }
}
opendir my $moddir, 'modules';
rename "modules/$file", "modules/$file~";
symlink "extra/$file", "modules/$file";
}
- if ($file =~ /^m_/ && -d "modules/$file" && dep_dir "modules/$file") {
+ if ($file =~ /^m_/ && -d "modules/$file" && dep_dir "modules/$file", "modules/$file") {
mkdir "$build/obj/$file";
push @modlist, "modules/$file.so";
}
}
my $core_mk = join ' ', @core_deps;
- my $cmds = join ' ', @cmdlist;
+ my $cmods = join ' ', @cmodlist;
my $mods = join ' ', @modlist;
print MAKE <<END;
inspircd: bin/inspircd
-commands: $cmds
+coremods: $cmods
modules: $mods
-.PHONY: all bad-target inspircd commands modules
+.PHONY: all bad-target inspircd coremods modules
END
}
END
my(@deps, @srcs);
- for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <commands/*.cpp>,
+ for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <coremods/*.cpp>, <coremods/core_*/*.cpp>,
<modules/*.cpp>, <modules/m_*/*.cpp>, "threadengines/threadengine_pthread.cpp") {
my $out = find_output $file, 1;
if ($out =~ m#obj/([^/]+)/[^/]+.o$#) {
mkdir "$ENV{BUILDPATH}/obj/$1";
}
dep_cpp $file, $out, 'gen-o';
- next if $file =~ m#^socketengines/# && $file ne "socketengines/$ENV{SOCKETENGINE}.cpp";
+ next if $file =~ m#^socketengines/# && $file ne "socketengines/socketengine_$ENV{SOCKETENGINE}.cpp";
push @deps, $out;
push @srcs, $file;
}
sub find_output {
my($file, $static) = @_;
my($path,$base) = $file =~ m#^((?:.*/)?)([^/]+)\.cpp# or die "Bad file $file";
- if ($path eq 'modules/' || $path eq 'commands/') {
+ if ($path eq 'modules/' || $path eq 'coremods/') {
return $static ? "obj/$base.o" : "modules/$base.so";
} elsif ($path eq '' || $path eq 'modes/' || $path =~ /^[a-z]+engines\/$/) {
return "obj/$base.o";
- } elsif ($path =~ m#modules/(m_.*)/#) {
+ } elsif ($path =~ m#modules/(m_.*)/# || $path =~ m#coremods/(core_.*)/#) {
return "obj/$1/$base.o";
} else {
die "Can't determine output for $file";
while (<$in>) {
if (/^\s*#\s*include\s*"([^"]+)"/) {
my $inc = $1;
- next if $inc eq 'inspircd_version.h' && $f eq '../include/inspircd.h';
+ next if $inc eq 'config.h' && $f eq '../include/inspircd.h';
my $found = 0;
for my $loc ("$basedir/$inc", "../include/$inc") {
next unless -e $loc;
sub dep_so($) {
my($file) = @_;
my $out = find_output $file;
- my $split = find_output $file, 1;
- if ($ENV{SPLIT_CC}) {
- dep_cpp $file, $split, 'gen-o';
- print MAKE "$out: $split\n";
- print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-so\$(VERBOSE) \$\@ \$(SOURCEPATH)/src/$file \$>\n";
- } else {
- dep_cpp $file, $out, 'gen-so';
- }
+ dep_cpp $file, $out, 'gen-so';
return $out;
}
-sub dep_dir($) {
- my($dir) = @_;
+sub dep_dir($$) {
+ my($dir, $outdir) = @_;
my @ofiles;
opendir DIR, $dir;
for my $file (sort readdir DIR) {
closedir DIR;
if (@ofiles) {
my $ofiles = join ' ', @ofiles;
- print MAKE "$dir.so: $ofiles\n";
+ print MAKE "$outdir.so: $ofiles\n";
print MAKE "\t@\$(SOURCEPATH)/make/unit-cc.pl link-dir\$(VERBOSE) \$\@ \$^ \$>\n";
return 1;
} else {
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include <sys/epoll.h>
-
-int main() {
- int fd = epoll_create(1);
- return (fd < 0);
-}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include <sys/eventfd.h>
-
-int main() {
- eventfd_t efd_data;
- int fd;
-
- fd = eventfd(0, EFD_NONBLOCK);
- eventfd_read(fd, &efd_data);
-
- return (fd < 0);
-}
#
# InspIRCd -- Internet Relay Chat Daemon
#
-# Copyright (C) 2012 Peter Powell <petpow@saberuk.com>
+# Copyright (C) 2012-2014 Peter Powell <petpow@saberuk.com>
# Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
# Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
# Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
#
-package make::configure;
+BEGIN {
+ require 5.8.0;
+}
-require 5.8.0;
+package make::configure;
use strict;
use warnings FATAL => qw(all);
+use Cwd 'getcwd';
use Exporter 'import';
-use POSIX;
+use File::Basename 'basename';
+
use make::utilities;
-our @EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies nopedantic resolve_directory yesno showhelp promptstring_s module_installed);
-
-my $no_git = 0;
-
-sub yesno {
- my ($flag,$prompt) = @_;
- print "$prompt [\e[1;32m$main::config{$flag}\e[0m] -> ";
- chomp(my $tmp = <STDIN>);
- if ($tmp eq "") { $tmp = $main::config{$flag} }
- if (($tmp eq "") || ($tmp =~ /^y/i))
- {
- $main::config{$flag} = "y";
- }
- else
- {
- $main::config{$flag} = "n";
+
+our @EXPORT = qw(cmd_clean cmd_help cmd_update
+ read_configure_cache write_configure_cache
+ get_compiler_info find_compiler
+ run_test test_file test_header
+ dump_hash get_property parse_templates);
+
+sub __get_socketengines() {
+ my @socketengines;
+ foreach (<src/socketengines/socketengine_*.cpp>) {
+ s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
+ push @socketengines, $1;
}
- return;
+ return @socketengines;
}
-sub resolve_directory
-{
- my $ret = $_[0];
- eval
- {
- use File::Spec;
- $ret = File::Spec->rel2abs($_[0]);
- };
- return $ret;
-}
+# TODO: when buildtool is done this can be mostly removed with
+# the remainder being merged into parse_templates.
+sub __get_template_settings($$) {
-sub getrevision {
- if ($no_git)
- {
- return "0";
+ # These are actually hash references
+ my ($config, $compiler) = @_;
+
+ #Â Start off by populating with the config
+ my %settings = %$config;
+
+ # Compiler information
+ while (my ($key, $value) = each %{$compiler}) {
+ $settings{'COMPILER_' . $key} = $value;
}
- my $data = `git describe --tags 2>/dev/null`;
- if ($data eq "")
- {
- $no_git = 1;
- return '0';
+
+ # Version information
+ my %version = get_version();
+ while (my ($key, $value) = each %version) {
+ $settings{'VERSION_' . $key} = $value;
}
- chomp $data; # remove \n
- return $data;
+
+ # Miscellaneous information
+ $settings{SYSTEM_NAME} = lc $^O;
+ chomp($settings{SYSTEM_NAME_VERSION} = `uname -sr 2>/dev/null`);
+
+ return %settings;
}
-sub getcompilerflags {
- my ($file) = @_;
- open(FLAGS, $file) or return "";
- while (<FLAGS>) {
- if ($_ =~ /^\/\* \$CompileFlags: (.+) \*\/\r?$/) {
- my $x = translate_functions($1, $file);
- next if ($x eq "");
- close(FLAGS);
- return $x;
- }
- }
- close(FLAGS);
- return "";
+sub cmd_clean {
+ unlink '.config.cache';
}
-sub getlinkerflags {
- my ($file) = @_;
- open(FLAGS, $file) or return "";
- while (<FLAGS>) {
- if ($_ =~ /^\/\* \$LinkerFlags: (.+) \*\/\r?$/) {
- my $x = translate_functions($1, $file);
- next if ($x eq "");
- close(FLAGS);
- return $x;
- }
- }
- close(FLAGS);
- return "";
+sub cmd_help {
+ my $PWD = getcwd();
+ my $SELIST = join ', ', __get_socketengines();
+ print <<EOH;
+Usage: $0 [options]
+
+When no options are specified, configure runs in interactive mode and you must
+specify any required values manually. If one or more options are specified,
+non-interactive configuration is started and any omitted values are defaulted.
+
+PATH OPTIONS
+
+ --system Automatically set up the installation paths
+ for system-wide installation.
+ --prefix=[dir] The root install directory. If this is set then
+ all subdirectories will be adjusted accordingly.
+ [$PWD/run]
+ --binary-dir=[dir] The location where the main server binary is
+ stored.
+ [$PWD/run/bin]
+ --config-dir=[dir] The location where the configuration files and
+ SSL certificates are stored.
+ [$PWD/run/conf]
+ --data-dir=[dir] The location where the data files, such as the
+ pid file, are stored.
+ [$PWD/run/data]
+ --log-dir=[dir] The location where the log files are stored.
+ [$PWD/run/logs]
+ --module-dir=[dir] The location where the loadable modules are
+ stored.
+ [$PWD/run/modules]
+ --build-dir=[dir] The location to store files in while building.
+
+
+EXTRA MODULE OPTIONS
+
+ --enable-extras=[extras] Enables a comma separated list of extra modules.
+ --disable-extras=[extras] Disables a comma separated list of extra modules.
+ --list-extras Shows the availability status of all extra
+ modules.
+
+MISC OPTIONS
+
+ --clean Remove the configuration cache file and start
+ the interactive configuration wizard.
+ --disable-interactive Disables the interactive configuration wizard.
+ --help Show this message and exit.
+ --uid=[name] Sets the user to run InspIRCd as.
+ --socketengine=[name] Sets the socket engine to be used. Possible
+ values are $SELIST.
+ --update Updates the build environment.
+
+
+FLAGS
+
+ CXX=[name] Sets the C++ compiler to use when building the
+ server. If not specified then the build system
+ will search for c++, g++, clang++ or icpc.
+
+If you have any problems with configuring InspIRCd then visit our IRC channel
+at irc.ChatSpike.net #InspIRCd.
+
+EOH
+ exit 0;
}
-sub getdependencies {
- my ($file) = @_;
- open(FLAGS, $file) or return "";
- while (<FLAGS>) {
- if ($_ =~ /^\/\* \$ModDep: (.+) \*\/\r?$/) {
- my $x = translate_functions($1, $file);
- next if ($x eq "");
- close(FLAGS);
- return $x;
- }
+sub cmd_update {
+ unless (-f '.config.cache') {
+ print "You have not run $0 before. Please do this before trying to update the build files.\n";
+ exit 1;
}
- close(FLAGS);
- return "";
+ print "Updating...\n";
+ my %config = read_configure_cache();
+ my %compiler = get_compiler_info($config{CXX});
+ parse_templates(\%config, \%compiler);
+ print "Update complete!\n";
+ exit 0;
}
-sub nopedantic {
- my ($file) = @_;
- open(FLAGS, $file) or return "";
- while (<FLAGS>) {
- if ($_ =~ /^\/\* \$NoPedantic \*\/\r?$/) {
- my $x = translate_functions($_, $file);
- next if ($x eq "");
- close(FLAGS);
- return 1;
- }
+sub read_configure_cache {
+ my %cfg = ();
+ open(CACHE, '.config.cache') or return %cfg;
+ while (my $line = <CACHE>) {
+ next if $line =~ /^\s*($|\#)/;
+ my ($key, $value) = ($line =~ /^(\S+)="(.*)"$/);
+ $cfg{$key} = $value;
}
- close(FLAGS);
- return 0;
+ close(CACHE);
+ return %cfg;
}
-sub getmodules
-{
- my ($silent) = @_;
-
- my $i = 0;
+sub write_configure_cache(%) {
+ my %cfg = @_;
+ open(CACHE, ">.config.cache") or return 0;
+ while (my ($key, $value) = each %cfg) {
+ $value = "" unless defined $value;
+ print CACHE "$key=\"$value\"\n";
+ }
+ close(CACHE);
+ return 1;
+}
- if (!$silent)
- {
- print "Detecting modules ";
+sub get_compiler_info($) {
+ my $binary = shift;
+ my $version = `$binary -v 2>&1`;
+ if ($version =~ /(?:clang|llvm)\sversion\s(\d+\.\d+)/i) {
+ return (
+ NAME => 'Clang',
+ VERSION => $1,
+ UNSUPPORTED => $1 lt '3.0',
+ REASON => 'Clang 2.9 and older do not have adequate C++ support.'
+ );
+ } elsif ($version =~ /gcc\sversion\s(\d+\.\d+)/i) {
+ return (
+ NAME => 'GCC',
+ VERSION => $1,
+ UNSUPPORTED => $1 lt '4.1',
+ REASON => 'GCC 4.0 and older do not have adequate C++ support.'
+ );
+ } elsif ($version =~ /(?:icc|icpc)\sversion\s(\d+\.\d+).\d+\s\(gcc\sversion\s(\d+\.\d+).\d+/i) {
+ return (
+ NAME => 'ICC',
+ VERSION => $1,
+ UNSUPPORTED => $2 lt '4.1',
+ REASON => "ICC $1 (GCC $2 compatibility mode) does not have adequate C++ support."
+ );
}
+ return (
+ NAME => $binary,
+ VERSION => '0.0'
+ );
+}
- opendir(DIRHANDLE, "src/modules") or die("WTF, missing src/modules!");
- foreach my $name (sort readdir(DIRHANDLE))
- {
- if ($name =~ /^m_(.+)\.cpp$/)
- {
- my $mod = $1;
- $main::modlist[$i++] = $mod;
- if (!$silent)
- {
- print ".";
- }
+sub find_compiler {
+ foreach my $compiler ('c++', 'g++', 'clang++', 'icpc') {
+ return $compiler unless system "$compiler -v > /dev/null 2>&1";
+ if ($^O eq 'Darwin') {
+ return $compiler unless system "xcrun $compiler -v > /dev/null 2>&1";
}
}
- closedir(DIRHANDLE);
+ return "";
+}
- if (!$silent)
- {
- print "\nOk, $i modules.\n";
- }
+sub run_test($$) {
+ my ($what, $result) = @_;
+ print "Checking whether $what is available... ";
+ print $result ? "yes\n" : "no\n";
+ return $result;
}
-sub promptnumeric($$)
-{
- my $continue = 0;
- my ($prompt, $configitem) = @_;
- while (!$continue)
- {
- print "Please enter the maximum $prompt?\n";
- print "[\e[1;32m$main::config{$configitem}\e[0m] -> ";
- chomp(my $var = <STDIN>);
- if ($var eq "")
- {
- $var = $main::config{$configitem};
- }
- if ($var =~ /^\d+$/) {
- # We don't care what the number is, set it and be on our way.
- $main::config{$configitem} = $var;
- $continue = 1;
- print "\n";
- } else {
- print "You must enter a number in this field. Please try again.\n\n";
- }
- }
+sub test_file($$;$) {
+ my ($cc, $file, $args) = @_;
+ my $status = 0;
+ $args ||= '';
+ $status ||= system "$cc -o __test_$file make/test/$file $args >/dev/null 2>&1";
+ $status ||= system "./__test_$file >/dev/null 2>&1";
+ unlink "./__test_$file";
+ return !$status;
}
-sub module_installed($)
-{
- my $module = shift;
- eval("use $module;");
- return !$@;
+sub test_header($$;$) {
+ my ($cc, $header, $args) = @_;
+ $args ||= '';
+ open(CC, "| $cc -E - $args >/dev/null 2>&1") or return 0;
+ print CC "#include <$header>";
+ close(CC);
+ return !$?;
}
-sub promptstring_s($$)
+sub get_property($$;$)
{
- my ($prompt,$default) = @_;
- my $var;
- print "$prompt\n";
- print "[\e[1;32m$default\e[0m] -> ";
- chomp($var = <STDIN>);
- $var = $default if $var eq "";
- print "\n";
- return $var;
+ my ($file, $property, $default) = @_;
+ open(MODULE, $file) or return $default;
+ while (<MODULE>) {
+ if ($_ =~ /^\/\* \$(\S+): (.+) \*\/$/) {
+ next unless $1 eq $property;
+ close(MODULE);
+ return translate_functions($2, $file);
+ }
+ }
+ close(MODULE);
+ return defined $default ? $default : '';
}
-sub dumphash()
-{
+sub dump_hash() {
print "\n\e[1;32mPre-build configuration is complete!\e[0m\n\n";
print "\e[0mBase install path:\e[1;32m\t\t$main::config{BASE_DIR}\e[0m\n";
print "\e[0mConfig path:\e[1;32m\t\t\t$main::config{CONFIG_DIR}\e[0m\n";
+ print "\e[0mData path:\e[1;32m\t\t\t$main::config{DATA_DIR}\e[0m\n";
+ print "\e[0mLog path:\e[1;32m\t\t\t$main::config{LOG_DIR}\e[0m\n";
print "\e[0mModule path:\e[1;32m\t\t\t$main::config{MODULE_DIR}\e[0m\n";
- print "\e[0mGCC Version Found:\e[1;32m\t\t$main::config{GCCVER}.$main::config{GCCMINOR}\e[0m\n";
- print "\e[0mCompiler program:\e[1;32m\t\t$main::config{CC}\e[0m\n";
- print "\e[0mGnuTLS Support:\e[1;32m\t\t\t$main::config{USE_GNUTLS}\e[0m\n";
- print "\e[0mOpenSSL Support:\e[1;32m\t\t$main::config{USE_OPENSSL}\e[0m\n\n";
- print "\e[1;32mImportant note: The maximum length values are now configured in the\e[0m\n";
- print "\e[1;32m configuration file, not in ./configure! See the <limits>\e[0m\n";
- print "\e[1;32m tag in the configuration file for more information.\e[0m\n\n";
+ print "\e[0mCompiler:\e[1;32m\t\t\t$main::cxx{NAME} $main::cxx{VERSION}\e[0m\n";
+ print "\e[0mSocket engine:\e[1;32m\t\t\t$main::config{SOCKETENGINE}\e[0m\n";
+ print "\e[0mGnuTLS support:\e[1;32m\t\t\t$main::config{USE_GNUTLS}\e[0m\n";
+ print "\e[0mOpenSSL support:\e[1;32m\t\t$main::config{USE_OPENSSL}\e[0m\n";
}
-sub is_dir
-{
- my ($path) = @_;
- if (chdir($path))
- {
- chdir($main::this);
- return 1;
- }
- else
- {
- # Just in case..
- chdir($main::this);
- return 0;
- }
-}
+sub parse_templates($$) {
+
+ # These are actually hash references
+ my ($config, $compiler) = @_;
+
+ # Collect settings to be used when generating files
+ my %settings = __get_template_settings($config, $compiler);
+
+ # Iterate through files in make/template.
+ foreach (<make/template/*>) {
+ print "Parsing $_...\n";
+ open(TEMPLATE, $_);
+ my (@lines, $mode, @platforms, %targets);
+
+ # First pass: parse template variables and directives.
+ while (my $line = <TEMPLATE>) {
+ chomp $line;
+
+ # Does this line match a variable?
+ while ($line =~ /(@(\w+?)@)/) {
+ my ($variable, $name) = ($1, $2);
+ if (defined $settings{$name}) {
+ $line =~ s/$variable/$settings{$name}/;
+ } else {
+ print STDERR "Warning: unknown template variable '$name' in $_!\n";
+ last;
+ }
+ }
-sub showhelp
-{
- chomp(my $PWD = `pwd`);
- print <<EOH;
-Usage: configure [options]
-
-*** NOTE: NON-INTERACTIVE CONFIGURE IS *NOT* SUPPORTED BY THE ***
-*** INSPIRCD DEVELOPMENT TEAM. DO NOT ASK FOR HELP REGARDING ***
-*** NON-INTERACTIVE CONFIGURE ON THE FORUMS OR ON IRC! ***
-
-Options: [defaults in brackets after descriptions]
-
-When no options are specified, interactive
-configuration is started and you must specify
-any required values manually. If one or more
-options are specified, non-interactive configuration
-is started, and any omitted values are defaulted.
-
-Arguments with a single \"-\" symbol, as in
-InspIRCd 1.0.x, are also allowed.
-
- --disable-interactive Sets no options itself, but
- will disable any interactive prompting.
- --update Update makefiles and dependencies
- --clean Remove .config.cache file and go interactive
- --enable-gnutls Enable GnuTLS module [no]
- --enable-openssl Enable OpenSSL module [no]
- --enable-epoll Enable epoll() where supported [set]
- --enable-kqueue Enable kqueue() where supported [set]
- --disable-epoll Do not enable epoll(), fall back
- to select() [not set]
- --disable-kqueue Do not enable kqueue(), fall back
- to select() [not set]
- --with-cc=[filename] Use an alternative compiler to
- build InspIRCd [g++]
- --with-maxbuf=[n] Change the per message buffer size [512]
- DO NOT ALTER THIS OPTION WITHOUT GOOD REASON
- AS IT *WILL* BREAK CLIENTS!!!
- --prefix=[directory] Base directory to install into (if defined,
- can automatically define config, module, bin
- and library dirs as subdirectories of prefix)
- [$PWD]
- --config-dir=[directory] Config file directory for config and SSL certs
- [$PWD/run/conf]
- --log-dir=[directory] Log file directory for logs
- [$PWD/run/logs]
- --data-dir=[directory] Data directory for variable data, such as the
- permchannel configuration and the XLine database
- [$PWD/run/data]
- --module-dir=[directory] Modules directory for loadable modules
- [$PWD/run/modules]
- --binary-dir=[directory] Binaries directory for core binary
- [$PWD/run/bin]
- --list-extras Show current status of extra modules
- --enable-extras=[extras] Enable the specified list of extras
- --disable-extras=[extras] Disable the specified list of extras
- --help Show this help text and exit
+ # Does this line match a directive?
+ if ($line =~ /^\s*%(\w+)\s+(.+)$/) {
+ if ($1 eq 'define') {
+ if ($settings{$2}) {
+ push @lines, "#define $2";
+ } else {
+ push @lines, "#undef $2";
+ }
+ } elsif ($1 eq 'mode') {
+ $mode = oct $2;
+ } elsif ($1 eq 'platform') {
+ push @platforms, $2;
+ } elsif ($1 eq 'target') {
+ if ($2 =~ /(\w+)\s(.+)/) {
+ $targets{$1} = $2;
+ } else {
+ $targets{DEFAULT} = $2;
+ }
+ } else {
+ print STDERR "Warning: unknown template command '$1' in $_!\n";
+ push @lines, $line;
+ }
+ next;
+ }
+ push @lines, $line;
+ }
+ close(TEMPLATE);
-EOH
- exit(0);
+ # Only proceed if this file should be templated on this platform.
+ if ($#platforms < 0 || grep { $_ eq $^O } @platforms) {
+
+ # Add a default target if the template has not defined one.
+ unless (scalar keys %targets) {
+ $targets{DEFAULT} = basename $_;
+ }
+
+ # Second pass: parse makefile junk and write files.
+ while (my ($name, $target) = each %targets) {
+
+ # TODO: when buildtool is done this mess can be removed completely.
+ my @final_lines;
+ foreach my $line (@lines) {
+
+ # Are we parsing a makefile and does this line match a statement?
+ if ($name =~ /(?:BSD|GNU)_MAKE/ && $line =~ /^\s*\@(\w+)(?:\s+(.+))?$/) {
+ my @tokens = split /\s/, $2 if defined $2;
+ if ($1 eq 'DO_EXPORT' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ foreach my $variable (@tokens) {
+ push @final_lines, "MAKEENV += $variable='\${$variable}'";
+ }
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "export $2";
+ }
+ } elsif ($1 eq 'ELSE') {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".else";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "else";
+ }
+ } elsif ($1 eq 'ENDIF') {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".endif";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "endif";
+ }
+ } elsif ($1 eq 'ELSIFEQ' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".elif $tokens[0] == $tokens[1]";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "else ifeq ($tokens[0], $tokens[1])";
+ }
+ } elsif ($1 eq 'IFDEF' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".if defined($2)";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "ifdef $2";
+ }
+ } elsif ($1 eq 'IFEQ' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".if $tokens[0] == $tokens[1]";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "ifeq ($tokens[0],$tokens[1])";
+ }
+ } elsif ($1 eq 'IFNEQ' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".if $tokens[0] != $tokens[1]";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "ifneq ($tokens[0],$tokens[1])";
+ }
+ } elsif ($1 eq 'IFNDEF' && defined $2) {
+ if ($name eq 'BSD_MAKE') {
+ push @final_lines, ".if !defined($2)";
+ } elsif ($name eq 'GNU_MAKE') {
+ push @final_lines, "ifndef $2";
+ }
+ } elsif ($1 eq 'TARGET' && defined $2) {
+ if ($tokens[0] eq $name) {
+ push @final_lines, substr($2, length($tokens[0]) + 1);
+ }
+ } elsif ($1 !~ /[A-Z]/) {
+ # HACK: silently ignore if lower case as these are probably make commands.
+ push @final_lines, $line;
+ } else {
+ print STDERR "Warning: unknown template command '$1' in $_!\n";
+ push @final_lines, $line;
+ }
+ next;
+ }
+
+ push @final_lines, $line;
+ }
+
+ # Write the template file.
+ print "Writing $target...\n";
+ open(TARGET, ">$target");
+ foreach (@final_lines) {
+ print TARGET $_, "\n";
+ }
+ close(TARGET);
+
+ # Set file permissions.
+ if (defined $mode) {
+ chmod $mode, $target;
+ }
+ }
+ }
+ }
}
1;
-
+++ /dev/null
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-# Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
-# Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
-#
-# This file is part of InspIRCd. InspIRCd 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, version 2.
-#
-# 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, see <http://www.gnu.org/licenses/>.
-#
-
-
-package make::gnutlscert;
-
-require 5.8.0;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use make::configure;
-our @EXPORT = qw(make_gnutls_cert);
-
-
-sub make_gnutls_cert()
-{
- open (FH, ">certtool.template");
- my $timestr = time();
- my $commonname = promptstring_s('What is the hostname of your server?', 'irc.example.com');
- my $email = promptstring_s('What email address can you be contacted at?', 'example@example.com');
- my $unit = promptstring_s('What is the name of your unit?', 'Server Admins');
- my $org = promptstring_s('What is the name of your organization?', 'Example IRC Network');
- my $city = promptstring_s('What city are you located in?', 'Example City');
- my $state = promptstring_s('What state are you located in?', 'Example State');
- my $country = promptstring_s('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
- my $days = promptstring_s('How many days do you want your certificate to be valid for?', '365');
- print FH <<__END__;
-# X.509 Certificate options
-#
-# DN options
-
-# The organization of the subject.
-organization = "$org"
-
-# The organizational unit of the subject.
-unit = "$unit"
-
-# The locality of the subject.
-locality = "$city"
-
-# The state of the certificate owner.
-state = "$state"
-
-# The country of the subject. Two letter code.
-country = "$country"
-
-# The common name of the certificate owner.
-cn = "$commonname"
-
-# A user id of the certificate owner.
-#uid = "clauper"
-
-# If the supported DN OIDs are not adequate you can set
-# any OID here.
-# For example set the X.520 Title and the X.520 Pseudonym
-# by using OID and string pairs.
-#dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal"
-
-# This is deprecated and should not be used in new
-# certificates.
-# pkcs9_email = "none\@none.org"
-
-# The serial number of the certificate
-serial = $timestr
-
-# In how many days, counting from today, this certificate will expire.
-expiration_days = $days
-
-# X.509 v3 extensions
-
-# A dnsname in case of a WWW server.
-#dns_name = "www.none.org"
-
-# An IP address in case of a server.
-#ip_address = "192.168.1.1"
-
-# An email in case of a person
-email = "$email"
-
-# An URL that has CRLs (certificate revocation lists)
-# available. Needed in CA certificates.
-#crl_dist_points = "http://www.getcrl.crl/getcrl/"
-
-# Whether this is a CA certificate or not
-#ca
-
-# Whether this certificate will be used for a TLS client
-tls_www_client
-
-# Whether this certificate will be used for a TLS server
-tls_www_server
-
-# Whether this certificate will be used to sign data (needed
-# in TLS DHE ciphersuites).
-signing_key
-
-# Whether this certificate will be used to encrypt data (needed
-# in TLS RSA ciphersuites). Note that it is prefered to use different
-# keys for encryption and signing.
-encryption_key
-
-# Whether this key will be used to sign other certificates.
-cert_signing_key
-
-# Whether this key will be used to sign CRLs.
-crl_signing_key
-
-# Whether this key will be used to sign code.
-code_signing_key
-
-# Whether this key will be used to sign OCSP data.
-ocsp_signing_key
-
-# Whether this key will be used for time stamping.
-time_stamping_key
-__END__
-close(FH);
-my $certtool = "certtool";
-if (`uname -s` eq "Darwin\n") {
- # On OS X the certtool binary name is different to prevent
- # collisions with the system certtool from NSS.
- $certtool = "gnutls-certtool";
-}
-if ( (my $status = system("$certtool --generate-privkey --outfile key.pem")) ne 0) { return 1; }
-if ( (my $status = system("$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template certtool.template")) ne 0) { return 1; }
-unlink("certtool.template");
-return 0;
-}
-
-1;
-
+++ /dev/null
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-# Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
-# Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
-#
-# This file is part of InspIRCd. InspIRCd 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, version 2.
-#
-# 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, see <http://www.gnu.org/licenses/>.
-#
-
-
-package make::opensslcert;
-
-require 5.8.0;
-
-use strict;
-use warnings FATAL => qw(all);
-
-use Exporter 'import';
-use make::configure;
-our @EXPORT = qw(make_openssl_cert);
-
-
-sub make_openssl_cert()
-{
- open (FH, ">openssl.template");
- my $commonname = promptstring_s('What is the hostname of your server?', 'irc.example.com');
- my $email = promptstring_s('What email address can you be contacted at?', 'example@example.com');
- my $unit = promptstring_s('What is the name of your unit?', 'Server Admins');
- my $org = promptstring_s('What is the name of your organization?', 'Example IRC Network');
- my $city = promptstring_s('What city are you located in?', 'Example City');
- my $state = promptstring_s('What state are you located in?', 'Example State');
- my $country = promptstring_s('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
- my $time = promptstring_s('How many days do you want your certificate to be valid for?', '365');
- print FH <<__END__;
-$country
-$state
-$city
-$org
-$unit
-$commonname
-$email
-__END__
-close(FH);
-system("cat openssl.template | openssl req -x509 -nodes -newkey rsa:1024 -keyout key.pem -out cert.pem -days $time 2>/dev/null");
-system("openssl dhparam -out dhparams.pem 1024");
-unlink("openssl.template");
-}
-
-1;
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#define INSPIRCD_BRANCH "InspIRCd-@VERSION_MAJOR@.@VERSION_MINOR@"
+#define INSPIRCD_VERSION "InspIRCd-@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@"
+#define INSPIRCD_REVISION "@VERSION_LABEL@"
+#define INSPIRCD_SYSTEM "@SYSTEM_NAME_VERSION@"
+
+#define INSPIRCD_CONFIG_PATH "@CONFIG_DIR@"
+#define INSPIRCD_DATA_PATH "@DATA_DIR@"
+#define INSPIRCD_LOG_PATH "@LOG_DIR@"
+#define INSPIRCD_MODULE_PATH "@MODULE_DIR@"
+
+#define INSPIRCD_SOCKETENGINE_NAME "@SOCKETENGINE@"
+
+#ifndef _WIN32
+ %target include/config.h
+ %define HAS_CLOCK_GETTIME
+ %define HAS_EVENTFD
+#endif
--- /dev/null
+%target .gdbargs
+handle SIGPIPE pass nostop noprint
+handle SIGHUP pass nostop noprint
+run
+%mode 0750
#!/usr/bin/env perl
#
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+# InspIRCd Start up the InspIRCd Internet Relay Chat Daemon
+#
+# chkconfig: 2345 55 25
+# description: InspIRCd -- Internet Relay Chat Daemon
+#
+# processname: inspircd
use strict;
use POSIX;
my $runpath = "@BASE_DIR@";
my $datadir = "@DATA_DIR@";
my $valgrindlogpath = "$basepath/valgrindlogs";
-my $executable = "@EXECUTABLE@";
-my $version = "@VERSION@";
+my $executable = "inspircd";
+my $version = "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@";
my $uid = "@UID@";
-if ($< == 0 || $> == 0) {
+if (!("--runasroot" ~~ @ARGV) && ($< == 0 || $> == 0)) {
if ($uid !~ /^\d+$/) {
# Named UID, look it up
$uid = getpwnam $uid;
--- /dev/null
+.\"
+.\" InspIRCd -- Internet Relay Chat Daemon
+.\"
+.\" Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+.\"
+.\" This file is part of InspIRCd. InspIRCd 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, version 2.
+.\"
+.\" 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, see <http://www.gnu.org/licenses/>.
+.\"
+
+
+.TH "InspIRCd" "1" "June 2014" "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@" "InspIRCd Manual"
+
+.SH "NAME"
+\t\fBInspIRCd\fR - \fIthe\fR stable, high-performance and modular Internet Relay Chat Daemon
+.BR
+
+.SH "SYNOPSIS"
+\t\fBinspircd-genssl\fR [ auto | gnutls | openssl ]
+
+.SH "OPTIONS"
+.TP
+.B "auto"
+.br
+Looks for both GnuTLS and OpenSSL and uses the first one which is available for certificate generation.
+.TP
+.B "gnutls"
+.br
+Generates certificates using GnuTLS.
+.TP
+.br
+.B "openssl"
+Generates certificates using OpenSSL.
+
+.SH "SUPPORT"
+IRC support for InspIRCd can be found at irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
--- /dev/null
+.\"
+.\" InspIRCd -- Internet Relay Chat Daemon
+.\"
+.\" Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+.\"
+.\" This file is part of InspIRCd. InspIRCd 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, version 2.
+.\"
+.\" 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, see <http://www.gnu.org/licenses/>.
+.\"
+
+
+.TH "InspIRCd" "1" "June 2014" "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@" "InspIRCd Manual"
+
+.SH "NAME"
+\t\fBInspIRCd\fR - \fIthe\fR stable, high-performance and modular Internet Relay Chat Daemon
+.BR
+
+.SH "SYNOPSIS"
+\t\fBinspircd\fR [--config <file>] [--debug] [--nofork] [--nolog] [--runasroot] [--version]
+
+.SH "OPTIONS"
+.TP
+.B "--config <file>"
+.br
+Sets the path to the main configuration file. Defaults to \fI@CONFIG_DIR@/inspircd.conf\fR.
+.TP
+.B "--debug"
+.br
+Log verbosely to the standard output stream.
+.TP
+.B "--nofork"
+.br
+Don't fork into the background after starting up.
+.TP
+.B "--nolog"
+.br
+Don't write to log files.
+.TP
+.B "--runasroot"
+.br
+Allow the server to start as root (not recommended).
+.TP
+.B "--version"
+.br
+Displays the InspIRCd version and exits.
+
+.SH "EXIT STATUS"
+.TP
+.B "0 (EXIT_STATUS_NOERROR)"
+.br
+The server exited cleanly.
+.TP
+.B "1 (EXIT_STATUS_DIE)"
+.br
+The server exited because the DIE command was executed.
+.TP
+.B "2 (EXIT_STATUS_CONFIG)"
+.br
+The server exited because of a configuration file error.
+.TP
+.B "3 (EXIT_STATUS_LOG)"
+.br
+The server exited because of a log file error.
+.TP
+.B "4 (EXIT_STATUS_FORK)"
+.br
+The server exited because it was unable to fork into the background.
+.TP
+.B "5 (EXIT_STATUS_ARGV)"
+.br
+The server exited because an invalid argument was passed to it on the command line.
+.TP
+.B "6 (EXIT_STATUS_PID)"
+.br
+The server exited because it was unable to write to the PID file.
+.TP
+.B "7 (EXIT_STATUS_SOCKETENGINE)"
+.br
+The server exited because it was unable to initialize the @SOCKETENGINE@ socket engine.
+.TP
+.B "8 (EXIT_STATUS_ROOT)"
+.br
+The server exited because the user tried to start as root without \fI--runasroot\fR.
+.TP
+.B "9 (EXIT_STATUS_MODULE)"
+.br
+The server exited because it was unable to load a module on first run.
+.TP
+.B "10 (EXIT_STATUS_SIGTERM)"
+.br
+The server exited because it received SIGTERM.
+
+.SH "SUPPORT"
+IRC support for InspIRCd can be found at irc://irc.inspircd.org/inspircd.
+
+Bug reports and feature requests can be filed at https://github.com/inspircd/inspircd/issues.
--- /dev/null
+%platform linux
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2014 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd. InspIRCd 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, version 2.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+
+[Unit]
+After=network.target
+Description=Inspire Internet Relay Chat Daemon
+Requires=network.target
+
+[Service]
+ExecReload=@BASE_DIR@/inspircd rehash
+ExecStart=@BASE_DIR@/inspircd start
+ExecStop=@BASE_DIR@/inspircd stop
+PIDFile=@DATA_DIR@/inspircd.pid
+Restart=on-failure
+Type=forking
+
+[Install]
+WantedBy=multi-user.target
+%target BSD_MAKE BSDmakefile
+%target GNU_MAKE GNUmakefile
#
# InspIRCd -- Internet Relay Chat Daemon
#
#
-CC = @CC@
-SYSTEM = @SYSTEM@
-BUILDPATH = @BUILD_DIR@
+CXX = @CXX@
+COMPILER = @COMPILER_NAME@
+SYSTEM = @SYSTEM_NAME@
+BUILDPATH ?= $(PWD)/build
SOCKETENGINE = @SOCKETENGINE@
-CXXFLAGS = -pipe -fPIC -DPIC
-LDLIBS = -pthread -lstdc++
-LDFLAGS =
+CORECXXFLAGS = -fPIC -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
+LDLIBS = -lstdc++
CORELDFLAGS = -rdynamic -L. $(LDFLAGS)
PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
BASE = "$(DESTDIR)@BASE_DIR@"
BINPATH = "$(DESTDIR)@BINARY_DIR@"
INSTALL = install
INSTUID = @UID@
-INSTMODE_DIR = 0755
-INSTMODE_BIN = 0755
-INSTMODE_LIB = 0644
+INSTMODE_DIR = 0750
+INSTMODE_BIN = 0750
+INSTMODE_LIB = 0640
-@IFEQ $(CC) icpc
- CXXFLAGS += -Wshadow
-@ELSE
- CXXFLAGS += -pedantic -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute -Wall
+@IFNEQ $(COMPILER) ICC
+ CORECXXFLAGS += -pedantic -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute
+@ENDIF
+
+@IFNEQ $(SYSTEM)-$(COMPILER) darwin-GCC
+ CORECXXFLAGS += -fvisibility=hidden
@ENDIF
+@IFNEQ $(SYSTEM) darwin
+ LDLIBS += -pthread
+@ENDIF
@IFEQ $(SYSTEM) linux
LDLIBS += -ldl -lrt
LDLIBS += -lsocket -lnsl -lrt -lresolv
INSTALL = ginstall
@ENDIF
-@IFEQ $(SYSTEM) sunos
- LDLIBS += -lsocket -lnsl -lrt -lresolv
- INSTALL = ginstall
-@ENDIF
@IFEQ $(SYSTEM) darwin
- CXXFLAGS += -DDARWIN -frtti
LDLIBS += -ldl
CORELDFLAGS = -dynamic -bind_at_load -L. $(LDFLAGS)
PICLDFLAGS = -fPIC -shared -twolevel_namespace -undefined dynamic_lookup $(LDFLAGS)
@ENDIF
-@IFEQ $(SYSTEM) interix
- CXXFLAGS += -D_ALL_SOURCE -I/usr/local/include
-@ENDIF
@IFNDEF D
D=0
DBGOK=0
@IFEQ $(D) 0
- CXXFLAGS += -O2
-@IFEQ $(CC) g++
- CXXFLAGS += -g1
+ CORECXXFLAGS += -O2
+@IFEQ $(COMPILER) GCC
+ CORECXXFLAGS += -g1
@ENDIF
HEADER = std-header
DBGOK=1
@ENDIF
@IFEQ $(D) 1
- CXXFLAGS += -O0 -g3 -Werror
+ CORECXXFLAGS += -O0 -g3 -Werror
HEADER = debug-header
DBGOK=1
@ENDIF
@IFEQ $(D) 2
- CXXFLAGS += -O2 -g3
+ CORECXXFLAGS += -O2 -g3
HEADER = debug-header
DBGOK=1
@ENDIF
FOOTER = finishmessage
-CXXFLAGS += -Iinclude
+@TARGET GNU_MAKE MAKEFLAGS += --no-print-directory
-@GNU_ONLY MAKEFLAGS += --no-print-directory
-
-@GNU_ONLY SOURCEPATH = $(shell /bin/pwd)
-@BSD_ONLY SOURCEPATH != /bin/pwd
+@TARGET GNU_MAKE SOURCEPATH = $(shell /bin/pwd)
+@TARGET BSD_MAKE SOURCEPATH != /bin/pwd
@IFDEF V
- RUNCC = $(CC)
- RUNLD = $(CC)
+ RUNCC = $(CXX)
+ RUNLD = $(CXX)
VERBOSE = -v
@ELSE
- @GNU_ONLY MAKEFLAGS += --silent
- @BSD_ONLY MAKE += -s
- RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
- RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
+ @TARGET GNU_MAKE MAKEFLAGS += --silent
+ @TARGET BSD_MAKE MAKE += -s
+ RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
+ RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
@ENDIF
@IFDEF PURE_STATIC
- CXXFLAGS += -DPURE_STATIC
+ CORECXXFLAGS += -DPURE_STATIC
@ENDIF
-@DO_EXPORT RUNCC RUNLD CXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
-@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC SPLIT_CC
+# Add the users CXXFLAGS to the base ones to allow them to override
+# things like -Wfatal-errors if they wish to.
+CORECXXFLAGS += $(CXXFLAGS)
+
+@DO_EXPORT RUNCC RUNLD CORECXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
+@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC
# Default target
TARGET = all
@IFDEF M
HEADER = mod-header
FOOTER = mod-footer
- @BSD_ONLY TARGET = modules/${M:S/.so$//}.so
- @GNU_ONLY TARGET = modules/$(M:.so=).so
+ @TARGET BSD_MAKE TARGET = modules/${M:S/.so$//}.so
+ @TARGET GNU_MAKE TARGET = modules/$(M:.so=).so
@ENDIF
@IFDEF T
@IFNDEF PURE_STATIC
[ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
@ENDIF
- -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
- -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) inspircd $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
+@IFEQ $(SYSTEM) darwin
+ -$(INSTALL) -m $(INSTMODE_BIN) org.inspircd.plist $(BASE) 2>/dev/null
+@ENDIF
+@IFEQ $(SYSTEM) linux
+ -$(INSTALL) -m $(INSTMODE_LIB) inspircd.service $(BASE) 2>/dev/null
+@ENDIF
+ -$(INSTALL) -m $(INSTMODE_LIB) inspircd.1 $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_LIB) inspircd-genssl.1 $(BASE) 2>/dev/null
+ -$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
-$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
@echo 'Remember to create your config file:' $(CONPATH)/inspircd.conf
@echo 'Examples are available at:' $(CONPATH)/examples/
-@GNU_ONLY RCS_FILES = $(wildcard .git/index src/version.sh)
-@BSD_ONLY RCS_FILES = src/version.sh
-GNUmakefile BSDmakefile: make/template/main.mk configure $(RCS_FILES)
+GNUmakefile BSDmakefile: make/template/main.mk src/version.sh configure .config.cache
./configure -update
-@BSD_ONLY .MAKEFILEDEPS: BSDmakefile
+@TARGET BSD_MAKE .MAKEFILEDEPS: BSDmakefile
clean:
@echo Cleaning...
-rm -f $(BASE)/.gdbargs
-rm -f $(BASE)/org.inspircd.plist
-squeakyclean: distclean
-
configureclean:
rm -f .config.cache
rm -f BSDmakefile
rm -f GNUmakefile
- rm -f include/inspircd_config.h
- rm -f include/inspircd_version.h
+ rm -f include/config.h
rm -f inspircd
-rm -f org.inspircd.plist
@echo ' deinstall Removes the files created by "make install"'
@echo
-.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall squeakyclean configureclean help
+.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall configureclean help
+%platform darwin
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <time.h>
+
+int main() {
+ timespec time_spec;
+ clock_gettime(CLOCK_REALTIME, &time_spec);
+ return 0;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <sys/eventfd.h>
+
+int main() {
+ eventfd_t efd_data;
+ int fd;
+
+ fd = eventfd(0, EFD_NONBLOCK);
+ eventfd_read(fd, &efd_data);
+
+ return (fd < 0);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <sys/types.h>
+#include <sys/event.h>
+
+int main() {
+ int fd = kqueue();
+ return (fd < 0);
+}
sub do_static_find {
my @flags;
for my $file (@ARGV) {
- push @flags, getlinkerflags($file);
+ push @flags, get_property($file, 'LinkerFlags');
}
open F, '>', $out;
print F join ' ', @flags;
my $libs = '';
my $binary = $ENV{RUNCC};
if ($do_compile) {
- $flags = $ENV{CXXFLAGS};
- $flags =~ s/ -pedantic// if nopedantic($file);
- $flags .= ' ' . getcompilerflags($file);
+ $flags = $ENV{CORECXXFLAGS} . ' ' . get_property($file, 'CompileFlags');
- if ($file =~ m#(?:^|/)((?:m|cmd)_[^/. ]+)(?:\.cpp|/.*\.cpp)$#) {
- $flags .= ' -DMODNAME='.$1.'.so';
+ if ($file =~ m#(?:^|/)((?:m|core)_[^/. ]+)(?:\.cpp|/.*\.cpp)$#) {
+ $flags .= ' -DMODNAME=\\"'.$1.'\\"';
}
} else {
$binary = $ENV{RUNLD};
if ($do_link) {
$flags = join ' ', $flags, $ENV{PICLDFLAGS};
- $libs = join ' ', getlinkerflags($file);
+ $libs = get_property($file, 'LinkerFlags');
} else {
$flags .= ' -c';
}
#
-package make::utilities;
+BEGIN {
+ require 5.8.0;
+}
-require 5.8.0;
+package make::utilities;
use strict;
use warnings FATAL => qw(all);
use Exporter 'import';
-use POSIX;
-use Getopt::Long;
use Fcntl;
-our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
-
-# Parse the output of a *_config program,
-# such as pcre_config, take out the -L
-# directive and return an rpath for it.
+use File::Path;
+use File::Spec::Functions qw(rel2abs);
+use Getopt::Long;
+use POSIX;
-# \e[1;32msrc/Makefile\e[0m
+our @EXPORT = qw(get_version module_installed prompt_bool prompt_dir prompt_string get_cpu_count make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
my %already_added = ();
-my $if_skip_lines = 0;
+my %version = ();
+
+sub get_version {
+ return %version if %version;
+
+ # Attempt to retrieve version information from src/version.sh
+ chomp(my $vf = `sh src/version.sh 2>/dev/null`);
+ if ($vf =~ /^InspIRCd-([0-9]+)\.([0-9]+)\.([0-9]+)(?:\+(\w+))?$/) {
+ %version = ( MAJOR => $1, MINOR => $2, PATCH => $3, LABEL => $4 );
+ }
+
+ # Attempt to retrieve missing version information from Git
+ chomp(my $gr = `git describe --tags 2>/dev/null`);
+ if ($gr =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\d+-(\w+))?$/) {
+ $version{MAJOR} = $1 unless defined $version{MAJOR};
+ $version{MINOR} = $2 unless defined $version{MINOR};
+ $version{PATCH} = $3 unless defined $version{PATCH};
+ $version{LABEL} = $4 if defined $4;
+ }
+
+ # The user is using a stable release which does not have
+ # a label attached.
+ $version{LABEL} = 'release' unless defined $version{LABEL};
+
+ # If any of these fields are missing then the user has deleted the
+ # version file and is not running from Git. Fill in the fields with
+ # dummy data so we don't get into trouble with undef values later.
+ $version{MAJOR} = '0' unless defined $version{MAJOR};
+ $version{MINOR} = '0' unless defined $version{MINOR};
+ $version{PATCH} = '0' unless defined $version{PATCH};
+
+ return %version;
+}
+
+sub module_installed($) {
+ my $module = shift;
+ eval("use $module;");
+ return !$@;
+}
+
+sub prompt_bool($$$) {
+ my ($interactive, $question, $default) = @_;
+ my $answer = prompt_string($interactive, $question, $default ? 'y' : 'n');
+ return $answer =~ /y/i;
+}
+
+sub prompt_dir($$$) {
+ my ($interactive, $question, $default) = @_;
+ my ($answer, $create) = (undef, 'y');
+ do {
+ $answer = rel2abs(prompt_string($interactive, $question, $default));
+ $create = prompt_bool($interactive && !-d $answer, "$answer does not exist. Create it?", 'y');
+ my $mkpath = eval {
+ mkpath($answer, 0, 0750);
+ return 1;
+ };
+ unless (defined $mkpath) {
+ print "Error: unable to create $answer!\n\n";
+ $create = 0;
+ }
+ } while (!$create);
+ return $answer;
+}
+
+sub prompt_string($$$) {
+ my ($interactive, $question, $default) = @_;
+ return $default unless $interactive;
+ print $question, "\n";
+ print "[\e[1;32m$default\e[0m] => ";
+ chomp(my $answer = <STDIN>);
+ print "\n";
+ return $answer ? $answer : $default;
+}
+
+sub get_cpu_count {
+ my $count = 1;
+ if ($^O =~ /bsd/) {
+ $count = `sysctl -n hw.ncpu`;
+ } elsif ($^O eq 'darwin') {
+ $count = `sysctl -n hw.activecpu`;
+ } elsif ($^O eq 'linux') {
+ $count = `getconf _NPROCESSORS_ONLN`;
+ } elsif ($^O eq 'solaris') {
+ $count = `psrinfo -p`;
+ }
+ chomp($count);
+ return $count;
+}
sub promptstring($$$$$)
{
sub extend_pkg_path()
{
- return if defined $ENV{DISABLE_EXTEND_PKG_PATH};
if (!exists $ENV{PKG_CONFIG_PATH})
{
$ENV{PKG_CONFIG_PATH} = "/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig:/usr/X11R6/libdata/pkgconfig";
{
my ($packagename, $headername, $defaults, $module) = @_;
- my $key = "default_includedir_$packagename";
- if (exists $main::config{$key})
- {
- print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
- my $ret = $main::config{$key};
- print "\e[1;32m$ret\e[0m (cached)\n";
- return $ret;
- }
-
extend_pkg_path();
print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
{
my ($packagename, $libname, $defaults, $module) = @_;
- my $key = "default_libdir_$packagename";
- if (exists $main::config{$key})
- {
- print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
- my $ret = $main::config{$key};
- print "\e[1;32m$ret\e[0m (cached)\n";
- return $ret;
- }
-
extend_pkg_path();
print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
$module =~ /modules*\/(.+?)$/;
$module = $1;
- # This is only a cursory check, just designed to catch casual accidental use of backticks.
- # There are pleanty of ways around it, but its not supposed to be for security, just checking
- # that people are using the new configuration api as theyre supposed to and not just using
- # backticks instead of eval(), being as eval has accountability. People wanting to get around
- # the accountability will do so anyway.
- if (($line =~ /`/) && ($line !~ /eval\(.+?`.+?\)/))
- {
- die "Developers should no longer use backticks in configuration macros. Please use exec() and eval() macros instead. Offending line: $line (In module: $module)";
- }
-
- if ($line =~ /if(gt|lt)\("(.+?)","(.+?)"\)/) {
- chomp(my $result = `$2 2>/dev/null`);
- if (($1 eq 'gt' && $result le $3) || ($1 eq 'lt' && $result ge $3)) {
- $line = substr $line, 0, $-[0];
- } else {
- $line =~ s/if$1\("$2","$3"\)//;
- }
- }
-
if ($line =~ /ifuname\(\!"(\w+)"\)/)
{
my $uname = $1;
use strict;
use warnings FATAL => qw(all);
-use make::configure;
+use make::utilities;
BEGIN {
unless (module_installed("LWP::Simple")) {
unless (module_installed("Crypt::SSLeay") || module_installed("IO::Socket::SSL")) {
die "Your system is missing the Crypt::SSLeay or IO::Socket::SSL Perl modules!";
}
+
}
+use File::Basename;
use LWP::Simple;
-our @modlist;
-
my %installed;
# $installed{name} = $version
}
close SRC;
-getmodules(1);
-
# determine core version
`./src/version.sh` =~ /InspIRCd-([0-9.]+)/ or die "Cannot determine inspircd version";
$installed{core} = $1;
};
# set up core module list
-for my $modname (@modlist) {
- my $mod = "m_$modname";
- my $modfile = "src/modules/$mod.cpp";
+for my $modname (<src/modules/m_*.cpp>) {
+ my $mod = basename($modname, '.cpp');
my $ver = getmodversion($mod) || '0.0';
$ver =~ s/\$Rev: (.*) \$/$1/; # for storing revision in SVN
$installed{$mod} = $ver;
*/
-/* $Core */
-
#include "inspircd.h"
-#include "bancache.h"
-BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason)
+BanCacheHit::BanCacheHit(const std::string& type, const std::string& reason, time_t seconds)
+ : Type(type)
+ , Reason(reason)
+ , Expiry(ServerInstance->Time() + seconds)
{
- BanCacheHit *b;
-
- if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
- return NULL;
-
- b = new BanCacheHit(ip, type, reason);
-
- this->BanHash->insert(std::make_pair(ip, b));
- return b;
}
BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds)
{
- BanCacheHit *b;
-
- if (this->BanHash->find(ip) != this->BanHash->end()) // can't have two cache entries on the same IP, sorry..
+ BanCacheHit*& b = BanHash[ip];
+ if (b != NULL) // can't have two cache entries on the same IP, sorry..
return NULL;
- b = new BanCacheHit(ip, type, reason, seconds);
-
- this->BanHash->insert(std::make_pair(ip, b));
+ b = new BanCacheHit(type, reason, (seconds ? seconds : 86400));
return b;
}
BanCacheHit *BanCacheManager::GetHit(const std::string &ip)
{
- BanCacheHash::iterator i = this->BanHash->find(ip);
+ BanCacheHash::iterator i = this->BanHash.find(ip);
- if (i == this->BanHash->end())
+ if (i == this->BanHash.end())
return NULL; // free and safe
- else
- {
- if (ServerInstance->Time() > i->second->Expiry)
- {
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "Hit on " + ip + " is out of date, removing!");
- RemoveHit(i->second);
- return NULL; // out of date
- }
- return i->second; // hit.
- }
+ if (RemoveIfExpired(i))
+ return NULL; // expired
+
+ return i->second; // hit.
}
-bool BanCacheManager::RemoveHit(BanCacheHit *b)
+bool BanCacheManager::RemoveIfExpired(BanCacheHash::iterator& it)
{
- BanCacheHash::iterator i;
-
- if (!b)
- return false; // I don't think so.
-
- i = this->BanHash->find(b->IP);
-
- if (i == this->BanHash->end())
- {
- // err..
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveHit(): I got asked to remove a hit that wasn't in the hash(?)");
- }
- else
- {
- this->BanHash->erase(i);
- }
+ if (ServerInstance->Time() < it->second->Expiry)
+ return false;
- delete b;
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "Hit on " + it->first + " is out of date, removing!");
+ delete it->second;
+ it = BanHash.erase(it);
return true;
}
-unsigned int BanCacheManager::RemoveEntries(const std::string &type, bool positive)
+void BanCacheManager::RemoveEntries(const std::string& type, bool positive)
{
- int removed = 0;
-
- BanCacheHash::iterator safei;
-
if (positive)
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing positive hits for " + type);
else
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing negative hits for " + type);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing all negative hits");
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
+ for (BanCacheHash::iterator i = BanHash.begin(); i != BanHash.end(); )
{
- safei = n;
- safei++;
+ if (RemoveIfExpired(i))
+ continue; // updates the iterator if expired
- BanCacheHit *b = n->second;
+ BanCacheHit* b = i->second;
+ bool remove = false;
- /* Safe to delete items here through iterator 'n' */
- if (b->Type == type || !positive) // if removing negative hits, ignore type..
+ if (positive)
{
- if ((positive && !b->Reason.empty()) || b->Reason.empty())
- {
- /* we need to remove this one. */
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + b->IP);
- delete b;
- BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
- removed++;
- }
+ // when removing positive hits, remove only if the type matches
+ remove = b->IsPositive() && (b->Type == type);
+ }
+ else
+ {
+ // when removing negative hits, remove all of them
+ remove = !b->IsPositive();
}
- /* End of safe section */
- n = safei;
- }
-
-
- return removed;
-}
-
-void BanCacheManager::RehashCache()
-{
- BanCacheHash* NewHash = new BanCacheHash();
-
- BanCacheHash::iterator safei;
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); )
- {
- safei = n;
- safei++;
-
- /* Safe to delete items here through iterator 'n' */
- BanCacheHit *b = n->second;
-
- if (ServerInstance->Time() > b->Expiry)
+ if (remove)
{
/* we need to remove this one. */
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCacheManager::RemoveEntries(): Removing a hit on " + i->first);
delete b;
- BanHash->erase(n); // WORD TO THE WISE: don't use RemoveHit here, because we MUST remove the iterator in a safe way.
+ i = BanHash.erase(i);
}
else
- {
- /* Actually inserts a std::pair */
- NewHash->insert(*n);
- }
-
- /* End of safe section */
-
- n = safei;
+ ++i;
}
-
- delete BanHash;
- BanHash = NewHash;
}
BanCacheManager::~BanCacheManager()
{
- for (BanCacheHash::iterator n = BanHash->begin(); n != BanHash->end(); ++n)
+ for (BanCacheHash::iterator n = BanHash.begin(); n != BanHash.end(); ++n)
delete n->second;
- delete BanHash;
}
classbase::classbase()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::+ @%p", (void*)this);
+ if (ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::+ @%p", (void*)this);
}
CullResult classbase::cull()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::-%s @%p",
+ if (ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::-%s @%p",
typeid(*this).name(), (void*)this);
return CullResult();
}
classbase::~classbase()
{
- if (ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::~ @%p", (void*)this);
+ if (ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::~ @%p", (void*)this);
}
CullResult::CullResult()
refcountbase::~refcountbase()
{
- if (refcount && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "refcountbase::~ @%p with refcount %d",
+ if (refcount && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "refcountbase::~ @%p with refcount %d",
(void*)this, refcount);
}
usecountbase::~usecountbase()
{
- if (usecount && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "usecountbase::~ @%p with refcount %d",
+ if (usecount && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "usecountbase::~ @%p with refcount %d",
(void*)this, usecount);
}
}
}
-static struct DummyExtensionItem : LocalExtItem
-{
- DummyExtensionItem() : LocalExtItem("", NULL) {}
- void free(void*) {}
-} dummy;
-
Extensible::Extensible()
+ : culled(false)
{
- extensions[&dummy] = NULL;
}
CullResult Extensible::cull()
+{
+ FreeAllExtItems();
+ culled = true;
+ return classbase::cull();
+}
+
+void Extensible::FreeAllExtItems()
{
for(ExtensibleStore::iterator i = extensions.begin(); i != extensions.end(); ++i)
{
i->first->free(i->second);
}
extensions.clear();
- return classbase::cull();
}
Extensible::~Extensible()
{
- if (!extensions.empty() && ServerInstance && ServerInstance->Logs)
- ServerInstance->Logs->Log("CULLLIST", DEBUG,
- "Extensible destructor called without cull @%p", (void*)this);
+ if ((!extensions.empty() || !culled) && ServerInstance)
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Extensible destructor called without cull @%p", (void*)this);
}
LocalExtItem::LocalExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
: CoreException(message, who ? who->ModuleSourceFile : "A Module")
{
}
-
*/
-/* $Core */
-
#include "inspircd.h"
-#include <cstdarg>
-#include "mode.h"
+#include "listmode.h"
-Channel::Channel(const std::string &cname, time_t ts)
+namespace
{
- if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second)
- throw CoreException("Cannot create duplicate channel " + cname);
-
- this->name = cname;
- this->age = ts ? ts : ServerInstance->Time();
-
- maxbans = topicset = 0;
- modes.reset();
+ ChanModeReference ban(NULL, "ban");
+ ChanModeReference inviteonlymode(NULL, "inviteonly");
+ ChanModeReference keymode(NULL, "key");
+ ChanModeReference limitmode(NULL, "limit");
+ ChanModeReference secretmode(NULL, "secret");
+ ChanModeReference privatemode(NULL, "private");
+ UserModeReference invisiblemode(NULL, "invisible");
}
-void Channel::SetMode(char mode,bool mode_on)
+Channel::Channel(const std::string &cname, time_t ts)
+ : name(cname), age(ts), topicset(0)
{
- modes[mode-65] = mode_on;
+ if (!ServerInstance->chanlist.insert(std::make_pair(cname, this)).second)
+ throw CoreException("Cannot create duplicate channel " + cname);
}
void Channel::SetMode(ModeHandler* mh, bool on)
{
- modes[mh->GetModeChar() - 65] = on;
-}
-
-void Channel::SetModeParam(char mode, const std::string& parameter)
-{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- // always erase, even if changing, so that the map gets the new value
- if (n != custom_mode_params.end())
- custom_mode_params.erase(n);
- if (parameter.empty())
- {
- modes[mode-65] = false;
- }
- else
- {
- custom_mode_params[mode] = parameter;
- modes[mode-65] = true;
- }
-}
-
-void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
-{
- SetModeParam(mode->GetModeChar(), parameter);
-}
-
-std::string Channel::GetModeParameter(char mode)
-{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- if (n != custom_mode_params.end())
- return n->second;
- return "";
+ modes[mh->GetId()] = on;
}
-std::string Channel::GetModeParameter(ModeHandler* mode)
+void Channel::SetTopic(User* u, const std::string& ntopic)
{
- CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
- if (n != custom_mode_params.end())
- return n->second;
- return "";
-}
-
-int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
-{
- if (!u)
- u = ServerInstance->FakeClient;
- if (IS_LOCAL(u) && !forceset)
- {
- ModResult res;
- FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
-
- if (res == MOD_RES_DENY)
- return CMD_FAILURE;
- if (res != MOD_RES_ALLOW)
- {
- if (!this->HasUser(u))
- {
- u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
- {
- u->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
-
this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
-
this->topicset = ServerInstance->Time();
- FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
-
- return CMD_SUCCESS;
-}
-
-long Channel::GetUserCounter()
-{
- return userlist.size();
+ FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
}
Membership* Channel::AddUser(User* user)
{
- Membership* memb = new Membership(user, this);
- userlist[user] = memb;
+ std::pair<MemberMap::iterator, bool> ret = userlist.insert(std::make_pair(user, insp::aligned_storage<Membership>()));
+ if (!ret.second)
+ return NULL;
+
+ Membership* memb = new(ret.first->second) Membership(user, this);
return memb;
}
void Channel::DelUser(User* user)
{
- UserMembIter a = userlist.find(user);
+ MemberMap::iterator it = userlist.find(user);
+ if (it != userlist.end())
+ DelUser(it);
+}
- if (a != userlist.end())
- {
- a->second->cull();
- delete a->second;
- userlist.erase(a);
- }
+void Channel::CheckDestroy()
+{
+ if (!userlist.empty())
+ return;
- if (userlist.empty())
- {
- ModResult res;
- FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
- if (res == MOD_RES_DENY)
- return;
- chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
- /* kill the record */
- if (iter != ServerInstance->chanlist->end())
- {
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
- ServerInstance->chanlist->erase(iter);
- }
+ ModResult res;
+ FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+ if (res == MOD_RES_DENY)
+ return;
- ClearInvites();
- ServerInstance->GlobalCulls.AddItem(this);
+ chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
+ /* kill the record */
+ if (iter != ServerInstance->chanlist.end())
+ {
+ FOREACH_MOD(OnChannelDelete, (this));
+ ServerInstance->chanlist.erase(iter);
}
+
+ ClearInvites();
+ ServerInstance->GlobalCulls.AddItem(this);
}
-bool Channel::HasUser(User* user)
+void Channel::DelUser(const MemberMap::iterator& membiter)
{
- return (userlist.find(user) != userlist.end());
+ Membership* memb = membiter->second;
+ memb->cull();
+ memb->~Membership();
+ userlist.erase(membiter);
+
+ // If this channel became empty then it should be removed
+ CheckDestroy();
}
Membership* Channel::GetUser(User* user)
{
- UserMembIter i = userlist.find(user);
+ MemberMap::iterator i = userlist.find(user);
if (i == userlist.end())
return NULL;
return i->second;
}
-const UserMembList* Channel::GetUsers()
-{
- return &userlist;
-}
-
void Channel::SetDefaultModes()
{
- ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
ServerInstance->Config->DefaultModes.c_str());
irc::spacesepstream list(ServerInstance->Config->DefaultModes);
std::string modeseq;
ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
if (mode)
{
+ if (mode->IsPrefixMode())
+ continue;
+
if (mode->GetNumParams(true))
{
list.GetToken(parameter);
* add a channel to a user, creating the record for it if needed and linking
* it to the user record
*/
-Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
+Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key)
{
- // Fix: unregistered users could be joined using /SAJOIN
- if (!user || !cn || user->registered != REG_ALL)
+ if (user->registered != REG_ALL)
+ {
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname);
return NULL;
-
- std::string privs;
- Channel *Ptr;
+ }
/*
* We don't restrict the number of channels that remote users or users that are override-joining may be in.
- * We restrict local users to MaxChans channels.
- * We restrict local operators to OperMaxChans channels.
+ * We restrict local users to <connect:maxchans> channels.
+ * We restrict local operators to <oper:maxchans> channels.
* This is a lot more logical than how it was formerly. -- w00t
*/
- if (IS_LOCAL(user) && !override)
+ if (!override)
{
- if (user->HasPrivPermission("channels/high-join-limit"))
+ unsigned int maxchans = user->GetClass()->maxchans;
+ if (user->IsOper())
{
- if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
- {
- user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
- return NULL;
- }
+ unsigned int opermaxchans = ConvToInt(user->oper->getConfig("maxchans"));
+ // If not set, use 2.0's <channels:opers>, if that's not set either, use limit from CC
+ if (!opermaxchans)
+ opermaxchans = ServerInstance->Config->OperMaxChans;
+ if (opermaxchans)
+ maxchans = opermaxchans;
}
- else
+ if (user->chans.size() >= maxchans)
{
- unsigned int maxchans = user->GetClass()->maxchans;
- if (!maxchans)
- maxchans = ServerInstance->Config->MaxChans;
- if (user->chans.size() >= maxchans)
- {
- user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
- return NULL;
- }
+ user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s :You are on too many channels", cname.c_str());
+ return NULL;
}
}
- std::string cname;
- cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
- Ptr = ServerInstance->FindChan(cname);
- bool created_by_local = false;
+ // Crop channel name if it's too long
+ if (cname.length() > ServerInstance->Config->Limits.ChanMax)
+ cname.resize(ServerInstance->Config->Limits.ChanMax);
- if (!Ptr)
+ Channel* chan = ServerInstance->FindChan(cname);
+ bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
+ std::string privs; // Prefix mode(letter)s to give to the joining user
+
+ if (!chan)
{
- /*
- * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
- */
- if (!IS_LOCAL(user))
- {
- if (!TS)
- ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
- }
- else
- {
- privs = "o";
- created_by_local = true;
- }
+ privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
- if (IS_LOCAL(user) && override == false)
+ if (override == false)
{
+ // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
+ FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
if (MOD_RESULT == MOD_RES_DENY)
- return NULL;
+ return NULL; // A module wasn't happy with the join, abort
}
- Ptr = new Channel(cname, TS);
+ chan = new Channel(cname, ServerInstance->Time());
+ // Set the default modes on the channel (<options:defaultmodes>)
+ chan->SetDefaultModes();
}
else
{
/* Already on the channel */
- if (Ptr->HasUser(user))
+ if (chan->HasUser(user))
return NULL;
- /*
- * remote users are allowed us to bypass channel modes
- * and bans (used by servers)
- */
- if (IS_LOCAL(user) && override == false)
+ if (override == false)
{
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
+ FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
+
+ // A module explicitly denied the join and (hopefully) generated a message
+ // describing the situation, so we may stop here without sending anything
if (MOD_RESULT == MOD_RES_DENY)
- {
return NULL;
- }
- else if (MOD_RESULT == MOD_RES_PASSTHRU)
+
+ // If no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
+ // most of the time) then proceed to check channel modes +k, +i, +l and bans,
+ // in this order.
+ // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
+ // then this entire section is skipped
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
{
- std::string ckey = Ptr->GetModeParameter('k');
- bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
+ std::string ckey = chan->GetModeParameter(keymode);
+ bool invited = user->IsInvited(chan);
bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
if (!ckey.empty())
{
- FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
- if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
+ FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, key));
+ if (!MOD_RESULT.check((ckey == key) || can_bypass))
{
// If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
- user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
+ user->WriteNumeric(ERR_BADCHANNELKEY, "%s :Cannot join channel (Incorrect channel key)", chan->name.c_str());
return NULL;
}
}
- if (Ptr->IsModeSet('i'))
+ if (chan->IsModeSet(inviteonlymode))
{
- FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
+ FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
if (!MOD_RESULT.check(invited))
{
- user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
+ user->WriteNumeric(ERR_INVITEONLYCHAN, "%s :Cannot join channel (Invite only)", chan->name.c_str());
return NULL;
}
}
- std::string limit = Ptr->GetModeParameter('l');
+ std::string limit = chan->GetModeParameter(limitmode);
if (!limit.empty())
{
- FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
- if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
+ FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
+ if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
{
- user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
+ user->WriteNumeric(ERR_CHANNELISFULL, "%s :Cannot join channel (Channel is full)", chan->name.c_str());
return NULL;
}
}
- if (Ptr->IsBanned(user) && !can_bypass)
+ if (chan->IsBanned(user) && !can_bypass)
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You're banned)", chan->name.c_str());
return NULL;
}
*/
if (invited)
{
- IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
+ user->RemoveInvite(chan);
}
}
}
}
- if (created_by_local)
- {
- /* As spotted by jilles, dont bother to set this on remote users */
- Ptr->SetDefaultModes();
- }
-
- return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
+ // We figured that this join is allowed and also created the
+ // channel if it didn't exist before, now do the actual join
+ chan->ForceJoin(user, &privs, false, created_by_local);
+ return chan;
}
-Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
+Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
{
- std::string nick = user->nick;
+ if (IS_SERVER(user))
+ {
+ ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
+ return NULL;
+ }
- Membership* memb = Ptr->AddUser(user);
- user->chans.insert(Ptr);
+ Membership* memb = this->AddUser(user);
+ if (!memb)
+ return NULL; // Already on the channel
- for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
+ user->chans.push_front(memb);
+
+ if (privs)
{
- const char status = *x;
- ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
- if (mh)
+ // If the user was granted prefix modes (in the OnUserPreJoin hook, or he's a
+ // remote user and his own server set the modes), then set them internally now
+ for (std::string::const_iterator i = privs->begin(); i != privs->end(); ++i)
{
- /* Set, and make sure that the mode handler knows this mode was now set */
- Ptr->SetPrefix(user, mh->GetModeChar(), true);
- mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh)
+ {
+ std::string nick = user->nick;
+ // Set the mode on the user
+ mh->OnModeChange(ServerInstance->FakeClient, NULL, this, nick, true);
+ }
}
}
+ // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
CUList except_list;
- FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
+ FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
- Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
+ this->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", this->name.c_str());
/* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
- if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
+ if ((GetUserCounter() > 1) && (!memb->modes.empty()))
{
std::string ms = memb->modes;
for(unsigned int i=0; i < memb->modes.length(); i++)
ms.append(" ").append(user->nick);
except_list.insert(user);
- Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
+ this->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", this->name.c_str(), ms.c_str());
}
if (IS_LOCAL(user))
{
- if (Ptr->topicset)
+ if (this->topicset)
{
- user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
- user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
+ user->WriteNumeric(RPL_TOPIC, "%s :%s", this->name.c_str(), this->topic.c_str());
+ user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", this->name.c_str(), this->setby.c_str(), (unsigned long)this->topicset);
}
- Ptr->UserList(user);
+ this->UserList(user);
}
- FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
- return Ptr;
+
+ FOREACH_MOD(OnPostJoin, (memb));
+ return memb;
}
bool Channel::IsBanned(User* user)
if (result != MOD_RES_PASSTHRU)
return (result == MOD_RES_DENY);
- for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ const ListModeBase::ModeList* bans = banlm->GetList(this);
+ if (bans)
{
- if (CheckBan(user, i->data))
- return true;
+ for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); it++)
+ {
+ if (CheckBan(user, it->mask))
+ return true;
+ }
}
return false;
}
if (at == std::string::npos)
return false;
- char tomatch[MAXBUF];
- snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
+ const std::string nickIdent = user->nick + "!" + user->ident;
std::string prefix = mask.substr(0, at);
- if (InspIRCd::Match(tomatch, prefix, NULL))
+ if (InspIRCd::Match(nickIdent, prefix, NULL))
{
std::string suffix = mask.substr(at + 1);
if (InspIRCd::Match(user->host, suffix, NULL) ||
FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
if (rv != MOD_RES_PASSTHRU)
return rv;
- for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
+
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ const ListModeBase::ModeList* bans = banlm->GetList(this);
+ if (bans)
{
- if (i->data[0] == type && i->data[1] == ':')
+ for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); ++it)
{
- std::string val = i->data.substr(2);
- if (CheckBan(user, val))
+ if (CheckBan(user, it->mask))
return MOD_RES_DENY;
}
}
}
/* Channel::PartUser
- * remove a channel from a users record, and return the number of users left.
- * Therefore, if this function returns 0 the caller should delete the Channel.
+ * Remove a channel from a users record, remove the reference to the Membership object
+ * from the channel and destroy it.
*/
void Channel::PartUser(User *user, std::string &reason)
{
- if (!user)
- return;
+ MemberMap::iterator membiter = userlist.find(user);
- Membership* memb = GetUser(user);
-
- if (memb)
+ if (membiter != userlist.end())
{
+ Membership* memb = membiter->second;
CUList except_list;
- FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
+ FOREACH_MOD(OnUserPart, (memb, reason, except_list));
WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
- user->chans.erase(this);
- this->RemoveAllPrefixes(user);
+ // Remove this channel from the user's chanlist
+ user->chans.erase(memb);
+ // Remove the Membership from this channel's userlist and destroy it
+ this->DelUser(membiter);
}
-
- this->DelUser(user);
}
-void Channel::KickUser(User *src, User *user, const char* reason)
+void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason)
{
- if (!src || !user || !reason)
- return;
-
- Membership* memb = GetUser(user);
- if (IS_LOCAL(src))
- {
- if (!memb)
- {
- src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
- return;
- }
- if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
- return;
- }
-
- ModResult res;
- if (ServerInstance->ULine(src->server))
- res = MOD_RES_ALLOW;
- else
- FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
-
- if (res == MOD_RES_DENY)
- return;
-
- if (res == MOD_RES_PASSTHRU)
- {
- unsigned int them = this->GetPrefixValue(src);
- unsigned int req = HALFOP_VALUE;
- for (std::string::size_type i = 0; i < memb->modes.length(); i++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
- if (mh && mh->GetLevelRequired() > req)
- req = mh->GetLevelRequired();
- }
-
- if (them < req)
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
- src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
- return;
- }
- }
- }
-
- if (memb)
- {
- CUList except_list;
- FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
-
- WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
+ Membership* memb = victimiter->second;
+ CUList except_list;
+ FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
- user->chans.erase(this);
- this->RemoveAllPrefixes(user);
- }
+ User* victim = memb->user;
+ WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
- this->DelUser(user);
+ victim->chans.erase(memb);
+ this->DelUser(victimiter);
}
void Channel::WriteChannel(User* user, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!user || !text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteChannel(user, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteChannel(user, textbuffer);
}
void Channel::WriteChannel(User* user, const std::string &text)
{
- char tb[MAXBUF];
-
- if (!user)
- return;
-
- snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
- std::string out = tb;
+ const std::string message = ":" + user->GetFullHost() + " " + text;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first))
- i->first->Write(out);
+ i->first->Write(message);
}
}
void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteChannelWithServ(ServName, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteChannelWithServ(ServName, textbuffer);
}
void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
{
- char tb[MAXBUF];
+ const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
- snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
- std::string out = tb;
-
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first))
- i->first->Write(out);
+ i->first->Write(message);
}
}
* for the sender (for privmsg etc) */
void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!text)
- return;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteAllExceptSender(user, serversource, status, textbuffer);
}
void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (!text)
- return;
-
- int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
- va_end(argsPtr);
-
- this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ textbuffer = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + textbuffer;
+ this->RawWriteAllExcept(user, serversource, status, except_list, textbuffer);
}
void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
{
- char tb[MAXBUF];
-
- snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
-
- this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
+ const std::string message = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + text;
+ this->RawWriteAllExcept(user, serversource, status, except_list, message);
}
void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
unsigned int minrank = 0;
if (status)
{
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
if (mh)
minrank = mh->GetPrefixRank();
}
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
{
this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
}
-/*
- * return a count of the users on a specific channel accounting for
- * invisible users who won't increase the count. e.g. for /LIST
- */
-int Channel::CountInvisible()
-{
- int count = 0;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
- {
- if (!i->first->quitting && !i->first->IsModeSet('i'))
- count++;
- }
-
- return count;
-}
-
-char* Channel::ChanModes(bool showkey)
+const char* Channel::ChanModes(bool showkey)
{
- static char scratch[MAXBUF];
- static char sparam[MAXBUF];
- char* offset = scratch;
- std::string extparam;
+ static std::string scratch;
+ std::string sparam;
- *scratch = '\0';
- *sparam = '\0';
+ scratch.clear();
/* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
for(int n = 0; n < 64; n++)
{
- if(this->modes[n])
+ ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_CHANNEL);
+ if (mh && IsModeSet(mh))
{
- *offset++ = n + 65;
- extparam.clear();
+ scratch.push_back(n + 65);
+
+ ParamModeBase* pm = mh->IsParameterMode();
+ if (!pm)
+ continue;
+
if (n == 'k' - 65 && !showkey)
{
- extparam = "<key>";
+ sparam += " <key>";
}
else
{
- extparam = this->GetModeParameter(n + 65);
- }
- if (!extparam.empty())
- {
- charlcat(sparam,' ',MAXBUF);
- strlcat(sparam,extparam.c_str(),MAXBUF);
+ sparam += ' ';
+ pm->GetParameter(this, sparam);
}
}
}
- /* Null terminate scratch */
- *offset = '\0';
- strlcat(scratch,sparam,MAXBUF);
- return scratch;
+ scratch += sparam;
+ return scratch.c_str();
}
/* compile a userlist of a channel into a string, each nick seperated by
* spaces and op, voice etc status shown as @ and +, and send it to 'user'
*/
-void Channel::UserList(User *user)
+void Channel::UserList(User* user, bool has_user)
{
- if (!IS_LOCAL(user))
- return;
-
bool has_privs = user->HasPrivPermission("channels/auspex");
-
- if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
- return;
- }
-
- std::string list = user->nick;
- list.push_back(' ');
- list.push_back(this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
+ std::string list;
+ list.push_back(this->IsModeSet(secretmode) ? '@' : this->IsModeSet(privatemode) ? '*' : '=');
list.push_back(' ');
list.append(this->name).append(" :");
std::string::size_type pos = list.size();
- bool has_one = false;
-
- /* Improvement by Brain - this doesnt change in value, so why was it inside
- * the loop?
- */
- bool has_user = this->HasUser(user);
-
- const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
+ const size_t maxlen = ServerInstance->Config->Limits.MaxLine - 10 - ServerInstance->Config->ServerName.size();
std::string prefixlist;
std::string nick;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
+ for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); ++i)
{
- if (i->first->quitting)
- continue;
- if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
+ if ((!has_user) && (i->first->IsModeSet(invisiblemode)) && (!has_privs))
{
/*
* user is +i, and source not on the channel, does not show
continue;
}
- prefixlist = this->GetPrefixChar(i->first);
+ Membership* memb = i->second;
+
+ prefixlist.clear();
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ prefixlist.push_back(prefix);
nick = i->first->nick;
- FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
+ ModResult res;
+ FIRST_MOD_RESULT(OnNamesListItem, res, (user, memb, prefixlist, nick));
- /* Nick was nuked, a module wants us to skip it */
- if (nick.empty())
+ // See if a module wants us to exclude this user from NAMES
+ if (res == MOD_RES_DENY)
continue;
if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen)
// Erase all nicks, keep the constant part
list.erase(pos);
- has_one = false;
}
list.append(prefixlist).append(nick).push_back(' ');
-
- has_one = true;
}
- /* if whats left in the list isnt empty, send it */
- if (has_one)
- {
+ // Only send the user list numeric if there is at least one user in it
+ if (list.size() != pos)
user->WriteNumeric(RPL_NAMREPLY, list);
- }
- user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
-}
-
-long Channel::GetMaxBans()
-{
- /* Return the cached value if there is one */
- if (this->maxbans)
- return this->maxbans;
-
- /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
- for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
- {
- if (InspIRCd::Match(this->name, n->first, NULL))
- {
- this->maxbans = n->second;
- return n->second;
- }
- }
-
- /* Screw it, just return the default of 64 */
- this->maxbans = 64;
- return this->maxbans;
-}
-
-void Channel::ResetMaxBans()
-{
- this->maxbans = 0;
+ user->WriteNumeric(RPL_ENDOFNAMES, "%s :End of /NAMES list.", this->name.c_str());
}
/* returns the status character for a given user on a channel, e.g. @ for op,
* % for halfop etc. If the user has several modes set, the highest mode
* the user has must be returned.
*/
-const char* Channel::GetPrefixChar(User *user)
+char Membership::GetPrefixChar() const
{
- static char pf[2] = {0, 0};
- *pf = 0;
+ char pf = 0;
unsigned int bestrank = 0;
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
+ for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
- for(unsigned int i=0; i < m->second->modes.length(); i++)
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
{
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
- if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
- {
- bestrank = mh->GetPrefixRank();
- pf[0] = mh->GetPrefix();
- }
+ bestrank = mh->GetPrefixRank();
+ pf = mh->GetPrefix();
}
}
return pf;
unsigned int rv = 0;
if (mchar)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
if (mh)
rv = mh->GetPrefixRank();
}
return rv;
}
-const char* Channel::GetAllPrefixChars(User* user)
+const char* Membership::GetAllPrefixChars() const
{
static char prefix[64];
int ctr = 0;
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
+ for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
{
- for(unsigned int i=0; i < m->second->modes.length(); i++)
- {
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
- if (mh && mh->GetPrefix())
- prefix[ctr++] = mh->GetPrefix();
- }
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
+ if (mh && mh->GetPrefix())
+ prefix[ctr++] = mh->GetPrefix();
}
prefix[ctr] = 0;
unsigned int Channel::GetPrefixValue(User* user)
{
- UserMembIter m = userlist.find(user);
+ MemberMap::iterator m = userlist.find(user);
if (m == userlist.end())
return 0;
return m->second->getRank();
}
-bool Channel::SetPrefix(User* user, char prefix, bool adding)
+bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
{
- ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
- if (!delta_mh)
- return false;
- UserMembIter m = userlist.find(user);
- if (m == userlist.end())
- return false;
- for(unsigned int i=0; i < m->second->modes.length(); i++)
+ char prefix = delta_mh->GetModeChar();
+ for (unsigned int i = 0; i < modes.length(); i++)
{
- char mchar = m->second->modes[i];
- ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
+ char mchar = modes[i];
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
{
- m->second->modes =
- m->second->modes.substr(0,i) +
+ modes = modes.substr(0,i) +
(adding ? std::string(1, prefix) : "") +
- m->second->modes.substr(mchar == prefix ? i+1 : i);
+ modes.substr(mchar == prefix ? i+1 : i);
return adding != (mchar == prefix);
}
}
if (adding)
- m->second->modes += std::string(1, prefix);
+ modes.push_back(prefix);
return adding;
}
-void Channel::RemoveAllPrefixes(User* user)
-{
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
- {
- m->second->modes.clear();
- }
-}
-
void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
{
if ((timeout != 0) && (ServerInstance->Time() >= timeout))
// Expired, don't bother
return;
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
Invitation* inv = Invitation::Find(c, u, false);
if (inv)
if ((inv->expiry == 0) || (inv->expiry > timeout))
return;
inv->expiry = timeout;
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
}
else
{
inv = new Invitation(c, u, timeout);
- c->invites.push_back(inv);
- u->invites.push_back(inv);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
+ c->invites.push_front(inv);
+ u->invites.push_front(inv);
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
}
}
Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
{
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
- if (!u || u->invites.empty())
- return NULL;
-
- InviteList locallist;
- locallist.swap(u->invites);
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
Invitation* result = NULL;
- for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
+ for (InviteList::iterator i = u->invites.begin(); i != u->invites.end(); )
{
Invitation* inv = *i;
+ ++i;
+
if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
{
/* Expired invite, remove it. */
- std::string expiration = ServerInstance->TimeString(inv->expiry);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
- i = locallist.erase(i);
- inv->cull();
+ std::string expiration = InspIRCd::TimeString(inv->expiry);
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
delete inv;
}
else
result = inv;
break;
}
- ++i;
}
}
- locallist.swap(u->invites);
- ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
+ ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find result=%p", (void*) result);
return result;
}
Invitation::~Invitation()
{
// Remove this entry from both lists
- InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
- if (it != chan->invites.end())
- chan->invites.erase(it);
- it = std::find(user->invites.begin(), user->invites.end(), this);
- if (it != user->invites.end())
- user->invites.erase(it);
-}
-
-void InviteBase::ClearInvites()
-{
- ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
- InviteList locallist;
- locallist.swap(invites);
- for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
- {
- (*i)->cull();
- delete *i;
- }
+ chan->invites.erase(this);
+ user->invites.erase(this);
+ ServerInstance->Logs->Log("INVITEBASE", LOG_DEBUG, "Invitation::~ %p", (void*) this);
}
*/
-/* $Core */
-
#include "inspircd.h"
/* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
return mask == mask2;
}
-
-
#include "inspircd.h"
-int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
{
ModResult res;
FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
/* Module matched */
if (res == MOD_RES_ALLOW)
- return 0;
+ return true;
/* Module explicitly didnt match */
if (res == MOD_RES_DENY)
- return 1;
+ return false;
/* We dont handle any hash types except for plaintext - Thanks tra26 */
if (!hashtype.empty() && hashtype != "plaintext")
- /* See below. 1 because they dont match */
- return 1;
+ return false;
- return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
+ return TimingSafeCompare(data, input);
}
-/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
- * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
- * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
- * the channel names and their keys as follows:
- * JOIN #chan1,#chan2,#chan3 key1,,key3
- * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
- * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
- * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
- * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
- */
-int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
+bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
{
if (splithere >= parameters.size())
- return 0;
+ return false;
- if (extra >= (signed)parameters.size())
- extra = -1;
-
- /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+ /* First check if we have more than one item in the list, if we don't we return false here and the handler
* which called us just carries on as it was.
*/
if (parameters[splithere].find(',') == std::string::npos)
- return 0;
+ return false;
/** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
* By using std::set (thanks for the idea w00t) we can cut this down a ton.
* ...VOOODOOOO!
+ *
+ * Only check for duplicates if there is one list (allow them in JOIN).
*/
std::set<irc::string> dupes;
+ bool check_dupes = (extra < 0);
- /* Create two lists, one for channel names, one for keys
+ /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
+ * an empty string. The second parameter of the constructor of the sepstream tells whether
+ * or not to allow empty tokens.
+ * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
*/
irc::commasepstream items1(parameters[splithere]);
- irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
- std::string extrastuff;
+ irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
std::string item;
unsigned int max = 0;
+ LocalUser* localuser = IS_LOCAL(user);
- /* Attempt to iterate these lists and call the command objech
- * which called us, for every parameter pair until there are
- * no more left to parse.
+ /* Attempt to iterate these lists and call the command handler
+ * for every parameter or parameter pair until there are no more
+ * left to parse.
*/
while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
{
- if (dupes.find(item.c_str()) == dupes.end())
+ if ((!check_dupes) || (dupes.insert(item.c_str()).second))
{
std::vector<std::string> new_parameters(parameters);
-
- if (!items2.GetToken(extrastuff))
- extrastuff.clear();
-
new_parameters[splithere] = item;
- if (extra >= 0)
- new_parameters[extra] = extrastuff;
-
- CommandObj->Handle(new_parameters, user);
-
- dupes.insert(item.c_str());
- }
- }
- return 1;
-}
-
-bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
-{
- Commandtable::iterator n = cmdlist.find(commandname);
- if (n != cmdlist.end())
- {
- if ((pcnt >= n->second->min_params))
- {
- if (IS_LOCAL(user) && n->second->flags_needed)
+ if (extra >= 0)
{
- if (user->IsModeSet(n->second->flags_needed))
- {
- return (user->HasPermission(commandname));
- }
+ // If we have two lists then get the next item from the second list.
+ // In case it runs out of elements then 'item' will be an empty string.
+ items2.GetToken(item);
+ new_parameters[extra] = item;
}
- else
+
+ CmdResult result = handler->Handle(new_parameters, user);
+ if (localuser)
{
- return true;
+ // Run the OnPostCommand hook with the last parameter (original line) being empty
+ // to indicate that the command had more targets in its original form.
+ item.clear();
+ FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, item));
}
}
}
- return false;
+
+ return true;
}
Command* CommandParser::GetHandler(const std::string &commandname)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
return n->second;
// calls a handler function for a command
-CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
+CmdResult CommandParser::CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
if (bOkay)
{
+ if (cmd)
+ *cmd = n->second;
return n->second->Handle(parameters,user);
}
}
return CMD_INVALID;
}
-bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
+void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
{
std::vector<std::string> command_p;
irc::tokenstream tokens(cmd);
if (command[0] == ':')
tokens.GetToken(command);
- while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
+ while (tokens.GetToken(token))
command_p.push_back(token);
std::transform(command.begin(), command.end(), command.begin(), ::toupper);
/* find the command, check it exists */
- Commandtable::iterator cm = cmdlist.find(command);
+ Command* handler = GetHandler(command);
/* Modify the user's penalty regardless of whether or not the command exists */
- bool do_more = true;
if (!user->HasPrivPermission("users/flood/no-throttle"))
{
// If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
- user->CommandFloodPenalty += cm != cmdlist.end() ? cm->second->Penalty * 1000 : 2000;
+ user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000;
}
-
- if (cm == cmdlist.end())
+ if (!handler)
{
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
if (MOD_RESULT == MOD_RES_DENY)
- return true;
+ return;
/*
* This double lookup is in case a module (abbreviation) wishes to change a command.
* Thanks dz for making me actually understand why this is necessary!
* -- w00t
*/
- cm = cmdlist.find(command);
- if (cm == cmdlist.end())
+ handler = GetHandler(command);
+ if (!handler)
{
if (user->registered == REG_ALL)
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
- ServerInstance->stats->statsUnknown++;
- return true;
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str());
+ ServerInstance->stats.Unknown++;
+ return;
}
}
- if (cm->second->max_params && command_p.size() > cm->second->max_params)
+ // If we were given more parameters than max_params then append the excess parameter(s)
+ // to command_p[maxparams-1], i.e. to the last param that is still allowed
+ if (handler->max_params && command_p.size() > handler->max_params)
{
/*
* command_p input (assuming max_params 1):
* a
* test
*/
- std::string lparam;
- /*
- * The '-1' here is a clever trick, we'll go backwards throwing everything into a temporary param
- * and then just toss that into the array.
- * -- w00t
- */
- while (command_p.size() > (cm->second->max_params - 1))
- {
- // BE CAREFUL: .end() returns past the end of the vector, hence decrement.
- std::vector<std::string>::iterator it = command_p.end() - 1;
+ // Iterator to the last parameter that will be kept
+ const std::vector<std::string>::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
+ // Iterator to the first excess parameter
+ const std::vector<std::string>::iterator firstexcess = lastkeep + 1;
- lparam.insert(0, " " + *(it));
- command_p.erase(it); // remove last element
+ // Append all excess parameter(s) to the last parameter, seperated by spaces
+ for (std::vector<std::string>::const_iterator i = firstexcess; i != command_p.end(); ++i)
+ {
+ lastkeep->push_back(' ');
+ lastkeep->append(*i);
}
- /* we now have (each iteration):
- * ' test'
- * ' a test'
- * ' is a test' <-- final string
- * ...now remove the ' ' at the start...
- */
- lparam.erase(lparam.begin());
-
- /* param is now 'is a test', which is exactly what we wanted! */
- command_p.push_back(lparam);
+ // Erase the excess parameter(s)
+ command_p.erase(firstexcess, command_p.end());
}
/*
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
if (MOD_RESULT == MOD_RES_DENY)
- return true;
+ return;
/* activity resets the ping pending timer */
user->nping = ServerInstance->Time() + user->MyClass->GetPingTime();
- if (cm->second->flags_needed)
+ if (handler->flags_needed)
{
- if (!user->IsModeSet(cm->second->flags_needed))
+ if (!user->IsModeSet(handler->flags_needed))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
+ return;
}
+
if (!user->HasPermission(command))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
- user->nick.c_str(), user->oper->NameStr(), command.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s",
+ user->oper->name.c_str(), command.c_str());
+ return;
}
}
- if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
+
+ if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
{
/* command is disabled! */
if (ServerInstance->Config->DisabledDontExist)
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str());
}
else
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
- user->nick.c_str(), command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
}
ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return do_more;
+ return;
}
- if ((!command_p.empty()) && (command_p.back().empty()) && (!cm->second->allow_empty_last_param))
+ if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
command_p.pop_back();
- if (command_p.size() < cm->second->min_params)
+ if (command_p.size() < handler->min_params)
{
- user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
- if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
- user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), cm->second->name.c_str(), cm->second->syntax.c_str());
- return do_more;
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str());
+ if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
+ user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
+ return;
}
- if ((user->registered != REG_ALL) && (!cm->second->WorksBeforeReg()))
+
+ if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
{
user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str());
- return do_more;
}
else
{
/* passed all checks.. first, do the (ugly) stats counters. */
- cm->second->use_count++;
- cm->second->total_bytes += cmd.length();
+ handler->use_count++;
/* module calls too */
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd));
if (MOD_RESULT == MOD_RES_DENY)
- return do_more;
+ return;
/*
* WARNING: be careful, the user may be deleted soon
*/
- CmdResult result = cm->second->Handle(command_p, user);
+ CmdResult result = handler->Handle(command_p, user);
- FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
- return do_more;
+ FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd));
}
}
void CommandParser::RemoveCommand(Command* x)
{
- Commandtable::iterator n = cmdlist.find(x->name);
+ CommandMap::iterator n = cmdlist.find(x->name);
if (n != cmdlist.end() && n->second == x)
cmdlist.erase(n);
}
+CommandBase::~CommandBase()
+{
+}
+
Command::~Command()
{
- ServerInstance->Parser->RemoveCommand(this);
+ ServerInstance->Parser.RemoveCommand(this);
}
-bool CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
+void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
{
- if (!user || buffer.empty())
- return true;
+ if (buffer.empty())
+ return;
- ServerInstance->Logs->Log("USERINPUT", RAWIO, "C[%s] I :%s %s",
+ ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
- return ProcessCommand(user,buffer);
+ ProcessCommand(user,buffer);
}
bool CommandParser::AddCommand(Command *f)
{
}
-int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
+std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final, CommandBase* custom_translator)
{
std::vector<TranslateType>::const_iterator types = to.begin();
- User* user = NULL;
- unsigned int i;
- int translations = 0;
- dest.clear();
+ std::string dest;
- for(i=0; i < source.size(); i++)
+ for (unsigned int i = 0; i < source.size(); i++)
{
- TranslateType t;
- std::string item = source[i];
-
- if (types == to.end())
- t = TR_TEXT;
- else
+ TranslateType t = TR_TEXT;
+ // They might supply less translation types than parameters,
+ // in that case pretend that all remaining types are TR_TEXT
+ if (types != to.end())
{
t = *types;
types++;
}
- if (prefix_final && i == source.size() - 1)
- dest.append(":");
+ bool last = (i == (source.size() - 1));
+ if (prefix_final && last)
+ dest.push_back(':');
- switch (t)
- {
- case TR_NICK:
- /* Translate single nickname */
- user = ServerInstance->FindNick(item);
- if (user)
- {
- dest.append(user->uuid);
- translations++;
- }
- else
- dest.append(item);
- break;
- case TR_CUSTOM:
- if (custom_translator)
- custom_translator->EncodeParameter(item, i);
- dest.append(item);
- break;
- case TR_END:
- case TR_TEXT:
- default:
- /* Do nothing */
- dest.append(item);
- break;
- }
- if (i != source.size() - 1)
- dest.append(" ");
+ TranslateSingleParam(t, source[i], dest, custom_translator, i);
+
+ if (!last)
+ dest.push_back(' ');
}
- return translations;
+ return dest;
}
-int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
+void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
{
- User* user = NULL;
- int translations = 0;
- dest.clear();
-
switch (to)
{
case TR_NICK:
+ {
/* Translate single nickname */
- user = ServerInstance->FindNick(source);
+ User* user = ServerInstance->FindNick(item);
if (user)
+ dest.append(user->uuid);
+ else
+ dest.append(item);
+ break;
+ }
+ case TR_CUSTOM:
+ {
+ if (custom_translator)
{
- dest = user->uuid;
- translations++;
+ std::string translated = item;
+ custom_translator->EncodeParameter(translated, paramnumber);
+ dest.append(translated);
+ break;
}
- else
- dest = source;
- break;
- case TR_END:
+ // If no custom translator was given, fall through
+ }
case TR_TEXT:
default:
/* Do nothing */
- dest = source;
+ dest.append(item);
break;
}
-
- return translations;
}
*/
-/* $Core */
-
#include "inspircd.h"
-#include "xline.h"
-#include "command_parse.h"
-
-/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */
-
-bool InspIRCd::HostMatchesEveryone(const std::string &mask, User* user)
-{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("hostmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
- {
- if ((InspIRCd::Match(u->second->MakeHost(), mask, ascii_case_insensitive_map)) ||
- (InspIRCd::Match(u->second->MakeHostIP(), mask, ascii_case_insensitive_map)))
- {
- matches++;
- }
- }
-
- if (!matches)
- return false;
-
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),mask.c_str(),percent);
- return true;
- }
- return false;
-}
-
-bool InspIRCd::IPMatchesEveryone(const std::string &ip, User* user)
-{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("ipmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
- {
- if (InspIRCd::Match(u->second->GetIPString(), ip, ascii_case_insensitive_map))
- matches++;
- }
-
- if (!matches)
- return false;
-
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),ip.c_str(),percent);
- return true;
- }
- return false;
-}
-
-bool InspIRCd::NickMatchesEveryone(const std::string &nick, User* user)
-{
- long matches = 0;
-
- ConfigTag* insane = Config->ConfValue("insane");
-
- if (insane->getBool("nickmasks"))
- return false;
-
- float itrigger = insane->getFloat("trigger", 95.5);
-
- for (user_hash::iterator u = this->Users->clientlist->begin(); u != this->Users->clientlist->end(); u++)
- {
- if (InspIRCd::Match(u->second->nick, nick))
- matches++;
- }
-
- if (!matches)
- return false;
-
- float percent = ((float)matches / (float)this->Users->clientlist->size()) * 100;
- if (percent > itrigger)
- {
- SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick.c_str(),nick.c_str(),percent);
- return true;
- }
- return false;
-}
CmdResult SplitCommand::Handle(const std::vector<std::string>& parms, User* u)
{
return HandleRemote(parms, IS_REMOTE(u));
if (IS_SERVER(u))
return HandleServer(parms, IS_SERVER(u));
- ServerInstance->Logs->Log("COMMAND", DEFAULT, "Unknown user type in command (uuid=%s)!", u->uuid.c_str());
+ ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Unknown user type in command (uuid=%s)!", u->uuid.c_str());
return CMD_INVALID;
}
{
return CMD_INVALID;
}
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /ADMIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAdmin : public Command
-{
- public:
- /** Constructor for admin.
- */
- CommandAdmin(Module* parent) : Command(parent,"ADMIN",0,0)
- {
- Penalty = 2;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /ADMIN
- */
-CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
- user->SendText(":%s %03d %s :Administrative info for %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
- if (!ServerInstance->Config->AdminName.empty())
- user->SendText(":%s %03d %s :Name - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC1, user->nick.c_str(), ServerInstance->Config->AdminName.c_str());
- user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINLOC2, user->nick.c_str(), ServerInstance->Config->AdminNick.c_str());
- user->SendText(":%s %03d %s :E-Mail - %s", ServerInstance->Config->ServerName.c_str(),
- RPL_ADMINEMAIL, user->nick.c_str(), ServerInstance->Config->AdminEmail.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAdmin)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /AWAY. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandAway : public Command
-{
- public:
- /** Constructor for away.
- */
- CommandAway ( Module* parent) : Command(parent,"AWAY",0,0) { syntax = "[<message>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /AWAY
- */
-CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ModResult MOD_RESULT;
-
- if ((parameters.size()) && (!parameters[0].empty()))
- {
- FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, parameters[0]));
-
- if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
- return CMD_FAILURE;
-
- user->awaytime = ServerInstance->Time();
- user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway);
-
- user->WriteNumeric(RPL_NOWAWAY, "%s :You have been marked as being away",user->nick.c_str());
- }
- else
- {
- FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, ""));
-
- if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
- return CMD_FAILURE;
-
- user->awaymsg.clear();
- user->WriteNumeric(RPL_UNAWAY, "%s :You are no longer marked as being away",user->nick.c_str());
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandAway)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /CLEARCACHE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandClearcache : public Command
-{
- public:
- /** Constructor for clearcache.
- */
- CommandClearcache ( Module* parent) : Command(parent,"CLEARCACHE",0) { flags_needed = 'o'; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /CLEARCACHE
- */
-CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user)
-{
- int n = ServerInstance->Res->ClearCache();
- user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick.c_str(), n);
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandClearcache)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /COMMANDS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandCommands : public Command
-{
- public:
- /** Constructor for commands.
- */
- CommandCommands(Module* parent) : Command(parent,"COMMANDS",0,0)
- {
- Penalty = 3;
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /COMMANDS
- */
-CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
-{
- std::vector<std::string> list;
- list.reserve(ServerInstance->Parser->cmdlist.size());
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
- {
- // Don't show S2S commands to users
- if (i->second->flags_needed == FLAG_SERVERONLY)
- continue;
-
- Module* src = i->second->creator;
- char buffer[MAXBUF];
- snprintf(buffer, MAXBUF, ":%s %03d %s :%s %s %d %d",
- ServerInstance->Config->ServerName.c_str(), RPL_COMMANDS, user->nick.c_str(),
- i->second->name.c_str(), src->ModuleSourceFile.c_str(),
- i->second->min_params, i->second->Penalty);
- list.push_back(buffer);
- }
- sort(list.begin(), list.end());
- for(unsigned int i=0; i < list.size(); i++)
- user->Write(list[i]);
- user->WriteNumeric(RPL_COMMANDSEND, "%s :End of COMMANDS list",user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandCommands)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /CONNECT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandConnect : public Command
-{
- public:
- /** Constructor for connect.
- */
- CommandConnect ( Module* parent) : Command(parent,"CONNECT",1) { flags_needed = 'o'; syntax = "<servername>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-/** Handle /CONNECT
- */
-CmdResult CommandConnect::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandConnect)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /DIE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandDie : public Command
-{
- public:
- /** Constructor for die.
- */
- CommandDie ( Module* parent) : Command(parent,"DIE",1) { flags_needed = 'o'; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-#include "exitcodes.h"
-
-/** Handle /DIE
- */
-CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (!ServerInstance->PassCompare(user, ServerInstance->Config->diepass, parameters[0].c_str(), ServerInstance->Config->powerhash))
- {
- {
- std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
- ServerInstance->Logs->Log("COMMAND",SPARSE, diebuf);
- ServerInstance->SendError(diebuf);
- }
-
- ServerInstance->Exit(EXIT_STATUS_DIE);
- }
- else
- {
- ServerInstance->Logs->Log("COMMAND",SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
- ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandDie)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-/** Handle /ELINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandEline : public Command
-{
- public:
- /** Constructor for eline.
- */
- CommandEline ( Module* parent) : Command(parent,"ELINE",1,3) { flags_needed = 'o'; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /ELINE
- */
-CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string target = parameters[0];
-
- if (parameters.size() >= 3)
- {
- IdentHostPair ih;
- User* find = ServerInstance->FindNick(target);
- if ((find) && (find->registered == REG_ALL))
- {
- ih.first = "*";
- ih.second = find->GetIPString();
- target = std::string("*@") + find->GetIPString();
- }
- else
- ih = ServerInstance->XLines->IdentSplit(target);
-
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
- return CMD_FAILURE;
-
- long duration = ServerInstance->Duration(parameters[1].c_str());
-
- ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
- if (ServerInstance->XLines->AddLine(el, user))
- {
- if (!duration)
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
- }
- else
- {
- time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
- ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
- timestr.c_str(), parameters[2].c_str());
- }
- }
- else
- {
- delete el;
- user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick.c_str(),target.c_str());
- }
- }
- else
- {
- if (ServerInstance->XLines->DelLine(target.c_str(), "E", user))
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed E-line on %s",user->nick.c_str(),target.c_str());
- }
- else
- {
- user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick.c_str(),target.c_str());
- }
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandEline)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-/** Handle /GLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandGline : public Command
-{
- public:
- /** Constructor for gline.
- */
- CommandGline (Module* parent) : Command(parent,"GLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /GLINE
- */
-CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string target = parameters[0];
-
- if (parameters.size() >= 3)
- {
- IdentHostPair ih;
- User* find = ServerInstance->FindNick(target);
- if ((find) && (find->registered == REG_ALL))
- {
- ih.first = "*";
- ih.second = find->GetIPString();
- target = std::string("*@") + find->GetIPString();
- }
- else
- ih = ServerInstance->XLines->IdentSplit(target);
-
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
- return CMD_FAILURE;
-
- else if (target.find('!') != std::string::npos)
- {
- user->WriteServ("NOTICE %s :*** G-Line cannot operate on nick!user@host masks",user->nick.c_str());
- return CMD_FAILURE;
- }
-
- long duration = ServerInstance->Duration(parameters[1].c_str());
- GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
- if (ServerInstance->XLines->AddLine(gl, user))
- {
- if (!duration)
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
- }
- else
- {
- time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
- ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
- timestr.c_str(), parameters[2].c_str());
- }
-
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete gl;
- user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick.c_str(),target.c_str());
- }
-
- }
- else
- {
- if (ServerInstance->XLines->DelLine(target.c_str(),"G",user))
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed G-line on %s",user->nick.c_str(),target.c_str());
- }
- else
- {
- user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick.c_str(),target.c_str());
- }
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandGline)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2011 Jackmcbarn <jackmcbarn@jackmcbarn.no-ip.org>
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /INFO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInfo : public Command
-{
- public:
- /** Constructor for info.
- */
- CommandInfo(Module* parent) : Command(parent,"INFO")
- {
- Penalty = 4;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-static const char* const lines[] = {
- " -/\\- \2InspIRCd\2 -\\/-",
- " November 2002 - Present",
- " ",
- "\2Core Developers\2:",
- " Craig Edwards, Brain, <brain@inspircd.org>",
- " Craig McLure, Craig, <craig@inspircd.org>",
- " Robin Burchell, w00t, <w00t@inspircd.org>",
- " Oliver Lupton, Om, <om@inspircd.org>",
- " John Brooks, Special, <special@inspircd.org>",
- " Dennis Friis, peavey, <peavey@inspircd.org>",
- " Thomas Stagner, aquanight, <aquanight@inspircd.org>",
- " Uli Schlachter, psychon, <psychon@inspircd.org>",
- " Matt Smith, dz, <dz@inspircd.org>",
- " Daniel De Graaf, danieldg, <danieldg@inspircd.org>",
- " jackmcbarn, <jackmcbarn@inspircd.org>",
- " Attila Molnar, Attila, <attilamolnar@hush.com>",
- " ",
- "\2Regular Contributors\2:",
- " Adam SaberUK",
- " ",
- "\2Other Contributors\2:",
- " ChrisTX Shawn Shutter",
- " ",
- "\2Former Contributors\2:",
- " dmb Zaba skenmy GreenReaper",
- " Dan Jason satmd owine",
- " Adremelech John2 jilles HiroP",
- " eggy Bricker AnMaster djGrrr",
- " nenolod Quension praetorian pippijn",
- " CC jamie typobox43 Burlex (win32)",
- " Stskeeps ThaPrince BuildSmart Thunderhacker",
- " Skip LeaChim Majic MacGyver",
- " Namegduf Ankit Phoenix Taros",
- " ",
- "\2Thanks To\2:",
- " searchirc.com irc-junkie.org Brik fraggeln",
- " ",
- " Best experienced with: \2An IRC client\2",
- NULL
-};
-
-/** Handle /INFO
- */
-CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
-
- int i=0;
- while (lines[i])
- user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_INFO, user->nick.c_str(), lines[i++]);
- FOREACH_MOD(I_OnInfo,OnInfo(user));
- user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandInfo)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /INVITE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandInvite : public Command
-{
- public:
- /** Constructor for invite.
- */
- CommandInvite ( Module* parent) : Command(parent,"INVITE", 0, 0) { Penalty = 4; syntax = "[<nick> <channel>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /INVITE
- */
-CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ModResult MOD_RESULT;
-
- if (parameters.size() == 2 || parameters.size() == 3)
- {
- User* u;
- if (IS_LOCAL(user))
- u = ServerInstance->FindNickOnly(parameters[0]);
- else
- u = ServerInstance->FindNick(parameters[0]);
-
- Channel* c = ServerInstance->FindChan(parameters[1]);
- time_t timeout = 0;
- if (parameters.size() == 3)
- {
- if (IS_LOCAL(user))
- timeout = ServerInstance->Time() + ServerInstance->Duration(parameters[2]);
- else
- timeout = ConvToInt(parameters[2]);
- }
-
- if ((!c) || (!u) || (u->registered != REG_ALL))
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), c ? parameters[0].c_str() : parameters[1].c_str());
- return CMD_FAILURE;
- }
-
- if ((IS_LOCAL(user)) && (!c->HasUser(user)))
- {
- user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str());
- return CMD_FAILURE;
- }
-
- if (c->HasUser(u))
- {
- user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
- return CMD_FAILURE;
- }
-
- FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
-
- if (MOD_RESULT == MOD_RES_DENY)
- {
- return CMD_FAILURE;
- }
- else if (MOD_RESULT == MOD_RES_PASSTHRU)
- {
- if (IS_LOCAL(user))
- {
- unsigned int rank = c->GetPrefixValue(user);
- if (rank < HALFOP_VALUE)
- {
- // Check whether halfop mode is available and phrase error message accordingly
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
- user->nick.c_str(), c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : ""));
- return CMD_FAILURE;
- }
- }
- }
-
- if (IS_LOCAL(u))
- IS_LOCAL(u)->InviteTo(c->name.c_str(), timeout);
- u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
- user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
- if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
- {
- char prefix;
- switch (ServerInstance->Config->AnnounceInvites)
- {
- case ServerConfig::INVITE_ANNOUNCE_OPS:
- {
- prefix = '@';
- break;
- }
- case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
- prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@');
- break;
- }
- default:
- {
- prefix = 0;
- break;
- }
- }
- c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
- }
- FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c,timeout));
- }
- else if (IS_LOCAL(user))
- {
- // pinched from ircu - invite with not enough parameters shows channels
- // youve been invited to but haven't joined yet.
- InviteList& il = IS_LOCAL(user)->GetInviteList();
- for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i)
- {
- user->WriteNumeric(RPL_INVITELIST, "%s :%s",user->nick.c_str(), (*i)->chan->name.c_str());
- }
- user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str());
- }
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandInvite)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /ISON. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandIson : public Command
-{
- public:
- /** Constructor for ison.
- */
- CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
- syntax = "<nick> {nick}";
- }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /ISON
- */
-CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::map<User*,User*> ison_already;
- User *u;
- std::string reply = "303 " + user->nick + " :";
-
- for (unsigned int i = 0; i < parameters.size(); i++)
- {
- u = ServerInstance->FindNickOnly(parameters[i]);
- if (ison_already.find(u) != ison_already.end())
- continue;
-
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- else
- {
- if ((i == parameters.size() - 1) && (parameters[i].find(' ') != std::string::npos))
- {
- /* Its a space seperated list of nicks (RFC1459 says to support this)
- */
- irc::spacesepstream list(parameters[i]);
- std::string item;
-
- while (list.GetToken(item))
- {
- u = ServerInstance->FindNickOnly(item);
- if (ison_already.find(u) != ison_already.end())
- continue;
-
- if ((u) && (u->registered == REG_ALL))
- {
- reply.append(u->nick).append(" ");
- if (reply.length() > 450)
- {
- user->WriteServ(reply);
- reply = "303 " + user->nick + " :";
- }
- ison_already[u] = u;
- }
- }
- }
- }
- }
-
- if (!reply.empty())
- user->WriteServ(reply);
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandIson)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /JOIN. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandJoin : public Command
-{
- public:
- /** Constructor for join.
- */
- CommandJoin ( Module* parent) : Command(parent,"JOIN", 1, 2) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; Penalty = 2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /JOIN
- */
-CmdResult CommandJoin::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 1)
- {
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, 1, false))
- return CMD_SUCCESS;
-
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, parameters[0].c_str(), false, parameters[1].c_str(), false);
- return CMD_SUCCESS;
- }
- }
- else
- {
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, -1, false))
- return CMD_SUCCESS;
-
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
- return CMD_SUCCESS;
- }
- }
-
- user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s %s :Invalid channel name",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandJoin)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /KICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKick : public Command
-{
- public:
- /** Constructor for kick.
- */
- CommandKick ( Module* parent) : Command(parent,"KICK",2,3) { syntax = "<channel> <nick>{,<nick>} [<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /KICK
- */
-CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string reason;
- Channel* c = ServerInstance->FindChan(parameters[0]);
- User* u;
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 1))
- return CMD_SUCCESS;
-
- if (IS_LOCAL(user))
- u = ServerInstance->FindNickOnly(parameters[1]);
- else
- u = ServerInstance->FindNick(parameters[1]);
-
- if ((!u) || (!c) || (u->registered != REG_ALL))
- {
- user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), c ? parameters[1].c_str() : parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server)))
- {
- user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if (parameters.size() > 2)
- {
- reason.assign(parameters[2], 0, ServerInstance->Config->Limits.MaxKick);
- }
- else
- {
- reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
- }
-
- c->KickUser(user, u, reason.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandKick)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /KILL. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKill : public Command
-{
- public:
- /** Constructor for kill.
- */
- CommandKill ( Module* parent) : Command(parent,"KILL",2,2) {
- flags_needed = 'o';
- syntax = "<nickname> <reason>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
- }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- // local kills of remote users are routed via the OnRemoteKill hook
- if (IS_LOCAL(user))
- return ROUTE_LOCALONLY;
- return ROUTE_BROADCAST;
- }
-};
-
-/** Handle /KILL
- */
-CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
-{
- /* Allow comma seperated lists of users for /KILL (thanks w00t) */
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- User *u = ServerInstance->FindNick(parameters[0]);
- if ((u) && (!IS_SERVER(u)))
- {
- /*
- * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
- * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
- *
- * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
- * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
- */
-
- std::string killreason;
- if (IS_LOCAL(user))
- {
- /*
- * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
- * and the other half not. This would be a bad thing. ;p -- w00t
- */
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
-
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- killreason = "Killed (";
- if (!ServerInstance->Config->HideKillsServer.empty())
- {
- // hidekills is on, use it
- killreason += ServerInstance->Config->HideKillsServer;
- }
- else
- {
- // hidekills is off, do nothing
- killreason += user->nick;
- }
-
- killreason += " (" + parameters[1] + "))";
- }
- else
- {
- /* Leave it alone, remote server has already formatted it */
- killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
- }
-
- /*
- * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
- * No time to fix it right now, so left a note. -- w00t
- */
- if (!IS_LOCAL(u))
- {
- // remote kill
- ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killreason));
- }
- else
- {
- // local kill
- /*
- * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
- * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
- */
- if (IS_LOCAL(user))
- ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- else
- ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- ServerInstance->Logs->Log("KILL",DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
- /* Bug #419, make sure this message can only occur once even in the case of multiple KILL messages crossing the network, and change to show
- * hidekillsserver as source if possible
- */
- if (!u->quitting)
- {
- u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- u->nick.c_str(),
- ServerInstance->Config->ServerName.c_str(),
- user->dhost.c_str(),
- ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- parameters[1].c_str());
- }
- }
-
- // send the quit out
- ServerInstance->Users->QuitUser(u, killreason);
- }
- else
- {
- user->WriteServ( "401 %s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandKill)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-/** Handle /KLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandKline : public Command
-{
- public:
- /** Constructor for kline.
- */
- CommandKline ( Module* parent) : Command(parent,"KLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ident@host> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /KLINE
- */
-CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string target = parameters[0];
-
- if (parameters.size() >= 3)
- {
- IdentHostPair ih;
- User* find = ServerInstance->FindNick(target);
- if ((find) && (find->registered == REG_ALL))
- {
- ih.first = "*";
- ih.second = find->GetIPString();
- target = std::string("*@") + find->GetIPString();
- }
- else
- ih = ServerInstance->XLines->IdentSplit(target);
-
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
- return CMD_FAILURE;
-
- if (target.find('!') != std::string::npos)
- {
- user->WriteServ("NOTICE %s :*** K-Line cannot operate on nick!user@host masks",user->nick.c_str());
- return CMD_FAILURE;
- }
-
- long duration = ServerInstance->Duration(parameters[1].c_str());
- KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
- if (ServerInstance->XLines->AddLine(kl,user))
- {
- if (!duration)
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
- }
- else
- {
- time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
- ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
- timestr.c_str(), parameters[2].c_str());
- }
-
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete kl;
- user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick.c_str(),target.c_str());
- }
- }
- else
- {
- if (ServerInstance->XLines->DelLine(target.c_str(),"K",user))
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed K-line on %s",user->nick.c_str(),target.c_str());
- }
- else
- {
- user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick.c_str(),target.c_str());
- }
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandKline)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LINKS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLinks : public Command
-{
- public:
- /** Constructor for links.
- */
- CommandLinks ( Module* parent) : Command(parent,"LINKS",0,0) { }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /LINKS
- */
-CmdResult CommandLinks::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteNumeric(364, "%s %s %s :0 %s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
- user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandLinks)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LIST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandList : public Command
-{
- public:
- /** Constructor for list.
- */
- CommandList ( Module* parent) : Command(parent,"LIST", 0, 0) { Penalty = 5; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /LIST
- */
-CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
-{
- int minusers = 0, maxusers = 0;
-
- user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
-
- /* Work around mIRC suckyness. YOU SUCK, KHALED! */
- if (parameters.size() == 1)
- {
- if (parameters[0][0] == '<')
- {
- maxusers = atoi((parameters[0].c_str())+1);
- }
- else if (parameters[0][0] == '>')
- {
- minusers = atoi((parameters[0].c_str())+1);
- }
- }
-
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
- {
- // attempt to match a glob pattern
- long users = i->second->GetUserCounter();
-
- bool too_few = (minusers && (users <= minusers));
- bool too_many = (maxusers && (users >= maxusers));
-
- if (too_many || too_few)
- continue;
-
- if (parameters.size() && !parameters[0].empty() && (parameters[0][0] != '<' && parameters[0][0] != '>'))
- {
- if (!InspIRCd::Match(i->second->name, parameters[0]) && !InspIRCd::Match(i->second->topic, parameters[0]))
- continue;
- }
-
- // if the channel is not private/secret, OR the user is on the channel anyway
- bool n = (i->second->HasUser(user) || user->HasPrivPermission("channels/auspex"));
-
- if (!n && i->second->IsModeSet('p'))
- {
- /* Channel is +p and user is outside/not privileged */
- user->WriteNumeric(322, "%s * %ld :",user->nick.c_str(), users);
- }
- else
- {
- if (n || !i->second->IsModeSet('s'))
- {
- /* User is in the channel/privileged, channel is not +s */
- user->WriteNumeric(322, "%s %s %ld :[+%s] %s",user->nick.c_str(),i->second->name.c_str(),users,i->second->ChanModes(n),i->second->topic.c_str());
- }
- }
- }
- user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandList)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /LOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLoadmodule : public Command
-{
- public:
- /** Constructor for loadmodule.
- */
- CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /LOADMODULE
- */
-CmdResult CommandLoadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (ServerInstance->Modules->Load(parameters[0]))
- {
- ServerInstance->SNO->WriteGlobalSno('a', "NEW MODULE: %s loaded %s",user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
- return CMD_FAILURE;
- }
-}
-
-COMMAND_INIT(CommandLoadmodule)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-struct LusersCounters
-{
- unsigned int max_local;
- unsigned int max_global;
- unsigned int invisible;
-
- LusersCounters()
- : max_local(ServerInstance->Users->LocalUserCount())
- , max_global(ServerInstance->Users->RegisteredUserCount())
- , invisible(ServerInstance->Users->ModeCount('i'))
- {
- }
-
- inline void UpdateMaxUsers()
- {
- unsigned int current = ServerInstance->Users->LocalUserCount();
- if (current > max_local)
- max_local = current;
-
- current = ServerInstance->Users->RegisteredUserCount();
- if (current > max_global)
- max_global = current;
- }
-};
-
-/** Handle /LUSERS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandLusers : public Command
-{
- LusersCounters& counters;
- public:
- /** Constructor for lusers.
- */
- CommandLusers(Module* parent, LusersCounters& Counters)
- : Command(parent,"LUSERS",0,0), counters(Counters)
- { }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /LUSERS
- */
-CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
-{
- unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
- ProtoServerList serverlist;
- ServerInstance->PI->GetServerList(serverlist);
- unsigned int n_serv = serverlist.size();
- unsigned int n_local_servs = 0;
- for(ProtoServerList::iterator i = serverlist.begin(); i != serverlist.end(); ++i)
- {
- if (i->parentname == ServerInstance->Config->ServerName)
- n_local_servs++;
- }
- // fix for default GetServerList not returning us
- if (!n_serv)
- n_serv = 1;
-
- counters.UpdateMaxUsers();
-
- user->WriteNumeric(251, "%s :There are %d users and %d invisible on %d servers",user->nick.c_str(),
- n_users - counters.invisible, counters.invisible, n_serv);
-
- if (ServerInstance->Users->OperCount())
- user->WriteNumeric(252, "%s %d :operator(s) online",user->nick.c_str(),ServerInstance->Users->OperCount());
-
- if (ServerInstance->Users->UnregisteredUserCount())
- user->WriteNumeric(253, "%s %d :unknown connections",user->nick.c_str(),ServerInstance->Users->UnregisteredUserCount());
-
- user->WriteNumeric(254, "%s %ld :channels formed",user->nick.c_str(),ServerInstance->ChannelCount());
- user->WriteNumeric(255, "%s :I have %d clients and %d servers",user->nick.c_str(),ServerInstance->Users->LocalUserCount(),n_local_servs);
- user->WriteNumeric(265, "%s :Current Local Users: %d Max: %d", user->nick.c_str(), ServerInstance->Users->LocalUserCount(), counters.max_local);
- user->WriteNumeric(266, "%s :Current Global Users: %d Max: %d", user->nick.c_str(), n_users, counters.max_global);
-
- return CMD_SUCCESS;
-}
-
-class InvisibleWatcher : public ModeWatcher
-{
- unsigned int& invisible;
-public:
- InvisibleWatcher(Module* mod, unsigned int& Invisible)
- : ModeWatcher(mod, 'i', MODETYPE_USER), invisible(Invisible)
- {
- }
-
- void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
- {
- if (dest->registered != REG_ALL)
- return;
-
- if (adding)
- invisible++;
- else
- invisible--;
- }
-};
-
-class ModuleLusers : public Module
-{
- LusersCounters counters;
- CommandLusers cmd;
- InvisibleWatcher mw;
-
- public:
- ModuleLusers()
- : cmd(this, counters), mw(this, counters.invisible)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- Implementation events[] = { I_OnPostConnect, I_OnUserQuit };
- ServerInstance->Modules->Attach(events, this, sizeof(events)/sizeof(Implementation));
- ServerInstance->Modes->AddModeWatcher(&mw);
- }
-
- void OnPostConnect(User* user)
- {
- counters.UpdateMaxUsers();
- if (user->IsModeSet('i'))
- counters.invisible++;
- }
-
- void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
- {
- if (user->IsModeSet('i'))
- counters.invisible--;
- }
-
- ~ModuleLusers()
- {
- ServerInstance->Modes->DelModeWatcher(&mw);
- }
-
- Version GetVersion()
- {
- return Version("LUSERS", VF_VENDOR | VF_CORE);
- }
-};
-
-MODULE_INIT(ModuleLusers)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandMap : public Command
-{
- public:
- /** Constructor for map.
- */
- CommandMap ( Module* parent) : Command(parent,"MAP",0,0) { Penalty=2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /MAP
- */
-CmdResult CommandMap::Handle (const std::vector<std::string>&, User *user)
-{
- // as with /LUSERS this does nothing without a linking
- // module to override its behaviour and display something
- // better.
-
- if (IS_OPER(user))
- {
- user->WriteNumeric(006, "%s :%s [%s]", user->nick.c_str(), ServerInstance->Config->ServerName.c_str(), ServerInstance->Config->GetSID().c_str());
- user->WriteNumeric(007, "%s :End of /MAP", user->nick.c_str());
- return CMD_SUCCESS;
- }
- user->WriteNumeric(006, "%s :%s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
- user->WriteNumeric(007, "%s :End of /MAP",user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMap)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MODE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandMode : public Command
-{
- public:
- /** Constructor for mode.
- */
- CommandMode ( Module* parent) : Command(parent,"MODE",1) { syntax = "<target> <modes> {<mode-parameters>}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/** Handle /MODE
- */
-CmdResult CommandMode::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ServerInstance->Modes->Process(parameters, user, false);
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandMode)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandModeNotice : public Command
-{
- public:
- CommandModeNotice(Module* parent) : Command(parent,"MODENOTICE",2,2)
- {
- syntax = "<modes> <message>";
- flags_needed = 'o';
- }
-
- CmdResult Handle(const std::vector<std::string>& parameters, User *src)
- {
- int mlen = parameters[0].length();
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
- {
- User* user = *i;
- for (int n = 0; n < mlen; n++)
- {
- if (!user->IsModeSet(parameters[0][n]))
- goto next_user;
- }
- user->Write(":%s NOTICE %s :*** From %s: %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), src->nick.c_str(), parameters[1].c_str());
-next_user: ;
- }
- return CMD_SUCCESS;
- }
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- return ROUTE_BROADCAST;
- }
-};
-
-COMMAND_INIT(CommandModeNotice)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MODULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandModules : public Command
-{
- public:
- /** Constructor for modules.
- */
- CommandModules(Module* parent) : Command(parent,"MODULES",0,0)
- {
- Penalty = 4;
- syntax = "[<servername>]";
- }
-
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() >= 1)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /MODULES
- */
-CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() >= 1 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
-
- std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
-
- for (unsigned int i = 0; i < module_names.size(); i++)
- {
- Module* m = ServerInstance->Modules->Find(module_names[i]);
- Version V = m->GetVersion();
-
- if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
- {
- std::string flags("SvcC");
- int pos = 0;
- for (int mult = 1; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
- if (!(V.Flags & mult))
- flags[pos] = '-';
-
-#ifdef PURE_STATIC
- user->SendText(":%s 702 %s :%p %s %s :%s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, module_names[i].c_str(), flags.c_str(), V.description.c_str());
-#else
- std::string srcrev = m->ModuleDLLManager->GetVersion();
- user->SendText(":%s 702 %s :%p %s %s :%s - %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), (void*)m, module_names[i].c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str());
-#endif
- }
- else
- {
- user->SendText(":%s 702 %s :%s %s", ServerInstance->Config->ServerName.c_str(),
- user->nick.c_str(), module_names[i].c_str(), V.description.c_str());
- }
- }
- user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandModules)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /MOTD. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandMotd : public Command
-{
- public:
- /** Constructor for motd.
- */
- CommandMotd ( Module* parent) : Command(parent,"MOTD",0,1) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-/** Handle /MOTD
- */
-CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
-
- ConfigTag* tag = ServerInstance->Config->EmptyTag;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
- std::string motd_name = tag->getString("motd", "motd");
- ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
- if (motd == ServerInstance->Config->Files.end())
- {
- user->SendText(":%s %03d %s :Message of the day file is missing.",
- ServerInstance->Config->ServerName.c_str(), ERR_NOMOTD, user->nick.c_str());
- return CMD_SUCCESS;
- }
-
- user->SendText(":%s %03d %s :%s message of the day", ServerInstance->Config->ServerName.c_str(),
- RPL_MOTDSTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
-
- for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
- user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_MOTD, user->nick.c_str(), i->c_str());
-
- user->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMotd)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /NAMES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNames : public Command
-{
- public:
- /** Constructor for names.
- */
- CommandNames ( Module* parent) : Command(parent,"NAMES",0,0) { syntax = "{<channel>{,<channel>}}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /NAMES
- */
-CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
-{
- Channel* c;
-
- if (!parameters.size())
- {
- user->WriteNumeric(366, "%s * :End of /NAMES list.",user->nick.c_str());
- return CMD_SUCCESS;
- }
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- c = ServerInstance->FindChan(parameters[0]);
- if (c)
- {
- c->UserList(user);
- }
- else
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandNames)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /NICK. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNick : public Command
-{
- public:
- /** Constructor for nick.
- */
- CommandNick ( Module* parent) : Command(parent,"NICK", 1, 1) { works_before_reg = true; syntax = "<newnick>"; Penalty = 0; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle nick changes from users.
- * NOTE: If you are used to ircds based on ircd2.8, and are looking
- * for the client introduction code in here, youre in the wrong place.
- * You need to look in the spanningtree module for this!
- */
-CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string oldnick = user->nick;
- std::string newnick = parameters[0];
-
- // anything except the initial NICK gets a flood penalty
- if (user->registered == REG_ALL && IS_LOCAL(user))
- IS_LOCAL(user)->CommandFloodPenalty += 4000;
-
- if (newnick.empty())
- {
- user->WriteNumeric(432, "%s * :Erroneous Nickname", oldnick.c_str());
- return CMD_FAILURE;
- }
-
- if (newnick == "0")
- {
- newnick = user->uuid;
- }
- else if (!ServerInstance->IsNick(newnick.c_str(), ServerInstance->Config->Limits.NickMax))
- {
- user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),newnick.c_str());
- return CMD_FAILURE;
- }
-
- if (!user->ChangeNick(newnick, false))
- return CMD_FAILURE;
-
- if (user->registered < REG_NICKUSER)
- {
- user->registered = (user->registered | REG_NICK);
- if (user->registered == REG_NICKUSER)
- {
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (IS_LOCAL(user)));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- // return early to not penalize new users
- return CMD_SUCCESS;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandNick)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-/** Handle /NOTICE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNotice : public Command
-{
- public:
- /** Constructor for notice.
- */
- CommandNotice ( Module* parent) : Command(parent,"NOTICE",2,2) { syntax = "<target>{,<target>} <message>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (IS_LOCAL(user))
- // This is handled by the OnUserNotice hook to split the LoopCall pieces
- return ROUTE_LOCALONLY;
- else
- return ROUTE_MESSAGE(parameters[0]);
- }
-};
-
-
-CmdResult CommandNotice::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- Channel *chan;
-
- CUList exempt_list;
-
- user->idle_lastmsg = ServerInstance->Time();
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
- if (parameters[0][0] == '$')
- {
- if (!user->HasPrivPermission("users/mass-message"))
- return CMD_SUCCESS;
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
- const char* text = temp.c_str();
- const char* servermask = (parameters[0].c_str()) + 1;
-
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- if (InspIRCd::Match(ServerInstance->Config->ServerName,servermask, NULL))
- {
- user->SendAll("NOTICE", "%s", text);
- }
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- return CMD_SUCCESS;
- }
- char status = 0;
- const char* target = parameters[0].c_str();
-
- if (ServerInstance->Modes->FindPrefix(*target))
- {
- status = *target;
- target++;
- }
- if (*target == '#')
- {
- chan = ServerInstance->FindChan(target);
-
- exempt_list.insert(user);
-
- if (chan)
- {
- if (IS_LOCAL(user))
- {
- if ((chan->IsModeSet('n')) && (!chan->HasUser(user)))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- if ((chan->IsModeSet('m')) && (chan->GetPrefixValue(user) < VOICE_VALUE))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- if (chan->IsBanned(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- if (temp.empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,exempt_list));
-
- if (status)
- {
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name.c_str(), text);
- }
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,text,status,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), target);
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
- }
-
- const char* destnick = parameters[0].c_str();
-
- if (IS_LOCAL(user))
- {
- const char* targetserver = strchr(destnick, '@');
-
- if (targetserver)
- {
- std::string nickonly;
-
- nickonly.assign(destnick, 0, targetserver - destnick);
- dest = ServerInstance->FindNickOnly(nickonly);
- if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
- {
- /* Incorrect server for user */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
- else
- dest = ServerInstance->FindNickOnly(destnick);
- }
- else
- dest = ServerInstance->FindNick(destnick);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- if (parameters[1].empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,dest,TYPE_USER,temp,0,exempt_list));
- if (MOD_RESULT == MOD_RES_DENY) {
- return CMD_FAILURE;
- }
- const char* text = temp.c_str();
-
- FOREACH_MOD(I_OnText,OnText(user,dest,TYPE_USER,text,0,exempt_list));
-
- if (IS_LOCAL(dest))
- {
- // direct write, same server
- user->WriteTo(dest, "NOTICE %s :%s", dest->nick.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,text,0,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-
-}
-
-COMMAND_INIT(CommandNotice)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-bool OneOfMatches(const char* host, const char* ip, const char* hostlist);
-
-/** Handle /OPER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandOper : public SplitCommand
-{
- public:
- /** Constructor for oper.
- */
- CommandOper ( Module* parent) : SplitCommand(parent,"OPER",2,2) { syntax = "<username> <password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
-CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- char TheHost[MAXBUF];
- char TheIP[MAXBUF];
- bool match_login = false;
- bool match_pass = false;
- bool match_hosts = false;
-
- snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(),user->host.c_str());
- snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(),user->GetIPString());
-
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
- if (i != ServerInstance->Config->oper_blocks.end())
- {
- OperInfo* ifo = i->second;
- ConfigTag* tag = ifo->oper_block;
- match_login = true;
- match_pass = !ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
- match_hosts = OneOfMatches(TheHost,TheIP,tag->getString("host"));
-
- if (match_pass && match_hosts)
- {
- /* found this oper's opertype */
- user->Oper(ifo);
- return CMD_SUCCESS;
- }
- }
-
- std::string fields;
- if (!match_login)
- fields.append("login ");
- if (!match_pass)
- fields.append("password ");
- if (!match_hosts)
- fields.append("hosts");
-
- // tell them they suck, and lag them up to help prevent brute-force attacks
- user->WriteNumeric(491, "%s :Invalid oper credentials",user->nick.c_str());
- user->CommandFloodPenalty += 10000;
-
- ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
- ServerInstance->Logs->Log("OPER",DEFAULT,"OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandOper)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PART. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPart : public Command
-{
- public:
- /** Constructor for part.
- */
- CommandPart (Module* parent) : Command(parent,"PART", 1, 2) { Penalty = 5; syntax = "<channel>{,<channel>} [<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string reason;
-
- if (IS_LOCAL(user))
- {
- if (!ServerInstance->Config->FixedPart.empty())
- reason = ServerInstance->Config->FixedPart;
- else if (parameters.size() > 1)
- reason = ServerInstance->Config->PrefixPart + parameters[1] + ServerInstance->Config->SuffixPart;
- }
- else
- {
- if (parameters.size() > 1)
- reason = parameters[1];
- }
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- Channel* c = ServerInstance->FindChan(parameters[0]);
-
- if (c)
- {
- c->PartUser(user, reason);
- }
- else
- {
- user->WriteServ( "401 %s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPart)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PASS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPass : public SplitCommand
-{
- public:
- /** Constructor for pass.
- */
- CommandPass (Module* parent) : SplitCommand(parent,"PASS",1,1) { works_before_reg = true; Penalty = 0; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-
-CmdResult CommandPass::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- // Check to make sure they haven't registered -- Fix by FCS
- if (user->registered == REG_ALL)
- {
- user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You may not reregister",user->nick.c_str());
- return CMD_FAILURE;
- }
- user->password = parameters[0];
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPass)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PING. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPing : public Command
-{
- public:
- /** Constructor for ping.
- */
- CommandPing ( Module* parent) : Command(parent,"PING", 1, 2) { Penalty = 0; syntax = "<servername> [:<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPing::Handle (const std::vector<std::string>& parameters, User *user)
-{
- user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPing)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PONG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPong : public Command
-{
- public:
- /** Constructor for pong.
- */
- CommandPong ( Module* parent) : Command(parent,"PONG", 0, 1) { Penalty = 0; syntax = "<ping-text>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandPong::Handle (const std::vector<std::string>&, User *user)
-{
- // set the user as alive so they survive to next ping
- if (IS_LOCAL(user))
- IS_LOCAL(user)->lastping = 1;
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPong)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /PRIVMSG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPrivmsg : public Command
-{
- public:
- /** Constructor for privmsg.
- */
- CommandPrivmsg ( Module* parent) : Command(parent,"PRIVMSG",2,2) { syntax = "<target>{,<target>} <message>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (IS_LOCAL(user))
- // This is handled by the OnUserMessage hook to split the LoopCall pieces
- return ROUTE_LOCALONLY;
- else
- return ROUTE_MESSAGE(parameters[0]);
- }
-};
-
-CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- Channel *chan;
- CUList except_list;
-
- user->idle_lastmsg = ServerInstance->Time();
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
- if (parameters[0][0] == '$')
- {
- if (!user->HasPrivPermission("users/mass-message"))
- return CMD_SUCCESS;
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
- const char* servermask = (parameters[0].c_str()) + 1;
-
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
- if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
- {
- user->SendAll("PRIVMSG", "%s", text);
- }
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
- return CMD_SUCCESS;
- }
- char status = 0;
- const char* target = parameters[0].c_str();
-
- if (ServerInstance->Modes->FindPrefix(*target))
- {
- status = *target;
- target++;
- }
- if (*target == '#')
- {
- chan = ServerInstance->FindChan(target);
-
- except_list.insert(user);
-
- if (chan)
- {
- if (IS_LOCAL(user) && chan->GetPrefixValue(user) < VOICE_VALUE)
- {
- if (chan->IsModeSet('n') && !chan->HasUser(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (chan->IsModeSet('m'))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- if (chan->IsBanned(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status,except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- /* Check again, a module may have zapped the input string */
- if (temp.empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,except_list));
-
- if (status)
- {
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name.c_str(), text);
- }
- }
- else
- {
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,text,status,except_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), target);
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
- }
-
- const char* destnick = parameters[0].c_str();
-
- if (IS_LOCAL(user))
- {
- const char* targetserver = strchr(destnick, '@');
-
- if (targetserver)
- {
- std::string nickonly;
-
- nickonly.assign(destnick, 0, targetserver - destnick);
- dest = ServerInstance->FindNickOnly(nickonly);
- if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
- {
- /* Incorrect server for user */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
- else
- dest = ServerInstance->FindNickOnly(destnick);
- }
- else
- dest = ServerInstance->FindNick(destnick);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- if (parameters[1].empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- if (IS_AWAY(dest))
- {
- /* auto respond with aweh msg */
- user->WriteNumeric(301, "%s %s :%s", user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
- }
-
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- FOREACH_MOD(I_OnText,OnText(user, dest, TYPE_USER, text, 0, except_list));
-
- if (IS_LOCAL(dest))
- {
- // direct write, same server
- user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, dest, TYPE_USER, text, 0, except_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandPrivmsg)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-/** Handle /QLINE. */
-class CommandQline : public Command
-{
- public:
- /** Constructor for qline.
- */
- CommandQline ( Module* parent) : Command(parent,"QLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<nick> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to the command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() >= 3)
- {
- if (ServerInstance->NickMatchesEveryone(parameters[0],user))
- return CMD_FAILURE;
-
- if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
- {
- user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick.c_str());
- return CMD_FAILURE;
- }
-
- long duration = ServerInstance->Duration(parameters[1].c_str());
- QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
- if (ServerInstance->XLines->AddLine(ql,user))
- {
- if (!duration)
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s: %s",user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
- }
- else
- {
- time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
- ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s: %s",user->nick.c_str(),parameters[0].c_str(),
- timestr.c_str(), parameters[2].c_str());
- }
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete ql;
- user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick.c_str(),parameters[0].c_str());
- }
- }
- else
- {
- if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "Q", user))
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed Q-line on %s",user->nick.c_str(),parameters[0].c_str());
- }
- else
- {
- user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandQline)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /QUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandQuit : public Command
-{
- public:
- /** Constructor for quit.
- */
- CommandQuit ( Module* parent) : Command(parent,"QUIT",0,1) { works_before_reg = true; syntax = "[<message>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandQuit::Handle (const std::vector<std::string>& parameters, User *user)
-{
-
- std::string quitmsg;
-
- if (IS_LOCAL(user))
- {
- if (!ServerInstance->Config->FixedQuit.empty())
- quitmsg = ServerInstance->Config->FixedQuit;
- else
- quitmsg = parameters.size() ?
- ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit
- : "Client exited";
- }
- else
- quitmsg = parameters.size() ? parameters[0] : "Client exited";
-
- std::string* operquit = ServerInstance->OperQuit.get(user);
- if (operquit)
- {
- ServerInstance->Users->QuitUser(user, quitmsg, operquit->c_str());
- }
- else
- {
- ServerInstance->Users->QuitUser(user, quitmsg);
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandQuit)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /REHASH. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRehash : public Command
-{
- public:
- /** Constructor for rehash.
- */
- CommandRehash ( Module* parent) : Command(parent,"REHASH",0) { flags_needed = 'o'; Penalty = 2; syntax = "[<servermask>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string param = parameters.size() ? parameters[0] : "";
-
- FOREACH_MOD(I_OnPreRehash,OnPreRehash(user, param));
-
- if (param.empty())
- {
- // standard rehash of local server
- }
- else if (param.find_first_of("*.") != std::string::npos)
- {
- // rehash of servers by server name (with wildcard)
- if (!InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0]))
- {
- // Doesn't match us. PreRehash is already done, nothing left to do
- return CMD_SUCCESS;
- }
- }
- else
- {
- // parameterized rehash
-
- // the leading "-" is optional; remove it if present.
- if (param[0] == '-')
- param = param.substr(1);
-
- FOREACH_MOD(I_OnModuleRehash,OnModuleRehash(user, param));
- return CMD_SUCCESS;
- }
-
- // Rehash for me. Try to start the rehash thread
- if (!ServerInstance->ConfigThread)
- {
- std::string m = user->nick + " is rehashing config file " + ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()) + " on " + ServerInstance->Config->ServerName;
- ServerInstance->SNO->WriteGlobalSno('a', m);
-
- if (IS_LOCAL(user))
- user->WriteNumeric(RPL_REHASHING, "%s %s :Rehashing",
- user->nick.c_str(),ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
- else
- ServerInstance->PI->SendUserNotice(user, std::string("*** Rehashing server ") +
- ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()));
-
- /* Don't do anything with the logs here -- logs are restarted
- * after the config thread has completed.
- */
- ServerInstance->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
-
-
- ServerInstance->ConfigThread = new ConfigReaderThread(user->uuid);
- ServerInstance->Threads->Start(ServerInstance->ConfigThread);
- }
- else
- {
- /*
- * A rehash is already in progress! ahh shit.
- * XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
- */
- if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :*** Could not rehash: A rehash is already in progress.", user->nick.c_str());
- else
- ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
- }
-
- // Always return success so spanningtree forwards an incoming REHASH even if we failed
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandRehash)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandReloadmodule : public Command
-{
- public:
- /** Constructor for reloadmodule.
- */
- CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-class ReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
- const std::string name;
- const std::string uid;
- ReloadModuleWorker(const std::string& uuid, const std::string& modn)
- : name(modn), uid(uuid) {}
- void Call(bool result)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
- name.c_str(), result ? "" : "un");
- User* user = ServerInstance->FindNick(uid);
- if (user)
- user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
- user->nick.c_str(), name.c_str(), result ? "" : "un");
- ServerInstance->GlobalCulls.AddItem(this);
- }
-};
-
-CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters[0] == "cmd_reloadmodule.so")
- {
- user->WriteNumeric(975, "%s %s :You cannot reload cmd_reloadmodule.so (unload and load it)",
- user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- Module* m = ServerInstance->Modules->Find(parameters[0]);
- if (m)
- {
- ServerInstance->Modules->Reload(m, new ReloadModuleWorker(user->uuid, parameters[0]));
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-}
-
-COMMAND_INIT(CommandReloadmodule)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /RESTART
- */
-class CommandRestart : public Command
-{
- public:
- /** Constructor for restart.
- */
- CommandRestart(Module* parent) : Command(parent,"RESTART",1,1) { flags_needed = 'o'; syntax = "<password>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
-{
- ServerInstance->Logs->Log("COMMAND",DEFAULT,"Restart: %s",user->nick.c_str());
- if (!ServerInstance->PassCompare(user, ServerInstance->Config->restartpass, parameters[0].c_str(), ServerInstance->Config->powerhash))
- {
- ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
-
- ServerInstance->SendError("Server restarting.");
-
-#ifndef _WIN32
- /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execv() below succeeds.
- * Certainly, this is not a nice way to do things and it's slow when the fd limit is high.
- *
- * A better solution would be to set the close-on-exec flag for each fd we create (or create them with O_CLOEXEC),
- * however there is no guarantee that third party libs will do the same.
- */
- for (int i = getdtablesize(); --i > 2;)
- {
- int flags = fcntl(i, F_GETFD);
- if (flags != -1)
- fcntl(i, F_SETFD, flags | FD_CLOEXEC);
- }
-#endif
-
- execv(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
- ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART - could not execute '%s' (%s)",
- ServerInstance->Config->cmdline.argv[0], strerror(errno));
- }
- else
- {
- ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART Command from %s.", user->GetFullRealHost().c_str());
- }
- return CMD_FAILURE;
-}
-
-
-COMMAND_INIT(CommandRestart)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /RULES. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandRules : public Command
-{
- public:
- /** Constructor for rules.
- */
- CommandRules ( Module* parent) : Command(parent,"RULES",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-CmdResult CommandRules::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
-
- ConfigTag* tag = ServerInstance->Config->EmptyTag;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
- std::string rules_name = tag->getString("rules", "rules");
- ConfigFileCache::iterator rules = ServerInstance->Config->Files.find(rules_name);
- if (rules == ServerInstance->Config->Files.end())
- {
- user->SendText(":%s %03d %s :RULES file is missing.",
- ServerInstance->Config->ServerName.c_str(), ERR_NORULES, user->nick.c_str());
- return CMD_SUCCESS;
- }
- user->SendText(":%s %03d %s :%s server rules:", ServerInstance->Config->ServerName.c_str(),
- RPL_RULESTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
-
- for (file_cache::iterator i = rules->second.begin(); i != rules->second.end(); i++)
- user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_RULES, user->nick.c_str(),i->c_str());
-
- user->SendText(":%s %03d %s :End of RULES command.", ServerInstance->Config->ServerName.c_str(), RPL_RULESEND, user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandRules)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /SERVER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandServer : public Command
-{
- public:
- /** Constructor for server.
- */
- CommandServer ( Module* parent) : Command(parent,"SERVER") { works_before_reg = true;}
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandServer::Handle (const std::vector<std::string>&, User *user)
-{
- if (user->registered == REG_ALL)
- {
- user->WriteNumeric(ERR_ALREADYREGISTERED, "%s :You are already registered. (Perhaps your IRC client does not have a /SERVER command).",user->nick.c_str());
- }
- else
- {
- user->WriteNumeric(ERR_NOTREGISTERED, "%s :You may not register as a server (servers have separate ports from clients, change your config)",name.c_str());
- }
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandServer)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /SQUIT. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandSquit : public Command
-{
- public:
- /** Constructor for squit.
- */
- CommandSquit ( Module* parent) : Command(parent,"SQUIT",1,2) { flags_needed = 'o'; syntax = "<servername>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-/*
- * This is handled by the server linking module, if necessary. Do not remove this stub.
- */
-
-
-CmdResult CommandSquit::Handle (const std::vector<std::string>&, User *user)
-{
- user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
- return CMD_FAILURE;
-}
-
-COMMAND_INIT(CommandSquit)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-#include "commands/cmd_whowas.h"
-
-#ifdef _WIN32
-#include <psapi.h>
-#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
-#endif
-
-/** Handle /STATS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandStats : public Command
-{
- void DoStats(char statschar, User* user, string_list &results);
- public:
- /** Constructor for stats.
- */
- CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 1)
- return ROUTE_UNICAST(parameters[1]);
- return ROUTE_LOCALONLY;
- }
-};
-
-void CommandStats::DoStats(char statschar, User* user, string_list &results)
-{
- std::string sn(ServerInstance->Config->ServerName);
-
- bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
- bool isRemoteOper = IS_REMOTE(user) && IS_OPER(user);
- bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
-
- if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
- {
- ServerInstance->SNO->WriteToSnoMask('t',
- "%s '%c' denied for %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"),
- statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- results.push_back(sn + " 481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv.");
- return;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
- ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return;
- }
-
- switch (statschar)
- {
- /* stats p (show listening ports) */
- case 'p':
- {
- for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
- {
- ListenSocket* ls = *i;
- std::string ip = ls->bind_addr;
- if (ip.empty())
- ip.assign("*");
- std::string type = ls->bind_tag->getString("type", "clients");
- std::string hook = ls->bind_tag->getString("ssl", "plaintext");
-
- results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
- " (" + type + ", " + hook + ")");
- }
- }
- break;
-
- /* These stats symbols must be handled by a linking module */
- case 'n':
- case 'c':
- break;
-
- case 'i':
- {
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
- {
- ConnectClass* c = *i;
- std::stringstream res;
- res << sn << " 215 " << user->nick << " I " << c->name << ' ';
- if (c->type == CC_ALLOW)
- res << '+';
- if (c->type == CC_DENY)
- res << '-';
-
- if (c->type == CC_NAMED)
- res << '*';
- else
- res << c->host;
-
- res << ' ' << c->config->getString("port", "*") << ' ';
-
- res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
- << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
- if (c->fakelag)
- res << '*';
- results.push_back(res.str());
- }
- }
- break;
-
- case 'Y':
- {
- int idx = 0;
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
- {
- ConnectClass* c = *i;
- results.push_back(sn+" 215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : ServerInstance->SE->GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
- results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
- ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
- idx++;
- }
- }
- break;
-
- case 'U':
- {
- for(std::map<irc::string, bool>::iterator i = ServerInstance->Config->ulines.begin(); i != ServerInstance->Config->ulines.end(); ++i)
- {
- results.push_back(sn+" 248 "+user->nick+" U "+std::string(i->first.c_str()));
- }
- }
- break;
-
- case 'P':
- {
- unsigned int idx = 0;
- for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
- {
- User* oper = *i;
- if (!ServerInstance->ULine(oper->server))
- {
- results.push_back(sn+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
- (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
- idx++;
- }
- }
- results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
- }
- break;
-
- case 'k':
- ServerInstance->XLines->InvokeStats("K",216,user,results);
- break;
- case 'g':
- ServerInstance->XLines->InvokeStats("G",223,user,results);
- break;
- case 'q':
- ServerInstance->XLines->InvokeStats("Q",217,user,results);
- break;
- case 'Z':
- ServerInstance->XLines->InvokeStats("Z",223,user,results);
- break;
- case 'e':
- ServerInstance->XLines->InvokeStats("E",223,user,results);
- break;
- case 'E':
- results.push_back(sn+" 249 "+user->nick+" :Total events: "+ConvToStr(ServerInstance->SE->TotalEvents));
- results.push_back(sn+" 249 "+user->nick+" :Read events: "+ConvToStr(ServerInstance->SE->ReadEvents));
- results.push_back(sn+" 249 "+user->nick+" :Write events: "+ConvToStr(ServerInstance->SE->WriteEvents));
- results.push_back(sn+" 249 "+user->nick+" :Error events: "+ConvToStr(ServerInstance->SE->ErrorEvents));
- break;
-
- /* stats m (list number of times each command has been used, plus bytecount) */
- case 'm':
- for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
- {
- if (i->second->use_count)
- {
- /* RPL_STATSCOMMANDS */
- results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
- }
- }
- break;
-
- /* stats z (debug and memory info) */
- case 'z':
- {
- results.push_back(sn+" 249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->clientlist->size()));
- results.push_back(sn+" 249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->chanlist->size()));
- results.push_back(sn+" 249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size()));
-
- if (ServerInstance->Config->WhoWasGroupSize && ServerInstance->Config->WhoWasMaxGroups)
- {
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_STATS);
- req.user = user;
- req.Send();
- results.push_back(sn+" 249 "+user->nick+" :"+req.value);
- }
- }
-
- float kbitpersec_in, kbitpersec_out, kbitpersec_total;
- char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
-
- ServerInstance->SE->GetStats(kbitpersec_in, kbitpersec_out, kbitpersec_total);
-
- snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
- snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
- snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
-
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
- results.push_back(sn+" 249 "+user->nick+" :Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
-
-#ifndef _WIN32
- /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
- * Also cuts out some identical code in both branches of the ifndef. -- Om
- */
- rusage R;
-
- /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
- if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
- {
- results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals));
- results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt));
- results.push_back(sn+" 249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap));
- results.push_back(sn+" 249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
-
- char percent[30];
-
- float n_elapsed = (ServerInstance->Time() - ServerInstance->stats->LastSampled.tv_sec) * 1000000
- + (ServerInstance->Time_ns() - ServerInstance->stats->LastSampled.tv_nsec) / 1000;
- float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec);
- float per = (n_eaten / n_elapsed) * 100;
-
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (now): "+percent);
-
- n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
- n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
- per = (n_eaten / n_elapsed) * 100;
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (total): "+percent);
- }
-#else
- PROCESS_MEMORY_COUNTERS MemCounters;
- if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
- {
- results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
- results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(MemCounters.PageFaultCount));
- }
-
- FILETIME CreationTime;
- FILETIME ExitTime;
- FILETIME KernelTime;
- FILETIME UserTime;
- LARGE_INTEGER ThisSample;
- if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
- QueryPerformanceCounter(&ThisSample))
- {
- KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
- KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
- double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats->LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats->LastCPU.dwLowDateTime) )/100000;
- double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats->LastSampled.QuadPart) / ServerInstance->stats->QPFrequency.QuadPart;
- double per = (n_eaten/n_elapsed);
-
- char percent[30];
-
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (now): "+percent);
-
- n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
- n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
- per = (n_eaten / n_elapsed);
- snprintf(percent, 30, "%03.5f%%", per);
- results.push_back(sn+" 249 "+user->nick+" :CPU Use (total): "+percent);
- }
-#endif
- }
- break;
-
- case 'T':
- {
- char buffer[MAXBUF];
- results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
- results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
- results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
- results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad));
- results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
- snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",
- user->nick.c_str(),ServerInstance->stats->statsSent / 1024.0,ServerInstance->stats->statsRecv / 1024.0);
- results.push_back(sn+buffer);
- }
- break;
-
- /* stats o */
- case 'o':
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- results.push_back(sn+" 243 "+user->nick+" O "+tag->getString("host")+" * "+
- tag->getString("name") + " " + tag->getString("type")+" 0");
- }
- }
- break;
- case 'O':
- {
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
- {
- // just the types, not the actual oper blocks...
- if (i->first[0] != ' ')
- continue;
- OperInfo* tag = i->second;
- tag->init();
- std::string umodes;
- std::string cmodes;
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
- if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
- umodes.push_back(c);
- mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
- cmodes.push_back(c);
- }
- results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes);
- }
- }
- break;
-
- /* stats l (show user I/O stats) */
- case 'l':
- results.push_back(sn+" 211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
- }
- break;
-
- /* stats L (show user I/O stats with IP addresses) */
- case 'L':
- results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
- for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++)
- {
- LocalUser* i = *n;
- results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
- }
- break;
-
- /* stats u (show server uptime) */
- case 'u':
- {
- time_t current_time = 0;
- current_time = ServerInstance->Time();
- time_t server_uptime = current_time - ServerInstance->startup_time;
- struct tm* stime;
- stime = gmtime(&server_uptime);
- /* i dont know who the hell would have an ircd running for over a year nonstop, but
- * Craig suggested this, and it seemed a good idea so in it went */
- if (stime->tm_year > 70)
- {
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick.c_str(),(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
- }
- else
- {
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick.c_str(),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
- }
- }
- break;
-
- default:
- break;
- }
-
- results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
- ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
- (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
- return;
-}
-
-CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
- string_list values;
- char search = parameters[0][0];
- DoStats(search, user, values);
- for (size_t i = 0; i < values.size(); i++)
- user->SendText(":%s", values[i].c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandStats)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /TIME. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTime : public Command
-{
- public:
- /** Constructor for time.
- */
- CommandTime ( Module* parent) : Command(parent,"TIME",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (parameters.size() > 0)
- return ROUTE_UNICAST(parameters[0]);
- return ROUTE_LOCALONLY;
- }
-};
-
-CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
- return CMD_SUCCESS;
- struct tm* timeinfo;
- time_t local = ServerInstance->Time();
-
- timeinfo = localtime(&local);
-
- char tms[26];
- snprintf(tms,26,"%s",asctime(timeinfo));
- tms[24] = 0;
-
- user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),tms);
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandTime)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /TOPIC. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandTopic : public Command
-{
- public:
- /** Constructor for topic.
- */
- CommandTopic ( Module* parent) : Command(parent,"TOPIC",1, 2) { syntax = "<channel> [<topic>]"; Penalty = 2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User *user)
-{
- Channel* c;
-
- c = ServerInstance->FindChan(parameters[0]);
- if (!c)
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- if (parameters.size() == 1)
- {
- if (c)
- {
- if ((c->IsModeSet('s')) && (!c->HasUser(user)))
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), c->name.c_str());
- return CMD_FAILURE;
- }
-
- if (c->topic.length())
- {
- user->WriteNumeric(332, "%s %s :%s", user->nick.c_str(), c->name.c_str(), c->topic.c_str());
- user->WriteNumeric(333, "%s %s %s %lu", user->nick.c_str(), c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset);
- }
- else
- {
- user->WriteNumeric(RPL_NOTOPICSET, "%s %s :No topic is set.", user->nick.c_str(), c->name.c_str());
- }
- }
- return CMD_SUCCESS;
- }
- else if (parameters.size()>1)
- {
- std::string t = parameters[1]; // needed, in case a module wants to change it
- c->SetTopic(user, t);
- }
-
- return CMD_SUCCESS;
-}
-
-
-COMMAND_INIT(CommandTopic)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /UNLOADMODULE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUnloadmodule : public Command
-{
- public:
- /** Constructor for unloadmodule.
- */
- CommandUnloadmodule ( Module* parent) : Command(parent,"UNLOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandUnloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
-{
- if (parameters[0] == "cmd_unloadmodule.so" || parameters[0] == "cmd_loadmodule.so")
- {
- user->WriteNumeric(972, "%s %s :You cannot unload module loading commands!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- Module* m = ServerInstance->Modules->Find(parameters[0]);
- if (m && ServerInstance->Modules->Unload(m))
- {
- ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
- user->WriteNumeric(973, "%s %s :Module successfully unloaded.",user->nick.c_str(), parameters[0].c_str());
- }
- else
- {
- user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(),
- m ? ServerInstance->Modules->LastError().c_str() : "No such module");
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUnloadmodule)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /USER. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUser : public SplitCommand
-{
- public:
- /** Constructor for user.
- */
- CommandUser ( Module* parent) : SplitCommand(parent,"USER",4,4) { works_before_reg = true; Penalty = 0; syntax = "<username> <localhost> <remotehost> <GECOS>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
-};
-
-CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
-{
- /* A user may only send the USER command once */
- if (!(user->registered & REG_USER))
- {
- if (!ServerInstance->IsIdent(parameters[0].c_str()))
- {
- /*
- * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
- * -- Craig, and then w00t.
- */
- user->WriteNumeric(461, "%s USER :Your username is not valid",user->nick.c_str());
- return CMD_FAILURE;
- }
- else
- {
- /*
- * The ident field is IDENTMAX+2 in size to account for +1 for the optional
- * ~ character, and +1 for null termination, therefore we can safely use up to
- * IDENTMAX here.
- */
- user->ChangeIdent(parameters[0].c_str());
- user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
- user->registered = (user->registered | REG_USER);
- }
- }
- else
- {
- user->WriteNumeric(462, "%s :You may not reregister", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- /* parameters 2 and 3 are local and remote hosts, and are ignored */
- if (user->registered == REG_NICKUSER)
- {
- ModResult MOD_RESULT;
-
- /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
- FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUser)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /USERHOST. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandUserhost : public Command
-{
- public:
- /** Constructor for userhost.
- */
- CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1, 5) {
- syntax = "<nick> [<nick> ...]";
- }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string retbuf = "302 " + user->nick + " :";
-
- for (unsigned int i = 0; i < parameters.size(); i++)
- {
- User *u = ServerInstance->FindNickOnly(parameters[i]);
-
- if ((u) && (u->registered == REG_ALL))
- {
- retbuf = retbuf + u->nick;
-
- if (IS_OPER(u))
- retbuf = retbuf + "*";
-
- retbuf = retbuf + "=";
-
- if (IS_AWAY(u))
- retbuf += "-";
- else
- retbuf += "+";
-
- retbuf = retbuf + u->ident + "@";
-
- if (user->HasPrivPermission("users/auspex"))
- {
- retbuf = retbuf + u->host;
- }
- else
- {
- retbuf = retbuf + u->dhost;
- }
-
- retbuf = retbuf + " ";
- }
- }
-
- user->WriteServ(retbuf);
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandUserhost)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /VERSION. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandVersion : public Command
-{
- public:
- /** Constructor for version.
- */
- CommandVersion ( Module* parent) : Command(parent,"VERSION",0,0) { syntax = "[<servername>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
-{
- std::string version = ServerInstance->GetVersionString(IS_OPER(user));
- user->WriteNumeric(RPL_VERSION, "%s :%s", user->nick.c_str(), version.c_str());
- ServerInstance->Config->Send005(user);
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandVersion)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WALLOPS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWallops : public Command
-{
- public:
- /** Constructor for wallops.
- */
- CommandWallops ( Module* parent) : Command(parent,"WALLOPS",1,1) { flags_needed = 'o'; syntax = "<any-text>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string wallop("WALLOPS :");
- wallop.append(parameters[0]);
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
- {
- User* t = *i;
- if (t->IsModeSet('w'))
- user->WriteTo(t,wallop);
- }
-
- FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWallops)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WHO. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWho : public Command
-{
- bool CanView(Channel* chan, User* user);
- bool opt_viewopersonly;
- bool opt_showrealhost;
- bool opt_realname;
- bool opt_mode;
- bool opt_ident;
- bool opt_metadata;
- bool opt_port;
- bool opt_away;
- bool opt_local;
- bool opt_far;
- bool opt_time;
-
- public:
- /** Constructor for who.
- */
- CommandWho ( Module* parent) : Command(parent,"WHO", 1) {
- syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
- }
- void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults);
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- bool whomatch(User* cuser, User* user, const char* matchtext);
-};
-
-
-static Channel* get_first_visible_channel(User *u)
-{
- UCListIter i = u->chans.begin();
- while (i != u->chans.end())
- {
- Channel* c = *i++;
- if (!c->IsModeSet('s'))
- return c;
- }
- return NULL;
-}
-
-bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
-{
- bool match = false;
- bool positive = false;
-
- if (user->registered != REG_ALL)
- return false;
-
- if (opt_local && !IS_LOCAL(user))
- return false;
- else if (opt_far && IS_LOCAL(user))
- return false;
-
- if (opt_mode)
- {
- for (const char* n = matchtext; *n; n++)
- {
- if (*n == '+')
- {
- positive = true;
- continue;
- }
- else if (*n == '-')
- {
- positive = false;
- continue;
- }
- if (user->IsModeSet(*n) != positive)
- return false;
- }
- return true;
- }
- else
- {
- /*
- * This was previously one awesome pile of ugly nested if, when really, it didn't need
- * to be, since only one condition was ever checked, a chained if works just fine.
- * -- w00t
- */
- if (opt_metadata)
- {
- match = false;
- const Extensible::ExtensibleStore& list = user->GetExtList();
- for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
- if (InspIRCd::Match(i->first->name, matchtext))
- match = true;
- }
- else if (opt_realname)
- match = InspIRCd::Match(user->fullname, matchtext);
- else if (opt_showrealhost)
- match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
- else if (opt_ident)
- match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
- else if (opt_port)
- {
- irc::portparser portrange(matchtext, false);
- long portno = -1;
- while ((portno = portrange.GetToken()))
- if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
- {
- match = true;
- break;
- }
- }
- else if (opt_away)
- match = InspIRCd::Match(user->awaymsg, matchtext);
- else if (opt_time)
- {
- long seconds = ServerInstance->Duration(matchtext);
-
- // Okay, so time matching, we want all users connected `seconds' ago
- if (user->age >= ServerInstance->Time() - seconds)
- match = true;
- }
-
- /*
- * Once the conditionals have been checked, only check dhost/nick/server
- * if they didn't match this user -- and only match if we don't find a match.
- *
- * This should make things minutely faster, and again, less ugly.
- * -- w00t
- */
- if (!match)
- match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
-
- if (!match)
- match = InspIRCd::Match(user->nick, matchtext);
-
- /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
- if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
- match = InspIRCd::Match(user->server, matchtext);
-
- return match;
- }
-}
-
-bool CommandWho::CanView(Channel* chan, User* user)
-{
- if (!user || !chan)
- return false;
-
- /* Bug #383 - moved higher up the list, because if we are in the channel
- * we can see all its users
- */
- if (chan->HasUser(user))
- return true;
- /* Opers see all */
- if (user->HasPrivPermission("users/auspex"))
- return true;
- /* Cant see inside a +s or a +p channel unless we are a member (see above) */
- else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
- return true;
-
- return false;
-}
-
-void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults)
-{
- if (!ch)
- ch = get_first_visible_channel(u);
-
- std::string wholine = initial + (ch ? ch->name : "*") + " " + u->ident + " " +
- (opt_showrealhost ? u->host : u->dhost) + " ";
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- wholine.append(ServerInstance->Config->HideWhoisServer);
- else
- wholine.append(u->server);
-
- wholine.append(" " + u->nick + " ");
-
- /* away? */
- if (IS_AWAY(u))
- {
- wholine.append("G");
- }
- else
- {
- wholine.append("H");
- }
-
- /* oper? */
- if (IS_OPER(u))
- {
- wholine.push_back('*');
- }
-
- if (ch)
- wholine.append(ch->GetPrefixChar(u));
-
- wholine.append(" :0 " + u->fullname);
-
- FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, parms, u, wholine));
-
- if (!wholine.empty())
- whoresults.push_back(wholine);
-}
-
-CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
-{
- /*
- * XXX - RFC says:
- * The <name> passed to WHO is matched against users' host, server, real
- * name and nickname
- * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
- */
-
- /* WHO options */
- opt_viewopersonly = false;
- opt_showrealhost = false;
- opt_realname = false;
- opt_mode = false;
- opt_ident = false;
- opt_metadata = false;
- opt_port = false;
- opt_away = false;
- opt_local = false;
- opt_far = false;
- opt_time = false;
-
- Channel *ch = NULL;
- std::vector<std::string> whoresults;
- std::string initial = "352 " + user->nick + " ";
-
- char matchtext[MAXBUF];
- bool usingwildcards = false;
-
- /* Change '0' into '*' so the wildcard matcher can grok it */
- if (parameters[0] == "0")
- strlcpy(matchtext, "*", MAXBUF);
- else
- strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
-
- for (const char* check = matchtext; *check; check++)
- {
- if (*check == '*' || *check == '?' || *check == '.')
- {
- usingwildcards = true;
- break;
- }
- }
-
- if (parameters.size() > 1)
- {
- /* Fix for bug #444, WHO flags count as a wildcard */
- usingwildcards = true;
-
- for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
- {
- switch (*iter)
- {
- case 'o':
- opt_viewopersonly = true;
- break;
- case 'h':
- if (user->HasPrivPermission("users/auspex"))
- opt_showrealhost = true;
- break;
- case 'r':
- opt_realname = true;
- break;
- case 'm':
- if (user->HasPrivPermission("users/auspex"))
- opt_mode = true;
- break;
- case 'M':
- if (user->HasPrivPermission("users/auspex"))
- opt_metadata = true;
- break;
- case 'i':
- opt_ident = true;
- break;
- case 'p':
- if (user->HasPrivPermission("users/auspex"))
- opt_port = true;
- break;
- case 'a':
- opt_away = true;
- break;
- case 'l':
- if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
- opt_local = true;
- break;
- case 'f':
- if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
- opt_far = true;
- break;
- case 't':
- opt_time = true;
- break;
- }
- }
- }
-
-
- /* who on a channel? */
- ch = ServerInstance->FindChan(matchtext);
-
- if (ch)
- {
- if (CanView(ch,user))
- {
- bool inside = ch->HasUser(user);
-
- /* who on a channel. */
- const UserMembList *cu = ch->GetUsers();
-
- for (UserMembCIter i = cu->begin(); i != cu->end(); i++)
- {
- /* None of this applies if we WHO ourselves */
- if (user != i->first)
- {
- /* opers only, please */
- if (opt_viewopersonly && !IS_OPER(i->first))
- continue;
-
- /* If we're not inside the channel, hide +i users */
- if (i->first->IsModeSet('i') && !inside && !user->HasPrivPermission("users/auspex"))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, ch, i->first, whoresults);
- }
- }
- }
- else
- {
- /* Match against wildcard of nick, server or host */
- if (opt_viewopersonly)
- {
- /* Showing only opers */
- for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
- {
- User* oper = *i;
-
- if (whomatch(user, oper, matchtext))
- {
- if (!user->SharesChannelWith(oper))
- {
- if (usingwildcards && (oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
- }
- }
- }
- else
- {
- for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
- {
- if (whomatch(user, i->second, matchtext))
- {
- if (!user->SharesChannelWith(i->second))
- {
- if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
- continue;
- }
-
- SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
- }
- }
- }
- }
- /* Send the results out */
- for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
- user->WriteServ(*n);
- user->WriteNumeric(315, "%s %s :End of /WHO list.",user->nick.c_str(), *parameters[0].c_str() ? parameters[0].c_str() : "*");
-
- // Penalize the user a bit for large queries
- // (add one unit of penalty per 200 results)
- if (IS_LOCAL(user))
- IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandWho)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /WHOIS. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandWhois : public Command
-{
- public:
- /** Constructor for whois.
- */
- CommandWhois ( Module* parent) : Command(parent,"WHOIS",1) { Penalty = 2; syntax = "<nick>{,<nick>}"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-
-CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- int userindex = 0;
- unsigned long idle = 0, signon = 0;
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
-
-
- /*
- * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
- * does, and use the second one, otherwise, use the only paramter. -- djGrrr
- */
- if (parameters.size() > 1)
- userindex = 1;
-
- if (IS_LOCAL(user))
- dest = ServerInstance->FindNickOnly(parameters[userindex]);
- else
- dest = ServerInstance->FindNick(parameters[userindex]);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- /*
- * Okay. Umpteenth attempt at doing this, so let's re-comment...
- * For local users (/w localuser), we show idletime if hidewhois is disabled
- * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
- * For remote users (/w remoteuser), we do NOT show idletime
- * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
- * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
- */
- if (IS_LOCAL(dest) && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
- {
- idle = labs((long)((dest->idle_lastmsg)-ServerInstance->Time()));
- signon = dest->signon;
- }
-
- ServerInstance->DoWhois(user,dest,signon,idle,parameters[userindex].c_str());
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
- user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-}
-
-
-
-COMMAND_INIT(CommandWhois)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "commands/cmd_whowas.h"
-
-WhoWasMaintainTimer * timer;
-
-CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
-{
- syntax = "<nick>{,<nick>}";
- Penalty = 2;
- timer = new WhoWasMaintainTimer(3600);
- ServerInstance->Timers->AddTimer(timer);
-}
-
-CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
-{
- /* if whowas disabled in config */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
- {
- user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
- return CMD_FAILURE;
- }
-
- whowas_users::iterator i = whowas.find(assign(parameters[0]));
-
- if (i == whowas.end())
- {
- user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
- }
- else
- {
- whowas_set* grp = i->second;
- if (grp->size())
- {
- for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
- {
- WhoWasGroup* u = *ux;
-
- user->WriteNumeric(314, "%s %s %s %s * :%s",user->nick.c_str(),parameters[0].c_str(),
- u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
-
- if (user->HasPrivPermission("users/auspex"))
- user->WriteNumeric(379, "%s %s :was connecting from *@%s",
- user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
-
- std::string signon = ServerInstance->TimeString(u->signon);
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
- else
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
- }
- }
- else
- {
- user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
-
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_SUCCESS;
-}
-
-std::string CommandWhowas::GetStats()
-{
- int whowas_size = 0;
- int whowas_bytes = 0;
- whowas_users_fifo::iterator iter;
- for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
- {
- whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
- if (n->size())
- {
- whowas_size += n->size();
- whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
- }
- }
- return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
-}
-
-void CommandWhowas::AddToWhoWas(User* user)
-{
- /* if whowas disabled */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
- {
- return;
- }
-
- whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
-
- if (iter == whowas.end())
- {
- whowas_set* n = new whowas_set;
- WhoWasGroup *a = new WhoWasGroup(user);
- n->push_back(a);
- whowas[user->nick.c_str()] = n;
- whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
-
- if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
- {
- whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
- if (iter2 != whowas.end())
- {
- whowas_set* n2 = (whowas_set*)iter2->second;
-
- if (n2->size())
- {
- while (n2->begin() != n2->end())
- {
- WhoWasGroup *a2 = *(n2->begin());
- delete a2;
- n2->pop_front();
- }
- }
-
- delete n2;
- whowas.erase(iter2);
- }
- whowas_fifo.pop_front();
- }
- }
- else
- {
- whowas_set* group = (whowas_set*)iter->second;
- WhoWasGroup *a = new WhoWasGroup(user);
- group->push_back(a);
-
- if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
- {
- WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
- delete a2;
- group->pop_front();
- }
- }
-}
-
-/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::PruneWhoWas(time_t t)
-{
- /* config values */
- int groupsize = ServerInstance->Config->WhoWasGroupSize;
- int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
- int maxkeep = ServerInstance->Config->WhoWasMaxKeep;
-
- /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
- }
- else
- break;
- }
-
- /* Then cut the whowas sets to new size (groupsize) */
- fifosize = (int)whowas_fifo.size();
- for (int i = 0; i < fifosize; i++)
- {
- iter = whowas.find(whowas_fifo[0].second);
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
- return;
- }
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- int nickcount = n->size();
- while (n->begin() != n->end() && nickcount > groupsize)
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- nickcount--;
- }
- }
- }
-}
-
-/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::MaintainWhoWas(time_t t)
-{
- for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
- {
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->erase(n->begin());
- }
- }
- }
-}
-
-CommandWhowas::~CommandWhowas()
-{
- if (timer)
- {
- ServerInstance->Timers->DelTimer(timer);
- }
-
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
- }
-}
-
-WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ident(user->ident),
- server(user->server), gecos(user->fullname), signon(user->signon)
-{
-}
-
-WhoWasGroup::~WhoWasGroup()
-{
-}
-
-/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
-void WhoWasMaintainTimer::Tick(time_t)
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
- }
-}
-
-class ModuleWhoWas : public Module
-{
- CommandWhowas cmd;
- public:
- ModuleWhoWas() : cmd(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- void OnRequest(Request& request)
- {
- WhowasRequest& req = static_cast<WhowasRequest&>(request);
- switch (req.type)
- {
- case WhowasRequest::WHOWAS_ADD:
- cmd.AddToWhoWas(req.user);
- break;
- case WhowasRequest::WHOWAS_STATS:
- req.value = cmd.GetStats();
- break;
- case WhowasRequest::WHOWAS_PRUNE:
- cmd.PruneWhoWas(ServerInstance->Time());
- break;
- case WhowasRequest::WHOWAS_MAINTAIN:
- cmd.MaintainWhoWas(ServerInstance->Time());
- break;
- }
- }
-
- Version GetVersion()
- {
- return Version("WHOWAS Command", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleWhoWas)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2009 Matt Smith <dz@inspircd.org>
- * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-/** Handle /ZLINE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandZline : public Command
-{
- public:
- /** Constructor for zline.
- */
- CommandZline ( Module* parent) : Command(parent,"ZLINE",1,3) { flags_needed = 'o'; Penalty = 0; syntax = "<ipmask> [<duration> :<reason>]"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User *user)
-{
- std::string target = parameters[0];
-
- if (parameters.size() >= 3)
- {
- if (target.find('!') != std::string::npos)
- {
- user->WriteServ("NOTICE %s :*** You cannot include a nickname in a zline, a zline must ban only an IP mask",user->nick.c_str());
- return CMD_FAILURE;
- }
-
- User *u = ServerInstance->FindNick(target);
-
- if ((u) && (u->registered == REG_ALL))
- {
- target = u->GetIPString();
- }
-
- const char* ipaddr = target.c_str();
-
- if (strchr(ipaddr,'@'))
- {
- while (*ipaddr != '@')
- ipaddr++;
- ipaddr++;
- }
-
- if (ServerInstance->IPMatchesEveryone(ipaddr,user))
- return CMD_FAILURE;
-
- long duration = ServerInstance->Duration(parameters[1].c_str());
-
- ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
- if (ServerInstance->XLines->AddLine(zl,user))
- {
- if (!duration)
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s: %s", user->nick.c_str(), ipaddr, parameters[2].c_str());
- }
- else
- {
- time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
- ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s: %s",user->nick.c_str(),ipaddr,
- timestr.c_str(), parameters[2].c_str());
- }
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete zl;
- user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick.c_str(),ipaddr);
- }
- }
- else
- {
- if (ServerInstance->XLines->DelLine(target.c_str(),"Z",user))
- {
- ServerInstance->SNO->WriteToSnoMask('x',"%s removed Z-line on %s",user->nick.c_str(),target.c_str());
- }
- else
- {
- user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick.c_str(),target.c_str());
- return CMD_FAILURE;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandZline)
while (1)
{
ch = next();
- if (ch == '&' && (flags & FLAG_USE_XML))
+ if (ch == '&' && !(flags & FLAG_USE_COMPAT))
{
std::string varname;
while (1)
{
ch = next();
- if (isalnum(ch))
+ if (isalnum(ch) || (varname.empty() && ch == '#'))
varname.push_back(ch);
else if (ch == ';')
break;
throw CoreException("Parse error");
}
}
- std::map<std::string, std::string>::iterator var = stack.vars.find(varname);
- if (var == stack.vars.end())
- throw CoreException("Undefined XML entity reference '&" + varname + ";'");
- value.append(var->second);
+ if (varname.empty())
+ throw CoreException("Empty XML entity reference");
+ else if (varname[0] == '#' && (varname.size() == 1 || (varname.size() == 2 && varname[1] == 'x')))
+ throw CoreException("Empty numeric character reference");
+ else if (varname[0] == '#')
+ {
+ const char* cvarname = varname.c_str();
+ char* endptr;
+ unsigned long lvalue;
+ if (cvarname[1] == 'x')
+ lvalue = strtoul(cvarname + 2, &endptr, 16);
+ else
+ lvalue = strtoul(cvarname + 1, &endptr, 10);
+ if (*endptr != '\0' || lvalue > 255)
+ throw CoreException("Invalid numeric character reference '&" + varname + ";'");
+ value.push_back(static_cast<char>(lvalue));
+ }
+ else
+ {
+ std::map<std::string, std::string>::iterator var = stack.vars.find(varname);
+ if (var == stack.vars.end())
+ throw CoreException("Undefined XML entity reference '&" + varname + ";'");
+ value.append(var->second);
+ }
}
- else if (ch == '\\' && !(flags & FLAG_USE_XML))
+ else if (ch == '\\' && (flags & FLAG_USE_COMPAT))
{
int esc = next();
if (esc == 'n')
std::set<std::string> seen;
tag = ConfigTag::create(name, current.filename, current.line, items);
- while (kv(items, seen));
+ while (kv(items, seen))
+ {
+ // Do nothing here (silences a GCC warning).
+ }
if (name == mandatory_tag)
{
}
else if (name == "define")
{
- if (!(flags & FLAG_USE_XML))
+ if (flags & FLAG_USE_COMPAT)
throw CoreException("<define> tags may only be used in XML-style config (add <config format=\"xml\">)");
std::string varname = tag->getString("name");
std::string value = tag->getString("value");
{
std::string format = tag->getString("format");
if (format == "xml")
- flags |= FLAG_USE_XML;
+ flags &= ~FLAG_USE_COMPAT;
else if (format == "compat")
- flags &= ~FLAG_USE_XML;
+ flags |= FLAG_USE_COMPAT;
else if (!format.empty())
throw CoreException("Unknown configuration format " + format);
}
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", false))
flags |= FLAG_NO_EXEC;
- if (!ParseFile(name, flags, mandatorytag))
+ if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
throw CoreException("Included");
}
else if (tag->readString("executable", name))
flags |= FLAG_NO_INC;
if (tag->getBool("noexec", true))
flags |= FLAG_NO_EXEC;
- if (!ParseExec(name, flags, mandatorytag))
+ if (!ParseFile(name, flags, mandatorytag, true))
throw CoreException("Included");
}
}
if (exec && (flags & FLAG_NO_EXEC))
throw CoreException("Invalid <execfiles> tag in file included with noexec=\"yes\"");
- FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(name.c_str(), "r"), exec);
+ std::string path = ServerInstance->Config->Paths.PrependConfig(name);
+ FileWrapper file(exec ? popen(name.c_str(), "r") : fopen(path.c_str(), "r"), exec);
if (!file)
- throw CoreException("Could not read \"" + name + "\" for \"" + key + "\" file");
+ throw CoreException("Could not read \"" + path + "\" for \"" + key + "\" file");
file_cache& cache = FilesOutput[key];
cache.clear();
- char linebuf[MAXBUF*10];
+ char linebuf[5120];
while (fgets(linebuf, sizeof(linebuf), file))
{
size_t len = strlen(linebuf);
}
}
-bool ParseStack::ParseFile(const std::string& name, int flags, const std::string& mandatory_tag)
-{
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading file %s", name.c_str());
- for (unsigned int t = 0; t < reading.size(); t++)
- {
- if (std::string(name) == reading[t])
- {
- throw CoreException("File " + name + " is included recursively (looped inclusion)");
- }
- }
-
- /* It's not already included, add it to the list of files we've loaded */
-
- FileWrapper file(fopen(name.c_str(), "r"));
- if (!file)
- throw CoreException("Could not read \"" + name + "\" for include");
-
- reading.push_back(name);
- Parser p(*this, flags, file, name, mandatory_tag);
- bool ok = p.outer_parse();
- reading.pop_back();
- return ok;
-}
-
-bool ParseStack::ParseExec(const std::string& name, int flags, const std::string& mandatory_tag)
+bool ParseStack::ParseFile(const std::string& path, int flags, const std::string& mandatory_tag, bool isexec)
{
- ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str());
- for (unsigned int t = 0; t < reading.size(); t++)
- {
- if (std::string(name) == reading[t])
- {
- throw CoreException("Executable " + name + " is included recursively (looped inclusion)");
- }
- }
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading (isexec=%d) %s", isexec, path.c_str());
+ if (std::find(reading.begin(), reading.end(), path) != reading.end())
+ throw CoreException((isexec ? "Executable " : "File ") + path + " is included recursively (looped inclusion)");
/* It's not already included, add it to the list of files we've loaded */
- FileWrapper file(popen(name.c_str(), "r"), true);
+ FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
if (!file)
- throw CoreException("Could not open executable \"" + name + "\" for include");
+ throw CoreException("Could not read \"" + path + "\" for include");
- reading.push_back(name);
- Parser p(*this, flags, file, name, mandatory_tag);
+ reading.push_back(path);
+ Parser p(*this, flags, file, path, mandatory_tag);
bool ok = p.outer_parse();
reading.pop_back();
return ok;
value = j->second;
if (!allow_lf && (value.find('\n') != std::string::npos))
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
" contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
for (std::string::iterator n = value.begin(); n != value.end(); n++)
if (*n == '\n')
return res;
}
-long ConfigTag::getInt(const std::string &key, long def)
+long ConfigTag::getInt(const std::string &key, long def, long min, long max)
{
std::string result;
if(!readString(key, result))
switch (toupper(*res_tail))
{
case 'K':
- res= res* 1024;
+ res = res * 1024;
break;
case 'M':
- res= res* 1024 * 1024;
+ res = res * 1024 * 1024;
break;
case 'G':
- res= res* 1024 * 1024 * 1024;
+ res = res * 1024 * 1024 * 1024;
break;
}
+
+ CheckRange(key, res, def, min, max);
return res;
}
+void ConfigTag::CheckRange(const std::string& key, long& res, long def, long min, long max)
+{
+ if (res < min || res > max)
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <%s:%s> value of %ld is not between %ld and %ld; set to %ld.",
+ tag.c_str(), key.c_str(), res, min, max, def);
+ res = def;
+ }
+}
+
+long ConfigTag::getDuration(const std::string& key, long def, long min, long max)
+{
+ std::string duration;
+ if (!readString(key, duration))
+ return def;
+
+ long ret = InspIRCd::Duration(duration);
+ CheckRange(key, ret, def, min, max);
+ return ret;
+}
+
double ConfigTag::getFloat(const std::string &key, double def)
{
std::string result;
if (result == "no" || result == "false" || result == "0" || result == "off")
return false;
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Value of <" + tag + ":" + key + "> at " + getTagLocation() +
" is not valid, ignoring");
return def;
}
#include "inspircd.h"
-#include <fstream>
#include "xline.h"
+#include "listmode.h"
#include "exitcodes.h"
-#include "commands/cmd_whowas.h"
#include "configparser.h"
#include <iostream>
-#ifdef _WIN32
-#include <Iphlpapi.h>
-#pragma comment(lib, "Iphlpapi.lib")
-#endif
ServerConfig::ServerConfig()
{
- WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
- RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
- WildcardIPv6 = CycleHosts = InvBypassModes = true;
+ RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
+ WildcardIPv6 = InvBypassModes = true;
dns_timeout = 5;
MaxTargets = 20;
NetBufferSize = 10240;
- SoftLimit = ServerInstance->SE->GetMaxFds();
MaxConn = SOMAXCONN;
MaxChans = 20;
OperMaxChans = 30;
delete EmptyTag;
}
-void ServerConfig::Update005()
-{
- std::stringstream out(data005);
- std::vector<std::string> data;
- std::string token;
- while (out >> token)
- data.push_back(token);
- sort(data.begin(), data.end());
-
- std::string line5;
- isupport.clear();
- for(unsigned int i=0; i < data.size(); i++)
- {
- token = data[i];
- line5 = line5 + token + " ";
- if (i % 13 == 12)
- {
- line5.append(":are supported by this server");
- isupport.push_back(line5);
- line5.clear();
- }
- }
- if (!line5.empty())
- {
- line5.append(":are supported by this server");
- isupport.push_back(line5);
- }
-}
-
-void ServerConfig::Send005(User* user)
-{
- for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
- user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
-}
-
-template<typename T, typename V>
-static void range(T& value, V min, V max, V def, const char* msg)
-{
- if (value >= (T)min && value <= (T)max)
- return;
- ServerInstance->Logs->Log("CONFIG", DEFAULT,
- "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
- msg, (long)value, (long)min, (long)max, (long)def);
- value = def;
-}
-
-
-static void ValidIP(const std::string& ip, const std::string& key)
-{
- irc::sockets::sockaddrs dummy;
- if (!irc::sockets::aptosa(ip, 0, dummy))
- throw CoreException("The value of "+key+" is not an IP address");
-}
-
static void ValidHost(const std::string& p, const std::string& msg)
{
int num_dots = 0;
std::string thiscmd;
/* Enable everything first */
- for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
x->second->Disable(false);
/* Now disable all the ones which the user wants disabled */
while (dcmds >> thiscmd)
{
- Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
- if (cm != ServerInstance->Parser->cmdlist.end())
- {
- cm->second->Disable(true);
- }
+ Command* handler = ServerInstance->Parser.GetHandler(thiscmd);
+ if (handler)
+ handler->Disable(true);
}
return true;
}
-static void FindDNS(std::string& server)
-{
- if (!server.empty())
- return;
-#ifdef _WIN32
- // attempt to look up their nameserver from the system
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
-
- PFIXED_INFO pFixedInfo;
- DWORD dwBufferSize = sizeof(FIXED_INFO);
- pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
-
- if(pFixedInfo)
- {
- if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) {
- HeapFree(GetProcessHeap(), 0, pFixedInfo);
- pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
- }
-
- if(pFixedInfo) {
- if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
- server = pFixedInfo->DnsServerList.IpAddress.String;
-
- HeapFree(GetProcessHeap(), 0, pFixedInfo);
- }
-
- if(!server.empty())
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
- return;
- }
- }
-
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
-#else
- // attempt to look up their nameserver from /etc/resolv.conf
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
-
- std::ifstream resolv("/etc/resolv.conf");
-
- while (resolv >> server)
- {
- if (server == "nameserver")
- {
- resolv >> server;
- if (server.find_first_not_of("0123456789.") == std::string::npos)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
- return;
- }
- }
- }
-
- ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
-#endif
- server = "127.0.0.1";
-}
-
static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
{
ConfigTagList tags = conf->ConfTags(tag);
std::string name = tag->getString("name");
if (name.empty())
throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
- if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
- throw CoreException("<type:name> is invalid (value '" + name + "')");
- if (oper_blocks.find(" " + name) != oper_blocks.end())
+ if (OperTypes.find(name) != OperTypes.end())
throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
OperInfo* ifo = new OperInfo;
- oper_blocks[" " + name] = ifo;
+ OperTypes[name] = ifo;
ifo->name = name;
ifo->type_block = tag;
throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
std::string type = tag->getString("type");
- OperIndex::iterator tblk = oper_blocks.find(" " + type);
- if (tblk == oper_blocks.end())
+ OperIndex::iterator tblk = OperTypes.find(type);
+ if (tblk == OperTypes.end())
throw CoreException("Oper block " + name + " has missing type " + type);
if (oper_blocks.find(name) != oper_blocks.end())
throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
{
ConnectClass* c = *i;
- if (c->name.substr(0, 8) != "unnamed-")
+ if (c->name.compare(0, 8, "unnamed-", 8))
{
oldBlocksByMask["n" + c->name] = c;
}
me->maxchans = tag->getInt("maxchans", me->maxchans);
me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
me->limit = tag->getInt("limit", me->limit);
+ me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
if (oldMask != oldBlocksByMask.end())
/** Represents a deprecated configuration tag.
*/
-struct Deprecated
+struct DeprecatedConfig
{
- /** Tag name
- */
- const char* tag;
- /** Tag value
- */
- const char* value;
- /** Reason for deprecation
- */
- const char* reason;
+ /** Tag name. */
+ std::string tag;
+
+ /** Attribute key. */
+ std::string key;
+
+ /** Attribute value. */
+ std::string value;
+
+ /** Reason for deprecation. */
+ std::string reason;
};
-static const Deprecated ChangedConfig[] = {
- {"options", "hidelinks", "has been moved to <security:hidelinks> as of 1.2a3"},
- {"options", "hidewhois", "has been moved to <security:hidewhois> as of 1.2a3"},
- {"options", "userstats", "has been moved to <security:userstats> as of 1.2a3"},
- {"options", "customversion", "has been moved to <security:customversion> as of 1.2a3"},
- {"options", "hidesplits", "has been moved to <security:hidesplits> as of 1.2a3"},
- {"options", "hidebans", "has been moved to <security:hidebans> as of 1.2a3"},
- {"options", "hidekills", "has been moved to <security:hidekills> as of 1.2a3"},
- {"options", "operspywhois", "has been moved to <security:operspywhois> as of 1.2a3"},
- {"options", "announceinvites", "has been moved to <security:announceinvites> as of 1.2a3"},
- {"options", "hidemodes", "has been moved to <security:hidemodes> as of 1.2a3"},
- {"options", "maxtargets", "has been moved to <security:maxtargets> as of 1.2a3"},
- {"options", "nouserdns", "has been moved to <performance:nouserdns> as of 1.2a3"},
- {"options", "maxwho", "has been moved to <performance:maxwho> as of 1.2a3"},
- {"options", "softlimit", "has been moved to <performance:softlimit> as of 1.2a3"},
- {"options", "somaxconn", "has been moved to <performance:somaxconn> as of 1.2a3"},
- {"options", "netbuffersize", "has been moved to <performance:netbuffersize> as of 1.2a3"},
- {"options", "maxwho", "has been moved to <performance:maxwho> as of 1.2a3"},
- {"options", "loglevel", "1.2+ does not use the loglevel value. Please define <log> tags instead."},
- {"die", "value", "you need to reread your config"},
- {"bind", "transport", "has been moved to <bind:ssl> as of 2.0a1"},
- {"link", "transport", "has been moved to <link:ssl> as of 2.0a1"},
- {"link", "autoconnect", "2.0+ does not use the autoconnect value. Please define <autoconnect> tags instead."},
+static const DeprecatedConfig ChangedConfig[] = {
+ { "bind", "transport", "", "has been moved to <bind:ssl> as of 2.0" },
+ { "die", "value", "", "you need to reread your config" },
+ { "gnutls", "starttls", "", "has been replaced with m_starttls as of 2.2" },
+ { "link", "autoconnect", "", "2.0+ does not use this attribute - define <autoconnect> tags instead" },
+ { "link", "transport", "", "has been moved to <link:ssl> as of 2.0" },
+ { "module", "name", "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
+ { "module", "name", "m_halfop.so", "has been replaced with m_customprefix as of 2.2" },
+ { "options", "cyclehosts", "", "has been replaced with m_hostcycle as of 2.2" },
+ { "performance", "nouserdns", "", "has been moved to <connect:resolvehostnames> as of 2.2" }
};
void ServerConfig::Fill()
ConfigTag* security = ConfValue("security");
if (sid.empty())
{
- ServerName = ConfValue("server")->getString("name");
- sid = ConfValue("server")->getString("id");
+ ServerName = ConfValue("server")->getString("name", "irc.example.com");
ValidHost(ServerName, "<server:name>");
- if (!sid.empty() && !ServerInstance->IsSID(sid))
+
+ sid = ConfValue("server")->getString("id");
+ if (!sid.empty() && !InspIRCd::IsSID(sid))
throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
}
else
{
if (ServerName != ConfValue("server")->getString("name"))
- throw CoreException("You must restart to change the server name or SID");
+ throw CoreException("You must restart to change the server name");
+
std::string nsid = ConfValue("server")->getString("id");
if (!nsid.empty() && nsid != sid)
- throw CoreException("You must restart to change the server name or SID");
- }
- diepass = ConfValue("power")->getString("diepass");
- restartpass = ConfValue("power")->getString("restartpass");
- powerhash = ConfValue("power")->getString("hash");
- PrefixQuit = options->getString("prefixquit");
- SuffixQuit = options->getString("suffixquit");
- FixedQuit = options->getString("fixedquit");
- PrefixPart = options->getString("prefixpart");
- SuffixPart = options->getString("suffixpart");
- FixedPart = options->getString("fixedpart");
- SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
+ throw CoreException("You must restart to change the server id");
+ }
+ SoftLimit = ConfValue("performance")->getInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
+ CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
- MoronBanner = options->getString("moronbanner", "You're banned!");
+ XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
ServerDesc = ConfValue("server")->getString("description", "Configure Me");
Network = ConfValue("server")->getString("network", "Network");
- AdminName = ConfValue("admin")->getString("name", "");
- AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
- AdminNick = ConfValue("admin")->getString("nick", "admin");
- ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
- NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
+ NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
dns_timeout = ConfValue("dns")->getInt("timeout", 5);
DisabledCommands = ConfValue("disabled")->getString("commands", "");
DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
UserStats = security->getString("userstats");
- CustomVersion = security->getString("customversion", Network + " IRCd");
+ CustomVersion = security->getString("customversion");
HideSplits = security->getBool("hidesplits");
HideBans = security->getBool("hidebans");
HideWhoisServer = security->getString("hidewhois");
HideKillsServer = security->getString("hidekills");
RestrictBannedUsers = security->getBool("restrictbannedusers", true);
GenericOper = security->getBool("genericoper");
- NoUserDns = ConfValue("performance")->getBool("nouserdns");
SyntaxHints = options->getBool("syntaxhints");
- CycleHosts = options->getBool("cyclehosts");
CycleHostsFromUser = options->getBool("cyclehostsfromuser");
UndernetMsgPrefix = options->getBool("ircumsgprefix");
FullHostInTopic = options->getBool("hostintopic");
- MaxTargets = security->getInt("maxtargets", 20);
- DefaultModes = options->getString("defaultmodes", "nt");
+ MaxTargets = security->getInt("maxtargets", 20, 1, 31);
+ DefaultModes = options->getString("defaultmodes", "not");
PID = ConfValue("pid")->getString("file");
- WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
- WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
- WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
MaxChans = ConfValue("channels")->getInt("users", 20);
- OperMaxChans = ConfValue("channels")->getInt("opers", 60);
+ OperMaxChans = ConfValue("channels")->getInt("opers");
c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
+ Limits.MaxHost = ConfValue("limits")->getInt("maxhost", 64);
Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
+ Limits.MaxLine = ConfValue("limits")->getInt("maxline", 512);
+ Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
+ Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
+ Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
+ Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
InvBypassModes = options->getBool("invitebypassmodes", true);
NoSnoticeStack = options->getBool("nosnoticestack", false);
- WelcomeNotice = options->getBool("welcomenotice", true);
-
- range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
- if (ConfValue("performance")->getBool("limitsomaxconn", true))
- range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
- range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
- range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
- range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
- range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
- range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
- ValidIP(DNSServer, "<dns:server>");
+ if (Network.find(' ') != std::string::npos)
+ throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
std::string defbind = options->getString("defaultbind");
if (assign(defbind) == "ipv4")
if (socktest < 0)
WildcardIPv6 = false;
else
- ServerInstance->SE->Close(socktest);
- }
- ConfigTagList tags = ConfTags("uline");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string server;
- if (!tag->readString("server", server))
- throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
- ulines[assign(server)] = tag->getBool("silent");
- }
-
- tags = ConfTags("banlist");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string chan;
- if (!tag->readString("chan", chan))
- throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
- maxbans[chan] = tag->getInt("limit");
+ SocketEngine::Close(socktest);
}
ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
DisabledCModes[*p - 'A'] = 1;
}
- memset(HideModeLists, 0, sizeof(HideModeLists));
- modes = ConfValue("security")->getString("hidemodes");
- for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
- HideModeLists[(unsigned char) *p] = true;
-
std::string v = security->getString("announceinvites");
if (v == "ops")
catch (CoreException& err)
{
valid = false;
- errstr << err.GetReason();
- }
- if (valid)
- {
- DNSServer = ConfValue("dns")->getString("server");
- FindDNS(DNSServer);
+ errstr << err.GetReason() << std::endl;
}
}
/* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
try
{
- for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
+ for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
{
- std::string dummy;
- ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
+ std::string value;
+ ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
for(ConfigIter i = tags.first; i != tags.second; ++i)
{
- if (i->second->readString(ChangedConfig[Index].value, dummy, true))
- errstr << "Your configuration contains a deprecated value: <"
- << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
- << " (at " << i->second->getTagLocation() << ")\n";
+ if (i->second->readString(ChangedConfig[index].key, value, true)
+ && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
+ {
+ errstr << "Your configuration contains a deprecated value: <" << ChangedConfig[index].tag;
+ if (ChangedConfig[index].value.empty())
+ {
+ errstr << ':' << ChangedConfig[index].key;
+ }
+ else
+ {
+ errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
+ }
+ errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
+ }
}
}
if (valid)
ServerInstance->WritePID(this->PID);
+ ConfigTagList binds = ConfTags("bind");
+ if (binds.first == binds.second)
+ errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
+ << "You will need to do this if you want clients to be able to connect!" << std::endl;
+
if (old)
{
// On first run, ports are bound later on
ServerInstance->BindPorts(pl);
if (pl.size())
{
- errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
+ errstr << "Not all your client ports could be bound." << std::endl
+ << "The following port(s) failed to bind:" << std::endl;
int j = 1;
for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
{
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "%d. Address: %s Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
- errstr << buf;
+ errstr << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first.c_str()) << "\tReason: "
+ << i->second << std::endl;
}
}
}
if (!valid)
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
Classes.clear();
}
ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
if (file != this->Files.end())
InspIRCd::ProcessColors(file->second);
-
- file = this->Files.find(tag->getString("rules", "rules"));
- if (file != this->Files.end())
- InspIRCd::ProcessColors(file->second);
}
/* No old configuration -> initial boot, nothing more to do here */
void ServerConfig::ApplyModules(User* user)
{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
-
- const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
std::vector<std::string> added_modules;
- std::set<std::string> removed_modules(v.begin(), v.end());
+ ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
ConfigTagList tags = ConfTags("module");
for(ConfigIter i = tags.first; i != tags.second; ++i)
}
}
- if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
- added_modules.push_back("m_halfop.so");
-
- for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
+ for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
{
- // Don't remove cmd_*.so, just remove m_*.so
- if (removing->c_str()[0] == 'c')
+ const std::string& modname = i->first;
+ // Don't remove core_*.so, just remove m_*.so
+ if (modname.c_str()[0] == 'c')
continue;
- Module* m = ServerInstance->Modules->Find(*removing);
- if (m && ServerInstance->Modules->Unload(m))
+ if (ServerInstance->Modules->Unload(i->second))
{
- ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
if (user)
- user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module %s successfully unloaded.", modname.c_str(), modname.c_str());
else
- ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :Failed to unload module %s: %s", modname.c_str(), modname.c_str(), ServerInstance->Modules->LastError().c_str());
else
- ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
}
}
{
ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
if (user)
- user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %s successfully loaded.", adding->c_str(), adding->c_str());
else
ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
}
else
{
if (user)
- user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, "%s :Failed to load module %s: %s", adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
else
ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
}
}
}
-bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
-{
- return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
-}
-
ConfigTag* ServerConfig::ConfValue(const std::string &tag)
{
ConfigTagList found = config_data.equal_range(tag);
ConfigTag* rv = found.first->second;
found.first++;
if (found.first != found.second)
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
"(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
return rv;
}
return config_data.equal_range(tag);
}
-bool ServerConfig::FileExists(const char* file)
+std::string ServerConfig::Escape(const std::string& str, bool xml)
{
- struct stat sb;
- if (stat(file, &sb) == -1)
- return false;
-
- if ((sb.st_mode & S_IFDIR) > 0)
- return false;
-
- FILE *input = fopen(file, "r");
- if (input == NULL)
- return false;
- else
+ std::string escaped;
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
{
- fclose(input);
- return true;
+ switch (*it)
+ {
+ case '"':
+ escaped += xml ? """ : "\"";
+ break;
+ case '&':
+ escaped += xml ? "&" : "&";
+ break;
+ case '\\':
+ escaped += xml ? "\\" : "\\\\";
+ break;
+ default:
+ escaped += *it;
+ break;
+ }
}
-}
-
-const char* ServerConfig::CleanFilename(const char* name)
-{
- const char* p = name + strlen(name);
- while ((p != name) && (*p != '/') && (*p != '\\')) p--;
- return (p != name ? ++p : p);
-}
-
-const std::string& ServerConfig::GetSID()
-{
- return sid;
+ return escaped;
}
void ConfigReaderThread::Run()
void ConfigReaderThread::Finish()
{
ServerConfig* old = ServerInstance->Config;
- ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
+ ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
ServerInstance->Config = this->Config;
Config->Apply(old, TheUserUID);
*/
ServerInstance->XLines->CheckELines();
ServerInstance->XLines->ApplyLines();
- ServerInstance->Res->Rehash();
- ServerInstance->ResetMaxBans();
+ ChanModeReference ban(NULL, "ban");
+ static_cast<ListModeBase*>(*ban)->DoRehash();
Config->ApplyDisabledCommands(Config->DisabledCommands);
User* user = ServerInstance->FindNick(TheUserUID);
- FOREACH_MOD(I_OnRehash, OnRehash(user));
- ServerInstance->BuildISupport();
+
+ ConfigStatus status(user);
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ i->second->ReadConfig(status);
+
+ // The description of this server may have changed - update it for WHOIS etc.
+ ServerInstance->FakeClient->server->description = Config->ServerDesc;
+
+ ServerInstance->ISupport.Build();
ServerInstance->Logs->CloseLogs();
ServerInstance->Logs->OpenFileLogs();
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandInvite::CommandInvite(Module* parent)
+ : Command(parent, "INVITE", 0, 0)
+{
+ Penalty = 4;
+ syntax = "[<nick> <channel>]";
+}
+
+/** Handle /INVITE
+ */
+CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ ModResult MOD_RESULT;
+
+ if (parameters.size() >= 2)
+ {
+ User* u;
+ if (IS_LOCAL(user))
+ u = ServerInstance->FindNickOnly(parameters[0]);
+ else
+ u = ServerInstance->FindNick(parameters[0]);
+
+ Channel* c = ServerInstance->FindChan(parameters[1]);
+ time_t timeout = 0;
+ if (parameters.size() >= 3)
+ {
+ if (IS_LOCAL(user))
+ timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[2]);
+ else if (parameters.size() > 3)
+ timeout = ConvToInt(parameters[3]);
+ }
+
+ if ((!c) || (!u) || (u->registered != REG_ALL))
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[0].c_str() : parameters[1].c_str());
+ return CMD_FAILURE;
+ }
+
+ // Verify channel timestamp if the INVITE is coming from a remote server
+ if (!IS_LOCAL(user))
+ {
+ // Remote INVITE commands must carry a channel timestamp
+ if (parameters.size() < 3)
+ return CMD_INVALID;
+
+ // Drop the invite if our channel TS is lower
+ time_t RemoteTS = ConvToInt(parameters[2]);
+ if (c->age < RemoteTS)
+ return CMD_FAILURE;
+ }
+
+ if ((IS_LOCAL(user)) && (!c->HasUser(user)))
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (c->HasUser(u))
+ {
+ user->WriteNumeric(ERR_USERONCHANNEL, "%s %s :is already on channel", u->nick.c_str(), c->name.c_str());
+ return CMD_FAILURE;
+ }
+
+ FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout));
+
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ return CMD_FAILURE;
+ }
+ else if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if (IS_LOCAL(user))
+ {
+ unsigned int rank = c->GetPrefixValue(user);
+ if (rank < HALFOP_VALUE)
+ {
+ // Check whether halfop mode is available and phrase error message accordingly
+ ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
+ c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : ""));
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ if (IS_LOCAL(u))
+ {
+ Invitation::Create(c, IS_LOCAL(u), timeout);
+ u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
+ }
+
+ if (IS_LOCAL(user))
+ user->WriteNumeric(RPL_INVITING, "%s %s", u->nick.c_str(),c->name.c_str());
+
+ if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
+ {
+ char prefix;
+ switch (ServerInstance->Config->AnnounceInvites)
+ {
+ case ServerConfig::INVITE_ANNOUNCE_OPS:
+ {
+ prefix = '@';
+ break;
+ }
+ case ServerConfig::INVITE_ANNOUNCE_DYNAMIC:
+ {
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+ prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@');
+ break;
+ }
+ default:
+ {
+ prefix = 0;
+ break;
+ }
+ }
+ c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
+ }
+ FOREACH_MOD(OnUserInvite, (user,u,c,timeout));
+ }
+ else if (IS_LOCAL(user))
+ {
+ // pinched from ircu - invite with not enough parameters shows channels
+ // youve been invited to but haven't joined yet.
+ InviteList& il = IS_LOCAL(user)->GetInviteList();
+ for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i)
+ {
+ user->WriteNumeric(RPL_INVITELIST, ":%s", (*i)->chan->name.c_str());
+ }
+ user->WriteNumeric(RPL_ENDOFINVITELIST, ":End of INVITE list");
+ }
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandInvite::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandJoin::CommandJoin(Module* parent)
+ : SplitCommand(parent, "JOIN", 1, 2)
+{
+ syntax = "<channel>{,<channel>} {<key>{,<key>}}";
+ Penalty = 2;
+}
+
+/** Handle /JOIN
+ */
+CmdResult CommandJoin::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
+{
+ if (parameters.size() > 1)
+ {
+ if (CommandParser::LoopCall(user, this, parameters, 0, 1, false))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ Channel::JoinUser(user, parameters[0], false, parameters[1]);
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ if (CommandParser::LoopCall(user, this, parameters, 0, -1, false))
+ return CMD_SUCCESS;
+
+ if (ServerInstance->IsChannel(parameters[0]))
+ {
+ Channel::JoinUser(user, parameters[0]);
+ return CMD_SUCCESS;
+ }
+ }
+
+ user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name", parameters[0].c_str());
+ return CMD_FAILURE;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandKick::CommandKick(Module* parent)
+ : Command(parent, "KICK", 2, 3)
+{
+ syntax = "<channel> <nick>{,<nick>} [<reason>]";
+}
+
+/** Handle /KICK
+ */
+CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+ User* u;
+
+ if (CommandParser::LoopCall(user, this, parameters, 1))
+ return CMD_SUCCESS;
+
+ if (IS_LOCAL(user))
+ u = ServerInstance->FindNickOnly(parameters[1]);
+ else
+ u = ServerInstance->FindNick(parameters[1]);
+
+ if ((!u) || (!c) || (u->registered != REG_ALL))
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[1].c_str() : parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ Membership* srcmemb = NULL;
+ if (IS_LOCAL(user))
+ {
+ srcmemb = c->GetUser(user);
+ if (!srcmemb)
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ if (u->server->IsULine())
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You may not kick a u-lined client", c->name.c_str());
+ return CMD_FAILURE;
+ }
+ }
+
+ const Channel::MemberMap::iterator victimiter = c->userlist.find(u);
+ if (victimiter == c->userlist.end())
+ {
+ user->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s :They are not on that channel", u->nick.c_str(), c->name.c_str());
+ return CMD_FAILURE;
+ }
+ Membership* const memb = victimiter->second;
+
+ // KICKs coming from servers can carry a membership id
+ if ((!IS_LOCAL(user)) && (parameters.size() > 3))
+ {
+ // If the current membership id is not equal to the one in the message then the user rejoined
+ if (memb->id != Membership::IdFromString(parameters[2]))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Dropped KICK due to membership id mismatch: " + ConvToStr(memb->id) + " != " + parameters[2]);
+ return CMD_FAILURE;
+ }
+ }
+
+ const bool has_reason = (parameters.size() > 2);
+ const std::string reason((has_reason ? parameters.back() : user->nick), 0, ServerInstance->Config->Limits.MaxKick);
+
+ // Do the following checks only if the KICK is done by a local user;
+ // each server enforces its own rules.
+ if (srcmemb)
+ {
+ // Modules are allowed to explicitly allow or deny kicks done by local users
+ ModResult res;
+ FIRST_MOD_RESULT(OnUserPreKick, res, (user, memb, reason));
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ if (res == MOD_RES_PASSTHRU)
+ {
+ unsigned int them = srcmemb->getRank();
+ unsigned int req = HALFOP_VALUE;
+ for (std::string::size_type i = 0; i < memb->modes.length(); i++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
+ if (mh && mh->GetLevelRequired() > req)
+ req = mh->GetLevelRequired();
+ }
+
+ if (them < req)
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
+ this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ c->KickUser(user, victimiter, reason);
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKick::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandNames::CommandNames(Module* parent)
+ : Command(parent, "NAMES", 0, 0)
+ , secretmode(parent, "secret")
+{
+ syntax = "{<channel>{,<channel>}}";
+}
+
+/** Handle /NAMES
+ */
+CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ Channel* c;
+
+ if (!parameters.size())
+ {
+ user->WriteNumeric(RPL_ENDOFNAMES, "* :End of /NAMES list.");
+ return CMD_SUCCESS;
+ }
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ c = ServerInstance->FindChan(parameters[0]);
+ if (c)
+ {
+ // Show the NAMES list if one of the following is true:
+ // - the channel is not secret
+ // - the user doing the /NAMES is inside the channel
+ // - the user doing the /NAMES has the channels/auspex privilege
+
+ bool has_user = c->HasUser(user);
+ if ((!c->IsModeSet(secretmode)) || (has_user) || (user->HasPrivPermission("channels/auspex")))
+ {
+ c->UserList(user, has_user);
+ return CMD_SUCCESS;
+ }
+ }
+
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+CommandTopic::CommandTopic(Module* parent)
+ : SplitCommand(parent, "TOPIC", 1, 2)
+ , secretmode(parent, "secret")
+ , topiclockmode(parent, "topiclock")
+{
+ syntax = "<channel> [<topic>]";
+ Penalty = 2;
+}
+
+CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+ if (!c)
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ if (parameters.size() == 1)
+ {
+ if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c->name.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (c->topic.length())
+ {
+ user->WriteNumeric(RPL_TOPIC, "%s :%s", c->name.c_str(), c->topic.c_str());
+ user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset);
+ }
+ else
+ {
+ user->WriteNumeric(RPL_NOTOPICSET, "%s :No topic is set.", c->name.c_str());
+ }
+ return CMD_SUCCESS;
+ }
+
+ std::string t = parameters[1]; // needed, in case a module wants to change it
+ ModResult res;
+ FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t));
+
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+ if (res != MOD_RES_ALLOW)
+ {
+ if (!c->HasUser(user))
+ {
+ user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str());
+ return CMD_FAILURE;
+ }
+ if (c->IsModeSet(topiclockmode) && !ServerInstance->OnCheckExemption(user, c, "topiclock").check(c->GetPrefixValue(user) >= HALFOP_VALUE))
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to change the topic on this channel", c->name.c_str());
+ return CMD_FAILURE;
+ }
+ }
+
+ c->SetTopic(user, t);
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_channel.h"
+
+class CoreModChannel : public Module
+{
+ CommandInvite cmdinvite;
+ CommandJoin cmdjoin;
+ CommandKick cmdkick;
+ CommandNames cmdnames;
+ CommandTopic cmdtopic;
+
+ public:
+ CoreModChannel()
+ : cmdinvite(this), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the INVITE, JOIN, KICK, NAMES, and TOPIC commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModChannel)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+/** Handle /INVITE.
+ */
+class CommandInvite : public Command
+{
+ public:
+ /** Constructor for invite.
+ */
+ CommandInvite (Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User*user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /JOIN.
+ */
+class CommandJoin : public SplitCommand
+{
+ public:
+ /** Constructor for join.
+ */
+ CommandJoin(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /TOPIC.
+ */
+class CommandTopic : public SplitCommand
+{
+ ChanModeReference secretmode;
+ ChanModeReference topiclockmode;
+
+ public:
+ /** Constructor for topic.
+ */
+ CommandTopic(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /NAMES.
+ */
+class CommandNames : public Command
+{
+ ChanModeReference secretmode;
+
+ public:
+ /** Constructor for names.
+ */
+ CommandNames(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+/** Handle /KICK.
+ */
+class CommandKick : public Command
+{
+ public:
+ /** Constructor for kick.
+ */
+ CommandKick(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "modules/dns.h"
+#include <iostream>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Iphlpapi.h>
+#pragma comment(lib, "Iphlpapi.lib")
+#endif
+
+using namespace DNS;
+
+/** A full packet sent or recieved to/from the nameserver
+ */
+class Packet : public Query
+{
+ void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
+ {
+ if (pos + name.length() + 2 > output_size)
+ throw Exception("Unable to pack name");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Packing name " + name);
+
+ irc::sepstream sep(name, '.');
+ std::string token;
+
+ while (sep.GetToken(token))
+ {
+ output[pos++] = token.length();
+ memcpy(&output[pos], token.data(), token.length());
+ pos += token.length();
+ }
+
+ output[pos++] = 0;
+ }
+
+ std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ std::string name;
+ unsigned short pos_ptr = pos, lowest_ptr = input_size;
+ bool compressed = false;
+
+ if (pos_ptr >= input_size)
+ throw Exception("Unable to unpack name - no input");
+
+ while (input[pos_ptr] > 0)
+ {
+ unsigned short offset = input[pos_ptr];
+
+ if (offset & POINTER)
+ {
+ if ((offset & POINTER) != POINTER)
+ throw Exception("Unable to unpack name - bogus compression header");
+ if (pos_ptr + 1 >= input_size)
+ throw Exception("Unable to unpack name - bogus compression header");
+
+ /* Place pos at the second byte of the first (farthest) compression pointer */
+ if (compressed == false)
+ {
+ ++pos;
+ compressed = true;
+ }
+
+ pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
+
+ /* Pointers can only go back */
+ if (pos_ptr >= lowest_ptr)
+ throw Exception("Unable to unpack name - bogus compression pointer");
+ lowest_ptr = pos_ptr;
+ }
+ else
+ {
+ if (pos_ptr + offset + 1 >= input_size)
+ throw Exception("Unable to unpack name - offset too large");
+ if (!name.empty())
+ name += ".";
+ for (unsigned i = 1; i <= offset; ++i)
+ name += input[pos_ptr + i];
+
+ pos_ptr += offset + 1;
+ if (compressed == false)
+ /* Move up pos */
+ pos = pos_ptr;
+ }
+ }
+
+ /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
+ ++pos;
+
+ if (name.empty())
+ throw Exception("Unable to unpack name - no name");
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unpack name " + name);
+
+ return name;
+ }
+
+ Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ Question question;
+
+ question.name = this->UnpackName(input, input_size, pos);
+
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack question");
+
+ question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+ pos += 2;
+
+ question.qclass = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ return question;
+ }
+
+ ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
+
+ if (pos + 6 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
+ pos += 4;
+
+ //record.rdlength = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ switch (record.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in4.sin_family = AF_INET;
+ addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
+ pos += 4;
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 16 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in6.sin6_family = AF_INET6;
+ for (int j = 0; j < 16; ++j)
+ addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
+ pos += 16;
+
+ record.rdata = addrs.addr();
+
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ record.rdata = this->UnpackName(input, input_size, pos);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!record.name.empty() && !record.rdata.empty())
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
+
+ return record;
+ }
+
+ public:
+ static const int POINTER = 0xC0;
+ static const int LABEL = 0x3F;
+ static const int HEADER_LENGTH = 12;
+
+ /* ID for this packet */
+ unsigned short id;
+ /* Flags on the packet */
+ unsigned short flags;
+
+ Packet() : id(0), flags(0)
+ {
+ }
+
+ void Fill(const unsigned char* input, const unsigned short len)
+ {
+ if (len < HEADER_LENGTH)
+ throw Exception("Unable to fill packet");
+
+ unsigned short packet_pos = 0;
+
+ this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ if (this->id >= MAX_REQUEST_ID)
+ throw Exception("Query ID too large?");
+
+ this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
+
+ for (unsigned i = 0; i < qdcount; ++i)
+ this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
+
+ for (unsigned i = 0; i < ancount; ++i)
+ this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+ }
+
+ unsigned short Pack(unsigned char* output, unsigned short output_size)
+ {
+ if (output_size < HEADER_LENGTH)
+ throw Exception("Unable to pack packet");
+
+ unsigned short pos = 0;
+
+ output[pos++] = this->id >> 8;
+ output[pos++] = this->id & 0xFF;
+ output[pos++] = this->flags >> 8;
+ output[pos++] = this->flags & 0xFF;
+ output[pos++] = this->questions.size() >> 8;
+ output[pos++] = this->questions.size() & 0xFF;
+ output[pos++] = this->answers.size() >> 8;
+ output[pos++] = this->answers.size() & 0xFF;
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+
+ for (unsigned i = 0; i < this->questions.size(); ++i)
+ {
+ Question& q = this->questions[i];
+
+ if (q.type == QUERY_PTR)
+ {
+ irc::sockets::sockaddrs ip;
+ irc::sockets::aptosa(q.name, 0, ip);
+
+ if (q.name.find(':') != std::string::npos)
+ {
+ static const char* const hex = "0123456789abcdef";
+ char reverse_ip[128];
+ unsigned reverse_ip_count = 0;
+ for (int j = 15; j >= 0; --j)
+ {
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
+ reverse_ip[reverse_ip_count++] = '.';
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
+ reverse_ip[reverse_ip_count++] = '.';
+ }
+ reverse_ip[reverse_ip_count++] = 0;
+
+ q.name = reverse_ip;
+ q.name += "ip6.arpa";
+ }
+ else
+ {
+ unsigned long forward = ip.in4.sin_addr.s_addr;
+ ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
+
+ q.name = ip.addr() + ".in-addr.arpa";
+ }
+ }
+
+ this->PackName(output, output_size, pos, q.name);
+
+ if (pos + 4 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ short s = htons(q.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(q.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+ }
+
+ for (unsigned int i = 0; i < answers.size(); i++)
+ {
+ ResourceRecord& rr = answers[i];
+
+ this->PackName(output, output_size, pos, rr.name);
+
+ if (pos + 8 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ short s = htons(rr.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(rr.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ long l = htonl(rr.ttl);
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
+
+ switch (rr.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 6 > output_size)
+ throw Exception("Unable to pack packet");
+
+ irc::sockets::sockaddrs a;
+ irc::sockets::aptosa(rr.rdata, 0, a);
+
+ s = htons(4);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.in4.sin_addr, 4);
+ pos += 4;
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 18 > output_size)
+ throw Exception("Unable to pack packet");
+
+ irc::sockets::sockaddrs a;
+ irc::sockets::aptosa(rr.rdata, 0, a);
+
+ s = htons(16);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.in6.sin6_addr, 16);
+ pos += 16;
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ if (pos + 2 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ unsigned short packet_pos_save = pos;
+ pos += 2;
+
+ this->PackName(output, output_size, pos, rr.rdata);
+
+ s = htons(pos - packet_pos_save - 2);
+ memcpy(&output[packet_pos_save], &s, 2);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return pos;
+ }
+};
+
+class MyManager : public Manager, public Timer, public EventHandler
+{
+ typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
+ cache_map cache;
+
+ irc::sockets::sockaddrs myserver;
+
+ static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
+ {
+ const ResourceRecord& req = record.answers[0];
+ return (req.created + static_cast<time_t>(req.ttl) < now);
+ }
+
+ /** Check the DNS cache to see if request can be handled by a cached result
+ * @return true if a cached result was found.
+ */
+ bool CheckCache(DNS::Request* req, const DNS::Question& question)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "cache: Checking cache for " + question.name);
+
+ cache_map::iterator it = this->cache.find(question);
+ if (it == this->cache.end())
+ return false;
+
+ Query& record = it->second;
+ if (IsExpired(record))
+ {
+ this->cache.erase(it);
+ return false;
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
+ record.cached = true;
+ req->OnLookupComplete(&record);
+ return true;
+ }
+
+ /** Add a record to the dns cache
+ * @param r The record
+ */
+ void AddCache(Query& r)
+ {
+ const ResourceRecord& rr = r.answers[0];
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
+ this->cache[r.questions[0]] = r;
+ }
+
+ public:
+ DNS::Request* requests[MAX_REQUEST_ID];
+
+ MyManager(Module* c) : Manager(c), Timer(3600, true)
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ requests[i] = NULL;
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ ~MyManager()
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* request = requests[i];
+ if (!request)
+ continue;
+
+ Query rr(*request);
+ rr.error = ERROR_UNKNOWN;
+ request->OnError(&rr);
+
+ delete request;
+ }
+ }
+
+ void Process(DNS::Request* req)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr());
+
+ /* Create an id */
+ unsigned int tries = 0;
+ do
+ {
+ req->id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
+
+ if (++tries == DNS::MAX_REQUEST_ID*5)
+ {
+ // If we couldn't find an empty slot this many times, do a sequential scan as a last
+ // resort. If an empty slot is found that way, go on, otherwise throw an exception
+ req->id = 0;
+ for (int i = 1; i < DNS::MAX_REQUEST_ID; i++)
+ {
+ if (!this->requests[i])
+ {
+ req->id = i;
+ break;
+ }
+ }
+
+ if (req->id == 0)
+ throw Exception("DNS: All ids are in use");
+
+ break;
+ }
+ }
+ while (!req->id || this->requests[req->id]);
+
+ this->requests[req->id] = req;
+
+ Packet p;
+ p.flags = QUERYFLAGS_RD;
+ p.id = req->id;
+ p.questions.push_back(*req);
+
+ unsigned char buffer[524];
+ unsigned short len = p.Pack(buffer, sizeof(buffer));
+
+ /* Note that calling Pack() above can actually change the contents of p.questions[0].name, if the query is a PTR,
+ * to contain the value that would be in the DNS cache, which is why this is here.
+ */
+ if (req->use_cache && this->CheckCache(req, p.questions[0]))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Using cached result");
+ delete req;
+ return;
+ }
+
+ if (SocketEngine::SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len)
+ throw Exception("DNS: Unable to send query");
+ }
+
+ void RemoveRequest(DNS::Request* req)
+ {
+ this->requests[req->id] = NULL;
+ }
+
+ std::string GetErrorStr(Error e)
+ {
+ switch (e)
+ {
+ case ERROR_UNLOADED:
+ return "Module is unloading";
+ case ERROR_TIMEDOUT:
+ return "Request timed out";
+ case ERROR_NOT_AN_ANSWER:
+ case ERROR_NONSTANDARD_QUERY:
+ case ERROR_FORMAT_ERROR:
+ return "Malformed answer";
+ case ERROR_SERVER_FAILURE:
+ case ERROR_NOT_IMPLEMENTED:
+ case ERROR_REFUSED:
+ case ERROR_INVALIDTYPE:
+ return "Nameserver failure";
+ case ERROR_DOMAIN_NOT_FOUND:
+ case ERROR_NO_RECORDS:
+ return "Domain not found";
+ case ERROR_NONE:
+ case ERROR_UNKNOWN:
+ default:
+ return "Unknown error";
+ }
+ }
+
+ void HandleEvent(EventType et, int)
+ {
+ if (et == EVENT_ERROR)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "UDP socket got an error event");
+ return;
+ }
+
+ unsigned char buffer[524];
+ irc::sockets::sockaddrs from;
+ socklen_t x = sizeof(from);
+
+ int length = SocketEngine::RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
+
+ if (length < Packet::HEADER_LENGTH)
+ return;
+
+ Packet recv_packet;
+
+ try
+ {
+ recv_packet.Fill(buffer, length);
+ }
+ catch (Exception& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
+ return;
+ }
+
+ if (myserver != from)
+ {
+ std::string server1 = from.str();
+ std::string server2 = myserver.str();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
+ server1.c_str(), server2.c_str());
+ return;
+ }
+
+ DNS::Request* request = this->requests[recv_packet.id];
+ if (request == NULL)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
+ return;
+ }
+
+ if (recv_packet.flags & QUERYFLAGS_OPCODE)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received a nonstandard query");
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_NONSTANDARD_QUERY;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ Error error = ERROR_UNKNOWN;
+
+ switch (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ case 1:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
+ error = ERROR_FORMAT_ERROR;
+ break;
+ case 2:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
+ error = ERROR_SERVER_FAILURE;
+ break;
+ case 3:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
+ error = ERROR_DOMAIN_NOT_FOUND;
+ break;
+ case 4:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
+ error = ERROR_NOT_IMPLEMENTED;
+ break;
+ case 5:
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
+ error = ERROR_REFUSED;
+ break;
+ default:
+ break;
+ }
+
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = error;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.questions.empty() || recv_packet.answers.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "No resource records returned");
+ ServerInstance->stats.DnsBad++;
+ recv_packet.error = ERROR_NO_RECORDS;
+ request->OnError(&recv_packet);
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Lookup complete for " + request->name);
+ ServerInstance->stats.DnsGood++;
+ request->OnLookupComplete(&recv_packet);
+ this->AddCache(recv_packet);
+ }
+
+ ServerInstance->stats.Dns++;
+
+ /* Request's destructor removes it from the request map */
+ delete request;
+ }
+
+ bool Tick(time_t now)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
+
+ for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
+ {
+ const Query& query = it->second;
+ if (IsExpired(query, now))
+ this->cache.erase(it++);
+ else
+ ++it;
+ }
+ return true;
+ }
+
+ void Rehash(const std::string& dnsserver)
+ {
+ if (this->GetFd() > -1)
+ {
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this);
+
+ /* Remove expired entries from the cache */
+ this->Tick(ServerInstance->Time());
+ }
+
+ irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
+
+ /* Initialize mastersocket */
+ int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
+ this->SetFd(s);
+
+ /* Have we got a socket? */
+ if (this->GetFd() != -1)
+ {
+ SocketEngine::SetReuse(s);
+ SocketEngine::NonBlocking(s);
+
+ irc::sockets::sockaddrs bindto;
+ memset(&bindto, 0, sizeof(bindto));
+ bindto.sa.sa_family = myserver.sa.sa_family;
+
+ if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
+ {
+ /* Failed to bind */
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error binding dns socket - hostnames will NOT resolve");
+ SocketEngine::Close(this->GetFd());
+ this->SetFd(-1);
+ }
+ else if (!SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
+ SocketEngine::Close(this->GetFd());
+ this->SetFd(-1);
+ }
+ }
+ else
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
+ }
+ }
+};
+
+class ModuleDNS : public Module
+{
+ MyManager manager;
+ std::string DNSServer;
+
+ void FindDNSServer()
+ {
+#ifdef _WIN32
+ // attempt to look up their nameserver from the system
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
+
+ PFIXED_INFO pFixedInfo;
+ DWORD dwBufferSize = sizeof(FIXED_INFO);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
+ {
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
+ }
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
+ DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
+
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ }
+
+ if (!DNSServer.empty())
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
+ return;
+ }
+ }
+
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
+#else
+ // attempt to look up their nameserver from /etc/resolv.conf
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+
+ std::ifstream resolv("/etc/resolv.conf");
+
+ while (resolv >> DNSServer)
+ {
+ if (DNSServer == "nameserver")
+ {
+ resolv >> DNSServer;
+ if (DNSServer.find_first_not_of("0123456789.") == std::string::npos)
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
+ return;
+ }
+ }
+ }
+
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+#endif
+ DNSServer = "127.0.0.1";
+ }
+
+ public:
+ ModuleDNS() : manager(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ std::string oldserver = DNSServer;
+ DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
+ if (DNSServer.empty())
+ FindDNSServer();
+
+ if (oldserver != DNSServer)
+ this->manager.Rehash(DNSServer);
+ }
+
+ void OnUnloadModule(Module* mod)
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* req = this->manager.requests[i];
+ if (!req)
+ continue;
+
+ if (req->creator == mod)
+ {
+ Query rr(*req);
+ rr.error = ERROR_UNLOADED;
+ req->OnError(&rr);
+
+ delete req;
+ }
+ }
+ }
+
+ Version GetVersion()
+ {
+ return Version("DNS support", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleDNS)
+
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/dns.h"
+
+namespace
+{
+ LocalIntExt* dl;
+ LocalStringExt* ph;
+}
+
+/** Derived from Resolver, and performs user forward/reverse lookups.
+ */
+class UserResolver : public DNS::Request
+{
+ /** UUID we are looking up */
+ const std::string uuid;
+
+ /** True if the lookup is forward, false if is a reverse lookup
+ */
+ const bool fwd;
+
+ public:
+ /** Create a resolver.
+ * @param mgr DNS Manager
+ * @param me this module
+ * @param user The user to begin lookup on
+ * @param to_resolve The IP or host to resolve
+ * @param qt The query type
+ */
+ UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
+ : DNS::Request(mgr, me, to_resolve, qt)
+ , uuid(user->uuid)
+ , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
+ {
+ }
+
+ /** Called on successful lookup
+ * if a previous result has already come back.
+ * @param r The finished query
+ */
+ void OnLookupComplete(const DNS::Query* r)
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (!bound_user)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
+ return;
+ }
+
+ const DNS::ResourceRecord& ans_record = r->answers[0];
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record.name.c_str(), ans_record.rdata.c_str());
+
+ if (!fwd)
+ {
+ // first half of resolution is done. We now need to verify that the host matches.
+ ph->set(bound_user, ans_record.rdata);
+
+ UserResolver* res_forward;
+ if (bound_user->client_sa.sa.sa_family == AF_INET6)
+ {
+ /* IPV6 forward lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_AAAA);
+ }
+ else
+ {
+ /* IPV4 lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_A);
+ }
+ try
+ {
+ this->manager->Process(res_forward);
+ }
+ catch (DNS::Exception& e)
+ {
+ delete res_forward;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ }
+ }
+ else
+ {
+ /* Both lookups completed */
+
+ irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
+ bool rev_match = false;
+ if (user_ip->sa.sa_family == AF_INET6)
+ {
+ struct in6_addr res_bin;
+ if (inet_pton(AF_INET6, ans_record.rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+ else
+ {
+ struct in_addr res_bin;
+ if (inet_pton(AF_INET, ans_record.rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+
+ dl->set(bound_user, 0);
+
+ if (rev_match)
+ {
+ std::string* hostname = ph->get(bound_user);
+
+ if (hostname == NULL)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ return;
+ }
+ else if (hostname->length() <= ServerInstance->Config->Limits.MaxHost)
+ {
+ /* Hostnames starting with : are not a good thing (tm) */
+ if ((*hostname)[0] == ':')
+ hostname->insert(0, "0");
+
+ bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
+ bound_user->host.assign(*hostname, 0, ServerInstance->Config->Limits.MaxHost);
+ bound_user->dhost = bound_user->host;
+
+ /* Invalidate cache */
+ bound_user->InvalidateCache();
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname is longer than the maximum of " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+
+ ph->unset(bound_user);
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+ }
+ }
+
+ /** Called on failed lookup
+ * @param query The errored query
+ */
+ void OnError(const DNS::Query* query)
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (bound_user)
+ {
+ bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ ServerInstance->stats.DnsBad++;
+ }
+ }
+};
+
+class ModuleHostnameLookup : public Module
+{
+ LocalIntExt dnsLookup;
+ LocalStringExt ptrHosts;
+ dynamic_reference<DNS::Manager> DNS;
+
+ public:
+ ModuleHostnameLookup()
+ : dnsLookup("dnsLookup", this)
+ , ptrHosts("ptrHosts", this)
+ , DNS(this, "DNS")
+ {
+ dl = &dnsLookup;
+ ph = &ptrHosts;
+ }
+
+ void OnUserInit(LocalUser *user)
+ {
+ if (!DNS || !user->MyClass->resolvehostnames)
+ {
+ user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
+ return;
+ }
+
+ user->WriteNotice("*** Looking up your hostname...");
+
+ UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
+ try
+ {
+ /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
+ * before Process() completes, which is why dnsLookup.set() is here, before Process()
+ */
+ this->dnsLookup.set(user, 1);
+ this->DNS->Process(res_reverse);
+ }
+ catch (DNS::Exception& e)
+ {
+ this->dnsLookup.set(user, 0);
+ delete res_reverse;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
+ ServerInstance->stats.DnsBad++;
+ }
+ }
+
+ ModResult OnCheckReady(LocalUser* user)
+ {
+ return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion()
+ {
+ return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHostnameLookup)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandAdmin::CommandAdmin(Module* parent)
+ : Command(parent, "ADMIN", 0, 0)
+{
+ Penalty = 2;
+ syntax = "[<servername>]";
+}
+
+/** Handle /ADMIN
+ */
+CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+ user->SendText(":%s %03d %s :Administrative info for %s", ServerInstance->Config->ServerName.c_str(),
+ RPL_ADMINME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
+ if (!AdminName.empty())
+ user->SendText(":%s %03d %s :Name - %s", ServerInstance->Config->ServerName.c_str(),
+ RPL_ADMINLOC1, user->nick.c_str(), AdminName.c_str());
+ user->SendText(":%s %03d %s :Nickname - %s", ServerInstance->Config->ServerName.c_str(),
+ RPL_ADMINLOC2, user->nick.c_str(), AdminNick.c_str());
+ user->SendText(":%s %03d %s :E-Mail - %s", ServerInstance->Config->ServerName.c_str(),
+ RPL_ADMINEMAIL, user->nick.c_str(), AdminEmail.c_str());
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAdmin::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (parameters.size() > 0)
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandCommands::CommandCommands(Module* parent)
+ : Command(parent, "COMMANDS", 0, 0)
+{
+ Penalty = 3;
+}
+
+/** Handle /COMMANDS
+ */
+CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
+{
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ std::vector<std::string> list;
+ list.reserve(commands.size());
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ // Don't show S2S commands to users
+ if (i->second->flags_needed == FLAG_SERVERONLY)
+ continue;
+
+ Module* src = i->second->creator;
+ list.push_back(InspIRCd::Format(":%s %03d %s :%s %s %d %d", ServerInstance->Config->ServerName.c_str(),
+ RPL_COMMANDS, user->nick.c_str(), i->second->name.c_str(), src->ModuleSourceFile.c_str(),
+ i->second->min_params, i->second->Penalty));
+ }
+ std::sort(list.begin(), list.end());
+ for(unsigned int i=0; i < list.size(); i++)
+ user->Write(list[i]);
+ user->WriteNumeric(RPL_COMMANDSEND, ":End of COMMANDS list");
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2011 Jackmcbarn <jackmcbarn@jackmcbarn.no-ip.org>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandInfo::CommandInfo(Module* parent)
+ : Command(parent, "INFO")
+{
+ Penalty = 4;
+ syntax = "[<servername>]";
+}
+
+static const char* const lines[] = {
+ " -/\\- \2InspIRCd\2 -\\/-",
+ " November 2002 - Present",
+ " ",
+ "\2Core Developers\2:",
+ " Craig Edwards, Brain, <brain@inspircd.org>",
+ " Craig McLure, Craig, <craig@inspircd.org>",
+ " Robin Burchell, w00t, <w00t@inspircd.org>",
+ " Oliver Lupton, Om, <om@inspircd.org>",
+ " John Brooks, Special, <special@inspircd.org>",
+ " Dennis Friis, peavey, <peavey@inspircd.org>",
+ " Thomas Stagner, aquanight, <aquanight@inspircd.org>",
+ " Uli Schlachter, psychon, <psychon@inspircd.org>",
+ " Matt Smith, dz, <dz@inspircd.org>",
+ " Daniel De Graaf, danieldg, <danieldg@inspircd.org>",
+ " jackmcbarn, <jackmcbarn@inspircd.org>",
+ " Attila Molnar, Attila, <attilamolnar@hush.com>",
+ " ",
+ "\2Regular Contributors\2:",
+ " Adam SaberUK",
+ " ",
+ "\2Other Contributors\2:",
+ " ChrisTX Shawn Shutter",
+ " ",
+ "\2Former Contributors\2:",
+ " dmb Zaba skenmy GreenReaper",
+ " Dan Jason satmd owine",
+ " Adremelech John2 jilles HiroP",
+ " eggy Bricker AnMaster djGrrr",
+ " nenolod Quension praetorian pippijn",
+ " CC jamie typobox43 Burlex (win32)",
+ " Stskeeps ThaPrince BuildSmart Thunderhacker",
+ " Skip LeaChim Majic MacGyver",
+ " Namegduf Ankit Phoenix Taros",
+ " ",
+ "\2Thanks To\2:",
+ " searchirc.com irc-junkie.org Brik fraggeln",
+ " ",
+ " Best experienced with: \2An IRC client\2",
+ NULL
+};
+
+/** Handle /INFO
+ */
+CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+
+ int i=0;
+ while (lines[i])
+ user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_INFO, user->nick.c_str(), lines[i++]);
+ FOREACH_MOD(OnInfo, (user));
+ user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandInfo::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (parameters.size() > 0)
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandModules::CommandModules(Module* parent)
+ : Command(parent, "MODULES", 0, 0)
+{
+ Penalty = 4;
+ syntax = "[<servername>]";
+}
+
+/** Handle /MODULES
+ */
+CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ // Don't ask remote servers about their modules unless the local user asking is an oper
+ // 2.0 asks anyway, so let's handle that the same way
+ bool for_us = (parameters.empty() || parameters[0] == ServerInstance->Config->ServerName);
+ if ((!for_us) || (!IS_LOCAL(user)))
+ {
+ if (!user->IsOper())
+ {
+ user->WriteNotice("*** You cannot check what modules other servers have loaded.");
+ return CMD_FAILURE;
+ }
+
+ // From an oper and not for us, forward
+ if (!for_us)
+ return CMD_SUCCESS;
+ }
+
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ {
+ Module* m = i->second;
+ Version V = m->GetVersion();
+
+ if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"))
+ {
+ std::string flags("SvcC");
+ int pos = 0;
+ for (int mult = 1; mult <= VF_OPTCOMMON; mult *= 2, ++pos)
+ if (!(V.Flags & mult))
+ flags[pos] = '-';
+
+#ifdef PURE_STATIC
+ user->SendText(":%s 702 %s :%p %s %s :%s", ServerInstance->Config->ServerName.c_str(),
+ user->nick.c_str(), (void*)m, m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str());
+#else
+ std::string srcrev = m->ModuleDLLManager->GetVersion();
+ user->SendText(":%s 702 %s :%p %s %s :%s - %s", ServerInstance->Config->ServerName.c_str(),
+ user->nick.c_str(), (void*)m, m->ModuleSourceFile.c_str(), flags.c_str(), V.description.c_str(), srcrev.c_str());
+#endif
+ }
+ else
+ {
+ user->SendText(":%s 702 %s :%s %s", ServerInstance->Config->ServerName.c_str(),
+ user->nick.c_str(), m->ModuleSourceFile.c_str(), V.description.c_str());
+ }
+ }
+ user->SendText(":%s 703 %s :End of MODULES list", ServerInstance->Config->ServerName.c_str(), user->nick.c_str());
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandModules::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (parameters.size() >= 1)
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandMotd::CommandMotd(Module* parent)
+ : Command(parent, "MOTD", 0, 1)
+{
+ syntax = "[<servername>]";
+}
+
+/** Handle /MOTD
+ */
+CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+
+ ConfigTag* tag = ServerInstance->Config->EmptyTag;
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ tag = localuser->GetClass()->config;
+ std::string motd_name = tag->getString("motd", "motd");
+ ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
+ if (motd == ServerInstance->Config->Files.end())
+ {
+ user->SendText(":%s %03d %s :Message of the day file is missing.",
+ ServerInstance->Config->ServerName.c_str(), ERR_NOMOTD, user->nick.c_str());
+ return CMD_SUCCESS;
+ }
+
+ user->SendText(":%s %03d %s :%s message of the day", ServerInstance->Config->ServerName.c_str(),
+ RPL_MOTDSTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
+
+ for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
+ user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_MOTD, user->nick.c_str(), i->c_str());
+
+ user->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandMotd::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (parameters.size() > 0)
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandTime::CommandTime(Module* parent)
+ : Command(parent, "TIME", 0, 0)
+{
+ syntax = "[<servername>]";
+}
+
+CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+
+ user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),
+ ServerInstance->Config->ServerName.c_str(), InspIRCd::TimeString(ServerInstance->Time()).c_str());
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandTime::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (parameters.size() > 0)
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+CommandVersion::CommandVersion(Module* parent)
+ : Command(parent, "VERSION", 0, 0)
+{
+ syntax = "[<servername>]";
+}
+
+CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
+{
+ std::string version = ServerInstance->GetVersionString((user->IsOper()));
+ user->WriteNumeric(RPL_VERSION, ":%s", version.c_str());
+ LocalUser *lu = IS_LOCAL(user);
+ if (lu != NULL)
+ {
+ ServerInstance->ISupport.SendTo(lu);
+ }
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_info.h"
+
+class CoreModInfo : public Module
+{
+ CommandAdmin cmdadmin;
+ CommandCommands cmdcommands;
+ CommandInfo cmdinfo;
+ CommandModules cmdmodules;
+ CommandMotd cmdmotd;
+ CommandTime cmdtime;
+ CommandVersion cmdversion;
+
+ public:
+ CoreModInfo()
+ : cmdadmin(this), cmdcommands(this), cmdinfo(this), cmdmodules(this), cmdmotd(this), cmdtime(this), cmdversion(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("admin");
+ cmdadmin.AdminName = tag->getString("name");
+ cmdadmin.AdminEmail = tag->getString("email", "null@example.com");
+ cmdadmin.AdminNick = tag->getString("nick", "admin");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the ADMIN, COMMANDS, INFO, MODULES, MOTD, TIME and VERSION commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModInfo)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+/** Handle /ADMIN.
+ */
+class CommandAdmin : public Command
+{
+ public:
+ /** Holds the admin's name, for output in
+ * the /ADMIN command.
+ */
+ std::string AdminName;
+
+ /** Holds the email address of the admin,
+ * for output in the /ADMIN command.
+ */
+ std::string AdminEmail;
+
+ /** Holds the admin's nickname, for output
+ * in the /ADMIN command
+ */
+ std::string AdminNick;
+
+ /** Constructor for admin.
+ */
+ CommandAdmin(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /COMMANDS.
+ */
+class CommandCommands : public Command
+{
+ public:
+ /** Constructor for commands.
+ */
+ CommandCommands(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /INFO.
+ */
+class CommandInfo : public Command
+{
+ public:
+ /** Constructor for info.
+ */
+ CommandInfo(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /MODULES.
+ */
+class CommandModules : public Command
+{
+ public:
+ /** Constructor for modules.
+ */
+ CommandModules(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /MOTD.
+ */
+class CommandMotd : public Command
+{
+ public:
+ /** Constructor for motd.
+ */
+ CommandMotd(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /TIME.
+ */
+class CommandTime : public Command
+{
+ public:
+ /** Constructor for time.
+ */
+ CommandTime(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /VERSION.
+ */
+class CommandVersion : public Command
+{
+ public:
+ /** Constructor for version.
+ */
+ CommandVersion(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /ISON.
+ */
+class CommandIson : public Command
+{
+ /** Helper function to append a nick to an ISON reply
+ * @param user User doing the /ISON
+ * @param toadd User to append to the ISON reply
+ * @param reply Reply string to append the nick to
+ * @param pos If the reply gets too long it is sent to the user and truncated from this position
+ */
+ static bool AddNick(User* user, User* toadd, std::string& reply, const std::string::size_type pos);
+
+ public:
+ /** Constructor for ison.
+ */
+ CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
+ syntax = "<nick> {nick}";
+ }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+bool CommandIson::AddNick(User* user, User* toadd, std::string& reply, const std::string::size_type pos)
+{
+ if ((toadd) && (toadd->registered == REG_ALL))
+ {
+ reply.append(toadd->nick).push_back(' ');
+ if (reply.length() > 450)
+ {
+ user->WriteServ(reply);
+ reply.erase(pos);
+ }
+ return true;
+ }
+ return false;
+}
+
+/** Handle /ISON
+ */
+CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string reply = "303 " + user->nick + " :";
+ const std::string::size_type pos = reply.size();
+
+ for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
+ {
+ const std::string& targetstr = *i;
+
+ User* const u = ServerInstance->FindNickOnly(targetstr);
+ if (!AddNick(user, u, reply, pos))
+ {
+ if ((i == parameters.end() - 1) && (targetstr.find(' ') != std::string::npos))
+ {
+ /* Its a space seperated list of nicks (RFC1459 says to support this)
+ */
+ irc::spacesepstream list(targetstr);
+ std::string item;
+
+ while (list.GetToken(item))
+ AddNick(user, ServerInstance->FindNickOnly(item), reply, pos);
+ }
+ }
+ }
+
+ user->WriteServ(reply);
+ return CMD_SUCCESS;
+}
+
+
+COMMAND_INIT(CommandIson)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /LIST.
+ */
+class CommandList : public Command
+{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+
+ public:
+ /** Constructor for list.
+ */
+ CommandList(Module* parent)
+ : Command(parent,"LIST", 0, 0)
+ , secretmode(creator, "secret")
+ , privatemode(creator, "private")
+ {
+ Penalty = 5;
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+
+/** Handle /LIST
+ */
+CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ int minusers = 0, maxusers = 0;
+
+ user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name");
+
+ /* Work around mIRC suckyness. YOU SUCK, KHALED! */
+ if (parameters.size() == 1)
+ {
+ if (parameters[0][0] == '<')
+ {
+ maxusers = atoi((parameters[0].c_str())+1);
+ }
+ else if (parameters[0][0] == '>')
+ {
+ minusers = atoi((parameters[0].c_str())+1);
+ }
+ }
+
+ const bool has_privs = user->HasPrivPermission("channels/auspex");
+ const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ Channel* const chan = i->second;
+
+ // attempt to match a glob pattern
+ long users = chan->GetUserCounter();
+
+ bool too_few = (minusers && (users <= minusers));
+ bool too_many = (maxusers && (users >= maxusers));
+
+ if (too_many || too_few)
+ continue;
+
+ if (match_name_topic)
+ {
+ if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
+ continue;
+ }
+
+ // if the channel is not private/secret, OR the user is on the channel anyway
+ bool n = (has_privs || chan->HasUser(user));
+
+ if ((!n) && (chan->IsModeSet(privatemode)))
+ {
+ /* Channel is +p and user is outside/not privileged */
+ user->WriteNumeric(RPL_LIST, "* %ld :", users);
+ }
+ else
+ {
+ if ((n) || (!chan->IsModeSet(secretmode)))
+ {
+ /* User is in the channel/privileged, channel is not +s */
+ user->WriteNumeric(RPL_LIST, "%s %ld :[+%s] %s", chan->name.c_str(), users, chan->ChanModes(n), chan->topic.c_str());
+ }
+ }
+ }
+ user->WriteNumeric(RPL_LISTEND, ":End of channel list.");
+
+ return CMD_SUCCESS;
+}
+
+
+COMMAND_INIT(CommandList)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /LOADMODULE.
+ */
+class CommandLoadmodule : public Command
+{
+ public:
+ /** Constructor for loadmodule.
+ */
+ CommandLoadmodule ( Module* parent) : Command(parent,"LOADMODULE",1,1) { flags_needed='o'; syntax = "<modulename>"; }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+/** Handle /LOADMODULE
+ */
+CmdResult CommandLoadmodule::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (ServerInstance->Modules->Load(parameters[0]))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "NEW MODULE: %s loaded %s",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ return CMD_FAILURE;
+ }
+}
+
+/** Handle /UNLOADMODULE.
+ */
+class CommandUnloadmodule : public Command
+{
+ public:
+ /** Constructor for unloadmodule.
+ */
+ CommandUnloadmodule(Module* parent)
+ : Command(parent,"UNLOADMODULE", 1)
+ {
+ flags_needed = 'o';
+ syntax = "<modulename>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+CmdResult CommandUnloadmodule::Handle(const std::vector<std::string>& parameters, User* user)
+{
+ if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+ InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ Module* m = ServerInstance->Modules->Find(parameters[0]);
+ if (m == creator)
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload module loading commands!", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ if (m && ServerInstance->Modules->Unload(m))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "MODULE UNLOADED: %s unloaded %s", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module successfully unloaded.", parameters[0].c_str());
+ }
+ else
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(),
+ m ? ServerInstance->Modules->LastError().c_str() : "No such module");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+class CoreModLoadModule : public Module
+{
+ CommandLoadmodule cmdloadmod;
+ CommandUnloadmodule cmdunloadmod;
+
+ public:
+ CoreModLoadModule()
+ : cmdloadmod(this), cmdunloadmod(this)
+ {
+ }
+
+ Version GetVersion()
+ {
+ return Version("Provides the LOADMODULE and UNLOADMODULE commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModLoadModule)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+struct LusersCounters
+{
+ unsigned int max_local;
+ unsigned int max_global;
+ unsigned int invisible;
+
+ LusersCounters(unsigned int inv)
+ : max_local(ServerInstance->Users->LocalUserCount())
+ , max_global(ServerInstance->Users->RegisteredUserCount())
+ , invisible(inv)
+ {
+ }
+
+ inline void UpdateMaxUsers()
+ {
+ unsigned int current = ServerInstance->Users->LocalUserCount();
+ if (current > max_local)
+ max_local = current;
+
+ current = ServerInstance->Users->RegisteredUserCount();
+ if (current > max_global)
+ max_global = current;
+ }
+};
+
+/** Handle /LUSERS.
+ */
+class CommandLusers : public Command
+{
+ LusersCounters& counters;
+ public:
+ /** Constructor for lusers.
+ */
+ CommandLusers(Module* parent, LusersCounters& Counters)
+ : Command(parent,"LUSERS",0,0), counters(Counters)
+ { }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+/** Handle /LUSERS
+ */
+CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
+{
+ unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
+ ProtocolInterface::ServerList serverlist;
+ ServerInstance->PI->GetServerList(serverlist);
+ unsigned int n_serv = serverlist.size();
+ unsigned int n_local_servs = 0;
+ for (ProtocolInterface::ServerList::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+ {
+ if (i->parentname == ServerInstance->Config->ServerName)
+ n_local_servs++;
+ }
+ // fix for default GetServerList not returning us
+ if (!n_serv)
+ n_serv = 1;
+
+ counters.UpdateMaxUsers();
+
+ user->WriteNumeric(RPL_LUSERCLIENT, ":There are %d users and %d invisible on %d servers",
+ n_users - counters.invisible, counters.invisible, n_serv);
+
+ if (ServerInstance->Users->OperCount())
+ user->WriteNumeric(RPL_LUSEROP, "%d :operator(s) online", ServerInstance->Users->OperCount());
+
+ if (ServerInstance->Users->UnregisteredUserCount())
+ user->WriteNumeric(RPL_LUSERUNKNOWN, "%d :unknown connections", ServerInstance->Users->UnregisteredUserCount());
+
+ user->WriteNumeric(RPL_LUSERCHANNELS, "%lu :channels formed", (unsigned long)ServerInstance->GetChans().size());
+ user->WriteNumeric(RPL_LUSERME, ":I have %d clients and %d servers", ServerInstance->Users->LocalUserCount(),n_local_servs);
+ user->WriteNumeric(RPL_LOCALUSERS, ":Current local users: %d Max: %d", ServerInstance->Users->LocalUserCount(), counters.max_local);
+ user->WriteNumeric(RPL_GLOBALUSERS, ":Current global users: %d Max: %d", n_users, counters.max_global);
+
+ return CMD_SUCCESS;
+}
+
+class InvisibleWatcher : public ModeWatcher
+{
+ unsigned int& invisible;
+public:
+ InvisibleWatcher(Module* mod, unsigned int& Invisible)
+ : ModeWatcher(mod, "invisible", MODETYPE_USER), invisible(Invisible)
+ {
+ }
+
+ void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
+ {
+ if (dest->registered != REG_ALL)
+ return;
+
+ if (adding)
+ invisible++;
+ else
+ invisible--;
+ }
+};
+
+class ModuleLusers : public Module
+{
+ UserModeReference invisiblemode;
+ LusersCounters counters;
+ CommandLusers cmd;
+ InvisibleWatcher mw;
+
+ unsigned int CountInvisible()
+ {
+ unsigned int c = 0;
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* u = i->second;
+ if (u->IsModeSet(invisiblemode))
+ c++;
+ }
+ return c;
+ }
+
+ public:
+ ModuleLusers()
+ : invisiblemode(this, "invisible")
+ , counters(CountInvisible())
+ , cmd(this, counters)
+ , mw(this, counters.invisible)
+ {
+ }
+
+ void OnPostConnect(User* user)
+ {
+ counters.UpdateMaxUsers();
+ if (user->IsModeSet(invisiblemode))
+ counters.invisible++;
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ {
+ if (user->IsModeSet(invisiblemode))
+ counters.invisible--;
+ }
+
+ Version GetVersion()
+ {
+ return Version("LUSERS", VF_VENDOR | VF_CORE);
+ }
+};
+
+MODULE_INIT(ModuleLusers)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "exitcodes.h"
+#include "core_oper.h"
+
+CommandDie::CommandDie(Module* parent)
+ : Command(parent, "DIE", 1)
+{
+ flags_needed = 'o';
+ syntax = "<password>";
+}
+
+/** Handle /DIE
+ */
+CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (DieRestart::CheckPass(user, parameters[0], "diepass"))
+ {
+ {
+ std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
+ ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, diebuf);
+ ServerInstance->SendError(diebuf);
+ }
+
+ ServerInstance->Exit(EXIT_STATUS_DIE);
+ }
+ else
+ {
+ ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandKill::CommandKill(Module* parent)
+ : Command(parent, "KILL", 2, 2)
+{
+ flags_needed = 'o';
+ syntax = "<nickname> <reason>";
+ TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
+}
+
+
+/** Handle /KILL
+ */
+CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ /* Allow comma seperated lists of users for /KILL (thanks w00t) */
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ {
+ // If we got a colon delimited list of nicks then the handler ran for each nick,
+ // and KILL commands were broadcast for remote targets.
+ return CMD_FAILURE;
+ }
+
+ User *u = ServerInstance->FindNick(parameters[0]);
+ if ((u) && (!IS_SERVER(u)))
+ {
+ /*
+ * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
+ * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
+ *
+ * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
+ * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
+ */
+
+ if (IS_LOCAL(user))
+ {
+ /*
+ * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
+ * and the other half not. This would be a bad thing. ;p -- w00t
+ */
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
+
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ killreason = "Killed (";
+ if (!ServerInstance->Config->HideKillsServer.empty())
+ {
+ // hidekills is on, use it
+ killreason += ServerInstance->Config->HideKillsServer;
+ }
+ else
+ {
+ // hidekills is off, do nothing
+ killreason += user->nick;
+ }
+
+ killreason += " (" + parameters[1] + "))";
+ }
+ else
+ {
+ /* Leave it alone, remote server has already formatted it */
+ killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
+ }
+
+ /*
+ * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
+ * No time to fix it right now, so left a note. -- w00t
+ */
+ if (!IS_LOCAL(u))
+ {
+ // remote kill
+ if (!user->server->IsULine())
+ ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ this->lastuuid = u->uuid;
+ }
+ else
+ {
+ // local kill
+ /*
+ * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
+ * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
+ */
+ if (!user->server->IsULine())
+ {
+ if (IS_LOCAL(user))
+ ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ else
+ ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
+ }
+
+ ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
+
+ u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+ u->nick.c_str(),
+ ServerInstance->Config->ServerName.c_str(),
+ user->dhost.c_str(),
+ ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
+ parameters[1].c_str());
+
+ this->lastuuid.clear();
+ }
+
+ // send the quit out
+ ServerInstance->Users->QuitUser(u, killreason);
+ }
+ else
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandKill::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ // FindNick() doesn't work here because we quit the target user in Handle() which
+ // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
+ // was for a local user, otherwise it contains the uuid of the user who was killed.
+ if (lastuuid.empty())
+ return ROUTE_LOCALONLY;
+ return ROUTE_BROADCAST;
+}
+
+
+void CommandKill::EncodeParameter(std::string& param, int index)
+{
+ // Manually translate the nick -> uuid (see above), and also the reason (params[1])
+ // because we decorate it if the oper is local and want remote servers to see the
+ // decorated reason not the original.
+ param = ((index == 0) ? lastuuid : killreason);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandOper::CommandOper(Module* parent)
+ : SplitCommand(parent, "OPER", 2, 2)
+{
+ syntax = "<username> <password>";
+}
+
+CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
+{
+ bool match_login = false;
+ bool match_pass = false;
+ bool match_hosts = false;
+
+ const std::string userHost = user->ident + "@" + user->host;
+ const std::string userIP = user->ident + "@" + user->GetIPString();
+
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ if (i != ServerInstance->Config->oper_blocks.end())
+ {
+ OperInfo* ifo = i->second;
+ ConfigTag* tag = ifo->oper_block;
+ match_login = true;
+ match_pass = ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
+ match_hosts = InspIRCd::MatchMask(tag->getString("host"), userHost, userIP);
+
+ if (match_pass && match_hosts)
+ {
+ /* found this oper's opertype */
+ user->Oper(ifo);
+ return CMD_SUCCESS;
+ }
+ }
+
+ std::string fields;
+ if (!match_login)
+ fields.append("login ");
+ if (!match_pass)
+ fields.append("password ");
+ if (!match_hosts)
+ fields.append("hosts");
+
+ // tell them they suck, and lag them up to help prevent brute-force attacks
+ user->WriteNumeric(ERR_NOOPERHOST, ":Invalid oper credentials");
+ user->CommandFloodPenalty += 10000;
+
+ ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
+ ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
+ return CMD_FAILURE;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandRehash::CommandRehash(Module* parent)
+ : Command(parent, "REHASH", 0)
+{
+ flags_needed = 'o';
+ Penalty = 2;
+ syntax = "[<servermask>]";
+}
+
+CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string param = parameters.size() ? parameters[0] : "";
+
+ FOREACH_MOD(OnPreRehash, (user, param));
+
+ if (param.empty())
+ {
+ // standard rehash of local server
+ }
+ else if (param.find_first_of("*.") != std::string::npos)
+ {
+ // rehash of servers by server name (with wildcard)
+ if (!InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0]))
+ {
+ // Doesn't match us. PreRehash is already done, nothing left to do
+ return CMD_SUCCESS;
+ }
+ }
+ else
+ {
+ // parameterized rehash
+
+ // the leading "-" is optional; remove it if present.
+ if (param[0] == '-')
+ param = param.substr(1);
+
+ FOREACH_MOD(OnModuleRehash, (user, param));
+ return CMD_SUCCESS;
+ }
+
+ // Rehash for me. Try to start the rehash thread
+ if (!ServerInstance->ConfigThread)
+ {
+ std::string m = user->nick + " is rehashing config file " + FileSystem::GetFileName(ServerInstance->ConfigFileName) + " on " + ServerInstance->Config->ServerName;
+ ServerInstance->SNO->WriteGlobalSno('a', m);
+
+ if (IS_LOCAL(user))
+ user->WriteNumeric(RPL_REHASHING, "%s :Rehashing", FileSystem::GetFileName(ServerInstance->ConfigFileName).c_str());
+ else
+ ServerInstance->PI->SendUserNotice(user, "*** Rehashing server " + FileSystem::GetFileName(ServerInstance->ConfigFileName));
+
+ /* Don't do anything with the logs here -- logs are restarted
+ * after the config thread has completed.
+ */
+ ServerInstance->Rehash(user->uuid);
+ }
+ else
+ {
+ /*
+ * A rehash is already in progress! ahh shit.
+ * XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
+ */
+ if (IS_LOCAL(user))
+ user->WriteNotice("*** Could not rehash: A rehash is already in progress.");
+ else
+ ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
+ }
+
+ // Always return success so spanningtree forwards an incoming REHASH even if we failed
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+CommandRestart::CommandRestart(Module* parent)
+ : Command(parent, "RESTART", 1, 1)
+{
+ flags_needed = 'o';
+ syntax = "<password>";
+}
+
+CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Restart: %s",user->nick.c_str());
+ if (DieRestart::CheckPass(user, parameters[0], "restartpass"))
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
+
+ ServerInstance->SendError("Server restarting.");
+
+#ifndef _WIN32
+ /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execv() below succeeds.
+ * Certainly, this is not a nice way to do things and it's slow when the fd limit is high.
+ *
+ * A better solution would be to set the close-on-exec flag for each fd we create (or create them with O_CLOEXEC),
+ * however there is no guarantee that third party libs will do the same.
+ */
+ for (int i = getdtablesize(); --i > 2;)
+ {
+ int flags = fcntl(i, F_GETFD);
+ if (flags != -1)
+ fcntl(i, F_SETFD, flags | FD_CLOEXEC);
+ }
+#endif
+
+ execv(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART - could not execute '%s' (%s)",
+ ServerInstance->Config->cmdline.argv[0], strerror(errno));
+ }
+ else
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART Command from %s.", user->GetFullRealHost().c_str());
+ }
+ return CMD_FAILURE;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_oper.h"
+
+namespace DieRestart
+{
+ bool CheckPass(User* user, const std::string& inputpass, const char* confentry)
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("power");
+ // The hash method for *BOTH* the die and restart passwords
+ const std::string hash = tag->getString("hash");
+ const std::string correctpass = tag->getString(confentry);
+ return ServerInstance->PassCompare(user, correctpass, inputpass, hash);
+ }
+}
+
+class CoreModOper : public Module
+{
+ CommandDie cmddie;
+ CommandKill cmdkill;
+ CommandOper cmdoper;
+ CommandRehash cmdrehash;
+ CommandRestart cmdrestart;
+
+ public:
+ CoreModOper()
+ : cmddie(this), cmdkill(this), cmdoper(this), cmdrehash(this), cmdrestart(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the DIE, KILL, OPER, REHASH, and RESTART commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModOper)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+namespace DieRestart
+{
+ /** Checks a die or restart password
+ * @param user The user executing /DIE or /RESTART
+ * @param inputpass The password given by the user
+ * @param confkey The name of the key in the power tag containing the correct password
+ * @return True if the given password was correct, false if it was not
+ */
+ bool CheckPass(User* user, const std::string& inputpass, const char* confkey);
+}
+
+/** Handle /DIE.
+ */
+class CommandDie : public Command
+{
+ public:
+ /** Constructor for die.
+ */
+ CommandDie(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /KILL.
+ */
+class CommandKill : public Command
+{
+ std::string lastuuid;
+ std::string killreason;
+
+ public:
+ /** Constructor for kill.
+ */
+ CommandKill(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+
+ void EncodeParameter(std::string& param, int index);
+};
+
+/** Handle /OPER.
+ */
+class CommandOper : public SplitCommand
+{
+ public:
+ /** Constructor for oper.
+ */
+ CommandOper(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /REHASH.
+ */
+class CommandRehash : public Command
+{
+ public:
+ /** Constructor for rehash.
+ */
+ CommandRehash(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+/** Handle /RESTART
+ */
+class CommandRestart : public Command
+{
+ public:
+ /** Constructor for restart.
+ */
+ CommandRestart(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+namespace
+{
+ const char* MessageTypeString[] = { "PRIVMSG", "NOTICE" };
+}
+
+class MessageCommandBase : public Command
+{
+ ChanModeReference moderatedmode;
+ ChanModeReference noextmsgmode;
+
+ /** Send a PRIVMSG or NOTICE message to all local users from the given user
+ * @param user User sending the message
+ * @param msg The message to send
+ * @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
+ */
+ static void SendAll(User* user, const std::string& msg, MessageType mt);
+
+ public:
+ MessageCommandBase(Module* parent, MessageType mt)
+ : Command(parent, MessageTypeString[mt], 2, 2)
+ , moderatedmode(parent, "moderated")
+ , noextmsgmode(parent, "noextmsg")
+ {
+ syntax = "<target>{,<target>} <message>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ if (IS_LOCAL(user))
+ // This is handled by the OnUserMessage hook to split the LoopCall pieces
+ return ROUTE_LOCALONLY;
+ else
+ return ROUTE_MESSAGE(parameters[0]);
+ }
+};
+
+void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt)
+{
+ const std::string message = ":" + user->GetFullHost() + " " + MessageTypeString[mt] + " $* :" + msg;
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ if ((*i)->registered == REG_ALL)
+ (*i)->Write(message);
+ }
+}
+
+CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt)
+{
+ User *dest;
+ Channel *chan;
+ CUList except_list;
+
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ localuser->idle_lastmsg = ServerInstance->Time();
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ if (parameters[0][0] == '$')
+ {
+ if (!user->HasPrivPermission("users/mass-message"))
+ return CMD_SUCCESS;
+
+ ModResult MOD_RESULT;
+ std::string temp = parameters[1];
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list, mt));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ const char* text = temp.c_str();
+ const char* servermask = (parameters[0].c_str()) + 1;
+
+ FOREACH_MOD(OnText, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
+ if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
+ {
+ SendAll(user, text, mt);
+ }
+ FOREACH_MOD(OnUserMessage, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list, mt));
+ return CMD_SUCCESS;
+ }
+ char status = 0;
+ const char* target = parameters[0].c_str();
+
+ if (ServerInstance->Modes->FindPrefix(*target))
+ {
+ status = *target;
+ target++;
+ }
+ if (*target == '#')
+ {
+ chan = ServerInstance->FindChan(target);
+
+ except_list.insert(user);
+
+ if (chan)
+ {
+ if (localuser && chan->GetPrefixValue(user) < VOICE_VALUE)
+ {
+ if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (no external messages)", chan->name.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (chan->IsModeSet(moderatedmode))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (+m)", chan->name.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (ServerInstance->Config->RestrictBannedUsers)
+ {
+ if (chan->IsBanned(user))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", chan->name.c_str());
+ return CMD_FAILURE;
+ }
+ }
+ }
+ ModResult MOD_RESULT;
+
+ std::string temp = parameters[1];
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, chan, TYPE_CHANNEL, temp, status, except_list, mt));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ const char* text = temp.c_str();
+
+ /* Check again, a module may have zapped the input string */
+ if (temp.empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, ":No text to send");
+ return CMD_FAILURE;
+ }
+
+ FOREACH_MOD(OnText, (user,chan,TYPE_CHANNEL,text,status,except_list));
+
+ if (status)
+ {
+ if (ServerInstance->Config->UndernetMsgPrefix)
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%c %s", MessageTypeString[mt], status, chan->name.c_str(), status, text);
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), text);
+ }
+ }
+ else
+ {
+ chan->WriteAllExcept(user, false, status, except_list, "%s %s :%s", MessageTypeString[mt], chan->name.c_str(), text);
+ }
+
+ FOREACH_MOD(OnUserMessage, (user,chan, TYPE_CHANNEL, text, status, except_list, mt));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target);
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+ }
+
+ const char* destnick = parameters[0].c_str();
+
+ if (localuser)
+ {
+ const char* targetserver = strchr(destnick, '@');
+
+ if (targetserver)
+ {
+ std::string nickonly;
+
+ nickonly.assign(destnick, 0, targetserver - destnick);
+ dest = ServerInstance->FindNickOnly(nickonly);
+ if (dest && strcasecmp(dest->server->GetName().c_str(), targetserver + 1))
+ {
+ /* Incorrect server for user */
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+ }
+ else
+ dest = ServerInstance->FindNickOnly(destnick);
+ }
+ else
+ dest = ServerInstance->FindNick(destnick);
+
+ if ((dest) && (dest->registered == REG_ALL))
+ {
+ if (parameters[1].empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, ":No text to send");
+ return CMD_FAILURE;
+ }
+
+ if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
+ {
+ /* auto respond with aweh msg */
+ user->WriteNumeric(RPL_AWAY, "%s :%s", dest->nick.c_str(), dest->awaymsg.c_str());
+ }
+
+ ModResult MOD_RESULT;
+
+ std::string temp = parameters[1];
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list, mt));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ const char* text = temp.c_str();
+
+ FOREACH_MOD(OnText, (user, dest, TYPE_USER, text, 0, except_list));
+
+ if (IS_LOCAL(dest))
+ {
+ // direct write, same server
+ dest->WriteFrom(user, "%s %s :%s", MessageTypeString[mt], dest->nick.c_str(), text);
+ }
+
+ FOREACH_MOD(OnUserMessage, (user, dest, TYPE_USER, text, 0, except_list, mt));
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+ return CMD_SUCCESS;
+}
+
+template<MessageType MT>
+class CommandMessage : public MessageCommandBase
+{
+ public:
+ CommandMessage(Module* parent)
+ : MessageCommandBase(parent, MT)
+ {
+ }
+
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ return HandleMessage(parameters, user, MT);
+ }
+};
+
+class ModuleCoreMessage : public Module
+{
+ CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
+ CommandMessage<MSG_NOTICE> CommandNotice;
+
+ public:
+ ModuleCoreMessage()
+ : CommandPrivmsg(this), CommandNotice(this)
+ {
+ }
+
+ Version GetVersion()
+ {
+ return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCoreMessage)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class CommandReloadmodule : public Command
+{
+ public:
+ /** Constructor for reloadmodule.
+ */
+ CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+class ReloadModuleWorker : public HandlerBase1<void, bool>
+{
+ public:
+ const std::string name;
+ const std::string uid;
+ ReloadModuleWorker(const std::string& uuid, const std::string& modn)
+ : name(modn), uid(uuid) {}
+ void Call(bool result)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
+ name.c_str(), result ? "" : "un");
+ User* user = ServerInstance->FindNick(uid);
+ if (user)
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
+ name.c_str(), result ? "" : "un");
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+};
+
+CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ Module* m = ServerInstance->Modules->Find(parameters[0]);
+ if (m == creator)
+ {
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :You cannot reload core_reloadmodule.so (unload and load it)",
+ parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ if (m)
+ {
+ ServerInstance->Modules->Reload(m, new ReloadModuleWorker(user->uuid, parameters[0]));
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+}
+
+COMMAND_INIT(CommandReloadmodule)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+
+#ifdef _WIN32
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
+#endif
+
+/** Handle /STATS.
+ */
+class CommandStats : public Command
+{
+ void DoStats(char statschar, User* user, string_list &results);
+ public:
+ /** Constructor for stats.
+ */
+ CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ if (parameters.size() > 1)
+ return ROUTE_UNICAST(parameters[1]);
+ return ROUTE_LOCALONLY;
+ }
+};
+
+static void GenerateStatsLl(User* user, string_list& results, char c)
+{
+ results.push_back(InspIRCd::Format("211 %s nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", user->nick.c_str(), (c == 'l' ? "host" : "ip")));
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* u = *i;
+ results.push_back("211 "+user->nick+" "+u->nick+"["+u->ident+"@"+(c == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->age));
+ }
+}
+
+void CommandStats::DoStats(char statschar, User* user, string_list &results)
+{
+ bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
+ bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
+ bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
+
+ if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
+ {
+ ServerInstance->SNO->WriteToSnoMask('t',
+ "%s '%c' denied for %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"),
+ statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ results.push_back("481 " + user->nick + " :Permission Denied - STATS " + statschar + " requires the servers/auspex priv.");
+ return;
+ }
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
+ if (MOD_RESULT == MOD_RES_DENY)
+ {
+ results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ return;
+ }
+
+ switch (statschar)
+ {
+ /* stats p (show listening ports) */
+ case 'p':
+ {
+ for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+ {
+ ListenSocket* ls = *i;
+ std::string ip = ls->bind_addr;
+ if (ip.empty())
+ ip.assign("*");
+ std::string type = ls->bind_tag->getString("type", "clients");
+ std::string hook = ls->bind_tag->getString("ssl", "plaintext");
+
+ results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
+ " (" + type + ", " + hook + ")");
+ }
+ }
+ break;
+
+ /* These stats symbols must be handled by a linking module */
+ case 'n':
+ case 'c':
+ break;
+
+ case 'i':
+ {
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
+ {
+ ConnectClass* c = *i;
+ std::stringstream res;
+ res << "215 " << user->nick << " I " << c->name << ' ';
+ if (c->type == CC_ALLOW)
+ res << '+';
+ if (c->type == CC_DENY)
+ res << '-';
+
+ if (c->type == CC_NAMED)
+ res << '*';
+ else
+ res << c->host;
+
+ res << ' ' << c->config->getString("port", "*") << ' ';
+
+ res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
+ << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
+ if (c->fakelag)
+ res << '*';
+ results.push_back(res.str());
+ }
+ }
+ break;
+
+ case 'Y':
+ {
+ int idx = 0;
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ ConnectClass* c = *i;
+ results.push_back("215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : SocketEngine::GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
+ results.push_back("218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
+ ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
+ idx++;
+ }
+ }
+ break;
+
+ case 'P':
+ {
+ unsigned int idx = 0;
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* oper = *i;
+ if (!oper->server->IsULine())
+ {
+ LocalUser* lu = IS_LOCAL(oper);
+ results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
+ idx++;
+ }
+ }
+ results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
+ }
+ break;
+
+ case 'k':
+ ServerInstance->XLines->InvokeStats("K",216,user,results);
+ break;
+ case 'g':
+ ServerInstance->XLines->InvokeStats("G",223,user,results);
+ break;
+ case 'q':
+ ServerInstance->XLines->InvokeStats("Q",217,user,results);
+ break;
+ case 'Z':
+ ServerInstance->XLines->InvokeStats("Z",223,user,results);
+ break;
+ case 'e':
+ ServerInstance->XLines->InvokeStats("E",223,user,results);
+ break;
+ case 'E':
+ {
+ const SocketEngine::Statistics& stats = SocketEngine::GetStats();
+ results.push_back("249 "+user->nick+" :Total events: "+ConvToStr(stats.TotalEvents));
+ results.push_back("249 "+user->nick+" :Read events: "+ConvToStr(stats.ReadEvents));
+ results.push_back("249 "+user->nick+" :Write events: "+ConvToStr(stats.WriteEvents));
+ results.push_back("249 "+user->nick+" :Error events: "+ConvToStr(stats.ErrorEvents));
+ break;
+ }
+
+ /* stats m (list number of times each command has been used, plus bytecount) */
+ case 'm':
+ {
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ if (i->second->use_count)
+ {
+ /* RPL_STATSCOMMANDS */
+ results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count));
+ }
+ }
+ }
+ break;
+
+ /* stats z (debug and memory info) */
+ case 'z':
+ {
+ results.push_back("249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
+ results.push_back("249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->GetChans().size()));
+ results.push_back("249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
+
+ float kbitpersec_in, kbitpersec_out, kbitpersec_total;
+ char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
+
+ SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
+
+ snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
+ snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
+ snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
+
+ results.push_back("249 "+user->nick+" :Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
+ results.push_back("249 "+user->nick+" :Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
+ results.push_back("249 "+user->nick+" :Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
+
+#ifndef _WIN32
+ /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
+ * Also cuts out some identical code in both branches of the ifndef. -- Om
+ */
+ rusage R;
+
+ /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
+ if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
+ {
+ results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
+ results.push_back("249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals));
+ results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt));
+ results.push_back("249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap));
+ results.push_back("249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
+
+ char percent[30];
+
+ float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
+ + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
+ float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
+ float per = (n_eaten / n_elapsed) * 100;
+
+ snprintf(percent, 30, "%03.5f%%", per);
+ results.push_back("249 "+user->nick+" :CPU Use (now): "+percent);
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
+ per = (n_eaten / n_elapsed) * 100;
+ snprintf(percent, 30, "%03.5f%%", per);
+ results.push_back("249 "+user->nick+" :CPU Use (total): "+percent);
+ }
+#else
+ PROCESS_MEMORY_COUNTERS MemCounters;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
+ {
+ results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
+ results.push_back("249 "+user->nick+" :Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
+ results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(MemCounters.PageFaultCount));
+ }
+
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+ LARGE_INTEGER ThisSample;
+ if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
+ QueryPerformanceCounter(&ThisSample))
+ {
+ KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
+ KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
+ double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
+ double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
+ double per = (n_eaten/n_elapsed);
+
+ char percent[30];
+
+ snprintf(percent, 30, "%03.5f%%", per);
+ results.push_back("249 "+user->nick+" :CPU Use (now): "+percent);
+
+ n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
+ n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
+ per = (n_eaten / n_elapsed);
+ snprintf(percent, 30, "%03.5f%%", per);
+ results.push_back("249 "+user->nick+" :CPU Use (total): "+percent);
+ }
+#endif
+ }
+ break;
+
+ case 'T':
+ {
+ results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
+ results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
+ results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
+ results.push_back("249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
+ results.push_back("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats.Connects));
+ results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(),
+ ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
+ }
+ break;
+
+ /* stats o */
+ case 'o':
+ {
+ ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
+ for(ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ results.push_back("243 "+user->nick+" O "+tag->getString("host")+" * "+
+ tag->getString("name") + " " + tag->getString("type")+" 0");
+ }
+ }
+ break;
+ case 'O':
+ {
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
+ {
+ OperInfo* tag = i->second;
+ tag->init();
+ std::string umodes;
+ std::string cmodes;
+ for(char c='A'; c <= 'z'; c++)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
+ if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
+ umodes.push_back(c);
+ mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
+ if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
+ cmodes.push_back(c);
+ }
+ results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + umodes + " " + cmodes);
+ }
+ }
+ break;
+
+ /* stats l (show user I/O stats) */
+ case 'l':
+ /* stats L (show user I/O stats with IP addresses) */
+ case 'L':
+ GenerateStatsLl(user, results, statschar);
+ break;
+
+ /* stats u (show server uptime) */
+ case 'u':
+ {
+ unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
+ results.push_back(InspIRCd::Format("242 %s :Server up %u days, %.2u:%.2u:%.2u", user->nick.c_str(),
+ up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
+ ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
+ (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
+ return;
+}
+
+CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
+ return CMD_SUCCESS;
+ string_list values;
+ char search = parameters[0][0];
+ DoStats(search, user, values);
+
+ const std::string p = ":" + ServerInstance->Config->ServerName + " ";
+ for (size_t i = 0; i < values.size(); i++)
+ user->SendText(p + values[i]);
+
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandStats)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+
+/** Handle /CONNECT.
+ */
+class CommandConnect : public Command
+{
+ public:
+ /** Constructor for connect.
+ */
+ CommandConnect(Module* parent)
+ : Command(parent, "CONNECT", 1)
+ {
+ flags_needed = 'o';
+ syntax = "<servername>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ /*
+ * This is handled by the server linking module, if necessary. Do not remove this stub.
+ */
+ user->WriteServ( "NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /LINKS.
+ */
+class CommandLinks : public Command
+{
+ public:
+ /** Constructor for links.
+ */
+ CommandLinks(Module* parent)
+ : Command(parent, "LINKS", 0, 0)
+ {
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ user->WriteNumeric(RPL_LINKS, "%s %s :0 %s", ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerName.c_str(),ServerInstance->Config->ServerDesc.c_str());
+ user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /SERVER.
+ */
+class CommandServer : public Command
+{
+ public:
+ /** Constructor for server.
+ */
+ CommandServer(Module* parent)
+ : Command(parent, "SERVER")
+ {
+ works_before_reg = true;
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(ERR_ALREADYREGISTERED, ":You are already registered. (Perhaps your IRC client does not have a /SERVER command).");
+ }
+ else
+ {
+ user->WriteNumeric(ERR_NOTREGISTERED, ":You may not register as a server (servers have separate ports from clients, change your config)");
+ }
+ return CMD_FAILURE;
+ }
+};
+
+/** Handle /SQUIT.
+ */
+class CommandSquit : public Command
+{
+ public:
+ /** Constructor for squit.
+ */
+ CommandSquit(Module* parent)
+ : Command(parent, "SQUIT", 1, 2)
+ {
+ flags_needed = 'o';
+ syntax = "<servername>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ user->WriteServ("NOTICE %s :Look into loading a linking module (like m_spanningtree) if you want this to do anything useful.", user->nick.c_str());
+ return CMD_FAILURE;
+ }
+};
+
+class CoreModStub : public Module
+{
+ CommandConnect cmdconnect;
+ CommandLinks cmdlinks;
+ CommandServer cmdserver;
+ CommandSquit cmdsquit;
+
+ public:
+ CoreModStub()
+ : cmdconnect(this), cmdlinks(this), cmdserver(this), cmdsquit(this)
+ {
+ }
+
+ Version GetVersion()
+ {
+ return Version("Provides the stub commands CONNECT, LINKS, SERVER and SQUIT", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModStub)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandAway::CommandAway(Module* parent)
+ : Command(parent, "AWAY", 0, 0)
+{
+ syntax = "[<message>]";
+}
+
+/** Handle /AWAY
+ */
+CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ ModResult MOD_RESULT;
+
+ if ((parameters.size()) && (!parameters[0].empty()))
+ {
+ FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, parameters[0]));
+
+ if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
+ return CMD_FAILURE;
+
+ user->awaytime = ServerInstance->Time();
+ user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway);
+
+ user->WriteNumeric(RPL_NOWAWAY, ":You have been marked as being away");
+ }
+ else
+ {
+ FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, ""));
+
+ if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user))
+ return CMD_FAILURE;
+
+ user->awaymsg.clear();
+ user->WriteNumeric(RPL_UNAWAY, ":You are no longer marked as being away");
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandAway::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandMode::CommandMode(Module* parent)
+ : Command(parent, "MODE", 1)
+ , seq(0)
+{
+ syntax = "<target> <modes> {<mode-parameters>}";
+ memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
+{
+ const std::string& target = parameters[0];
+ Channel* targetchannel = ServerInstance->FindChan(target);
+ User* targetuser = NULL;
+ if (!targetchannel)
+ {
+ if (IS_LOCAL(user))
+ targetuser = ServerInstance->FindNickOnly(target);
+ else
+ targetuser = ServerInstance->FindNick(target);
+ }
+
+ if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
+ return CMD_FAILURE;
+ }
+ if (parameters.size() == 1)
+ {
+ this->DisplayCurrentModes(user, targetuser, targetchannel);
+ return CMD_SUCCESS;
+ }
+
+ // Populate a temporary Modes::ChangeList with the parameters
+ Modes::ChangeList changelist;
+ ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+ ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
+ if (IS_LOCAL(user))
+ {
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if ((targetuser) && (user != targetuser))
+ {
+ // Local users may only change the modes of other users if a module explicitly allows it
+ user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users");
+ return CMD_FAILURE;
+ }
+
+ // This is a mode change by a local user and modules didn't explicitly allow/deny.
+ // Ensure access checks will happen for each mode being changed.
+ flags |= ModeParser::MODE_CHECKACCESS;
+ }
+ else if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE; // Entire mode change denied by a module
+ }
+ else
+ flags |= ModeParser::MODE_LOCALONLY;
+
+ if (IS_LOCAL(user))
+ ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+ else
+ ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
+
+ if ((ServerInstance->Modes.GetLastParse().empty()) && (targetchannel) && (parameters.size() == 2))
+ {
+ /* Special case for displaying the list for listmodes,
+ * e.g. MODE #chan b, or MODE #chan +b without a parameter
+ */
+ this->DisplayListModes(user, targetchannel, parameters[1]);
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandMode::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
+
+void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
+{
+ seq++;
+
+ for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
+ {
+ unsigned char mletter = *i;
+ if (mletter == '+')
+ continue;
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
+ if (!mh || !mh->IsListMode())
+ return;
+
+ /* Ensure the user doesnt request the same mode twice,
+ * so they can't flood themselves off out of idiocy.
+ */
+ if (sent[mletter] == seq)
+ continue;
+
+ sent[mletter] = seq;
+ ServerInstance->Modes.ShowListModeList(user, chan, mh);
+ }
+}
+
+void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
+{
+ if (targetchannel)
+ {
+ // Display channel's current mode string
+ user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
+ user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
+ }
+ else
+ {
+ if (targetuser == user || user->HasPrivPermission("users/auspex"))
+ {
+ // Display user's current mode string
+ user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
+ if (targetuser->IsOper())
+ {
+ ModeHandler* snomask = ServerInstance->Modes->FindMode('s', MODETYPE_USER);
+ user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
+ }
+ }
+ else
+ {
+ user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
+ }
+ }
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandNick::CommandNick(Module* parent)
+ : SplitCommand(parent, "NICK", 1, 1)
+{
+ works_before_reg = true;
+ syntax = "<newnick>";
+ Penalty = 0;
+}
+
+/** Handle nick changes from users.
+ * NOTE: If you are used to ircds based on ircd2.8, and are looking
+ * for the client introduction code in here, youre in the wrong place.
+ * You need to look in the spanningtree module for this!
+ */
+CmdResult CommandNick::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+ std::string oldnick = user->nick;
+ std::string newnick = parameters[0];
+
+ // anything except the initial NICK gets a flood penalty
+ if (user->registered == REG_ALL)
+ user->CommandFloodPenalty += 4000;
+
+ if (newnick.empty())
+ {
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, "* :Erroneous Nickname");
+ return CMD_FAILURE;
+ }
+
+ if (newnick == "0")
+ {
+ newnick = user->uuid;
+ }
+ else if (!ServerInstance->IsNick(newnick))
+ {
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname", newnick.c_str());
+ return CMD_FAILURE;
+ }
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (user, newnick));
+
+ // If a module denied the change, abort now
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+
+ // Disallow the nick change if <security:restrictbannedusers> is on and there is a ban matching this user in
+ // one of the channels they are on
+ if (ServerInstance->Config->RestrictBannedUsers)
+ {
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
+ {
+ Channel* chan = (*i)->chan;
+ if (chan->GetPrefixValue(user) < VOICE_VALUE && chan->IsBanned(user))
+ {
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", chan->name.c_str());
+ return CMD_FAILURE;
+ }
+ }
+ }
+
+ if (!user->ChangeNick(newnick))
+ return CMD_FAILURE;
+
+ if (user->registered < REG_NICKUSER)
+ {
+ user->registered = (user->registered | REG_NICK);
+ return CommandUser::CheckRegister(user);
+ }
+
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandPart::CommandPart(Module* parent)
+ : Command(parent, "PART", 1, 2)
+{
+ Penalty = 5;
+ syntax = "<channel>{,<channel>} [<reason>]";
+}
+
+CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string reason;
+ if (parameters.size() > 1)
+ {
+ if (IS_LOCAL(user))
+ msgwrap.Wrap(parameters[1], reason);
+ else
+ reason = parameters[1];
+ }
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ Channel* c = ServerInstance->FindChan(parameters[0]);
+
+ if (c)
+ {
+ c->PartUser(user, reason);
+ }
+ else
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandPart::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandQuit::CommandQuit(Module* parent)
+ : Command(parent, "QUIT", 0, 1)
+{
+ works_before_reg = true;
+ syntax = "[<message>]";
+}
+
+CmdResult CommandQuit::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string quitmsg;
+ if (parameters.empty())
+ quitmsg = "Client exited";
+ else if (IS_LOCAL(user))
+ msgwrap.Wrap(parameters[0], quitmsg);
+ else
+ quitmsg = parameters[0];
+
+ std::string* operquit = ServerInstance->OperQuit.get(user);
+ ServerInstance->Users->QuitUser(user, quitmsg, operquit);
+
+ return CMD_SUCCESS;
+}
+
+RouteDescriptor CommandQuit::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+CommandUser::CommandUser(Module* parent)
+ : SplitCommand(parent, "USER", 4, 4)
+{
+ works_before_reg = true;
+ Penalty = 0;
+ syntax = "<username> <localhost> <remotehost> <GECOS>";
+}
+
+CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
+{
+ /* A user may only send the USER command once */
+ if (!(user->registered & REG_USER))
+ {
+ if (!ServerInstance->IsIdent(parameters[0]))
+ {
+ /*
+ * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
+ * -- Craig, and then w00t.
+ */
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, "USER :Your username is not valid");
+ return CMD_FAILURE;
+ }
+ else
+ {
+ /*
+ * The ident field is IDENTMAX+2 in size to account for +1 for the optional
+ * ~ character, and +1 for null termination, therefore we can safely use up to
+ * IDENTMAX here.
+ */
+ user->ChangeIdent(parameters[0]);
+ user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
+ user->registered = (user->registered | REG_USER);
+ }
+ }
+ else
+ {
+ user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
+ return CMD_FAILURE;
+ }
+
+ /* parameters 2 and 3 are local and remote hosts, and are ignored */
+ return CheckRegister(user);
+}
+
+CmdResult CommandUser::CheckRegister(LocalUser* user)
+{
+ // If the user is registered, return CMD_SUCCESS/CMD_FAILURE depending on what modules say, otherwise just
+ // return CMD_SUCCESS without doing anything, knowing the other handler will call us again
+ if (user->registered == REG_NICKUSER)
+ {
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "core_user.h"
+
+/** Handle /PASS.
+ */
+class CommandPass : public SplitCommand
+{
+ public:
+ /** Constructor for pass.
+ */
+ CommandPass(Module* parent)
+ : SplitCommand(parent, "PASS", 1, 1)
+ {
+ works_before_reg = true;
+ Penalty = 0;
+ syntax = "<password>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
+ {
+ // Check to make sure they haven't registered -- Fix by FCS
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
+ return CMD_FAILURE;
+ }
+ user->password = parameters[0];
+
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /PING.
+ */
+class CommandPing : public Command
+{
+ public:
+ /** Constructor for ping.
+ */
+ CommandPing(Module* parent)
+ : Command(parent, "PING", 1, 2)
+ {
+ Penalty = 0;
+ syntax = "<servername> [:<servername>]";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
+ return CMD_SUCCESS;
+ }
+};
+
+/** Handle /PONG.
+ */
+class CommandPong : public Command
+{
+ public:
+ /** Constructor for pong.
+ */
+ CommandPong(Module* parent)
+ : Command(parent, "PONG", 0, 1)
+ {
+ Penalty = 0;
+ syntax = "<ping-text>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ // set the user as alive so they survive to next ping
+ if (IS_LOCAL(user))
+ IS_LOCAL(user)->lastping = 1;
+ return CMD_SUCCESS;
+ }
+};
+
+void MessageWrapper::Wrap(const std::string& message, std::string& out)
+{
+ // If there is a fixed message, it is stored in prefix. Otherwise prefix contains
+ // only the prefix, so append the message and the suffix
+ out.assign(prefix);
+ if (!fixed)
+ out.append(message).append(suffix);
+}
+
+void MessageWrapper::ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname)
+{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+ prefix = tag->getString(fixedname);
+ fixed = (!prefix.empty());
+ if (!fixed)
+ {
+ prefix = tag->getString(prefixname);
+ suffix = tag->getString(suffixname);
+ }
+}
+
+class CoreModUser : public Module
+{
+ CommandAway cmdaway;
+ CommandMode cmdmode;
+ CommandNick cmdnick;
+ CommandPart cmdpart;
+ CommandPass cmdpass;
+ CommandPing cmdping;
+ CommandPong cmdpong;
+ CommandQuit cmdquit;
+ CommandUser cmduser;
+
+ public:
+ CoreModUser()
+ : cmdaway(this), cmdmode(this), cmdnick(this), cmdpart(this), cmdpass(this), cmdping(this)
+ , cmdpong(this), cmdquit(this), cmduser(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ cmdpart.msgwrap.ReadConfig("prefixpart", "suffixpart", "fixedpart");
+ cmdquit.msgwrap.ReadConfig("prefixquit", "suffixquit", "fixedquit");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the AWAY, MODE, NICK, PART, PASS, PING, PONG, QUIT and USER commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModUser)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+class MessageWrapper
+{
+ std::string prefix;
+ std::string suffix;
+ bool fixed;
+
+ public:
+ /**
+ * Wrap the given message according to the config rules
+ * @param message The message to wrap
+ * @param out String where the result is placed
+ */
+ void Wrap(const std::string& message, std::string& out);
+
+ /**
+ * Read the settings from the given config keys (options block)
+ * @param prefixname Name of the config key to read the prefix from
+ * @param suffixname Name of the config key to read the suffix from
+ * @param fixedname Name of the config key to read the fixed string string from.
+ * If this key has a non-empty value, all messages will be replaced with it.
+ */
+ void ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname);
+};
+
+/** Handle /AWAY.
+ */
+class CommandAway : public Command
+{
+ public:
+ /** Constructor for away.
+ */
+ CommandAway(Module* parent);
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+class CommandMode : public Command
+{
+ unsigned int sent[256];
+ unsigned int seq;
+
+ /** Show the list of one or more list modes to a user.
+ * @param user User to send to.
+ * @param chan Channel whose lists to show.
+ * @param mode_sequence Mode letters to show the lists of.
+ */
+ void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
+
+ /** Show the current modes of a channel or a user to a user.
+ * @param user User to show the modes to.
+ * @param targetuser User whose modes to show. NULL if showing the modes of a channel.
+ * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
+ */
+ void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
+
+ public:
+ /** Constructor for mode.
+ */
+ CommandMode(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /NICK.
+ */
+class CommandNick : public SplitCommand
+{
+ public:
+ /** Constructor for nick.
+ */
+ CommandNick(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+};
+
+/** Handle /PART.
+ */
+class CommandPart : public Command
+{
+ public:
+ MessageWrapper msgwrap;
+
+ /** Constructor for part.
+ */
+ CommandPart(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /QUIT.
+ */
+class CommandQuit : public Command
+{
+ public:
+ MessageWrapper msgwrap;
+
+ /** Constructor for quit.
+ */
+ CommandQuit(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User*user);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+/** Handle /USER.
+ */
+class CommandUser : public SplitCommand
+{
+ public:
+ /** Constructor for user.
+ */
+ CommandUser(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
+
+ /** Run the OnUserRegister hook if the user has sent both NICK and USER. Called after an unregistered user
+ * successfully executes the USER or the NICK command.
+ * @param user User to inspect and possibly pass to the OnUserRegister hook
+ * @return CMD_FAILURE if OnUserRegister was called and it returned MOD_RES_DENY, CMD_SUCCESS in every other case
+ * (i.e. if the hook wasn't fired because the user still needs to send NICK/USER or if it was fired and finished with
+ * a non-MOD_RES_DENY result).
+ */
+ static CmdResult CheckRegister(LocalUser* user);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /USERHOST.
+ */
+class CommandUserhost : public Command
+{
+ public:
+ /** Constructor for userhost.
+ */
+ CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1, 5) {
+ syntax = "<nick> [<nick> ...]";
+ }
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+};
+
+CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ const bool has_privs = user->HasPrivPermission("users/auspex");
+
+ std::string retbuf = "302 " + user->nick + " :";
+
+ for (unsigned int i = 0; i < parameters.size(); i++)
+ {
+ User *u = ServerInstance->FindNickOnly(parameters[i]);
+
+ if ((u) && (u->registered == REG_ALL))
+ {
+ retbuf += u->nick;
+
+ if (u->IsOper())
+ retbuf += '*';
+
+ retbuf += '=';
+ retbuf += (u->IsAway() ? '-' : '+');
+ retbuf += u->ident;
+ retbuf += '@';
+ retbuf += (((u == user) || (has_privs)) ? u->host : u->dhost);
+ retbuf += ' ';
+ }
+ }
+
+ user->WriteServ(retbuf);
+
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandUserhost)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /WALLOPS.
+ */
+class CommandWallops : public Command
+{
+ SimpleUserModeHandler wallopsmode;
+
+ public:
+ /** Constructor for wallops.
+ */
+ CommandWallops(Module* parent)
+ : Command(parent, "WALLOPS", 1, 1)
+ , wallopsmode(parent, "wallops", 'w')
+ {
+ flags_needed = 'o';
+ syntax = "<any-text>";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return ROUTE_BROADCAST;
+ }
+};
+
+CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string wallop("WALLOPS :");
+ wallop.append(parameters[0]);
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ User* t = *i;
+ if (t->IsModeSet(wallopsmode))
+ t->WriteFrom(user, wallop);
+ }
+
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandWallops)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /WHO.
+ */
+class CommandWho : public Command
+{
+ bool CanView(Channel* chan, User* user);
+ bool opt_viewopersonly;
+ bool opt_showrealhost;
+ bool opt_realname;
+ bool opt_mode;
+ bool opt_ident;
+ bool opt_metadata;
+ bool opt_port;
+ bool opt_away;
+ bool opt_local;
+ bool opt_far;
+ bool opt_time;
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference invisiblemode;
+
+ Membership* get_first_visible_channel(User* u)
+ {
+ for (User::ChanList::iterator i = u->chans.begin(); i != u->chans.end(); ++i)
+ {
+ Membership* memb = *i;
+ if (!memb->chan->IsModeSet(secretmode))
+ return memb;
+ }
+ return NULL;
+ }
+
+ public:
+ /** Constructor for who.
+ */
+ CommandWho(Module* parent)
+ : Command(parent, "WHO", 1)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , invisiblemode(parent, "invisible")
+ {
+ syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
+ }
+
+ void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults);
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ bool whomatch(User* cuser, User* user, const char* matchtext);
+};
+
+bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
+{
+ bool match = false;
+ bool positive = false;
+
+ if (user->registered != REG_ALL)
+ return false;
+
+ if (opt_local && !IS_LOCAL(user))
+ return false;
+ else if (opt_far && IS_LOCAL(user))
+ return false;
+
+ if (opt_mode)
+ {
+ for (const char* n = matchtext; *n; n++)
+ {
+ if (*n == '+')
+ {
+ positive = true;
+ continue;
+ }
+ else if (*n == '-')
+ {
+ positive = false;
+ continue;
+ }
+ if (user->IsModeSet(*n) != positive)
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ /*
+ * This was previously one awesome pile of ugly nested if, when really, it didn't need
+ * to be, since only one condition was ever checked, a chained if works just fine.
+ * -- w00t
+ */
+ if (opt_metadata)
+ {
+ match = false;
+ const Extensible::ExtensibleStore& list = user->GetExtList();
+ for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
+ if (InspIRCd::Match(i->first->name, matchtext))
+ match = true;
+ }
+ else if (opt_realname)
+ match = InspIRCd::Match(user->fullname, matchtext);
+ else if (opt_showrealhost)
+ match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
+ else if (opt_ident)
+ match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
+ else if (opt_port)
+ {
+ irc::portparser portrange(matchtext, false);
+ long portno = -1;
+ while ((portno = portrange.GetToken()))
+ if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
+ {
+ match = true;
+ break;
+ }
+ }
+ else if (opt_away)
+ match = InspIRCd::Match(user->awaymsg, matchtext);
+ else if (opt_time)
+ {
+ long seconds = InspIRCd::Duration(matchtext);
+
+ // Okay, so time matching, we want all users connected `seconds' ago
+ if (user->age >= ServerInstance->Time() - seconds)
+ match = true;
+ }
+
+ /*
+ * Once the conditionals have been checked, only check dhost/nick/server
+ * if they didn't match this user -- and only match if we don't find a match.
+ *
+ * This should make things minutely faster, and again, less ugly.
+ * -- w00t
+ */
+ if (!match)
+ match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
+
+ if (!match)
+ match = InspIRCd::Match(user->nick, matchtext);
+
+ /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
+ if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
+ match = InspIRCd::Match(user->server->GetName(), matchtext);
+
+ return match;
+ }
+}
+
+bool CommandWho::CanView(Channel* chan, User* user)
+{
+ /* Bug #383 - moved higher up the list, because if we are in the channel
+ * we can see all its users
+ */
+ if (chan->HasUser(user))
+ return true;
+ /* Opers see all */
+ if (user->HasPrivPermission("users/auspex"))
+ return true;
+ /* Cant see inside a +s or a +p channel unless we are a member (see above) */
+ else if (!chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode))
+ return true;
+
+ return false;
+}
+
+void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults)
+{
+ if (!memb)
+ memb = get_first_visible_channel(u);
+
+ std::string wholine = initial + (memb ? memb->chan->name : "*") + " " + u->ident + " " +
+ (opt_showrealhost ? u->host : u->dhost) + " ";
+ if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+ wholine.append(ServerInstance->Config->HideWhoisServer);
+ else
+ wholine.append(u->server->GetName());
+
+ wholine.append(" " + u->nick + " ");
+
+ /* away? */
+ if (u->IsAway())
+ {
+ wholine.append("G");
+ }
+ else
+ {
+ wholine.append("H");
+ }
+
+ /* oper? */
+ if (u->IsOper())
+ {
+ wholine.push_back('*');
+ }
+
+ if (memb)
+ {
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ wholine.push_back(prefix);
+ }
+
+ wholine.append(" :0 " + u->fullname);
+
+ FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, wholine));
+
+ if (!wholine.empty())
+ whoresults.push_back(wholine);
+}
+
+CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ /*
+ * XXX - RFC says:
+ * The <name> passed to WHO is matched against users' host, server, real
+ * name and nickname
+ * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
+ */
+
+ /* WHO options */
+ opt_viewopersonly = false;
+ opt_showrealhost = false;
+ opt_realname = false;
+ opt_mode = false;
+ opt_ident = false;
+ opt_metadata = false;
+ opt_port = false;
+ opt_away = false;
+ opt_local = false;
+ opt_far = false;
+ opt_time = false;
+
+ std::vector<std::string> whoresults;
+ std::string initial = "352 " + user->nick + " ";
+
+ /* Change '0' into '*' so the wildcard matcher can grok it */
+ std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
+
+ // WHO flags count as a wildcard
+ bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos));
+
+ if (parameters.size() > 1)
+ {
+ for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
+ {
+ switch (*iter)
+ {
+ case 'o':
+ opt_viewopersonly = true;
+ break;
+ case 'h':
+ if (user->HasPrivPermission("users/auspex"))
+ opt_showrealhost = true;
+ break;
+ case 'r':
+ opt_realname = true;
+ break;
+ case 'm':
+ if (user->HasPrivPermission("users/auspex"))
+ opt_mode = true;
+ break;
+ case 'M':
+ if (user->HasPrivPermission("users/auspex"))
+ opt_metadata = true;
+ break;
+ case 'i':
+ opt_ident = true;
+ break;
+ case 'p':
+ if (user->HasPrivPermission("users/auspex"))
+ opt_port = true;
+ break;
+ case 'a':
+ opt_away = true;
+ break;
+ case 'l':
+ if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
+ opt_local = true;
+ break;
+ case 'f':
+ if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
+ opt_far = true;
+ break;
+ case 't':
+ opt_time = true;
+ break;
+ }
+ }
+ }
+
+
+ /* who on a channel? */
+ Channel* ch = ServerInstance->FindChan(matchtext);
+
+ if (ch)
+ {
+ if (CanView(ch,user))
+ {
+ bool inside = ch->HasUser(user);
+
+ /* who on a channel. */
+ const Channel::MemberMap& cu = ch->GetUsers();
+ for (Channel::MemberMap::const_iterator i = cu.begin(); i != cu.end(); ++i)
+ {
+ /* None of this applies if we WHO ourselves */
+ if (user != i->first)
+ {
+ /* opers only, please */
+ if (opt_viewopersonly && !i->first->IsOper())
+ continue;
+
+ /* If we're not inside the channel, hide +i users */
+ if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex"))
+ continue;
+ }
+
+ SendWhoLine(user, parameters, initial, i->second, i->first, whoresults);
+ }
+ }
+ }
+ else
+ {
+ /* Match against wildcard of nick, server or host */
+ if (opt_viewopersonly)
+ {
+ /* Showing only opers */
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* oper = *i;
+
+ if (whomatch(user, oper, matchtext.c_str()))
+ {
+ if (!user->SharesChannelWith(oper))
+ {
+ if (usingwildcards && (oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
+ continue;
+ }
+
+ SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
+ }
+ }
+ }
+ else
+ {
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ if (whomatch(user, i->second, matchtext.c_str()))
+ {
+ if (!user->SharesChannelWith(i->second))
+ {
+ if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
+ continue;
+ }
+
+ SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
+ }
+ }
+ }
+ }
+ /* Send the results out */
+ for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
+ user->WriteServ(*n);
+ user->WriteNumeric(RPL_ENDOFWHO, "%s :End of /WHO list.", *parameters[0].c_str() ? parameters[0].c_str() : "*");
+
+ // Penalize the user a bit for large queries
+ // (add one unit of penalty per 200 results)
+ if (IS_LOCAL(user))
+ IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandWho)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/** Handle /WHOIS.
+ */
+class CommandWhois : public SplitCommand
+{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference snomaskmode;
+
+ void SplitChanList(User* source, User* dest, const std::string& cl);
+ void DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle);
+ std::string ChannelList(User* source, User* dest, bool spy);
+
+ public:
+ /** Constructor for whois.
+ */
+ CommandWhois(Module* parent)
+ : SplitCommand(parent, "WHOIS", 1)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , snomaskmode(parent, "snomask")
+ {
+ Penalty = 2;
+ syntax = "<nick>{,<nick>}";
+ }
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+ CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target);
+};
+
+std::string CommandWhois::ChannelList(User* source, User* dest, bool spy)
+{
+ std::string list;
+
+ for (User::ChanList::iterator i = dest->chans.begin(); i != dest->chans.end(); i++)
+ {
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+ /* If the target is the sender, neither +p nor +s is set, or
+ * the channel contains the user, it is not a spy channel
+ */
+ if (spy != (source == dest || !(c->IsModeSet(privatemode) || c->IsModeSet(secretmode)) || c->HasUser(source)))
+ {
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ list.push_back(prefix);
+ list.append(c->name).push_back(' ');
+ }
+ }
+
+ return list;
+}
+
+void CommandWhois::SplitChanList(User* source, User* dest, const std::string& cl)
+{
+ std::string line;
+ std::ostringstream prefix;
+ std::string::size_type start, pos;
+
+ prefix << dest->nick << " :";
+ line = prefix.str();
+ int namelen = ServerInstance->Config->ServerName.length() + 6;
+
+ for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
+ {
+ if (line.length() + namelen + pos - start > 510)
+ {
+ ServerInstance->SendWhoisLine(source, dest, 319, line);
+ line = prefix.str();
+ }
+
+ line.append(cl.substr(start, pos - start + 1));
+ }
+
+ if (line.length() != prefix.str().length())
+ {
+ ServerInstance->SendWhoisLine(source, dest, 319, line);
+ }
+}
+
+void CommandWhois::DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle)
+{
+ ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s * :%s", dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
+ if (user == dest || user->HasPrivPermission("users/auspex"))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 378, "%s :is connecting from %s@%s %s", dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str());
+ }
+
+ std::string cl = ChannelList(user, dest, false);
+ const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
+
+ if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
+ cl.append(ChannelList(user, dest, true));
+
+ SplitChanList(user, dest, cl);
+
+ if (state == ServerConfig::SPYWHOIS_SPLITMSG)
+ {
+ std::string scl = ChannelList(user, dest, true);
+ if (scl.length())
+ {
+ ServerInstance->SendWhoisLine(user, dest, 336, "%s :is on private/secret channels:", dest->nick.c_str());
+ SplitChanList(user, dest, scl);
+ }
+ }
+ if (user != dest && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s :%s", dest->nick.c_str(), ServerInstance->Config->HideWhoisServer.c_str(), ServerInstance->Config->Network.c_str());
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s :%s", dest->nick.c_str(), dest->server->GetName().c_str(), dest->server->GetDesc().c_str());
+ }
+
+ if (dest->IsAway())
+ {
+ ServerInstance->SendWhoisLine(user, dest, 301, "%s :%s", dest->nick.c_str(), dest->awaymsg.c_str());
+ }
+
+ if (dest->IsOper())
+ {
+ if (ServerInstance->Config->GenericOper)
+ ServerInstance->SendWhoisLine(user, dest, 313, "%s :is an IRC operator", dest->nick.c_str());
+ else
+ ServerInstance->SendWhoisLine(user, dest, 313, "%s :is %s %s on %s", dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->name.c_str(), ServerInstance->Config->Network.c_str());
+ }
+
+ if (user == dest || user->HasPrivPermission("users/auspex"))
+ {
+ if (dest->IsModeSet(snomaskmode))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 379, "%s :is using modes +%s %s", dest->nick.c_str(), dest->FormatModes(), snomaskmode->GetUserParameter(dest).c_str());
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 379, "%s :is using modes +%s", dest->nick.c_str(), dest->FormatModes());
+ }
+ }
+
+ FOREACH_MOD(OnWhois, (user,dest));
+
+ /*
+ * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
+ * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
+ */
+ if ((idle) || (signon))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 317, "%s %lu %lu :seconds idle, signon time", dest->nick.c_str(), idle, signon);
+ }
+
+ ServerInstance->SendWhoisLine(user, dest, 318, "%s :End of /WHOIS list.", dest->nick.c_str());
+}
+
+CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target)
+{
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
+
+ User* user = ServerInstance->FindUUID(parameters[0]);
+ if (!user)
+ return CMD_FAILURE;
+
+ unsigned long idle = ConvToInt(parameters.back());
+ DoWhois(user, target, target->signon, idle);
+
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandWhois::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+{
+ User *dest;
+ int userindex = 0;
+ unsigned long idle = 0, signon = 0;
+
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ return CMD_SUCCESS;
+
+ /*
+ * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
+ * does, and use the second one, otherwise, use the only paramter. -- djGrrr
+ */
+ if (parameters.size() > 1)
+ userindex = 1;
+
+ dest = ServerInstance->FindNickOnly(parameters[userindex]);
+
+ if ((dest) && (dest->registered == REG_ALL))
+ {
+ /*
+ * Okay. Umpteenth attempt at doing this, so let's re-comment...
+ * For local users (/w localuser), we show idletime if hidewhois is disabled
+ * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
+ * For remote users (/w remoteuser), we do NOT show idletime
+ * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
+ * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
+ */
+ LocalUser* localuser = IS_LOCAL(dest);
+ if (localuser && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
+ {
+ idle = labs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
+ signon = dest->signon;
+ }
+
+ DoWhois(user,dest,signon,idle);
+ }
+ else
+ {
+ /* no such nick/channel */
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
+ user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", !parameters[userindex].empty() ? parameters[userindex].c_str() : "*");
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+}
+
+COMMAND_INIT(CommandWhois)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "commands/cmd_whowas.h"
+
+CommandWhowas::CommandWhowas( Module* parent)
+ : Command(parent, "WHOWAS", 1)
+{
+ syntax = "<nick>{,<nick>}";
+ Penalty = 2;
+}
+
+CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
+{
+ /* if whowas disabled in config */
+ if (!manager.IsEnabled())
+ {
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", name.c_str());
+ return CMD_FAILURE;
+ }
+
+ const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
+ if (!nick)
+ {
+ user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str());
+ }
+ else
+ {
+ const WhoWas::Nick::List& list = nick->entries;
+ if (!list.empty())
+ {
+ for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ WhoWas::Entry* u = *i;
+
+ user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(),
+ u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
+
+ if (user->HasPrivPermission("users/auspex"))
+ user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s",
+ parameters[0].c_str(), u->host.c_str());
+
+ std::string signon = InspIRCd::TimeString(u->signon);
+ bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+ user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
+ }
+ }
+ }
+
+ user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str());
+ return CMD_SUCCESS;
+}
+
+WhoWas::Manager::Manager()
+ : GroupSize(0), MaxGroups(0), MaxKeep(0)
+{
+}
+
+const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
+{
+ whowas_users::const_iterator it = whowas.find(nickname);
+ if (it == whowas.end())
+ return NULL;
+
+ const Nick* nick = it->second;
+ if (nick->entries.empty())
+ return NULL;
+ return nick;
+}
+
+WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
+{
+ size_t entrycount = 0;
+ for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ entrycount += list.size();
+ }
+
+ Stats stats;
+ stats.entrycount = entrycount;
+ return stats;
+}
+
+void WhoWas::Manager::Add(User* user)
+{
+ if (!IsEnabled())
+ return;
+
+ // Insert nick if it doesn't exist
+ // 'first' will point to the newly inserted element or to the existing element with an equivalent key
+ std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
+
+ if (ret.second) // If inserted
+ {
+ // This nick is new, create a list for it and add the first record to it
+ WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
+ nick->entries.push_back(new Entry(user));
+ ret.first->second = nick;
+
+ // Add this nick to the fifo too
+ whowas_fifo.push_back(nick);
+
+ if (whowas.size() > this->MaxGroups)
+ {
+ // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
+ nick = whowas_fifo.front();
+ whowas_fifo.pop_front();
+ whowas.erase(nick->nick);
+ delete nick;
+ }
+ }
+ else
+ {
+ // We've met this nick before, add a new record to the list
+ WhoWas::Nick::List& list = ret.first->second->entries;
+ list.push_back(new Entry(user));
+
+ // If there are too many records for this nick, remove the oldest (front)
+ if (list.size() > this->GroupSize)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+ }
+}
+
+/* on rehash, refactor maps according to new conf values */
+void WhoWas::Manager::Prune()
+{
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+
+ /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
+ while (!whowas_fifo.empty())
+ {
+ WhoWas::Nick* nick = whowas_fifo.front();
+ if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
+ {
+ /* hopefully redundant integrity check, but added while debugging r6216 */
+ if (!whowas.erase(nick->nick))
+ {
+ /* this should never happen, if it does maps are corrupt */
+ ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
+ return;
+ }
+
+ whowas_fifo.pop_front();
+ delete nick;
+ }
+ else
+ break;
+ }
+
+ /* Then cut the whowas sets to new size (groupsize) */
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ while (list.size() > this->GroupSize)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+ }
+}
+
+/* call maintain once an hour to remove expired nicks */
+void WhoWas::Manager::Maintain()
+{
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick::List& list = i->second->entries;
+ while (!list.empty() && list.front()->signon < min)
+ {
+ delete list.front();
+ list.pop_front();
+ }
+ }
+}
+
+WhoWas::Manager::~Manager()
+{
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+ {
+ WhoWas::Nick* nick = i->second;
+ delete nick;
+ }
+}
+
+bool WhoWas::Manager::IsEnabled() const
+{
+ return ((GroupSize != 0) && (MaxGroups != 0));
+}
+
+void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
+{
+ if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
+ return;
+
+ GroupSize = NewGroupSize;
+ MaxGroups = NewMaxGroups;
+ MaxKeep = NewMaxKeep;
+ Prune();
+}
+
+WhoWas::Entry::Entry(User* user)
+ : host(user->host)
+ , dhost(user->dhost)
+ , ident(user->ident)
+ , server(user->server->GetName())
+ , gecos(user->fullname)
+ , signon(user->signon)
+{
+}
+
+WhoWas::Nick::Nick(const std::string& nickname)
+ : addtime(ServerInstance->Time())
+ , nick(nickname)
+{
+}
+
+WhoWas::Nick::~Nick()
+{
+ stdalgo::delete_all(entries);
+}
+
+class ModuleWhoWas : public Module
+{
+ CommandWhowas cmd;
+
+ public:
+ ModuleWhoWas() : cmd(this)
+ {
+ }
+
+ void OnGarbageCollect()
+ {
+ // Remove all entries older than MaxKeep
+ cmd.manager.Maintain();
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ {
+ cmd.manager.Add(user);
+ }
+
+ ModResult OnStats(char symbol, User* user, string_list &results)
+ {
+ if (symbol == 'z')
+ results.push_back("249 "+user->nick+" :Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
+ unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
+ unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
+ unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+ cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
+ }
+
+ Version GetVersion()
+ {
+ return Version("WHOWAS", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleWhoWas)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+CommandEline::CommandEline(Module* parent)
+ : Command(parent, "ELINE", 1, 3)
+{
+ flags_needed = 'o';
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
+
+/** Handle /ELINE
+ */
+CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string target = parameters[0];
+
+ if (parameters.size() >= 3)
+ {
+ IdentHostPair ih;
+ User* find = ServerInstance->FindNick(target);
+ if ((find) && (find->registered == REG_ALL))
+ {
+ ih.first = "*";
+ ih.second = find->GetIPString();
+ target = std::string("*@") + find->GetIPString();
+ }
+ else
+ ih = ServerInstance->XLines->IdentSplit(target);
+
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
+
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "E", "hostmasks"))
+ return CMD_FAILURE;
+
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
+ ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+ if (ServerInstance->XLines->AddLine(el, user))
+ {
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s: %s", user->nick.c_str(), target.c_str(), parameters[2].c_str());
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
+ timestr.c_str(), parameters[2].c_str());
+ }
+ }
+ else
+ {
+ delete el;
+ user->WriteNotice("*** E-Line for " + target + " already exists");
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->DelLine(target.c_str(), "E", user))
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed E-line on %s",user->nick.c_str(),target.c_str());
+ }
+ else
+ {
+ user->WriteNotice("*** E-Line " + target + " not found in list, try /stats e");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+CommandGline::CommandGline(Module* parent)
+ : Command(parent, "GLINE", 1, 3)
+{
+ flags_needed = 'o';
+ Penalty = 0;
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
+
+/** Handle /GLINE
+ */
+CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string target = parameters[0];
+
+ if (parameters.size() >= 3)
+ {
+ IdentHostPair ih;
+ User* find = ServerInstance->FindNick(target);
+ if ((find) && (find->registered == REG_ALL))
+ {
+ ih.first = "*";
+ ih.second = find->GetIPString();
+ target = std::string("*@") + find->GetIPString();
+ }
+ else
+ ih = ServerInstance->XLines->IdentSplit(target);
+
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
+
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "G", "hostmasks"))
+ return CMD_FAILURE;
+
+ else if (target.find('!') != std::string::npos)
+ {
+ user->WriteNotice("*** G-Line cannot operate on nick!user@host masks");
+ return CMD_FAILURE;
+ }
+
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
+ GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+ if (ServerInstance->XLines->AddLine(gl, user))
+ {
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
+ timestr.c_str(), parameters[2].c_str());
+ }
+
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete gl;
+ user->WriteNotice("** G-Line for " + target + " already exists");
+ }
+
+ }
+ else
+ {
+ if (ServerInstance->XLines->DelLine(target.c_str(),"G",user))
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed G-line on %s",user->nick.c_str(),target.c_str());
+ }
+ else
+ {
+ user->WriteNotice("*** G-Line " + target + " not found in list, try /stats g.");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+CommandKline::CommandKline(Module* parent)
+ : Command(parent, "KLINE", 1, 3)
+{
+ flags_needed = 'o';
+ Penalty = 0;
+ syntax = "<ident@host> [<duration> :<reason>]";
+}
+
+/** Handle /KLINE
+ */
+CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string target = parameters[0];
+
+ if (parameters.size() >= 3)
+ {
+ IdentHostPair ih;
+ User* find = ServerInstance->FindNick(target);
+ if ((find) && (find->registered == REG_ALL))
+ {
+ ih.first = "*";
+ ih.second = find->GetIPString();
+ target = std::string("*@") + find->GetIPString();
+ }
+ else
+ ih = ServerInstance->XLines->IdentSplit(target);
+
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
+
+ InsaneBan::IPHostMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "K", "hostmasks"))
+ return CMD_FAILURE;
+
+ if (target.find('!') != std::string::npos)
+ {
+ user->WriteNotice("*** K-Line cannot operate on nick!user@host masks");
+ return CMD_FAILURE;
+ }
+
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
+ KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
+ if (ServerInstance->XLines->AddLine(kl,user))
+ {
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s: %s",user->nick.c_str(),target.c_str(), parameters[2].c_str());
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s: %s",user->nick.c_str(),target.c_str(),
+ timestr.c_str(), parameters[2].c_str());
+ }
+
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete kl;
+ user->WriteNotice("*** K-Line for " + target + " already exists");
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->DelLine(target.c_str(),"K",user))
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed K-line on %s",user->nick.c_str(),target.c_str());
+ }
+ else
+ {
+ user->WriteNotice("*** K-Line " + target + " not found in list, try /stats k.");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+CommandQline::CommandQline(Module* parent)
+ : Command(parent, "QLINE", 1, 3)
+{
+ flags_needed = 'o';
+ Penalty = 0;
+ syntax = "<nick> [<duration> :<reason>]";
+}
+
+CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ if (parameters.size() >= 3)
+ {
+ NickMatcher matcher;
+ if (InsaneBan::MatchesEveryone(parameters[0], matcher, user, "Q", "nickmasks"))
+ return CMD_FAILURE;
+
+ if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
+ {
+ user->WriteNotice("*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.");
+ return CMD_FAILURE;
+ }
+
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
+ QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
+ if (ServerInstance->XLines->AddLine(ql,user))
+ {
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s: %s",user->nick.c_str(), parameters[0].c_str(), parameters[2].c_str());
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s: %s",user->nick.c_str(),parameters[0].c_str(),
+ timestr.c_str(), parameters[2].c_str());
+ }
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete ql;
+ user->WriteNotice("*** Q-Line for " + parameters[0] + " already exists");
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "Q", user))
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed Q-line on %s",user->nick.c_str(),parameters[0].c_str());
+ }
+ else
+ {
+ user->WriteNotice("*** Q-Line " + parameters[0] + " not found in list, try /stats q.");
+ return CMD_FAILURE;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+bool CommandQline::NickMatcher::Check(User* user, const std::string& nick) const
+{
+ return InspIRCd::Match(user->nick, nick);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2009 Matt Smith <dz@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+CommandZline::CommandZline(Module* parent)
+ : Command(parent, "ZLINE", 1, 3)
+{
+ flags_needed = 'o';
+ Penalty = 0;
+ syntax = "<ipmask> [<duration> :<reason>]";
+}
+
+CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User *user)
+{
+ std::string target = parameters[0];
+
+ if (parameters.size() >= 3)
+ {
+ if (target.find('!') != std::string::npos)
+ {
+ user->WriteNotice("*** You cannot include a nickname in a zline, a zline must ban only an IP mask");
+ return CMD_FAILURE;
+ }
+
+ User *u = ServerInstance->FindNick(target);
+
+ if ((u) && (u->registered == REG_ALL))
+ {
+ target = u->GetIPString();
+ }
+
+ const char* ipaddr = target.c_str();
+
+ if (strchr(ipaddr,'@'))
+ {
+ while (*ipaddr != '@')
+ ipaddr++;
+ ipaddr++;
+ }
+
+ IPMatcher matcher;
+ if (InsaneBan::MatchesEveryone(ipaddr, matcher, user, "Z", "ipmasks"))
+ return CMD_FAILURE;
+
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
+ ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
+ if (ServerInstance->XLines->AddLine(zl,user))
+ {
+ if (!duration)
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s: %s", user->nick.c_str(), ipaddr, parameters[2].c_str());
+ }
+ else
+ {
+ time_t c_requires_crap = duration + ServerInstance->Time();
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
+ ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s: %s",user->nick.c_str(),ipaddr,
+ timestr.c_str(), parameters[2].c_str());
+ }
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete zl;
+ user->WriteNotice("*** Z-Line for " + std::string(ipaddr) + " already exists");
+ }
+ }
+ else
+ {
+ if (ServerInstance->XLines->DelLine(target.c_str(),"Z",user))
+ {
+ ServerInstance->SNO->WriteToSnoMask('x',"%s removed Z-line on %s",user->nick.c_str(),target.c_str());
+ }
+ else
+ {
+ user->WriteNotice("*** Z-Line " + target + " not found in list, try /stats Z.");
+ return CMD_FAILURE;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+bool CommandZline::IPMatcher::Check(User* user, const std::string& ip) const
+{
+ return InspIRCd::Match(user->GetIPString(), ip, ascii_case_insensitive_map);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+#include "core_xline.h"
+
+bool InsaneBan::MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey)
+{
+ ConfigTag* insane = ServerInstance->Config->ConfValue("insane");
+
+ if (insane->getBool(confkey))
+ return false;
+
+ float itrigger = insane->getFloat("trigger", 95.5);
+
+ long matches = test.Run(mask);
+
+ if (!matches)
+ return false;
+
+ float percent = ((float)matches / (float)ServerInstance->Users->GetUsers().size()) * 100;
+ if (percent > itrigger)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "\2WARNING\2: %s tried to set a %s-line mask of %s, which covers %.2f%% of the network!", user->nick.c_str(), bantype, mask.c_str(), percent);
+ return true;
+ }
+ return false;
+}
+
+bool InsaneBan::IPHostMatcher::Check(User* user, const std::string& mask) const
+{
+ return ((InspIRCd::Match(user->MakeHost(), mask, ascii_case_insensitive_map)) ||
+ (InspIRCd::Match(user->MakeHostIP(), mask, ascii_case_insensitive_map)));
+}
+
+class CoreModXLine : public Module
+{
+ CommandEline cmdeline;
+ CommandGline cmdgline;
+ CommandKline cmdkline;
+ CommandQline cmdqline;
+ CommandZline cmdzline;
+
+ public:
+ CoreModXLine()
+ : cmdeline(this), cmdgline(this), cmdkline(this), cmdqline(this), cmdzline(this)
+ {
+ }
+
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
+ {
+ // Check Q-Lines (for local nick changes only, remote servers have our Q-Lines to enforce themselves)
+
+ XLine* xline = ServerInstance->XLines->MatchesLine("Q", newnick);
+ if (!xline)
+ return MOD_RES_PASSTHRU; // No match
+
+ // A Q-Line matched the new nick, tell opers if the user is registered
+ if (user->registered == REG_ALL)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
+ newnick.c_str(), user->GetFullRealHost().c_str(), xline->reason.c_str());
+ }
+
+ // Send a numeric because if we deny then the core doesn't reply anything
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Invalid nickname: %s", newnick.c_str(), xline->reason.c_str());
+ return MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the ELINE, GLINE, KLINE, QLINE, and ZLINE commands", VF_VENDOR|VF_CORE);
+ }
+};
+
+MODULE_INIT(CoreModXLine)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "inspircd.h"
+
+class InsaneBan
+{
+ public:
+ class MatcherBase
+ {
+ public:
+ virtual long Run(const std::string& mask) = 0;
+ };
+
+ template <typename T>
+ class Matcher : public MatcherBase
+ {
+ public:
+ long Run(const std::string& mask)
+ {
+ long matches = 0;
+ const T* c = static_cast<T*>(this);
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ if (c->Check(i->second, mask))
+ matches++;
+ }
+ return matches;
+ }
+ };
+
+ class IPHostMatcher : public Matcher<IPHostMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ /** Check if the given mask matches too many users according to the config, send an announcement if yes
+ * @param mask A mask to match against
+ * @param test The test that determines if a user matches the mask or not
+ * @param user A user whose nick will be included in the announcement if one is made
+ * @param bantype Type of the ban being set, will be used in the announcement if one is made
+ * @param confkey Name of the config key (inside the insane tag) which if false disables any checking
+ * @return True if the given mask matches too many users, false if not
+ */
+ static bool MatchesEveryone(const std::string& mask, MatcherBase& test, User* user, const char* bantype, const char* confkey);
+};
+
+/** Handle /ELINE.
+ */
+class CommandEline : public Command
+{
+ public:
+ /** Constructor for eline.
+ */
+ CommandEline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /GLINE.
+ */
+class CommandGline : public Command
+{
+ public:
+ /** Constructor for gline.
+ */
+ CommandGline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /KLINE.
+ */
+class CommandKline : public Command
+{
+ public:
+ /** Constructor for kline.
+ */
+ CommandKline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /QLINE.
+ */
+class CommandQline : public Command
+{
+ class NickMatcher : public InsaneBan::Matcher<NickMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ public:
+ /** Constructor for qline.
+ */
+ CommandQline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param pcnt The number of parameters passed to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
+
+/** Handle /ZLINE.
+ */
+class CommandZline : public Command
+{
+ class IPMatcher : public InsaneBan::Matcher<IPMatcher>
+ {
+ public:
+ bool Check(User* user, const std::string& mask) const;
+ };
+
+ public:
+ /** Constructor for zline.
+ */
+ CommandZline(Module* parent);
+
+ /** Handle command.
+ * @param parameters The parameters to the command
+ * @param user The user issuing the command
+ * @return A value from CmdResult to indicate command success or failure.
+ */
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+};
classbase* c = list[i];
if (gone.insert(c).second)
{
- ServerInstance->Logs->Log("CULLLIST", DEBUG, "Deleting %s @%p", typeid(*c).name(),
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting %s @%p", typeid(*c).name(),
(void*)c);
c->cull();
queue.push_back(c);
}
else
{
- ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Object @%p culled twice!",
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Object @%p culled twice!",
(void*)c);
}
}
}
if (list.size())
{
- ServerInstance->Logs->Log("CULLLIST",DEBUG, "WARNING: Objects added to cull list in a destructor");
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "WARNING: Objects added to cull list in a destructor");
Apply();
}
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $Core */
-
-/*
-dns.cpp - Nonblocking DNS functions.
-Very very loosely based on the firedns library,
-Copyright (C) 2002 Ian Gulliver. This file is no
-longer anything like firedns, there are many major
-differences between this code and the original.
-Please do not assume that firedns works like this,
-looks like this, walks like this or tastes like this.
-*/
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#else
-#include "inspircd_win32wrapper.h"
-#endif
-
-#include "inspircd.h"
-#include "socketengine.h"
-#include "configreader.h"
-#include "socket.h"
-
-#define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */
-
-/** Masks to mask off the responses we get from the DNSRequest methods
- */
-enum QueryInfo
-{
- ERROR_MASK = 0x10000 /* Result is an error */
-};
-
-/** Flags which can be ORed into a request or reply for different meanings
- */
-enum QueryFlags
-{
- FLAGS_MASK_RD = 0x01, /* Recursive */
- FLAGS_MASK_TC = 0x02,
- FLAGS_MASK_AA = 0x04, /* Authoritative */
- FLAGS_MASK_OPCODE = 0x78,
- FLAGS_MASK_QR = 0x80,
- FLAGS_MASK_RCODE = 0x0F, /* Request */
- FLAGS_MASK_Z = 0x70,
- FLAGS_MASK_RA = 0x80
-};
-
-
-/** Represents a dns resource record (rr)
- */
-struct ResourceRecord
-{
- QueryType type; /* Record type */
- unsigned int rr_class; /* Record class */
- unsigned long ttl; /* Time to live */
- unsigned int rdlength; /* Record length */
-};
-
-/** Represents a dns request/reply header, and its payload as opaque data.
- */
-class DNSHeader
-{
- public:
- unsigned char id[2]; /* Request id */
- unsigned int flags1; /* Flags */
- unsigned int flags2; /* Flags */
- unsigned int qdcount;
- unsigned int ancount; /* Answer count */
- unsigned int nscount; /* Nameserver count */
- unsigned int arcount;
- unsigned char payload[512]; /* Packet payload */
-};
-
-class DNSRequest
-{
- public:
- unsigned char id[2]; /* Request id */
- unsigned char* res; /* Result processing buffer */
- unsigned int rr_class; /* Request class */
- QueryType type; /* Request type */
- DNS* dnsobj; /* DNS caller (where we get our FD from) */
- unsigned long ttl; /* Time to live */
- std::string orig; /* Original requested name/ip */
-
- DNSRequest(DNS* dns, int id, const std::string &original);
- ~DNSRequest();
- DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
- int SendRequests(const DNSHeader *header, const int length, QueryType qt);
-};
-
-class CacheTimer : public Timer
-{
- private:
- DNS* dns;
- public:
- CacheTimer(DNS* thisdns)
- : Timer(3600, ServerInstance->Time(), true), dns(thisdns) { }
-
- virtual void Tick(time_t)
- {
- dns->PruneCache();
- }
-};
-
-class RequestTimeout : public Timer
-{
- DNSRequest* watch;
- int watchid;
- public:
- RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
- {
- }
- ~RequestTimeout()
- {
- if (ServerInstance->Res)
- Tick(0);
- }
-
- void Tick(time_t)
- {
- if (ServerInstance->Res->requests[watchid] == watch)
- {
- /* Still exists, whack it */
- if (ServerInstance->Res->Classes[watchid])
- {
- ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
- delete ServerInstance->Res->Classes[watchid];
- ServerInstance->Res->Classes[watchid] = NULL;
- }
- ServerInstance->Res->requests[watchid] = NULL;
- delete watch;
- }
- }
-};
-
-CachedQuery::CachedQuery(const std::string &res, QueryType qt, unsigned int ttl) : data(res), type(qt)
-{
- expires = ServerInstance->Time() + ttl;
-}
-
-int CachedQuery::CalcTTLRemaining()
-{
- int n = expires - ServerInstance->Time();
- return (n < 0 ? 0 : n);
-}
-
-/* Allocate the processing buffer */
-DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
-{
- /* hardening against overflow here: make our work buffer twice the theoretical
- * maximum size so that hostile input doesn't screw us over.
- */
- res = new unsigned char[sizeof(DNSHeader) * 2];
- *res = 0;
- orig = original;
- RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
- ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
-}
-
-/* Deallocate the processing buffer */
-DNSRequest::~DNSRequest()
-{
- delete[] res;
-}
-
-/** Fill a ResourceRecord class based on raw data input */
-inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
-{
- rr->type = (QueryType)((input[0] << 8) + input[1]);
- rr->rr_class = (input[2] << 8) + input[3];
- rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
- rr->rdlength = (input[8] << 8) + input[9];
-}
-
-/** Fill a DNSHeader class based on raw data input of a given length */
-inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
-{
- header->id[0] = input[0];
- header->id[1] = input[1];
- header->flags1 = input[2];
- header->flags2 = input[3];
- header->qdcount = (input[4] << 8) + input[5];
- header->ancount = (input[6] << 8) + input[7];
- header->nscount = (input[8] << 8) + input[9];
- header->arcount = (input[10] << 8) + input[11];
- memcpy(header->payload,&input[12],length);
-}
-
-/** Empty a DNSHeader class out into raw data, ready for transmission */
-inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
-{
- output[0] = header->id[0];
- output[1] = header->id[1];
- output[2] = header->flags1;
- output[3] = header->flags2;
- output[4] = header->qdcount >> 8;
- output[5] = header->qdcount & 0xFF;
- output[6] = header->ancount >> 8;
- output[7] = header->ancount & 0xFF;
- output[8] = header->nscount >> 8;
- output[9] = header->nscount & 0xFF;
- output[10] = header->arcount >> 8;
- output[11] = header->arcount & 0xFF;
- memcpy(&output[12],header->payload,length);
-}
-
-/** Send requests we have previously built down the UDP socket */
-int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
-{
- ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
-
- unsigned char payload[sizeof(DNSHeader)];
-
- this->rr_class = 1;
- this->type = qt;
-
- DNS::EmptyHeader(payload,header,length);
-
- if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
- return -1;
-
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
- return 0;
-}
-
-/** Add a query with a predefined header, and allocate an ID for it. */
-DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
-{
- /* Is the DNS connection down? */
- if (this->GetFd() == -1)
- return NULL;
-
- /* Create an id */
- unsigned int tries = 0;
- do {
- id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
- if (++tries == DNS::MAX_REQUEST_ID*5)
- {
- // If we couldn't find an empty slot this many times, do a sequential scan as a last
- // resort. If an empty slot is found that way, go on, otherwise throw an exception
- id = -1;
- for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
- {
- if (!requests[i])
- {
- id = i;
- break;
- }
- }
-
- if (id == -1)
- throw ModuleException("DNS: All ids are in use");
-
- break;
- }
- } while (requests[id]);
-
- DNSRequest* req = new DNSRequest(this, id, original);
-
- header->id[0] = req->id[0] = id >> 8;
- header->id[1] = req->id[1] = id & 0xFF;
- header->flags1 = FLAGS_MASK_RD;
- header->flags2 = 0;
- header->qdcount = 1;
- header->ancount = 0;
- header->nscount = 0;
- header->arcount = 0;
-
- /* At this point we already know the id doesnt exist,
- * so there needs to be no second check for the ::end()
- */
- requests[id] = req;
-
- /* According to the C++ spec, new never returns NULL. */
- return req;
-}
-
-int DNS::ClearCache()
-{
- /* This ensures the buckets are reset to sane levels */
- int rv = this->cache->size();
- delete this->cache;
- this->cache = new dnscache();
- return rv;
-}
-
-int DNS::PruneCache()
-{
- int n = 0;
- dnscache* newcache = new dnscache();
- for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
- /* Dont include expired items (theres no point) */
- if (i->second.CalcTTLRemaining())
- newcache->insert(*i);
- else
- n++;
-
- delete this->cache;
- this->cache = newcache;
- return n;
-}
-
-void DNS::Rehash()
-{
- if (this->GetFd() > -1)
- {
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
-
- /* Rehash the cache */
- this->PruneCache();
- }
- else
- {
- /* Create initial dns cache */
- this->cache = new dnscache();
- }
-
- irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
-
- /* Initialize mastersocket */
- int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
- this->SetFd(s);
-
- /* Have we got a socket and is it nonblocking? */
- if (this->GetFd() != -1)
- {
- ServerInstance->SE->SetReuse(s);
- ServerInstance->SE->NonBlocking(s);
- irc::sockets::sockaddrs bindto;
- memset(&bindto, 0, sizeof(bindto));
- bindto.sa.sa_family = myserver.sa.sa_family;
- if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
- {
- /* Failed to bind */
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
- }
- else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
- {
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
- }
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
- }
-}
-
-/** Initialise the DNS UDP socket so that we can send requests */
-DNS::DNS()
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
- /* Clear the Resolver class table */
- memset(Classes,0,sizeof(Classes));
-
- /* Clear the requests class table */
- memset(requests,0,sizeof(requests));
-
- /* DNS::Rehash() sets this to a valid ptr
- */
- this->cache = NULL;
-
- /* Again, DNS::Rehash() sets this to a
- * valid value
- */
- this->SetFd(-1);
-
- /* Actually read the settings
- */
- this->Rehash();
-
- this->PruneTimer = new CacheTimer(this);
-
- ServerInstance->Timers->AddTimer(this->PruneTimer);
-}
-
-/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
-int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
-{
- short payloadpos = 0;
- const char* tempchr, *tempchr2 = name;
- unsigned short length;
-
- /* split name up into labels, create query */
- while ((tempchr = strchr(tempchr2,'.')) != NULL)
- {
- length = tempchr - tempchr2;
- if (payloadpos + length + 1 > 507)
- return -1;
- payload[payloadpos++] = length;
- memcpy(&payload[payloadpos],tempchr2,length);
- payloadpos += length;
- tempchr2 = &tempchr[1];
- }
- length = strlen(tempchr2);
- if (length)
- {
- if (payloadpos + length + 2 > 507)
- return -1;
- payload[payloadpos++] = length;
- memcpy(&payload[payloadpos],tempchr2,length);
- payloadpos += length;
- payload[payloadpos++] = 0;
- }
- if (payloadpos > 508)
- return -1;
- length = htons(rr);
- memcpy(&payload[payloadpos],&length,2);
- length = htons(rr_class);
- memcpy(&payload[payloadpos + 2],&length,2);
- return payloadpos + 4;
-}
-
-/** Start lookup of an hostname to an IP address */
-int DNS::GetIP(const char *name)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, name);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of an hostname to an IPv6 address */
-int DNS::GetIP6(const char *name)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, name);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of a cname to another name */
-int DNS::GetCName(const char *alias)
-{
- DNSHeader h;
- int id;
- int length;
-
- if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
- return -1;
-
- DNSRequest* req = this->AddQuery(&h, id, alias);
-
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
- return -1;
-
- return id;
-}
-
-/** Start lookup of an IP address to a hostname */
-int DNS::GetNameForce(const char *ip, ForceProtocol fp)
-{
- char query[128];
- DNSHeader h;
- int id;
- int length;
-
- if (fp == PROTOCOL_IPV6)
- {
- in6_addr i;
- if (inet_pton(AF_INET6, ip, &i) > 0)
- {
- DNS::MakeIP6Int(query, &i);
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
- /* Invalid IP address */
- return -1;
- }
- }
- else
- {
- in_addr i;
- if (inet_aton(ip, &i))
- {
- unsigned char* c = (unsigned char*)&i.s_addr;
- sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
- /* Invalid IP address */
- return -1;
- }
- }
-
- length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
- if (length == -1)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
- return -1;
- }
-
- DNSRequest* req = this->AddQuery(&h, id, ip);
-
- if (!req)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
- return -1;
- }
-
- if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
- return -1;
- }
-
- return id;
-}
-
-/** Build an ipv6 reverse domain from an in6_addr
- */
-void DNS::MakeIP6Int(char* query, const in6_addr *ip)
-{
- const char* hex = "0123456789abcdef";
- for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
- {
- if (index % 2)
- /* low nibble */
- *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
- else
- /* high nibble */
- *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
- *query++ = '.'; /* Seperator */
- }
- strcpy(query,"ip6.arpa"); /* Suffix the string */
-}
-
-/** Return the next id which is ready, and the result attached to it */
-DNSResult DNS::GetResult()
-{
- /* Fetch dns query response and decide where it belongs */
- DNSHeader header;
- DNSRequest *req;
- unsigned char buffer[sizeof(DNSHeader)];
- irc::sockets::sockaddrs from;
- memset(&from, 0, sizeof(from));
- socklen_t x = sizeof(from);
-
- int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
-
- /* Did we get the whole header? */
- if (length < 12)
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
- /* Nope - something screwed up. */
- return DNSResult(-1,"",0,"");
- }
-
- /* Check wether the reply came from a different DNS
- * server to the one we sent it to, or the source-port
- * is not 53.
- * A user could in theory still spoof dns packets anyway
- * but this is less trivial than just sending garbage
- * to the server, which is possible without this check.
- *
- * -- Thanks jilles for pointing this one out.
- */
- if (from != myserver)
- {
- std::string server1 = from.str();
- std::string server2 = myserver.str();
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
- server1.c_str(), server2.c_str());
- return DNSResult(-1,"",0,"");
- }
-
- /* Put the read header info into a header class */
- DNS::FillHeader(&header,buffer,length - 12);
-
- /* Get the id of this request.
- * Its a 16 bit value stored in two char's,
- * so we use logic shifts to create the value.
- */
- unsigned long this_id = header.id[1] + (header.id[0] << 8);
-
- /* Do we have a pending request matching this id? */
- if (!requests[this_id])
- {
- /* Somehow we got a DNS response for a request we never made... */
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
- return DNSResult(-1,"",0,"");
- }
- else
- {
- /* Remove the query from the list of pending queries */
- req = requests[this_id];
- requests[this_id] = NULL;
- }
-
- /* Inform the DNSRequest class that it has a result to be read.
- * When its finished it will return a DNSInfo which is a pair of
- * unsigned char* resource record data, and an error message.
- */
- DNSInfo data = req->ResultIsReady(header, length);
- std::string resultstr;
-
- /* Check if we got a result, if we didnt, its an error */
- if (data.first == NULL)
- {
- /* An error.
- * Mask the ID with the value of ERROR_MASK, so that
- * the dns_deal_with_classes() function knows that its
- * an error response and needs to be treated uniquely.
- * Put the error message in the second field.
- */
- std::string ro = req->orig;
- delete req;
- return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
- }
- else
- {
- unsigned long ttl = req->ttl;
- char formatted[128];
-
- /* Forward lookups come back as binary data. We must format them into ascii */
- switch (req->type)
- {
- case DNS_QUERY_A:
- snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
- resultstr = formatted;
- break;
-
- case DNS_QUERY_AAAA:
- {
- if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
- {
- std::string ro = req->orig;
- delete req;
- return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
- }
-
- resultstr = formatted;
-
- /* Special case. Sending ::1 around between servers
- * and to clients is dangerous, because the : on the
- * start makes the client or server interpret the IP
- * as the last parameter on the line with a value ":1".
- */
- if (*formatted == ':')
- resultstr.insert(0, "0");
- }
- break;
-
- case DNS_QUERY_CNAME:
- /* Identical handling to PTR */
-
- case DNS_QUERY_PTR:
- /* Reverse lookups just come back as char* */
- resultstr = std::string((const char*)data.first);
- break;
-
- default:
- break;
- }
-
- /* Build the reply with the id and hostname/ip in it */
- std::string ro = req->orig;
- DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
- delete req;
- return result;
- }
-}
-
-/** A result is ready, process it */
-DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
-{
- unsigned i = 0, o;
- int q = 0;
- int curanswer;
- ResourceRecord rr;
- unsigned short ptr;
-
- /* This is just to keep _FORTIFY_SOURCE happy */
- rr.type = DNS_QUERY_NONE;
- rr.rdlength = 0;
- rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
- rr.rr_class = 0; /* Same for VC++ */
-
- if (!(header.flags1 & FLAGS_MASK_QR))
- return std::make_pair((unsigned char*)NULL,"Not a query result");
-
- if (header.flags1 & FLAGS_MASK_OPCODE)
- return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
-
- if (header.flags2 & FLAGS_MASK_RCODE)
- return std::make_pair((unsigned char*)NULL,"Domain name not found");
-
- if (header.ancount < 1)
- return std::make_pair((unsigned char*)NULL,"No resource records returned");
-
- /* Subtract the length of the header from the length of the packet */
- length -= 12;
-
- while ((unsigned int)q < header.qdcount && i < length)
- {
- if (header.payload[i] > 63)
- {
- i += 6;
- q++;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- q++;
- i += 5;
- }
- else i += header.payload[i] + 1;
- }
- }
- curanswer = 0;
- while ((unsigned)curanswer < header.ancount)
- {
- q = 0;
- while (q == 0 && i < length)
- {
- if (header.payload[i] > 63)
- {
- i += 2;
- q = 1;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- i++;
- q = 1;
- }
- else i += header.payload[i] + 1; /* skip length and label */
- }
- }
- if (static_cast<int>(length - i) < 10)
- return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
-
- /* XXX: We actually initialise 'rr' here including its ttl field */
- DNS::FillResourceRecord(&rr,&header.payload[i]);
-
- i += 10;
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver: rr.type is %d and this.type is %d rr.class %d this.class %d", rr.type, this->type, rr.rr_class, this->rr_class);
- if (rr.type != this->type)
- {
- curanswer++;
- i += rr.rdlength;
- continue;
- }
- if (rr.rr_class != this->rr_class)
- {
- curanswer++;
- i += rr.rdlength;
- continue;
- }
- break;
- }
- if ((unsigned int)curanswer == header.ancount)
- return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
-
- if (i + rr.rdlength > (unsigned int)length)
- return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
-
- if (rr.rdlength > 1023)
- return std::make_pair((unsigned char*)NULL,"Resource record too large");
-
- this->ttl = rr.ttl;
-
- switch (rr.type)
- {
- /*
- * CNAME and PTR are compressed. We need to decompress them.
- */
- case DNS_QUERY_CNAME:
- case DNS_QUERY_PTR:
- {
- unsigned short lowest_pos = length;
- o = 0;
- q = 0;
- while (q == 0 && i < length && o + 256 < 1023)
- {
- /* DN label found (byte over 63) */
- if (header.payload[i] > 63)
- {
- memcpy(&ptr,&header.payload[i],2);
-
- i = ntohs(ptr);
-
- /* check that highest two bits are set. if not, we've been had */
- if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
- return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
-
- /* mask away the two highest bits. */
- i &= ~DN_COMP_BITMASK;
-
- /* and decrease length by 12 bytes. */
- i -= 12;
-
- if (i >= lowest_pos)
- return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
- lowest_pos = i;
- }
- else
- {
- if (header.payload[i] == 0)
- {
- q = 1;
- }
- else
- {
- res[o] = 0;
- if (o != 0)
- res[o++] = '.';
-
- if (o + header.payload[i] > sizeof(DNSHeader))
- return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
-
- memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
- o += header.payload[i];
- i += header.payload[i] + 1;
- }
- }
- }
- res[o] = 0;
- }
- break;
- case DNS_QUERY_AAAA:
- if (rr.rdlength != sizeof(struct in6_addr))
- return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
-
- memcpy(res,&header.payload[i],rr.rdlength);
- res[rr.rdlength] = 0;
- break;
- case DNS_QUERY_A:
- if (rr.rdlength != sizeof(struct in_addr))
- return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
-
- memcpy(res,&header.payload[i],rr.rdlength);
- res[rr.rdlength] = 0;
- break;
- default:
- return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
- break;
- }
- return std::make_pair(res,"No error");
-}
-
-/** Close the master socket */
-DNS::~DNS()
-{
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- ServerInstance->Timers->DelTimer(this->PruneTimer);
- if (cache)
- delete cache;
-}
-
-CachedQuery* DNS::GetCache(const std::string &source)
-{
- dnscache::iterator x = cache->find(source.c_str());
- if (x != cache->end())
- return &(x->second);
- else
- return NULL;
-}
-
-void DNS::DelCache(const std::string &source)
-{
- cache->erase(source.c_str());
-}
-
-void Resolver::TriggerCachedResult()
-{
- if (CQ)
- OnLookupComplete(CQ->data, time_left, true);
-}
-
-/** High level abstraction of dns used by application at large */
-Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
- cached = false;
-
- CQ = ServerInstance->Res->GetCache(source);
- if (CQ)
- {
- time_left = CQ->CalcTTLRemaining();
- if (!time_left)
- {
- ServerInstance->Res->DelCache(source);
- }
- else if (CQ->type == qt)
- {
- cached = true;
- return;
- }
- CQ = NULL;
- }
-
- switch (querytype)
- {
- case DNS_QUERY_A:
- this->myid = ServerInstance->Res->GetIP(source.c_str());
- break;
-
- case DNS_QUERY_PTR4:
- querytype = DNS_QUERY_PTR;
- this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
- break;
-
- case DNS_QUERY_PTR6:
- querytype = DNS_QUERY_PTR;
- this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
- break;
-
- case DNS_QUERY_AAAA:
- this->myid = ServerInstance->Res->GetIP6(source.c_str());
- break;
-
- case DNS_QUERY_CNAME:
- this->myid = ServerInstance->Res->GetCName(source.c_str());
- break;
-
- default:
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
- this->myid = -1;
- break;
- }
- if (this->myid == -1)
- {
- throw ModuleException("Resolver: Couldn't get an id to make a request");
- }
- else
- {
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
- }
-}
-
-/** Called when an error occurs */
-void Resolver::OnError(ResolverError, const std::string&)
-{
- /* Nothing in here */
-}
-
-/** Destroy a resolver */
-Resolver::~Resolver()
-{
- /* Nothing here (yet) either */
-}
-
-/** Get the request id associated with this class */
-int Resolver::GetId()
-{
- return this->myid;
-}
-
-Module* Resolver::GetCreator()
-{
- return this->Creator;
-}
-
-/** Process a socket read event */
-void DNS::HandleEvent(EventType, int)
-{
- /* Fetch the id and result of the next available packet */
- DNSResult res(0,"",0,"");
- res.id = 0;
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
-
- res = this->GetResult();
-
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
-
- /* Is there a usable request id? */
- if (res.id != -1)
- {
- /* Its an error reply */
- if (res.id & ERROR_MASK)
- {
- /* Mask off the error bit */
- res.id -= ERROR_MASK;
- /* Marshall the error to the correct class */
- if (Classes[res.id])
- {
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDnsBad++;
- Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
- delete Classes[res.id];
- Classes[res.id] = NULL;
- }
- return;
- }
- else
- {
- /* It is a non-error result, marshall the result to the correct class */
- if (Classes[res.id])
- {
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDnsGood++;
-
- if (!this->GetCache(res.original.c_str()))
- this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
-
- Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
- delete Classes[res.id];
- Classes[res.id] = NULL;
- }
- }
-
- if (ServerInstance && ServerInstance->stats)
- ServerInstance->stats->statsDns++;
- }
-}
-
-/** Add a derived Resolver to the working set */
-bool DNS::AddResolverClass(Resolver* r)
-{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
- /* Check the pointers validity and the id's validity */
- if ((r) && (r->GetId() > -1))
- {
- /* Check the slot isnt already occupied -
- * This should NEVER happen unless we have
- * a severely broken DNS server somewhere
- */
- if (!Classes[r->GetId()])
- {
- /* Set up the pointer to the class */
- Classes[r->GetId()] = r;
- return true;
- }
- }
-
- /* Pointer or id not valid, or duplicate id.
- * Free the item and return
- */
- delete r;
- return false;
-}
-
-void DNS::CleanResolvers(Module* module)
-{
- for (int i = 0; i < MAX_REQUEST_ID; i++)
- {
- if (Classes[i])
- {
- if (Classes[i]->GetCreator() == module)
- {
- Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
- delete Classes[i];
- Classes[i] = NULL;
- }
- }
- }
-}
#include "inspircd.h"
-#include "dynamic.h"
+
#ifndef _WIN32
#include <dlfcn.h>
#else
*/
-/* $Core */
-
#include "inspircd.h"
#include <fstream>
-#include "socketengine.h"
-#include "filelogger.h"
-FileLogStream::FileLogStream(int loglevel, FileWriter *fw)
- : LogStream(loglevel), f(fw)
+FileLogStream::FileLogStream(LogLevel loglevel, FileWriter *fw) : LogStream(loglevel), f(fw)
{
ServerInstance->Logs->AddLoggerRef(f);
}
ServerInstance->Logs->DelLoggerRef(f);
}
-void FileLogStream::OnLog(int loglevel, const std::string &type, const std::string &text)
+void FileLogStream::OnLog(LogLevel loglevel, const std::string &type, const std::string &text)
{
- static char TIMESTR[26];
+ static std::string TIMESTR;
static time_t LAST = 0;
if (loglevel < this->loglvl)
if (ServerInstance->Time() != LAST)
{
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = localtime(&local);
-
- strlcpy(TIMESTR,asctime(timeinfo),26);
- TIMESTR[24] = ':';
+ TIMESTR = InspIRCd::TimeString(ServerInstance->Time());
LAST = ServerInstance->Time();
}
- std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
- this->f->WriteLogLine(out);
+ this->f->WriteLogLine(TIMESTR + " " + type + ": " + text + "\n");
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include <fstream>
+
+FileReader::FileReader(const std::string& filename)
+{
+ Load(filename);
+}
+
+void FileReader::Load(const std::string& filename)
+{
+ // If the file is stored in the file cache then we used that version instead.
+ ConfigFileCache::const_iterator it = ServerInstance->Config->Files.find(filename);
+ if (it != ServerInstance->Config->Files.end())
+ {
+ this->lines = it->second;
+ }
+ else
+ {
+ const std::string realName = ServerInstance->Config->Paths.PrependConfig(filename);
+ lines.clear();
+
+ std::ifstream stream(realName.c_str());
+ if (!stream.is_open())
+ throw CoreException(filename + " does not exist or is not readable!");
+
+ std::string line;
+ while (std::getline(stream, line))
+ {
+ lines.push_back(line);
+ totalSize += line.size() + 2;
+ }
+
+ stream.close();
+ }
+}
+
+std::string FileReader::GetString() const
+{
+ std::string buffer;
+ for (file_cache::const_iterator it = this->lines.begin(); it != this->lines.end(); ++it)
+ {
+ buffer.append(*it);
+ buffer.append("\r\n");
+ }
+ return buffer;
+}
+
+std::string FileSystem::ExpandPath(const std::string& base, const std::string& fragment)
+{
+ // The fragment is an absolute path, don't modify it.
+ if (fragment[0] == '/' || FileSystem::StartsWithWindowsDriveLetter(fragment))
+ return fragment;
+
+ return base + '/' + fragment;
+}
+
+bool FileSystem::FileExists(const std::string& file)
+{
+ struct stat sb;
+ if (stat(file.c_str(), &sb) == -1)
+ return false;
+
+ if ((sb.st_mode & S_IFDIR) > 0)
+ return false;
+
+ return !access(file.c_str(), F_OK);
+}
+
+std::string FileSystem::GetFileName(const std::string& name)
+{
+#ifdef _WIN32
+ size_t pos = name.find_last_of("\\/");
+#else
+ size_t pos = name.rfind('/');
+#endif
+ return pos == std::string::npos ? name : name.substr(++pos);
+}
+
+bool FileSystem::StartsWithWindowsDriveLetter(const std::string& path)
+{
+ return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
+}
*/
-/* $Core */
-
#include "inspircd.h"
-#include "hashcomp.h"
-#include "hash_map.h"
/******************************************************
*
* scene spend a lot of time debating (arguing) about
* the best way to write hash functions to hash irc
* nicknames, channels etc.
- * We are lucky as C++ developers as hash_map does
+ * We are lucky as C++ developers as unordered_map does
* a lot of this for us. It does intellegent memory
* requests, bucketing, search functions, insertion
* and deletion etc. All we have to do is write some
*
******************************************************/
-/** A mapping of uppercase to lowercase, including scandinavian
- * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
- */
-unsigned const char rfc_case_insensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 0-19 */
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, /* 20-39 */
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 40-59 */
- 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, /* 60-79 */
- 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99, /* 80-99 */
- 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, /* 100-119 */
- 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, /* 120-139 */
- 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, /* 140-159 */
- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, /* 160-179 */
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, /* 180-199 */
- 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, /* 200-219 */
- 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, /* 220-239 */
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 /* 240-255 */
-};
-/** Case insensitive map, ASCII rules.
- * That is;
- * [ != {, but A == a.
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the ASCII character set.
*/
unsigned const char ascii_case_insensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 0-19 */
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, /* 20-39 */
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 40-59 */
- 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, /* 60-79 */
- 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, /* 80-99 */
- 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, /* 100-119 */
- 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, /* 120-139 */
- 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, /* 140-159 */
- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, /* 160-179 */
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, /* 180-199 */
- 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, /* 200-219 */
- 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, /* 220-239 */
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 /* 240-255 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 0-9
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 10-19
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, // 20-29
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, // 30-39
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, // 40-49
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 50-59
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, // 60-69
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, // 90-99
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-249
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+ 250, 251, 252, 253, 254, 255, // 250-255
};
-/** Case sensitive map.
- * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
+
+
+/**
+ * A case insensitive mapping of characters from upper case to lower case for
+ * the character set of RFC 1459. This is identical to ASCII with the small
+ * exception of {}| being considered to be the lower case equivalents of the
+ * characters []\ respectively.
*/
-unsigned const char rfc_case_sensitive_map[256] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
- 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
- 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
- 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
- 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
- 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
- 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
- 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
- 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+unsigned const char rfc_case_insensitive_map[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 0-9
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 10-19
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, // 20-29
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, // 30-39
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, // 40-49
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 50-59
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, // 60-69
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
+ 122, 123, 124, 125, 94, 95, 96, 97, 98, 99, // 90-99
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+ 250, 251, 252, 253, 254, 255, // 250-255
};
-/* convert a string to lowercase. Note following special circumstances
- * taken from RFC 1459. Many "official" server branches still hold to this
- * rule so i will too;
- *
- * Because of IRC's scandanavian origin, the characters {}| are
- * considered to be the lower case equivalents of the characters []\,
- * respectively. This is a critical issue when determining the
- * equivalence of two nicknames.
+/**
+ * A case sensitive mapping of characters from upper case to lower case for the
+ * character set of RFC 1459. This is identical to ASCII.
*/
-void nspace::strlower(char *n)
-{
- if (n)
- {
- for (char* t = n; *t; t++)
- *t = national_case_insensitive_map[(unsigned char)*t];
- }
-}
-
-#ifdef HASHMAP_DEPRECATED
- size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
-#else
- size_t nspace::hash<std::string>::operator()(const std::string &s) const
-#endif
-
-{
- /* XXX: NO DATA COPIES! :)
- * The hash function here is practically
- * a copy of the one in STL's hash_fun.h,
- * only with *x replaced with national_case_insensitive_map[*x].
- * This avoids a copy to use hash<const char*>
- */
- register size_t t = 0;
- for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
- t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
- return t;
-}
-
+unsigned const char rfc_case_sensitive_map[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 0-9
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 10-19
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, // 20-29
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, // 30-39
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, // 40-49
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 50-59
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, // 60-69
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 70-79
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, // 80-89
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, // 90-99
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
+ 250, 251, 252, 253, 254, 255, // 250-255
+};
size_t CoreExport irc::hash::operator()(const irc::string &s) const
{
return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
}
+bool irc::insensitive_swo::operator()(const std::string& a, const std::string& b) const
+{
+ const unsigned char* charmap = national_case_insensitive_map;
+ std::string::size_type asize = a.size();
+ std::string::size_type bsize = b.size();
+ std::string::size_type maxsize = std::min(asize, bsize);
+
+ for (std::string::size_type i = 0; i < maxsize; i++)
+ {
+ unsigned char A = charmap[(unsigned char)a[i]];
+ unsigned char B = charmap[(unsigned char)b[i]];
+ if (A > B)
+ return false;
+ else if (A < B)
+ return true;
+ }
+ return (asize < bsize);
+}
+
+size_t irc::insensitive::operator()(const std::string &s) const
+{
+ /* XXX: NO DATA COPIES! :)
+ * The hash function here is practically
+ * a copy of the one in STL's hash_fun.h,
+ * only with *x replaced with national_case_insensitive_map[*x].
+ * This avoids a copy to use hash<const char*>
+ */
+ register size_t t = 0;
+ for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
+ t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
+ return t;
+}
+
/******************************************************
*
* This is the implementation of our special irc::string
return (n >= 0) ? s1 : NULL;
}
-irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
-{
- /* Record starting position and current position */
- last_starting_position = tokens.begin();
- n = tokens.begin();
-}
-
-irc::tokenstream::~tokenstream()
+irc::tokenstream::tokenstream(const std::string &source) : spacesepstream(source)
{
}
bool irc::tokenstream::GetToken(std::string &token)
{
- std::string::iterator lsp = last_starting_position;
-
- while (n != tokens.end())
- {
- /** Skip multi space, converting " " into " "
- */
- while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
- n++;
-
- if ((last_pushed) && (*n == ':'))
- {
- /* If we find a token thats not the first and starts with :,
- * this is the last token on the line
- */
- std::string::iterator curr = ++n;
- n = tokens.end();
- token = std::string(curr, tokens.end());
- return true;
- }
+ bool first = !pos;
- last_pushed = false;
+ if (!spacesepstream::GetToken(token))
+ return false;
- if ((*n == ' ') || (n+1 == tokens.end()))
+ /* This is the last parameter */
+ if (token[0] == ':' && !first)
+ {
+ token = token.substr(1);
+ if (!StreamEnd())
{
- /* If we find a space, or end of string, this is the end of a token.
- */
- last_starting_position = n+1;
- last_pushed = *n == ' ';
-
- std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++);
- while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
- strip.erase(strip.end() - 1);
-
- token = strip;
- return !token.empty();
+ token += ' ';
+ token += GetRemaining();
}
-
- n++;
+ pos = tokens.length() + 1;
}
- token.clear();
- return false;
+
+ return true;
}
bool irc::tokenstream::GetToken(irc::string &token)
return returnval;
}
-irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
+irc::sepstream::sepstream(const std::string& source, char separator, bool allowempty)
+ : tokens(source), sep(separator), pos(0), allow_empty(allowempty)
{
- last_starting_position = tokens.begin();
- n = tokens.begin();
}
bool irc::sepstream::GetToken(std::string &token)
{
- std::string::iterator lsp = last_starting_position;
-
- while (n != tokens.end())
+ if (this->StreamEnd())
{
- if ((*n == sep) || (n+1 == tokens.end()))
- {
- last_starting_position = n+1;
- token = std::string(lsp, n+1 == tokens.end() ? n+1 : n++);
-
- while ((token.length()) && (token.find_last_of(sep) == token.length() - 1))
- token.erase(token.end() - 1);
-
- if (token.empty())
- n++;
-
- return n == tokens.end() ? false : true;
- }
-
- n++;
- }
-
- token.clear();
- return false;
-}
-
-const std::string irc::sepstream::GetRemaining()
-{
- return std::string(n, tokens.end());
-}
-
-bool irc::sepstream::StreamEnd()
-{
- return ((n + 1) == tokens.end());
-}
-
-irc::sepstream::~sepstream()
-{
-}
-
-std::string irc::hex(const unsigned char *raw, size_t rawsz)
-{
- if (!rawsz)
- return "";
-
- /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
-
- const char *hex = "0123456789abcdef";
- static char hexbuf[MAXBUF];
-
- size_t i, j;
- for (i = 0, j = 0; j < rawsz; ++j)
- {
- hexbuf[i++] = hex[raw[j] / 16];
- hexbuf[i++] = hex[raw[j] % 16];
- }
- hexbuf[i] = 0;
-
- return hexbuf;
-}
-
-CoreExport const char* irc::Spacify(const char* n)
-{
- static char x[MAXBUF];
- strlcpy(x,n,MAXBUF);
- for (char* y = x; *y; y++)
- if (*y == '_')
- *y = ' ';
- return x;
-}
-
-
-irc::modestacker::modestacker(bool add) : adding(add)
-{
- sequence.clear();
- sequence.push_back("");
-}
-
-void irc::modestacker::Push(char modeletter, const std::string ¶meter)
-{
- *(sequence.begin()) += modeletter;
- sequence.push_back(parameter);
-}
-
-void irc::modestacker::Push(char modeletter)
-{
- this->Push(modeletter,"");
-}
-
-void irc::modestacker::PushPlus()
-{
- this->Push('+',"");
-}
-
-void irc::modestacker::PushMinus()
-{
- this->Push('-',"");
-}
-
-int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
-{
- if (sequence.empty())
- {
- return 0;
+ token.clear();
+ return false;
}
- unsigned int n = 0;
- int size = 1; /* Account for initial +/- char */
- int nextsize = 0;
- int start = result.size();
- std::string modeline = adding ? "+" : "-";
- result.push_back(modeline);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- while (!sequence[0].empty() && (sequence.size() > 1) && (n < ServerInstance->Config->Limits.MaxModes) && ((size + nextsize) < max_line_size))
+ if (!this->allow_empty)
{
- modeline += *(sequence[0].begin());
- if (!sequence[1].empty())
+ this->pos = this->tokens.find_first_not_of(this->sep, this->pos);
+ if (this->pos == std::string::npos)
{
- result.push_back(sequence[1]);
- size += nextsize; /* Account for mode character and whitespace */
+ this->pos = this->tokens.length() + 1;
+ token.clear();
+ return false;
}
- sequence[0].erase(sequence[0].begin());
- sequence.erase(sequence.begin() + 1);
-
- if (sequence.size() > 1)
- nextsize = sequence[1].length() + 2;
-
- n++;
}
- result[start] = modeline;
- return n;
-}
+ size_t p = this->tokens.find(this->sep, this->pos);
+ if (p == std::string::npos)
+ p = this->tokens.length();
-irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
-{
- if (end < begin)
- return; // nothing to do here
+ token = this->tokens.substr(this->pos, p - this->pos);
+ this->pos = p + 1;
- for (int v = begin; v < end; v++)
- joined.append(sequence[v]).append(seperator);
- joined.append(sequence[end]);
+ return true;
}
-irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
+const std::string irc::sepstream::GetRemaining()
{
- if (end < begin)
- return; // nothing to do here
-
- for (int v = begin; v < end; v++)
- joined.append(sequence[v]).append(seperator);
- joined.append(sequence[end]);
+ return !this->StreamEnd() ? this->tokens.substr(this->pos) : "";
}
-irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end)
+bool irc::sepstream::StreamEnd()
{
- if (end < begin)
- return; // nothing to do here
-
- for (int v = begin; v < end; v++)
- joined.append(sequence[v]).append(seperator);
- joined.append(sequence[end]);
+ return this->pos > this->tokens.length();
}
-std::string& irc::stringjoiner::GetJoined()
+std::string irc::stringjoiner(const std::vector<std::string>& sequence, char separator)
{
+ std::string joined;
+ if (sequence.empty())
+ return joined; // nothing to do here
+
+ for (std::vector<std::string>::const_iterator i = sequence.begin(); i != sequence.end(); ++i)
+ joined.append(*i).push_back(separator);
+ joined.erase(joined.end()-1);
return joined;
}
return atoi(x.c_str());
}
}
-
-/*const std::basic_string& SearchAndReplace(std::string& text, const std::string& pattern, const std::string& replace)
-{
- std::string replacement;
- if ((!pattern.empty()) && (!text.empty()))
- {
- for (std::string::size_type n = 0; n != text.length(); ++n)
- {
- if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
- {
- replacement.append(replace);
- n = n + pattern.length() - 1;
- }
- else
- {
- replacement += text[n];
- }
- }
- }
- text = replacement;
- return text;
-}*/
*/
-/* $Core */
-
#ifdef _WIN32
#define _CRT_RAND_S
#include <stdlib.h>
#include "exitcodes.h"
#include <iostream>
-std::string InspIRCd::GetServerDescription(const std::string& servername)
-{
- std::string description;
-
- FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
-
- if (!description.empty())
- {
- return description;
- }
- else
- {
- // not a remote server that can be found, it must be me.
- return Config->ServerDesc;
- }
-}
-
/* Find a user record by nickname and return a pointer to it */
User* InspIRCd::FindNick(const std::string &nick)
{
if (!nick.empty() && isdigit(*nick.begin()))
return FindUUID(nick);
- user_hash::iterator iter = this->Users->clientlist->find(nick);
+ user_hash::iterator iter = this->Users->clientlist.find(nick);
- if (iter == this->Users->clientlist->end())
+ if (iter == this->Users->clientlist.end())
/* Couldn't find it */
return NULL;
return iter->second;
}
-User* InspIRCd::FindNick(const char* nick)
-{
- if (isdigit(*nick))
- return FindUUID(nick);
-
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
-}
-
User* InspIRCd::FindNickOnly(const std::string &nick)
{
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
-}
-
-User* InspIRCd::FindNickOnly(const char* nick)
-{
- user_hash::iterator iter = this->Users->clientlist->find(nick);
+ user_hash::iterator iter = this->Users->clientlist.find(nick);
- if (iter == this->Users->clientlist->end())
+ if (iter == this->Users->clientlist.end())
return NULL;
return iter->second;
User *InspIRCd::FindUUID(const std::string &uid)
{
- user_hash::iterator finduuid = this->Users->uuidlist->find(uid);
+ user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
- if (finduuid == this->Users->uuidlist->end())
+ if (finduuid == this->Users->uuidlist.end())
return NULL;
return finduuid->second;
}
-
-User *InspIRCd::FindUUID(const char *uid)
-{
- return FindUUID(std::string(uid));
-}
-
/* find a channel record by channel name and return a pointer to it */
-Channel* InspIRCd::FindChan(const char* chan)
-{
- chan_hash::iterator iter = chanlist->find(chan);
-
- if (iter == chanlist->end())
- /* Couldn't find it */
- return NULL;
-
- return iter->second;
-}
Channel* InspIRCd::FindChan(const std::string &chan)
{
- chan_hash::iterator iter = chanlist->find(chan);
+ chan_hash::iterator iter = chanlist.find(chan);
- if (iter == chanlist->end())
+ if (iter == chanlist.end())
/* Couldn't find it */
return NULL;
/* Send an error notice to all users, registered or not */
void InspIRCd::SendError(const std::string &s)
{
- for (LocalUserList::const_iterator i = this->Users->local_users.begin(); i != this->Users->local_users.end(); i++)
+ const UserManager::LocalList& list = Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
User* u = *i;
if (u->registered == REG_ALL)
{
- u->WriteServ("NOTICE %s :%s",u->nick.c_str(),s.c_str());
- }
+ u->WriteNotice(s);
+ }
else
{
/* Unregistered connections receive ERROR, not a NOTICE */
}
}
-/* return channel count */
-long InspIRCd::ChannelCount()
-{
- return chanlist->size();
-}
-
bool InspIRCd::IsValidMask(const std::string &mask)
{
const char* dest = mask.c_str();
}
/* true for valid channel name, false else */
-bool IsChannelHandler::Call(const char *chname, size_t max)
+bool IsChannelHandler::Call(const std::string& chname)
{
- const char *c = chname + 1;
+ if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
+ return false;
- /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
- if (!chname || *chname != '#')
- {
+ if (chname[0] != '#')
return false;
- }
- while (*c)
+ for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
{
- switch (*c)
+ switch (*i)
{
case ' ':
case ',':
case 7:
return false;
}
-
- c++;
- }
-
- size_t len = c - chname;
- /* too long a name - note funky pointer arithmetic here. */
- if (len > max)
- {
- return false;
}
return true;
}
/* true for valid nickname, false else */
-bool IsNickHandler::Call(const char* n, size_t max)
+bool IsNickHandler::Call(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
return false;
- unsigned int p = 0;
- for (const char* i = n; *i; i++, p++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
continue;
}
- if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
+ if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
{
/* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
continue;
return false;
}
- /* too long? or not */
- return (p <= max);
+ return true;
}
/* return true for good ident, false else */
-bool IsIdentHandler::Call(const char* n)
+bool IsIdentHandler::Call(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty())
return false;
- for (const char* i = n; *i; i++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
return true;
}
-bool IsSIDHandler::Call(const std::string &str)
+bool InspIRCd::IsSID(const std::string &str)
{
/* Returns true if the string given is exactly 3 characters long,
* starts with a digit, and the other two characters are A-Z or digits
((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
}
-/* open the proper logfile */
-bool InspIRCd::OpenLog(char**, int)
-{
- if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
-
- if (Config->cmdline.startup_log.empty())
- Config->cmdline.startup_log = LOG_PATH "/startup.log";
- FILE* startup = fopen(Config->cmdline.startup_log.c_str(), "a+");
-
- if (!startup)
- {
- return false;
- }
-
- FileWriter* fw = new FileWriter(startup);
- FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
-
- this->Logs->AddLogType("*", f, true);
-
- return true;
-}
-
void InspIRCd::CheckRoot()
{
#ifndef _WIN32
if (geteuid() == 0)
{
std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Can't start as root");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
Exit(EXIT_STATUS_ROOT);
}
#endif
FIRST_MOD_RESULT(OnWhoisLine, MOD_RESULT, (user, dest, numeric, copy_text));
if (MOD_RESULT != MOD_RES_DENY)
- user->WriteServ("%d %s", numeric, copy_text.c_str());
+ user->WriteNumeric(numeric, copy_text);
}
void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, format);
- vsnprintf(textbuffer, MAXBUF, format, argsPtr);
- va_end(argsPtr);
-
- this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, format, format)
+ this->SendWhoisLine(user, dest, numeric, textbuffer);
}
/** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
* lookups and pointer maths.
*/
-long InspIRCd::Duration(const std::string &str)
+unsigned long InspIRCd::Duration(const std::string &str)
{
unsigned char multiplier = 0;
long total = 0;
return total + subtotal;
}
-bool InspIRCd::ULine(const std::string& sserver)
+const char* InspIRCd::Format(va_list &vaList, const char* formatString)
{
- if (sserver.empty())
- return true;
+ static std::vector<char> formatBuffer(1024);
+
+ while (true)
+ {
+ va_list dst;
+ va_copy(dst, vaList);
+
+ int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
+ va_end(dst);
+
+ if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
+ {
+ break;
+ }
- return (Config->ulines.find(sserver.c_str()) != Config->ulines.end());
+ formatBuffer.resize(formatBuffer.size() * 2);
+ }
+
+ return &formatBuffer[0];
}
-bool InspIRCd::SilentULine(const std::string& sserver)
+const char* InspIRCd::Format(const char* formatString, ...)
{
- std::map<irc::string,bool>::iterator n = Config->ulines.find(sserver.c_str());
- if (n != Config->ulines.end())
- return n->second;
- else
- return false;
+ const char* ret;
+ VAFORMAT(ret, formatString, formatString);
+ return ret;
}
-std::string InspIRCd::TimeString(time_t curtime)
+std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
{
#ifdef _WIN32
if (curtime < 0)
curtime = 0;
#endif
- struct tm* timeinfo = localtime(&curtime);
+ struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
if (!timeinfo)
{
curtime = 0;
else if (timeinfo->tm_year + 1900 < 1000)
timeinfo->tm_year = 0;
- return std::string(asctime(timeinfo),24);
-}
+ // This is the default format used by asctime without the terminating new line.
+ if (!format)
+ format = "%a %b %d %H:%M:%S %Y";
-// You should only pass a single character to this.
-void InspIRCd::AddExtBanChar(char c)
-{
- std::string &tok = Config->data005;
- std::string::size_type ebpos = tok.find(" EXTBAN=,");
+ char buffer[512];
+ if (!strftime(buffer, sizeof(buffer), format, timeinfo))
+ buffer[0] = '\0';
- if (ebpos == std::string::npos)
- {
- tok.append(" EXTBAN=,");
- tok.push_back(c);
- }
- else
- {
- ebpos += 9;
- while (isalpha(tok[ebpos]) && tok[ebpos] < c)
- ebpos++;
- tok.insert(ebpos, 1, c);
- }
+ return buffer;
}
std::string InspIRCd::GenRandomStr(int length, bool printable)
std::string::size_type pos = current.find(':');
if (pos == std::string::npos)
continue;
- if (current.substr(0,pos) == restriction)
+ if (!current.compare(0, pos, restriction))
minmode = current[pos+1];
}
- ModeHandler* mh = ServerInstance->Modes->FindMode(minmode, MODETYPE_CHANNEL);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode);
if (mh && mypfx >= mh->GetPrefixRank())
return MOD_RES_ALLOW;
if (mh || minmode == '*')
*/
-/* $Core */
#include "inspircd.h"
-#include "inspircd_version.h"
#include <signal.h>
#ifndef _WIN32
#include <fstream>
#include <iostream>
#include "xline.h"
-#include "bancache.h"
-#include "socketengine.h"
-#include "socket.h"
-#include "command_parse.h"
#include "exitcodes.h"
-#include "caller.h"
#include "testsuite.h"
InspIRCd* ServerInstance = NULL;
*/
const char* ExitCodes[] =
{
- "No error", /* 0 */
- "DIE command", /* 1 */
- "execv() failed", /* 2 */
- "Internal error", /* 3 */
- "Config file error", /* 4 */
- "Logfile error", /* 5 */
- "POSIX fork failed", /* 6 */
- "Bad commandline parameters", /* 7 */
- "No ports could be bound", /* 8 */
- "Can't write PID file", /* 9 */
- "SocketEngine could not initialize", /* 10 */
- "Refusing to start up as root", /* 11 */
- "Found a <die> tag!", /* 12 */
- "Couldn't load module on startup", /* 13 */
- "Could not create windows forked process", /* 14 */
- "Received SIGTERM", /* 15 */
- "Bad command handler loaded", /* 16 */
- "RegisterServiceCtrlHandler failed", /* 17 */
- "UpdateSCMStatus failed", /* 18 */
- "CreateEvent failed" /* 19 */
+ "No error", // 0
+ "DIE command", // 1
+ "Config file error", // 2
+ "Logfile error", // 3
+ "POSIX fork failed", // 4
+ "Bad commandline parameters", // 5
+ "Can't write PID file", // 6
+ "SocketEngine could not initialize", // 7
+ "Refusing to start up as root", // 8
+ "Couldn't load module on startup", // 9
+ "Received SIGTERM" // 10
};
+#ifdef INSPIRCD_ENABLE_TESTSUITE
+/** True if we have been told to run the testsuite from the commandline,
+ * rather than entering the mainloop.
+ */
+static int do_testsuite = 0;
+#endif
+
template<typename T> static void DeleteZero(T*&n)
{
T* t = n;
void InspIRCd::Cleanup()
{
+ // Close all listening sockets
for (unsigned int i = 0; i < ports.size(); i++)
{
- /* This calls the constructor and closes the listening socket */
ports[i]->cull();
delete ports[i];
}
ports.clear();
/* Close all client sockets, or the new process inherits them */
- LocalUserList::reverse_iterator i = Users->local_users.rbegin();
- while (i != this->Users->local_users.rend())
- {
- User* u = *i++;
- Users->QuitUser(u, "Server shutdown");
- }
+ const UserManager::LocalList& list = Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ Users->QuitUser(*i, "Server shutdown");
GlobalCulls.Apply();
Modules->UnloadAll();
/* Delete objects dynamically allocated in constructor (destructor would be more appropriate, but we're likely exiting) */
/* Must be deleted before modes as it decrements modelines */
if (FakeClient)
+ {
+ delete FakeClient->server;
FakeClient->cull();
- if (Res)
- Res->cull();
+ }
DeleteZero(this->FakeClient);
- DeleteZero(this->Users);
- DeleteZero(this->Modes);
DeleteZero(this->XLines);
- DeleteZero(this->Parser);
- DeleteZero(this->stats);
- DeleteZero(this->Modules);
- DeleteZero(this->BanCache);
- DeleteZero(this->SNO);
DeleteZero(this->Config);
- DeleteZero(this->Res);
- DeleteZero(this->chanlist);
- DeleteZero(this->PI);
- DeleteZero(this->Threads);
- DeleteZero(this->Timers);
- DeleteZero(this->SE);
- /* Close logging */
- this->Logs->CloseLogs();
- DeleteZero(this->Logs);
-}
-
-void InspIRCd::Restart(const std::string &reason)
-{
- /* SendError flushes each client's queue,
- * regardless of writeability state
- */
- this->SendError(reason);
-
- /* Figure out our filename (if theyve renamed it, we're boned) */
- std::string me;
-
- char** argv = Config->cmdline.argv;
-
-#ifdef _WIN32
- char module[MAX_PATH];
- if (GetModuleFileNameA(NULL, module, MAX_PATH))
- me = module;
-#else
- me = argv[0];
-#endif
-
- this->Cleanup();
-
- if (execv(me.c_str(), argv) == -1)
- {
- /* Will raise a SIGABRT if not trapped */
- throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
- }
-}
-
-void InspIRCd::ResetMaxBans()
-{
- for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
- i->second->ResetMaxBans();
-}
-
-/** Because hash_map doesn't free its buckets when we delete items, we occasionally
- * recreate the hash to free them up.
- * We do this by copying the entries from the old hash to a new hash, causing all
- * empty buckets to be weeded out of the hash.
- * Since this is quite expensive, it's not done very often.
- */
-void InspIRCd::RehashUsersAndChans()
-{
- user_hash* old_users = Users->clientlist;
- Users->clientlist = new user_hash;
- for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
- Users->clientlist->insert(*n);
- delete old_users;
-
- user_hash* old_uuid = Users->uuidlist;
- Users->uuidlist = new user_hash;
- for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
- Users->uuidlist->insert(*n);
- delete old_uuid;
-
- chan_hash* old_chans = chanlist;
- chanlist = new chan_hash;
- for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
- chanlist->insert(*n);
- delete old_chans;
-
- // Reset the already_sent IDs so we don't wrap it around and drop a message
- LocalUser::already_sent_id = 0;
- for (LocalUserList::const_iterator i = Users->local_users.begin(); i != Users->local_users.end(); i++)
- {
- (**i).already_sent = 0;
- (**i).RemoveExpiredInvites();
- }
+ SocketEngine::Deinit();
+ Logs->CloseLogs();
}
void InspIRCd::SetSignals()
// Do not use QuickExit here: It will exit with status SIGTERM which would break e.g. daemon scripts
signal(SIGTERM, VoidSignalHandler);
- int childpid;
- if ((childpid = fork ()) < 0)
+ int childpid = fork();
+ if (childpid < 0)
return false;
else if (childpid > 0)
{
rlimit rl;
if (getrlimit(RLIMIT_CORE, &rl) == -1)
{
- this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to getrlimit()!");
return false;
}
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
- this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setrlimit() failed, cannot increase coredump size.");
return true;
#endif
#ifndef _WIN32
std::string fname(filename);
if (fname.empty())
- fname = DATA_PATH "/inspircd.pid";
+ fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
std::ofstream outfile(fname.c_str());
if (outfile.is_open())
{
else
{
std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
Exit(EXIT_STATUS_PID);
}
#endif
}
InspIRCd::InspIRCd(int argc, char** argv) :
- ConfigFileName(CONFIG_PATH "/inspircd.conf"),
+ ConfigFileName(INSPIRCD_CONFIG_PATH "/inspircd.conf"),
+ PI(&DefaultProtocolInterface),
/* Functor pointer initialisation.
*
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- NICKForced("NICKForced", NULL),
- OperQuit("OperQuit", NULL),
+ OperQuit("operquit", NULL),
GenRandom(&HandleGenRandom),
IsChannel(&HandleIsChannel),
- IsSID(&HandleIsSID),
- Rehash(&HandleRehash),
IsNick(&HandleIsNick),
IsIdent(&HandleIsIdent),
- FloodQuitUser(&HandleFloodQuitUser),
OnCheckExemption(&HandleOnCheckExemption)
{
ServerInstance = this;
- Extensions.Register(&NICKForced);
Extensions.Register(&OperQuit);
FailedPortList pl;
+ // Flag variables passed to getopt_long() later
int do_version = 0, do_nofork = 0, do_debug = 0,
- do_nolog = 0, do_root = 0, do_testsuite = 0; /* flag variables */
- int c = 0;
+ do_nolog = 0, do_root = 0;
// Initialize so that if we exit before proper initialization they're not deleted
- this->Logs = 0;
- this->Threads = 0;
- this->PI = 0;
- this->Users = 0;
- this->chanlist = 0;
this->Config = 0;
- this->SNO = 0;
- this->BanCache = 0;
- this->Modules = 0;
- this->stats = 0;
- this->Timers = 0;
- this->Parser = 0;
this->XLines = 0;
- this->Modes = 0;
- this->Res = 0;
this->ConfigThread = NULL;
this->FakeClient = NULL;
UpdateTime();
this->startup_time = TIME.tv_sec;
- // This must be created first, so other parts of Insp can use it while starting up
- this->Logs = new LogManager;
-
- SE = CreateSocketEngine();
-
- this->Threads = new ThreadEngine;
-
- /* Default implementation does nothing */
- this->PI = new ProtocolInterface;
-
- this->s_signal = 0;
-
- // Create base manager classes early, so nothing breaks
- this->Users = new UserManager;
-
- this->Users->clientlist = new user_hash();
- this->Users->uuidlist = new user_hash();
- this->chanlist = new chan_hash();
+ SocketEngine::Init();
this->Config = new ServerConfig;
- this->SNO = new SnomaskManager;
- this->BanCache = new BanCacheManager;
- this->Modules = new ModuleManager();
- this->stats = new serverstats();
- this->Timers = new TimerManager;
- this->Parser = new CommandParser;
+ dynamic_reference_base::reset_all();
this->XLines = new XLineManager;
this->Config->cmdline.argv = argv;
struct option longopts[] =
{
{ "nofork", no_argument, &do_nofork, 1 },
- { "logfile", required_argument, NULL, 'f' },
{ "config", required_argument, NULL, 'c' },
{ "debug", no_argument, &do_debug, 1 },
{ "nolog", no_argument, &do_nolog, 1 },
{ "runasroot", no_argument, &do_root, 1 },
{ "version", no_argument, &do_version, 1 },
+#ifdef INSPIRCD_ENABLE_TESTSUITE
{ "testsuite", no_argument, &do_testsuite, 1 },
+#endif
{ 0, 0, 0, 0 }
};
+ int c;
int index;
- while ((c = getopt_long(argc, argv, ":c:f:", longopts, &index)) != -1)
+ while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1)
{
switch (c)
{
- case 'f':
- /* Log filename was set */
- Config->cmdline.startup_log = optarg;
- break;
case 'c':
/* Config filename was set */
- ConfigFileName = optarg;
+ ConfigFileName = ServerInstance->Config->Paths.PrependConfig(optarg);
break;
case 0:
/* getopt_long_only() set an int variable, just keep going */
default:
/* Fall through to handle other weird values too */
std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl;
- std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--logfile <filename>] " << std::endl <<
- std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--config <config>] [--testsuite]" << std::endl;
+ std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--config <config>]" << std::endl <<
+ std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version]" << std::endl;
Exit(EXIT_STATUS_ARGV);
break;
}
}
+#ifdef INSPIRCD_ENABLE_TESTSUITE
if (do_testsuite)
do_nofork = do_debug = true;
+#endif
if (do_version)
{
- std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
+ std::cout << std::endl << INSPIRCD_VERSION << " " << INSPIRCD_REVISION << std::endl;
Exit(EXIT_STATUS_NOERROR);
}
Config->cmdline.nofork = (do_nofork != 0);
Config->cmdline.forcedebug = (do_debug != 0);
Config->cmdline.writelog = !do_nolog;
- Config->cmdline.TestSuite = (do_testsuite != 0);
if (do_debug)
{
FileWriter* fw = new FileWriter(stdout);
- FileLogStream* fls = new FileLogStream(RAWIO, fw);
+ FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
Logs->AddLogTypes("*", fls, true);
}
- else if (!this->OpenLog(argv, argc))
- {
- std::cout << "ERROR: Could not open initial logfile " << Config->cmdline.startup_log << ": " << strerror(errno) << std::endl << std::endl;
- Exit(EXIT_STATUS_LOG);
- }
- if (!ServerConfig::FileExists(ConfigFileName.c_str()))
+ if (!FileSystem::FileExists(ConfigFileName))
{
#ifdef _WIN32
/* Windows can (and defaults to) hide file extensions, so let's play a bit nice for windows users. */
std::string txtconf = this->ConfigFileName;
txtconf.append(".txt");
- if (ServerConfig::FileExists(txtconf.c_str()))
+ if (FileSystem::FileExists(txtconf))
{
ConfigFileName = txtconf;
}
#endif
{
std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", ConfigFileName.c_str());
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str());
Exit(EXIT_STATUS_CONFIG);
}
}
std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl;
std::cout << "Developers:" << std::endl;
std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl;
- std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;\r
+ std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;
std::cout << "\tAttila" << con_reset << std::endl << std::endl;
std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl;
- this->Modes = new ModeParser;
-
#ifndef _WIN32
if (!do_root)
this->CheckRoot();
else
{
- std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl\r
- << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl\r
- << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl\r
- << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl\r
- << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl\r
- << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl\r
- << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl\r
- << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;\r
+ std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
+ << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
+ << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
+ << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
+ << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
+ << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
+ << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
+ << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
sleep(20);
}
#endif
if (!this->DaemonSeed())
{
std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl;
- Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
Exit(EXIT_STATUS_FORK);
}
}
- SE->RecoverFromFork();
+ SocketEngine::RecoverFromFork();
- /* During startup we don't actually initialize this
- * in the thread engine.
+ /* During startup we read the configuration now, not in
+ * a seperate thread
*/
this->Config->Read();
this->Config->Apply(NULL, "");
Logs->OpenFileLogs();
+ ModeParser::InitBuiltinModes();
- this->Res = new DNS();
-
- /*
- * Initialise SID/UID.
- * For an explanation as to exactly how this works, and why it works this way, see GetUID().
- * -- w00t
- */
+ // If we don't have a SID, generate one based on the server name and the server description
if (Config->sid.empty())
- {
- // Generate one
- unsigned int sid = 0;
- char sidstr[4];
+ Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc);
- for (const char* x = Config->ServerName.c_str(); *x; ++x)
- sid = 5 * sid + *x;
- for (const char* y = Config->ServerDesc.c_str(); *y; ++y)
- sid = 5 * sid + *y;
- sprintf(sidstr, "%03d", sid % 1000);
+ // Initialize the UID generator with our sid
+ this->UIDGen.init(Config->sid);
- Config->sid = sidstr;
- }
-
- /* set up fake client again this time with the correct uid */
- this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
+ // Create the server user for this server
+ this->FakeClient = new FakeUser(Config->sid, Config->ServerName, Config->ServerDesc);
- // Get XLine to do it's thing.
- this->XLines->CheckELines();
+ // This is needed as all new XLines are marked pending until ApplyLines() is called
this->XLines->ApplyLines();
int bounditems = BindPorts(pl);
this->Modules->LoadAll();
- /* Just in case no modules were loaded - fix for bug #101 */
- this->BuildISupport();
+ // Build ISupport as ModuleManager::LoadAll() does not do it
+ this->ISupport.Build();
Config->ApplyDisabledCommands(Config->DisabledCommands);
if (!pl.empty())
std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl;
}
- std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SE->GetMaxFds() << " max open sockets" << std::endl;
+ std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl;
#ifndef _WIN32
if (!Config->cmdline.nofork)
if (kill(getppid(), SIGTERM) == -1)
{
std::cout << "Error killing parent process: " << strerror(errno) << std::endl;
- Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
+ Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno));
}
}
*
* -- nenolod
*/
- if ((!do_nofork) && (!do_testsuite) && (!Config->cmdline.forcedebug))
+ if ((!do_nofork) && (!Config->cmdline.forcedebug))
{
int fd = open("/dev/null", O_RDWR);
fclose(stdout);
if (dup2(fd, STDIN_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdin.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin.");
if (dup2(fd, STDOUT_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdout.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout.");
if (dup2(fd, STDERR_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stderr.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr.");
close(fd);
}
else
{
- Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground.");
}
#else
/* Set win32 service as running, if we are running as a service */
FreeConsole();
}
- QueryPerformanceFrequency(&stats->QPFrequency);
+ QueryPerformanceFrequency(&stats.QPFrequency);
#endif
- Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
+ Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds());
#ifndef _WIN32
std::string SetUser = Config->ConfValue("security")->getString("runasuser");
if (ret == -1)
{
- this->Logs->Log("SETGROUPS", DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
this->QuickExit(0);
}
if (!g)
{
- this->Logs->Log("SETGUID", DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
this->QuickExit(0);
}
if (ret == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
this->QuickExit(0);
}
}
if (!u)
{
- this->Logs->Log("SETGUID", DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
this->QuickExit(0);
}
if (ret == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
this->QuickExit(0);
}
}
#endif
}
-int InspIRCd::Run()
+void InspIRCd::Run()
{
+#ifdef INSPIRCD_ENABLE_TESTSUITE
/* See if we're supposed to be running the test suite rather than entering the mainloop */
- if (Config->cmdline.TestSuite)
+ if (do_testsuite)
{
TestSuite* ts = new TestSuite;
delete ts;
- Exit(0);
+ return;
}
+#endif
UpdateTime();
time_t OLDTIME = TIME.tv_sec;
if (this->ConfigThread && this->ConfigThread->IsDone())
{
/* Rehash has completed */
- this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
+ this->Logs->Log("CONFIG", LOG_DEBUG, "Detected ConfigThread exiting, tidying up...");
this->ConfigThread->Finish();
{
#ifndef _WIN32
getrusage(RUSAGE_SELF, &ru);
- stats->LastSampled = TIME;
- stats->LastCPU = ru.ru_utime;
+ stats.LastSampled = TIME;
+ stats.LastCPU = ru.ru_utime;
#else
- if(QueryPerformanceCounter(&stats->LastSampled))
+ if(QueryPerformanceCounter(&stats.LastSampled))
{
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
- stats->LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
- stats->LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
+ stats.LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
+ stats.LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
}
#endif
{
SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME);
}
-\r
+
OLDTIME = TIME.tv_sec;
if ((TIME.tv_sec % 3600) == 0)
{
- this->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+ Users->GarbageCollect();
+ FOREACH_MOD(OnGarbageCollect, ());
}
- Timers->TickTimers(TIME.tv_sec);
- this->DoBackgroundUserStuff();
+ Timers.TickTimers(TIME.tv_sec);
+ Users->DoBackgroundUserStuff();
if ((TIME.tv_sec % 5) == 0)
{
- FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
+ FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
SNO->FlushSnotices();
}
}
* This will cause any read or write events to be
* dispatched to their handlers.
*/
- this->SE->DispatchTrialWrites();
- this->SE->DispatchEvents();
+ SocketEngine::DispatchTrialWrites();
+ SocketEngine::DispatchEvents();
/* if any users were quit, take them out */
GlobalCulls.Apply();
s_signal = 0;
}
}
-
- return 0;
-}
-
-/**********************************************************************************/
-
-/**
- * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
- */
-
-/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
- * (until this returns true, a user will block in the waiting state, waiting to connect up to the
- * registration timeout maximum seconds)
- */
-bool InspIRCd::AllModulesReportReady(LocalUser* user)
-{
- ModResult res;
- FIRST_MOD_RESULT(OnCheckReady, res, (user));
- return (res == MOD_RES_PASSTHRU);
}
sig_atomic_t InspIRCd::s_signal = 0;
#include "inspircd.h"
-#include "socket.h"
-#include "inspstring.h"
-#include "socketengine.h"
+#include "iohook.h"
#ifndef DISABLE_WRITEV
#include <sys/uio.h>
this->fd = newfd;
this->state = I_CONNECTED;
if (fd > -1)
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
}
void BufferedSocket::DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip)
irc::sockets::sockaddrs addr, bind;
if (!irc::sockets::aptosa(ipaddr, aport, addr))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
return I_ERR_CONNECT;
}
if (bind.sa.sa_family != 0)
{
- if (ServerInstance->SE->Bind(fd, bind) < 0)
+ if (SocketEngine::Bind(fd, bind) < 0)
return I_ERR_BIND;
}
- ServerInstance->SE->NonBlocking(fd);
+ SocketEngine::NonBlocking(fd);
- if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1)
+ if (SocketEngine::Connect(this, &dest.sa, dest.sa_size()) == -1)
{
if (errno != EINPROGRESS)
return I_ERR_CONNECT;
this->state = I_CONNECTING;
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE | FD_WRITE_WILL_BLOCK))
return I_ERR_NOMOREFDS;
- this->Timeout = new SocketTimeout(this->GetFd(), this, timeout, ServerInstance->Time());
- ServerInstance->Timers->AddTimer(this->Timeout);
+ this->Timeout = new SocketTimeout(this->GetFd(), this, timeout);
+ ServerInstance->Timers.AddTimer(this->Timeout);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
return I_ERR_NONE;
}
{
// final chance, dump as much of the sendq as we can
DoWrite();
- if (IOHook)
+ if (GetIOHook())
{
try
{
- IOHook->OnStreamSocketClose(this);
+ GetIOHook()->OnStreamSocketClose(this);
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
+ modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
}
- IOHook = NULL;
+ delete iohook;
+ DelIOHook();
}
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(this);
- fd = -1;
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this);
}
}
void StreamSocket::DoRead()
{
- if (IOHook)
+ if (GetIOHook())
{
int rv = -1;
try
{
- rv = IOHook->OnStreamSocketRead(this, recvq);
+ rv = GetIOHook()->OnStreamSocketRead(this, recvq);
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
+ modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
return;
}
if (rv > 0)
else
{
char* ReadBuffer = ServerInstance->GetReadBuffer();
- int n = ServerInstance->SE->Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
+ int n = SocketEngine::Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
if (n == ServerInstance->Config->NetBufferSize)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
recvq.append(ReadBuffer, n);
OnDataReady();
}
else if (n > 0)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
recvq.append(ReadBuffer, n);
OnDataReady();
}
else if (n == 0)
{
error = "Connection closed";
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
else if (SocketEngine::IgnoreError())
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
}
else if (errno == EINTR)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
}
else
{
error = SocketEngine::LastError();
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
}
}
return;
if (!error.empty() || fd < 0 || fd == INT_MAX)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DoWrite on errored or closed socket");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
return;
}
#ifndef DISABLE_WRITEV
- if (IOHook)
+ if (GetIOHook())
#endif
{
int rv = -1;
}
std::string& front = sendq.front();
int itemlen = front.length();
- if (IOHook)
+ if (GetIOHook())
{
- rv = IOHook->OnStreamSocketWrite(this, front);
+ rv = GetIOHook()->OnStreamSocketWrite(this, front);
if (rv > 0)
{
// consumed the entire string, and is ready for more
#ifdef DISABLE_WRITEV
else
{
- rv = ServerInstance->SE->Send(this, front.data(), itemlen, 0);
+ rv = SocketEngine::Send(this, front.data(), itemlen, 0);
if (rv == 0)
{
SetError("Connection closed");
else if (rv < 0)
{
if (errno == EINTR || SocketEngine::IgnoreError())
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
else
SetError(SocketEngine::LastError());
return;
}
else if (rv < itemlen)
{
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
front = front.substr(rv);
sendq_len -= rv;
return;
sendq_len -= itemlen;
sendq.pop_front();
if (sendq.empty())
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_EDGE_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_EDGE_WRITE);
}
}
#endif
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s",
- modexcept.GetSource(), modexcept.GetReason());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s",
+ modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
}
}
#ifndef DISABLE_WRITEV
if (!error.empty())
{
// error - kill all events
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
else
{
- ServerInstance->SE->ChangeEventMask(this, eventChange);
+ SocketEngine::ChangeEventMask(this, eventChange);
}
}
#endif
{
if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Attempt to write data to dead socket: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to write data to dead socket: %s",
data.c_str());
return;
}
sendq.push_back(data);
sendq_len += data.length();
- ServerInstance->SE->ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
}
-void SocketTimeout::Tick(time_t)
+bool SocketTimeout::Tick(time_t)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SocketTimeout::Tick");
- if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
- return;
+ if (SocketEngine::GetRef(this->sfd) != this->sock)
+ {
+ delete this;
+ return false;
+ }
if (this->sock->state == I_CONNECTING)
{
}
this->sock->Timeout = NULL;
+ delete this;
+ return false;
}
void BufferedSocket::OnConnected() { }
{
state = I_CONNECTED;
this->OnConnected();
- if (GetIOHook())
- GetIOHook()->OnStreamSocketConnect(this);
- else
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
+ if (!GetIOHook())
+ SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
}
this->StreamSocket::DoWrite();
}
this->Close();
if (Timeout)
{
- ServerInstance->Timers->DelTimer(Timeout);
- Timeout = NULL;
+ // The timer is removed from the TimerManager in Timer::~Timer()
+ delete Timeout;
}
}
}
catch (CoreException& ex)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
- fd, ex.GetReason());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
+ fd, ex.GetReason().c_str());
SetError(ex.GetReason());
}
if (!error.empty())
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
OnError(errcode);
}
}
-
#include "inspircd.h"
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef HAS_STRLCPY
-CoreExport size_t strlcat(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz, dlen;
-
- while (n-- != 0 && *d != '\0')
- d++;
-
- dlen = d - dst;
- n = siz - dlen;
-
- if (n == 0)
- return(dlen + strlen(s));
-
- while (*s != '\0')
- {
- if (n != 1)
- {
- *d++ = *s;
- n--;
- }
-
- s++;
- }
-
- *d = '\0';
- return(dlen + (s - src)); /* count does not include NUL */
-}
-
-CoreExport size_t strlcpy(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
-
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0)
- {
- do
- {
- if ((*d++ = *s++) == 0)
- break;
- } while (--n != 0);
- }
-
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0)
- {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++);
- }
-
- return(s - src - 1); /* count does not include NUL */
-}
-#endif
-
-CoreExport int charlcat(char* x,char y,int z)
-{
- char* x__n = x;
- int v = 0;
-
- while(*x__n++)
- v++;
-
- if (v < z - 1)
- {
- *--x__n = y;
- *++x__n = 0;
- }
-
- return v;
-}
-
-CoreExport bool charremove(char* mp, char remove)
-{
- char* mptr = mp;
- bool shift_down = false;
-
- while (*mptr)
- {
- if (*mptr == remove)
- shift_down = true;
-
- if (shift_down)
- *mptr = *(mptr+1);
-
- mptr++;
- }
-
- return shift_down;
-}
-
static const char hextable[] = "0123456789abcdef";
-std::string BinToHex(const std::string& data)
+std::string BinToHex(const void* raw, size_t l)
{
- int l = data.length();
+ const char* data = static_cast<const char*>(raw);
std::string rv;
rv.reserve(l * 2);
- for(int i=0; i < l; i++)
+ for (size_t i = 0; i < l; i++)
{
unsigned char c = data[i];
- rv.append(1, hextable[c >> 4]);
- rv.append(1, hextable[c & 0xF]);
+ rv.push_back(hextable[c >> 4]);
+ rv.push_back(hextable[c & 0xF]);
}
return rv;
}
}
return rv;
}
+
+bool InspIRCd::TimingSafeCompare(const std::string& one, const std::string& two)
+{
+ if (one.length() != two.length())
+ return false;
+
+ unsigned int diff = 0;
+ for (std::string::const_iterator i = one.begin(), j = two.begin(); i != one.end(); ++i, ++j)
+ {
+ unsigned char a = static_cast<unsigned char>(*i);
+ unsigned char b = static_cast<unsigned char>(*j);
+ diff |= a ^ b;
+ }
+
+ return (diff == 0);
+}
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#endif
ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to)
: bind_tag(tag)
+ , iohookprov(NULL, std::string())
{
irc::sockets::satoap(bind_to, bind_addr, bind_port);
- bind_desc = irc::sockets::satouser(bind_to);
+ bind_desc = bind_to.str();
fd = socket(bind_to.sa.sa_family, SOCK_STREAM, 0);
}
#endif
- ServerInstance->SE->SetReuse(fd);
- int rv = ServerInstance->SE->Bind(this->fd, bind_to);
+ SocketEngine::SetReuse(fd);
+ int rv = SocketEngine::Bind(this->fd, bind_to);
if (rv >= 0)
- rv = ServerInstance->SE->Listen(this->fd, ServerInstance->Config->MaxConn);
+ rv = SocketEngine::Listen(this->fd, ServerInstance->Config->MaxConn);
+
+ int timeout = tag->getInt("defer", 0);
+ if (timeout && !rv)
+ {
+#if defined TCP_DEFER_ACCEPT
+ setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(timeout));
+#elif defined SO_ACCEPTFILTER
+ struct accept_filter_arg afa;
+ memset(&afa, 0, sizeof(afa));
+ strcpy(afa.af_name, "dataready");
+ setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
+#endif
+ }
if (rv < 0)
{
int errstore = errno;
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
+ SocketEngine::Shutdown(this, 2);
+ SocketEngine::Close(this->GetFd());
this->fd = -1;
errno = errstore;
}
else
{
- ServerInstance->SE->NonBlocking(this->fd);
- ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::NonBlocking(this->fd);
+ SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+
+ this->ResetIOHookProvider();
}
}
{
if (this->GetFd() > -1)
{
- ServerInstance->SE->DelFd(this);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
- ServerInstance->SE->Shutdown(this, 2);
- if (ServerInstance->SE->Close(this) != 0)
- ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
- this->fd = -1;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Shut down listener on fd %d", this->fd);
+ SocketEngine::Shutdown(this, 2);
+ if (SocketEngine::Close(this) != 0)
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Failed to cancel listener: %s", strerror(errno));
}
}
irc::sockets::sockaddrs server;
socklen_t length = sizeof(client);
- int incomingSockfd = ServerInstance->SE->Accept(this, &client.sa, &length);
+ int incomingSockfd = SocketEngine::Accept(this, &client.sa, &length);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensocket %s nfd=%d", bind_desc.c_str(), incomingSockfd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "HandleEvent for Listensocket %s nfd=%d", bind_desc.c_str(), incomingSockfd);
if (incomingSockfd < 0)
{
- ServerInstance->stats->statsRefused++;
+ ServerInstance->stats.Refused++;
return;
}
socklen_t sz = sizeof(server);
if (getsockname(incomingSockfd, &server.sa, &sz))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Can't get peername: %s", strerror(errno));
irc::sockets::aptosa(bind_addr, bind_port, server);
}
- /*
- * XXX -
- * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
- * its a pretty big but for the moment valid assumption:
- * file descriptors are handed out starting at 0, and are recycled as theyre freed.
- * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
- * irc server at once (or the irc server otherwise initiating this many connections, files etc)
- * which for the time being is a physical impossibility (even the largest networks dont have more
- * than about 10,000 users on ONE server!)
- */
- if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
- {
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
- ServerInstance->SE->Shutdown(incomingSockfd, 2);
- ServerInstance->SE->Close(incomingSockfd);
- ServerInstance->stats->statsRefused++;
- return;
- }
-
if (client.sa.sa_family == AF_INET6)
{
/*
}
}
- ServerInstance->SE->NonBlocking(incomingSockfd);
+ SocketEngine::NonBlocking(incomingSockfd);
ModResult res;
FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
}
if (res == MOD_RES_ALLOW)
{
- ServerInstance->stats->statsAccept++;
+ ServerInstance->stats.Accept++;
}
else
{
- ServerInstance->stats->statsRefused++;
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Refusing connection on %s - %s",
+ ServerInstance->stats.Refused++;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Refusing connection on %s - %s",
bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
- ServerInstance->SE->Close(incomingSockfd);
+ SocketEngine::Close(incomingSockfd);
}
}
switch (e)
{
case EVENT_ERROR:
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
break;
case EVENT_WRITE:
- ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
break;
case EVENT_READ:
this->AcceptInternal();
break;
}
}
+
+bool ListenSocket::ResetIOHookProvider()
+{
+ std::string provname = bind_tag->getString("ssl");
+ if (!provname.empty())
+ provname.insert(0, "ssl/");
+
+ // Set the new provider name, dynref handles the rest
+ iohookprov.SetProvider(provname);
+
+ // Return true if no provider was set, or one was set and it was also found
+ return (provname.empty() || iohookprov);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "listmode.h"
+
+ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag)
+ : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST),
+ listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
+ configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
+{
+ list = true;
+}
+
+void ListModeBase::DisplayList(User* user, Channel* channel)
+{
+ ChanData* cd = extItem.get(channel);
+ if (cd)
+ {
+ for (ModeList::reverse_iterator it = cd->list.rbegin(); it != cd->list.rend(); ++it)
+ {
+ user->WriteNumeric(listnumeric, "%s %s %s %lu", channel->name.c_str(), it->mask.c_str(), (!it->setter.empty() ? it->setter.c_str() : ServerInstance->Config->ServerName.c_str()), (unsigned long) it->time);
+ }
+ }
+ user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+}
+
+void ListModeBase::DisplayEmptyList(User* user, Channel* channel)
+{
+ user->WriteNumeric(endoflistnumeric, "%s :%s", channel->name.c_str(), endofliststring.c_str());
+}
+
+void ListModeBase::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
+{
+ ChanData* cd = extItem.get(channel);
+ if (cd)
+ {
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
+ {
+ changelist.push_remove(this, it->mask);
+ }
+ }
+}
+
+void ListModeBase::DoRehash()
+{
+ ConfigTagList tags = ServerInstance->Config->ConfTags(configtag);
+
+ limitlist oldlimits = chanlimits;
+ chanlimits.clear();
+
+ for (ConfigIter i = tags.first; i != tags.second; i++)
+ {
+ // For each <banlist> tag
+ ConfigTag* c = i->second;
+ ListLimit limit(c->getString("chan"), c->getInt("limit"));
+
+ if (limit.mask.size() && limit.limit > 0)
+ chanlimits.push_back(limit);
+ }
+
+ // Add the default entry. This is inserted last so if the user specifies a
+ // wildcard record in the config it will take precedence over this entry.
+ chanlimits.push_back(ListLimit("*", 64));
+
+ // Most of the time our settings are unchanged, so we can avoid iterating the chanlist
+ if (oldlimits == chanlimits)
+ return;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ {
+ ChanData* cd = extItem.get(i->second);
+ if (cd)
+ cd->maxitems = -1;
+ }
+}
+
+unsigned int ListModeBase::FindLimit(const std::string& channame)
+{
+ for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); ++it)
+ {
+ if (InspIRCd::Match(channame, it->mask))
+ {
+ // We have a pattern matching the channel
+ return it->limit;
+ }
+ }
+ return 64;
+}
+
+unsigned int ListModeBase::GetLimitInternal(const std::string& channame, ChanData* cd)
+{
+ if (cd->maxitems < 0)
+ cd->maxitems = FindLimit(channame);
+ return cd->maxitems;
+}
+
+unsigned int ListModeBase::GetLimit(Channel* channel)
+{
+ ChanData* cd = extItem.get(channel);
+ if (!cd) // just find the limit
+ return FindLimit(channel->name);
+
+ return GetLimitInternal(channel->name, cd);
+}
+
+ModeAction ListModeBase::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
+{
+ // Try and grab the list
+ ChanData* cd = extItem.get(channel);
+
+ if (adding)
+ {
+ if (tidy)
+ ModeParser::CleanMask(parameter);
+
+ if (parameter.length() > 250)
+ return MODEACTION_DENY;
+
+ // If there was no list
+ if (!cd)
+ {
+ // Make one
+ cd = new ChanData;
+ extItem.set(channel, cd);
+ }
+
+ // Check if the item already exists in the list
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++)
+ {
+ if (parameter == it->mask)
+ {
+ /* Give a subclass a chance to error about this */
+ TellAlreadyOnList(source, channel, parameter);
+
+ // it does, deny the change
+ return MODEACTION_DENY;
+ }
+ }
+
+ if ((IS_LOCAL(source)) && (cd->list.size() >= GetLimitInternal(channel->name, cd)))
+ {
+ /* List is full, give subclass a chance to send a custom message */
+ TellListTooLong(source, channel, parameter);
+ return MODEACTION_DENY;
+ }
+
+ /* Ok, it *could* be allowed, now give someone subclassing us
+ * a chance to validate the parameter.
+ * The param is passed by reference, so they can both modify it
+ * and tell us if we allow it or not.
+ *
+ * eg, the subclass could:
+ * 1) allow
+ * 2) 'fix' parameter and then allow
+ * 3) deny
+ */
+ if (ValidateParam(source, channel, parameter))
+ {
+ // And now add the mask onto the list...
+ cd->list.push_back(ListItem(parameter, source->nick, ServerInstance->Time()));
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ /* If they deny it they have the job of giving an error message */
+ return MODEACTION_DENY;
+ }
+ }
+ else
+ {
+ // We're taking the mode off
+ if (cd)
+ {
+ for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); ++it)
+ {
+ if (parameter == it->mask)
+ {
+ cd->list.erase(it);
+ return MODEACTION_ALLOW;
+ }
+ }
+ }
+
+ /* Tried to remove something that wasn't set */
+ TellNotSet(source, channel, parameter);
+ return MODEACTION_DENY;
+ }
+}
+
+bool ListModeBase::ValidateParam(User*, Channel*, std::string&)
+{
+ return true;
+}
+
+void ListModeBase::TellListTooLong(User* source, Channel* channel, std::string& parameter)
+{
+ source->WriteNumeric(ERR_BANLISTFULL, "%s %s :Channel ban list is full", channel->name.c_str(), parameter.c_str());
+}
+
+void ListModeBase::TellAlreadyOnList(User*, Channel*, std::string&)
+{
+}
+
+void ListModeBase::TellNotSet(User*, Channel*, std::string&)
+{
+}
#include "inspircd.h"
-#include "filelogger.h"
-
/*
* Suggested implementation...
* class LogManager
*
*/
+const char LogStream::LogHeader[] =
+ "Log started for " INSPIRCD_VERSION " (" INSPIRCD_REVISION ", " MODULE_INIT_STR ")"
+ " - compiled on " INSPIRCD_SYSTEM;
+
LogManager::LogManager()
+ : Logging(false)
{
- Logging = false;
}
LogManager::~LogManager()
}
std::string type = tag->getString("type");
std::string level = tag->getString("level");
- int loglevel = DEFAULT;
+ LogLevel loglevel = LOG_DEFAULT;
if (level == "rawio")
{
- loglevel = RAWIO;
+ loglevel = LOG_RAWIO;
ServerInstance->Config->RawLog = true;
}
else if (level == "debug")
{
- loglevel = DEBUG;
+ loglevel = LOG_DEBUG;
}
else if (level == "verbose")
{
- loglevel = VERBOSE;
+ loglevel = LOG_VERBOSE;
}
else if (level == "default")
{
- loglevel = DEFAULT;
+ loglevel = LOG_DEFAULT;
}
else if (level == "sparse")
{
- loglevel = SPARSE;
+ loglevel = LOG_SPARSE;
}
else if (level == "none")
{
- loglevel = NONE;
+ loglevel = LOG_NONE;
}
FileWriter* fw;
- std::string target = tag->getString("target");
+ std::string target = ServerInstance->Config->Paths.PrependLog(tag->getString("target"));
std::map<std::string, FileWriter*>::iterator fwi = logmap.find(target);
if (fwi == logmap.end())
{
- char realtarget[MAXBUF];
+ char realtarget[256];
time_t time = ServerInstance->Time();
struct tm *mytime = gmtime(&time);
- strftime(realtarget, MAXBUF, target.c_str(), mytime);
+ strftime(realtarget, sizeof(realtarget), target.c_str(), mytime);
FILE* f = fopen(realtarget, "a");
fw = new FileWriter(f);
logmap.insert(std::make_pair(target, fw));
fw = fwi->second;
}
FileLogStream* fls = new FileLogStream(loglevel, fw);
- fls->OnLog(SPARSE, "HEADER", InspIRCd::LogHeader);
+ fls->OnLog(LOG_SPARSE, "HEADER", LogStream::LogHeader);
AddLogTypes(type, fls, true);
}
}
{
if (ServerInstance->Config && ServerInstance->Config->cmdline.forcedebug)
return;
- std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
- std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
+
+ LogStreams.clear();
+ GlobalLogStreams.clear();
+
for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
{
delete i->first;
}
- std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
+
+ AllLogStreams.clear();
}
void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
{
- std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
-
- if (i != LogStreams.end())
- {
- i->second.push_back(l);
- }
- else
- {
- std::vector<LogStream *> v;
- v.push_back(l);
- LogStreams[type] = v;
- }
+ LogStreams[type].push_back(l);
if (type == "*")
- {
GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
- }
if (autoclose)
- {
- std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
- if (ai == AllLogStreams.end())
- {
- AllLogStreams.insert(std::make_pair(l, 1));
- }
- else
- {
- ++ai->second;
- }
- }
+ AllLogStreams[l]++;
return true;
}
std::vector<LogStream*>::iterator it;
while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
{
- if (it == i->second.end())
- continue;
i->second.erase(it);
}
}
- std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
- if (gi != GlobalLogStreams.end())
- {
- GlobalLogStreams.erase(gi);
- }
+
+ GlobalLogStreams.erase(l);
+
std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
if (ai == AllLogStreams.end())
{
return; /* Done. */
}
+
delete ai->first;
AllLogStreams.erase(ai);
}
std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
if (type == "*")
{
- std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
- if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
+ GlobalLogStreams.erase(l);
}
if (i != LogStreams.end())
return true;
}
-void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const char *fmt, ...)
{
if (Logging)
- {
return;
- }
-
- va_list a;
- static char buf[65536];
-
- va_start(a, fmt);
- vsnprintf(buf, 65536, fmt, a);
- va_end(a);
- this->Log(type, loglevel, std::string(buf));
+ std::string buf;
+ VAFORMAT(buf, fmt, fmt);
+ this->Log(type, loglevel, buf);
}
-void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
+void LogManager::Log(const std::string &type, LogLevel loglevel, const std::string &msg)
{
if (Logging)
{
// XXX: For now, just return. Don't throw an exception. It'd be nice to find out if this is happening, but I'm terrified of breaking so close to final release. -- w00t
// throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
- fprintf(log,"%s",line.c_str());
- if (writeops++ % 20)
+ fputs(line.c_str(), log);
+ if (++writeops % 20 == 0)
{
fflush(log);
}
#include "inspircd.h"
+#include "builtinmodes.h"
-/* +s (secret) */
-/* +p (private) */
-/* +m (moderated) */
-/* +t (only (half) ops can change topic) */
-/* +n (no external messages) */
-/* +i (invite only) */
-/* +w (see wallops) */
-/* +i (invisible) */
-#include "modes/simplemodes.h"
-/* +b (bans) */
-#include "modes/cmode_b.h"
-/* +k (keyed channel) */
-#include "modes/cmode_k.h"
-/* +l (channel user limit) */
-#include "modes/cmode_l.h"
-/* +o (channel op) */
-#include "modes/cmode_o.h"
-/* +v (channel voice) */
-#include "modes/cmode_v.h"
-/* +o (operator) */
-#include "modes/umode_o.h"
-/* +s (server notice masks) */
-#include "modes/umode_s.h"
-
-ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type)
- : ServiceProvider(Creator, Name, SERVICE_MODE), m_paramtype(TR_TEXT),
- parameters_taken(Params), mode(modeletter), prefix(0), oper(false),
- list(false), m_type(type), levelrequired(HALFOP_VALUE)
+ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type, Class mclass)
+ : ServiceProvider(Creator, Name, SERVICE_MODE), modeid(ModeParser::MODEID_MAX),
+ parameters_taken(Params), mode(modeletter), oper(false),
+ list(false), m_type(type), type_id(mclass), levelrequired(HALFOP_VALUE)
{
}
CullResult ModeHandler::cull()
{
- if (ServerInstance->Modes)
+ if (ServerInstance)
ServerInstance->Modes->DelMode(this);
return classbase::cull();
}
{
}
-bool ModeHandler::IsListMode()
-{
- return list;
-}
-
-unsigned int ModeHandler::GetPrefixRank()
-{
- return 0;
-}
-
int ModeHandler::GetNumParams(bool adding)
{
switch (parameters_taken)
{
/* We're either trying to add a mode we already have or
remove a mode we don't have, deny. */
- if (dest->IsModeSet(this->GetModeChar()) == adding)
+ if (dest->IsModeSet(this) == adding)
return MODEACTION_DENY;
/* adding will be either true or false, depending on if we
aren't removing a mode we don't have, we don't have to do any
other checks here to see if it's true or false, just add or
remove the mode */
- dest->SetMode(this->GetModeChar(), adding);
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
{
/* We're either trying to add a mode we already have or
remove a mode we don't have, deny. */
- if (channel->IsModeSet(this->GetModeChar()) == adding)
+ if (channel->IsModeSet(this) == adding)
return MODEACTION_DENY;
/* adding will be either true or false, depending on if we
aren't removing a mode we don't have, we don't have to do any
other checks here to see if it's true or false, just add or
remove the mode */
- channel->SetMode(this->GetModeChar(), adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
-ModeAction ParamChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
-{
- if (adding && !ParamValidate(parameter))
- return MODEACTION_DENY;
- std::string now = channel->GetModeParameter(this);
- if (parameter == now)
- return MODEACTION_DENY;
- if (adding)
- channel->SetModeParam(this, parameter);
- else
- channel->SetModeParam(this, "");
- return MODEACTION_ALLOW;
-}
-
-bool ParamChannelModeHandler::ParamValidate(std::string& parameter)
-{
- return true;
-}
-
-ModeWatcher::ModeWatcher(Module* Creator, char modeletter, ModeType type)
- : mode(modeletter), m_type(type), creator(Creator)
+ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
+ : mode(modename), m_type(type), creator(Creator)
{
+ ServerInstance->Modes->AddModeWatcher(this);
}
ModeWatcher::~ModeWatcher()
{
-}
-
-char ModeWatcher::GetModeChar()
-{
- return mode;
+ ServerInstance->Modes->DelModeWatcher(this);
}
ModeType ModeWatcher::GetModeType()
return m_type;
}
-bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool, ModeType)
+bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
{
return true;
}
-void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool, ModeType)
+void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
{
}
-User* ModeParser::SanityChecks(User *user, const char *dest, Channel *chan, int)
+PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
+ : ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
+ , prefix(PrefixChar), prefixrank(Rank)
{
- User *d;
- if ((!user) || (!dest) || (!chan) || (!*dest))
- {
- return NULL;
- }
- d = ServerInstance->FindNick(dest);
- if (!d)
+ list = true;
+}
+
+ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
+{
+ User* target;
+ if (IS_LOCAL(source))
+ target = ServerInstance->FindNickOnly(parameter);
+ else
+ target = ServerInstance->FindNick(parameter);
+
+ if (!target)
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), dest);
- return NULL;
+ source->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameter.c_str());
+ return MODEACTION_DENY;
}
- return d;
+
+ Membership* memb = chan->GetUser(target);
+ if (!memb)
+ return MODEACTION_DENY;
+
+ parameter = target->nick;
+ return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
}
-void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text)
+ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
{
- if (targetchannel)
+ if (adding)
{
- /* Display channel's current mode string */
- user->WriteNumeric(RPL_CHANNELMODEIS, "%s %s +%s",user->nick.c_str(), targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
- user->WriteNumeric(RPL_CHANNELCREATED, "%s %s %lu", user->nick.c_str(), targetchannel->name.c_str(), (unsigned long)targetchannel->age);
- return;
+ if (chan->GetModeParameter(this) == parameter)
+ return MODEACTION_DENY;
+
+ if (OnSet(source, chan, parameter) != MODEACTION_ALLOW)
+ return MODEACTION_DENY;
+
+ chan->SetMode(this, true);
+
+ // Handler might have changed the parameter internally
+ parameter.clear();
+ this->GetParameter(chan, parameter);
}
else
{
- if (targetuser == user || user->HasPrivPermission("users/auspex"))
- {
- /* Display user's current mode string */
- user->WriteNumeric(RPL_UMODEIS, "%s :+%s",targetuser->nick.c_str(),targetuser->FormatModes());
- if (IS_OPER(targetuser))
- user->WriteNumeric(RPL_SNOMASKIS, "%s +%s :Server notice mask", targetuser->nick.c_str(), targetuser->FormatNoticeMasks());
- return;
- }
- else
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't view modes for other users", user->nick.c_str());
- return;
- }
+ if (!chan->IsModeSet(this))
+ return MODEACTION_DENY;
+ this->OnUnsetInternal(source, chan);
+ chan->SetMode(this, false);
}
+ return MODEACTION_ALLOW;
}
-ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar,
- std::string ¶meter, bool SkipACL)
+ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Modes::Change& mcitem, bool SkipACL)
{
ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER;
- unsigned char mask = chan ? MASK_CHANNEL : MASK_USER;
- ModeHandler *mh = FindMode(modechar, type);
+ ModeHandler* mh = mcitem.mh;
+ bool adding = mcitem.adding;
int pcnt = mh->GetNumParams(adding);
+ std::string& parameter = mcitem.param;
// crop mode parameter size to 250 characters
if (parameter.length() > 250 && adding)
parameter = parameter.substr(0, 250);
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, modechar, parameter, adding, pcnt));
+ FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, parameter, adding));
if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY))
return MODEACTION_DENY;
+ const char modechar = mh->GetModeChar();
+
if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW))
{
MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding);
unsigned int ourrank = chan->GetPrefixValue(user);
if (ourrank < neededrank)
{
- ModeHandler* neededmh = NULL;
+ PrefixMode* neededmh = NULL;
for(char c='A'; c <= 'z'; c++)
{
- ModeHandler *privmh = FindMode(c, MODETYPE_CHANNEL);
+ PrefixMode* privmh = FindPrefixMode(c);
if (privmh && privmh->GetPrefixRank() >= neededrank)
{
// this mode is sufficient to allow this action
}
}
if (neededmh)
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have channel %s access or above to %sset channel mode %c",
- user->nick.c_str(), chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must have channel %s access or above to %sset channel mode %c",
+ chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
else
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You cannot %sset channel mode %c",
- user->nick.c_str(), chan->name.c_str(), adding ? "" : "un", modechar);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You cannot %sset channel mode %c",
+ chan->name.c_str(), adding ? "" : "un", modechar);
return MODEACTION_DENY;
}
}
}
- unsigned char handler_id = (modechar - 'A') | mask;
-
- for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ // Ask mode watchers whether this mode change is OK
+ std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
{
- if ((*watchers)->BeforeMode(user, targetuser, chan, parameter, adding, type) == false)
- return MODEACTION_DENY;
- /* A module whacked the parameter completely, and there was one. abort. */
- if (pcnt && parameter.empty())
- return MODEACTION_DENY;
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == type)
+ {
+ if (!mw->BeforeMode(user, targetuser, chan, parameter, adding))
+ return MODEACTION_DENY;
+
+ // A module whacked the parameter completely, and there was one. Abort.
+ if (pcnt && parameter.empty())
+ return MODEACTION_DENY;
+ }
}
- if (IS_LOCAL(user) && !IS_OPER(user))
+ if (IS_LOCAL(user) && !user->IsOper())
{
char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
if (disabled[modechar - 'A'])
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - %s mode %c has been locked by the administrator",
- user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - %s mode %c has been locked by the administrator",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
return MODEACTION_DENY;
}
}
if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
{
/* It's an oper only mode, and they don't have access to it. */
- if (IS_OPER(user))
+ if (user->IsOper())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to set %s mode %c",
- user->nick.c_str(), user->oper->NameStr(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to set %s mode %c",
+ user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
}
else
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Only operators may set %s mode %c",
- user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Only operators may set %s mode %c",
+ type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
}
return MODEACTION_DENY;
}
- if (mh->GetTranslateType() == TR_NICK)
- {
- User* prefixtarget;
- if (IS_LOCAL(user))
- prefixtarget = ServerInstance->FindNickOnly(parameter);
- else
- prefixtarget = ServerInstance->FindNick(parameter);
-
- if (!prefixtarget)
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameter.c_str());
- return MODEACTION_DENY;
- }
- }
-
- if (mh->GetPrefixRank() && chan)
- {
- User* user_to_prefix = ServerInstance->FindNick(parameter);
- if (!user_to_prefix)
- return MODEACTION_DENY;
- if (!chan->SetPrefix(user_to_prefix, modechar, adding))
- return MODEACTION_DENY;
- }
-
/* Call the handler for the mode */
ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding);
if (ma != MODEACTION_ALLOW)
return ma;
- for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
- (*watchers)->AfterMode(user, targetuser, chan, parameter, adding, type);
+ itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
+ {
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == type)
+ mw->AfterMode(user, targetuser, chan, parameter, adding);
+ }
return MODEACTION_ALLOW;
}
-void ModeParser::Process(const std::vector<std::string>& parameters, User *user, bool merge)
+void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
{
- const std::string& target = parameters[0];
- Channel* targetchannel = ServerInstance->FindChan(target);
- User* targetuser = NULL;
- if (!targetchannel)
- {
- if (IS_LOCAL(user))
- targetuser = ServerInstance->FindNickOnly(target);
- else
- targetuser = ServerInstance->FindNick(target);
- }
- ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
-
- LastParse.clear();
- LastParseParams.clear();
- LastParseTranslate.clear();
-
- if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
- {
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str());
- return;
- }
- if (parameters.size() == 1)
- {
- this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str());
- return;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters));
-
- bool SkipAccessChecks = false;
-
- if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW)
- SkipAccessChecks = true;
- else if (MOD_RESULT == MOD_RES_DENY)
- return;
+ if (endindex > parameters.size())
+ endindex = parameters.size();
- if (targetuser && !SkipAccessChecks && user != targetuser)
- {
- user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str());
- return;
- }
-
- std::string mode_sequence = parameters[1];
-
- std::string output_mode;
- std::ostringstream output_parameters;
- LastParseParams.push_back(output_mode);
- LastParseTranslate.push_back(TR_TEXT);
+ const std::string& mode_sequence = parameters[beginindex];
bool adding = true;
- char output_pm = '\0'; // current output state, '+' or '-'
- unsigned int param_at = 2;
+ unsigned int param_at = beginindex+1;
for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
if (!mh)
{
/* No mode handler? Unknown mode character then. */
- user->WriteServ("%d %s %c :is unknown mode char to me", type == MODETYPE_CHANNEL ? 472 : 501, user->nick.c_str(), modechar);
+ user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, "%c :is unknown mode char to me", modechar);
continue;
}
std::string parameter;
- int pcnt = mh->GetNumParams(adding);
- if (pcnt && param_at == parameters.size())
- {
- /* No parameter, continue to the next mode */
- mh->OnParameterMissing(user, targetuser, targetchannel);
- continue;
- }
- else if (pcnt)
- {
+ if (mh->GetNumParams(adding) && param_at < endindex)
parameter = parameters[param_at++];
- /* Make sure the user isn't trying to slip in an invalid parameter */
- if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
+
+ changelist.push(mh, adding, parameter);
+ }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+ // An empty parameter is never acceptable
+ if (item.param.empty())
+ {
+ item.mh->OnParameterMissing(user, targetuser, targetchannel);
+ return false;
+ }
+
+ // The parameter cannot begin with a ':' character or contain a space
+ if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+ return false;
+
+ return true;
+}
+
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+ ModeHandler* mh = item.mh;
+ if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+ // Mode not set here or merge is not applicable, apply the incoming mode
+ return true;
+
+ // Mode handler decides
+ std::string ours = chan->GetModeParameter(mh);
+ return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+ // Call ProcessSingle until the entire list is processed, but at least once to ensure
+ // LastParse and LastChangeList are cleared
+ unsigned int processed = 0;
+ do
+ {
+ unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+ processed += n;
+ }
+ while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
+{
+ LastParse.clear();
+ LastChangeList.clear();
+
+ unsigned int modes_processed = 0;
+ std::string output_mode;
+ std::string output_parameters;
+
+ char output_pm = '\0'; // current output state, '+' or '-'
+ Modes::ChangeList::List& list = changelist.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
+ {
+ modes_processed++;
+
+ Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+
+ // If the mode is supposed to have a parameter then we first take a look at item.param
+ // and, if we were asked to, also handle mode merges now
+ if (mh->GetNumParams(item.adding))
+ {
+ // Skip the mode if the parameter does not pass basic validation
+ if (!IsModeParamValid(user, targetchannel, targetuser, item))
+ continue;
+
+ // If this is a merge and we won we don't apply this mode
+ if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
continue;
- if (merge && targetchannel && targetchannel->IsModeSet(modechar) && !mh->IsListMode())
- {
- std::string ours = targetchannel->GetModeParameter(modechar);
- if (!mh->ResolveModeConflict(parameter, ours, targetchannel))
- /* we won the mode merge, don't apply this mode */
- continue;
- }
}
- ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks);
+ ModeAction ma = TryMode(user, targetuser, targetchannel, item, (!(flags & MODE_CHECKACCESS)));
if (ma != MODEACTION_ALLOW)
continue;
- char needed_pm = adding ? '+' : '-';
+ char needed_pm = item.adding ? '+' : '-';
if (needed_pm != output_pm)
{
output_pm = needed_pm;
output_mode.append(1, output_pm);
}
- output_mode.append(1, modechar);
+ output_mode.push_back(mh->GetModeChar());
- if (pcnt)
+ if (!item.param.empty())
{
- TranslateType tt = mh->GetTranslateType();
- if (tt == TR_NICK)
- {
- User* u = ServerInstance->FindNick(parameter);
- if (u)
- parameter = u->nick;
- }
- output_parameters << " " << parameter;
- LastParseParams.push_back(parameter);
- LastParseTranslate.push_back(tt);
+ output_parameters.push_back(' ');
+ output_parameters.append(item.param);
}
+ LastChangeList.push(mh, item.adding, item.param);
- if ( (output_mode.length() + output_parameters.str().length() > 450)
+ if ((output_mode.length() + output_parameters.length() > 450)
|| (output_mode.length() > 100)
- || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
+ || (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes))
{
/* mode sequence is getting too long */
break;
}
}
- LastParseParams[0] = output_mode;
-
if (!output_mode.empty())
{
LastParse = targetchannel ? targetchannel->name : targetuser->nick;
LastParse.append(" ");
LastParse.append(output_mode);
- LastParse.append(output_parameters.str());
+ LastParse.append(output_parameters);
if (targetchannel)
- {
- targetchannel->WriteChannel(user, "MODE %s", LastParse.c_str());
- FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, LastParseParams, LastParseTranslate));
- }
+ targetchannel->WriteChannel(user, "MODE " + LastParse);
else
- {
- targetuser->WriteFrom(user, "MODE %s", LastParse.c_str());
- FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, LastParseParams, LastParseTranslate));
- }
- }
- else if (targetchannel && parameters.size() == 2)
- {
- /* Special case for displaying the list for listmodes,
- * e.g. MODE #chan b, or MODE #chan +b without a parameter
- */
- this->DisplayListModes(user, targetchannel, mode_sequence);
+ targetuser->WriteFrom(user, "MODE " + LastParse);
+
+ FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
}
+
+ return modes_processed;
}
-void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence)
+void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
{
- seq++;
-
- for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
- unsigned char mletter = *letter;
- if (mletter == '+')
- continue;
-
- /* Ensure the user doesnt request the same mode twice,
- * so they cant flood themselves off out of idiocy.
- */
- if (sent[mletter] == seq)
- continue;
-
- sent[mletter] = seq;
-
- ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
-
- if (!mh || !mh->IsListMode())
- return;
-
ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mletter, "", true, 0));
+ FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, "", true));
if (MOD_RESULT == MOD_RES_DENY)
- continue;
+ return;
bool display = true;
- if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE))
- {
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You do not have access to view the +%c list",
- user->nick.c_str(), chan->name.c_str(), mletter);
- display = false;
- }
- unsigned char handler_id = (mletter - 'A') | MASK_CHANNEL;
-
- for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
+ // Ask mode watchers whether it's OK to show the list
+ std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
{
- std::string dummyparam;
+ ModeWatcher* mw = i->second;
+ if (mw->GetModeType() == MODETYPE_CHANNEL)
+ {
+ std::string dummyparam;
- if (!((*watchers)->BeforeMode(user, NULL, chan, dummyparam, true, MODETYPE_CHANNEL)))
- display = false;
+ if (!mw->BeforeMode(user, NULL, chan, dummyparam, true))
+ {
+ // A mode watcher doesn't want us to show the list
+ display = false;
+ break;
+ }
+ }
}
+
if (display)
mh->DisplayList(user, chan);
else
}
}
-const std::string& ModeParser::GetLastParse()
-{
- return LastParse;
-}
-
void ModeParser::CleanMask(std::string &mask)
{
std::string::size_type pos_of_pling = mask.find_first_of('!');
}
}
-bool ModeParser::AddMode(ModeHandler* mh)
+ModeHandler::Id ModeParser::AllocateModeId(ModeType mt)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
+ for (ModeHandler::Id i = 0; i != MODEID_MAX; ++i)
+ {
+ if (!modehandlersbyid[mt][i])
+ return i;
+ }
+
+ throw ModuleException("Out of ModeIds");
+}
+void ModeParser::AddMode(ModeHandler* mh)
+{
/* Yes, i know, this might let people declare modes like '_' or '^'.
* If they do that, thats their problem, and if i ever EVER see an
* official InspIRCd developer do that, i'll beat them with a paddle!
*/
- if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126))
- return false;
+ if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
+ throw ModuleException("Invalid letter for mode " + mh->name);
/* A mode prefix of ',' is not acceptable, it would fuck up server to server.
* A mode prefix of ':' will fuck up both server to server, and client to server.
* A mode prefix of '#' will mess up /whois and /privmsg
*/
- if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))
- return false;
+ PrefixMode* pm = mh->IsPrefixMode();
+ if (pm)
+ {
+ if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#'))
+ throw ModuleException("Invalid prefix for mode " + mh->name);
- if (mh->GetPrefix() && FindPrefix(mh->GetPrefix()))
- return false;
+ if (FindPrefix(pm->GetPrefix()))
+ throw ModuleException("Prefix already exists for mode " + mh->name);
+ }
- mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mh->GetModeChar()-65) | mask;
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot)
+ throw ModuleException("Letter is already in use for mode " + mh->name);
- if (modehandlers[pos])
- return false;
+ // The mode needs an id if it is either a user mode, a simple mode (flag) or a parameter mode.
+ // Otherwise (for listmodes and prefix modes) the id remains MODEID_MAX, which is invalid.
+ ModeHandler::Id modeid = MODEID_MAX;
+ if ((mh->GetModeType() == MODETYPE_USER) || (mh->IsParameterMode()) || (!mh->IsListMode()))
+ modeid = AllocateModeId(mh->GetModeType());
- modehandlers[pos] = mh;
- return true;
+ if (!modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh)).second)
+ throw ModuleException("Mode name already in use: " + mh->name);
+
+ // Everything is fine, add the mode
+
+ // If we allocated an id for this mode then save it and put the mode handler into the slot
+ if (modeid != MODEID_MAX)
+ {
+ mh->modeid = modeid;
+ modehandlersbyid[mh->GetModeType()][modeid] = mh;
+ }
+
+ slot = mh;
+ if (pm)
+ mhlist.prefix.push_back(pm);
+ else if (mh->IsListModeBase())
+ mhlist.list.push_back(mh->IsListModeBase());
+
+ RecreateModeListFor004Numeric();
}
bool ModeParser::DelMode(ModeHandler* mh)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
return false;
- mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mh->GetModeChar()-65) | mask;
+ ModeHandlerMap& mhmap = modehandlersbyname[mh->GetModeType()];
+ ModeHandlerMap::iterator mhmapit = mhmap.find(mh->name);
+ if ((mhmapit == mhmap.end()) || (mhmapit->second != mh))
+ return false;
- if (modehandlers[pos] != mh)
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot != mh)
return false;
/* Note: We can't stack here, as we have modes potentially being removed across many different channels.
switch (mh->GetModeType())
{
case MODETYPE_USER:
- for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); )
+ {
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); )
{
User* user = i->second;
++i;
mh->RemoveMode(user);
}
+ }
break;
case MODETYPE_CHANNEL:
- for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); )
+ {
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
{
// The channel may not be in the hash after RemoveMode(), see m_permchannels
Channel* chan = i->second;
++i;
- mh->RemoveMode(chan);
+
+ Modes::ChangeList changelist;
+ mh->RemoveMode(chan, changelist);
+ this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
}
+ }
break;
}
- modehandlers[pos] = NULL;
+ mhmap.erase(mhmapit);
+ if (mh->GetId() != MODEID_MAX)
+ modehandlersbyid[mh->GetModeType()][mh->GetId()] = NULL;
+ slot = NULL;
+ if (mh->IsPrefixMode())
+ mhlist.prefix.erase(std::find(mhlist.prefix.begin(), mhlist.prefix.end(), mh->IsPrefixMode()));
+ else if (mh->IsListModeBase())
+ mhlist.list.erase(std::find(mhlist.list.begin(), mhlist.list.end(), mh->IsListModeBase()));
+ RecreateModeListFor004Numeric();
return true;
}
-ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+ModeHandler* ModeParser::FindMode(const std::string& modename, ModeType mt)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
+ ModeHandlerMap& mhmap = modehandlersbyname[mt];
+ ModeHandlerMap::const_iterator it = mhmap.find(modename);
+ if (it != mhmap.end())
+ return it->second;
+ return NULL;
+}
+
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+{
if ((modeletter < 'A') || (modeletter > 'z'))
return NULL;
- mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (modeletter-65) | mask;
-
- return modehandlers[pos];
+ return modehandlers[mt][modeletter-65];
}
-std::string ModeParser::UserModeList()
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
{
- char modestr[256];
- int pointer = 0;
-
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
- {
- unsigned char pos = (mode-65) | MASK_USER;
-
- if (modehandlers[pos])
- modestr[pointer++] = mode;
- }
- modestr[pointer++] = 0;
- return modestr;
+ ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
+ if (!mh)
+ return NULL;
+ return mh->IsPrefixMode();
}
-std::string ModeParser::ChannelModeList()
+std::string ModeParser::CreateModeList(ModeType mt, bool needparam)
{
- char modestr[256];
- int pointer = 0;
+ std::string modestr;
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if (modehandlers[pos])
- modestr[pointer++] = mode;
+ ModeHandler* mh = modehandlers[mt][mode-65];
+ if ((mh) && ((!needparam) || (mh->GetNumParams(true))))
+ modestr.push_back(mode);
}
- modestr[pointer++] = 0;
+
return modestr;
}
-std::string ModeParser::ParaModeList()
+void ModeParser::RecreateModeListFor004Numeric()
{
- char modestr[256];
- int pointer = 0;
-
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
- {
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
- modestr[pointer++] = mode;
- }
- modestr[pointer++] = 0;
- return modestr;
+ Cached004ModeList = CreateModeList(MODETYPE_USER) + " " + CreateModeList(MODETYPE_CHANNEL) + " " + CreateModeList(MODETYPE_CHANNEL, true);
}
-ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
+PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter)
{
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
- {
- return modehandlers[pos];
- }
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix() == pfxletter)
+ return pm;
}
return NULL;
}
-std::string ModeParser::GiveModeList(ModeMasks m)
+std::string ModeParser::GiveModeList(ModeType mt)
{
std::string type1; /* Listmodes EXCEPT those with a prefix */
std::string type2; /* Modes that take a param when adding or removing */
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
- unsigned char pos = (mode-65) | m;
+ ModeHandler* mh = modehandlers[mt][mode-65];
/* One parameter when adding */
- if (modehandlers[pos])
+ if (mh)
{
- if (modehandlers[pos]->GetNumParams(true))
+ if (mh->GetNumParams(true))
{
- if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
+ PrefixMode* pm = mh->IsPrefixMode();
+ if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
{
- type1 += modehandlers[pos]->GetModeChar();
+ type1 += mh->GetModeChar();
}
else
{
/* ... and one parameter when removing */
- if (modehandlers[pos]->GetNumParams(false))
+ if (mh->GetNumParams(false))
{
/* But not a list mode */
- if (!modehandlers[pos]->GetPrefix())
+ if (!pm)
{
- type2 += modehandlers[pos]->GetModeChar();
+ type2 += mh->GetModeChar();
}
}
else
{
/* No parameters when removing */
- type3 += modehandlers[pos]->GetModeChar();
+ type3 += mh->GetModeChar();
}
}
}
else
{
- type4 += modehandlers[pos]->GetModeChar();
+ type4 += mh->GetModeChar();
}
}
}
std::string mprefixes;
std::map<int,std::pair<char,char> > prefixes;
- for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- unsigned char pos = (mode-65) | MASK_CHANNEL;
-
- if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
- {
- prefixes[modehandlers[pos]->GetPrefixRank()] = std::make_pair(
- modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetModeChar());
- }
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix())
+ prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
}
for(std::map<int,std::pair<char,char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); n++)
return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
}
-bool ModeParser::AddModeWatcher(ModeWatcher* mw)
+void ModeParser::AddModeWatcher(ModeWatcher* mw)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if (!mw)
- return false;
-
- if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
- return false;
-
- mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mw->GetModeChar()-65) | mask;
-
- modewatchers[pos].push_back(mw);
-
- return true;
+ modewatchermap.insert(std::make_pair(mw->GetModeName(), mw));
}
bool ModeParser::DelModeWatcher(ModeWatcher* mw)
{
- unsigned char mask = 0;
- unsigned char pos = 0;
-
- if (!mw)
- return false;
-
- if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
- return false;
-
- mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
- pos = (mw->GetModeChar()-65) | mask;
-
- ModeWatchIter a = std::find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
-
- if (a == modewatchers[pos].end())
+ std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mw->GetModeName());
+ for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
{
- return false;
+ if (i->second == mw)
+ {
+ modewatchermap.erase(i);
+ return true;
+ }
}
- modewatchers[pos].erase(a);
-
- return true;
+ return false;
}
-/** This default implementation can remove simple user modes
- */
-void ModeHandler::RemoveMode(User* user, irc::modestacker* stack)
+void ModeHandler::RemoveMode(User* user)
{
+ // Remove the mode if it's set on the user
if (user->IsModeSet(this->GetModeChar()))
{
- if (stack)
- {
- stack->Push(this->GetModeChar());
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(user->nick);
- parameters.push_back("-");
- parameters[1].push_back(this->GetModeChar());
- ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient);
- }
+ Modes::ChangeList changelist;
+ changelist.push_remove(this);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
}
}
-/** This default implementation can remove simple channel modes
- * (no parameters)
- */
-void ModeHandler::RemoveMode(Channel* channel, irc::modestacker* stack)
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
{
- if (channel->IsModeSet(this->GetModeChar()))
+ if (channel->IsModeSet(this))
{
- if (stack)
- {
- stack->Push(this->GetModeChar());
- }
+ if (this->GetNumParams(false))
+ // Removing this mode requires a parameter
+ changelist.push_remove(this, channel->GetModeParameter(this));
else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-");
- parameters[1].push_back(this->GetModeChar());
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
+ changelist.push_remove(this);
+ }
+}
+
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
+{
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
+ {
+ if (i->second->hasMode(this->GetModeChar()))
+ changelist.push_remove(this, i->first->nick);
}
}
struct builtin_modes
{
- ModeChannelSecret s;
- ModeChannelPrivate p;
- ModeChannelModerated m;
- ModeChannelTopicOps t;
+ SimpleChannelModeHandler s;
+ SimpleChannelModeHandler p;
+ SimpleChannelModeHandler m;
+ SimpleChannelModeHandler t;
- ModeChannelNoExternal n;
- ModeChannelInviteOnly i;
+ SimpleChannelModeHandler n;
+ SimpleChannelModeHandler i;
ModeChannelKey k;
ModeChannelLimit l;
ModeChannelOp o;
ModeChannelVoice v;
- ModeUserWallops uw;
- ModeUserInvisible ui;
+ SimpleUserModeHandler ui;
ModeUserOperator uo;
ModeUserServerNoticeMask us;
- void init(ModeParser* modes)
- {
- modes->AddMode(&s);
- modes->AddMode(&p);
- modes->AddMode(&m);
- modes->AddMode(&t);
- modes->AddMode(&n);
- modes->AddMode(&i);
- modes->AddMode(&k);
- modes->AddMode(&l);
- modes->AddMode(&b);
- modes->AddMode(&o);
- modes->AddMode(&v);
- modes->AddMode(&uw);
- modes->AddMode(&ui);
- modes->AddMode(&uo);
- modes->AddMode(&us);
+ builtin_modes()
+ : s(NULL, "secret", 's')
+ , p(NULL, "private", 'p')
+ , m(NULL, "moderated", 'm')
+ , t(NULL, "topiclock", 't')
+ , n(NULL, "noextmsg", 'n')
+ , i(NULL, "inviteonly", 'i')
+ , ui(NULL, "invisible", 'i')
+ {
+ }
+
+ void init()
+ {
+ ServiceProvider* modes[] = { &s, &p, &m, &t, &n, &i, &k, &l, &b, &o, &v,
+ &ui, &uo, &us };
+ ServerInstance->Modules->AddServices(modes, sizeof(modes)/sizeof(ServiceProvider*));
}
};
static builtin_modes static_modes;
+void ModeParser::InitBuiltinModes()
+{
+ static_modes.init();
+ static_modes.b.DoRehash();
+}
+
ModeParser::ModeParser()
{
/* Clear mode handler list */
memset(modehandlers, 0, sizeof(modehandlers));
-
- /* Last parse string */
- LastParse.clear();
-
- seq = 0;
- memset(&sent, 0, sizeof(sent));
-
- static_modes.init(this);
+ memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
}
ModeParser::~ModeParser()
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include <string>
-#include <vector>
-#include "inspircd_config.h"
-#include "configreader.h"
-#include "hash_map.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "inspstring.h"
-#include "hashcomp.h"
-#include "modes/cmode_b.h"
-
-ModeChannelBan::ModeChannelBan() : ModeHandler(NULL, "ban", 'b', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
-}
-
-ModeAction ModeChannelBan::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
-{
- int status = channel->GetPrefixValue(source);
- /* Call the correct method depending on wether we're adding or removing the mode */
- if (adding)
- {
- this->AddBan(source, parameter, channel, status);
- }
- else
- {
- this->DelBan(source, parameter, channel, status);
- }
- /* If the method above 'ate' the parameter by reducing it to an empty string, then
- * it won't matter wether we return ALLOW or DENY here, as an empty string overrides
- * the return value and is always MODEACTION_DENY if the mode is supposed to have
- * a parameter.
- */
- return MODEACTION_ALLOW;
-}
-
-void ModeChannelBan::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- BanList copy;
-
- for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
- {
- copy.push_back(*i);
- }
-
- for (BanList::iterator i = copy.begin(); i != copy.end(); i++)
- {
- if (stack)
- {
- stack->Push(this->GetModeChar(), i->data);
- }
- else
- {
- std::vector<std::string> parameters; parameters.push_back(channel->name); parameters.push_back("-b"); parameters.push_back(i->data);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelBan::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-void ModeChannelBan::DisplayList(User* user, Channel* channel)
-{
- /* Display the channel banlist */
- for (BanList::reverse_iterator i = channel->bans.rbegin(); i != channel->bans.rend(); ++i)
- {
- user->WriteServ("367 %s %s %s %s %lu",user->nick.c_str(), channel->name.c_str(), i->data.c_str(), i->set_by.c_str(), (unsigned long)i->set_time);
- }
- user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
- return;
-}
-
-void ModeChannelBan::DisplayEmptyList(User* user, Channel* channel)
-{
- user->WriteServ("368 %s %s :End of channel ban list",user->nick.c_str(), channel->name.c_str());
-}
-
-std::string& ModeChannelBan::AddBan(User *user, std::string &dest, Channel *chan, int)
-{
- if ((!user) || (!chan))
- {
- ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
- dest.clear();
- return dest;
- }
-
- /* Attempt to tidy the mask */
- ModeParser::CleanMask(dest);
- /* If the mask was invalid, we exit */
- if (dest.empty() || dest.length() > 250)
- return dest;
-
- long maxbans = chan->GetMaxBans();
- if (IS_LOCAL(user) && ((unsigned)chan->bans.size() >= (unsigned)maxbans))
- {
- user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)",user->nick.c_str(), chan->name.c_str(), chan->name.c_str(), maxbans);
- dest.clear();
- return dest;
- }
-
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnAddBan, MOD_RESULT, (user,chan,dest));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- dest.clear();
- return dest;
- }
-
- for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
- {
- if (i->data == dest)
- {
- /* dont allow a user to set the same ban twice */
- dest.clear();
- return dest;
- }
- }
-
- b.set_time = ServerInstance->Time();
- b.data.assign(dest, 0, MAXBUF);
- b.set_by.assign(user->nick, 0, 64);
- chan->bans.push_back(b);
- return dest;
-}
-
-std::string& ModeChannelBan::DelBan(User *user, std::string& dest, Channel *chan, int)
-{
- if ((!user) || (!chan))
- {
- ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
- dest.clear();
- return dest;
- }
-
- for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
- {
- if (!strcasecmp(i->data.c_str(), dest.c_str()))
- {
- ModResult MOD_RESULT;
- FIRST_MOD_RESULT(OnDelBan, MOD_RESULT, (user, chan, dest));
- if (MOD_RESULT == MOD_RES_DENY)
- {
- dest.clear();
- return dest;
- }
- dest = i->data;
- chan->bans.erase(i);
- return dest;
- }
- }
- dest.clear();
- return dest;
-}
-
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_k.h"
+#include "builtinmodes.h"
-ModeChannelKey::ModeChannelKey() : ModeHandler(NULL, "key", 'k', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
-}
-
-void ModeChannelKey::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- /** +k needs a parameter when being removed,
- * so we have a special-case RemoveMode here for it
- */
-
- if (channel->IsModeSet('k'))
- {
- if (stack)
- {
- stack->Push('k', channel->GetModeParameter('k'));
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-k");
- parameters.push_back(channel->GetModeParameter('k'));
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelKey::RemoveMode(User*, irc::modestacker* stack)
+ModeChannelKey::ModeChannelKey()
+ : ParamMode<ModeChannelKey, LocalStringExt>(NULL, "key", 'k', PARAM_ALWAYS)
{
}
ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
{
- bool exists = channel->IsModeSet('k');
+ const std::string* key = ext.get(channel);
+ bool exists = (key != NULL);
if (IS_LOCAL(source))
{
if (exists == adding)
return MODEACTION_DENY;
- if (exists && (parameter != channel->GetModeParameter('k')))
+ if (exists && (parameter != *key))
{
/* Key is currently set and the correct key wasnt given */
return MODEACTION_DENY;
}
} else {
- if (exists && adding && parameter == channel->GetModeParameter('k'))
+ if (exists && adding && parameter == *key)
{
/* no-op, don't show */
return MODEACTION_DENY;
}
}
- /* invalid keys */
- if (!parameter.length())
- return MODEACTION_DENY;
-
- if (parameter.rfind(' ') != std::string::npos)
- return MODEACTION_DENY;
-
+ channel->SetMode(this, adding);
if (adding)
{
- std::string ckey;
- ckey.assign(parameter, 0, 32);
- parameter = ckey;
- channel->SetModeParam('k', parameter);
+ parameter = parameter.substr(0, 32);
+ ext.set(channel, parameter);
}
else
- {
- channel->SetModeParam('k', "");
- }
+ ext.unset(channel);
+
return MODEACTION_ALLOW;
}
+
+void ModeChannelKey::SerializeParam(Channel* chan, const std::string* key, std::string& out)
+{
+ out += *key;
+}
+
+ModeAction ModeChannelKey::OnSet(User* source, Channel* chan, std::string& param)
+{
+ // Dummy function, never called
+ return MODEACTION_DENY;
+}
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/cmode_l.h"
+#include "builtinmodes.h"
-ModeChannelLimit::ModeChannelLimit() : ParamChannelModeHandler(NULL, "limit", 'l')
+ModeChannelLimit::ModeChannelLimit()
+ : ParamMode<ModeChannelLimit, LocalIntExt>(NULL, "limit", 'l')
{
}
return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
}
-bool ModeChannelLimit::ParamValidate(std::string ¶meter)
+ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter)
{
- int limit = atoi(parameter.c_str());
-
+ int limit = ConvToInt(parameter);
if (limit < 0)
- return false;
+ return MODEACTION_DENY;
+
+ ext.set(chan, limit);
+ return MODEACTION_ALLOW;
+}
- parameter = ConvToStr(limit);
- return true;
+void ModeChannelLimit::SerializeParam(Channel* chan, intptr_t n, std::string& out)
+{
+ out += ConvToStr(n);
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_o.h"
-
-ModeChannelOp::ModeChannelOp() : ModeHandler(NULL, "op", 'o', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '@';
- levelrequired = OP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelOp::GetPrefixRank()
-{
- return OP_VALUE;
-}
-
-void ModeChannelOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-o");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelOp::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "configreader.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modules.h"
-#include "modes/cmode_v.h"
-
-ModeChannelVoice::ModeChannelVoice() : ModeHandler(NULL, "voice", 'v', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '+';
- levelrequired = HALFOP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelVoice::GetPrefixRank()
-{
- return VOICE_VALUE;
-}
-
-void ModeChannelVoice::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-v");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-}
-
-void ModeChannelVoice::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelVoice::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_o.h"
+#include "builtinmodes.h"
ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE, MODETYPE_USER)
{
ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, std::string&, bool adding)
{
/* Only opers can execute this class at all */
- if (!ServerInstance->ULine(source->server) && !IS_OPER(source))
+ if (!source->server->IsULine() && !source->IsOper())
return MODEACTION_DENY;
/* Not even opers can GIVE the +o mode, only take it away */
* to your User!
*/
char snomask = IS_LOCAL(dest) ? 'o' : 'O';
- ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(),
- source->nick.empty() ? source->server.c_str() : source->nick.c_str());
+ ServerInstance->SNO->WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(), source->nick.c_str());
dest->UnOper();
return MODEACTION_ALLOW;
#include "inspircd.h"
-#include "mode.h"
-#include "channels.h"
-#include "users.h"
-#include "modes/umode_s.h"
+#include "builtinmodes.h"
ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomask", 's', PARAM_SETONLY, MODETYPE_USER)
{
ModeAction ModeUserServerNoticeMask::OnModeChange(User* source, User* dest, Channel*, std::string ¶meter, bool adding)
{
- /* Set the array fields */
if (adding)
{
- /* Fix for bug #310 reported by Smartys */
- if (!dest->modes[UM_SNOMASK])
- dest->snomasks.reset();
-
- dest->modes[UM_SNOMASK] = true;
- parameter = dest->ProcessNoticeMasks(parameter.c_str());
+ dest->SetMode(this, true);
+ // Process the parameter (remove chars we don't understand, remove redundant chars, etc.)
+ parameter = ProcessNoticeMasks(dest, parameter);
return MODEACTION_ALLOW;
}
else
{
- if (dest->modes[UM_SNOMASK] != false)
+ if (dest->IsModeSet(this))
{
- dest->modes[UM_SNOMASK] = false;
+ dest->SetMode(this, false);
+ dest->snomasks.reset();
return MODEACTION_ALLOW;
}
}
- /* Allow the change */
+ // Mode not set and trying to unset, deny
return MODEACTION_DENY;
}
std::string ModeUserServerNoticeMask::GetUserParameter(User* user)
{
- std::string masks = user->FormatNoticeMasks();
- if (masks.length())
- masks = "+" + masks;
- return masks;
+ std::string ret;
+ if (!user->IsModeSet(this))
+ return ret;
+
+ ret.push_back('+');
+ for (unsigned char n = 0; n < 64; n++)
+ {
+ if (user->snomasks[n])
+ ret.push_back(n + 'A');
+ }
+ return ret;
}
void ModeUserServerNoticeMask::OnParameterMissing(User* user, User* dest, Channel* channel)
{
- user->WriteServ("NOTICE %s :*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.",
- user->nick.c_str());
+ user->WriteNotice("*** The user mode +s requires a parameter (server notice mask). Please provide a parameter, e.g. '+s +*'.");
}
+std::string ModeUserServerNoticeMask::ProcessNoticeMasks(User* user, const std::string& input)
+{
+ bool adding = true;
+ std::bitset<64> curr = user->snomasks;
+
+ for (std::string::const_iterator i = input.begin(); i != input.end(); ++i)
+ {
+ switch (*i)
+ {
+ case '+':
+ adding = true;
+ break;
+ case '-':
+ adding = false;
+ break;
+ case '*':
+ for (size_t j = 0; j < 64; j++)
+ {
+ if (ServerInstance->SNO->IsSnomaskUsable(j+'A'))
+ curr[j] = adding;
+ }
+ break;
+ default:
+ // For local users check whether the given snomask is valid and enabled - IsSnomaskUsable() tests both.
+ // For remote users accept what we were told, unless the snomask char is not a letter.
+ if (IS_LOCAL(user))
+ {
+ if (!ServerInstance->SNO->IsSnomaskUsable(*i))
+ {
+ user->WriteNumeric(ERR_UNKNOWNSNOMASK, "%c :is unknown snomask char to me", *i);
+ continue;
+ }
+ }
+ else if (!(((*i >= 'a') && (*i <= 'z')) || ((*i >= 'A') && (*i <= 'Z'))))
+ continue;
+
+ size_t index = ((*i) - 'A');
+ curr[index] = adding;
+ break;
+ }
+ }
+
+ std::string plus = "+";
+ std::string minus = "-";
+
+ // Apply changes and construct two strings consisting of the newly added and the removed snomask chars
+ for (size_t i = 0; i < 64; i++)
+ {
+ bool isset = curr[i];
+ if (user->snomasks[i] != isset)
+ {
+ user->snomasks[i] = isset;
+ std::string& appendhere = (isset ? plus : minus);
+ appendhere.push_back(i+'A');
+ }
+ }
+
+ // Create the final string that will be shown to the user and sent to servers
+ // Form: "+ABc-de"
+ std::string output;
+ if (plus.length() > 1)
+ output = plus;
+
+ if (minus.length() > 1)
+ output += minus;
+
+ // Unset the snomask usermode itself if every snomask was unset
+ if (user->snomasks.none())
+ user->SetMode(this, false);
+
+ return output;
+}
#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
#include "exitcodes.h"
#include <iostream>
if (filename.find('/') != std::string::npos)
return false;
- char modfile[MAXBUF];
- snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
+ const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
- if (!ServerConfig::FileExists(modfile))
+ if (!FileSystem::FileExists(moduleFile))
{
LastModuleError = "Module file could not be found: " + filename;
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
if (Modules.find(filename) != Modules.end())
{
LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
Module* newmod = NULL;
- DLLManager* newhandle = new DLLManager(modfile);
+ DLLManager* newhandle = new DLLManager(moduleFile.c_str());
+ ServiceList newservices;
+ if (!defer)
+ this->NewServices = &newservices;
try
{
newmod = newhandle->CallInit();
+ this->NewServices = NULL;
if (newmod)
{
std::string version = newhandle->GetVersion();
if (defer)
{
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)",
filename.c_str(), version.c_str());
}
else
{
+ ConfigStatus confstatus;
+
+ AttachAll(newmod);
+ AddServices(newservices);
newmod->init();
+ newmod->ReadConfig(confstatus);
Version v = newmod->GetVersion();
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)%s",
filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
}
}
else
{
LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
delete newhandle;
return false;
}
}
catch (CoreException& modexcept)
{
+ this->NewServices = NULL;
+
// failure in module constructor
if (newmod)
{
else
delete newhandle;
LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
- this->ModCount++;
if (defer)
return true;
- FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename);
- }
-
- ServerInstance->BuildISupport();
- return true;
-}
-
-namespace {
- struct UnloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- UnloadAction(Module* m) : mod(m) {}
- void Call()
- {
- DLLManager* dll = mod->ModuleDLLManager;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- delete dll;
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-
- struct ReloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- HandlerBase1<void, bool>* const callback;
- ReloadAction(Module* m, HandlerBase1<void, bool>* c)
- : mod(m), callback(c) {}
- void Call()
- {
- DLLManager* dll = mod->ModuleDLLManager;
- std::string name = mod->ModuleSourceFile;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- delete dll;
- bool rv = ServerInstance->Modules->Load(name.c_str());
- if (callback)
- callback->Call(rv);
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-}
-
-bool ModuleManager::Unload(Module* mod)
-{
- if (!CanUnload(mod))
- return false;
- ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+ FOREACH_MOD(OnLoadModule, (newmod));
+ PrioritizeHooks();
+ ServerInstance->ISupport.Build();
return true;
}
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
- if (CanUnload(mod))
- ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
- else if (callback)
- callback->Call(false);
-}
-
/* We must load the modules AFTER initializing the socket engine, now */
-void ModuleManager::LoadAll()
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
{
- ModCount = 0;
-
std::cout << std::endl << "Loading core commands";
fflush(stdout);
- DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
+ DIR* library = opendir(ServerInstance->Config->Paths.Module.c_str());
if (library)
{
dirent* entry = NULL;
while (0 != (entry = readdir(library)))
{
- if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
+ if (InspIRCd::Match(entry->d_name, "core_*.so", ascii_case_insensitive_map))
{
std::cout << ".";
fflush(stdout);
+ this->NewServices = &servicemap[entry->d_name];
+
if (!Load(entry->d_name, true))
{
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
ServerInstance->Exit(EXIT_STATUS_MODULE);
}
closedir(library);
std::cout << std::endl;
}
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("module");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string name = tag->getString("name");
- std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
- if (!this->Load(name, true))
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
- {
- Module* mod = i->second;
- try
- {
- ServerInstance->Logs->Log("MODULE", DEBUG, "Initializing %s", i->first.c_str());
- mod->init();
- }
- catch (CoreException& modexcept)
- {
- LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
}
#endif
*/
-#define MODNAME cmd_all
+#define MODNAME "cmd_all"
#include "inspircd.h"
#include "exitcodes.h"
{
Command* c = (*i)(this);
cmds.push_back(c);
- ServerInstance->AddCommand(c);
}
}
catch (...)
~AllModule()
{
- for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i)
- delete *i;
+ stdalgo::delete_all(cmds);
}
Version GetVersion()
if (it == modlist->end())
return false;
Module* mod = NULL;
+
+ ServiceList newservices;
+ if (!defer)
+ this->NewServices = &newservices;
+
try
{
mod = (*it->second->init)();
mod->ModuleDLLManager = NULL;
mod->dying = false;
Modules[name] = mod;
+ this->NewServices = NULL;
if (defer)
{
- ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s", name.c_str());
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s", name.c_str());
return true;
}
else
{
+ ConfigStatus confstatus;
+
+ AttachAll(mod);
+ AddServices(newservices);
mod->init();
+ mod->ReadConfig(confstatus);
}
}
catch (CoreException& modexcept)
{
+ this->NewServices = NULL;
+
if (mod)
DoSafeUnload(mod);
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Unable to load " + name + ": " + modexcept.GetReason());
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Unable to load " + name + ": " + modexcept.GetReason());
return false;
}
- FOREACH_MOD(I_OnLoadModule,OnLoadModule(mod));
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + name);
- }
-
- ServerInstance->BuildISupport();
- return true;
-}
-
-namespace {
- struct UnloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- UnloadAction(Module* m) : mod(m) {}
- void Call()
- {
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-
- struct ReloadAction : public HandlerBase0<void>
- {
- Module* const mod;
- HandlerBase1<void, bool>* const callback;
- ReloadAction(Module* m, HandlerBase1<void, bool>* c)
- : mod(m), callback(c) {}
- void Call()
- {
- std::string name = mod->ModuleSourceFile;
- ServerInstance->Modules->DoSafeUnload(mod);
- ServerInstance->GlobalCulls.Apply();
- bool rv = ServerInstance->Modules->Load(name.c_str());
- callback->Call(rv);
- ServerInstance->GlobalCulls.AddItem(this);
- }
- };
-}
-bool ModuleManager::Unload(Module* mod)
-{
- if (!CanUnload(mod))
- return false;
- ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+ FOREACH_MOD(OnLoadModule, (mod));
+ PrioritizeHooks();
+ ServerInstance->ISupport.Build();
return true;
}
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
+void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
{
- if (CanUnload(mod))
- ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
- else if (callback)
- callback->Call(false);
-}
-
-void ModuleManager::LoadAll()
-{
- Load("cmd_all", true);
- Load("cmd_whowas.so", true);
- Load("cmd_lusers.so", true);
-
- ConfigTagList tags = ServerInstance->Config->ConfTags("module");
- for(ConfigIter i = tags.first; i != tags.second; ++i)
- {
- ConfigTag* tag = i->second;
- std::string name = tag->getString("name");
- std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
-
- if (!this->Load(name, true))
- {
- ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
+ for (modmap::const_iterator i = modlist->begin(); i != modlist->end(); ++i)
{
- Module* mod = i->second;
- try
- {
- mod->init();
- }
- catch (CoreException& modexcept)
- {
- LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
- ServerInstance->Exit(EXIT_STATUS_MODULE);
- }
- }
-
- /* We give every module a chance to re-prioritize when we introduce a new one,
- * not just the one thats loading, as the new module could affect the preference
- * of others
- */
- for(int tries = 0; tries < 20; tries++)
- {
- prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
- for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
- n->second->Prioritize();
-
- if (prioritizationState == PRIO_STATE_LAST)
- break;
- if (tries == 19)
+ const std::string modname = i->first;
+ if (modname[0] == 'c')
{
- ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
- ServerInstance->Exit(EXIT_STATUS_MODULE);
+ this->NewServices = &servicemap[modname];
+ Load(modname, true);
}
}
+ this->NewServices = NULL;
}
#endif
*/
+#include <iostream>
#include "inspircd.h"
-#include "xline.h"
-#include "socket.h"
-#include "socketengine.h"
-#include "command_parse.h"
-#include "dns.h"
#include "exitcodes.h"
#ifndef _WIN32
#include <dirent.h>
#endif
-static std::vector<dynamic_reference_base*>* dynrefs = NULL;
+static insp::intrusive_list<dynamic_reference_base>* dynrefs = NULL;
void dynamic_reference_base::reset_all()
{
if (!dynrefs)
return;
- for(unsigned int i = 0; i < dynrefs->size(); i++)
- (*dynrefs)[i]->ClearCache();
+ for (insp::intrusive_list<dynamic_reference_base>::iterator i = dynrefs->begin(); i != dynrefs->end(); ++i)
+ (*i)->resolve();
}
// Version is a simple class for holding a modules version number
{
}
-Request::Request(Module* src, Module* dst, const char* idstr)
-: id(idstr), source(src), dest(dst)
-{
-}
-
-void Request::Send()
-{
- if (dest)
- dest->OnRequest(*this);
-}
-
Event::Event(Module* src, const std::string &eventid) : source(src), id(eventid) { }
void Event::Send()
{
- FOREACH_MOD(I_OnEvent,OnEvent(*this));
+ FOREACH_MOD(OnEvent, (*this));
}
// These declarations define the behavours of the base class Module (which does nothing at all)
{
}
-ModResult Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { return MOD_RES_PASSTHRU; }
-void Module::OnUserConnect(LocalUser*) { }
-void Module::OnUserQuit(User*, const std::string&, const std::string&) { }
-void Module::OnUserDisconnect(LocalUser*) { }
-void Module::OnUserJoin(Membership*, bool, bool, CUList&) { }
-void Module::OnPostJoin(Membership*) { }
-void Module::OnUserPart(Membership*, std::string&, CUList&) { }
-void Module::OnPreRehash(User*, const std::string&) { }
-void Module::OnModuleRehash(User*, const std::string&) { }
-void Module::OnRehash(User*) { }
-ModResult Module::OnUserPreJoin(User*, Channel*, const char*, std::string&, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnMode(User*, void*, int, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void Module::OnOper(User*, const std::string&) { }
-void Module::OnPostOper(User*, const std::string&, const std::string &) { }
-void Module::OnInfo(User*) { }
-void Module::OnWhois(User*, User*) { }
-ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreNotice(User*, void*, int, std::string&, char, CUList&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreNick(User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnUserPostNick(User*, const std::string&) { }
-ModResult Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { return MOD_RES_PASSTHRU; }
-void Module::On005Numeric(std::string&) { }
-ModResult Module::OnKill(User*, User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnLoadModule(Module*) { }
-void Module::OnUnloadModule(Module*) { }
-void Module::OnBackgroundTimer(time_t) { }
-ModResult Module::OnPreCommand(std::string&, std::vector<std::string>&, LocalUser*, bool, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnPostCommand(const std::string&, const std::vector<std::string>&, LocalUser*, CmdResult, const std::string&) { }
-void Module::OnUserInit(LocalUser*) { }
-ModResult Module::OnCheckReady(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserRegister(LocalUser*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnUserPreKick(User*, Membership*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { }
-ModResult Module::OnRawMode(User*, Channel*, const char, const std::string &, bool, int) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckInvite(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckKey(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckLimit(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckChannelBan(User*, Channel*) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnCheckBan(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnExtBanCheck(User*, Channel*, char) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnStats(char, User*, string_list&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnPreTopicChange(User*, Channel*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnEvent(Event&) { }
-void Module::OnRequest(Request&) { }
-ModResult Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { return MOD_RES_PASSTHRU; }
-void Module::OnGlobalOper(User*) { }
-void Module::OnPostConnect(User*) { }
-ModResult Module::OnAddBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnDelBan(User*, Channel*, const std::string &) { return MOD_RES_PASSTHRU; }
-void Module::OnStreamSocketAccept(StreamSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { }
-int Module::OnStreamSocketWrite(StreamSocket*, std::string&) { return -1; }
-void Module::OnStreamSocketClose(StreamSocket*) { }
-void Module::OnStreamSocketConnect(StreamSocket*) { }
-int Module::OnStreamSocketRead(StreamSocket*, std::string&) { return -1; }
-void Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&) { }
-void Module::OnUserNotice(User*, void*, int, const std::string&, char, const CUList&) { }
-void Module::OnRemoteKill(User*, User*, const std::string&, const std::string&) { }
-void Module::OnUserInvite(User*, User*, Channel*, time_t) { }
-void Module::OnPostTopicChange(User*, Channel*, const std::string&) { }
-void Module::OnGetServerDescription(const std::string&, std::string&) { }
-void Module::OnSyncUser(User*, Module*, void*) { }
-void Module::OnSyncChannel(Channel*, Module*, void*) { }
-void Module::OnSyncNetwork(Module*, void*) { }
-void Module::ProtoSendMode(void*, TargetTypeFlags, void*, const std::vector<std::string>&, const std::vector<TranslateType>&) { }
-void Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { }
-void Module::ProtoSendMetaData(void*, Extensible*, const std::string&, const std::string&) { }
-void Module::OnWallops(User*, const std::string&) { }
-void Module::OnChangeHost(User*, const std::string&) { }
-void Module::OnChangeName(User*, const std::string&) { }
-void Module::OnChangeIdent(User*, const std::string&) { }
-void Module::OnAddLine(User*, XLine*) { }
-void Module::OnDelLine(User*, XLine*) { }
-void Module::OnExpireLine(XLine*) { }
+void Module::DetachEvent(Implementation i)
+{
+ ServerInstance->Modules->Detach(i, this);
+}
+
+void Module::ReadConfig(ConfigStatus& status) { }
+ModResult Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { DetachEvent(I_OnSendSnotice); return MOD_RES_PASSTHRU; }
+void Module::OnUserConnect(LocalUser*) { DetachEvent(I_OnUserConnect); }
+void Module::OnUserQuit(User*, const std::string&, const std::string&) { DetachEvent(I_OnUserQuit); }
+void Module::OnUserDisconnect(LocalUser*) { DetachEvent(I_OnUserDisconnect); }
+void Module::OnUserJoin(Membership*, bool, bool, CUList&) { DetachEvent(I_OnUserJoin); }
+void Module::OnPostJoin(Membership*) { DetachEvent(I_OnPostJoin); }
+void Module::OnUserPart(Membership*, std::string&, CUList&) { DetachEvent(I_OnUserPart); }
+void Module::OnPreRehash(User*, const std::string&) { DetachEvent(I_OnPreRehash); }
+void Module::OnModuleRehash(User*, const std::string&) { DetachEvent(I_OnModuleRehash); }
+ModResult Module::OnUserPreJoin(LocalUser*, Channel*, const std::string&, std::string&, const std::string&) { DetachEvent(I_OnUserPreJoin); return MOD_RES_PASSTHRU; }
+void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag, const std::string&) { DetachEvent(I_OnMode); }
+void Module::OnOper(User*, const std::string&) { DetachEvent(I_OnOper); }
+void Module::OnPostOper(User*, const std::string&, const std::string &) { DetachEvent(I_OnPostOper); }
+void Module::OnInfo(User*) { DetachEvent(I_OnInfo); }
+void Module::OnWhois(User*, User*) { DetachEvent(I_OnWhois); }
+ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreMessage(User*, void*, int, std::string&, char, CUList&, MessageType) { DetachEvent(I_OnUserPreMessage); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreNick(LocalUser*, const std::string&) { DetachEvent(I_OnUserPreNick); return MOD_RES_PASSTHRU; }
+void Module::OnUserPostNick(User*, const std::string&) { DetachEvent(I_OnUserPostNick); }
+ModResult Module::OnPreMode(User*, User*, Channel*, Modes::ChangeList&) { DetachEvent(I_OnPreMode); return MOD_RES_PASSTHRU; }
+void Module::On005Numeric(std::map<std::string, std::string>&) { DetachEvent(I_On005Numeric); }
+ModResult Module::OnKill(User*, User*, const std::string&) { DetachEvent(I_OnKill); return MOD_RES_PASSTHRU; }
+void Module::OnLoadModule(Module*) { DetachEvent(I_OnLoadModule); }
+void Module::OnUnloadModule(Module*) { DetachEvent(I_OnUnloadModule); }
+void Module::OnBackgroundTimer(time_t) { DetachEvent(I_OnBackgroundTimer); }
+ModResult Module::OnPreCommand(std::string&, std::vector<std::string>&, LocalUser*, bool, const std::string&) { DetachEvent(I_OnPreCommand); return MOD_RES_PASSTHRU; }
+void Module::OnPostCommand(Command*, const std::vector<std::string>&, LocalUser*, CmdResult, const std::string&) { DetachEvent(I_OnPostCommand); }
+void Module::OnUserInit(LocalUser*) { DetachEvent(I_OnUserInit); }
+ModResult Module::OnCheckReady(LocalUser*) { DetachEvent(I_OnCheckReady); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserRegister(LocalUser*) { DetachEvent(I_OnUserRegister); return MOD_RES_PASSTHRU; }
+ModResult Module::OnUserPreKick(User*, Membership*, const std::string&) { DetachEvent(I_OnUserPreKick); return MOD_RES_PASSTHRU; }
+void Module::OnUserKick(User*, Membership*, const std::string&, CUList&) { DetachEvent(I_OnUserKick); }
+ModResult Module::OnRawMode(User*, Channel*, ModeHandler*, const std::string&, bool) { DetachEvent(I_OnRawMode); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckInvite(User*, Channel*) { DetachEvent(I_OnCheckInvite); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckKey(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckKey); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckLimit(User*, Channel*) { DetachEvent(I_OnCheckLimit); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckChannelBan(User*, Channel*) { DetachEvent(I_OnCheckChannelBan); return MOD_RES_PASSTHRU; }
+ModResult Module::OnCheckBan(User*, Channel*, const std::string&) { DetachEvent(I_OnCheckBan); return MOD_RES_PASSTHRU; }
+ModResult Module::OnExtBanCheck(User*, Channel*, char) { DetachEvent(I_OnExtBanCheck); return MOD_RES_PASSTHRU; }
+ModResult Module::OnStats(char, User*, string_list&) { DetachEvent(I_OnStats); return MOD_RES_PASSTHRU; }
+ModResult Module::OnChangeLocalUserHost(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserHost); return MOD_RES_PASSTHRU; }
+ModResult Module::OnChangeLocalUserGECOS(LocalUser*, const std::string&) { DetachEvent(I_OnChangeLocalUserGECOS); return MOD_RES_PASSTHRU; }
+ModResult Module::OnPreTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPreTopicChange); return MOD_RES_PASSTHRU; }
+void Module::OnEvent(Event&) { DetachEvent(I_OnEvent); }
+ModResult Module::OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype) { DetachEvent(I_OnPassCompare); return MOD_RES_PASSTHRU; }
+void Module::OnGlobalOper(User*) { DetachEvent(I_OnGlobalOper); }
+void Module::OnPostConnect(User*) { DetachEvent(I_OnPostConnect); }
+void Module::OnUserMessage(User*, void*, int, const std::string&, char, const CUList&, MessageType) { DetachEvent(I_OnUserMessage); }
+void Module::OnUserInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserInvite); }
+void Module::OnPostTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPostTopicChange); }
+void Module::OnSyncUser(User*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncUser); }
+void Module::OnSyncChannel(Channel*, ProtocolInterface::Server&) { DetachEvent(I_OnSyncChannel); }
+void Module::OnSyncNetwork(ProtocolInterface::Server&) { DetachEvent(I_OnSyncNetwork); }
+void Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { DetachEvent(I_OnDecodeMetaData); }
+void Module::OnChangeHost(User*, const std::string&) { DetachEvent(I_OnChangeHost); }
+void Module::OnChangeName(User*, const std::string&) { DetachEvent(I_OnChangeName); }
+void Module::OnChangeIdent(User*, const std::string&) { DetachEvent(I_OnChangeIdent); }
+void Module::OnAddLine(User*, XLine*) { DetachEvent(I_OnAddLine); }
+void Module::OnDelLine(User*, XLine*) { DetachEvent(I_OnDelLine); }
+void Module::OnExpireLine(XLine*) { DetachEvent(I_OnExpireLine); }
void Module::OnCleanup(int, void*) { }
-ModResult Module::OnChannelPreDelete(Channel*) { return MOD_RES_PASSTHRU; }
-void Module::OnChannelDelete(Channel*) { }
-ModResult Module::OnSetAway(User*, const std::string &) { return MOD_RES_PASSTHRU; }
-ModResult Module::OnWhoisLine(User*, User*, int&, std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnBuildNeighborList(User*, UserChanList&, std::map<User*,bool>&) { }
-void Module::OnGarbageCollect() { }
-ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { return MOD_RES_PASSTHRU; }
-void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { }
+ModResult Module::OnChannelPreDelete(Channel*) { DetachEvent(I_OnChannelPreDelete); return MOD_RES_PASSTHRU; }
+void Module::OnChannelDelete(Channel*) { DetachEvent(I_OnChannelDelete); }
+ModResult Module::OnSetAway(User*, const std::string &) { DetachEvent(I_OnSetAway); return MOD_RES_PASSTHRU; }
+ModResult Module::OnWhoisLine(User*, User*, int&, std::string&) { DetachEvent(I_OnWhoisLine); return MOD_RES_PASSTHRU; }
+void Module::OnBuildNeighborList(User*, IncludeChanList&, std::map<User*,bool>&) { DetachEvent(I_OnBuildNeighborList); }
+void Module::OnGarbageCollect() { DetachEvent(I_OnGarbageCollect); }
+ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { DetachEvent(I_OnSetConnectClass); return MOD_RES_PASSTHRU; }
+void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { DetachEvent(I_OnText); }
+ModResult Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); return MOD_RES_PASSTHRU; }
+ModResult Module::OnNumeric(User*, unsigned int, const std::string&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
+ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { DetachEvent(I_OnAcceptConnection); return MOD_RES_PASSTHRU; }
+void Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, std::string&) { DetachEvent(I_OnSendWhoLine); }
+void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
+
+#ifdef INSPIRCD_ENABLE_TESTSUITE
void Module::OnRunTestSuite() { }
-void Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { }
-ModResult Module::OnNumeric(User*, unsigned int, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnHookIO(StreamSocket*, ListenSocket*) { }
-ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { return MOD_RES_PASSTHRU; }
-void Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, std::string&) { }
-void Module::OnSetUserIP(LocalUser*) { }
+#endif
+
+ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
+ : creator(Creator), name(Name), service(Type)
+{
+ if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+ ServerInstance->Modules->NewServices->push_back(this);
+}
+
+void ServiceProvider::DisableAutoRegister()
+{
+ if ((ServerInstance) && (ServerInstance->Modules->NewServices))
+ {
+ ModuleManager::ServiceList& list = *ServerInstance->Modules->NewServices;
+ ModuleManager::ServiceList::iterator it = std::find(list.begin(), list.end(), this);
+ if (it != list.end())
+ list.erase(it);
+ }
+}
-ModuleManager::ModuleManager() : ModCount(0)
+ModuleManager::ModuleManager()
{
}
Attach(i[n], mod);
}
+void ModuleManager::AttachAll(Module* mod)
+{
+ for (size_t i = I_BEGIN + 1; i != I_END; ++i)
+ Attach((Implementation)i, mod);
+}
+
void ModuleManager::DetachAll(Module* mod)
{
for (size_t n = I_BEGIN + 1; n != I_END; ++n)
return false;
found_src:
+ // The modules registered for a hook are called in reverse order (to allow for easier removal
+ // of list entries while looping), meaning that the Priority given to us has the exact opposite effect
+ // on the list, e.g.: PRIORITY_BEFORE will actually put 'mod' after 'which', etc.
size_t swap_pos = my_pos;
switch (s)
{
- case PRIORITY_FIRST:
+ case PRIORITY_LAST:
if (prioritizationState != PRIO_STATE_FIRST)
return true;
else
swap_pos = 0;
break;
- case PRIORITY_LAST:
+ case PRIORITY_FIRST:
if (prioritizationState != PRIO_STATE_FIRST)
return true;
else
swap_pos = EventHandlers[i].size() - 1;
break;
- case PRIORITY_AFTER:
+ case PRIORITY_BEFORE:
{
/* Find the latest possible position, only searching AFTER our position */
for (size_t x = EventHandlers[i].size() - 1; x > my_pos; --x)
return true;
}
/* Place this module before a set of other modules */
- case PRIORITY_BEFORE:
+ case PRIORITY_AFTER:
{
for (size_t x = 0; x < my_pos; ++x)
{
return true;
}
+bool ModuleManager::PrioritizeHooks()
+{
+ /* We give every module a chance to re-prioritize when we introduce a new one,
+ * not just the one thats loading, as the new module could affect the preference
+ * of others
+ */
+ for (int tries = 0; tries < 20; tries++)
+ {
+ prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
+ for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
+ n->second->Prioritize();
+
+ if (prioritizationState == PRIO_STATE_LAST)
+ break;
+ if (tries == 19)
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Hook priority dependency loop detected");
+ return false;
+ }
+ }
+ return true;
+}
+
bool ModuleManager::CanUnload(Module* mod)
{
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
if ((modfind == Modules.end()) || (modfind->second != mod) || (mod->dying))
{
LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
if (mod->GetVersion().Flags & VF_STATIC)
{
LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
void ModuleManager::DoSafeUnload(Module* mod)
{
+ // First, notify all modules that a module is about to be unloaded, so in case
+ // they pass execution to the soon to be unloaded module, it will happen now,
+ // i.e. before we unregister the services of the module being unloaded
+ FOREACH_MOD(OnUnloadModule, (mod));
+
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
std::vector<reference<ExtensionItem> > items;
ServerInstance->Extensions.BeginUnregister(modfind->second, items);
/* Give the module a chance to tidy out all its metadata */
- for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); )
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator c = chans.begin(); c != chans.end(); )
{
Channel* chan = c->second;
++c;
mod->OnCleanup(TYPE_CHANNEL, chan);
chan->doUnhookExtensions(items);
- const UserMembList* users = chan->GetUsers();
- for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator mi = users.begin(); mi != users.end(); ++mi)
mi->second->doUnhookExtensions(items);
}
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); )
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); )
{
User* user = u->second;
// The module may quit the user (e.g. SSL mod unloading) and that will remove it from the container
mod->OnCleanup(TYPE_USER, user);
user->doUnhookExtensions(items);
}
- for(char m='A'; m <= 'z'; m++)
+
+ const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
+ for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); )
+ {
+ ModeHandler* mh = i->second;
+ ++i;
+ if (mh->creator == mod)
+ this->DelService(*mh);
+ }
+
+ const ModeParser::ModeHandlerMap& chanmodes = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+ for (ModeParser::ModeHandlerMap::const_iterator i = chanmodes.begin(); i != chanmodes.end(); )
{
- ModeHandler* mh;
- mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
- if (mh && mh->creator == mod)
- ServerInstance->Modes->DelMode(mh);
- mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
- if (mh && mh->creator == mod)
- ServerInstance->Modes->DelMode(mh);
+ ModeHandler* mh = i->second;
+ ++i;
+ if (mh->creator == mod)
+ this->DelService(*mh);
}
+
for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
dynamic_reference_base::reset_all();
- /* Tidy up any dangling resolvers */
- ServerInstance->Res->CleanResolvers(mod);
-
- FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
-
DetachAll(mod);
Modules.erase(modfind);
ServerInstance->GlobalCulls.AddItem(mod);
- ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",mod->ModuleSourceFile.c_str());
- this->ModCount--;
- ServerInstance->BuildISupport();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Module %s unloaded",mod->ModuleSourceFile.c_str());
+ ServerInstance->ISupport.Build();
}
void ModuleManager::UnloadAll()
}
}
-std::string& ModuleManager::LastError()
+namespace
{
- return LastModuleError;
+ struct UnloadAction : public HandlerBase0<void>
+ {
+ Module* const mod;
+ UnloadAction(Module* m) : mod(m) {}
+ void Call()
+ {
+ DLLManager* dll = mod->ModuleDLLManager;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ // In pure static mode this is always NULL
+ delete dll;
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+ };
+
+ struct ReloadAction : public HandlerBase0<void>
+ {
+ Module* const mod;
+ HandlerBase1<void, bool>* const callback;
+ ReloadAction(Module* m, HandlerBase1<void, bool>* c)
+ : mod(m), callback(c) {}
+ void Call()
+ {
+ DLLManager* dll = mod->ModuleDLLManager;
+ std::string name = mod->ModuleSourceFile;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ delete dll;
+ bool rv = ServerInstance->Modules->Load(name);
+ if (callback)
+ callback->Call(rv);
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+ };
}
-CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user)
+bool ModuleManager::Unload(Module* mod)
{
- return this->Parser->CallHandler(commandname, parameters, user);
+ if (!CanUnload(mod))
+ return false;
+ ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+ return true;
}
-bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, User* user)
+void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
{
- return this->Parser->IsValidCommand(commandname, pcnt, user);
+ if (CanUnload(mod))
+ ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
+ else if (callback)
+ callback->Call(false);
+}
+
+void ModuleManager::LoadAll()
+{
+ std::map<std::string, ServiceList> servicemap;
+ LoadCoreModules(servicemap);
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("module");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string name = tag->getString("name");
+ this->NewServices = &servicemap[name];
+ std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
+
+ if (!this->Load(name, true))
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+ }
+ }
+
+ ConfigStatus confstatus;
+
+ for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
+ {
+ Module* mod = i->second;
+ try
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Initializing %s", i->first.c_str());
+ AttachAll(mod);
+ AddServices(servicemap[i->first]);
+ mod->init();
+ mod->ReadConfig(confstatus);
+ }
+ catch (CoreException& modexcept)
+ {
+ LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+ }
+ }
+
+ this->NewServices = NULL;
+
+ if (!PrioritizeHooks())
+ ServerInstance->Exit(EXIT_STATUS_MODULE);
+}
+
+std::string& ModuleManager::LastError()
+{
+ return LastModuleError;
+}
+
+void ModuleManager::AddServices(const ServiceList& list)
+{
+ for (ServiceList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ ServiceProvider& s = **i;
+ AddService(s);
+ }
}
void ModuleManager::AddService(ServiceProvider& item)
switch (item.service)
{
case SERVICE_COMMAND:
- if (!ServerInstance->Parser->AddCommand(static_cast<Command*>(&item)))
+ if (!ServerInstance->Parser.AddCommand(static_cast<Command*>(&item)))
throw ModuleException("Command "+std::string(item.name)+" already exists.");
return;
case SERVICE_MODE:
- if (!ServerInstance->Modes->AddMode(static_cast<ModeHandler*>(&item)))
- throw ModuleException("Mode "+std::string(item.name)+" already exists.");
+ {
+ ModeHandler* mh = static_cast<ModeHandler*>(&item);
+ ServerInstance->Modes->AddMode(mh);
+ DataProviders.insert(std::make_pair((mh->GetModeType() == MODETYPE_CHANNEL ? "mode/" : "umode/") + item.name, &item));
+ dynamic_reference_base::reset_all();
return;
+ }
case SERVICE_METADATA:
if (!ServerInstance->Extensions.Register(static_cast<ExtensionItem*>(&item)))
throw ModuleException("Extension " + std::string(item.name) + " already exists.");
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
+ if ((!item.name.compare(0, 5, "mode/", 5)) || (!item.name.compare(0, 6, "umode/", 6)))
+ throw ModuleException("The \"mode/\" and the \"umode\" service name prefixes are reserved.");
+
DataProviders.insert(std::make_pair(item.name, &item));
std::string::size_type slash = item.name.find('/');
if (slash != std::string::npos)
DataProviders.insert(std::make_pair(item.name.substr(0, slash), &item));
DataProviders.insert(std::make_pair(item.name.substr(slash + 1), &item));
}
+ dynamic_reference_base::reset_all();
return;
}
default:
case SERVICE_MODE:
if (!ServerInstance->Modes->DelMode(static_cast<ModeHandler*>(&item)))
throw ModuleException("Mode "+std::string(item.name)+" does not exist.");
- return;
+ // Fall through
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
: name(Name), value(NULL), creator(Creator)
{
if (!dynrefs)
- dynrefs = new std::vector<dynamic_reference_base*>;
- dynrefs->push_back(this);
+ dynrefs = new insp::intrusive_list<dynamic_reference_base>;
+ dynrefs->push_front(this);
+
+ // Resolve unless there is no ModuleManager (part of class InspIRCd)
+ if (ServerInstance)
+ resolve();
}
dynamic_reference_base::~dynamic_reference_base()
{
- for(unsigned int i = 0; i < dynrefs->size(); i++)
+ dynrefs->erase(this);
+ if (dynrefs->empty())
{
- if (dynrefs->at(i) == this)
- {
- unsigned int last = dynrefs->size() - 1;
- if (i != last)
- dynrefs->at(i) = dynrefs->at(last);
- dynrefs->erase(dynrefs->begin() + last);
- if (dynrefs->empty())
- {
- delete dynrefs;
- dynrefs = NULL;
- }
- return;
- }
+ delete dynrefs;
+ dynrefs = NULL;
}
}
void dynamic_reference_base::SetProvider(const std::string& newname)
{
name = newname;
- ClearCache();
-}
-
-void dynamic_reference_base::lookup()
-{
- if (!*this)
- throw ModuleException("Dynamic reference to '" + name + "' failed to resolve");
+ resolve();
}
-dynamic_reference_base::operator bool()
+void dynamic_reference_base::resolve()
{
- if (!value)
- {
- std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
- if (i != ServerInstance->Modules->DataProviders.end())
- value = static_cast<DataProvider*>(i->second);
- }
- return (value != NULL);
-}
-
-void InspIRCd::SendMode(const std::vector<std::string>& parameters, User *user)
-{
- this->Modes->Process(parameters, user);
-}
-
-
-void InspIRCd::SendGlobalMode(const std::vector<std::string>& parameters, User *user)
-{
- Modes->Process(parameters, user);
- if (!Modes->GetLastParse().empty())
- this->PI->SendMode(parameters[0], Modes->GetLastParseParams(), Modes->GetLastParseTranslate());
-}
-
-bool InspIRCd::AddResolver(Resolver* r, bool cached)
-{
- if (!cached)
- return this->Res->AddResolverClass(r);
+ std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.find(name);
+ if (i != ServerInstance->Modules->DataProviders.end())
+ value = static_cast<DataProvider*>(i->second);
else
- {
- r->TriggerCachedResult();
- delete r;
- return true;
- }
+ value = NULL;
}
Module* ModuleManager::Find(const std::string &name)
else
return modfind->second;
}
-
-const std::vector<std::string> ModuleManager::GetAllModuleNames(int filter)
-{
- std::vector<std::string> retval;
- for (std::map<std::string, Module*>::iterator x = Modules.begin(); x != Modules.end(); ++x)
- if (!filter || (x->second->GetVersion().Flags & filter))
- retval.push_back(x->first);
- return retval;
-}
-
-ConfigReader::ConfigReader()
-{
- this->error = 0;
- ServerInstance->Logs->Log("MODULE", DEBUG, "ConfigReader is deprecated in 2.0; "
- "use ServerInstance->Config->ConfValue(\"key\") or ->ConfTags(\"key\") instead");
-}
-
-
-ConfigReader::~ConfigReader()
-{
-}
-
-static ConfigTag* SlowGetTag(const std::string &tag, int index)
-{
- ConfigTagList tags = ServerInstance->Config->ConfTags(tag);
- while (tags.first != tags.second)
- {
- if (!index)
- return tags.first->second;
- tags.first++;
- index--;
- }
- return NULL;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
-{
- std::string result = default_value;
- if (!SlowGetTag(tag, index)->readString(name, result, allow_linefeeds))
- {
- this->error = CONF_VALUE_NOT_FOUND;
- }
- return result;
-}
-
-std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds)
-{
- return ReadValue(tag, name, "", index, allow_linefeeds);
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
-{
- bool def = (default_value == "yes");
- return SlowGetTag(tag, index)->getBool(name, def);
-}
-
-bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
-{
- return ReadFlag(tag, name, "", index);
-}
-
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive)
-{
- int v = atoi(default_value.c_str());
- int result = SlowGetTag(tag, index)->getInt(name, v);
-
- if ((need_positive) && (result < 0))
- {
- this->error = CONF_INT_NEGATIVE;
- return 0;
- }
-
- return result;
-}
-
-int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive)
-{
- return ReadInteger(tag, name, "", index, need_positive);
-}
-
-long ConfigReader::GetError()
-{
- long olderr = this->error;
- this->error = 0;
- return olderr;
-}
-
-int ConfigReader::Enumerate(const std::string &tag)
-{
- ServerInstance->Logs->Log("MODULE", DEBUG, "Module is using ConfigReader::Enumerate on %s; this is slow!",
- tag.c_str());
- int i=0;
- while (SlowGetTag(tag, i)) i++;
- return i;
-}
-
-FileReader::FileReader(const std::string &filename)
-{
- LoadFile(filename);
-}
-
-FileReader::FileReader()
-{
-}
-
-std::string FileReader::Contents()
-{
- std::string x;
- for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
- {
- x.append(*a);
- x.append("\r\n");
- }
- return x;
-}
-
-unsigned long FileReader::ContentSize()
-{
- return this->contentsize;
-}
-
-void FileReader::CalcSize()
-{
- unsigned long n = 0;
- for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
- n += (a->length() + 2);
- this->contentsize = n;
-}
-
-void FileReader::LoadFile(const std::string &filename)
-{
- std::map<std::string, file_cache>::iterator file = ServerInstance->Config->Files.find(filename);
- if (file != ServerInstance->Config->Files.end())
- {
- this->fc = file->second;
- }
- else
- {
- fc.clear();
- FILE* f = fopen(filename.c_str(), "r");
- if (!f)
- return;
- char linebuf[MAXBUF*10];
- while (fgets(linebuf, sizeof(linebuf), f))
- {
- int len = strlen(linebuf);
- if (len)
- fc.push_back(std::string(linebuf, len - 1));
- }
- fclose(f);
- }
- CalcSize();
-}
-
-
-FileReader::~FileReader()
-{
-}
-
-bool FileReader::Exists()
-{
- return (!(fc.size() == 0));
-}
-
-std::string FileReader::GetLine(int x)
-{
- if ((x<0) || ((unsigned)x>=fc.size()))
- return "";
- return fc[x];
-}
-
-int FileReader::FileSize()
-{
- return fc.size();
-}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef ACCOUNT_H
-#define ACCOUNT_H
-
-#include <map>
-#include <string>
-
-class AccountEvent : public Event
-{
- public:
- User* const user;
- const std::string account;
- AccountEvent(Module* me, User* u, const std::string& name)
- : Event(me, "account_login"), user(u), account(name)
- {
- }
-};
-
-typedef StringExtItem AccountExtItem;
-
-inline AccountExtItem* GetAccountExtItem()
-{
- return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname"));
-}
-
-#endif
# pragma comment(lib, "GeoIP.lib")
#endif
-/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */
/* $LinkerFlags: -lGeoIP */
class ModuleGeoIP : public Module
std::string* SetExt(LocalUser* user)
{
- const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString());
+ const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString().c_str());
if (!c)
c = "UNK";
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
gi = GeoIP_new(GEOIP_STANDARD);
if (gi == NULL)
throw ModuleException("Unable to initialize geoip, are you missing GeoIP.dat?");
- ServerInstance->Modules->AddService(ext);
- Implementation eventlist[] = { I_OnSetConnectClass, I_OnStats };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
LocalUser* user = *i;
if ((user->registered == REG_ALL) && (!ext.get(user)))
GeoIP_delete(gi);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_VENDOR);
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
std::string* cc = ext.get(user);
if (!cc)
return MOD_RES_DENY;
}
- ModResult OnStats(char symbol, User* user, string_list &out)
+ ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
{
if (symbol != 'G')
return MOD_RES_PASSTHRU;
unsigned int unknown = 0;
std::map<std::string, unsigned int> results;
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
std::string* cc = ext.get(*i);
if (cc)
unknown++;
}
- std::string p = ServerInstance->Config->ServerName + " 801 " + user->nick + " :GeoIPSTATS ";
+ std::string p = "801 " + user->nick + " :GeoIPSTATS ";
for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i)
{
out.push_back(p + i->first + " " + ConvToStr(i->second));
};
MODULE_INIT(ModuleGeoIP)
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013-2014 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2014 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+#include <ldap.h>
+
+#ifdef _WIN32
+# pragma comment(lib, "libldap.lib")
+# pragma comment(lib, "liblber.lib")
+#endif
+
+/* $LinkerFlags: -lldap */
+
+class LDAPService : public LDAPProvider, public SocketThread
+{
+ LDAP* con;
+ reference<ConfigTag> config;
+ time_t last_connect;
+ int searchscope;
+ time_t timeout;
+ time_t last_timeout_check;
+
+ LDAPMod** BuildMods(const LDAPMods& attributes)
+ {
+ LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
+ memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
+ for (unsigned int x = 0; x < attributes.size(); ++x)
+ {
+ const LDAPModification& l = attributes[x];
+ LDAPMod* mod = new LDAPMod;
+ mods[x] = mod;
+
+ if (l.op == LDAPModification::LDAP_ADD)
+ mod->mod_op = LDAP_MOD_ADD;
+ else if (l.op == LDAPModification::LDAP_DEL)
+ mod->mod_op = LDAP_MOD_DELETE;
+ else if (l.op == LDAPModification::LDAP_REPLACE)
+ mod->mod_op = LDAP_MOD_REPLACE;
+ else if (l.op != 0)
+ {
+ FreeMods(mods);
+ throw LDAPException("Unknown LDAP operation");
+ }
+ mod->mod_type = strdup(l.name.c_str());
+ mod->mod_values = new char*[l.values.size() + 1];
+ memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
+ for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
+ if (!l.values[j].empty())
+ mod->mod_values[c++] = strdup(l.values[j].c_str());
+ }
+ return mods;
+ }
+
+ void FreeMods(LDAPMod** mods)
+ {
+ for (unsigned int i = 0; mods[i] != NULL; ++i)
+ {
+ LDAPMod* mod = mods[i];
+ if (mod->mod_type != NULL)
+ free(mod->mod_type);
+ if (mod->mod_values != NULL)
+ {
+ for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
+ free(mod->mod_values[j]);
+ delete[] mod->mod_values;
+ }
+ }
+ delete[] mods;
+ }
+
+ void Reconnect()
+ {
+ // Only try one connect a minute. It is an expensive blocking operation
+ if (last_connect > ServerInstance->Time() - 60)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
+ last_connect = ServerInstance->Time();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ Connect();
+ }
+
+ void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
+ {
+ if (i != NULL)
+ {
+ this->LockQueue();
+ this->queries[msgid] = std::make_pair(ServerInstance->Time(), i);
+ this->UnlockQueueWakeup();
+ }
+ }
+
+ void Timeout()
+ {
+ if (last_timeout_check == ServerInstance->Time())
+ return;
+ last_timeout_check = ServerInstance->Time();
+
+ for (query_queue::iterator it = this->queries.begin(); it != this->queries.end(); )
+ {
+ LDAPQuery msgid = it->first;
+ time_t created = it->second.first;
+ LDAPInterface* i = it->second.second;
+ ++it;
+
+ if (ServerInstance->Time() > created + timeout)
+ {
+ LDAPResult* ldap_result = new LDAPResult();
+ ldap_result->id = msgid;
+ ldap_result->error = "Query timed out";
+
+ this->queries.erase(msgid);
+ this->results.push_back(std::make_pair(i, ldap_result));
+
+ this->NotifyParent();
+ }
+ }
+ }
+
+ public:
+ typedef std::map<LDAPQuery, std::pair<time_t, LDAPInterface*> > query_queue;
+ typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
+ query_queue queries;
+ result_queue results;
+
+ LDAPService(Module* c, ConfigTag* tag)
+ : LDAPProvider(c, "LDAP/" + tag->getString("id"))
+ , con(NULL), config(tag), last_connect(0), last_timeout_check(0)
+ {
+ std::string scope = config->getString("searchscope");
+ if (scope == "base")
+ searchscope = LDAP_SCOPE_BASE;
+ else if (scope == "onelevel")
+ searchscope = LDAP_SCOPE_ONELEVEL;
+ else
+ searchscope = LDAP_SCOPE_SUBTREE;
+ timeout = config->getInt("timeout", 5);
+
+ Connect();
+ }
+
+ ~LDAPService()
+ {
+ this->LockQueue();
+
+ for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i)
+ {
+ LDAPQuery msgid = i->first;
+ LDAPInterface* inter = i->second.second;
+
+ ldap_abandon_ext(this->con, msgid, NULL, NULL);
+
+ if (inter)
+ {
+ LDAPResult r;
+ r.error = "LDAP Interface is going away";
+ inter->OnError(r);
+ }
+ }
+ this->queries.clear();
+
+ for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
+ {
+ LDAPInterface* inter = i->first;
+ LDAPResult* r = i->second;
+
+ r->error = "LDAP Interface is going away";
+ if (inter)
+ inter->OnError(*r);
+ }
+ this->results.clear();
+
+ this->UnlockQueue();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ }
+
+ void Connect()
+ {
+ std::string server = config->getString("server");
+ int i = ldap_initialize(&this->con, server.c_str());
+ if (i != LDAP_SUCCESS)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
+
+ const int version = LDAP_VERSION3;
+ i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (i != LDAP_OPT_SUCCESS)
+ {
+ ldap_unbind_ext(this->con, NULL, NULL);
+ this->con = NULL;
+ throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
+ }
+
+ const struct timeval tv = { 0, 0 };
+ i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+ if (i != LDAP_OPT_SUCCESS)
+ {
+ ldap_unbind_ext(this->con, NULL, NULL);
+ this->con = NULL;
+ throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
+ }
+ }
+
+ LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
+ {
+ std::string binddn = config->getString("binddn");
+ std::string bindauth = config->getString("bindauth");
+ return this->Bind(i, binddn, bindauth);
+ }
+
+ LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
+ {
+ berval cred;
+ cred.bv_val = strdup(pass.c_str());
+ cred.bv_len = pass.length();
+
+ LDAPQuery msgid;
+ int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
+ free(cred.bv_val);
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Bind(i, who, pass);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
+ {
+ if (i == NULL)
+ throw LDAPException("No interface");
+
+ LDAPQuery msgid;
+ int ret = ldap_search_ext(this->con, base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Search(i, base, filter);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPMod** mods = this->BuildMods(attributes);
+ LDAPQuery msgid;
+ int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
+ this->FreeMods(mods);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Add(i, dn, attributes);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
+ {
+ LDAPQuery msgid;
+ int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Del(i, dn);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPMod** mods = this->BuildMods(attributes);
+ LDAPQuery msgid;
+ int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
+ this->FreeMods(mods);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Modify(i, base, attributes);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
+ {
+ berval cred;
+ cred.bv_val = strdup(val.c_str());
+ cred.bv_len = val.length();
+
+ LDAPQuery msgid;
+ int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
+ free(cred.bv_val);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Compare(i, dn, attr, val);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ void Run() CXX11_OVERRIDE
+ {
+ while (!this->GetExitFlag())
+ {
+ this->LockQueue();
+ if (this->queries.empty())
+ {
+ this->WaitForQueue();
+ this->UnlockQueue();
+ continue;
+ }
+ this->Timeout();
+ this->UnlockQueue();
+
+ struct timeval tv = { 1, 0 };
+ LDAPMessage* result;
+ int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
+ if (rtype <= 0 || this->GetExitFlag())
+ continue;
+
+ int cur_id = ldap_msgid(result);
+
+ this->LockQueue();
+
+ query_queue::iterator it = this->queries.find(cur_id);
+ if (it == this->queries.end())
+ {
+ this->UnlockQueue();
+ ldap_msgfree(result);
+ continue;
+ }
+ LDAPInterface* i = it->second.second;
+ this->queries.erase(it);
+
+ this->UnlockQueue();
+
+ LDAPResult* ldap_result = new LDAPResult();
+ ldap_result->id = cur_id;
+
+ for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
+ {
+ int cur_type = ldap_msgtype(cur);
+
+ LDAPAttributes attributes;
+
+ {
+ char* dn = ldap_get_dn(this->con, cur);
+ if (dn != NULL)
+ {
+ attributes["dn"].push_back(dn);
+ ldap_memfree(dn);
+ }
+ }
+
+ switch (cur_type)
+ {
+ case LDAP_RES_BIND:
+ ldap_result->type = LDAPResult::QUERY_BIND;
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ ldap_result->type = LDAPResult::QUERY_SEARCH;
+ break;
+ case LDAP_RES_ADD:
+ ldap_result->type = LDAPResult::QUERY_ADD;
+ break;
+ case LDAP_RES_DELETE:
+ ldap_result->type = LDAPResult::QUERY_DELETE;
+ break;
+ case LDAP_RES_MODIFY:
+ ldap_result->type = LDAPResult::QUERY_MODIFY;
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
+ // then the result set is empty
+ ldap_result->type = LDAPResult::QUERY_SEARCH;
+ break;
+ case LDAP_RES_COMPARE:
+ ldap_result->type = LDAPResult::QUERY_COMPARE;
+ break;
+ default:
+ continue;
+ }
+
+ switch (cur_type)
+ {
+ case LDAP_RES_SEARCH_ENTRY:
+ {
+ BerElement* ber = NULL;
+ for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
+ {
+ berval** vals = ldap_get_values_len(this->con, cur, attr);
+ int count = ldap_count_values_len(vals);
+
+ std::vector<std::string> attrs;
+ for (int j = 0; j < count; ++j)
+ attrs.push_back(vals[j]->bv_val);
+ attributes[attr] = attrs;
+
+ ldap_value_free_len(vals);
+ ldap_memfree(attr);
+ }
+ if (ber != NULL)
+ ber_free(ber, 0);
+
+ break;
+ }
+ case LDAP_RES_BIND:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_COMPARE:
+ {
+ int errcode = -1;
+ int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
+ if (parse_result != LDAP_SUCCESS)
+ {
+ ldap_result->error = ldap_err2string(parse_result);
+ }
+ else
+ {
+ if (cur_type == LDAP_RES_COMPARE)
+ {
+ if (errcode != LDAP_COMPARE_TRUE)
+ ldap_result->error = ldap_err2string(errcode);
+ }
+ else if (errcode != LDAP_SUCCESS)
+ ldap_result->error = ldap_err2string(errcode);
+ }
+ break;
+ }
+ default:
+ continue;
+ }
+
+ ldap_result->messages.push_back(attributes);
+ }
+
+ ldap_msgfree(result);
+
+ this->LockQueue();
+ this->results.push_back(std::make_pair(i, ldap_result));
+ this->UnlockQueueWakeup();
+
+ this->NotifyParent();
+ }
+ }
+
+ void OnNotify() CXX11_OVERRIDE
+ {
+ LDAPService::result_queue r;
+
+ this->LockQueue();
+ this->results.swap(r);
+ this->UnlockQueue();
+
+ for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
+ {
+ LDAPInterface* li = i->first;
+ LDAPResult* res = i->second;
+
+ if (!res->error.empty())
+ li->OnError(*res);
+ else
+ li->OnResult(*res);
+
+ delete res;
+ }
+ }
+};
+
+class ModuleLDAP : public Module
+{
+ typedef std::map<std::string, LDAPService*> ServiceMap;
+ ServiceMap LDAPServices;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ServiceMap conns;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("database");
+ for (ConfigIter i = tags.first; i != tags.second; i++)
+ {
+ const reference<ConfigTag>& tag = i->second;
+
+ if (tag->getString("module") != "ldap")
+ continue;
+
+ std::string id = tag->getString("id");
+
+ ServiceMap::iterator curr = LDAPServices.find(id);
+ if (curr == LDAPServices.end())
+ {
+ LDAPService* conn = new LDAPService(this, tag);
+ conns[id] = conn;
+
+ ServerInstance->Modules->AddService(*conn);
+ ServerInstance->Threads.Start(conn);
+ }
+ else
+ {
+ conns.insert(*curr);
+ LDAPServices.erase(curr);
+ }
+ }
+
+ for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ ServerInstance->Modules->DelService(*conn);
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+
+ LDAPServices.swap(conns);
+ }
+
+ void OnUnloadModule(Module* m) CXX11_OVERRIDE
+ {
+ for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
+ {
+ LDAPService* s = it->second;
+ s->LockQueue();
+ for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
+ {
+ int msgid = it2->first;
+ LDAPInterface* i = it2->second.second;
+ ++it2;
+
+ if (i->creator == m)
+ s->queries.erase(msgid);
+ }
+ for (unsigned int i = s->results.size(); i > 0; --i)
+ {
+ LDAPInterface* li = s->results[i - 1].first;
+ LDAPResult* r = s->results[i - 1].second;
+
+ if (li->creator == m)
+ {
+ s->results.erase(s->results.begin() + i - 1);
+ delete r;
+ }
+ }
+ s->UnlockQueue();
+ }
+ }
+
+ ~ModuleLDAP()
+ {
+ for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("LDAP support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAP)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
- * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Allow/Deny connections based upon answer from LDAP server */
-/* $LinkerFlags: -lldap */
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-struct RAIILDAPMessage
-{
- RAIILDAPMessage()
- {
- }
-
- ~RAIILDAPMessage()
- {
- dealloc();
- }
-
- void dealloc()
- {
- ldap_msgfree(msg);
- }
-
- operator LDAPMessage*()
- {
- return msg;
- }
-
- LDAPMessage **operator &()
- {
- return &msg;
- }
-
- LDAPMessage *msg;
-};
-
-class ModuleLDAPAuth : public Module
-{
- LocalIntExt ldapAuthed;
- LocalStringExt ldapVhost;
- std::string base;
- std::string attribute;
- std::string ldapserver;
- std::string allowpattern;
- std::string killreason;
- std::string username;
- std::string password;
- std::string vhost;
- std::vector<std::string> whitelistedcidrs;
- std::vector<std::pair<std::string, std::string> > requiredattributes;
- int searchscope;
- bool verbose;
- bool useusername;
- LDAP *conn;
-
-public:
- ModuleLDAPAuth()
- : ldapAuthed("ldapauth", this)
- , ldapVhost("ldapauth_vhost", this)
- {
- conn = NULL;
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(ldapAuthed);
- ServerInstance->Modules->AddService(ldapVhost);
- Implementation eventlist[] = { I_OnCheckReady, I_OnRehash,I_OnUserRegister, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- void OnRehash(User* user)
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
- whitelistedcidrs.clear();
- requiredattributes.clear();
-
- base = tag->getString("baserdn");
- attribute = tag->getString("attribute");
- ldapserver = tag->getString("server");
- allowpattern = tag->getString("allowpattern");
- killreason = tag->getString("killreason");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- vhost = tag->getString("host");
- verbose = tag->getBool("verbose"); /* Set to true if failed connects should be reported to operators */
- useusername = tag->getBool("userfield");
-
- ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
-
- for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
- {
- std::string cidr = i->second->getString("cidr");
- if (!cidr.empty()) {
- whitelistedcidrs.push_back(cidr);
- }
- }
-
- ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
-
- for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
- {
- const std::string attr = i->second->getString("attribute");
- const std::string val = i->second->getString("value");
-
- if (!attr.empty() && !val.empty())
- requiredattributes.push_back(make_pair(attr, val));
- }
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP connection failed: %s", ldap_err2string(res));
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP set protocol to v3 failed: %s", ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- std::string SafeReplace(const std::string &text, std::map<std::string,
- std::string> &replacements)
- {
- std::string result;
- result.reserve(MAXBUF);
-
- for (unsigned int i = 0; i < text.length(); ++i) {
- char c = text[i];
- if (c == '$') {
- // find the first nonalpha
- i++;
- unsigned int start = i;
-
- while (i < text.length() - 1 && isalpha(text[i + 1]))
- ++i;
-
- std::string key = text.substr(start, (i - start) + 1);
- result.append(replacements[key]);
- } else {
- result.push_back(c);
- }
- }
-
- return result;
- }
-
- virtual void OnUserConnect(LocalUser *user)
- {
- std::string* cc = ldapVhost.get(user);
- if (cc)
- {
- user->ChangeDisplayedHost(cc->c_str());
- ldapVhost.unset(user);
- }
- }
-
- ModResult OnUserRegister(LocalUser* user)
- {
- if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
-
- for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
- {
- if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
- }
-
- if (!CheckCredentials(user))
- {
- ServerInstance->Users->QuitUser(user, killreason);
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool CheckCredentials(LocalUser* user)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- if (user->password.empty())
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
- return false;
- }
-
- int res;
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = const_cast<char*>(password.c_str());
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP bind failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
-
- RAIILDAPMessage msg;
- std::string what = (attribute + "=" + (useusername ? user->ident : user->nick));
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- // Do a second search, based on password, if it contains a :
- // That is, PASS <user>:<password> will work.
- size_t pos = user->password.find(":");
- if (pos != std::string::npos)
- {
- // manpage says we must deallocate regardless of success or failure
- // since we're about to do another query (and reset msg), first
- // free the old one.
- msg.dealloc();
-
- std::string cutpassword = user->password.substr(0, pos);
- res = ldap_search_ext_s(conn, base.c_str(), searchscope, cutpassword.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg);
-
- if (res == LDAP_SUCCESS)
- {
- // Trim the user: prefix, leaving just 'pass' for later password check
- user->password = user->password.substr(pos + 1);
- }
- }
-
- // It may have found based on user:pass check above.
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned more than one result: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- LDAPMessage *entry;
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned no results: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- cred.bv_val = (char*)user->password.data();
- cred.bv_len = user->password.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- if (!requiredattributes.empty())
- {
- bool authed = false;
-
- for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
- {
- const std::string &attr = it->first;
- const std::string &val = it->second;
-
- struct berval attr_value;
- attr_value.bv_val = const_cast<char*>(val.c_str());
- attr_value.bv_len = val.length();
-
- ServerInstance->Logs->Log("m_ldapauth", DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
-
- authed = (ldap_compare_ext_s(conn, DN, attr.c_str(), &attr_value, NULL, NULL) == LDAP_COMPARE_TRUE);
-
- if (authed)
- break;
- }
-
- if (!authed)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Lacks required LDAP attributes)", user->GetFullRealHost().c_str());
- return false;
- }
- }
-
- if (!vhost.empty())
- {
- irc::commasepstream stream(DN);
-
- // mashed map of key:value parts of the DN
- std::map<std::string, std::string> dnParts;
-
- std::string dnPart;
- while (stream.GetToken(dnPart))
- {
- std::string::size_type pos = dnPart.find('=');
- if (pos == std::string::npos) // malformed
- continue;
-
- std::string key = dnPart.substr(0, pos);
- std::string value = dnPart.substr(pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
- dnParts[key] = value;
- }
-
- // change host according to config key
- ldapVhost.set(user, SafeReplace(vhost, dnParts));
- }
-
- ldapAuthed.set(user,1);
- return true;
- }
-
- ModResult OnCheckReady(LocalUser* user)
- {
- return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
- }
-
- Version GetVersion()
- {
- return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "libldap.lib")
-# pragma comment(lib, "liblber.lib")
-#endif
-
-/* $ModDesc: Adds the ability to authenticate opers via LDAP */
-/* $LinkerFlags: -lldap */
-
-// Duplicated code, also found in cmd_oper and m_sqloper
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-class ModuleLDAPAuth : public Module
-{
- std::string base;
- std::string ldapserver;
- std::string username;
- std::string password;
- std::string attribute;
- int searchscope;
- LDAP *conn;
-
- bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
- {
- OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
- if (it == ServerInstance->Config->oper_blocks.end())
- return false;
-
- ConfigTag* tag = it->second->oper_block;
- if (!tag)
- return false;
-
- std::string acceptedhosts = tag->getString("host");
- std::string hostname = user->ident + "@" + user->host;
- if (!OneOfMatches(hostname.c_str(), user->GetIPString(), acceptedhosts))
- return false;
-
- if (!LookupOper(opername, inputpass))
- return false;
-
- user->Oper(it->second);
- return true;
- }
-
-public:
- ModuleLDAPAuth()
- : conn(NULL)
- {
- }
-
- void init()
- {
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- virtual ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- virtual void OnRehash(User* user)
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
-
- base = tag->getString("baserdn");
- ldapserver = tag->getString("server");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- attribute = tag->getString("attribute");
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line)
- {
- if (validated && command == "OPER" && parameters.size() >= 2)
- {
- if (HandleOper(user, parameters[0], parameters[1]))
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool LookupOper(const std::string& opername, const std::string& opassword)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- int res;
- char* authpass = strdup(password.c_str());
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = authpass;
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- free(authpass);
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
- free(authpass);
-
- LDAPMessage *msg, *entry;
- std::string what = attribute + "=" + opername;
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- return false;
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- ldap_msgfree(msg);
- return false;
- }
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- ldap_msgfree(msg);
- return false;
- }
- authpass = strdup(opassword.c_str());
- cred.bv_val = authpass;
- cred.bv_len = opassword.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
- {
- free(authpass);
- ldap_msgfree(msg);
- return true;
- }
- else
- {
- free(authpass);
- ldap_msgfree(msg);
- return false;
- }
- }
-
- virtual Version GetVersion()
- {
- return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
#include "inspircd.h"
#include <tds.h>
#include <tdsconvert.h>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
#include "m_sqlv2.h"
-/* $ModDesc: MsSQL provider */
/* $CompileFlags: exec("grep VERSION_NO /usr/include/tdsver.h 2>/dev/null | perl -e 'print "-D_TDSVER=".((<> =~ /freetds v(\d+\.\d+)/i) ? $1*100 : 0);'") */
/* $LinkerFlags: -ltds */
-/* $ModDep: m_sqlv2.h */
class SQLConn;
class MsSQLResult;
public:
QueryThread(ModuleMsSQL* mod) : Parent(mod) { }
~QueryThread() { }
- virtual void Run();
- virtual void OnNotify();
+ void Run();
+ void OnNotify();
};
class MsSQLResult : public SQLresult
{
}
- ~MsSQLResult()
- {
- }
-
void AddRow(int colsnum, char **dat, char **colname)
{
colnames.clear();
rows++;
}
- virtual int Rows()
+ int Rows()
{
return rows;
}
- virtual int Cols()
+ int Cols()
{
return cols;
}
- virtual std::string ColName(int column)
+ std::string ColName(int column)
{
if (column < (int)colnames.size())
{
return "";
}
- virtual int ColNum(const std::string &column)
+ int ColNum(const std::string &column)
{
for (unsigned int i = 0; i < colnames.size(); i++)
{
return 0;
}
- virtual SQLfield GetValue(int row, int column)
+ SQLfield GetValue(int row, int column)
{
if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
{
return SQLfield("",true);
}
- virtual SQLfieldList& GetRow()
+ SQLfieldList& GetRow()
{
if (currentrow < rows)
return fieldlists[currentrow];
return emptyfieldlist;
}
- virtual SQLfieldMap& GetRowMap()
+ SQLfieldMap& GetRowMap()
{
/* In an effort to reduce overhead we don't actually allocate the map
* until the first time it's needed...so...
return *fieldmap;
}
- virtual SQLfieldList* GetRowPtr()
+ SQLfieldList* GetRowPtr()
{
fieldlist = new SQLfieldList();
return fieldlist;
}
- virtual SQLfieldMap* GetRowMapPtr()
+ SQLfieldMap* GetRowMapPtr()
{
fieldmap = new SQLfieldMap();
return fieldmap;
}
- virtual void Free(SQLfieldMap* fm)
+ void Free(SQLfieldMap* fm)
{
delete fm;
}
- virtual void Free(SQLfieldList* fl)
+ void Free(SQLfieldList* fl)
{
delete fl;
}
if (tds_process_simple_query(sock) != TDS_SUCCEED)
{
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
LoggingMutex->Unlock();
CloseDB();
}
else
{
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
LoggingMutex->Unlock();
CloseDB();
}
else
{
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
LoggingMutex->Unlock();
CloseDB();
}
char* msquery = strdup(req->query.q.data());
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEBUG,"doing Query: %s",msquery);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "doing Query: %s",msquery);
LoggingMutex->Unlock();
if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
{
int tds_res;
while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED)
{
- //ServerInstance->Logs->Log("m_mssql",DEBUG,"<******> result type: %d", tds_res);
- //ServerInstance->Logs->Log("m_mssql",DEBUG,"AFFECTED ROWS: %d", sock->rows_affected);
+ //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "<******> result type: %d", tds_res);
+ //ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "AFFECTED ROWS: %d", sock->rows_affected);
switch (tds_res)
{
case TDS_ROWFMT_RESULT:
if (sock->res_info->row_count > 0)
{
int cols = sock->res_info->num_cols;
- char** name = new char*[MAXBUF];
- char** data = new char*[MAXBUF];
+ char** name = new char*[512];
+ char** data = new char*[512];
for (int j=0; j<cols; j++)
{
TDSCOLUMN* col = sock->current_results->columns[j];
{
SQLConn* sc = (SQLConn*)pContext->parent;
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql", DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
LoggingMutex->Unlock();
return 0;
}
{
SQLConn* sc = (SQLConn*)pContext->parent;
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql", DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
LoggingMutex->Unlock();
return 0;
}
queryDispatcher = new QueryThread(this);
}
- void init()
+ void init() CXX11_OVERRIDE
{
ReadConf();
- ServerInstance->Threads->Start(queryDispatcher);
-
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->Modules->AddService(sqlserv);
+ ServerInstance->Threads.Start(queryDispatcher);
}
- virtual ~ModuleMsSQL()
+ ~ModuleMsSQL()
{
queryDispatcher->join();
delete queryDispatcher;
if (HasHost(hi))
{
LoggingMutex->Lock();
- ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
LoggingMutex->Unlock();
return;
}
connections.clear();
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
queryDispatcher->LockQueue();
ReadConf();
queryDispatcher->UnlockQueueWakeup();
}
- void OnRequest(Request& request)
+ void OnRequest(Request& request) CXX11_OVERRIDE
{
if(strcmp(SQLREQID, request.id) == 0)
{
return ++currid;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("MsSQL provider", VF_VENDOR);
}
#include "inspircd.h"
#include <mysql.h>
-#include "sql.h"
+#include "modules/sql.h"
#ifdef _WIN32
# pragma comment(lib, "libmysql.lib")
/* VERSION 3 API: With nonblocking (threaded) requests */
-/* $ModDesc: SQL Service Provider module for all other m_sql* modules */
/* $CompileFlags: exec("mysql_config --include") */
/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */
ConnMap connections; // main thread only
ModuleSQL();
- void init();
+ void init() CXX11_OVERRIDE;
~ModuleSQL();
- void OnRehash(User* user);
- void OnUnloadModule(Module* mod);
- Version GetVersion();
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+ Version GetVersion() CXX11_OVERRIDE;
};
class DispatcherThread : public SocketThread
public:
DispatcherThread(ModuleSQL* CreatorModule) : Parent(CreatorModule) { }
~DispatcherThread() { }
- virtual void Run();
- virtual void OnNotify();
+ void Run();
+ void OnNotify();
};
#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
}
- ~MySQLresult()
- {
- }
-
- virtual int Rows()
+ int Rows()
{
return rows;
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result)
{
result.assign(colnames.begin(), colnames.end());
}
- virtual SQLEntry GetValue(int row, int column)
+ SQLEntry GetValue(int row, int column)
{
if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size()))
{
return SQLEntry();
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQLEntries& result)
{
if (currentrow < rows)
{
void ModuleSQL::init()
{
Dispatcher = new DispatcherThread(this);
- ServerInstance->Threads->Start(Dispatcher);
-
- Implementation eventlist[] = { I_OnRehash, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
+ ServerInstance->Threads.Start(Dispatcher);
}
ModuleSQL::~ModuleSQL()
}
}
-void ModuleSQL::OnRehash(User* user)
+void ModuleSQL::ReadConfig(ConfigStatus& status)
{
ConnMap conns;
ConfigTagList tags = ServerInstance->Config->ConfTags("database");
#include "inspircd.h"
#include <cstdlib>
-#include <sstream>
#include <libpq-fe.h>
-#include "sql.h"
+#include "modules/sql.h"
-/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */
/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */
private:
ModulePgSQL* mod;
public:
- ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m)
+ ReconnectTimer(ModulePgSQL* m) : Timer(5, false), mod(m)
{
}
- virtual void Tick(time_t TIME);
+ bool Tick(time_t TIME);
};
struct QueueItem
PQclear(res);
}
- virtual int Rows()
+ int Rows()
{
return rows;
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result)
{
result.resize(PQnfields(res));
for(unsigned int i=0; i < result.size(); i++)
}
}
- virtual SQLEntry GetValue(int row, int column)
+ SQLEntry GetValue(int row, int column)
{
char* v = PQgetvalue(res, row, column);
if (!v || PQgetisnull(res, row, column))
return SQLEntry(std::string(v, PQgetlength(res, row, column)));
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQLEntries& result)
{
if (currentrow >= PQntuples(res))
return false;
{
if (!DoConnect())
{
- ServerInstance->Logs->Log("m_pgsql",DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
DelayReconnect();
}
}
}
}
- virtual void HandleEvent(EventType et, int errornum)
+ void HandleEvent(EventType et, int errornum)
{
switch (et)
{
if(this->fd <= -1)
return false;
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
{
- ServerInstance->Logs->Log("m_pgsql",DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
return false;
}
switch(PQconnectPoll(sql))
{
case PGRES_POLLING_WRITING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
status = CWRITE;
return true;
case PGRES_POLLING_READING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = WWRITE;
DoConnectedPoll();
default:
switch(PQresetPoll(sql))
{
case PGRES_POLLING_WRITING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
status = CWRITE;
return DoPoll();
case PGRES_POLLING_READING:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
status = WWRITE;
DoConnectedPoll();
default:
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
#else
size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
#endif
int error;
size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
#else
size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
#endif
void Close()
{
- ServerInstance->SE->DelFd(this);
+ SocketEngine::DelFd(this);
if(sql)
{
ReconnectTimer* retimer;
ModulePgSQL()
+ : retimer(NULL)
{
}
- void init()
+ ~ModulePgSQL()
{
- ReadConf();
-
- Implementation eventlist[] = { I_OnUnloadModule, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePgSQL()
- {
- if (retimer)
- ServerInstance->Timers->DelTimer(retimer);
+ delete retimer;
ClearAllConnections();
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ReadConf();
}
connections.clear();
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
SQLerror err(SQL_BAD_DBID);
for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API", VF_VENDOR);
}
};
-void ReconnectTimer::Tick(time_t time)
+bool ReconnectTimer::Tick(time_t time)
{
mod->retimer = NULL;
mod->ReadConf();
+ delete this;
+ return false;
}
void SQLConn::DelayReconnect()
if (!mod->retimer)
{
mod->retimer = new ReconnectTimer(mod);
- ServerInstance->Timers->AddTimer(mod->retimer);
+ ServerInstance->Timers.AddTimer(mod->retimer);
}
}
}
#include "inspircd.h"
#include <pcre.h>
-#include "m_regex.h"
+#include "modules/regex.h"
-/* $ModDesc: Regex Provider Module for PCRE */
-/* $ModDep: m_regex.h */
/* $CompileFlags: exec("pcre-config --cflags") */
/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
# pragma comment(lib, "libpcre.lib")
#endif
-class PCREException : public ModuleException
-{
-public:
- PCREException(const std::string& rx, const std::string& error, int erroffset)
- : ModuleException("Error in regex " + rx + " at offset " + ConvToStr(erroffset) + ": " + error)
- {
- }
-};
-
class PCRERegex : public Regex
{
-private:
pcre* regex;
-public:
+ public:
PCRERegex(const std::string& rx) : Regex(rx)
{
const char* error;
regex = pcre_compile(rx.c_str(), 0, &error, &erroffset, NULL);
if (!regex)
{
- ServerInstance->Logs->Log("REGEX", DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
- throw PCREException(rx, error, erroffset);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "pcre_compile failed: /%s/ [%d] %s", rx.c_str(), erroffset, error);
+ throw RegexException(rx, error, erroffset);
}
}
- virtual ~PCRERegex()
+ ~PCRERegex()
{
pcre_free(regex);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (pcre_exec(regex, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) >= 0);
}
};
{
public:
PCREFactory(Module* m) : RegexFactory(m, "regex/pcre") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new PCRERegex(expr);
}
class ModuleRegexPCRE : public Module
{
-public:
+ public:
PCREFactory ref;
- ModuleRegexPCRE() : ref(this) {
- ServerInstance->Modules->AddService(ref);
+ ModuleRegexPCRE() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for PCRE", VF_VENDOR);
}
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include <sys/types.h>
#include <regex.h>
-/* $ModDesc: Regex Provider Module for POSIX Regular Expressions */
-/* $ModDep: m_regex.h */
-
-class POSIXRegexException : public ModuleException
-{
-public:
- POSIXRegexException(const std::string& rx, const std::string& error)
- : ModuleException("Error in regex " + rx + ": " + error)
- {
- }
-};
-
class POSIXRegex : public Regex
{
-private:
regex_t regbuf;
-public:
+ public:
POSIXRegex(const std::string& rx, bool extended) : Regex(rx)
{
int flags = (extended ? REG_EXTENDED : 0) | REG_NOSUB;
error = errbuf;
delete[] errbuf;
regfree(®buf);
- throw POSIXRegexException(rx, error);
+ throw RegexException(rx, error);
}
}
- virtual ~POSIXRegex()
+ ~POSIXRegex()
{
regfree(®buf);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (regexec(®buf, text.c_str(), 0, NULL, 0) == 0)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (regexec(®buf, text.c_str(), 0, NULL, 0) == 0);
}
};
public:
bool extended;
PosixFactory(Module* m) : RegexFactory(m, "regex/posix") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new POSIXRegex(expr, extended);
}
class ModuleRegexPOSIX : public Module
{
PosixFactory ref;
-public:
- ModuleRegexPOSIX() : ref(this) {
- ServerInstance->Modules->AddService(ref);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+
+ public:
+ ModuleRegexPOSIX() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for POSIX Regular Expressions", VF_VENDOR);
}
- void OnRehash(User* u)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ref.extended = ServerInstance->Config->ConfValue("posix")->getBool("extended");
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+ * Copyright (C) 2012 ChrisTX <chris@rev-crew.info>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/regex.h"
+
+// Fix warnings about the use of `long long` on C++03 and
+// shadowing on GCC.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include <re2/re2.h>
+
+/* $LinkerFlags: -lre2 */
+
+class RE2Regex : public Regex
+{
+ RE2 regexcl;
+
+ public:
+ RE2Regex(const std::string& rx) : Regex(rx), regexcl(rx, RE2::Quiet)
+ {
+ if (!regexcl.ok())
+ {
+ throw RegexException(rx, regexcl.error());
+ }
+ }
+
+ bool Matches(const std::string& text) CXX11_OVERRIDE
+ {
+ return RE2::FullMatch(text, regexcl);
+ }
+};
+
+class RE2Factory : public RegexFactory
+{
+ public:
+ RE2Factory(Module* m) : RegexFactory(m, "regex/re2") { }
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
+ {
+ return new RE2Regex(expr);
+ }
+};
+
+class ModuleRegexRE2 : public Module
+{
+ RE2Factory ref;
+
+ public:
+ ModuleRegexRE2() : ref(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Regex Provider Module for RE2", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleRegexRE2)
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
+
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include <regex>
-/* $ModDesc: Regex Provider Module for std::regex Regular Expressions */
-/* $ModConfig: <stdregex type="ecmascript">
- * Specify the Regular Expression engine to use here. Valid settings are
- * bre, ere, awk, grep, egrep, ecmascript (default if not specified)*/
/* $CompileFlags: -std=c++11 */
-/* $ModDep: m_regex.h */
-
-class StdRegexException : public ModuleException
-{
-public:
- StdRegexException(const std::string& rx, const std::string& error)
- : ModuleException(std::string("Error in regex ") + rx + ": " + error)
- {
- }
-};
class StdRegex : public Regex
{
-private:
std::regex regexcl;
-public:
+
+ public:
StdRegex(const std::string& rx, std::regex::flag_type fltype) : Regex(rx)
{
try{
}
catch(std::regex_error rxerr)
{
- throw StdRegexException(rx, rxerr.what());
+ throw RegexException(rx, rxerr.what());
}
}
-
- virtual bool Matches(const std::string& text)
+
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
return std::regex_search(text, regexcl);
}
public:
std::regex::flag_type regextype;
StdRegexFactory(Module* m) : RegexFactory(m, "regex/stdregex") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new StdRegex(expr, regextype);
}
{
public:
StdRegexFactory ref;
- ModuleRegexStd() : ref(this) {
- ServerInstance->Modules->AddService(ref);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
+ ModuleRegexStd() : ref(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex Provider Module for std::regex", VF_VENDOR);
}
-
- void OnRehash(User* u)
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* Conf = ServerInstance->Config->ConfValue("stdregex");
std::string regextype = Conf->getString("type", "ecmascript");
-
+
if(regextype == "bre")
ref.regextype = std::regex::basic;
else if(regextype == "ere")
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include <sys/types.h>
#include <tre/regex.h>
-/* $ModDesc: Regex Provider Module for TRE Regular Expressions */
/* $CompileFlags: pkgconfincludes("tre","tre/regex.h","") */
/* $LinkerFlags: pkgconflibs("tre","/libtre.so","-ltre") rpath("pkg-config --libs tre") */
-/* $ModDep: m_regex.h */
-
-class TRERegexException : public ModuleException
-{
-public:
- TRERegexException(const std::string& rx, const std::string& error)
- : ModuleException("Error in regex " + rx + ": " + error)
- {
- }
-};
class TRERegex : public Regex
{
-private:
regex_t regbuf;
public:
error = errbuf;
delete[] errbuf;
regfree(®buf);
- throw TRERegexException(rx, error);
+ throw RegexException(rx, error);
}
}
- virtual ~TRERegex()
+ ~TRERegex()
{
regfree(®buf);
}
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
- if (regexec(®buf, text.c_str(), 0, NULL, 0) == 0)
- {
- // Bang. :D
- return true;
- }
- return false;
+ return (regexec(®buf, text.c_str(), 0, NULL, 0) == 0);
}
};
-class TREFactory : public RegexFactory {
+class TREFactory : public RegexFactory
+{
public:
TREFactory(Module* m) : RegexFactory(m, "regex/tre") {}
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new TRERegex(expr);
}
class ModuleRegexTRE : public Module
{
TREFactory trf;
-public:
- ModuleRegexTRE() : trf(this) {
- ServerInstance->Modules->AddService(trf);
- }
- Version GetVersion()
+ public:
+ ModuleRegexTRE() : trf(this)
{
- return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
}
- ~ModuleRegexTRE()
+ Version GetVersion() CXX11_OVERRIDE
{
+ return Version("Regex Provider Module for TRE Regular Expressions", VF_VENDOR);
}
};
#include "inspircd.h"
+#include "modules/sql.h"
+
+// Fix warnings about the use of `long long` on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
#include <sqlite3.h>
-#include "sql.h"
#ifdef _WIN32
# pragma comment(lib, "sqlite3.lib")
#endif
-/* $ModDesc: sqlite3 provider */
/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */
/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
-/* $NoPedantic */
class SQLConn;
typedef std::map<std::string, SQLConn*> ConnMap;
{
}
- ~SQLite3Result()
- {
- }
-
- virtual int Rows()
+ int Rows()
{
return rows;
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQLEntries& result)
{
if (currentrow < rows)
{
}
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result)
{
result.assign(columns.begin(), columns.end());
}
class SQLConn : public SQLProvider
{
- private:
sqlite3* conn;
reference<ConfigTag> config;
std::string host = tag->getString("hostname");
if (sqlite3_open_v2(host.c_str(), &conn, SQLITE_OPEN_READWRITE, 0) != SQLITE_OK)
{
- ServerInstance->Logs->Log("m_sqlite3",DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not open DB with id: " + tag->getString("id"));
conn = NULL;
}
}
sqlite3_finalize(stmt);
}
- virtual void submit(SQLQuery* query, const std::string& q)
+ void submit(SQLQuery* query, const std::string& q)
{
Query(query, q);
delete query;
}
- virtual void submit(SQLQuery* query, const std::string& q, const ParamL& p)
+ void submit(SQLQuery* query, const std::string& q, const ParamL& p)
{
std::string res;
unsigned int param = 0;
submit(query, res);
}
- virtual void submit(SQLQuery* query, const std::string& q, const ParamM& p)
+ void submit(SQLQuery* query, const std::string& q, const ParamM& p)
{
std::string res;
for(std::string::size_type i = 0; i < q.length(); i++)
class ModuleSQLite3 : public Module
{
- private:
ConnMap conns;
public:
- ModuleSQLite3()
- {
- }
-
- void init()
- {
- ReadConf();
-
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleSQLite3()
+ ~ModuleSQLite3()
{
ClearConns();
}
conns.clear();
}
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ClearConns();
ConfigTagList tags = ServerInstance->Config->ConfTags("database");
}
}
- void OnRehash(User* user)
- {
- ReadConf();
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("sqlite3 provider", VF_VENDOR);
}
#include "inspircd.h"
+#include "modules/ssl.h"
+#include <memory>
+
+// Fix warnings about the use of commas at end of enumerator lists on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-pedantic"
+#endif
+
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
-#include "ssl.h"
-#include "m_cap.h"
+
+#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 9) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 9 && GNUTLS_VERSION_PATCH >= 8))
+#define GNUTLS_HAS_MAC_GET_ID
+#include <gnutls/crypto.h>
+#endif
+
+#if (GNUTLS_VERSION_MAJOR > 2 || GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 12)
+# define GNUTLS_HAS_RND
+#else
+# include <gcrypt.h>
+#endif
#ifdef _WIN32
# pragma comment(lib, "libgnutls-28.lib")
#endif
-/* $ModDesc: Provides SSL support for clients */
-/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --cflags") */
-/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") iflt("pkg-config --modversion gnutls","2.12") exec("libgcrypt-config --libs") */
-/* $NoPedantic */
+/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") eval("print `libgcrypt-config --cflags | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
+/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") eval("print `libgcrypt-config --libs | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
#ifndef GNUTLS_VERSION_MAJOR
#define GNUTLS_VERSION_MAJOR LIBGNUTLS_VERSION_MAJOR
typedef gnutls_dh_params_t gnutls_dh_params;
#endif
-#if (GNUTLS_VERSION_MAJOR > 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR >= 12))
-# define GNUTLS_HAS_RND
-# include <gnutls/crypto.h>
-#else
-# include <gcrypt.h>
-#endif
-
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
-struct SSLConfig : public refcountbase
-{
- gnutls_certificate_credentials_t x509_cred;
- std::vector<gnutls_x509_crt_t> x509_certs;
- gnutls_x509_privkey_t x509_key;
- gnutls_dh_params_t dh_params;
-#ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_t priority;
-#endif
-
- SSLConfig()
- : x509_cred(NULL)
- , x509_key(NULL)
- , dh_params(NULL)
-#ifdef GNUTLS_NEW_PRIO_API
- , priority(NULL)
-#endif
- {
- }
-
- ~SSLConfig()
- {
- ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Destroying SSLConfig %p", (void*)this);
-
- if (x509_cred)
- gnutls_certificate_free_credentials(x509_cred);
-
- for (unsigned int i = 0; i < x509_certs.size(); i++)
- gnutls_x509_crt_deinit(x509_certs[i]);
-
- if (x509_key)
- gnutls_x509_privkey_deinit(x509_key);
-
- if (dh_params)
- gnutls_dh_params_deinit(dh_params);
-
-#ifdef GNUTLS_NEW_PRIO_API
- if (priority)
- gnutls_priority_deinit(priority);
-#endif
- }
-};
-
-static reference<SSLConfig> currconf;
-
-static SSLConfig* GetSessionConfig(gnutls_session_t session);
-
-#if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
- const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) {
-
- st->type = GNUTLS_CRT_X509;
+#if (GNUTLS_VERSION_MAJOR > 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR >= 12))
+#define GNUTLS_NEW_CERT_CALLBACK_API
+typedef gnutls_retr2_st cert_cb_last_param_type;
#else
-static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
- const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) {
- st->cert_type = GNUTLS_CRT_X509;
- st->key_type = GNUTLS_PRIVKEY_X509;
+typedef gnutls_retr_st cert_cb_last_param_type;
#endif
- SSLConfig* conf = GetSessionConfig(session);
- std::vector<gnutls_x509_crt_t>& x509_certs = conf->x509_certs;
- st->ncerts = x509_certs.size();
- st->cert.x509 = &x509_certs[0];
- st->key.x509 = conf->x509_key;
- st->deinit_all = 0;
-
- return 0;
-}
class RandGen : public HandlerBase2<void, char*, size_t>
{
public:
- RandGen() {}
void Call(char* buffer, size_t len)
{
#ifdef GNUTLS_HAS_RND
}
};
-/** Represents an SSL user's extra data
- */
-class issl_session
+namespace GnuTLS
{
-public:
- StreamSocket* socket;
- gnutls_session_t sess;
- issl_status status;
- reference<ssl_cert> cert;
- reference<SSLConfig> config;
-
- issl_session() : socket(NULL), sess(NULL), status(ISSL_NONE) {}
-};
+ class Init
+ {
+ public:
+ Init() { gnutls_global_init(); }
+ ~Init() { gnutls_global_deinit(); }
+ };
-static SSLConfig* GetSessionConfig(gnutls_session_t sess)
-{
- issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess));
- return session->config;
-}
+ class Exception : public ModuleException
+ {
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
-class CommandStartTLS : public SplitCommand
-{
- public:
- bool enabled;
- CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
+ void ThrowOnError(int errcode, const char* msg)
{
- enabled = true;
- works_before_reg = true;
+ if (errcode < 0)
+ {
+ std::string reason = msg;
+ reason.append(" :").append(gnutls_strerror(errcode));
+ throw Exception(reason);
+ }
}
- CmdResult HandleLocal(const std::vector<std::string> ¶meters, LocalUser *user)
+ /** Used to create a gnutls_datum_t* from a std::string
+ */
+ class Datum
+ {
+ gnutls_datum_t datum;
+
+ public:
+ Datum(const std::string& dat)
+ {
+ datum.data = (unsigned char*)dat.data();
+ datum.size = static_cast<unsigned int>(dat.length());
+ }
+
+ const gnutls_datum_t* get() const { return &datum; }
+ };
+
+ class Hash
{
- if (!enabled)
+ gnutls_digest_algorithm_t hash;
+
+ public:
+ // Nothing to deallocate, constructor may throw freely
+ Hash(const std::string& hashname)
{
- user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
- return CMD_FAILURE;
+ // As older versions of gnutls can't do this, let's disable it where needed.
+#ifdef GNUTLS_HAS_MAC_GET_ID
+ // As gnutls_digest_algorithm_t and gnutls_mac_algorithm_t are mapped 1:1, we can do this
+ // There is no gnutls_dig_get_id() at the moment, but it may come later
+ hash = (gnutls_digest_algorithm_t)gnutls_mac_get_id(hashname.c_str());
+ if (hash == GNUTLS_DIG_UNKNOWN)
+ throw Exception("Unknown hash type " + hashname);
+
+ // Check if the user is giving us something that is a valid MAC but not digest
+ gnutls_hash_hd_t is_digest;
+ if (gnutls_hash_init(&is_digest, hash) < 0)
+ throw Exception("Unknown hash type " + hashname);
+ gnutls_hash_deinit(is_digest, NULL);
+#else
+ if (hashname == "md5")
+ hash = GNUTLS_DIG_MD5;
+ else if (hashname == "sha1")
+ hash = GNUTLS_DIG_SHA1;
+#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT
+ else if (hashname == "sha256")
+ hash = GNUTLS_DIG_SHA256;
+#endif
+ else
+ throw Exception("Unknown hash type " + hashname);
+#endif
}
- if (user->registered == REG_ALL)
+ gnutls_digest_algorithm_t get() const { return hash; }
+ };
+
+ class DHParams
+ {
+ gnutls_dh_params_t dh_params;
+
+ DHParams()
{
- user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
+ ThrowOnError(gnutls_dh_params_init(&dh_params), "gnutls_dh_params_init() failed");
}
- else
+
+ public:
+ /** Import */
+ static std::auto_ptr<DHParams> Import(const std::string& dhstr)
+ {
+ std::auto_ptr<DHParams> dh(new DHParams);
+ int ret = gnutls_dh_params_import_pkcs3(dh->dh_params, Datum(dhstr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to import DH params");
+ return dh;
+ }
+
+ /** Generate */
+ static std::auto_ptr<DHParams> Generate(unsigned int bits)
+ {
+ std::auto_ptr<DHParams> dh(new DHParams);
+ ThrowOnError(gnutls_dh_params_generate2(dh->dh_params, bits), "Unable to generate DH params");
+ return dh;
+ }
+
+ ~DHParams()
{
- if (!user->eh.GetIOHook())
+ gnutls_dh_params_deinit(dh_params);
+ }
+
+ const gnutls_dh_params_t& get() const { return dh_params; }
+ };
+
+ class X509Key
+ {
+ /** Ensure that the key is deinited in case the constructor of X509Key throws
+ */
+ class RAIIKey
+ {
+ public:
+ gnutls_x509_privkey_t key;
+
+ RAIIKey()
{
- user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
- /* We need to flush the write buffer prior to adding the IOHook,
- * otherwise we'll be sending this line inside the SSL session - which
- * won't start its handshake until the client gets this line. Currently,
- * we assume the write will not block here; this is usually safe, as
- * STARTTLS is sent very early on in the registration phase, where the
- * user hasn't built up much sendq. Handling a blocked write here would
- * be very annoying.
- */
- user->eh.DoWrite();
- user->eh.AddIOHook(creator);
- creator->OnStreamSocketAccept(&user->eh, NULL, NULL);
+ ThrowOnError(gnutls_x509_privkey_init(&key), "gnutls_x509_privkey_init() failed");
}
- else
- user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
+
+ ~RAIIKey()
+ {
+ gnutls_x509_privkey_deinit(key);
+ }
+ } key;
+
+ public:
+ /** Import */
+ X509Key(const std::string& keystr)
+ {
+ int ret = gnutls_x509_privkey_import(key.key, Datum(keystr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to import private key");
}
- return CMD_FAILURE;
- }
-};
+ gnutls_x509_privkey_t& get() { return key.key; }
+ };
-class ModuleSSLGnuTLS : public Module
-{
- issl_session* sessions;
+ class X509CertList
+ {
+ std::vector<gnutls_x509_crt_t> certs;
+
+ public:
+ /** Import */
+ X509CertList(const std::string& certstr)
+ {
+ unsigned int certcount = 3;
+ certs.resize(certcount);
+ Datum datum(certstr);
- gnutls_digest_algorithm_t hash;
+ int ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ // the buffer wasn't big enough to hold all certs but gnutls changed certcount to the number of available certs,
+ // try again with a bigger buffer
+ certs.resize(certcount);
+ ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ }
- std::string sslports;
- int dh_bits;
+ ThrowOnError(ret, "Unable to load certificates");
- RandGen randhandler;
- CommandStartTLS starttls;
+ // Resize the vector to the actual number of certs because we rely on its size being correct
+ // when deallocating the certs
+ certs.resize(certcount);
+ }
- GenericCap capHandler;
- ServiceProvider iohook;
+ ~X509CertList()
+ {
+ for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i)
+ gnutls_x509_crt_deinit(*i);
+ }
- inline static const char* UnknownIfNULL(const char* str)
- {
- return str ? str : "UNKNOWN";
- }
+ gnutls_x509_crt_t* raw() { return &certs[0]; }
+ unsigned int size() const { return certs.size(); }
+ };
- static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
+ class X509CRL : public refcountbase
{
- issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
- if (session->socket->GetEventMask() & FD_READ_WILL_BLOCK)
+ class RAIICRL
{
-#ifdef _WIN32
- gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
- errno = EAGAIN;
-#endif
- return -1;
- }
+ public:
+ gnutls_x509_crl_t crl;
- int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0);
+ RAIICRL()
+ {
+ ThrowOnError(gnutls_x509_crl_init(&crl), "gnutls_x509_crl_init() failed");
+ }
-#ifdef _WIN32
- if (rv < 0)
+ ~RAIICRL()
+ {
+ gnutls_x509_crl_deinit(crl);
+ }
+ } crl;
+
+ public:
+ /** Import */
+ X509CRL(const std::string& crlstr)
{
- /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
- * and then set errno appropriately.
- * The gnutls library may also have a different errno variable than us, see
- * gnutls_transport_set_errno(3).
- */
- gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ int ret = gnutls_x509_crl_import(get(), Datum(crlstr).get(), GNUTLS_X509_FMT_PEM);
+ ThrowOnError(ret, "Unable to load certificate revocation list");
}
-#endif
- if (rv < (int)size)
- ServerInstance->SE->ChangeEventMask(session->socket, FD_READ_WILL_BLOCK);
- return rv;
- }
+ gnutls_x509_crl_t& get() { return crl.crl; }
+ };
- static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
+#ifdef GNUTLS_NEW_PRIO_API
+ class Priority
{
- issl_session* session = reinterpret_cast<issl_session*>(session_wrap);
- if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK)
+ gnutls_priority_t priority;
+
+ public:
+ Priority(const std::string& priorities)
{
-#ifdef _WIN32
- gnutls_transport_set_errno(session->sess, EAGAIN);
-#else
- errno = EAGAIN;
-#endif
- return -1;
+ // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
+ // If the user did not supply anything then the string is already set to "NORMAL"
+ const char* priocstr = priorities.c_str();
+ const char* prioerror;
+
+ int ret = gnutls_priority_init(&priority, priocstr, &prioerror);
+ if (ret < 0)
+ {
+ // gnutls did not understand the user supplied string
+ throw Exception("Unable to initialize priorities to \"" + priorities + "\": " + gnutls_strerror(ret) + " Syntax error at position " + ConvToStr((unsigned int) (prioerror - priocstr)));
+ }
}
- int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0);
+ ~Priority()
+ {
+ gnutls_priority_deinit(priority);
+ }
-#ifdef _WIN32
- if (rv < 0)
+ void SetupSession(gnutls_session_t sess)
{
- /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
- * and then set errno appropriately.
- * The gnutls library may also have a different errno variable than us, see
- * gnutls_transport_set_errno(3).
- */
- gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ gnutls_priority_set(sess, priority);
+ }
+ };
+#else
+ /** Dummy class, used when gnutls_priority_set() is not available
+ */
+ class Priority
+ {
+ public:
+ Priority(const std::string& priorities)
+ {
+ if (priorities != "NORMAL")
+ throw Exception("You've set a non-default priority string, but GnuTLS lacks support for it");
+ }
+
+ static void SetupSession(gnutls_session_t sess)
+ {
+ // Always set the default priorities
+ gnutls_set_default_priority(sess);
}
+ };
#endif
- if (rv < (int)size)
- ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK);
- return rv;
- }
+ class CertCredentials
+ {
+ /** DH parameters associated with these credentials
+ */
+ std::auto_ptr<DHParams> dh;
- public:
+ protected:
+ gnutls_certificate_credentials_t cred;
- ModuleSSLGnuTLS()
- : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
- {
-#ifndef GNUTLS_HAS_RND
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
+ public:
+ CertCredentials()
+ {
+ ThrowOnError(gnutls_certificate_allocate_credentials(&cred), "Cannot allocate certificate credentials");
+ }
- sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+ ~CertCredentials()
+ {
+ gnutls_certificate_free_credentials(cred);
+ }
- gnutls_global_init(); // This must be called once in the program
- }
+ /** Associates these credentials with the session
+ */
+ void SetupSession(gnutls_session_t sess)
+ {
+ gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred);
+ }
- void init()
- {
- currconf = new SSLConfig;
- InitSSLConfig(currconf);
+ /** Set the given DH parameters to be used with these credentials
+ */
+ void SetDH(std::auto_ptr<DHParams>& DH)
+ {
+ dh = DH;
+ gnutls_certificate_set_dh_params(cred, dh->get());
+ }
+ };
- ServerInstance->GenRandom = &randhandler;
+ class X509Credentials : public CertCredentials
+ {
+ /** Private key
+ */
+ X509Key key;
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect,
- I_OnEvent, I_OnHookIO };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ /** Certificate list, presented to the peer
+ */
+ X509CertList certs;
- ServerInstance->Modules->AddService(iohook);
- ServerInstance->Modules->AddService(starttls);
- }
+ /** Trusted CA, may be NULL
+ */
+ std::auto_ptr<X509CertList> trustedca;
- void OnRehash(User* user)
- {
- sslports.clear();
+ /** Certificate revocation list, may be NULL
+ */
+ std::auto_ptr<X509CRL> crl;
- ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
- starttls.enabled = Conf->getBool("starttls", true);
+ static int cert_callback(gnutls_session_t session, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st);
- if (Conf->getBool("showports", true))
+ public:
+ X509Credentials(const std::string& certstr, const std::string& keystr)
+ : key(keystr)
+ , certs(certstr)
{
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
+ // Throwing is ok here, the destructor of Credentials is called in that case
+ int ret = gnutls_certificate_set_x509_key(cred, certs.raw(), certs.size(), key.get());
+ ThrowOnError(ret, "Unable to set cert/key pair");
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
- {
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "gnutls")
- continue;
+#ifdef GNUTLS_NEW_CERT_CALLBACK_API
+ gnutls_certificate_set_retrieve_function(cred, cert_callback);
+#else
+ gnutls_certificate_client_set_retrieve_function(cred, cert_callback);
+#endif
+ }
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
+ /** Sets the trusted CA and the certificate revocation list
+ * to use when verifying certificates
+ */
+ void SetCA(std::auto_ptr<X509CertList>& certlist, std::auto_ptr<X509CRL>& CRL)
+ {
+ // Do nothing if certlist is NULL
+ if (certlist.get())
+ {
+ int ret = gnutls_certificate_set_x509_trust(cred, certlist->raw(), certlist->size());
+ ThrowOnError(ret, "gnutls_certificate_set_x509_trust() failed");
- if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+ if (CRL.get())
{
- /*
- * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
- * the IP:port in ISUPPORT.
- *
- * We used to advertise all ports seperated by a ';' char that matched the above criteria,
- * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
- * To solve this by default we now only display the first IP:port found and let the user
- * configure the exact value for the 005 token, if necessary.
- */
- sslports = portid;
- break;
+ ret = gnutls_certificate_set_x509_crl(cred, &CRL->get(), 1);
+ ThrowOnError(ret, "gnutls_certificate_set_x509_crl() failed");
}
+
+ trustedca = certlist;
+ crl = CRL;
}
}
- }
+ };
- void OnModuleRehash(User* user, const std::string ¶m)
+ class Profile : public refcountbase
{
- if(param != "ssl")
- return;
+ /** Name of this profile
+ */
+ const std::string name;
- reference<SSLConfig> newconf = new SSLConfig;
- try
+ /** X509 certificate(s) and key
+ */
+ X509Credentials x509cred;
+
+ /** The minimum length in bits for the DH prime to be accepted as a client
+ */
+ unsigned int min_dh_bits;
+
+ /** Hashing algorithm to use when generating certificate fingerprints
+ */
+ Hash hash;
+
+ /** Priorities for ciphers, compression methods, etc.
+ */
+ Priority priority;
+
+ Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr,
+ std::auto_ptr<DHParams>& DH, unsigned int mindh, const std::string& hashstr,
+ const std::string& priostr, std::auto_ptr<X509CertList>& CA, std::auto_ptr<X509CRL>& CRL)
+ : name(profilename)
+ , x509cred(certstr, keystr)
+ , min_dh_bits(mindh)
+ , hash(hashstr)
+ , priority(priostr)
{
- InitSSLConfig(newconf);
+ x509cred.SetDH(DH);
+ x509cred.SetCA(CA, CRL);
}
- catch (ModuleException& ex)
+
+ static std::string ReadFile(const std::string& filename)
{
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Not applying new config. %s", ex.GetReason());
- return;
+ FileReader reader(filename);
+ std::string ret = reader.GetString();
+ if (ret.empty())
+ throw Exception("Cannot read file " + filename);
+ return ret;
}
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls: Applying new config, old config is in use by %d connection(s)", currconf->GetReferenceCount()-1);
- currconf = newconf;
- }
+ public:
+ static reference<Profile> Create(const std::string& profilename, ConfigTag* tag)
+ {
+ std::string certstr = ReadFile(tag->getString("certfile", "cert.pem"));
+ std::string keystr = ReadFile(tag->getString("keyfile", "key.pem"));
- void InitSSLConfig(SSLConfig* config)
- {
- ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Initializing new SSLConfig %p", (void*)config);
+ std::auto_ptr<DHParams> dh;
+ int gendh = tag->getInt("gendh");
+ if (gendh)
+ {
+ gendh = (gendh < 1024 ? 1024 : gendh);
+ dh = DHParams::Generate(gendh);
+ }
+ else
+ dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem")));
+
+ // Use default priority string if this tag does not specify one
+ std::string priostr = tag->getString("priority", "NORMAL");
+ unsigned int mindh = tag->getInt("mindhbits", 1024);
+ std::string hashstr = tag->getString("hash", "md5");
+
+ // Load trusted CA and revocation list, if set
+ std::auto_ptr<X509CertList> ca;
+ std::auto_ptr<X509CRL> crl;
+ std::string filename = tag->getString("cafile");
+ if (!filename.empty())
+ {
+ ca.reset(new X509CertList(ReadFile(filename)));
+
+ filename = tag->getString("crlfile");
+ if (!filename.empty())
+ crl.reset(new X509CRL(ReadFile(filename)));
+ }
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- std::string crlfile;
- OnRehash(NULL);
+ return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl);
+ }
- ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
+ /** Set up the given session with the settings in this profile
+ */
+ void SetupSession(gnutls_session_t sess)
+ {
+ priority.SetupSession(sess);
+ x509cred.SetupSession(sess);
+ gnutls_dh_set_prime_bits(sess, min_dh_bits);
+ }
- cafile = Conf->getString("cafile", CONFIG_PATH "/ca.pem");
- crlfile = Conf->getString("crlfile", CONFIG_PATH "/crl.pem");
- certfile = Conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = Conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dh_bits = Conf->getInt("dhbits");
- std::string hashname = Conf->getString("hash", "md5");
+ const std::string& GetName() const { return name; }
+ X509Credentials& GetX509Credentials() { return x509cred; }
+ gnutls_digest_algorithm_t GetHash() const { return hash.get(); }
+ };
+}
- // The GnuTLS manual states that the gnutls_set_default_priority()
- // call we used previously when initializing the session is the same
- // as setting the "NORMAL" priority string.
- // Thus if the setting below is not in the config we will behave exactly
- // the same as before, when the priority setting wasn't available.
- std::string priorities = Conf->getString("priority", "NORMAL");
+class GnuTLSIOHook : public SSLIOHook
+{
+ private:
+ gnutls_session_t sess;
+ issl_status status;
+ reference<GnuTLS::Profile> profile;
- if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
- dh_bits = 1024;
+ void InitSession(StreamSocket* user, bool me_server)
+ {
+ gnutls_init(&sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
- if (hashname == "md5")
- hash = GNUTLS_DIG_MD5;
- else if (hashname == "sha1")
- hash = GNUTLS_DIG_SHA1;
-#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT
- else if (hashname == "sha256")
- hash = GNUTLS_DIG_SHA256;
-#endif
- else
- throw ModuleException("Unknown hash type " + hashname);
+ profile->SetupSession(sess);
+ gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(user));
+ gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
+ gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
+ if (me_server)
+ gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+ }
- int ret;
+ void CloseSession()
+ {
+ if (this->sess)
+ {
+ gnutls_bye(this->sess, GNUTLS_SHUT_WR);
+ gnutls_deinit(this->sess);
+ }
+ sess = NULL;
+ certificate = NULL;
+ status = ISSL_NONE;
+ }
- gnutls_certificate_credentials_t& x509_cred = config->x509_cred;
+ bool Handshake(StreamSocket* user)
+ {
+ int ret = gnutls_handshake(this->sess);
- ret = gnutls_certificate_allocate_credentials(&x509_cred);
if (ret < 0)
{
- // Set to NULL because we can't be sure what value is in it and we must not try to
- // deallocate it in case of an error
- x509_cred = NULL;
- throw ModuleException("Failed to allocate certificate credentials: " + std::string(gnutls_strerror(ret)));
- }
+ if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ // Handshake needs resuming later, read() or write() would have blocked.
- if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
+ if (gnutls_record_get_direction(this->sess) == 0)
+ {
+ // gnutls_handshake() wants to read() again.
+ this->status = ISSL_HANDSHAKING_READ;
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ }
+ else
+ {
+ // gnutls_handshake() wants to write() again.
+ this->status = ISSL_HANDSHAKING_WRITE;
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ }
+ }
+ else
+ {
+ user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
+ CloseSession();
+ this->status = ISSL_CLOSING;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Change the seesion state
+ this->status = ISSL_HANDSHAKEN;
- if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
+ VerifyCertificate();
- FileReader reader;
+ // Finish writing, if any left
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- reader.LoadFile(certfile);
- std::string cert_string = reader.Contents();
- gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), static_cast<unsigned int>(cert_string.length()) };
+ return true;
+ }
+ }
- reader.LoadFile(keyfile);
- std::string key_string = reader.Contents();
- gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), static_cast<unsigned int>(key_string.length()) };
+ void VerifyCertificate()
+ {
+ unsigned int certstatus;
+ const gnutls_datum_t* cert_list;
+ int ret;
+ unsigned int cert_list_size;
+ gnutls_x509_crt_t cert;
+ char str[512];
+ unsigned char digest[512];
+ size_t digest_size = sizeof(digest);
+ size_t name_size = sizeof(str);
+ ssl_cert* certinfo = new ssl_cert;
+ this->certificate = certinfo;
- std::vector<gnutls_x509_crt_t>& x509_certs = config->x509_certs;
+ /* This verification function uses the trusted CAs in the credentials
+ * structure. So you must have installed one or more CA certificates.
+ */
+ ret = gnutls_certificate_verify_peers2(this->sess, &certstatus);
- // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
- unsigned int certcount = 3;
- x509_certs.resize(certcount);
- ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
- if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ if (ret < 0)
{
- // the buffer wasn't big enough to hold all certs but gnutls updated certcount to the number of available certs, try again with a bigger buffer
- x509_certs.resize(certcount);
- ret = gnutls_x509_crt_list_import(&x509_certs[0], &certcount, &cert_datum, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ certinfo->error = std::string(gnutls_strerror(ret));
+ return;
}
- if (ret <= 0)
+ certinfo->invalid = (certstatus & GNUTLS_CERT_INVALID);
+ certinfo->unknownsigner = (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND);
+ certinfo->revoked = (certstatus & GNUTLS_CERT_REVOKED);
+ certinfo->trusted = !(certstatus & GNUTLS_CERT_SIGNER_NOT_CA);
+
+ /* Up to here the process is the same for X.509 certificates and
+ * OpenPGP keys. From now on X.509 certificates are assumed. This can
+ * be easily extended to work with openpgp keys as well.
+ */
+ if (gnutls_certificate_type_get(this->sess) != GNUTLS_CRT_X509)
{
- // clear the vector so we won't call gnutls_x509_crt_deinit() on the (uninited) certs later
- x509_certs.clear();
- throw ModuleException("Unable to load GnuTLS server certificate (" + certfile + "): " + ((ret < 0) ? (std::string(gnutls_strerror(ret))) : "No certs could be read"));
+ certinfo->error = "No X509 keys sent";
+ return;
}
- x509_certs.resize(ret);
- gnutls_x509_privkey_t& x509_key = config->x509_key;
- if (gnutls_x509_privkey_init(&x509_key) < 0)
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
{
- // Make sure the destructor does not try to deallocate this, see above
- x509_key = NULL;
- throw ModuleException("Unable to initialize private key");
+ certinfo->error = gnutls_strerror(ret);
+ return;
}
- if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
- throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
-
- if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
- throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
-
- #ifdef GNUTLS_NEW_PRIO_API
- // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
- // If the user did not supply anything then the string is already set to "NORMAL"
- const char* priocstr = priorities.c_str();
- const char* prioerror;
-
- gnutls_priority_t& priority = config->priority;
- if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
+ cert_list_size = 0;
+ cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size);
+ if (cert_list == NULL)
{
- // gnutls did not understand the user supplied string, log and fall back to the default priorities
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
- gnutls_priority_init(&priority, "NORMAL", NULL);
+ certinfo->error = "No certificate was found";
+ goto info_done_dealloc;
}
- #else
- if (priorities != "NORMAL")
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
- #endif
-
- #if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
- gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
- #else
- gnutls_certificate_set_retrieve_function (x509_cred, cert_callback);
- #endif
+ /* This is not a real world example, since we only check the first
+ * certificate in the given chain.
+ */
- gnutls_dh_params_t& dh_params = config->dh_params;
- ret = gnutls_dh_params_init(&dh_params);
+ ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
if (ret < 0)
{
- // Make sure the destructor does not try to deallocate this, see above
- dh_params = NULL;
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
- return;
+ certinfo->error = gnutls_strerror(ret);
+ goto info_done_dealloc;
}
- std::string dhfile = Conf->getString("dhfile");
- if (!dhfile.empty())
+ if (gnutls_x509_crt_get_dn(cert, str, &name_size) == 0)
{
- // Try to load DH params from file
- reader.LoadFile(dhfile);
- std::string dhstring = reader.Contents();
- gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) };
+ std::string& dn = certinfo->dn;
+ dn = str;
+ // Make sure there are no chars in the string that we consider invalid
+ if (dn.find_first_of("\r\n") != std::string::npos)
+ dn.clear();
+ }
- if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
- {
- // File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
- GenerateDHParams(dh_params);
- }
+ name_size = sizeof(str);
+ if (gnutls_x509_crt_get_issuer_dn(cert, str, &name_size) == 0)
+ {
+ std::string& issuer = certinfo->issuer;
+ issuer = str;
+ if (issuer.find_first_of("\r\n") != std::string::npos)
+ issuer.clear();
+ }
+
+ if ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0)
+ {
+ certinfo->error = gnutls_strerror(ret);
+ }
+ else
+ {
+ certinfo->fingerprint = BinToHex(digest, digest_size);
}
- else
+
+ /* Beware here we do not check for errors.
+ */
+ if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
{
- GenerateDHParams(dh_params);
+ certinfo->error = "Not activated, or expired certificate";
}
- gnutls_certificate_set_dh_params(x509_cred, dh_params);
+info_done_dealloc:
+ gnutls_x509_crt_deinit(cert);
}
- void GenerateDHParams(gnutls_dh_params_t dh_params)
+ static const char* UnknownIfNULL(const char* str)
{
- // Generate Diffie Hellman parameters - for use with DHE
- // kx algorithms. These should be discarded and regenerated
- // once a day, once a week or once a month. Depending on the
- // security requirements.
-
- int ret;
-
- if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
+ return str ? str : "UNKNOWN";
}
- ~ModuleSSLGnuTLS()
+ static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
{
- currconf = NULL;
-
- gnutls_global_deinit();
- delete[] sessions;
- ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
- }
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+#endif
- void OnCleanup(int target_type, void* item)
- {
- if(target_type == TYPE_USER)
+ if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
{
- LocalUser* user = IS_LOCAL(static_cast<User*>(item));
-
- if (user && user->eh.GetIOHook() == this)
- {
- // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
- // Potentially there could be multiple SSL modules loaded at once on different ports.
- ServerInstance->Users->QuitUser(user, "SSL module unloading");
- }
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
}
- }
-
- Version GetVersion()
- {
- return Version("Provides SSL support for clients", VF_VENDOR);
- }
-
- void On005Numeric(std::string &output)
- {
- if (!sslports.empty())
- output.append(" SSL=" + sslports);
- if (starttls.enabled)
- output.append(" STARTTLS");
- }
+ int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
- {
- if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
+#ifdef _WIN32
+ if (rv < 0)
{
- /* Hook the user with our module */
- user->AddIOHook(this);
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
}
+#endif
+
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+ return rv;
}
- void OnRequest(Request& request)
+ static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
{
- if (strcmp("GET_SSL_CERT", request.id) == 0)
- {
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
+#ifdef _WIN32
+ GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook());
+#endif
- req.cert = session->cert;
- }
- else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+ if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
{
- SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
- if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
- req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+#ifdef _WIN32
+ gnutls_transport_set_errno(session->sess, EAGAIN);
+#else
+ errno = EAGAIN;
+#endif
+ return -1;
}
- }
-
- void InitSession(StreamSocket* user, bool me_server)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
- session->socket = user;
- session->config = currconf;
-
- #ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_set(session->sess, currconf->priority);
- #else
- gnutls_set_default_priority(session->sess);
- #endif
- gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, currconf->x509_cred);
- gnutls_dh_set_prime_bits(session->sess, dh_bits);
- gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
- gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
- gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
- if (me_server)
- gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
-
- Handshake(session, user);
- }
-
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- {
- issl_session* session = &sessions[user->GetFd()];
+ int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
- /* For STARTTLS: Don't try and init a session on a socket that already has a session */
- if (session->sess)
- return;
+#ifdef _WIN32
+ if (rv < 0)
+ {
+ /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
+ * and then set errno appropriately.
+ * The gnutls library may also have a different errno variable than us, see
+ * gnutls_transport_set_errno(3).
+ */
+ gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
+ }
+#endif
- InitSession(user, true);
+ if (rv < (int)size)
+ SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+ return rv;
}
- void OnStreamSocketConnect(StreamSocket* user)
+ public:
+ GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool outbound, const reference<GnuTLS::Profile>& sslprofile)
+ : SSLIOHook(hookprov)
+ , sess(NULL)
+ , status(ISSL_NONE)
+ , profile(sslprofile)
{
- InitSession(user, false);
+ InitSession(sock, outbound);
+ sock->AddIOHook(this);
+ Handshake(sock);
}
- void OnStreamSocketClose(StreamSocket* user)
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
{
- CloseSession(&sessions[user->GetFd()]);
+ CloseSession();
}
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
{
- issl_session* session = &sessions[user->GetFd()];
-
- if (!session->sess)
+ if (!this->sess)
{
- CloseSession(session);
+ CloseSession();
user->SetError("No SSL session");
return -1;
}
- if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
+ if (this->status == ISSL_HANDSHAKING_READ || this->status == ISSL_HANDSHAKING_WRITE)
{
// The handshake isn't finished, try to finish it.
- if(!Handshake(session, user))
+ if (!Handshake(user))
{
- if (session->status != ISSL_CLOSING)
+ if (this->status != ISSL_CLOSING)
return 0;
return -1;
}
}
- // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
+ // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
- if (session->status == ISSL_HANDSHAKEN)
+ if (this->status == ISSL_HANDSHAKEN)
{
char* buffer = ServerInstance->GetReadBuffer();
size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
+ int ret = gnutls_record_recv(this->sess, buffer, bufsiz);
if (ret > 0)
{
recvq.append(buffer, ret);
else if (ret == 0)
{
user->SetError("Connection closed");
- CloseSession(session);
+ CloseSession();
return -1;
}
else
{
user->SetError(gnutls_strerror(ret));
- CloseSession(session);
+ CloseSession();
return -1;
}
}
- else if (session->status == ISSL_CLOSING)
+ else if (this->status == ISSL_CLOSING)
return -1;
return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
+ int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE
{
- issl_session* session = &sessions[user->GetFd()];
-
- if (!session->sess)
+ if (!this->sess)
{
- CloseSession(session);
+ CloseSession();
user->SetError("No SSL session");
return -1;
}
- if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
+ if (this->status == ISSL_HANDSHAKING_WRITE || this->status == ISSL_HANDSHAKING_READ)
{
// The handshake isn't finished, try to finish it.
- Handshake(session, user);
- if (session->status != ISSL_CLOSING)
+ Handshake(user);
+ if (this->status != ISSL_CLOSING)
return 0;
return -1;
}
int ret = 0;
- if (session->status == ISSL_HANDSHAKEN)
+ if (this->status == ISSL_HANDSHAKEN)
{
- ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
+ ret = gnutls_record_send(this->sess, sendq.data(), sendq.length());
if (ret == (int)sendq.length())
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
return 1;
}
else if (ret > 0)
{
sendq = sendq.substr(ret);
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else // (ret < 0)
{
user->SetError(gnutls_strerror(ret));
- CloseSession(session);
+ CloseSession();
return -1;
}
}
return 0;
}
- bool Handshake(issl_session* session, StreamSocket* user)
+ void TellCiphersAndFingerprint(LocalUser* user)
{
- int ret = gnutls_handshake(session->sess);
-
- if (ret < 0)
+ if (sess)
{
- if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
- {
- // Handshake needs resuming later, read() or write() would have blocked.
+ std::string text = "*** You are connected using SSL cipher '";
- if(gnutls_record_get_direction(session->sess) == 0)
- {
- // gnutls_handshake() wants to read() again.
- session->status = ISSL_HANDSHAKING_READ;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- }
- else
- {
- // gnutls_handshake() wants to write() again.
- session->status = ISSL_HANDSHAKING_WRITE;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
- }
- }
- else
- {
- user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
- CloseSession(session);
- session->status = ISSL_CLOSING;
- }
+ text += UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
+ text.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
+ text.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))).append("'");
- return false;
+ if (!certificate->fingerprint.empty())
+ text += " and your SSL certificate fingerprint is " + certificate->fingerprint;
+
+ user->WriteNotice(text);
}
- else
- {
- // Change the seesion state
- session->status = ISSL_HANDSHAKEN;
+ }
- VerifyCertificate(session,user);
+ GnuTLS::Profile* GetProfile() { return profile; }
+};
- // Finish writing, if any left
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
+{
+#ifndef GNUTLS_NEW_CERT_CALLBACK_API
+ st->type = GNUTLS_CRT_X509;
+#else
+ st->cert_type = GNUTLS_CRT_X509;
+ st->key_type = GNUTLS_PRIVKEY_X509;
+#endif
+ StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
+ GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetIOHook())->GetProfile()->GetX509Credentials();
- return true;
- }
- }
+ st->ncerts = cred.certs.size();
+ st->cert.x509 = cred.certs.raw();
+ st->key.x509 = cred.key.get();
+ st->deinit_all = 0;
+
+ return 0;
+}
- void OnUserConnect(LocalUser* user)
+class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider
+{
+ reference<GnuTLS::Profile> profile;
+
+ public:
+ GnuTLSIOHookProvider(Module* mod, reference<GnuTLS::Profile>& prof)
+ : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+ , profile(prof)
{
- if (user->eh.GetIOHook() == this)
- {
- if (sessions[user->eh.GetFd()].sess)
- {
- const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
- std::string cipher = UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
- cipher.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
- cipher.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
-
- ssl_cert* cert = sessions[user->eh.GetFd()].cert;
- if (cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), cipher.c_str(), cert->fingerprint.c_str());
- }
- }
+ ServerInstance->Modules->AddService(*this);
}
- void CloseSession(issl_session* session)
+ ~GnuTLSIOHookProvider()
{
- if (session->sess)
- {
- gnutls_bye(session->sess, GNUTLS_SHUT_WR);
- gnutls_deinit(session->sess);
- }
- session->socket = NULL;
- session->sess = NULL;
- session->cert = NULL;
- session->status = ISSL_NONE;
- session->config = NULL;
+ ServerInstance->Modules->DelService(*this);
}
- void VerifyCertificate(issl_session* session, StreamSocket* user)
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
- if (!session->sess || !user)
- return;
+ new GnuTLSIOHook(this, sock, true, profile);
+ }
- unsigned int status;
- const gnutls_datum_t* cert_list;
- int ret;
- unsigned int cert_list_size;
- gnutls_x509_crt_t cert;
- char name[MAXBUF];
- unsigned char digest[MAXBUF];
- size_t digest_size = sizeof(digest);
- size_t name_size = sizeof(name);
- ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new GnuTLSIOHook(this, sock, false, profile);
+ }
+};
- /* This verification function uses the trusted CAs in the credentials
- * structure. So you must have installed one or more CA certificates.
- */
- ret = gnutls_certificate_verify_peers2(session->sess, &status);
+class ModuleSSLGnuTLS : public Module
+{
+ typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
- if (ret < 0)
- {
- certinfo->error = std::string(gnutls_strerror(ret));
- return;
- }
+ // First member of the class, gets constructed first and destructed last
+ GnuTLS::Init libinit;
+ RandGen randhandler;
+ ProfileList profiles;
- certinfo->invalid = (status & GNUTLS_CERT_INVALID);
- certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
- certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
- certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
+ void ReadProfiles()
+ {
+ // First, store all profiles in a new, temporary container. If no problems occur, swap the two
+ // containers; this way if something goes wrong we can go back and continue using the current profiles,
+ // avoiding unpleasant situations where no new SSL connections are possible.
+ ProfileList newprofiles;
- /* Up to here the process is the same for X.509 certificates and
- * OpenPGP keys. From now on X.509 certificates are assumed. This can
- * be easily extended to work with openpgp keys as well.
- */
- if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
{
- certinfo->error = "No X509 keys sent";
- return;
- }
+ // No <sslprofile> tags found, create a profile named "gnutls" from settings in the <gnutls> block
+ const std::string defname = "gnutls";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <gnutls> tag");
- ret = gnutls_x509_crt_init(&cert);
- if (ret < 0)
- {
- certinfo->error = gnutls_strerror(ret);
- return;
+ try
+ {
+ reference<GnuTLS::Profile> profile(GnuTLS::Profile::Create(defname, tag));
+ newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
+ }
}
- cert_list_size = 0;
- cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
- if (cert_list == NULL)
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- certinfo->error = "No certificate was found";
- goto info_done_dealloc;
- }
+ ConfigTag* tag = i->second;
+ if (tag->getString("provider") != "gnutls")
+ continue;
- /* This is not a real world example, since we only check the first
- * certificate in the given chain.
- */
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
- ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
- if (ret < 0)
- {
- certinfo->error = gnutls_strerror(ret);
- goto info_done_dealloc;
- }
+ reference<GnuTLS::Profile> profile;
+ try
+ {
+ profile = GnuTLS::Profile::Create(name, tag);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
- if (gnutls_x509_crt_get_dn(cert, name, &name_size) == 0)
- {
- std::string& dn = certinfo->dn;
- dn = name;
- // Make sure there are no chars in the string that we consider invalid
- if (dn.find_first_of("\r\n") != std::string::npos)
- dn.clear();
+ newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
}
- name_size = sizeof(name);
- if (gnutls_x509_crt_get_issuer_dn(cert, name, &name_size) == 0)
- {
- std::string& issuer = certinfo->issuer;
- issuer = name;
- if (issuer.find_first_of("\r\n") != std::string::npos)
- issuer.clear();
- }
+ // New profiles are ok, begin using them
+ // Old profiles are deleted when their refcount drops to zero
+ profiles.swap(newprofiles);
+ }
+
+ public:
+ ModuleSSLGnuTLS()
+ {
+#ifndef GNUTLS_HAS_RND
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ ReadProfiles();
+ ServerInstance->GenRandom = &randhandler;
+ }
- if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
+ void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE
+ {
+ if(param != "ssl")
+ return;
+
+ try
{
- certinfo->error = gnutls_strerror(ret);
+ ReadProfiles();
}
- else
+ catch (ModuleException& ex)
{
- certinfo->fingerprint = irc::hex(digest, digest_size);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
}
+ }
- /* Beware here we do not check for errors.
- */
- if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
+ ~ModuleSSLGnuTLS()
+ {
+ ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
+ }
+
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if(target_type == TYPE_USER)
{
- certinfo->error = "Not activated, or expired certificate";
+ LocalUser* user = IS_LOCAL(static_cast<User*>(item));
+
+ if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this)
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
}
+ }
-info_done_dealloc:
- gnutls_x509_crt_deinit(cert);
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
- void OnEvent(Event& ev)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- if (starttls.enabled)
- capHandler.HandleEvent(ev);
+ IOHook* hook = user->eh.GetIOHook();
+ if (hook && hook->prov->creator == this)
+ static_cast<GnuTLSIOHook*>(hook)->TellCiphersAndFingerprint(user);
}
};
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /* HACK: This prevents OpenSSL on OS X 10.7 and later from spewing deprecation
- * warnings for every single function call. As far as I (SaberUK) know, Apple
- * have no plans to remove OpenSSL so this warning just causes needless spam.
- */
-#ifdef __APPLE__
-# define __AVAILABILITYMACROS__
-# define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
-#endif
-
+
#include "inspircd.h"
+#include "iohook.h"
+#include "modules/ssl.h"
+
+// Ignore OpenSSL deprecation warnings on OS X Lion and newer.
+#if defined __APPLE__
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#include <openssl/ssl.h>
#include <openssl/err.h>
-#include "ssl.h"
#ifdef _WIN32
# pragma comment(lib, "ssleay32.lib")
# pragma comment(lib, "libeay32.lib")
-# undef MAX_DESCRIPTORS
-# define MAX_DESCRIPTORS 10000
#endif
-/* $ModDesc: Provides SSL support for clients */
-
-/* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
-/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
-/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
-
-/* $NoPedantic */
-
-
-class ModuleSSLOpenSSL;
+/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
+/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
static bool SelfSigned = false;
-
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
-static ModuleSSLOpenSSL* opensslmod = NULL;
-#endif
+static int exdataindex;
char* get_error()
{
return ERR_error_string(ERR_get_error(), NULL);
}
-static int error_callback(const char *str, size_t len, void *u);
+static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
-/** Represents an SSL user's extra data
- */
-class issl_session
+namespace OpenSSL
{
-public:
- SSL* sess;
- issl_status status;
- reference<ssl_cert> cert;
-
- bool outbound;
- bool data_to_write;
-
- issl_session()
- : sess(NULL)
- , status(ISSL_NONE)
+ class Exception : public ModuleException
{
- outbound = false;
- data_to_write = false;
- }
-};
+ public:
+ Exception(const std::string& reason)
+ : ModuleException(reason) { }
+ };
-static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
-{
- /* XXX: This will allow self signed certificates.
- * In the future if we want an option to not allow this,
- * we can just return preverify_ok here, and openssl
- * will boot off self-signed and invalid peer certs.
- */
- int ve = X509_STORE_CTX_get_error(ctx);
-
- SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
-
- return 1;
-}
+ class DHParams
+ {
+ DH* dh;
-class ModuleSSLOpenSSL : public Module
-{
- issl_session* sessions;
+ public:
+ DHParams(const std::string& filename)
+ {
+ BIO* dhpfile = BIO_new_file(filename.c_str(), "r");
+ if (dhpfile == NULL)
+ throw Exception("Couldn't open DH file " + filename);
- SSL_CTX* ctx;
- SSL_CTX* clictx;
+ dh = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
+ BIO_free(dhpfile);
- long ctx_options;
- long clictx_options;
+ if (!dh)
+ throw Exception("Couldn't read DH params from file " + filename);
+ }
- std::string sslports;
- bool use_sha;
+ ~DHParams()
+ {
+ DH_free(dh);
+ }
- ServiceProvider iohook;
+ DH* get()
+ {
+ return dh;
+ }
+ };
- static void SetContextOptions(SSL_CTX* ctx, long defoptions, const std::string& ctxname, ConfigTag* tag)
+ class Context
{
- long setoptions = tag->getInt(ctxname + "setoptions");
- // User-friendly config options for setting context options
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
- if (tag->getBool("cipherserverpref"))
- setoptions |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+ SSL_CTX* const ctx;
+ long ctx_options;
+
+ public:
+ Context(SSL_CTX* context)
+ : ctx(context)
+ {
+ // Sane default options for OpenSSL see https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
+ // and when choosing a cipher, use the server's preferences instead of the client preferences.
+ long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE;
+ // Only turn options on if they exist
+#ifdef SSL_OP_SINGLE_ECDH_USE
+ opts |= SSL_OP_SINGLE_ECDH_USE;
#endif
-#ifdef SSL_OP_NO_COMPRESSION
- if (!tag->getBool("compression", true))
- setoptions |= SSL_OP_NO_COMPRESSION;
+#ifdef SSL_OP_NO_TICKET
+ opts |= SSL_OP_NO_TICKET;
#endif
- if (!tag->getBool("sslv3", true))
- setoptions |= SSL_OP_NO_SSLv3;
- if (!tag->getBool("tlsv1", true))
- setoptions |= SSL_OP_NO_TLSv1;
- long clearoptions = tag->getInt(ctxname + "clearoptions");
- ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Setting OpenSSL %s context options, default: %ld set: %ld clear: %ld", ctxname.c_str(), defoptions, setoptions, clearoptions);
+ ctx_options = SSL_CTX_set_options(ctx, opts);
+ SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
+ }
- // Clear everything
- SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
+ ~Context()
+ {
+ SSL_CTX_free(ctx);
+ }
- // Set the default options and what is in the conf
- SSL_CTX_set_options(ctx, defoptions | setoptions);
- long final = SSL_CTX_clear_options(ctx, clearoptions);
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "OpenSSL %s context options: %ld", ctxname.c_str(), final);
- }
+ bool SetDH(DHParams& dh)
+ {
+ ERR_clear_error();
+ return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
+ }
#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
- void SetupECDH(ConfigTag* tag)
- {
- std::string curvename = tag->getString("ecdhcurve", "prime256v1");
- if (curvename.empty())
- return;
-
- int nid = OBJ_sn2nid(curvename.c_str());
- if (nid == 0)
+ void SetECDH(const std::string& curvename)
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
- return;
+ int nid = OBJ_sn2nid(curvename.c_str());
+ if (nid == 0)
+ throw Exception("Unknown curve: " + curvename);
+
+ EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
+ if (!eckey)
+ throw Exception("Unable to create EC key object");
+
+ ERR_clear_error();
+ bool ret = (SSL_CTX_set_tmp_ecdh(ctx, eckey) >= 0);
+ EC_KEY_free(eckey);
+ if (!ret)
+ throw Exception("Couldn't set ECDH parameters");
}
+#endif
- EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
- if (!eckey)
+ bool SetCiphers(const std::string& ciphers)
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unable to create EC key object");
- return;
+ ERR_clear_error();
+ return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
}
- ERR_clear_error();
- if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+ bool SetCerts(const std::string& filename)
{
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set ECDH parameters");
- ERR_print_errors_cb(error_callback, this);
+ ERR_clear_error();
+ return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
}
- EC_KEY_free(eckey);
- }
-#endif
+ bool SetPrivateKey(const std::string& filename)
+ {
+ ERR_clear_error();
+ return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+ }
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- static void SSLInfoCallback(const SSL* ssl, int where, int rc)
- {
- int fd = SSL_get_fd(const_cast<SSL*>(ssl));
- issl_session& session = opensslmod->sessions[fd];
+ bool SetCA(const std::string& filename)
+ {
+ ERR_clear_error();
+ return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
+ }
- if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+ long GetDefaultContextOptions() const
{
- // The other side is trying to renegotiate, kill the connection and change status
- // to ISSL_NONE so CheckRenego() closes the session
- session.status = ISSL_NONE;
- ServerInstance->SE->Shutdown(fd, 2);
+ return ctx_options;
}
- }
- bool CheckRenego(StreamSocket* sock, issl_session* session)
- {
- if (session->status != ISSL_NONE)
- return true;
+ long SetRawContextOptions(long setoptions, long clearoptions)
+ {
+ // Clear everything
+ SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
- ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Session %p killed, attempted to renegotiate", (void*)session->sess);
- CloseSession(session);
- sock->SetError("Renegotiation is not allowed");
- return false;
- }
-#endif
+ // Set the default options and what is in the conf
+ SSL_CTX_set_options(ctx, ctx_options | setoptions);
+ return SSL_CTX_clear_options(ctx, clearoptions);
+ }
- public:
+ SSL* CreateSession()
+ {
+ return SSL_new(ctx);
+ }
+ };
- ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
+ class Profile : public refcountbase
{
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- opensslmod = this;
-#endif
- sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
-
- /* Global SSL library initialization*/
- SSL_library_init();
- SSL_load_error_strings();
-
- /* Build our SSL contexts:
- * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
+ /** Name of this profile
*/
- ctx = SSL_CTX_new( SSLv23_server_method() );
- clictx = SSL_CTX_new( SSLv23_client_method() );
-
- SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ const std::string name;
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
- SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ /** DH parameters in use
+ */
+ DHParams dh;
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
- SSL_CTX_set_session_cache_mode(clictx, SSL_SESS_CACHE_OFF);
+ /** OpenSSL makes us have two contexts, one for servers and one for clients
+ */
+ Context ctx;
+ Context clictx;
- long opts = SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE;
- // Only turn options on if they exist
-#ifdef SSL_OP_SINGLE_ECDH_USE
- opts |= SSL_OP_SINGLE_ECDH_USE;
-#endif
-#ifdef SSL_OP_NO_TICKET
- opts |= SSL_OP_NO_TICKET;
-#endif
+ /** Digest to use when generating fingerprints
+ */
+ const EVP_MD* digest;
- ctx_options = SSL_CTX_set_options(ctx, opts);
- clictx_options = SSL_CTX_set_options(clictx, opts);
- }
+ /** Last error, set by error_callback()
+ */
+ std::string lasterr;
- void init()
- {
- // Needs the flag as it ignores a plain /rehash
- OnModuleRehash(NULL,"ssl");
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->Modules->AddService(iohook);
- }
+ /** True if renegotiations are allowed, false if not
+ */
+ const bool allowrenego;
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
- {
- if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
+ static int error_callback(const char* str, size_t len, void* u)
{
- /* Hook the user with our module */
- user->AddIOHook(this);
+ Profile* profile = reinterpret_cast<Profile*>(u);
+ profile->lasterr = std::string(str, len - 1);
+ return 0;
}
- }
- void OnRehash(User* user)
- {
- sslports.clear();
+ /** Set raw OpenSSL context (SSL_CTX) options from a config tag
+ * @param ctxname Name of the context, client or server
+ * @param tag Config tag defining this profile
+ * @param context Context object to manipulate
+ */
+ void SetContextOptions(const std::string& ctxname, ConfigTag* tag, Context& context)
+ {
+ long setoptions = tag->getInt(ctxname + "setoptions");
+ long clearoptions = tag->getInt(ctxname + "clearoptions");
+#ifdef SSL_OP_NO_COMPRESSION
+ if (!tag->getBool("compression", true))
+ setoptions |= SSL_OP_NO_COMPRESSION;
+#endif
+ if (!tag->getBool("sslv3", true))
+ setoptions |= SSL_OP_NO_SSLv3;
+ if (!tag->getBool("tlsv1", true))
+ setoptions |= SSL_OP_NO_TLSv1;
- ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
+ if (!setoptions && !clearoptions)
+ return; // Nothing to do
-#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- // Set the callback if we are not allowing renegotiations, unset it if we do
- if (Conf->getBool("renegotiation", true))
- {
- SSL_CTX_set_info_callback(ctx, NULL);
- SSL_CTX_set_info_callback(clictx, NULL);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting %s %s context options, default: %ld set: %ld clear: %ld", name.c_str(), ctxname.c_str(), ctx.GetDefaultContextOptions(), setoptions, clearoptions);
+ long final = context.SetRawContextOptions(setoptions, clearoptions);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "%s %s context options: %ld", name.c_str(), ctxname.c_str(), final);
}
- else
- {
- SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
- SSL_CTX_set_info_callback(clictx, SSLInfoCallback);
- }
-#endif
- if (Conf->getBool("showports", true))
+ public:
+ Profile(const std::string& profilename, ConfigTag* tag)
+ : name(profilename)
+ , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dh.pem")))
+ , ctx(SSL_CTX_new(SSLv23_server_method()))
+ , clictx(SSL_CTX_new(SSLv23_client_method()))
+ , allowrenego(tag->getBool("renegotiation", true))
{
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
-
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
- {
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "openssl")
- continue;
+ if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+ throw Exception("Couldn't set DH parameters");
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
+ std::string hash = tag->getString("hash", "md5");
+ digest = EVP_get_digestbyname(hash.c_str());
+ if (digest == NULL)
+ throw Exception("Unknown hash type " + hash);
- if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+ std::string ciphers = tag->getString("ciphers");
+ if (!ciphers.empty())
+ {
+ if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
{
- /*
- * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
- * the IP:port in ISUPPORT.
- *
- * We used to advertise all ports seperated by a ';' char that matched the above criteria,
- * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
- * To solve this by default we now only display the first IP:port found and let the user
- * configure the exact value for the 005 token, if necessary.
- */
- sslports = portid;
- break;
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
}
}
- }
- }
- void OnModuleRehash(User* user, const std::string ¶m)
- {
- if (param != "ssl")
- return;
-
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- std::string dhfile;
- OnRehash(user);
-
- ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+ std::string curvename = tag->getString("ecdhcurve", "prime256v1");
+ if (!curvename.empty())
+ ctx.SetECDH(curvename);
+#endif
- cafile = conf->getString("cafile", CONFIG_PATH "/ca.pem");
- certfile = conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dhfile = conf->getString("dhfile", CONFIG_PATH "/dhparams.pem");
- std::string hash = conf->getString("hash", "md5");
- if (hash != "sha1" && hash != "md5")
- throw ModuleException("Unknown hash type " + hash);
- use_sha = (hash == "sha1");
+ SetContextOptions("server", tag, ctx);
+ SetContextOptions("client", tag, clictx);
- if (conf->getBool("customcontextoptions"))
- {
- SetContextOptions(ctx, ctx_options, "server", conf);
- SetContextOptions(clictx, clictx_options, "client", conf);
- }
+ /* Load our keys and certificates
+ * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+ */
+ std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
+ if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read certificate file: " + lasterr);
+ }
- std::string ciphers = conf->getString("ciphers", "");
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
+ if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read key file: " + lasterr);
+ }
- if (!ciphers.empty())
- {
- ERR_clear_error();
- if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
+ // Load the CAs we trust
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
+ if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
ERR_print_errors_cb(error_callback, this);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", filename.c_str(), lasterr.c_str());
}
}
- /* Load our keys and certificates
- * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
- */
- ERR_clear_error();
- if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
- }
+ const std::string& GetName() const { return name; }
+ SSL* CreateServerSession() { return ctx.CreateSession(); }
+ SSL* CreateClientSession() { return clictx.CreateSession(); }
+ const EVP_MD* GetDigest() { return digest; }
+ bool AllowRenegotiation() const { return allowrenego; }
+ };
+}
- ERR_clear_error();
- if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
- }
+static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
+{
+ /* XXX: This will allow self signed certificates.
+ * In the future if we want an option to not allow this,
+ * we can just return preverify_ok here, and openssl
+ * will boot off self-signed and invalid peer certs.
+ */
+ int ve = X509_STORE_CTX_get_error(ctx);
- /* Load the CAs we trust*/
- ERR_clear_error();
- if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
- }
+ SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
-#ifdef _WIN32
- BIO* dhpfile = BIO_new_file(dhfile.c_str(), "r");
-#else
- FILE* dhpfile = fopen(dhfile.c_str(), "r");
-#endif
- DH* ret;
+ return 1;
+}
- if (dhpfile == NULL)
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
- throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
- }
+class OpenSSLIOHook : public SSLIOHook
+{
+ private:
+ SSL* sess;
+ issl_status status;
+ const bool outbound;
+ bool data_to_write;
+ reference<OpenSSL::Profile> profile;
+
+ bool Handshake(StreamSocket* user)
+ {
+ int ret;
+
+ ERR_clear_error();
+ if (outbound)
+ ret = SSL_connect(sess);
else
+ ret = SSL_accept(sess);
+
+ if (ret < 0)
{
-#ifdef _WIN32
- ret = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
- BIO_free(dhpfile);
-#else
- ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
-#endif
+ int err = SSL_get_error(sess, ret);
- ERR_clear_error();
- if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
+ if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
- ERR_print_errors_cb(error_callback, this);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ this->status = ISSL_HANDSHAKING;
+ return true;
+ }
+ else if (err == SSL_ERROR_WANT_WRITE)
+ {
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ this->status = ISSL_HANDSHAKING;
+ return true;
+ }
+ else
+ {
+ CloseSession();
}
- DH_free(ret);
- }
-
-#ifndef _WIN32
- fclose(dhpfile);
-#endif
-#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
- SetupECDH(conf);
-#endif
- }
+ return false;
+ }
+ else if (ret > 0)
+ {
+ // Handshake complete.
+ VerifyCertificate();
- void On005Numeric(std::string &output)
- {
- if (!sslports.empty())
- output.append(" SSL=" + sslports);
- }
+ status = ISSL_OPEN;
- ~ModuleSSLOpenSSL()
- {
- SSL_CTX_free(ctx);
- SSL_CTX_free(clictx);
- delete[] sessions;
- }
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
- void OnUserConnect(LocalUser* user)
- {
- if (user->eh.GetIOHook() == this)
+ return true;
+ }
+ else if (ret == 0)
{
- if (sessions[user->eh.GetFd()].sess)
- {
- if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
- }
+ CloseSession();
}
+ return false;
}
- void OnCleanup(int target_type, void* item)
+ void CloseSession()
{
- if (target_type == TYPE_USER)
+ if (sess)
{
- LocalUser* user = IS_LOCAL((User*)item);
-
- if (user && user->eh.GetIOHook() == this)
- {
- // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
- // Potentially there could be multiple SSL modules loaded at once on different ports.
- ServerInstance->Users->QuitUser(user, "SSL module unloading");
- }
+ SSL_shutdown(sess);
+ SSL_free(sess);
}
+ sess = NULL;
+ certificate = NULL;
+ status = ISSL_NONE;
}
- Version GetVersion()
+ void VerifyCertificate()
{
- return Version("Provides SSL support for clients", VF_VENDOR);
- }
+ X509* cert;
+ ssl_cert* certinfo = new ssl_cert;
+ this->certificate = certinfo;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
- void OnRequest(Request& request)
- {
- if (strcmp("GET_SSL_CERT", request.id) == 0)
+ cert = SSL_get_peer_certificate(sess);
+
+ if (!cert)
{
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
+ certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+ return;
+ }
+
+ certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
- req.cert = session->cert;
+ if (!SelfSigned)
+ {
+ certinfo->unknownsigner = false;
+ certinfo->trusted = true;
}
- else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+ else
{
- SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
- if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
- req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+ certinfo->unknownsigner = true;
+ certinfo->trusted = false;
}
- }
-
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- {
- int fd = user->GetFd();
- issl_session* session = &sessions[fd];
+ char buf[512];
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+ certinfo->dn = buf;
+ // Make sure there are no chars in the string that we consider invalid
+ if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
+ certinfo->dn.clear();
- session->sess = SSL_new(ctx);
- session->status = ISSL_NONE;
- session->outbound = false;
- session->data_to_write = false;
+ X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
+ certinfo->issuer = buf;
+ if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
+ certinfo->issuer.clear();
- if (session->sess == NULL)
- return;
+ if (!X509_digest(cert, profile->GetDigest(), md, &n))
+ {
+ certinfo->error = "Out of memory generating fingerprint";
+ }
+ else
+ {
+ certinfo->fingerprint = BinToHex(md, n);
+ }
- if (SSL_set_fd(session->sess, fd) == 0)
+ if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
- return;
+ certinfo->error = "Not activated, or expired certificate";
}
- Handshake(user, session);
+ X509_free(cert);
}
- void OnStreamSocketConnect(StreamSocket* user)
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+ void SSLInfoCallback(int where, int rc)
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
- return;
-
- issl_session* session = &sessions[fd];
-
- session->sess = SSL_new(clictx);
- session->status = ISSL_NONE;
- session->outbound = true;
- session->data_to_write = false;
-
- if (session->sess == NULL)
- return;
-
- if (SSL_set_fd(session->sess, fd) == 0)
+ if ((where & SSL_CB_HANDSHAKE_START) && (status == ISSL_OPEN))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
- return;
+ if (profile->AllowRenegotiation())
+ return;
+
+ // The other side is trying to renegotiate, kill the connection and change status
+ // to ISSL_NONE so CheckRenego() closes the session
+ status = ISSL_NONE;
+ SocketEngine::Shutdown(SSL_get_fd(sess), 2);
}
+ }
+
+ bool CheckRenego(StreamSocket* sock)
+ {
+ if (status != ISSL_NONE)
+ return true;
- Handshake(user, session);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
+ CloseSession();
+ sock->SetError("Renegotiation is not allowed");
+ return false;
}
+#endif
+
+ // Calls our private SSLInfoCallback()
+ friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
- void OnStreamSocketClose(StreamSocket* user)
+ public:
+ OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool is_outbound, SSL* session, const reference<OpenSSL::Profile>& sslprofile)
+ : SSLIOHook(hookprov)
+ , sess(session)
+ , status(ISSL_NONE)
+ , outbound(is_outbound)
+ , data_to_write(false)
+ , profile(sslprofile)
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
+ if (sess == NULL)
return;
+ if (SSL_set_fd(sess, sock->GetFd()) == 0)
+ throw ModuleException("Can't set fd with SSL_set_fd: " + ConvToStr(sock->GetFd()));
- CloseSession(&sessions[fd]);
+ SSL_set_ex_data(sess, exdataindex, this);
+ sock->AddIOHook(this);
+ Handshake(sock);
}
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
{
- int fd = user->GetFd();
- /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
- if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
- return -1;
-
- issl_session* session = &sessions[fd];
+ CloseSession();
+ }
- if (!session->sess)
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+ {
+ if (!sess)
{
- CloseSession(session);
+ CloseSession();
return -1;
}
- if (session->status == ISSL_HANDSHAKING)
+ if (status == ISSL_HANDSHAKING)
{
// The handshake isn't finished and it wants to read, try to finish it.
- if (!Handshake(user, session))
+ if (!Handshake(user))
{
// Couldn't resume handshake.
- if (session->status == ISSL_NONE)
+ if (status == ISSL_NONE)
return -1;
return 0;
}
}
- // If we resumed the handshake then session->status will be ISSL_OPEN
+ // If we resumed the handshake then this->status will be ISSL_OPEN
- if (session->status == ISSL_OPEN)
+ if (status == ISSL_OPEN)
{
ERR_clear_error();
char* buffer = ServerInstance->GetReadBuffer();
size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = SSL_read(session->sess, buffer, bufsiz);
+ int ret = SSL_read(sess, buffer, bufsiz);
#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- if (!CheckRenego(user, session))
+ if (!CheckRenego(user))
return -1;
#endif
if (ret > 0)
{
recvq.append(buffer, ret);
- if (session->data_to_write)
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+ if (data_to_write)
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
return 1;
}
else if (ret == 0)
{
// Client closed connection.
- CloseSession(session);
+ CloseSession();
user->SetError("Connection closed");
return -1;
}
else if (ret < 0)
{
- int err = SSL_get_error(session->sess, ret);
+ int err = SSL_get_error(sess, ret);
if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
return 0;
}
else if (err == SSL_ERROR_WANT_WRITE)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
return 0;
}
else
{
- CloseSession(session);
+ CloseSession();
return -1;
}
}
return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
+ int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE
{
- int fd = user->GetFd();
-
- issl_session* session = &sessions[fd];
-
- if (!session->sess)
+ if (!sess)
{
- CloseSession(session);
+ CloseSession();
return -1;
}
- session->data_to_write = true;
+ data_to_write = true;
- if (session->status == ISSL_HANDSHAKING)
+ if (status == ISSL_HANDSHAKING)
{
- if (!Handshake(user, session))
+ if (!Handshake(user))
{
// Couldn't resume handshake.
- if (session->status == ISSL_NONE)
+ if (status == ISSL_NONE)
return -1;
return 0;
}
}
- if (session->status == ISSL_OPEN)
+ if (status == ISSL_OPEN)
{
ERR_clear_error();
- int ret = SSL_write(session->sess, buffer.data(), buffer.size());
+ int ret = SSL_write(sess, buffer.data(), buffer.size());
#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
- if (!CheckRenego(user, session))
+ if (!CheckRenego(user))
return -1;
#endif
if (ret == (int)buffer.length())
{
- session->data_to_write = false;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ data_to_write = false;
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
return 1;
}
else if (ret > 0)
{
buffer = buffer.substr(ret);
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else if (ret == 0)
{
- CloseSession(session);
+ CloseSession();
return -1;
}
else if (ret < 0)
{
- int err = SSL_get_error(session->sess, ret);
+ int err = SSL_get_error(sess, ret);
if (err == SSL_ERROR_WANT_WRITE)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
return 0;
}
else if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
+ SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
return 0;
}
else
{
- CloseSession(session);
+ CloseSession();
return -1;
}
}
return 0;
}
- bool Handshake(StreamSocket* user, issl_session* session)
+ void TellCiphersAndFingerprint(LocalUser* user)
{
- int ret;
+ if (sess)
+ {
+ std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(sess)) + "'";
+ const std::string& fingerprint = certificate->fingerprint;
+ if (!fingerprint.empty())
+ text += " and your SSL certificate fingerprint is " + fingerprint;
- ERR_clear_error();
- if (session->outbound)
- ret = SSL_connect(session->sess);
- else
- ret = SSL_accept(session->sess);
+ user->WriteNotice(text);
+ }
+ }
+};
- if (ret < 0)
+static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
+{
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+ OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
+ hook->SSLInfoCallback(where, rc);
+#endif
+}
+
+class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
+{
+ reference<OpenSSL::Profile> profile;
+
+ public:
+ OpenSSLIOHookProvider(Module* mod, reference<OpenSSL::Profile>& prof)
+ : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
+ , profile(prof)
+ {
+ ServerInstance->Modules->AddService(*this);
+ }
+
+ ~OpenSSLIOHookProvider()
+ {
+ ServerInstance->Modules->DelService(*this);
+ }
+
+ void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ new OpenSSLIOHook(this, sock, false, profile->CreateServerSession(), profile);
+ }
+
+ void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ new OpenSSLIOHook(this, sock, true, profile->CreateClientSession(), profile);
+ }
+};
+
+class ModuleSSLOpenSSL : public Module
+{
+ typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList;
+
+ ProfileList profiles;
+
+ void ReadProfiles()
+ {
+ ProfileList newprofiles;
+ ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
+ if (tags.first == tags.second)
{
- int err = SSL_get_error(session->sess, ret);
+ // Create a default profile named "openssl"
+ const std::string defname = "openssl";
+ ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found, using settings from the <openssl> tag");
- if (err == SSL_ERROR_WANT_READ)
+ try
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
+ reference<OpenSSL::Profile> profile(new OpenSSL::Profile(defname, tag));
+ newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
}
- else if (err == SSL_ERROR_WANT_WRITE)
+ catch (OpenSSL::Exception& ex)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
- }
- else
- {
- CloseSession(session);
+ throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
}
-
- return false;
}
- else if (ret > 0)
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- // Handshake complete.
- VerifyCertificate(session, user);
+ ConfigTag* tag = i->second;
+ if (tag->getString("provider") != "openssl")
+ continue;
- session->status = ISSL_OPEN;
+ std::string name = tag->getString("name");
+ if (name.empty())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
+ continue;
+ }
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+ reference<OpenSSL::Profile> profile;
+ try
+ {
+ profile = new OpenSSL::Profile(name, tag);
+ }
+ catch (CoreException& ex)
+ {
+ throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
+ }
- return true;
- }
- else if (ret == 0)
- {
- CloseSession(session);
+ newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
}
- return false;
+
+ profiles.swap(newprofiles);
}
- void CloseSession(issl_session* session)
+ public:
+ ModuleSSLOpenSSL()
{
- if (session->sess)
- {
- SSL_shutdown(session->sess);
- SSL_free(session->sess);
- }
-
- session->sess = NULL;
- session->status = ISSL_NONE;
- session->cert = NULL;
+ // Initialize OpenSSL
+ SSL_library_init();
+ SSL_load_error_strings();
}
- void VerifyCertificate(issl_session* session, StreamSocket* user)
+ void init() CXX11_OVERRIDE
{
- if (!session->sess || !user)
- return;
+ // Register application specific data
+ char exdatastr[] = "inspircd";
+ exdataindex = SSL_get_ex_new_index(0, exdatastr, NULL, NULL, NULL);
+ if (exdataindex < 0)
+ throw ModuleException("Failed to register application specific data");
- X509* cert;
- ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
- unsigned int n;
- unsigned char md[EVP_MAX_MD_SIZE];
- const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
-
- cert = SSL_get_peer_certificate((SSL*)session->sess);
+ ReadProfiles();
+ }
- if (!cert)
- {
- certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+ void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE
+ {
+ if (param != "ssl")
return;
- }
-
- certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
- if (!SelfSigned)
+ try
{
- certinfo->unknownsigner = false;
- certinfo->trusted = true;
+ ReadProfiles();
}
- else
+ catch (ModuleException& ex)
{
- certinfo->unknownsigner = true;
- certinfo->trusted = false;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
}
+ }
- char buf[512];
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
- certinfo->dn = buf;
- // Make sure there are no chars in the string that we consider invalid
- if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
- certinfo->dn.clear();
-
- X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
- certinfo->issuer = buf;
- if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
- certinfo->issuer.clear();
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ IOHook* hook = user->eh.GetIOHook();
+ if (hook && hook->prov->creator == this)
+ static_cast<OpenSSLIOHook*>(hook)->TellCiphersAndFingerprint(user);
+ }
- if (!X509_digest(cert, digest, md, &n))
- {
- certinfo->error = "Out of memory generating fingerprint";
- }
- else
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if (target_type == TYPE_USER)
{
- certinfo->fingerprint = irc::hex(md, n);
- }
+ LocalUser* user = IS_LOCAL((User*)item);
- if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
- {
- certinfo->error = "Not activated, or expired certificate";
+ if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == this)
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
}
+ }
- X509_free(cert);
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
};
-static int error_callback(const char *str, size_t len, void *u)
-{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
-
- //
- // XXX: Remove this line, it causes valgrind warnings...
- //
- // MD_update(&m, buf, j);
- //
- //
- // ... ONLY JOKING! :-)
- //
-
- return 0;
-}
-
MODULE_INIT(ModuleSSLOpenSSL)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef HASH_H
-#define HASH_H
-
-#include "modules.h"
-
-class HashProvider : public DataProvider
-{
- public:
- const unsigned int out_size;
- const unsigned int block_size;
- HashProvider(Module* mod, const std::string& Name, int osiz, int bsiz)
- : DataProvider(mod, Name), out_size(osiz), block_size(bsiz) {}
- virtual std::string sum(const std::string& data) = 0;
- inline std::string hexsum(const std::string& data)
- {
- return BinToHex(sum(data));
- }
-
- inline std::string b64sum(const std::string& data)
- {
- return BinToBase64(sum(data), NULL, 0);
- }
-
- /** Allows the IVs for the hash to be specified. As the choice of initial IV is
- * important for the security of a hash, this should not be used except to
- * maintain backwards compatability. This also allows you to change the hex
- * sequence from its default of "0123456789abcdef", which does not improve the
- * strength of the output, but helps confuse those attempting to implement it.
- *
- * Example:
- * \code
- * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC };
- * std::string result = Hash.sumIV(iv, "fedcba9876543210", "data");
- * \endcode
- */
- virtual std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata) = 0;
-
- /** HMAC algorithm, RFC 2104 */
- std::string hmac(const std::string& key, const std::string& msg)
- {
- std::string hmac1, hmac2;
- std::string kbuf = key.length() > block_size ? sum(key) : key;
- kbuf.resize(block_size);
-
- for (size_t n = 0; n < block_size; n++)
- {
- hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C));
- hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36));
- }
- hmac2.append(msg);
- hmac1.append(sum(hmac2));
- return sum(hmac1);
- }
-};
-
-#endif
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "base.h"
-
-#ifndef HTTPD_H
-#define HTTPD_H
-
-#include <string>
-#include <sstream>
-#include <map>
-
-/** A modifyable list of HTTP header fields
- */
-class HTTPHeaders
-{
- protected:
- std::map<std::string,std::string> headers;
- public:
-
- /** Set the value of a header
- * Sets the value of the named header. If the header is already present, it will be replaced
- */
- void SetHeader(const std::string &name, const std::string &data)
- {
- headers[name] = data;
- }
-
- /** Set the value of a header, only if it doesn't exist already
- * Sets the value of the named header. If the header is already present, it will NOT be updated
- */
- void CreateHeader(const std::string &name, const std::string &data)
- {
- if (!IsSet(name))
- SetHeader(name, data);
- }
-
- /** Remove the named header
- */
- void RemoveHeader(const std::string &name)
- {
- headers.erase(name);
- }
-
- /** Remove all headers
- */
- void Clear()
- {
- headers.clear();
- }
-
- /** Get the value of a header
- * @return The value of the header, or an empty string
- */
- std::string GetHeader(const std::string &name)
- {
- std::map<std::string,std::string>::iterator it = headers.find(name);
- if (it == headers.end())
- return std::string();
-
- return it->second;
- }
-
- /** Check if the given header is specified
- * @return true if the header is specified
- */
- bool IsSet(const std::string &name)
- {
- std::map<std::string,std::string>::iterator it = headers.find(name);
- return (it != headers.end());
- }
-
- /** Get all headers, formatted by the HTTP protocol
- * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end
- */
- std::string GetFormattedHeaders()
- {
- std::string re;
-
- for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++)
- re += i->first + ": " + i->second + "\r\n";
-
- return re;
- }
-};
-
-class HttpServerSocket;
-
-/** This class represents a HTTP request.
- */
-class HTTPRequest : public Event
-{
- protected:
- std::string type;
- std::string document;
- std::string ipaddr;
- std::string postdata;
-
- public:
-
- HTTPHeaders *headers;
- int errorcode;
-
- /** A socket pointer, which you must return in your HTTPDocument class
- * if you reply to this request.
- */
- HttpServerSocket* sock;
-
- /** Initialize HTTPRequest.
- * This constructor is called by m_httpd.so to initialize the class.
- * @param request_type The request type, e.g. GET, POST, HEAD
- * @param uri The URI, e.g. /page
- * @param hdr The headers sent with the request
- * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
- * @param ip The IP address making the web request.
- * @param pdata The post data (content after headers) received with the request, up to Content-Length in size
- */
- HTTPRequest(Module* me, const std::string &eventid, const std::string &request_type, const std::string &uri,
- HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata)
- : Event(me, eventid), type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket)
- {
- }
-
- /** Get the post data (request content).
- * All post data will be returned, including carriage returns and linefeeds.
- * @return The postdata
- */
- std::string& GetPostData()
- {
- return postdata;
- }
-
- /** Get the request type.
- * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
- * @return The request type, e.g. GET, POST, HEAD
- */
- std::string& GetType()
- {
- return type;
- }
-
- /** Get URI.
- * The URI string (URL minus hostname and scheme) will be provided by this function.
- * @return The URI being requested
- */
- std::string& GetURI()
- {
- return document;
- }
-
- /** Get IP address of requester.
- * The requesting system's ip address will be returned.
- * @return The IP address as a string
- */
- std::string& GetIP()
- {
- return ipaddr;
- }
-};
-
-/** You must return a HTTPDocument to the httpd module by using the Request class.
- * When you initialize this class you may initialize it with all components required to
- * form a valid HTTP response, including document data, headers, and a response code.
- */
-class HTTPDocumentResponse : public Request
-{
- public:
- std::stringstream* document;
- int responsecode;
- HTTPHeaders headers;
- HTTPRequest& src;
-
- /** Initialize a HTTPRequest ready for sending to m_httpd.so.
- * @param opaque The socket pointer you obtained from the HTTPRequest at an earlier time
- * @param doc A stringstream containing the document body
- * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
- * based upon the response code.
- * @param extra Any extra headers to include with the defaults, seperated by carriage return and linefeed.
- */
- HTTPDocumentResponse(Module* me, HTTPRequest& req, std::stringstream* doc, int response)
- : Request(me, req.source, "HTTP-DOC"), document(doc), responsecode(response), src(req)
- {
- }
-};
-
-#endif
-
#include "inspircd.h"
-/* $ModDesc: Provides the ability to abbreviate commands a-la BBC BASIC keywords. */
-
class ModuleAbbreviation : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnPreCommand, this);
- }
-
void Prioritize()
{
ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to abbreviate commands a-la BBC BASIC keywords.",VF_VENDOR);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
/* Command is already validated, has a length of 0, or last character is not a . */
if (validated || command.empty() || *command.rbegin() != '.')
return MOD_RES_PASSTHRU;
- /* Whack the . off the end */
- command.erase(command.end() - 1);
-
/* Look for any command that starts with the same characters, if it does, replace the command string with it */
- size_t clen = command.length();
+ size_t clen = command.length() - 1;
std::string foundcommand, matchlist;
bool foundmatch = false;
- for (Commandtable::iterator n = ServerInstance->Parser->cmdlist.begin(); n != ServerInstance->Parser->cmdlist.end(); ++n)
+ const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
+ for (CommandParser::CommandMap::const_iterator n = commands.begin(); n != commands.end(); ++n)
{
- if (n->first.length() < clen)
- continue;
-
- if (command == n->first.substr(0, clen))
+ if (!command.compare(0, clen, n->first, 0, clen))
{
if (matchlist.length() > 450)
{
- user->WriteNumeric(420, "%s :Ambiguous abbreviation and too many possible matches.", user->nick.c_str());
+ user->WriteNumeric(420, ":Ambiguous abbreviation and too many possible matches.");
return MOD_RES_DENY;
}
/* Ambiguous command, list the matches */
if (!matchlist.empty())
{
- user->WriteNumeric(420, "%s :Ambiguous abbreviation, posssible matches: %s%s", user->nick.c_str(), foundcommand.c_str(), matchlist.c_str());
+ user->WriteNumeric(420, ":Ambiguous abbreviation, posssible matches: %s%s", foundcommand.c_str(), matchlist.c_str());
return MOD_RES_DENY;
}
- if (foundcommand.empty())
- {
- /* No match, we have to put the . back again so that the invalid command numeric looks correct. */
- command += '.';
- }
- else
+ if (!foundcommand.empty())
{
command = foundcommand;
}
#include "inspircd.h"
-/* $ModDesc: Provides aliases of commands. */
-
/** An alias definition
*/
class Alias
{
public:
/** The text of the alias command */
- irc::string AliasedCommand;
+ std::string AliasedCommand;
/** Text to replace with */
std::string ReplaceFormat;
class ModuleAlias : public Module
{
- private:
-
char fprefix;
/* We cant use a map, there may be multiple aliases with the same name.
* We can, however, use a fancy invention: the multimap. Maps a key to one or more values.
* -- w00t
- */
- std::multimap<irc::string, Alias> Aliases;
+ */
+ typedef std::multimap<std::string, Alias, irc::insensitive_swo> AliasMap;
+
+ AliasMap Aliases;
/* whether or not +B users are allowed to use fantasy commands */
bool AllowBots;
+ UserModeReference botmode;
- virtual void ReadAliases()
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* fantasy = ServerInstance->Config->ConfValue("fantasy");
AllowBots = fantasy->getBool("allowbots", false);
{
ConfigTag* tag = i->second;
Alias a;
- std::string aliastext = tag->getString("text");
- a.AliasedCommand = aliastext.c_str();
+ a.AliasedCommand = tag->getString("text");
+ std::transform(a.AliasedCommand.begin(), a.AliasedCommand.end(), a.AliasedCommand.begin(), ::toupper);
tag->readString("replace", a.ReplaceFormat, true);
a.RequiredNick = tag->getString("requires");
a.ULineOnly = tag->getBool("uline");
}
}
- public:
-
- void init()
+ ModuleAlias()
+ : botmode(this, "bot")
{
- ReadAliases();
- Implementation eventlist[] = { I_OnPreCommand, I_OnRehash, I_OnUserMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleAlias()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides aliases of commands.", VF_VENDOR);
}
return word;
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
- std::multimap<irc::string, Alias>::iterator i, upperbound;
-
/* If theyre not registered yet, we dont want
* to know.
*/
return MOD_RES_PASSTHRU;
/* We dont have any commands looking like this? Stop processing. */
- i = Aliases.find(command.c_str());
- if (i == Aliases.end())
+ std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(command);
+ if (iters.first == iters.second)
return MOD_RES_PASSTHRU;
- /* Avoid iterating on to different aliases if no patterns match. */
- upperbound = Aliases.upper_bound(command.c_str());
- irc::string c = command.c_str();
/* The parameters for the command in their original form, with the command stripped off */
std::string compare = original_line.substr(command.length());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
- while (i != upperbound)
+ for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
{
if (i->second.UserCommand)
{
return MOD_RES_DENY;
}
}
-
- i++;
}
// If we made it here, no aliases actually matched.
return MOD_RES_PASSTHRU;
}
- virtual void OnUserMessage(User *user, void *dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+ void OnUserMessage(User *user, void *dest, int target_type, const std::string &text, char status, const CUList &exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
- if (target_type != TYPE_CHANNEL)
+ if ((target_type != TYPE_CHANNEL) || (msgtype != MSG_PRIVMSG))
{
return;
}
// fcommands are only for local users. Spanningtree will send them back out as their original cmd.
- if (!user || !IS_LOCAL(user))
+ if (!IS_LOCAL(user))
{
return;
}
/* Stop here if the user is +B and allowbot is set to no. */
- if (!AllowBots && user->IsModeSet('B'))
+ if (!AllowBots && user->IsModeSet(botmode))
{
return;
}
// text is like "!moo cows bite me", we want "!moo" first
irc::spacesepstream ss(text);
ss.GetToken(scommand);
- irc::string fcommand = scommand.c_str();
- if (fcommand.empty())
+ if (scommand.empty())
{
return; // wtfbbq
}
// we don't want to touch non-fantasy stuff
- if (*fcommand.c_str() != fprefix)
+ if (*scommand.c_str() != fprefix)
{
return;
}
// nor do we give a shit about the prefix
- fcommand.erase(fcommand.begin());
+ scommand.erase(scommand.begin());
- std::multimap<irc::string, Alias>::iterator i = Aliases.find(fcommand);
-
- if (i == Aliases.end())
+ std::pair<AliasMap::iterator, AliasMap::iterator> iters = Aliases.equal_range(scommand);
+ if (iters.first == iters.second)
return;
- /* Avoid iterating on to other aliases if no patterns match */
- std::multimap<irc::string, Alias>::iterator upperbound = Aliases.upper_bound(fcommand);
-
-
/* The parameters for the command in their original form, with the command stripped off */
- std::string compare = text.substr(fcommand.length() + 1);
+ std::string compare = text.substr(scommand.length() + 1);
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
- while (i != upperbound)
+ for (AliasMap::iterator i = iters.first; i != iters.second; ++i)
{
if (i->second.ChannelCommand)
{
if (DoAlias(user, c, &(i->second), compare, text.substr(1)))
return;
}
-
- i++;
}
}
int DoAlias(User *user, Channel *c, Alias *a, const std::string& compare, const std::string& safe)
{
- User *u = NULL;
-
/* Does it match the pattern? */
if (!a->format.empty())
{
}
}
- if ((a->OperOnly) && (!IS_OPER(user)))
+ if ((a->OperOnly) && (!user->IsOper()))
return 0;
if (!a->RequiredNick.empty())
{
- u = ServerInstance->FindNick(a->RequiredNick);
+ User* u = ServerInstance->FindNick(a->RequiredNick);
if (!u)
{
- user->WriteNumeric(401, ""+user->nick+" "+a->RequiredNick+" :is currently unavailable. Please try again later.");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick + " :is currently unavailable. Please try again later.");
return 1;
}
- }
- if ((u != NULL) && (!a->RequiredNick.empty()) && (a->ULineOnly))
- {
- if (!ServerInstance->ULine(u->server))
+
+ if ((a->ULineOnly) && (!u->server->IsULine()))
{
- ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+std::string(a->AliasedCommand.c_str())+" is not on a u-lined server, possibly underhanded antics detected!");
- user->WriteNumeric(401, ""+user->nick+" "+a->RequiredNick+" :is an imposter! Please inform an IRC operator as soon as possible.");
+ ServerInstance->SNO->WriteToSnoMask('a', "NOTICE -- Service "+a->RequiredNick+" required by alias "+a->AliasedCommand+" is not on a u-lined server, possibly underhanded antics detected!");
+ user->WriteNumeric(ERR_NOSUCHNICK, a->RequiredNick + " :is an imposter! Please inform an IRC operator as soon as possible.");
return 1;
}
}
void DoCommand(const std::string& newline, User* user, Channel *chan, const std::string &original_line)
{
std::string result;
- result.reserve(MAXBUF);
+ result.reserve(newline.length());
for (unsigned int i = 0; i < newline.length(); i++)
{
char c = newline[i];
result.append(GetVar(var, original_line));
i += len - 1;
}
- else if (newline.substr(i, 5) == "$nick")
+ else if (!newline.compare(i, 5, "$nick", 5))
{
result.append(user->nick);
i += 4;
}
- else if (newline.substr(i, 5) == "$host")
+ else if (!newline.compare(i, 5, "$host", 5))
{
result.append(user->host);
i += 4;
}
- else if (newline.substr(i, 5) == "$chan")
+ else if (!newline.compare(i, 5, "$chan", 5))
{
if (chan)
result.append(chan->name);
i += 4;
}
- else if (newline.substr(i, 6) == "$ident")
+ else if (!newline.compare(i, 6, "$ident", 6))
{
result.append(user->ident);
i += 5;
}
- else if (newline.substr(i, 6) == "$vhost")
+ else if (!newline.compare(i, 6, "$vhost", 6))
{
result.append(user->dhost);
i += 5;
std::string command, token;
ss.GetToken(command);
- while (ss.GetToken(token) && (pars.size() <= MAXPARAMETERS))
+ while (ss.GetToken(token))
{
pars.push_back(token);
}
- ServerInstance->Parser->CallHandler(command, pars, user);
+ ServerInstance->Parser.CallHandler(command, pars, user);
}
- virtual void OnRehash(User* user)
- {
- ReadAliases();
- }
-
- virtual void Prioritize()
+ void Prioritize()
{
// Prioritise after spanningtree so that channel aliases show the alias before the effects.
Module* linkmod = ServerInstance->Modules->Find("m_spanningtree.so");
#include "inspircd.h"
-/* $ModDesc: Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it */
-
class AllowInvite : public SimpleChannelModeHandler
{
public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(ni);
- Implementation eventlist[] = { I_OnUserPreInvite, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('A');
+ tokens["EXTBAN"].push_back('A');
}
- virtual ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout)
+ ModResult OnUserPreInvite(User* user,User* dest,Channel* channel, time_t timeout) CXX11_OVERRIDE
{
if (IS_LOCAL(user))
{
if (res == MOD_RES_DENY)
{
// Matching extban, explicitly deny /invite
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You are banned from using INVITE", user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You are banned from using INVITE", channel->name.c_str());
return res;
}
- if (channel->IsModeSet('A') || res == MOD_RES_ALLOW)
+ if (channel->IsModeSet(ni) || res == MOD_RES_ALLOW)
{
// Explicitly allow /invite
return MOD_RES_ALLOW;
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleAllowInvite()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +A, allowing /invite freely on a channel and extban A to deny specific users it",VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Display timestamps from all servers connected to the network */
-
class CommandAlltime : public Command
{
public:
CommandAlltime(Module* Creator) : Command(Creator, "ALLTIME", 0)
{
flags_needed = 'o';
- translation.push_back(TR_END);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
{
- char fmtdate[64];
- time_t now = ServerInstance->Time();
- strftime(fmtdate, sizeof(fmtdate), "%Y-%m-%d %H:%M:%S", gmtime(&now));
+ const std::string fmtdate = InspIRCd::TimeString(ServerInstance->Time(), "%Y-%m-%d %H:%M:%S", true);
std::string msg = ":" + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :System time is " + fmtdate + " (" + ConvToStr(ServerInstance->Time()) + ") on " + ServerInstance->Config->ServerName;
}
};
-
class Modulealltime : public Module
{
CommandAlltime mycommand;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mycommand);
- }
-
- virtual ~Modulealltime()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Display timestamps from all servers connected to the network", VF_OPTCOMMON | VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
-
-class AuditoriumMode : public ModeHandler
+class AuditoriumMode : public SimpleChannelModeHandler
{
public:
- AuditoriumMode(Module* Creator) : ModeHandler(Creator, "auditorium", 'u', PARAM_NONE, MODETYPE_CHANNEL)
+ AuditoriumMode(Module* Creator) : SimpleChannelModeHandler(Creator, "auditorium", 'u')
{
levelrequired = OP_VALUE;
}
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- if (channel->IsModeSet(this) == adding)
- return MODEACTION_DENY;
- channel->SetMode(this, adding);
- return MODEACTION_ALLOW;
- }
};
class ModuleAuditorium : public Module
{
- private:
AuditoriumMode aum;
bool OpsVisible;
bool OpsCanSee;
bool OperCanSee;
+
public:
ModuleAuditorium() : aum(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(aum);
-
- OnRehash(NULL);
-
- Implementation eventlist[] = {
- I_OnUserJoin, I_OnUserPart, I_OnUserKick,
- I_OnBuildNeighborList, I_OnNamesListItem, I_OnSendWhoLine,
- I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleAuditorium()
- {
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("auditorium");
OpsVisible = tag->getBool("opvisible");
OperCanSee = tag->getBool("opercansee", true);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list", VF_VENDOR);
}
return false;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- // Some module already hid this from being displayed, don't bother
- if (nick.empty())
- return;
-
if (IsVisible(memb))
- return;
+ return MOD_RES_PASSTHRU;
if (CanSee(issuer, memb))
- return;
+ return MOD_RES_PASSTHRU;
- nick.clear();
+ // Don't display this user in the NAMES list
+ return MOD_RES_DENY;
}
/** Build CUList for showing this join/part/kick */
if (IsVisible(memb))
return;
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (IS_LOCAL(i->first) && !CanSee(i->first, memb))
excepts.insert(i->first);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
}
- void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
}
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
}
- void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
{
- UCListIter i = include.begin();
- while (i != include.end())
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
{
- Channel* c = *i++;
- Membership* memb = c->GetUser(source);
- if (!memb || IsVisible(memb))
+ Membership* memb = *i;
+ if (IsVisible(memb))
+ {
+ ++i;
continue;
+ }
+
// this channel should not be considered when listing my neighbors
- include.erase(c);
+ i = include.erase(i);
// however, that might hide me from ops that can see me...
- const UserMembList* users = c->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); j++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for(Channel::MemberMap::const_iterator j = users.begin(); j != users.end(); ++j)
{
if (IS_LOCAL(j->first) && CanSee(j->first, memb))
exception[j->first] = true;
}
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
{
- Channel* channel = ServerInstance->FindChan(params[0]);
- if (!channel)
+ if (!memb)
return;
- Membership* memb = channel->GetUser(user);
- if ((!memb) || (IsVisible(memb)))
+ if (IsVisible(memb))
return;
if (CanSee(source, memb))
return;
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +w channel mode, autoop list */
+#include "listmode.h"
/** Handles +w channel mode
*/
tidy = false;
}
- ModeHandler* FindMode(const std::string& mid)
+ PrefixMode* FindMode(const std::string& mid)
{
if (mid.length() == 1)
- return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->name == mid)
- return mh;
- }
- return NULL;
+ return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+ return mh ? mh->IsPrefixMode() : NULL;
}
ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding)
return adding ? MOD_RES_DENY : MOD_RES_PASSTHRU;
unsigned int mylevel = channel->GetPrefixValue(source);
std::string mid = parameter.substr(0, pos);
- ModeHandler* mh = FindMode(mid);
+ PrefixMode* mh = FindMode(mid);
- if (adding && (!mh || !mh->GetPrefixRank()))
+ if (adding && !mh)
{
- source->WriteNumeric(415, "%s %s :Cannot find prefix mode '%s' for autoop",
- source->nick.c_str(), mid.c_str(), mid.c_str());
+ source->WriteNumeric(415, "%s :Cannot find prefix mode '%s' for autoop",
+ mid.c_str(), mid.c_str());
return MOD_RES_DENY;
}
else if (!mh)
return MOD_RES_DENY;
if (mh->GetLevelRequired() > mylevel)
{
- source->WriteNumeric(482, "%s %s :You must be able to set mode '%s' to include it in an autoop",
- source->nick.c_str(), channel->name.c_str(), mid.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be able to set mode '%s' to include it in an autoop",
+ channel->name.c_str(), mid.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
{
AutoOpList mh;
-public:
+ public:
ModuleAutoOp() : mh(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mh);
- mh.DoImplements(this);
-
- Implementation list[] = { I_OnPostJoin, };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
- }
-
- void OnPostJoin(Membership *memb)
+ void OnPostJoin(Membership *memb) CXX11_OVERRIDE
{
if (!IS_LOCAL(memb->user))
return;
- modelist* list = mh.extItem.get(memb->chan);
+ ListModeBase::ModeList* list = mh.GetList(memb->chan);
if (list)
{
- std::string modeline("+");
- std::vector<std::string> modechange;
- modechange.push_back(memb->chan->name);
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ Modes::ChangeList changelist;
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
std::string::size_type colon = it->mask.find(':');
if (colon == std::string::npos)
continue;
if (memb->chan->CheckBan(memb->user, it->mask.substr(colon+1)))
{
- ModeHandler* given = mh.FindMode(it->mask.substr(0, colon));
- if (given && given->GetPrefixRank())
- modeline.push_back(given->GetModeChar());
+ PrefixMode* given = mh.FindMode(it->mask.substr(0, colon));
+ if (given)
+ changelist.push_add(given, memb->user->nick);
}
}
- modechange.push_back(modeline);
- for(std::string::size_type i = modeline.length(); i > 1; --i) // we use "i > 1" instead of "i" so we skip the +
- modechange.push_back(memb->user->nick);
- if(modechange.size() >= 3)
- ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
}
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- mh.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
mh.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +w channel mode", VF_VENDOR);
}
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +e channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
/* Written by Om<om@inspircd.org>, April 2005. */
/* Rewritten to use the listmode utility by Om, December 2005 */
{
BanException be;
-public:
+ public:
ModuleBanException() : be(this)
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(be);
-
- be.DoImplements(this);
- Implementation list[] = { I_OnRehash, I_On005Numeric, I_OnExtBanCheck, I_OnCheckChannelBan };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
+ tokens["EXCEPTS"] = "e";
}
- void On005Numeric(std::string &output)
+ ModResult OnExtBanCheck(User *user, Channel *chan, char type) CXX11_OVERRIDE
{
- output.append(" EXCEPTS=e");
- }
+ ListModeBase::ModeList* list = be.GetList(chan);
+ if (!list)
+ return MOD_RES_PASSTHRU;
- ModResult OnExtBanCheck(User *user, Channel *chan, char type)
- {
- if (chan != NULL)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
- modelist *list = be.extItem.get(chan);
-
- if (!list)
- return MOD_RES_PASSTHRU;
+ if (it->mask[0] != type || it->mask[1] != ':')
+ continue;
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ if (chan->CheckBan(user, it->mask.substr(2)))
{
- if (it->mask[0] != type || it->mask[1] != ':')
- continue;
-
- if (chan->CheckBan(user, it->mask.substr(2)))
- {
- // They match an entry on the list, so let them pass this.
- return MOD_RES_ALLOW;
- }
+ // They match an entry on the list, so let them pass this.
+ return MOD_RES_ALLOW;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckChannelBan(User* user, Channel* chan)
+ ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
{
- if (chan)
+ ListModeBase::ModeList* list = be.GetList(chan);
+ if (!list)
{
- modelist *list = be.extItem.get(chan);
-
- if (!list)
- {
- // No list, proceed normally
- return MOD_RES_PASSTHRU;
- }
+ // No list, proceed normally
+ return MOD_RES_PASSTHRU;
+ }
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
+ {
+ if (chan->CheckBan(user, it->mask))
{
- if (chan->CheckBan(user, it->mask))
- {
- // They match an entry on the list, so let them in.
- return MOD_RES_ALLOW;
- }
+ // They match an entry on the list, so let them in.
+ return MOD_RES_ALLOW;
}
}
return MOD_RES_PASSTHRU;
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- be.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
be.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +e channel mode", VF_VENDOR);
}
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
+#include "listmode.h"
/* Originally written by Om, January 2009
*/
};
typedef std::vector<BanRedirectEntry> BanRedirectList;
-typedef std::deque<std::string> StringDeque;
class BanRedirect : public ModeWatcher
{
+ ChanModeReference ban;
public:
SimpleExtItem<BanRedirectList> extItem;
- BanRedirect(Module* parent) : ModeWatcher(parent, 'b', MODETYPE_CHANNEL),
- extItem("banredirect", parent)
+ BanRedirect(Module* parent)
+ : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
+ , ban(parent, "ban")
+ , extItem("banredirect", parent)
{
}
- bool BeforeMode(User* source, User* dest, Channel* channel, std::string ¶m, bool adding, ModeType type)
+ bool BeforeMode(User* source, User* dest, Channel* channel, std::string ¶m, bool adding)
{
/* nick!ident@host -> nick!ident@host
* nick!ident@host#chan -> nick!ident@host#chan
* nick#chan -> nick!*@*#chan
*/
- if(channel && (type == MODETYPE_CHANNEL) && param.length())
+ if ((channel) && !param.empty())
{
BanRedirectList* redirects;
std::string mask[4];
enum { NICK, IDENT, HOST, CHAN } current = NICK;
std::string::iterator start_pos = param.begin();
- long maxbans = channel->GetMaxBans();
if (param.length() >= 2 && param[1] == ':')
return true;
if (param.find('#') == std::string::npos)
return true;
- if(adding && (channel->bans.size() > static_cast<unsigned>(maxbans)))
+ ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
+ unsigned int maxbans = banlm->GetLimit(channel);
+ ListModeBase::ModeList* list = banlm->GetList(channel);
+ if ((list) && (adding) && (maxbans <= list->size()))
{
- source->WriteNumeric(478, "%s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)", source->nick.c_str(), channel->name.c_str(), channel->name.c_str(), maxbans);
+ source->WriteNumeric(ERR_BANLISTFULL, "%s :Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), channel->name.c_str(), maxbans);
return false;
}
switch(*curr)
{
case '!':
+ if (current != NICK)
+ break;
mask[current].assign(start_pos, curr);
current = IDENT;
start_pos = curr+1;
break;
case '@':
+ if (current != IDENT)
+ break;
mask[current].assign(start_pos, curr);
current = HOST;
start_pos = curr+1;
break;
case '#':
- /* bug #921: don't barf when redirecting to ## channels */
- if (current != CHAN)
- {
- mask[current].assign(start_pos, curr);
- current = CHAN;
- start_pos = curr;
- }
+ if (current == CHAN)
+ break;
+ mask[current].assign(start_pos, curr);
+ current = CHAN;
+ start_pos = curr;
break;
}
}
{
if (adding && IS_LOCAL(source))
{
- if (!ServerInstance->IsChannel(mask[CHAN].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(mask[CHAN]))
{
- source->WriteNumeric(403, "%s %s :Invalid channel name in redirection (%s)", source->nick.c_str(), channel->name.c_str(), mask[CHAN].c_str());
+ source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name in redirection (%s)", channel->name.c_str(), mask[CHAN].c_str());
return false;
}
Channel *c = ServerInstance->FindChan(mask[CHAN]);
if (!c)
{
- source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),mask[CHAN].c_str());
+ source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str());
return false;
}
else if (adding && c->GetPrefixValue(source) < OP_VALUE)
{
- source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(), mask[CHAN].c_str());
+ source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.", mask[CHAN].c_str());
return false;
}
if (assign(channel->name) == mask[CHAN])
{
- source->WriteNumeric(690, "%s %s :You cannot set a ban redirection to the channel the ban is on", source->nick.c_str(), channel->name.c_str());
+ source->WriteNumeric(690, "%s :You cannot set a ban redirection to the channel the ban is on", channel->name.c_str());
return false;
}
}
{
BanRedirect re;
bool nofollow;
+ ChanModeReference limitmode;
+ ChanModeReference redirectmode;
public:
ModuleBanRedirect()
- : re(this)
+ : re(this)
+ , nofollow(false)
+ , limitmode(this, "limit")
+ , redirectmode(this, "redirect")
{
- nofollow = false;
- }
-
-
- void init()
- {
- if(!ServerInstance->Modes->AddModeWatcher(&re))
- throw ModuleException("Could not add mode watcher");
-
- ServerInstance->Modules->AddService(re.extItem);
- Implementation list[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
}
- virtual void OnCleanup(int target_type, void* item)
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
{
if(target_type == TYPE_CHANNEL)
{
if(redirects)
{
- irc::modestacker modestack(false);
- StringDeque stackresult;
- std::vector<std::string> mode_junk;
- mode_junk.push_back(chan->name);
+ ModeHandler* ban = ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL);
+ Modes::ChangeList changelist;
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.Push('b', i->targetchan.insert(0, i->banmask));
- }
+ changelist.push_remove(ban, i->targetchan.insert(0, i->banmask));
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
- {
- modestack.PushPlus();
- modestack.Push('b', i->banmask);
- }
+ changelist.push_add(ban, i->banmask);
- while(modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY);
}
}
}
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
std::string destlimit;
if (destchan)
- destlimit = destchan->GetModeParameter('l');
+ destlimit = destchan->GetModeParameter(limitmode);
- if(destchan && ServerInstance->Modules->Find("m_redirect.so") && destchan->IsModeSet('L') && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
+ if(destchan && destchan->IsModeSet(redirectmode) && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
{
- user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
- user->WriteNumeric(470, "%s %s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", user->nick.c_str(), chan->name.c_str(), redir->targetchan.c_str());
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
+ user->WriteNumeric(470, "%s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", chan->name.c_str(), redir->targetchan.c_str());
nofollow = true;
- Channel::JoinUser(user, redir->targetchan.c_str(), false, "", false, ServerInstance->Time());
+ Channel::JoinUser(user, redir->targetchan);
nofollow = false;
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleBanRedirect()
- {
- /* XXX is this the best place to do this? */
- if (!ServerInstance->Modes->DelModeWatcher(&re))
- ServerInstance->Logs->Log("m_banredirect.so", DEBUG, "Failed to delete modewatcher!");
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows an extended ban (+b) syntax redirecting banned users to another channel", VF_COMMON|VF_VENDOR);
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Most of the code in this file is taken from
+ * http://openwall.com/crypt/crypt_blowfish-1.3.tar.gz
+ */
+
+/*
+ * The crypt_blowfish homepage is:
+ *
+ * http://www.openwall.com/crypt/
+ *
+ * This code comes from John the Ripper password cracker, with reentrant
+ * and crypt(3) interfaces added, but optimizations specific to password
+ * cracking removed.
+ *
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * It is my intent that you should be able to use this on your system,
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose. I would appreciate
+ * it if you give credit where it is due and keep your modifications in
+ * the public domain as well, but I don't require that in order to let
+ * you place this code and any modifications you make under a license
+ * of your choice.
+ *
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
+ * some of his ideas. The password hashing algorithm was designed by David
+ * Mazieres <dm at lcs.mit.edu>. For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the included crypt(3) man page.
+ *
+ * There's a paper on the algorithm that explains its design decisions:
+ *
+ * http://www.usenix.org/events/usenix99/provos.html
+ *
+ * Some of the tricks in BF_ROUND might be inspired by Eric Young's
+ * Blowfish library (I can't be sure if I would think of something if I
+ * hadn't seen his code).
+ */
+
+#include <string.h>
+
+#ifdef __i386__
+#define BF_SCALE 1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_SCALE 1
+#else
+#define BF_SCALE 0
+#endif
+
+typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
+
+/* Number of Blowfish rounds, this is also hardcoded into a few places */
+#define BF_N 16
+
+typedef BF_word BF_key[BF_N + 2];
+
+typedef struct {
+ BF_word S[4][0x100];
+ BF_key P;
+} BF_ctx;
+
+/*
+ * Magic IV for 64 Blowfish encryptions that we do at the end.
+ * The string is "OrpheanBeholderScryDoubt" on big-endian.
+ */
+static BF_word BF_magic_w[6] = {
+ 0x4F727068, 0x65616E42, 0x65686F6C,
+ 0x64657253, 0x63727944, 0x6F756274
+};
+
+/*
+ * P-box and S-box tables initialized with digits of Pi.
+ */
+static BF_ctx BF_init_state = {
+ {
+ {
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+ }, {
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+ }, {
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+ }, {
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+ }
+ }, {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ }
+};
+
+static unsigned char BF_itoa64[64 + 1] =
+ "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static unsigned char BF_atoi64[0x60] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
+ 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
+ 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
+};
+
+#define BF_safe_atoi64(dst, src) \
+{ \
+ tmp = (unsigned char)(src); \
+ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
+ tmp = BF_atoi64[tmp]; \
+ if (tmp > 63) return -1; \
+ (dst) = tmp; \
+}
+
+static int BF_decode(BF_word *dst, const char *src, int size)
+{
+ unsigned char *dptr = (unsigned char *)dst;
+ unsigned char *end = dptr + size;
+ const unsigned char *sptr = (const unsigned char *)src;
+ unsigned int tmp, c1, c2, c3, c4;
+
+ do {
+ BF_safe_atoi64(c1, *sptr++);
+ BF_safe_atoi64(c2, *sptr++);
+ *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c3, *sptr++);
+ *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c4, *sptr++);
+ *dptr++ = ((c3 & 0x03) << 6) | c4;
+ } while (dptr < end);
+
+ return 0;
+}
+
+static void BF_encode(char *dst, const BF_word *src, int size)
+{
+ const unsigned char *sptr = (const unsigned char *)src;
+ const unsigned char *end = sptr + size;
+ unsigned char *dptr = (unsigned char *)dst;
+ unsigned int c1, c2;
+
+ do {
+ c1 = *sptr++;
+ *dptr++ = BF_itoa64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 4;
+ *dptr++ = BF_itoa64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 6;
+ *dptr++ = BF_itoa64[c1];
+ *dptr++ = BF_itoa64[c2 & 0x3f];
+ } while (sptr < end);
+}
+
+static void BF_swap(BF_word *x, int count)
+{
+ static int endianness_check = 1;
+ char *is_little_endian = (char *)&endianness_check;
+ BF_word tmp;
+
+ if (*is_little_endian)
+ do {
+ tmp = *x;
+ tmp = (tmp << 16) | (tmp >> 16);
+ *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
+ } while (--count);
+}
+
+#if BF_SCALE
+/* Architectures which can shift addresses left by 2 bits with no extra cost */
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp2 = L >> 8; \
+ tmp2 &= 0xFF; \
+ tmp3 = L >> 16; \
+ tmp3 &= 0xFF; \
+ tmp4 = L >> 24; \
+ tmp1 = data.ctx.S[3][tmp1]; \
+ tmp2 = data.ctx.S[2][tmp2]; \
+ tmp3 = data.ctx.S[1][tmp3]; \
+ tmp3 += data.ctx.S[0][tmp4]; \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#else
+/* Architectures with no complicated addressing modes supported */
+#define BF_INDEX(S, i) \
+ (*((BF_word *)(((unsigned char *)S) + (i))))
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp1 <<= 2; \
+ tmp2 = L >> 6; \
+ tmp2 &= 0x3FC; \
+ tmp3 = L >> 14; \
+ tmp3 &= 0x3FC; \
+ tmp4 = L >> 22; \
+ tmp4 &= 0x3FC; \
+ tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
+ tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
+ tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
+ tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#endif
+
+/*
+ * Encrypt one block, BF_N is hardcoded here.
+ */
+#define BF_ENCRYPT \
+ L ^= data.ctx.P[0]; \
+ BF_ROUND(L, R, 0); \
+ BF_ROUND(R, L, 1); \
+ BF_ROUND(L, R, 2); \
+ BF_ROUND(R, L, 3); \
+ BF_ROUND(L, R, 4); \
+ BF_ROUND(R, L, 5); \
+ BF_ROUND(L, R, 6); \
+ BF_ROUND(R, L, 7); \
+ BF_ROUND(L, R, 8); \
+ BF_ROUND(R, L, 9); \
+ BF_ROUND(L, R, 10); \
+ BF_ROUND(R, L, 11); \
+ BF_ROUND(L, R, 12); \
+ BF_ROUND(R, L, 13); \
+ BF_ROUND(L, R, 14); \
+ BF_ROUND(R, L, 15); \
+ tmp4 = R; \
+ R = L; \
+ L = tmp4 ^ data.ctx.P[BF_N + 1];
+
+#define BF_body() \
+ L = R = 0; \
+ ptr = data.ctx.P; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.P[BF_N + 2]); \
+\
+ ptr = data.ctx.S[0]; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
+ unsigned char flags)
+{
+ const char *ptr = key;
+ unsigned int bug, i, j;
+ BF_word safety, sign, diff, tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function. While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others. The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash. Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them). This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups. (One conditional branch based on password
+ * length remains. It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+ bug = (unsigned int)flags & 1;
+ safety = ((BF_word)flags & 2) << 15;
+
+ sign = diff = 0;
+
+ for (i = 0; i < BF_N + 2; i++) {
+ tmp[0] = tmp[1] = 0;
+ for (j = 0; j < 4; j++) {
+ tmp[0] <<= 8;
+ tmp[0] |= (unsigned char)*ptr; /* correct */
+ tmp[1] <<= 8;
+ tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word. For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs. Once this flag is set, it remains set.
+ */
+ if (j)
+ sign |= tmp[1] & 0x80;
+ if (!*ptr)
+ ptr = key;
+ else
+ ptr++;
+ }
+ diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
+
+ expanded[i] = tmp[bug];
+ initial[i] = BF_init_state.P[i] ^ tmp[bug];
+ }
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result. If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm. Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases. Let's check for this.
+ */
+ diff |= diff >> 16; /* still zero iff exact match */
+ diff &= 0xffff; /* ditto */
+ diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+ sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+ sign &= ~diff & safety; /* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
+ * let's stick to it now. It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop. By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+ initial[0] ^= sign;
+}
+
+static const unsigned char flags_by_subtype[26] =
+ {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *BF_crypt(const char *key, const char *setting,
+ char *output, int size,
+ BF_word min)
+{
+ struct {
+ BF_ctx ctx;
+ BF_key expanded_key;
+ union {
+ BF_word salt[4];
+ BF_word output[6];
+ } binary;
+ } data;
+ BF_word L, R;
+ BF_word tmp1, tmp2, tmp3, tmp4;
+ BF_word *ptr;
+ BF_word count;
+ int i;
+
+ if (size < 7 + 22 + 31 + 1) {
+ return NULL;
+ }
+
+ if (setting[0] != '$' ||
+ setting[1] != '2' ||
+ setting[2] < 'a' || setting[2] > 'z' ||
+ !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
+ setting[3] != '$' ||
+ setting[4] < '0' || setting[4] > '3' ||
+ setting[5] < '0' || setting[5] > '9' ||
+ (setting[4] == '3' && setting[5] > '1') ||
+ setting[6] != '$') {
+ return NULL;
+ }
+
+ count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+ if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
+ return NULL;
+ }
+ BF_swap(data.binary.salt, 4);
+
+ BF_set_key(key, data.expanded_key, data.ctx.P,
+ flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
+
+ memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
+
+ L = R = 0;
+ for (i = 0; i < BF_N + 2; i += 2) {
+ L ^= data.binary.salt[i & 2];
+ R ^= data.binary.salt[(i & 2) + 1];
+ BF_ENCRYPT;
+ data.ctx.P[i] = L;
+ data.ctx.P[i + 1] = R;
+ }
+
+ ptr = data.ctx.S[0];
+ do {
+ ptr += 4;
+ L ^= data.binary.salt[(BF_N + 2) & 3];
+ R ^= data.binary.salt[(BF_N + 3) & 3];
+ BF_ENCRYPT;
+ *(ptr - 4) = L;
+ *(ptr - 3) = R;
+
+ L ^= data.binary.salt[(BF_N + 4) & 3];
+ R ^= data.binary.salt[(BF_N + 5) & 3];
+ BF_ENCRYPT;
+ *(ptr - 2) = L;
+ *(ptr - 1) = R;
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+ do {
+ int done;
+
+ for (i = 0; i < BF_N + 2; i += 2) {
+ data.ctx.P[i] ^= data.expanded_key[i];
+ data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+ }
+
+ done = 0;
+ do {
+ BF_body();
+ if (done)
+ break;
+ done = 1;
+
+ tmp1 = data.binary.salt[0];
+ tmp2 = data.binary.salt[1];
+ tmp3 = data.binary.salt[2];
+ tmp4 = data.binary.salt[3];
+ for (i = 0; i < BF_N; i += 4) {
+ data.ctx.P[i] ^= tmp1;
+ data.ctx.P[i + 1] ^= tmp2;
+ data.ctx.P[i + 2] ^= tmp3;
+ data.ctx.P[i + 3] ^= tmp4;
+ }
+ data.ctx.P[16] ^= tmp1;
+ data.ctx.P[17] ^= tmp2;
+ } while (1);
+ } while (--count);
+
+ for (i = 0; i < 6; i += 2) {
+ L = BF_magic_w[i];
+ R = BF_magic_w[i + 1];
+
+ count = 64;
+ do {
+ BF_ENCRYPT;
+ } while (--count);
+
+ data.binary.output[i] = L;
+ data.binary.output[i + 1] = R;
+ }
+
+ memcpy(output, setting, 7 + 22 - 1);
+ output[7 + 22 - 1] = BF_itoa64[(int)
+ BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
+
+/* This has to be bug-compatible with the original implementation, so
+ * only encode 23 of the 24 bytes. :-) */
+ BF_swap(data.binary.output, 6);
+ BF_encode(&output[7 + 22], data.binary.output, 23);
+ output[7 + 22 + 31] = '\0';
+
+ return output;
+}
+
+static int _crypt_output_magic(const char *setting, char *output, int size)
+{
+ if (size < 3)
+ return -1;
+
+ output[0] = '*';
+ output[1] = '0';
+ output[2] = '\0';
+
+ if (setting[0] == '*' && setting[1] == '0')
+ output[1] = '1';
+
+ return 0;
+}
+
+/*
+ * Please preserve the runtime self-test. It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build. Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings). It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough. Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers. Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+static char *_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size)
+{
+ const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+ const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+ static const char * const test_hashes[2] =
+ {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
+ "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
+ const char *test_hash = test_hashes[0];
+ char *retval;
+ const char *p;
+ int ok;
+ struct {
+ char s[7 + 22 + 1];
+ char o[7 + 22 + 31 + 1 + 1 + 1];
+ } buf;
+
+/* Hash the supplied password */
+ _crypt_output_magic(setting, output, size);
+ retval = BF_crypt(key, setting, output, size, 16);
+
+/*
+ * Do a quick self-test. It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+ memcpy(buf.s, test_setting, sizeof(buf.s));
+ if (retval) {
+ unsigned int flags = flags_by_subtype[
+ (unsigned int)(unsigned char)setting[2] - 'a'];
+ test_hash = test_hashes[flags & 1];
+ buf.s[2] = setting[2];
+ }
+ memset(buf.o, 0x55, sizeof(buf.o));
+ buf.o[sizeof(buf.o) - 1] = 0;
+ p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+ ok = (p == buf.o &&
+ !memcmp(p, buf.s, 7 + 22) &&
+ !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+ {
+ const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+ BF_key ae, ai, ye, yi;
+ BF_set_key(k, ae, ai, 2); /* $2a$ */
+ BF_set_key(k, ye, yi, 4); /* $2y$ */
+ ai[0] ^= 0x10000; /* undo the safety (for comparison) */
+ ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+ !memcmp(ae, ye, sizeof(ae)) &&
+ !memcmp(ai, yi, sizeof(ai));
+ }
+
+ if (ok)
+ return retval;
+
+/* Should not happen */
+ _crypt_output_magic(setting, output, size);
+ /* pretend we don't support this hash type */
+ return NULL;
+}
+
+static char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ if (size < 16 || output_size < 7 + 22 + 1 ||
+ (count && (count < 4 || count > 31)) ||
+ prefix[0] != '$' || prefix[1] != '2' ||
+ (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
+ if (output_size > 0) output[0] = '\0';
+ return NULL;
+ }
+
+ if (!count) count = 5;
+
+ output[0] = '$';
+ output[1] = '2';
+ output[2] = prefix[2];
+ output[3] = '$';
+ output[4] = '0' + count / 10;
+ output[5] = '0' + count % 10;
+ output[6] = '$';
+
+ BF_encode(&output[7], (const BF_word *)input, 16);
+ output[7 + 22] = '\0';
+
+ return output;
+}
+
+// Start inspircd-specific code
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+class BCryptProvider : public HashProvider
+{
+ private:
+ std::string Salt()
+ {
+ char entropy[16];
+ for (unsigned int i = 0; i < sizeof(entropy); ++i)
+ entropy[i] = ServerInstance->GenRandomInt(0xFF);
+
+ char salt[32];
+ if (!_crypt_gensalt_blowfish_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt)))
+ throw ModuleException("Could not generate salt - this should never happen");
+
+ return salt;
+ }
+
+ public:
+ unsigned int rounds;
+
+ std::string Generate(const std::string& data, const std::string& salt)
+ {
+ char hash[64];
+ _crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash));
+ return hash;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ return Generate(data, Salt());
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ std::string ret = Generate(input, hash);
+ if (ret.empty())
+ return false;
+
+ if (ret == hash)
+ return true;
+ return false;
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ BCryptProvider(Module* parent)
+ : HashProvider(parent, "bcrypt", 60)
+ , rounds(10)
+ {
+ }
+};
+
+class ModuleBCrypt : public Module
+{
+ BCryptProvider bcrypt;
+
+ public:
+ ModuleBCrypt() : bcrypt(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("bcrypt");
+ bcrypt.rounds = conf->getInt("rounds", 10, 1);
+ }
+
+ Version GetVersion()
+ {
+ return Version("Implements bcrypt hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleBCrypt)
#include "inspircd.h"
-/* $ModDesc: Attempt to block /amsg, at least some of the irritating mIRC scripts. */
-
enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOPERS, IBLOCK_SILENT };
/* IBLOCK_NOTICE - Send a notice to the user informing them of what happened.
* IBLOCK_NOTICEOPERS - Send a notice to the user informing them and send an oper notice.
*/
class BlockedMessage
{
-public:
+ public:
std::string message;
irc::string target;
time_t sent;
- BlockedMessage(const std::string &msg, const irc::string &tgt, time_t when)
- : message(msg), target(tgt), sent(when)
+ BlockedMessage(const std::string& msg, const std::string& tgt, time_t when)
+ : message(msg), target(tgt.c_str()), sent(when)
{
}
};
{
}
- void init()
- {
- this->OnRehash(NULL);
- ServerInstance->Modules->AddService(blockamsg);
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleBlockAmsg()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Attempt to block /amsg, at least some of the irritating mIRC scripts.",VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("blockamsg");
ForgetDelay = tag->getInt("delay", -1);
std::string act = tag->getString("action");
- if(act == "notice")
+ if (act == "notice")
action = IBLOCK_NOTICE;
- else if(act == "noticeopers")
+ else if (act == "noticeopers")
action = IBLOCK_NOTICEOPERS;
- else if(act == "silent")
+ else if (act == "silent")
action = IBLOCK_SILENT;
- else if(act == "kill")
+ else if (act == "kill")
action = IBLOCK_KILL;
else
action = IBLOCK_KILLOPERS;
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
// Don't do anything with unregistered users
if (user->registered != REG_ALL)
if ((validated) && (parameters.size() >= 2) && ((command == "PRIVMSG") || (command == "NOTICE")))
{
- // parameters[0] should have the target(s) in it.
- // I think it will be faster to first check if there are any commas, and if there are then try and parse it out.
- // Most messages have a single target so...
+ // parameters[0] is the target list, count how many channels are there
+ unsigned int targets = 0;
+ // Is the first target a channel?
+ if (*parameters[0].c_str() == '#')
+ targets = 1;
- int targets = 1;
- int userchans = 0;
-
- if(*parameters[0].c_str() != '#')
+ for (const char* c = parameters[0].c_str(); *c; c++)
{
- // Decrement if the first target wasn't a channel.
- targets--;
- }
-
- for(const char* c = parameters[0].c_str(); *c; c++)
- if((*c == ',') && *(c+1) && (*(c+1) == '#'))
+ if ((*c == ',') && (*(c+1) == '#'))
targets++;
+ }
/* targets should now contain the number of channel targets the msg/notice was pointed at.
* If the msg/notice was a PM there should be no channel targets and 'targets' should = 0.
* We don't want to block PMs so...
*/
- if(targets == 0)
- {
+ if (targets == 0)
return MOD_RES_PASSTHRU;
- }
-
- userchans = user->chans.size();
// Check that this message wasn't already sent within a few seconds.
BlockedMessage* m = blockamsg.get(user);
// OR
// The number of target channels is equal to the number of channels the sender is on..a little suspicious.
// Check it's more than 1 too, or else users on one channel would have fun.
- if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans)))
+ if ((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == user->chans.size())))
{
// Block it...
- if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
+ if (action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame denied", user->nick.c_str());
- if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
+ if (action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
ServerInstance->Users->QuitUser(user, "Attempted to global message (/amsg or /ame)");
- else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
- user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) denied", user->nick.c_str());
+ else if (action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
+ user->WriteNotice("Global message (/amsg or /ame) denied");
return MOD_RES_DENY;
}
- if(m)
+ if (m)
{
// If there's already a BlockedMessage allocated, use it.
m->message = parameters[1];
}
else
{
- m = new BlockedMessage(parameters[1], parameters[0].c_str(), ServerInstance->Time());
+ m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
blockamsg.set(user, m);
}
}
}
};
-
MODULE_INIT(ModuleBlockAmsg)
#include "inspircd.h"
-/* $ModDesc: Provides support to block all-CAPS channel messages and notices */
-
/** Handles the +B channel mode
*/
class ModuleBlockCAPS : public Module
{
BlockCaps bc;
- int percent;
+ unsigned int percent;
unsigned int minlen;
char capsmap[256];
-public:
+public:
ModuleBlockCAPS() : bc(this)
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(bc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('B');
+ tokens["EXTBAN"].push_back('B');
}
- virtual void OnRehash(User* user)
- {
- ReadConf();
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (target_type == TYPE_CHANNEL)
{
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet('B')))
+ if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet(bc)))
{
- int caps = 0;
- const char* actstr = "\1ACTION ";
- int act = 0;
-
- for (std::string::iterator i = text.begin(); i != text.end(); i++)
- {
- /* Smart fix for suggestion from Jobe, ignore CTCP ACTION (part of /ME) */
- if (*actstr && *i == *actstr++ && act != -1)
- {
- act++;
- continue;
- }
- else
- act = -1;
+ std::string::size_type caps = 0;
+ unsigned int offset = 0;
+ // Ignore the beginning of the text if it's a CTCP ACTION (/me)
+ if (!text.compare(0, 8, "\1ACTION ", 8))
+ offset = 8;
+ for (std::string::const_iterator i = text.begin() + offset; i != text.end(); ++i)
caps += capsmap[(unsigned char)*i];
- }
- if ( ((caps*100)/(int)text.length()) >= percent )
+
+ if (((caps * 100) / text.length()) >= percent)
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Your message cannot contain more than %d%% capital letters if it's longer than %d characters", user->nick.c_str(), c->name.c_str(), percent, minlen);
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Your message cannot contain more than %d%% capital letters if it's longer than %d characters", c->name.c_str(), percent, minlen);
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("blockcaps");
- percent = tag->getInt("percent", 100);
- minlen = tag->getInt("minlen", 1);
+ percent = tag->getInt("percent", 100, 1, 100);
+ minlen = tag->getInt("minlen", 1, 1, ServerInstance->Config->Limits.MaxLine);
std::string hmap = tag->getString("capsmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
memset(capsmap, 0, sizeof(capsmap));
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
capsmap[(unsigned char)*n] = 1;
- if (percent < 1 || percent > 100)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:percent> out of range, setting to default of 100.");
- percent = 100;
- }
- if (minlen < 1 || minlen > MAXBUF-1)
- {
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "<blockcaps:minlen> out of range, setting to default of 1.");
- minlen = 1;
- }
- }
-
- virtual ~ModuleBlockCAPS()
- {
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support to block all-CAPS channel messages and notices", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +c to block color */
-
/** Handles the +c channel mode
*/
class BlockColor : public SimpleChannelModeHandler
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(bc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["EXTBAN"].push_back('c');
}
- virtual void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('c');
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet('c')))
+ if (!c->GetExtBanStatus(user, 'c').check(!c->IsModeSet(bc)))
{
for (std::string::iterator i = text.begin(); i != text.end(); i++)
{
case 21:
case 22:
case 31:
- user->WriteNumeric(404, "%s %s :Can't send colors to channel (+c set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Can't send colors to channel (+c set)", c->name.c_str());
return MOD_RES_DENY;
break;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual ~ModuleBlockColor()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +c to block color",VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides user mode +B to mark the user as a bot */
-
/** Handles user mode +B
*/
class BotMode : public SimpleUserModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(bm);
- Implementation eventlist[] = { I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleBotMode()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides user mode +B to mark the user as a bot",VF_VENDOR);
}
- virtual void OnWhois(User* src, User* dst)
+ void OnWhois(User* src, User* dst) CXX11_OVERRIDE
{
- if (dst->IsModeSet('B'))
+ if (dst->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(src, dst, 335, src->nick+" "+dst->nick+" :is a bot on "+ServerInstance->Config->Network);
+ ServerInstance->SendWhoisLine(src, dst, 335, dst->nick+" :is a bot on "+ServerInstance->Config->Network);
}
}
-
};
-
MODULE_INIT(ModuleBotMode)
#include "inspircd.h"
-/* $ModDesc: Implementation of callerid, usermode +g, /accept */
+enum
+{
+ RPL_ACCEPTLIST = 281,
+ RPL_ENDOFACCEPT = 282,
+ ERR_ACCEPTFULL = 456,
+ ERR_ACCEPTEXIST = 457,
+ ERR_ACCEPTNOT = 458,
+ ERR_TARGUMODEG = 716,
+ RPL_TARGNOTIFY = 717,
+ RPL_UMODEGMSG = 718
+};
class callerid_data
{
std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
{
- callerid_data* dat = static_cast<callerid_data*>(item);
- return dat->ToString(format);
+ std::string ret;
+ if (format != FORMAT_NETWORK)
+ {
+ callerid_data* dat = static_cast<callerid_data*>(item);
+ ret = dat->ToString(format);
+ }
+ return ret;
}
void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
{
+ if (format == FORMAT_NETWORK)
+ return;
+
callerid_data* dat = new callerid_data;
irc::commasepstream s(value);
std::string tok;
while (s.GetToken(tok))
{
- if (tok.empty())
- continue;
-
User *u = ServerInstance->FindNick(tok);
if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u)))
{
if (!targ)
{
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (1)");
continue; // shouldn't happen, but oh well.
}
if (it2 != targ->wholistsme.end())
targ->wholistsme.erase(it2);
else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (2)");
}
delete dat;
}
class CommandAccept : public Command
{
+ /** Pair: first is the target, second is true to add, false to remove
+ */
+ typedef std::pair<User*, bool> ACCEPTAction;
+
+ static ACCEPTAction GetTargetAndAction(std::string& tok, User* cmdfrom = NULL)
+ {
+ bool remove = (tok[0] == '-');
+ if ((remove) || (tok[0] == '+'))
+ tok.erase(tok.begin());
+
+ User* target;
+ if (!cmdfrom || !IS_LOCAL(cmdfrom))
+ target = ServerInstance->FindNick(tok);
+ else
+ target = ServerInstance->FindNickOnly(tok);
+
+ if ((!target) || (target->registered != REG_ALL) || (target->quitting) || (IS_SERVER(target)))
+ target = NULL;
+
+ return std::make_pair(target, !remove);
+ }
+
public:
CallerIDExtInfo extInfo;
unsigned int maxaccepts;
{
allow_empty_last_param = false;
syntax = "*|(+|-)<nick>[,(+|-)<nick> ...]";
- TRANSLATE2(TR_CUSTOM, TR_END);
+ TRANSLATE1(TR_CUSTOM);
}
- virtual void EncodeParameter(std::string& parameter, int index)
+ void EncodeParameter(std::string& parameter, int index)
{
- if (index != 0)
+ // Send lists as-is (part of 2.0 compat)
+ if (parameter.find(',') != std::string::npos)
return;
- std::string out;
- irc::commasepstream nicks(parameter);
- std::string tok;
- while (nicks.GetToken(tok))
- {
- if (tok == "*")
- {
- continue; // Drop list requests, since remote servers ignore them anyway.
- }
- if (!out.empty())
- out.append(",");
- bool dash = false;
- if (tok[0] == '-')
- {
- dash = true;
- tok.erase(0, 1); // Remove the dash.
- }
- else if (tok[0] == '+')
- tok.erase(0, 1);
- User* u = ServerInstance->FindNick(tok);
- if ((!u) || (u->registered != REG_ALL) || (u->quitting) || (IS_SERVER(u)))
- continue;
+ // Convert a [+|-]<nick> into a [-]<uuid>
+ ACCEPTAction action = GetTargetAndAction(parameter);
+ if (!action.first)
+ return;
- if (dash)
- out.append("-");
- out.append(u->uuid);
- }
- parameter = out;
+ parameter = (action.second ? "" : "-") + action.first->uuid;
}
/** Will take any number of nicks (up to MaxTargets), which can be seperated by commas.
*/
CmdResult Handle(const std::vector<std::string> ¶meters, User* user)
{
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
+
/* Even if callerid mode is not set, we let them manage their ACCEPT list so that if they go +g they can
* have a list already setup. */
- const std::string& tok = parameters[0];
-
- if (tok == "*")
+ if (parameters[0] == "*")
{
- if (IS_LOCAL(user))
- ListAccept(user);
+ ListAccept(user);
return CMD_SUCCESS;
}
- else if (tok[0] == '-')
- {
- User* whotoremove;
- if (IS_LOCAL(user))
- whotoremove = ServerInstance->FindNickOnly(tok.substr(1));
- else
- whotoremove = ServerInstance->FindNick(tok.substr(1));
- if (whotoremove)
- return (RemoveAccept(user, whotoremove) ? CMD_SUCCESS : CMD_FAILURE);
- else
- return CMD_FAILURE;
- }
- else
+ std::string tok = parameters[0];
+ ACCEPTAction action = GetTargetAndAction(tok, user);
+ if (!action.first)
{
- const std::string target = (tok[0] == '+' ? tok.substr(1) : tok);
- User* whotoadd;
- if (IS_LOCAL(user))
- whotoadd = ServerInstance->FindNickOnly(target);
- else
- whotoadd = ServerInstance->FindNick(target);
-
- if ((whotoadd) && (whotoadd->registered == REG_ALL) && (!whotoadd->quitting) && (!IS_SERVER(whotoadd)))
- return (AddAccept(user, whotoadd) ? CMD_SUCCESS : CMD_FAILURE);
- else
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), tok.c_str());
- return CMD_FAILURE;
- }
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", tok.c_str());
+ return CMD_FAILURE;
}
+
+ if ((!IS_LOCAL(user)) && (!IS_LOCAL(action.first)))
+ // Neither source nor target is local, forward the command to the server of target
+ return CMD_SUCCESS;
+
+ // The second item in the pair is true if the first char is a '+' (or nothing), false if it's a '-'
+ if (action.second)
+ return (AddAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
+ else
+ return (RemoveAccept(user, action.first) ? CMD_SUCCESS : CMD_FAILURE);
}
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- return ROUTE_BROADCAST;
+ // There is a list in parameters[0] in two cases:
+ // Either when the source is remote, this happens because 2.0 servers send comma seperated uuid lists,
+ // we don't split those but broadcast them, as before.
+ //
+ // Or if the source is local then LoopCall() runs OnPostCommand() after each entry in the list,
+ // meaning the linking module has sent an ACCEPT already for each entry in the list to the
+ // appropiate server and the ACCEPT with the list of nicks (this) doesn't need to be sent anywhere.
+ if ((!IS_LOCAL(user)) && (parameters[0].find(',') != std::string::npos))
+ return ROUTE_BROADCAST;
+
+ // Find the target
+ std::string targetstring = parameters[0];
+ ACCEPTAction action = GetTargetAndAction(targetstring, user);
+ if (!action.first)
+ // Target is a "*" or source is local and the target is a list of nicks
+ return ROUTE_LOCALONLY;
+
+ // Route to the server of the target
+ return ROUTE_UNICAST(action.first->server);
}
void ListAccept(User* user)
if (dat)
{
for (std::set<User*>::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
- user->WriteNumeric(281, "%s %s", user->nick.c_str(), (*i)->nick.c_str());
+ user->WriteNumeric(RPL_ACCEPTLIST, (*i)->nick);
}
- user->WriteNumeric(282, "%s :End of ACCEPT list", user->nick.c_str());
+ user->WriteNumeric(RPL_ENDOFACCEPT, ":End of ACCEPT list");
}
bool AddAccept(User* user, User* whotoadd)
callerid_data* dat = extInfo.get(user, true);
if (dat->accepting.size() >= maxaccepts)
{
- user->WriteNumeric(456, "%s :Accept list is full (limit is %d)", user->nick.c_str(), maxaccepts);
+ user->WriteNumeric(ERR_ACCEPTFULL, ":Accept list is full (limit is %d)", maxaccepts);
return false;
}
if (!dat->accepting.insert(whotoadd).second)
{
- user->WriteNumeric(457, "%s %s :is already on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTEXIST, "%s :is already on your accept list", whotoadd->nick.c_str());
return false;
}
callerid_data *targ = extInfo.get(whotoadd, true);
targ->wholistsme.push_back(dat);
- user->WriteServ("NOTICE %s :%s is now on your accept list", user->nick.c_str(), whotoadd->nick.c_str());
+ user->WriteNotice(whotoadd->nick + " is now on your accept list");
return true;
}
callerid_data* dat = extInfo.get(user, false);
if (!dat)
{
- user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, "%s :is not on your accept list", whotoremove->nick.c_str());
return false;
}
std::set<User*>::iterator i = dat->accepting.find(whotoremove);
if (i == dat->accepting.end())
{
- user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNumeric(ERR_ACCEPTNOT, "%s :is not on your accept list", whotoremove->nick.c_str());
return false;
}
if (!dat2)
{
// How the fuck is this possible.
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (3)");
return false;
}
// Found me!
dat2->wholistsme.erase(it);
else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (4)");
- user->WriteServ("NOTICE %s :%s is no longer on your accept list", user->nick.c_str(), whotoremove->nick.c_str());
+ user->WriteNotice(whotoremove->nick + " is no longer on your accept list");
return true;
}
};
class ModuleCallerID : public Module
{
-private:
CommandAccept cmd;
User_g myumode;
if (it2 != dat->accepting.end())
dat->accepting.erase(it2);
else
- ServerInstance->Logs->Log("m_callerid", DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in callerid state, please report (5)");
}
userdata->wholistsme.clear();
{
}
- void init()
- {
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(myumode);
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.extInfo);
-
- Implementation eventlist[] = { I_OnRehash, I_OnUserPostNick, I_OnUserQuit, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleCallerID()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implementation of callerid, usermode +g, /accept", VF_COMMON | VF_VENDOR);
}
- virtual void On005Numeric(std::string& output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output += " CALLERID=g";
+ tokens["CALLERID"] = "g";
}
- ModResult PreText(User* user, User* dest, std::string& text)
+ ModResult OnUserPreMessage(User* user, void* voiddest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
- if (!dest->IsModeSet('g') || (user == dest))
+ if (!IS_LOCAL(user) || target_type != TYPE_USER)
+ return MOD_RES_PASSTHRU;
+
+ User* dest = static_cast<User*>(voiddest);
+ if (!dest->IsModeSet(myumode) || (user == dest))
return MOD_RES_PASSTHRU;
- if (operoverride && IS_OPER(user))
+ if (operoverride && user->IsOper())
return MOD_RES_PASSTHRU;
callerid_data* dat = cmd.extInfo.get(dest, true);
{
time_t now = ServerInstance->Time();
/* +g and *not* accepted */
- user->WriteNumeric(716, "%s %s :is in +g mode (server-side ignore).", user->nick.c_str(), dest->nick.c_str());
+ user->WriteNumeric(ERR_TARGUMODEG, "%s :is in +g mode (server-side ignore).", dest->nick.c_str());
if (now > (dat->lastnotify + (time_t)notify_cooldown))
{
- user->WriteNumeric(717, "%s %s :has been informed that you messaged them.", user->nick.c_str(), dest->nick.c_str());
- dest->SendText(":%s 718 %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
- ServerInstance->Config->ServerName.c_str(), dest->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), user->nick.c_str());
+ user->WriteNumeric(RPL_TARGNOTIFY, "%s :has been informed that you messaged them.", dest->nick.c_str());
+ dest->SendText(":%s %03d %s %s %s@%s :is messaging you, and you have umode +g. Use /ACCEPT +%s to allow.",
+ ServerInstance->Config->ServerName.c_str(), RPL_UMODEGMSG, dest->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), user->nick.c_str());
dat->lastnotify = now;
}
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
- {
- if (IS_LOCAL(user) && target_type == TYPE_USER)
- return PreText(user, (User*)dest, text);
-
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
- {
- if (IS_LOCAL(user) && target_type == TYPE_USER)
- return PreText(user, (User*)dest, text);
-
- return MOD_RES_PASSTHRU;
- }
-
- void OnUserPostNick(User* user, const std::string& oldnick)
+ void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
{
if (!tracknick)
RemoveFromAllAccepts(user);
}
- void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
{
RemoveFromAllAccepts(user);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("callerid");
cmd.maxaccepts = tag->getInt("maxaccepts", 16);
};
MODULE_INIT(ModuleCallerID)
-
-
#include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the CAP negotiation mechanism seen in ratbox-derived ircds */
+#include "modules/cap.h"
/*
CAP LS
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
- irc::string subcommand = parameters[0].c_str();
+ std::string subcommand(parameters[0].length(), ' ');
+ std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
if (subcommand == "REQ")
{
while (cap_stream.GetToken(cap_))
{
+ std::transform(cap_.begin(), cap_.end(), cap_.begin(), ::tolower);
Data.wanted.push_back(cap_);
}
if (Data.ack.size() > 0)
{
- std::string AckResult = irc::stringjoiner(" ", Data.ack, 0, Data.ack.size() - 1).GetJoined();
- user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), AckResult.c_str());
+ std::string AckResult = irc::stringjoiner(Data.ack);
+ user->WriteCommand("CAP", "ACK :" + AckResult);
}
if (Data.wanted.size() > 0)
{
- std::string NakResult = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
- user->WriteServ("CAP %s NAK :%s", user->nick.c_str(), NakResult.c_str());
+ std::string NakResult = irc::stringjoiner(Data.wanted);
+ user->WriteCommand("CAP", "NAK :" + NakResult);
}
}
else if (subcommand == "END")
reghold.set(user, 1);
Data.Send();
- std::string Result;
- if (Data.wanted.size() > 0)
- Result = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
-
- user->WriteServ("CAP %s %s :%s", user->nick.c_str(), subcommand.c_str(), Result.c_str());
+ std::string Result = irc::stringjoiner(Data.wanted);
+ user->WriteCommand("CAP", subcommand + " :" + Result);
}
else if (subcommand == "CLEAR")
{
reghold.set(user, 1);
Data.Send();
- std::string Result;
- if (!Data.ack.empty())
- Result = irc::stringjoiner(" ", Data.ack, 0, Data.ack.size() - 1).GetJoined();
- user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), Result.c_str());
+ std::string Result = irc::stringjoiner(Data.ack);
+ user->WriteCommand("CAP", "ACK :" + Result);
}
else
{
- user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s %s :Invalid CAP subcommand", user->nick.c_str(), subcommand.c_str());
+ user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
return CMD_FAILURE;
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.reghold);
-
- Implementation eventlist[] = { I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
/* Users in CAP state get held until CAP END */
if (cmd.reghold.get(user))
return MOD_RES_PASSTHRU;
}
- ~ModuleCAP()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Client CAP extension support", VF_VENDOR);
}
};
MODULE_INIT(ModuleCAP)
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef M_CAP_H
-#define M_CAP_H
-
-class CapEvent : public Event
-{
- public:
- enum CapEventType
- {
- CAPEVENT_REQ,
- CAPEVENT_LS,
- CAPEVENT_LIST,
- CAPEVENT_CLEAR
- };
-
- CapEventType type;
- std::vector<std::string> wanted;
- std::vector<std::string> ack;
- User* user;
- CapEvent(Module* sender, User* u, CapEventType capevtype) : Event(sender, "cap_request"), type(capevtype), user(u) {}
-};
-
-class GenericCap
-{
- public:
- LocalIntExt ext;
- const std::string cap;
- GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap)
- {
- ServerInstance->Modules->AddService(ext);
- }
-
- void HandleEvent(Event& ev)
- {
- if (ev.id != "cap_request")
- return;
-
- CapEvent *data = static_cast<CapEvent*>(&ev);
- if (data->type == CapEvent::CAPEVENT_REQ)
- {
- for (std::vector<std::string>::iterator it = data->wanted.begin(); it != data->wanted.end(); ++it)
- {
- if (it->empty())
- continue;
- bool enablecap = ((*it)[0] != '-');
- if (((enablecap) && (*it == cap)) || (*it == "-" + cap))
- {
- // we can handle this, so ACK it, and remove it from the wanted list
- data->ack.push_back(*it);
- data->wanted.erase(it);
- ext.set(data->user, enablecap ? 1 : 0);
- break;
- }
- }
- }
- else if (data->type == CapEvent::CAPEVENT_LS)
- {
- data->wanted.push_back(cap);
- }
- else if (data->type == CapEvent::CAPEVENT_LIST)
- {
- if (ext.get(data->user))
- data->wanted.push_back(cap);
- }
- else if (data->type == CapEvent::CAPEVENT_CLEAR)
- {
- data->ack.push_back("-" + cap);
- ext.set(data->user, 0);
- }
- }
-};
-
-#endif
#include "inspircd.h"
#include "xline.h"
-/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
-
/** Holds a CBAN item
*/
class CBan : public XLine
{
-public:
+private:
+ std::string displaytext;
irc::string matchtext;
+public:
CBan(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& ch)
: XLine(s_time, d, src, re, "CBAN")
{
+ this->displaytext = ch;
this->matchtext = ch.c_str();
}
- ~CBan()
- {
- }
-
// XXX I shouldn't have to define this
bool Matches(User *u)
{
return false;
}
- void DisplayExpiry()
+ const std::string& Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired CBan %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
- }
-
- const char* Displayable()
- {
- return matchtext.c_str();
+ return displaytext;
}
};
CommandCBan(Module* Creator) : Command(Creator, "CBAN", 1, 3)
{
flags_needed = 'o'; this->syntax = "<channel> [<duration> :<reason>]";
- TRANSLATE4(TR_TEXT,TR_TEXT,TR_TEXT,TR_END);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
}
else
{
- user->WriteServ("NOTICE %s :*** CBan %s not found in list, try /stats C.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** CBan " + parameters[0] + " not found in list, try /stats C.");
return CMD_FAILURE;
}
}
else
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
const char *reason = (parameters.size() > 2) ? parameters[2].c_str() : "No reason supplied";
CBan* r = new CBan(ServerInstance->Time(), duration, user->nick.c_str(), reason, parameters[0].c_str());
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteGlobalSno('x', "%s added timed CBan for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), reason);
}
}
else
{
delete r;
- user->WriteServ("NOTICE %s :*** CBan for %s already exists", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** CBan for " + parameters[0] + " already exists");
return CMD_FAILURE;
}
}
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&f);
-
- ServerInstance->Modules->AddService(mycommand);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnStats };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleCBan()
+ ~ModuleCBan()
{
ServerInstance->XLines->DelAll("CBAN");
ServerInstance->XLines->UnregisterFactory(&f);
}
- virtual ModResult OnStats(char symbol, User* user, string_list &out)
+ ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
{
if (symbol != 'C')
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
- virtual ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
XLine *rl = ServerInstance->XLines->MatchesLine("CBAN", cname);
if (rl)
{
// Channel is banned.
- user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick.c_str(), cname, rl->reason.c_str());
+ user->WriteNumeric(384, "%s :Cannot join channel, CBANed (%s)", cname.c_str(), rl->reason.c_str());
ServerInstance->SNO->WriteGlobalSno('a', "%s tried to join %s which is CBANed (%s)",
- user->nick.c_str(), cname, rl->reason.c_str());
+ user->nick.c_str(), cname.c_str(), rl->reason.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives /cban, aka C:lines. Think Q:lines, for channels.", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleCBan)
-
*/
-/* $ModDesc: Provides user and channel +G mode */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
#include "inspircd.h"
-#include <iostream>
typedef std::map<irc::string,irc::string> censor_t;
public:
ModuleCensor() : cu(this), cc(this) { }
- void init()
- {
- /* Read the configuration file on startup.
- */
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cu);
- ServerInstance->Modules->AddService(cc);
- Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual ~ModuleCensor()
- {
- }
-
// format of a config entry is <badword text="shit" replace="poo">
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
bool active = false;
if (target_type == TYPE_USER)
- active = ((User*)dest)->IsModeSet('G');
+ active = ((User*)dest)->IsModeSet(cu);
else if (target_type == TYPE_CHANNEL)
{
- active = ((Channel*)dest)->IsModeSet('G');
Channel* c = (Channel*)dest;
+ active = c->IsModeSet(cc);
ModResult res = ServerInstance->OnCheckExemption(user,c,"censor");
if (res == MOD_RES_ALLOW)
{
if (index->second.empty())
{
- user->WriteNumeric(ERR_WORDFILTERED, "%s %s %s :Your message contained a censored word, and was blocked", user->nick.c_str(), ((Channel*)dest)->name.c_str(), index->first.c_str());
+ user->WriteNumeric(ERR_WORDFILTERED, "%s %s :Your message contained a censored word, and was blocked", ((Channel*)dest)->name.c_str(), index->first.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
/*
* reload our config file on rehash - we must destroy and re-allocate the classes
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides user and channel +G mode",VF_VENDOR);
}
#include "inspircd.h"
#include "xline.h"
-
-/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
+#include "modules/dns.h"
enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
+// We need this method up here so that it can be accessed from anywhere
+static void ChangeIP(User* user, const std::string& newip)
+{
+ ServerInstance->Users->RemoveCloneCounts(user);
+ user->SetClientIP(newip.c_str());
+ ServerInstance->Users->AddClone(user);
+}
/** Holds a CGI site's details
*/
bool notify;
StringExtItem realhost;
StringExtItem realip;
- LocalStringExt webirc_hostname;
- LocalStringExt webirc_ip;
CGIHostlist Hosts;
CommandWebirc(Module* Creator)
: Command(Creator, "WEBIRC", 4),
- realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator),
- webirc_hostname("cgiirc_webirc_hostname", Creator), webirc_ip("cgiirc_webirc_ip", Creator)
+ realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator)
{
works_before_reg = true;
this->syntax = "password client hostname ip";
realhost.set(user, user->host);
realip.set(user, user->GetIPString());
- bool host_ok = (parameters[2].length() < 64);
+ // Check if we're happy with the provided hostname. If it's problematic then make sure we won't set a host later, just the IP
+ bool host_ok = (parameters[2].length() <= ServerInstance->Config->Limits.MaxHost);
const std::string& newhost = (host_ok ? parameters[2] : parameters[3]);
if (notify)
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick.c_str(), user->host.c_str(), newhost.c_str(), user->host.c_str());
+ ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick.c_str(), user->host.c_str(), newhost.c_str(), user->host.c_str());
- // Check if we're happy with the provided hostname. If it's problematic then make sure we won't set a host later, just the IP
- if (host_ok)
- webirc_hostname.set(user, parameters[2]);
- else
- webirc_hostname.unset(user);
+ // Where the magic happens - change their IP
+ ChangeIP(user, parameters[3]);
+ // And follow this up by changing their host
+ user->host = user->dhost = newhost;
+ user->InvalidateCache();
- webirc_ip.set(user, parameters[3]);
return CMD_SUCCESS;
}
}
}
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
+ ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s tried to use WEBIRC, but didn't match any configured webirc blocks.", user->GetFullRealHost().c_str());
return CMD_FAILURE;
}
};
/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
*/
-class CGIResolver : public Resolver
+class CGIResolver : public DNS::Request
{
std::string typ;
std::string theiruid;
LocalIntExt& waiting;
bool notify;
public:
- CGIResolver(Module* me, bool NotifyOpers, const std::string &source, LocalUser* u,
- const std::string &type, bool &cached, LocalIntExt& ext)
- : Resolver(source, DNS_QUERY_PTR4, cached, me), typ(type), theiruid(u->uuid),
+ CGIResolver(DNS::Manager *mgr, Module* me, bool NotifyOpers, const std::string &source, LocalUser* u,
+ const std::string &ttype, LocalIntExt& ext)
+ : DNS::Request(mgr, me, source, DNS::QUERY_PTR), typ(ttype), theiruid(u->uuid),
waiting(ext), notify(NotifyOpers)
{
}
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
{
/* Check the user still exists */
User* them = ServerInstance->FindUUID(theiruid);
if ((them) && (!them->quitting))
{
- if (notify)
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), result.c_str(), typ.c_str());
+ LocalUser* lu = IS_LOCAL(them);
+ if (!lu)
+ return;
- if (result.length() > 64)
+ const DNS::ResourceRecord &ans_record = r->answers[0];
+ if (ans_record.rdata.empty() || ans_record.rdata.length() > ServerInstance->Config->Limits.MaxHost)
return;
- them->host = result;
- them->dhost = result;
+
+ if (notify)
+ ServerInstance->SNO->WriteGlobalSno('w', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), ans_record.rdata.c_str(), typ.c_str());
+
+ them->host = them->dhost = ans_record.rdata;
them->InvalidateCache();
- them->CheckLines(true);
+ lu->CheckLines(true);
}
}
- virtual void OnError(ResolverError e, const std::string &errormessage)
+ void OnError(const DNS::Query *r) CXX11_OVERRIDE
{
if (!notify)
return;
User* them = ServerInstance->FindUUID(theiruid);
if ((them) && (!them->quitting))
{
- ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick.c_str(), them->host.c_str(), typ.c_str());
+ ServerInstance->SNO->WriteToSnoMask('w', "Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick.c_str(), them->host.c_str(), typ.c_str());
}
}
- virtual ~CGIResolver()
+ ~CGIResolver()
{
User* them = ServerInstance->FindUUID(theiruid);
if (!them)
{
CommandWebirc cmd;
LocalIntExt waiting;
+ dynamic_reference<DNS::Manager> DNS;
static void RecheckClass(LocalUser* user)
{
user->CheckClass();
}
- static void ChangeIP(LocalUser* user, const std::string& newip)
- {
- ServerInstance->Users->RemoveCloneCounts(user);
- user->SetClientIP(newip.c_str());
- ServerInstance->Users->AddLocalClone(user);
- ServerInstance->Users->AddGlobalClone(user);
- }
-
void HandleIdentOrPass(LocalUser* user, const std::string& newip, bool was_pass)
{
cmd.realhost.set(user, user->host);
user->host = user->dhost = user->GetIPString();
user->InvalidateCache();
RecheckClass(user);
+
// Don't create the resolver if the core couldn't put the user in a connect class or when dns is disabled
- if (user->quitting || ServerInstance->Config->NoUserDns)
+ if (user->quitting || !DNS || !user->MyClass->resolvehostnames)
return;
+ CGIResolver* r = new CGIResolver(*this->DNS, this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), waiting);
try
{
- bool cached;
- CGIResolver* r = new CGIResolver(this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), cached, waiting);
waiting.set(user, waiting.get(user) + 1);
- ServerInstance->AddResolver(r, cached);
+ this->DNS->Process(r);
}
- catch (...)
+ catch (DNS::Exception &ex)
{
+ int count = waiting.get(user);
+ if (count)
+ waiting.set(user, count - 1);
+ delete r;
if (cmd.notify)
- ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str());
+ ServerInstance->SNO->WriteToSnoMask('w', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname; %s", user->nick.c_str(), user->host.c_str(), ex.GetReason().c_str());
}
}
public:
- ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this)
+ ModuleCgiIRC()
+ : cmd(this)
+ , waiting("cgiirc-delay", this)
+ , DNS(this, "DNS")
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
- ServiceProvider* providerlist[] = { &cmd, &cmd.realhost, &cmd.realip, &cmd.webirc_hostname, &cmd.webirc_ip, &waiting };
- ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
-
- Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ServerInstance->SNO->EnableSnomask('w', "CGIIRC");
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
cmd.Hosts.clear();
{
if (type == "webirc" && password.empty())
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
}
else
{
else
{
cgitype = PASS;
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:type> value in config: %s, setting it to \"pass\"", type.c_str());
}
cmd.Hosts.push_back(CGIhost(hostmask, cgitype, password));
}
else
{
- ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
continue;
}
}
}
- ModResult OnCheckReady(LocalUser *user)
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
if (waiting.get(user))
return MOD_RES_DENY;
- std::string *webirc_ip = cmd.webirc_ip.get(user);
- if (!webirc_ip)
+ if (!cmd.realip.get(user))
return MOD_RES_PASSTHRU;
- ChangeIP(user, *webirc_ip);
-
- std::string* webirc_hostname = cmd.webirc_hostname.get(user);
- user->host = user->dhost = (webirc_hostname ? *webirc_hostname : user->GetIPString());
- user->InvalidateCache();
-
RecheckClass(user);
if (user->quitting)
return MOD_RES_DENY;
if (user->quitting)
return MOD_RES_DENY;
- cmd.webirc_hostname.unset(user);
- cmd.webirc_ip.unset(user);
-
return MOD_RES_PASSTHRU;
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
for(CGIHostlist::iterator iter = cmd.Hosts.begin(); iter != cmd.Hosts.end(); iter++)
{
bool IsValidHost(const std::string &host)
{
- if(!host.size() || host.size() > 64)
+ if(!host.size() || host.size() > ServerInstance->Config->Limits.MaxHost)
return false;
for(unsigned int i = 0; i < host.size(); i++)
return true;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Change user's hosts connecting from known CGI:IRC hosts",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleCgiIRC)
#include "inspircd.h"
-/* $ModDesc: Provides snomasks 'j' and 'J', to which notices about newly created channels are sent */
-
class ModuleChanCreate : public Module
{
- private:
public:
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->SNO->EnableSnomask('j', "CHANCREATE");
- Implementation eventlist[] = { I_OnUserJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides snomasks 'j' and 'J', to which notices about newly created channels are sent",VF_VENDOR);
}
-
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except) CXX11_OVERRIDE
{
if ((created) && (IS_LOCAL(memb->user)))
{
*/
-/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
-
-#define _CRT_SECURE_NO_DEPRECATE
-#define _SCL_SECURE_NO_DEPRECATE
-
#include "inspircd.h"
-#include "u_listmode.h"
+#include "listmode.h"
/** Handles channel mode +g
*/
public:
ChanFilter(Module* Creator) : ListModeBase(Creator, "filter", 'g', "End of channel spamfilter list", 941, 940, false, "chanfilter") { }
- virtual bool ValidateParam(User* user, Channel* chan, std::string &word)
+ bool ValidateParam(User* user, Channel* chan, std::string &word)
{
- if ((word.length() > 35) || (word.empty()))
+ if (word.length() > 35)
{
- user->WriteNumeric(935, "%s %s %s :word is too %s for censor list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+ user->WriteNumeric(935, "%s %s :word is too long for censor list", chan->name.c_str(), word.c_str());
return false;
}
return true;
}
- virtual bool TellListTooLong(User* user, Channel* chan, std::string &word)
+ void TellListTooLong(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(939, "%s %s %s :Channel spamfilter list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
- return true;
+ user->WriteNumeric(939, "%s %s :Channel spamfilter list is full", chan->name.c_str(), word.c_str());
}
- virtual void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
+ void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(937, "%s %s :The word %s is already on the spamfilter list",user->nick.c_str(), chan->name.c_str(), word.c_str());
+ user->WriteNumeric(937, "%s :The word %s is already on the spamfilter list", chan->name.c_str(), word.c_str());
}
- virtual void TellNotSet(User* user, Channel* chan, std::string &word)
+ void TellNotSet(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(938, "%s %s :No such spamfilter word is set",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(938, "%s :No such spamfilter word is set", chan->name.c_str());
}
};
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cf);
-
- cf.DoImplements(this);
- Implementation eventlist[] = { I_OnRehash, I_OnUserPreMessage, I_OnUserPreNotice, I_OnSyncChannel };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
hidemask = ServerInstance->Config->ConfValue("chanfilter")->getBool("hidemask");
cf.DoRehash();
}
- virtual ModResult ProcessMessages(User* user,Channel* chan,std::string &text)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
+ if (target_type != TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
+
+ Channel* chan = static_cast<Channel*>(dest);
ModResult res = ServerInstance->OnCheckExemption(user,chan,"filter");
if (!IS_LOCAL(user) || res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- modelist* list = cf.extItem.get(chan);
+ ListModeBase::ModeList* list = cf.GetList(chan);
if (list)
{
- for (modelist::iterator i = list->begin(); i != list->end(); i++)
+ for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); i++)
{
if (InspIRCd::Match(text, i->mask))
{
if (hidemask)
- user->WriteNumeric(404, "%s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (your message contained a censored word)", chan->name.c_str());
else
- user->WriteNumeric(404, "%s %s %s :Cannot send to channel (your message contained a censored word)",user->nick.c_str(), chan->name.c_str(), i->mask.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Cannot send to channel (your message contained a censored word)", chan->name.c_str(), i->mask.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- {
- return ProcessMessages(user,(Channel*)dest,text);
- }
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- cf.DoSyncChannel(chan, proto, opaque);
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel-specific censor lists (like mode +G but varies from channel to channel)", VF_VENDOR);
}
-
- virtual ~ModuleChanFilter()
- {
- }
};
MODULE_INIT(ModuleChanFilter)
#include "inspircd.h"
-/* $ModDesc: Provides channel history for a given number of lines */
-
struct HistoryItem
{
time_t ts;
{
std::deque<HistoryItem> lines;
unsigned int maxlen, maxtime;
- HistoryList(unsigned int len, unsigned int time) : maxlen(len), maxtime(time) {}
+ std::string param;
+
+ HistoryList(unsigned int len, unsigned int time, const std::string& oparam)
+ : maxlen(len), maxtime(time), param(oparam) { }
};
-class HistoryMode : public ModeHandler
+class HistoryMode : public ParamMode<HistoryMode, SimpleExtItem<HistoryList> >
{
bool IsValidDuration(const std::string& duration)
{
}
public:
- SimpleExtItem<HistoryList> ext;
unsigned int maxlines;
- HistoryMode(Module* Creator) : ModeHandler(Creator, "history", 'H', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("history", Creator) { }
+ HistoryMode(Module* Creator)
+ : ParamMode<HistoryMode, SimpleExtItem<HistoryList> >(Creator, "history", 'H')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if (colon == std::string::npos)
+ return MODEACTION_DENY;
+
+ std::string duration = parameter.substr(colon+1);
+ if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
+ return MODEACTION_DENY;
+
+ unsigned int len = ConvToInt(parameter.substr(0, colon));
+ int time = InspIRCd::Duration(duration);
+ if (len == 0 || time < 0)
+ return MODEACTION_DENY;
+ if (len > maxlines && IS_LOCAL(source))
+ return MODEACTION_DENY;
+ if (len > maxlines)
+ len = maxlines;
+
+ HistoryList* history = ext.get(channel);
+ if (history)
{
- std::string::size_type colon = parameter.find(':');
- if (colon == std::string::npos)
- return MODEACTION_DENY;
-
- std::string duration = parameter.substr(colon+1);
- if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
- return MODEACTION_DENY;
-
- unsigned int len = ConvToInt(parameter.substr(0, colon));
- int time = ServerInstance->Duration(duration);
- if (len == 0 || time < 0)
- return MODEACTION_DENY;
- if (len > maxlines && IS_LOCAL(source))
- return MODEACTION_DENY;
- if (len > maxlines)
- len = maxlines;
- if (parameter == channel->GetModeParameter(this))
- return MODEACTION_DENY;
-
- HistoryList* history = ext.get(channel);
- if (history)
- {
- // Shrink the list if the new line number limit is lower than the old one
- if (len < history->lines.size())
- history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
+ // Shrink the list if the new line number limit is lower than the old one
+ if (len < history->lines.size())
+ history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
- history->maxlen = len;
- history->maxtime = time;
- }
- else
- {
- ext.set(channel, new HistoryList(len, time));
- }
- channel->SetModeParam('H', parameter);
+ history->maxlen = len;
+ history->maxtime = time;
+ history->param = parameter;
}
else
{
- if (!channel->IsModeSet('H'))
- return MODEACTION_DENY;
- ext.unset(channel);
- channel->SetModeParam('H', "");
+ ext.set(channel, new HistoryList(len, time, parameter));
}
return MODEACTION_ALLOW;
}
+
+ void SerializeParam(Channel* chan, const HistoryList* history, std::string& out)
+ {
+ out.append(history->param);
+ }
};
class ModuleChanHistory : public Module
{
HistoryMode m;
bool sendnotice;
+ UserModeReference botmode;
+ bool dobots;
public:
- ModuleChanHistory() : m(this)
- {
- }
-
- void init()
+ ModuleChanHistory() : m(this), botmode(this, "bot")
{
- ServerInstance->Modules->AddService(m);
- ServerInstance->Modules->AddService(m.ext);
-
- Implementation eventlist[] = { I_OnPostJoin, I_OnUserMessage, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- void OnRehash(User*)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("chanhistory");
m.maxlines = tag->getInt("maxlines", 50);
sendnotice = tag->getBool("notice", true);
+ dobots = tag->getBool("bots", true);
}
- void OnUserMessage(User* user,void* dest,int target_type, const std::string &text, char status, const CUList&)
+ void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList&, MessageType msgtype) CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL && status == 0)
+ if ((target_type == TYPE_CHANNEL) && (status == 0) && (msgtype == MSG_PRIVMSG))
{
Channel* c = (Channel*)dest;
HistoryList* list = m.ext.get(c);
if (list)
{
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, ":%s PRIVMSG %s :%s",
- user->GetFullHost().c_str(), c->name.c_str(), text.c_str());
- list->lines.push_back(HistoryItem(buf));
+ const std::string line = ":" + user->GetFullHost() + " PRIVMSG " + c->name + " :" + text;
+ list->lines.push_back(HistoryItem(line));
if (list->lines.size() > list->maxlen)
list->lines.pop_front();
}
}
}
- void OnPostJoin(Membership* memb)
+ void OnPostJoin(Membership* memb) CXX11_OVERRIDE
{
if (IS_REMOTE(memb->user))
return;
+ if (memb->user->IsModeSet(botmode) && !dobots)
+ return;
+
HistoryList* list = m.ext.get(memb->chan);
if (!list)
return;
if (sendnotice)
{
- memb->user->WriteServ("NOTICE %s :Replaying up to %d lines of pre-join history spanning up to %d seconds",
- memb->chan->name.c_str(), list->maxlen, list->maxtime);
+ memb->user->WriteNotice("Replaying up to " + ConvToStr(list->maxlen) + " lines of pre-join history spanning up to " + ConvToStr(list->maxtime) + " seconds");
}
for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel history replayed on join", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Logs snomask output to channel(s). */
-
class ModuleChanLog : public Module
{
- private:
/*
* Multimap so people can redirect a snomask to multiple channels.
*/
ChanLogTargets logstreams;
public:
- void init()
- {
- Implementation eventlist[] = { I_OnRehash, I_OnSendSnotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- virtual ~ModuleChanLog()
- {
- }
-
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string snomasks;
std::string channel;
if (channel.empty() || snomasks.empty())
{
- ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Malformed chanlog tag, ignoring");
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Malformed chanlog tag, ignoring");
continue;
}
for (std::string::const_iterator it = snomasks.begin(); it != snomasks.end(); it++)
{
logstreams.insert(std::make_pair(*it, channel));
- ServerInstance->Logs->Log("m_chanlog", DEFAULT, "Logging %c to %s", *it, channel.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Logging %c to %s", *it, channel.c_str());
}
}
}
- virtual ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg)
+ ModResult OnSendSnotice(char &sno, std::string &desc, const std::string &msg) CXX11_OVERRIDE
{
std::pair<ChanLogTargets::const_iterator, ChanLogTargets::const_iterator> itpair = logstreams.equal_range(sno);
if (itpair.first == itpair.second)
return MOD_RES_PASSTHRU;
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "\2%s\2: %s", desc.c_str(), msg.c_str());
+ const std::string snotice = "\2" + desc + "\2: " + msg;
for (ChanLogTargets::const_iterator it = itpair.first; it != itpair.second; ++it)
{
Channel *c = ServerInstance->FindChan(it->second);
if (c)
{
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
- ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
+ c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), snotice.c_str());
+ ServerInstance->PI->SendMessage(c, 0, snotice);
}
}
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Logs snomask output to channel(s).", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleChanLog)
-
-
-
-
-
-
-
-
-
-/*
- * This is for the "old" chanlog module which intercepted messages going to the logfile..
- * I don't consider it all that useful, and it's quite dangerous if setup incorrectly, so
- * this is defined out but left intact in case someone wants to develop it further someday.
- *
- * -- w00t (aug 23rd, 2008)
- */
-#define OLD_CHANLOG 0
-
-#if OLD_CHANLOG
-class ChannelLogStream : public LogStream
-{
- private:
- std::string channel;
-
- public:
- ChannelLogStream(int loglevel, const std::string &chan) : LogStream(loglevel), channel(chan)
- {
- }
-
- virtual void OnLog(int loglevel, const std::string &type, const std::string &msg)
- {
- Channel *c = ServerInstance->FindChan(channel);
- static bool Logging = false;
-
- if (loglevel < this->loglvl)
- return;
-
- if (Logging)
- return;
-
- if (c)
- {
- Logging = true; // this avoids (rare chance) loops with logging server IO on networks
- char buf[MAXBUF];
- snprintf(buf, MAXBUF, "\2%s\2: %s", type.c_str(), msg.c_str());
-
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), buf);
- ServerInstance->PI->SendChannelPrivmsg(c, 0, buf);
- Logging = false;
- }
- }
-};
-#endif
-
#include "inspircd.h"
-/* $ModDesc: Implements config tags which allow changing characters allowed in channel names */
-
static std::bitset<256> allowedmap;
-class NewIsChannelHandler : public HandlerBase2<bool, const char*, size_t>
+class NewIsChannelHandler : public HandlerBase1<bool, const std::string&>
{
public:
- NewIsChannelHandler() { }
- virtual ~NewIsChannelHandler() { }
- virtual bool Call(const char*, size_t);
+ bool Call(const std::string&);
};
-bool NewIsChannelHandler::Call(const char* c, size_t max)
+bool NewIsChannelHandler::Call(const std::string& channame)
{
- /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
- if (!c || *c++ != '#')
+ if (channame.empty() || channame.length() > ServerInstance->Config->Limits.ChanMax || channame[0] != '#')
+ return false;
+
+ for (std::string::const_iterator c = channame.begin(); c != channame.end(); ++c)
+ {
+ unsigned int i = *c & 0xFF;
+ if (!allowedmap[i])
return false;
+ }
- while (*c && --max)
- {
- unsigned int i = *c++ & 0xFF;
- if (!allowedmap[i])
- return false;
- }
- // a name of exactly max length will have max = 1 here; the null does not trigger --max
- return max;
+ return true;
}
class ModuleChannelNames : public Module
{
- private:
NewIsChannelHandler myhandler;
- caller2<bool, const char*, size_t> rememberer;
+ caller1<bool, const std::string&> rememberer;
bool badchan;
+ ChanModeReference permchannelmode;
public:
- ModuleChannelNames() : rememberer(ServerInstance->IsChannel), badchan(false)
+ ModuleChannelNames()
+ : rememberer(ServerInstance->IsChannel)
+ , badchan(false)
+ , permchannelmode(this, "permanent")
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->IsChannel = &myhandler;
- Implementation eventlist[] = { I_OnRehash, I_OnUserKick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
void ValidateChans()
{
+ Modes::ChangeList removepermchan;
+
badchan = true;
- std::vector<Channel*> chanvec;
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i)
- {
- if (!ServerInstance->IsChannel(i->second->name.c_str(), MAXBUF))
- chanvec.push_back(i->second);
- }
- std::vector<Channel*>::reverse_iterator c2 = chanvec.rbegin();
- while (c2 != chanvec.rend())
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
{
- Channel* c = *c2++;
- if (c->IsModeSet('P') && c->GetUserCounter())
- {
- std::vector<std::string> modes;
- modes.push_back(c->name);
- modes.push_back("-P");
+ Channel* c = i->second;
+ // Move iterator before we begin kicking
+ ++i;
+ if (ServerInstance->IsChannel(c->name))
+ continue; // The name of this channel is still valid
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
+ if (c->IsModeSet(permchannelmode) && c->GetUserCounter())
+ {
+ removepermchan.clear();
+ removepermchan.push_remove(*permchannelmode);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, removepermchan);
}
- const UserMembList* users = c->GetUsers();
- for(UserMembCIter j = users->begin(); j != users->end(); )
+
+ Channel::MemberMap& users = c->userlist;
+ for (Channel::MemberMap::iterator j = users.begin(); j != users.end(); )
{
if (IS_LOCAL(j->first))
{
// KickUser invalidates the iterator
- UserMembCIter it = j++;
- c->KickUser(ServerInstance->FakeClient, it->first, "Channel name no longer valid");
+ Channel::MemberMap::iterator it = j++;
+ c->KickUser(ServerInstance->FakeClient, it, "Channel name no longer valid");
}
else
++j;
badchan = false;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("channames");
std::string denyToken = tag->getString("denyrange");
ValidateChans();
}
- virtual void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list) CXX11_OVERRIDE
{
if (badchan)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
if (i->first != memb->user)
except_list.insert(i->first);
}
}
- virtual ~ModuleChannelNames()
+ CullResult cull() CXX11_OVERRIDE
{
ServerInstance->IsChannel = rememberer;
ValidateChans();
+ return Module::cull();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements config tags which allow changing characters allowed in channel names", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Implements extban +b j: - matching channel bans */
-
class ModuleBadChannelExtban : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleBadChannelExtban()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Extban 'j' - channel status/join ban", VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'j') && (mask[1] == ':'))
{
rm = mask.substr(3);
status = mh->GetModeChar();
}
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- if (InspIRCd::Match((**i).name, rm))
+ if (InspIRCd::Match((*i)->chan->name, rm))
{
- if (status)
- {
- Membership* memb = (**i).GetUser(user);
- if (memb && memb->hasMode(status))
- return MOD_RES_DENY;
- }
- else
+ if (!status || (*i)->hasMode(status))
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('j');
+ tokens["EXTBAN"].push_back('j');
}
};
-
MODULE_INIT(ModuleBadChannelExtban)
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006-2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/* $ModDesc: Provides channel modes +a and +q */
-
-#define PROTECT_VALUE 40000
-#define FOUNDER_VALUE 50000
-
-struct ChanProtectSettings
-{
- bool DeprivSelf;
- bool DeprivOthers;
- bool FirstInGetsFounder;
- bool booting;
- ChanProtectSettings() : booting(true) {}
-};
-
-static ChanProtectSettings settings;
-
-/** Handles basic operation of +qa channel modes
- */
-class FounderProtectBase
-{
- private:
- const std::string type;
- const char mode;
- const int list;
- const int end;
- public:
- FounderProtectBase(char Mode, const std::string &mtype, int l, int e) :
- type(mtype), mode(Mode), list(l), end(e)
- {
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode(mode))
- {
- if (stack)
- stack->Push(mode, i->first->nick);
- else
- modestack.Push(mode, i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- const UserMembList* cl = channel->GetUsers();
- for (UserMembCIter i = cl->begin(); i != cl->end(); ++i)
- {
- if (i->second->hasMode(mode))
- {
- user->WriteServ("%d %s %s %s", list, user->nick.c_str(), channel->name.c_str(), i->first->nick.c_str());
- }
- }
- user->WriteServ("%d %s %s :End of channel %s list", end, user->nick.c_str(), channel->name.c_str(), type.c_str());
- }
-
- bool CanRemoveOthers(User* u1, Channel* c)
- {
- Membership* m1 = c->GetUser(u1);
- return (settings.DeprivOthers && m1 && m1->hasMode(mode));
- }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +q
- */
-class ChanFounder : public ModeHandler, public FounderProtectBase
-{
- public:
- ChanFounder(Module* Creator)
- : ModeHandler(Creator, "founder", 'q', PARAM_ALWAYS, MODETYPE_CHANNEL),
- FounderProtectBase('q', "founder", 386, 387)
- {
- ModeHandler::list = true;
- levelrequired = FOUNDER_VALUE;
- m_paramtype = TR_NICK;
- }
-
- void setPrefix(int pfx)
- {
- prefix = pfx;
- }
-
- unsigned int GetPrefixRank()
- {
- return FOUNDER_VALUE;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- FounderProtectBase::RemoveMode(channel, stack);
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding)
- {
- User* theuser = ServerInstance->FindNick(parameter);
- // remove own privs?
- if (source == theuser && !adding && settings.DeprivSelf)
- return MOD_RES_ALLOW;
-
- if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
- {
- return MOD_RES_PASSTHRU;
- }
- else
- {
- source->WriteNumeric(468, "%s %s :Only servers may set channel mode +q", source->nick.c_str(), channel->name.c_str());
- return MOD_RES_DENY;
- }
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- FounderProtectBase::DisplayList(user,channel);
- }
-};
-
-/** Abstraction of FounderProtectBase for channel mode +a
- */
-class ChanProtect : public ModeHandler, public FounderProtectBase
-{
- public:
- ChanProtect(Module* Creator)
- : ModeHandler(Creator, "admin", 'a', PARAM_ALWAYS, MODETYPE_CHANNEL),
- FounderProtectBase('a',"protected user", 388, 389)
- {
- ModeHandler::list = true;
- levelrequired = PROTECT_VALUE;
- m_paramtype = TR_NICK;
- }
-
- void setPrefix(int pfx)
- {
- prefix = pfx;
- }
-
-
- unsigned int GetPrefixRank()
- {
- return PROTECT_VALUE;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- FounderProtectBase::RemoveMode(channel, stack);
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding)
- {
- User* theuser = ServerInstance->FindNick(parameter);
- // source has +q
- if (channel->GetPrefixValue(source) > PROTECT_VALUE)
- return MOD_RES_ALLOW;
-
- // removing own privs?
- if (source == theuser && !adding && settings.DeprivSelf)
- return MOD_RES_ALLOW;
-
- if (!adding && FounderProtectBase::CanRemoveOthers(source, channel))
- {
- return MOD_RES_PASSTHRU;
- }
- else
- {
- source->WriteNumeric(482, "%s %s :You are not a channel founder", source->nick.c_str(), channel->name.c_str());
- return MOD_RES_DENY;
- }
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
- void DisplayList(User* user, Channel* channel)
- {
- FounderProtectBase::DisplayList(user, channel);
- }
-
-};
-
-class ModuleChanProtect : public Module
-{
- ChanProtect cp;
- ChanFounder cf;
- public:
- ModuleChanProtect() : cp(this), cf(this)
- {
- }
-
- void init()
- {
- /* Load config stuff */
- LoadSettings();
- settings.booting = false;
-
- ServerInstance->Modules->AddService(cf);
- ServerInstance->Modules->AddService(cp);
-
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void LoadSettings()
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("chanprotect");
-
- settings.FirstInGetsFounder = tag->getBool("noservices");
-
- std::string qpre = tag->getString("qprefix");
- char QPrefix = qpre.empty() ? 0 : qpre[0];
-
- std::string apre = tag->getString("aprefix");
- char APrefix = apre.empty() ? 0 : apre[0];
-
- if ((APrefix && QPrefix) && APrefix == QPrefix)
- throw ModuleException("What the smeg, why are both your +q and +a prefixes the same character?");
-
- if (settings.booting)
- {
- if (APrefix && ServerInstance->Modes->FindPrefix(APrefix) && ServerInstance->Modes->FindPrefix(APrefix) != &cp)
- throw ModuleException("Looks like the +a prefix you picked for m_chanprotect is already in use. Pick another.");
-
- if (QPrefix && ServerInstance->Modes->FindPrefix(QPrefix) && ServerInstance->Modes->FindPrefix(QPrefix) != &cf)
- throw ModuleException("Looks like the +q prefix you picked for m_chanprotect is already in use. Pick another.");
-
- cp.setPrefix(APrefix);
- cf.setPrefix(QPrefix);
- }
- settings.DeprivSelf = tag->getBool("deprotectself", true);
- settings.DeprivOthers = tag->getBool("deprotectothers", true);
- }
-
- ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
- {
- // if the user is the first user into the channel, mark them as the founder, but only if
- // the config option for it is set
-
- if (settings.FirstInGetsFounder && !chan)
- privs += 'q';
-
- return MOD_RES_PASSTHRU;
- }
-
- Version GetVersion()
- {
- return Version("Founder and Protect modes (+qa)", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleChanProtect)
*/
-/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
-
#include "inspircd.h"
+#include "listmode.h"
/** Handle /CHECK
*/
class CommandCheck : public Command
{
+ UserModeReference snomaskmode;
+
+ std::string GetSnomasks(User* user)
+ {
+ std::string ret;
+ if (snomaskmode)
+ ret = snomaskmode->GetUserParameter(user);
+
+ if (ret.empty())
+ ret = "+";
+ return ret;
+ }
+
+ static void dumpListMode(User* user, const std::string& checkstr, const ListModeBase::ModeList* list)
+ {
+ if (!list)
+ return;
+
+ std::string buf = checkstr + " modelist";
+ const std::string::size_type headlen = buf.length();
+ const size_t maxline = ServerInstance->Config->Limits.MaxLine;
+ for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+ {
+ if (buf.size() + i->mask.size() + 1 > maxline)
+ {
+ user->SendText(buf);
+ buf.erase(headlen);
+ }
+ buf.append(" ").append(i->mask);
+ }
+ if (buf.length() > headlen)
+ user->SendText(buf);
+ }
+
public:
- CommandCheck(Module* parent) : Command(parent,"CHECK", 1)
+ CommandCheck(Module* parent)
+ : Command(parent,"CHECK", 1)
+ , snomaskmode(parent, "snomask")
{
flags_needed = 'o'; syntax = "<nickname>|<ip>|<hostmask>|<channel> <server>";
}
user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost());
user->SendText(checkstr + " realname " + targuser->fullname);
user->SendText(checkstr + " modes +" + targuser->FormatModes());
- user->SendText(checkstr + " snomasks +" + targuser->FormatNoticeMasks());
- user->SendText(checkstr + " server " + targuser->server);
+ user->SendText(checkstr + " snomasks " + GetSnomasks(targuser));
+ user->SendText(checkstr + " server " + targuser->server->GetName());
user->SendText(checkstr + " uid " + targuser->uuid);
user->SendText(checkstr + " signon " + timestring(targuser->signon));
user->SendText(checkstr + " nickts " + timestring(targuser->age));
if (loctarg)
- user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
+ user->SendText(checkstr + " lastmsg " + timestring(loctarg->idle_lastmsg));
- if (IS_AWAY(targuser))
+ if (targuser->IsAway())
{
/* user is away */
user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime));
user->SendText(checkstr + " awaymsg " + targuser->awaymsg);
}
- if (IS_OPER(targuser))
+ if (targuser->IsOper())
{
OperInfo* oper = targuser->oper;
/* user is an oper of type ____ */
- user->SendText(checkstr + " opertype " + oper->NameStr());
+ user->SendText(checkstr + " opertype " + oper->name);
if (loctarg)
{
std::string umodes;
if (loctarg)
{
- user->SendText(checkstr + " clientaddr " + irc::sockets::satouser(loctarg->client_sa));
- user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa));
+ user->SendText(checkstr + " clientaddr " + loctarg->client_sa.str());
+ user->SendText(checkstr + " serveraddr " + loctarg->server_sa.str());
std::string classname = loctarg->GetClass()->name;
if (!classname.empty())
else
user->SendText(checkstr + " onip " + targuser->GetIPString());
- for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++)
+ for (User::ChanList::iterator i = targuser->chans.begin(); i != targuser->chans.end(); i++)
{
- Channel* c = *i;
- chliststr.append(c->GetPrefixChar(targuser)).append(c->name).append(" ");
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+ char prefix = memb->GetPrefixChar();
+ if (prefix)
+ chliststr.push_back(prefix);
+ chliststr.append(c->name).push_back(' ');
}
std::stringstream dump(chliststr);
/* now the ugly bit, spool current members of a channel. :| */
- const UserMembList *ulist= targchan->GetUsers();
+ const Channel::MemberMap& ulist = targchan->GetUsers();
/* note that unlike /names, we do NOT check +i vs in the channel */
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- char tmpbuf[MAXBUF];
/*
- * Unlike Asuka, I define a clone as coming from the same host. --w00t
- */
- snprintf(tmpbuf, MAXBUF, "%-3lu %s%s (%s@%s) %s ", ServerInstance->Users->GlobalCloneCount(i->first), targchan->GetAllPrefixChars(i->first), i->first->nick.c_str(), i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
- user->SendText(checkstr + " member " + tmpbuf);
+ * Unlike Asuka, I define a clone as coming from the same host. --w00t
+ */
+ const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
+ user->SendText("%s member %-3u %s%s (%s@%s) %s ",
+ checkstr.c_str(), clonecount.global,
+ i->second->GetAllPrefixChars(), i->first->nick.c_str(),
+ i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
}
- irc::modestacker modestack(true);
- for(BanList::iterator b = targchan->bans.begin(); b != targchan->bans.end(); ++b)
- {
- modestack.Push('b', b->data);
- }
- std::vector<std::string> stackresult;
- std::vector<TranslateType> dummy;
- while (modestack.GetStackedLine(stackresult))
- {
- creator->ProtoSendMode(user, TYPE_CHANNEL, targchan, stackresult, dummy);
- stackresult.clear();
- }
- FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(targchan,creator,user));
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
+ dumpListMode(user, checkstr, (*i)->GetList(targchan));
+
dumpExt(user, checkstr, targchan);
}
else
long x = 0;
/* hostname or other */
- for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); a++)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator a = users.begin(); a != users.end(); ++a)
{
if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map))
{
}
};
-
class ModuleCheck : public Module
{
- private:
CommandCheck mycommand;
public:
ModuleCheck() : mycommand(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(mycommand);
- }
-
- ~ModuleCheck()
- {
- }
-
- void ProtoSendMode(void* uv, TargetTypeFlags, void*, const std::vector<std::string>& result, const std::vector<TranslateType>&)
- {
- User* user = (User*)uv;
- std::string checkstr(":");
- checkstr.append(ServerInstance->Config->ServerName);
- checkstr.append(" 304 ");
- checkstr.append(user->nick);
- checkstr.append(" :CHECK modelist");
- for(unsigned int i=0; i < result.size(); i++)
- {
- checkstr.append(" ");
- checkstr.append(result[i]);
- }
- user->SendText(checkstr);
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("CHECK command, view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
}
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGHOST command */
-
/** Handle /CHGHOST
*/
class CommandChghost : public Command
{
- private:
char* hostmap;
public:
CommandChghost(Module* Creator, char* hmap) : Command(Creator,"CHGHOST", 2), hostmap(hmap)
allow_empty_last_param = false;
flags_needed = 'o';
syntax = "<nick> <newhost>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
{
const char* x = parameters[1].c_str();
- if (parameters[1].length() > 63)
+ if (parameters[1].length() > ServerInstance->Config->Limits.MaxHost)
{
- user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick.c_str());
+ user->WriteNotice("*** CHGHOST: Host too long");
return CMD_FAILURE;
}
{
if (!hostmap[(unsigned char)*x])
{
- user->WriteServ("NOTICE "+user->nick+" :*** CHGHOST: Invalid characters in hostname");
+ user->WriteNotice("*** CHGHOST: Invalid characters in hostname");
return CMD_FAILURE;
}
}
User* dest = ServerInstance->FindNick(parameters[0]);
// Allow services to change the host of unregistered users
- if ((!dest) || ((dest->registered != REG_ALL) && (!ServerInstance->ULine(user->server))))
+ if ((!dest) || ((dest->registered != REG_ALL) && (!user->server->IsULine())))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- if ((dest->ChangeDisplayedHost(parameters[1].c_str())) && (!ServerInstance->ULine(user->server)))
+ if ((dest->ChangeDisplayedHost(parameters[1])) && (!user->server->IsULine()))
{
// fix by brain - ulines set hosts silently
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost);
{
CommandChghost cmd;
char hostmap[256];
+
public:
ModuleChgHost() : cmd(this, hostmap)
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
hostmap[(unsigned char)*n] = 1;
}
- ~ModuleChgHost()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGHOST command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgHost)
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGIDENT command */
-
/** Handle /CHGIDENT
*/
class CommandChgident : public Command
allow_empty_last_param = false;
flags_needed = 'o';
syntax = "<nick> <newident>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (parameters[1].length() > ServerInstance->Config->Limits.IdentMax)
{
- user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick.c_str());
+ user->WriteNotice("*** CHGIDENT: Ident is too long");
return CMD_FAILURE;
}
- if (!ServerInstance->IsIdent(parameters[1].c_str()))
+ if (!ServerInstance->IsIdent(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick.c_str());
+ user->WriteNotice("*** CHGIDENT: Invalid characters in ident");
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- dest->ChangeIdent(parameters[1].c_str());
+ dest->ChangeIdent(parameters[1]);
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGIDENT to change %s's ident to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str());
}
}
};
-
class ModuleChgIdent : public Module
{
CommandChgident cmd;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleChgIdent()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGIDENT command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgIdent)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for the CHGNAME command */
-
/** Handle /CHGNAME
*/
class CommandChgname : public Command
allow_empty_last_param = false;
flags_needed = 'o';
syntax = "<nick> <newname>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
if ((!dest) || (dest->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (parameters[1].empty())
{
- user->WriteServ("NOTICE %s :*** CHGNAME: GECOS must be specified", user->nick.c_str());
+ user->WriteNotice("*** CHGNAME: GECOS must be specified");
return CMD_FAILURE;
}
if (parameters[1].length() > ServerInstance->Config->Limits.MaxGecos)
{
- user->WriteServ("NOTICE %s :*** CHGNAME: GECOS too long", user->nick.c_str());
+ user->WriteNotice("*** CHGNAME: GECOS too long");
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
- dest->ChangeName(parameters[1].c_str());
+ dest->ChangeName(parameters[1]);
ServerInstance->SNO->WriteGlobalSno('a', "%s used CHGNAME to change %s's GECOS to '%s'", user->nick.c_str(), dest->nick.c_str(), dest->fullname.c_str());
}
}
};
-
class ModuleChgName : public Module
{
CommandChgname cmd;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleChgName()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the CHGNAME command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleChgName)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "xline.h"
+
+class CommandClearChan : public Command
+{
+ public:
+ Channel* activechan;
+
+ CommandClearChan(Module* Creator)
+ : Command(Creator, "CLEARCHAN", 1, 3)
+ {
+ syntax = "<channel> [<KILL|KICK|G|Z>] [<reason>]";
+ flags_needed = 'o';
+
+ // Stop the linking mod from forwarding ENCAP'd CLEARCHAN commands, see below why
+ force_manual_route = true;
+ }
+
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ Channel* chan = activechan = ServerInstance->FindChan(parameters[0]);
+ if (!chan)
+ {
+ user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+ return CMD_FAILURE;
+ }
+
+ // See what method the oper wants to use, default to KILL
+ std::string method("KILL");
+ if (parameters.size() > 1)
+ {
+ method = parameters[1];
+ std::transform(method.begin(), method.end(), method.begin(), ::toupper);
+ }
+
+ XLineFactory* xlf = NULL;
+ bool kick = (method == "KICK");
+ if ((!kick) && (method != "KILL"))
+ {
+ if ((method != "Z") && (method != "G"))
+ {
+ user->WriteNotice("Invalid method for clearing " + chan->name);
+ return CMD_FAILURE;
+ }
+
+ xlf = ServerInstance->XLines->GetFactory(method);
+ if (!xlf)
+ return CMD_FAILURE;
+ }
+
+ const std::string reason = parameters.size() > 2 ? parameters.back() : "Clearing " + chan->name;
+
+ if (!user->server->IsSilentULine())
+ ServerInstance->SNO->WriteToSnoMask((IS_LOCAL(user) ? 'a' : 'A'), user->nick + " has cleared \002" + chan->name + "\002 (" + method + "): " + reason);
+
+ user->WriteNotice("Clearing \002" + chan->name + "\002 (" + method + "): " + reason);
+
+ {
+ // Route this command manually so it is sent before the QUITs we are about to generate.
+ // The idea is that by the time our QUITs reach the next hop, it has already removed all their
+ // clients from the channel, meaning victims on other servers won't see the victims on this
+ // server quitting.
+ std::vector<std::string> eparams;
+ eparams.push_back(chan->name);
+ eparams.push_back(method);
+ eparams.push_back(":");
+ eparams.back().append(reason);
+ ServerInstance->PI->BroadcastEncap(this->name, eparams, user, user);
+ }
+
+ // Attach to the appropriate hook so we're able to hide the QUIT/KICK messages
+ Implementation hook = (kick ? I_OnUserKick : I_OnBuildNeighborList);
+ ServerInstance->Modules->Attach(hook, creator);
+
+ std::string mask;
+ // Now remove all local non-opers from the channel
+ Channel::MemberMap& users = chan->userlist;
+ for (Channel::MemberMap::iterator i = users.begin(); i != users.end(); )
+ {
+ User* curr = i->first;
+ const Channel::MemberMap::iterator currit = i;
+ ++i;
+
+ if (!IS_LOCAL(curr) || curr->IsOper())
+ continue;
+
+ // If kicking users, remove them and skip the QuitUser()
+ if (kick)
+ {
+ chan->KickUser(ServerInstance->FakeClient, currit, reason);
+ continue;
+ }
+
+ // If we are banning users then create the XLine and add it
+ if (xlf)
+ {
+ XLine* xline;
+ try
+ {
+ mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->host);
+ xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
+ }
+ catch (ModuleException& ex)
+ {
+ // Nothing, move on to the next user
+ continue;
+ }
+
+ if (!ServerInstance->XLines->AddLine(xline, user))
+ delete xline;
+ }
+
+ ServerInstance->Users->QuitUser(curr, reason);
+ }
+
+ ServerInstance->Modules->Detach(hook, creator);
+ if (xlf)
+ ServerInstance->XLines->ApplyLines();
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleClearChan : public Module
+{
+ CommandClearChan cmd;
+
+ public:
+ ModuleClearChan()
+ : cmd(this)
+ {
+ }
+
+ void init()
+ {
+ // Only attached while we are working; don't react to events otherwise
+ ServerInstance->Modules->DetachAll(this);
+ }
+
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
+ {
+ bool found = false;
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
+ {
+ if ((*i)->chan == cmd.activechan)
+ {
+ // Don't show the QUIT to anyone in the channel by default
+ include.erase(i);
+ found = true;
+ break;
+ }
+ }
+
+ const Channel::MemberMap& users = cmd.activechan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ LocalUser* curr = IS_LOCAL(i->first);
+ if (!curr)
+ continue;
+
+ if (curr->IsOper())
+ {
+ // If another module has removed the channel we're working on from the list of channels
+ // to consider for sending the QUIT to then don't add exceptions for opers, because the
+ // module before us doesn't want them to see it or added the exceptions already.
+ // If there is a value for this oper in excepts already, this won't overwrite it.
+ if (found)
+ exception.insert(std::make_pair(curr, true));
+ continue;
+ }
+ else if (!include.empty() && curr->chans.size() > 1)
+ {
+ // This is a victim and potentially has another common channel with the user quitting,
+ // add a negative exception overwriting the previous value, if any.
+ exception[curr] = false;
+ }
+ }
+ }
+
+ void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
+ {
+ // Hide the KICK from all non-opers
+ User* leaving = memb->user;
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* curr = i->first;
+ if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
+ excepts.insert(curr);
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Adds /CLEARCHAN that allows opers to masskick, masskill or mass-G/ZLine users on a channel", VF_VENDOR|VF_OPTCOMMON);
+ }
+};
+
+MODULE_INIT(ModuleClearChan)
#include "inspircd.h"
-#include "hash.h"
-
-/* $ModDesc: Provides masking of user hostnames */
+#include "modules/hash.h"
enum CloakMode
{
- /** 1.2-compatible host-based cloak */
- MODE_COMPAT_HOST,
- /** 1.2-compatible IP-only cloak */
- MODE_COMPAT_IPONLY,
/** 2.0 cloak of "half" of the hostname plus the full IP hash */
MODE_HALF_CLOAK,
/** 2.0 cloak of IP hash, split at 2 common CIDR range points */
{
public:
LocalStringExt ext;
-
std::string debounce_uid;
time_t debounce_ts;
int debounce_count;
*/
if (!user)
{
- dest->SetMode('x',adding);
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
debounce_ts = ServerInstance->Time();
}
- if (adding == user->IsModeSet('x'))
+ if (adding == user->IsModeSet(this))
return MODEACTION_DENY;
/* don't allow this user to spam modechanges */
}
if (cloak)
{
- user->ChangeDisplayedHost(cloak->c_str());
- user->SetMode('x',true);
+ user->ChangeDisplayedHost(*cloak);
+ user->SetMode(this, true);
return MODEACTION_ALLOW;
}
else
/* User is removing the mode, so restore their real host
* and make it match the displayed one.
*/
- user->SetMode('x',false);
+ user->SetMode(this, false);
user->ChangeDisplayedHost(user->host.c_str());
return MODEACTION_ALLOW;
}
}
-
};
class CommandCloak : public Command
std::string prefix;
std::string suffix;
std::string key;
- unsigned int compatkey[4];
const char* xtab[4];
dynamic_reference<HashProvider> Hash;
{
}
- void init()
- {
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(cu);
- ServerInstance->Modules->AddService(ck);
- ServerInstance->Modules->AddService(cu.ext);
-
- Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect, I_OnChangeHost };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
/** This function takes a domain name string and returns just the last two domain parts,
* or the last domain part if only two are available. Failing that it just returns what it was given.
*
input.append(1, '\0'); // null does not terminate a C++ string
input.append(item);
- std::string rv = Hash->sum(input).substr(0,len);
+ std::string rv = Hash->Generate(input).substr(0,len);
for(int i=0; i < len; i++)
{
// this discards 3 bits per byte. We have an
return rv;
}
- std::string CompatCloak4(const char* ip)
- {
- irc::sepstream seps(ip, '.');
- std::string octet[4];
- int i[4];
-
- for (int j = 0; j < 4; j++)
- {
- seps.GetToken(octet[j]);
- i[j] = atoi(octet[j].c_str());
- }
-
- octet[3] = octet[0] + "." + octet[1] + "." + octet[2] + "." + octet[3];
- octet[2] = octet[0] + "." + octet[1] + "." + octet[2];
- octet[1] = octet[0] + "." + octet[1];
-
- /* Reset the Hash module and send it our IV */
-
- std::string rv;
-
- /* Send the Hash module a different hex table for each octet group's Hash sum */
- for (int k = 0; k < 4; k++)
- {
- rv.append(Hash->sumIV(compatkey, xtab[(compatkey[k]+i[k]) % 4], octet[k]).substr(0,6));
- if (k < 3)
- rv.append(".");
- }
- /* Stick them all together */
- return rv;
- }
-
- std::string CompatCloak6(const char* ip)
- {
- std::vector<std::string> hashies;
- std::string item;
- int rounds = 0;
-
- /* Reset the Hash module and send it our IV */
-
- for (const char* input = ip; *input; input++)
- {
- item += *input;
- if (item.length() > 7)
- {
- hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
- item.clear();
- }
- rounds++;
- }
- if (!item.empty())
- {
- hashies.push_back(Hash->sumIV(compatkey, xtab[(compatkey[0]+rounds) % 4], item).substr(0,8));
- }
- /* Stick them all together */
- return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
- }
-
std::string SegmentIP(const irc::sockets::sockaddrs& ip, bool full)
{
std::string bindata;
return rv;
}
- ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+ ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
{
LocalUser* lu = IS_LOCAL(user);
if (!lu)
/* Check if they have a cloaked host, but are not using it */
if (cloak && *cloak != user->dhost)
{
- char cmask[MAXBUF];
- snprintf(cmask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), cloak->c_str());
- if (InspIRCd::Match(cmask,mask))
+ const std::string cloakMask = user->nick + "!" + user->ident + "@" + *cloak;
+ if (InspIRCd::Match(cloakMask, mask))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
// this unsets umode +x on every host change. If we are actually doing a +x
// mode change, we will call SetMode back to true AFTER the host change is done.
- void OnChangeHost(User* u, const std::string& host)
+ void OnChangeHost(User* u, const std::string& host) CXX11_OVERRIDE
{
- if(u->IsModeSet('x'))
+ if (u->IsModeSet(cu))
{
- u->SetMode('x', false);
- u->WriteServ("MODE %s -x", u->nick.c_str());
+ u->SetMode(cu, false);
+ u->WriteCommand("MODE", "-" + ConvToStr(cu.GetModeChar()));
}
}
- ~ModuleCloaking()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
std::string testcloak = "broken";
if (Hash)
{
switch (mode)
{
- case MODE_COMPAT_HOST:
- testcloak = prefix + "-" + Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
- break;
- case MODE_COMPAT_IPONLY:
- testcloak = Hash->sumIV(compatkey, xtab[0], "*").substr(0,10);
- break;
case MODE_HALF_CLOAK:
testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
break;
return Version("Provides masking of user hostnames", VF_COMMON|VF_VENDOR, testcloak);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("cloak");
prefix = tag->getString("prefix");
suffix = tag->getString("suffix", ".IP");
std::string modestr = tag->getString("mode");
- if (modestr == "compat-host")
- mode = MODE_COMPAT_HOST;
- else if (modestr == "compat-ip")
- mode = MODE_COMPAT_IPONLY;
- else if (modestr == "half")
+ if (modestr == "half")
mode = MODE_HALF_CLOAK;
else if (modestr == "full")
mode = MODE_OPAQUE;
else
- throw ModuleException("Bad value for <cloak:mode>; must be one of compat-host, compat-ip, half, full");
-
- if (mode == MODE_COMPAT_HOST || mode == MODE_COMPAT_IPONLY)
- {
- bool lowercase = tag->getBool("lowercase");
-
- /* These are *not* using the need_positive parameter of ReadInteger -
- * that will limit the valid values to only the positive values in a
- * signed int. Instead, accept any value that fits into an int and
- * cast it to an unsigned int. That will, a bit oddly, give us the full
- * spectrum of an unsigned integer. - Special
- *
- * We must limit the keys or else we get different results on
- * amd64/x86 boxes. - psychon */
- const unsigned int limit = 0x80000000;
- compatkey[0] = (unsigned int) tag->getInt("key1");
- compatkey[1] = (unsigned int) tag->getInt("key2");
- compatkey[2] = (unsigned int) tag->getInt("key3");
- compatkey[3] = (unsigned int) tag->getInt("key4");
-
- if (!lowercase)
- {
- xtab[0] = "F92E45D871BCA630";
- xtab[1] = "A1B9D80C72E653F4";
- xtab[2] = "1ABC078934DEF562";
- xtab[3] = "ABCDEF5678901234";
- }
- else
- {
- xtab[0] = "f92e45d871bca630";
- xtab[1] = "a1b9d80c72e653f4";
- xtab[2] = "1abc078934def562";
- xtab[3] = "abcdef5678901234";
- }
-
- if (prefix.empty())
- prefix = ServerInstance->Config->Network;
+ throw ModuleException("Bad value for <cloak:mode>; must be half or full");
- if (!compatkey[0] || !compatkey[1] || !compatkey[2] || !compatkey[3] ||
- compatkey[0] >= limit || compatkey[1] >= limit || compatkey[2] >= limit || compatkey[3] >= limit)
- {
- std::string detail;
- if (!compatkey[0] || compatkey[0] >= limit)
- detail = "<cloak:key1> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[1] || compatkey[1] >= limit)
- detail = "<cloak:key2> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[2] || compatkey[2] >= limit)
- detail = "<cloak:key3> is not valid, it may be set to a too high/low value, or it may not exist.";
- else if (!compatkey[3] || compatkey[3] >= limit)
- detail = "<cloak:key4> is not valid, it may be set to a too high/low value, or it may not exist.";
-
- throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED! - " + detail);
- }
- }
- else
- {
- key = tag->getString("key");
- if (key.empty() || key == "secret")
- throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
- }
+ key = tag->getString("key");
+ if (key.empty() || key == "secret")
+ throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
}
std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
switch (mode)
{
- case MODE_COMPAT_HOST:
- {
- if (ipstr != host)
- {
- std::string tail = LastTwoDomainParts(host);
-
- // xtab is not used here due to a bug in 1.2 cloaking
- chost = prefix + "-" + Hash->sumIV(compatkey, "0123456789abcdef", host).substr(0,8) + tail;
-
- /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
- * according to the DNS RFC) then they get cloaked as an IP.
- */
- if (chost.length() <= 64)
- break;
- }
- // fall through to IP cloak
- }
- case MODE_COMPAT_IPONLY:
- if (ip.sa.sa_family == AF_INET6)
- chost = CompatCloak6(ipstr.c_str());
- else
- chost = CompatCloak4(ipstr.c_str());
- break;
case MODE_HALF_CLOAK:
{
if (ipstr != host)
return chost;
}
- void OnUserConnect(LocalUser* dest)
+ void OnUserConnect(LocalUser* dest) CXX11_OVERRIDE
{
std::string* cloak = cu.ext.get(dest);
if (cloak)
else
cloak = mod->GenCloak(sa, "", parameters[0]);
- user->WriteServ("NOTICE %s :*** Cloak for %s is %s", user->nick.c_str(), parameters[0].c_str(), cloak.c_str());
+ user->WriteNotice("*** Cloak for " + parameters[0] + " is " + cloak);
return CMD_SUCCESS;
}
#include "inspircd.h"
-/* $ModDesc: Provides the /CLONES command to retrieve information on clones. */
-
/** Handle /CLONES
*/
class CommandClones : public Command
user->WriteServ(clonesstr + " START");
/* hostname or other */
- // XXX I really don't like marking global_clones public for this. at all. -- w00t
- for (clonemap::iterator x = ServerInstance->Users->global_clones.begin(); x != ServerInstance->Users->global_clones.end(); x++)
+ const UserManager::CloneMap& clonemap = ServerInstance->Users->GetCloneMap();
+ for (UserManager::CloneMap::const_iterator i = clonemap.begin(); i != clonemap.end(); ++i)
{
- if (x->second >= limit)
- user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + x->first.str());
+ const UserManager::CloneCounts& counts = i->second;
+ if (counts.global >= limit)
+ user->WriteServ(clonesstr + " " + ConvToStr(counts.global) + " " + i->first.str());
}
user->WriteServ(clonesstr + " END");
}
};
-
class ModuleClones : public Module
{
- private:
CommandClones cmd;
public:
ModuleClones() : cmd(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleClones()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /CLONES command to retrieve information on clones.", VF_VENDOR);
}
-
-
};
MODULE_INIT(ModuleClones)
#include "inspircd.h"
-/* $ModDesc: Provides /CLOSE functionality */
-
/** Handle /CLOSE
*/
class CommandClose : public Command
/* Command 'close', needs operator */
CommandClose(Module* Creator) : Command(Creator,"CLOSE", 0)
{
- flags_needed = 'o'; }
+ flags_needed = 'o';
+ }
CmdResult Handle (const std::vector<std::string> ¶meters, User *src)
{
std::map<std::string,int> closed;
- for (LocalUserList::const_iterator u = ServerInstance->Users->local_users.begin(); u != ServerInstance->Users->local_users.end(); ++u)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u = list.begin(); u != list.end(); ++u)
{
LocalUser* user = *u;
if (user->registered != REG_ALL)
int total = 0;
for (std::map<std::string,int>::iterator ci = closed.begin(); ci != closed.end(); ci++)
{
- src->WriteServ("NOTICE %s :*** Closed %d unknown connection%s from [%s]",src->nick.c_str(),(*ci).second,((*ci).second>1)?"s":"",(*ci).first.c_str());
- total += (*ci).second;
+ src->WriteNotice("*** Closed " + ConvToStr(ci->second) + " unknown " + (ci->second == 1 ? "connection" : "connections") +
+ " from [" + ci->first + "]");
+ total += ci->second;
}
if (total)
- src->WriteServ("NOTICE %s :*** %i unknown connection%s closed",src->nick.c_str(),total,(total>1)?"s":"");
+ src->WriteNotice("*** " + ConvToStr(total) + " unknown " + (total == 1 ? "connection" : "connections") + " closed");
else
- src->WriteServ("NOTICE %s :*** No unknown connections found",src->nick.c_str());
+ src->WriteNotice("*** No unknown connections found");
return CMD_SUCCESS;
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleClose()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides /CLOSE functionality", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Adds user mode +c, which if set, users must be on a common channel with you to private message you */
-
/** Handles user mode +c
*/
class PrivacyMode : public SimpleUserModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(pm);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePrivacyMode()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds user mode +c, which if set, users must be on a common channel with you to private message you", VF_VENDOR);
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (target_type == TYPE_USER)
{
User* t = (User*)dest;
- if (!IS_OPER(user) && (t->IsModeSet('c')) && (!ServerInstance->ULine(user->server)) && !user->SharesChannelWith(t))
+ if (!user->IsOper() && (t->IsModeSet(pm)) && (!user->server->IsULine()) && !user->SharesChannelWith(t))
{
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user (+c set)", user->nick.c_str(), t->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, "%s :You are not permitted to send private messages to this user (+c set)", t->nick.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
};
-
MODULE_INIT(ModulePrivacyMode)
#include "inspircd.h"
-/* $ModDesc: Forces users to join the specified channel(s) on connect */
+static void JoinChannels(LocalUser* u, const std::string& chanlist)
+{
+ irc::commasepstream chans(chanlist);
+ std::string chan;
+
+ while (chans.GetToken(chan))
+ {
+ if (ServerInstance->IsChannel(chan))
+ Channel::JoinUser(u, chan);
+ }
+}
+
+class JoinTimer : public Timer
+{
+ private:
+ LocalUser* const user;
+ const std::string channels;
+ SimpleExtItem<JoinTimer>& ext;
+
+ public:
+ JoinTimer(LocalUser* u, SimpleExtItem<JoinTimer>& ex, const std::string& chans, unsigned int delay)
+ : Timer(delay, false)
+ , user(u), channels(chans), ext(ex)
+ {
+ ServerInstance->Timers.AddTimer(this);
+ }
+
+ bool Tick(time_t time) CXX11_OVERRIDE
+ {
+ if (user->chans.empty())
+ JoinChannels(user, channels);
+
+ ext.unset(user);
+ return false;
+ }
+};
class ModuleConnJoin : public Module
{
- public:
- void init()
- {
- Implementation eventlist[] = { I_OnPostConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ SimpleExtItem<JoinTimer> ext;
+ std::string defchans;
+ unsigned int defdelay;
- void Prioritize()
- {
- ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
- }
+ public:
+ ModuleConnJoin() : ext("join_timer", this)
+ {
+ }
- Version GetVersion()
- {
- return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
- }
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("autojoin");
+ defchans = tag->getString("channel");
+ defdelay = tag->getInt("delay", 0, 0, 60);
+ }
- void OnPostConnect(User* user)
- {
- if (!IS_LOCAL(user))
- return;
+ void Prioritize()
+ {
+ ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_LAST);
+ }
- std::string chanlist = ServerInstance->Config->ConfValue("autojoin")->getString("channel");
- chanlist = user->GetClass()->config->getString("autojoin", chanlist);
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Forces users to join the specified channel(s) on connect", VF_VENDOR);
+ }
- irc::commasepstream chans(chanlist);
- std::string chan;
+ void OnPostConnect(User* user) CXX11_OVERRIDE
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
+ return;
- while (chans.GetToken(chan))
- {
- if (ServerInstance->IsChannel(chan.c_str(), ServerInstance->Config->Limits.ChanMax))
- Channel::JoinUser(user, chan.c_str(), false, "", false, ServerInstance->Time());
- }
+ std::string chanlist = localuser->GetClass()->config->getString("autojoin");
+ unsigned int chandelay = localuser->GetClass()->config->getInt("autojoindelay", 0, 0, 60);
+
+ if (chanlist.empty())
+ {
+ if (defchans.empty())
+ return;
+ chanlist = defchans;
+ chandelay = defdelay;
}
-};
+ if (!chandelay)
+ JoinChannels(localuser, chanlist);
+ else
+ ext.set(localuser, new JoinTimer(localuser, ext, chanlist, chandelay));
+ }
+};
MODULE_INIT(ModuleConnJoin)
#include "inspircd.h"
-/* $ModDesc: Sets (and unsets) modes on users when they connect */
-
class ModuleModesOnConnect : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnUserConnect, this);
- }
-
void Prioritize()
{
// for things like +x on connect, important, otherwise we have to resort to config order (bleh) -- w00t
ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIORITY_FIRST);
}
- virtual ~ModuleModesOnConnect()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sets (and unsets) modes on users when they connect", VF_VENDOR);
}
- virtual void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
// Backup and zero out the disabled usermodes, so that we can override them here.
char save[64];
std::string buf;
std::stringstream ss(ThisModes);
- std::vector<std::string> tokens;
-
- // split ThisUserModes into modes and mode params
- while (ss >> buf)
- tokens.push_back(buf);
-
std::vector<std::string> modes;
modes.push_back(user->nick);
- modes.push_back(tokens[0]);
- if (tokens.size() > 1)
- {
- // process mode params
- for (unsigned int k = 1; k < tokens.size(); k++)
- {
- modes.push_back(tokens[k]);
- }
- }
+ // split ThisUserModes into modes and mode params
+ while (ss >> buf)
+ modes.push_back(buf);
- ServerInstance->Parser->CallHandler("MODE", modes, user);
+ ServerInstance->Parser.CallHandler("MODE", modes, user);
}
memcpy(ServerInstance->Config->DisabledUModes, save, 64);
#include "inspircd.h"
-/* $ModDesc: Forces connecting clients to send a PONG message back to the server before they can complete their connection */
-
class ModuleWaitPong : public Module
{
bool sendsnotice;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(ext);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserRegister, I_OnCheckReady, I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("waitpong");
sendsnotice = tag->getBool("sendsnotice", true);
killonbadreply = tag->getBool("killonbadreply", true);
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
std::string pingrpl = ServerInstance->GenRandomStr(10);
user->Write("PING :%s", pingrpl.c_str());
if(sendsnotice)
- user->WriteServ("NOTICE %s :*** If you are having problems connecting due to ping timeouts, please type /quote PONG %s or /raw PONG %s now.", user->nick.c_str(), pingrpl.c_str(), pingrpl.c_str());
+ user->WriteNotice("*** If you are having problems connecting due to ping timeouts, please type /quote PONG " + pingrpl + " or /raw PONG " + pingrpl + " now.");
ext.set(user, pingrpl);
return MOD_RES_PASSTHRU;
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser* user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser* user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
if (command == "PONG")
{
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
return ext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
- ~ModuleWaitPong()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Require pong prior to registration", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleWaitPong)
#include "inspircd.h"
#include "xline.h"
-/* $ModDesc: Throttles the connections of IP ranges who try to connect flood. */
-
class ModuleConnectBan : public Module
{
- private:
- clonemap connects;
+ typedef std::map<irc::sockets::cidr_mask, unsigned int> ConnectMap;
+ ConnectMap connects;
unsigned int threshold;
unsigned int banduration;
unsigned int ipv4_cidr;
unsigned int ipv6_cidr;
- public:
- void init()
- {
- Implementation eventlist[] = { I_OnSetUserIP, I_OnGarbageCollect, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
+ std::string banmessage;
- virtual ~ModuleConnectBan()
- {
- }
-
- virtual Version GetVersion()
+ public:
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Throttles the connections of IP ranges who try to connect flood.", VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("connectban");
- ipv4_cidr = tag->getInt("ipv4cidr", 32);
- if (ipv4_cidr == 0)
- ipv4_cidr = 32;
-
- ipv6_cidr = tag->getInt("ipv6cidr", 128);
- if (ipv6_cidr == 0)
- ipv6_cidr = 128;
-
- threshold = tag->getInt("threshold", 10);
- if (threshold == 0)
- threshold = 10;
-
- banduration = ServerInstance->Duration(tag->getString("duration", "10m"));
- if (banduration == 0)
- banduration = 10*60;
+ ipv4_cidr = tag->getInt("ipv4cidr", 32, 1, 32);
+ ipv6_cidr = tag->getInt("ipv6cidr", 128, 1, 128);
+ threshold = tag->getInt("threshold", 10, 1);
+ banduration = tag->getDuration("duration", 10*60, 1);
+ banmessage = tag->getString("banmessage", "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.");
}
- virtual void OnSetUserIP(LocalUser* u)
+ void OnSetUserIP(LocalUser* u) CXX11_OVERRIDE
{
if (u->exempt)
return;
int range = 32;
- clonemap::iterator i;
switch (u->client_sa.sa.sa_family)
{
}
irc::sockets::cidr_mask mask(u->client_sa, range);
- i = connects.find(mask);
+ ConnectMap::iterator i = connects.find(mask);
if (i != connects.end())
{
if (i->second >= threshold)
{
// Create zline for set duration.
- ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.", mask.str());
+ ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, banmessage, mask.str());
if (!ServerInstance->XLines->AddLine(zl, NULL))
{
delete zl;
}
ServerInstance->XLines->ApplyLines();
std::string maskstr = mask.str();
- std::string timestr = ServerInstance->TimeString(zl->expiry);
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
ServerInstance->SNO->WriteGlobalSno('x',"Module m_connectban added Z:line on *@%s to expire on %s: Connect flooding",
maskstr.c_str(), timestr.c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Connect flooding from IP range %s (%d)", maskstr.c_str(), threshold);
}
}
- virtual void OnGarbageCollect()
+ void OnGarbageCollect()
{
- ServerInstance->Logs->Log("m_connectban",DEBUG, "Clearing map.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map.");
connects.clear();
}
};
#include "inspircd.h"
-/* $ModDesc: Connection throttle */
-
class ModuleConnFlood : public Module
{
-private:
int seconds, timeout, boot_wait;
unsigned int conns;
unsigned int maxconns;
{
}
- void init()
- {
- InitConf();
- Implementation eventlist[] = { I_OnRehash, I_OnUserRegister };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Connection throttle", VF_VENDOR);
}
- void InitConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
/* read configuration variables */
ConfigTag* tag = ServerInstance->Config->ConfValue("connflood");
first = ServerInstance->Time();
}
- virtual ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
if (user->exempt)
return MOD_RES_PASSTHRU;
}
return MOD_RES_PASSTHRU;
}
-
- virtual void OnRehash(User* user)
- {
- InitConf();
- }
-
};
MODULE_INIT(ModuleConnFlood)
#include "inspircd.h"
-/* $ModDesc: Allows custom prefix modes to be created. */
-
-class CustomPrefixMode : public ModeHandler
+class CustomPrefixMode : public PrefixMode
{
public:
reference<ConfigTag> tag;
- int rank;
bool depriv;
+
CustomPrefixMode(Module* parent, ConfigTag* Tag)
- : ModeHandler(parent, Tag->getString("name"), 0, PARAM_ALWAYS, MODETYPE_CHANNEL), tag(Tag)
+ : PrefixMode(parent, Tag->getString("name"), 0, Tag->getInt("rank"))
+ , tag(Tag)
{
- list = true;
- m_paramtype = TR_NICK;
std::string v = tag->getString("prefix");
prefix = v.c_str()[0];
v = tag->getString("letter");
mode = v.c_str()[0];
- rank = tag->getInt("rank");
- levelrequired = tag->getInt("ranktoset", rank);
+ levelrequired = tag->getInt("ranktoset", prefixrank);
depriv = tag->getBool("depriv", true);
}
- unsigned int GetPrefixRank()
- {
- return rank;
- }
-
ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
{
if (!adding && src->nick == value && depriv)
return MOD_RES_ALLOW;
return MOD_RES_PASSTHRU;
}
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode(mode))
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- modestack.Push(this->GetModeChar(), i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
- }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
};
class ModuleCustomPrefix : public Module
{
std::vector<CustomPrefixMode*> modes;
public:
- ModuleCustomPrefix()
- {
- }
-
- void init()
+ void init() CXX11_OVERRIDE
{
ConfigTagList tags = ServerInstance->Config->ConfTags("customprefix");
while (tags.first != tags.second)
tags.first++;
CustomPrefixMode* mh = new CustomPrefixMode(this, tag);
modes.push_back(mh);
- if (mh->rank <= 0)
+ if (mh->GetPrefixRank() == 0)
throw ModuleException("Rank must be specified for prefix at " + tag->getTagLocation());
if (!isalpha(mh->GetModeChar()))
throw ModuleException("Mode must be a letter for prefix at " + tag->getTagLocation());
}
catch (ModuleException& e)
{
- throw ModuleException(e.err + " (while creating mode from " + tag->getTagLocation() + ")");
+ throw ModuleException(e.GetReason() + " (while creating mode from " + tag->getTagLocation() + ")");
}
}
}
~ModuleCustomPrefix()
{
- for (std::vector<CustomPrefixMode*>::iterator i = modes.begin(); i != modes.end(); i++)
- delete *i;
+ stdalgo::delete_all(modes);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides custom prefix channel modes", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides the TITLE command which allows setting of CUSTOM WHOIS TITLE line */
-
/** Handle /TITLE
*/
class CommandTitle : public Command
syntax = "<user> <password>";
}
- bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
- {
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
- }
-
CmdResult Handle(const std::vector<std::string> ¶meters, User* user)
{
- char TheHost[MAXBUF];
- char TheIP[MAXBUF];
-
- snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(), user->host.c_str());
- snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(), user->GetIPString());
+ const std::string userHost = user->ident + "@" + user->host;
+ const std::string userIP = user->ident + "@" + user->GetIPString();
ConfigTagList tags = ServerInstance->Config->ConfTags("title");
for (ConfigIter i = tags.first; i != tags.second; ++i)
std::string title = i->second->getString("title");
std::string vhost = i->second->getString("vhost");
- if (Name == parameters[0] && !ServerInstance->PassCompare(user, pass, parameters[1], hash) && OneOfMatches(TheHost,TheIP,host.c_str()) && !title.empty())
+ if (Name == parameters[0] && ServerInstance->PassCompare(user, pass, parameters[1], hash) &&
+ InspIRCd::MatchMask(host, userHost, userIP) && !title.empty())
{
ctitle.set(user, title);
ServerInstance->PI->SendMetaData(user, "ctitle", title);
if (!vhost.empty())
- user->ChangeDisplayedHost(vhost.c_str());
+ user->ChangeDisplayedHost(vhost);
- user->WriteServ("NOTICE %s :Custom title set to '%s'",user->nick.c_str(), title.c_str());
+ user->WriteNotice("Custom title set to '" + title + "'");
return CMD_SUCCESS;
}
}
- user->WriteServ("NOTICE %s :Invalid title credentials",user->nick.c_str());
+ user->WriteNotice("Invalid title credentials");
return CMD_SUCCESS;
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.ctitle);
- ServerInstance->Modules->Attach(I_OnWhoisLine, this);
- }
-
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
if (numeric == 312)
const std::string* ctitle = cmd.ctitle.get(dest);
if (ctitle)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), ctitle->c_str());
+ ServerInstance->SendWhoisLine(user, dest, 320, "%s :%s", dest->nick.c_str(), ctitle->c_str());
}
}
/* Don't block anything */
return MOD_RES_PASSTHRU;
}
- ~ModuleCustomTitle()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Custom Title for users", VF_OPTCOMMON | VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel. */
-
/** Handle /CYCLE
*/
-class CommandCycle : public Command
+class CommandCycle : public SplitCommand
{
public:
- CommandCycle(Module* Creator) : Command(Creator,"CYCLE", 1)
+ CommandCycle(Module* Creator)
+ : SplitCommand(Creator, "CYCLE", 1)
{
Penalty = 3; syntax = "<channel> :[reason]";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
+ CmdResult HandleLocal(const std::vector<std::string> ¶meters, LocalUser* user)
{
Channel* channel = ServerInstance->FindChan(parameters[0]);
- std::string reason = ConvToStr("Cycling");
+ std::string reason = "Cycling";
if (parameters.size() > 1)
{
if (!channel)
{
- user->WriteNumeric(403, "%s %s :No such channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :No such channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (channel->HasUser(user))
{
- /*
- * technically, this is only ever sent locally, but pays to be safe ;p
- */
- if (IS_LOCAL(user))
+ if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
{
- if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user))
- {
- /* banned, boned. drop the message. */
- user->WriteServ("NOTICE "+user->nick+" :*** You may not cycle, as you are banned on channel " + channel->name);
- return CMD_FAILURE;
- }
-
- channel->PartUser(user, reason);
-
- Channel::JoinUser(user, parameters[0].c_str(), true, "", false, ServerInstance->Time());
+ // User is banned, send an error and don't cycle them
+ user->WriteNotice("*** You may not cycle, as you are banned on channel " + channel->name);
+ return CMD_FAILURE;
}
+ channel->PartUser(user, reason);
+ Channel::JoinUser(user, parameters[0], true);
+
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(442, "%s %s :You're not on that channel", user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel", channel->name.c_str());
}
return CMD_FAILURE;
class ModuleCycle : public Module
{
CommandCycle cmd;
+
public:
ModuleCycle()
: cmd(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleCycle()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command CYCLE, acts as a server-side HOP command to part and rejoin a channel.", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleCycle)
#include "inspircd.h"
-/* $ModDesc: Provides support for the /DCCALLOW command */
-
class BannedFileList
{
public:
dccallowlist* dl;
typedef std::vector<BannedFileList> bannedfilelist;
bannedfilelist bfl;
-SimpleExtItem<dccallowlist>* ext;
+typedef SimpleExtItem<dccallowlist> DCCAllowExt;
class CommandDccallow : public Command
{
+ DCCAllowExt& ext;
+
public:
- CommandDccallow(Module* parent) : Command(parent, "DCCALLOW", 0)
+ CommandDccallow(Module* parent, DCCAllowExt& Ext)
+ : Command(parent, "DCCALLOW", 0)
+ , ext(Ext)
{
syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
/* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
}
else
{
- user->WriteNumeric(998, "%s :DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP", user->nick.c_str());
+ user->WriteNumeric(998, ":DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
return CMD_FAILURE;
}
}
if (action == '-')
{
// check if it contains any entries
- dl = ext->get(user);
+ dl = ext.get(user);
if (dl)
{
for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
if (i->nickname == target->nick)
{
dl->erase(i);
- user->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
break;
}
}
{
if (target == user)
{
- user->WriteNumeric(996, "%s %s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str(), user->nick.c_str());
+ user->WriteNumeric(996, "%s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str());
return CMD_FAILURE;
}
- dl = ext->get(user);
+ dl = ext.get(user);
if (!dl)
{
dl = new dccallowlist;
- ext->set(user, dl);
+ ext.set(user, dl);
// add this user to the userlist
ul.push_back(user);
}
{
if (k->nickname == target->nick)
{
- user->WriteNumeric(996, "%s %s :%s is already on your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(996, "%s :%s is already on your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
return CMD_FAILURE;
}
}
std::string mask = target->nick+"!"+target->ident+"@"+target->dhost;
std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
- long length;
+ unsigned long length;
if (parameters.size() < 2)
{
- length = ServerInstance->Duration(default_length);
+ length = InspIRCd::Duration(default_length);
}
else if (!atoi(parameters[1].c_str()))
{
}
else
{
- length = ServerInstance->Duration(parameters[1]);
+ length = InspIRCd::Duration(parameters[1]);
}
- if (!ServerInstance->IsValidMask(mask))
+ if (!InspIRCd::IsValidMask(mask))
{
return CMD_FAILURE;
}
if (length > 0)
{
- user->WriteNumeric(993, "%s %s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), user->nick.c_str(), target->nick.c_str(), length);
+ user->WriteNumeric(993, "%s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), target->nick.c_str(), length);
}
else
{
- user->WriteNumeric(994, "%s %s :Added %s to DCCALLOW list for this session", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+ user->WriteNumeric(994, "%s :Added %s to DCCALLOW list for this session", user->nick.c_str(), target->nick.c_str());
}
/* route it. */
else
{
// nick doesn't exist
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), nick.c_str());
+ user->WriteNumeric(401, "%s :No such nick/channel", nick.c_str());
return CMD_FAILURE;
}
}
void DisplayHelp(User* user)
{
- user->WriteNumeric(998, "%s :DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]", user->nick.c_str());
- user->WriteNumeric(998, "%s :You may allow DCCs from specific users by specifying a", user->nick.c_str());
- user->WriteNumeric(998, "%s :DCC allow for the user you want to receive DCCs from.", user->nick.c_str());
- user->WriteNumeric(998, "%s :For example, to allow the user Brain to send you inspircd.exe", user->nick.c_str());
- user->WriteNumeric(998, "%s :you would type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW +Brain", user->nick.c_str());
- user->WriteNumeric(998, "%s :Brain would then be able to send you files. They would have to", user->nick.c_str());
- user->WriteNumeric(998, "%s :resend the file again if the server gave them an error message", user->nick.c_str());
- user->WriteNumeric(998, "%s :before you added them to your DCCALLOW list.", user->nick.c_str());
- user->WriteNumeric(998, "%s :DCCALLOW entries will be temporary by default, if you want to add", user->nick.c_str());
- user->WriteNumeric(998, "%s :them to your DCCALLOW list until you leave IRC, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW +Brain 0", user->nick.c_str());
- user->WriteNumeric(998, "%s :To remove the user from your DCCALLOW list, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW -Brain", user->nick.c_str());
- user->WriteNumeric(998, "%s :To see the users in your DCCALLOW list, type:", user->nick.c_str());
- user->WriteNumeric(998, "%s :/DCCALLOW LIST", user->nick.c_str());
- user->WriteNumeric(998, "%s :NOTE: If the user leaves IRC or changes their nickname", user->nick.c_str());
- user->WriteNumeric(998, "%s : they will be removed from your DCCALLOW list.", user->nick.c_str());
- user->WriteNumeric(998, "%s : your DCCALLOW list will be deleted when you leave IRC.", user->nick.c_str());
- user->WriteNumeric(999, "%s :End of DCCALLOW HELP", user->nick.c_str());
+ user->WriteNumeric(998, ":DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
+ user->WriteNumeric(998, ":You may allow DCCs from specific users by specifying a");
+ user->WriteNumeric(998, ":DCC allow for the user you want to receive DCCs from.");
+ user->WriteNumeric(998, ":For example, to allow the user Brain to send you inspircd.exe");
+ user->WriteNumeric(998, ":you would type:");
+ user->WriteNumeric(998, ":/DCCALLOW +Brain");
+ user->WriteNumeric(998, ":Brain would then be able to send you files. They would have to");
+ user->WriteNumeric(998, ":resend the file again if the server gave them an error message");
+ user->WriteNumeric(998, ":before you added them to your DCCALLOW list.");
+ user->WriteNumeric(998, ":DCCALLOW entries will be temporary by default, if you want to add");
+ user->WriteNumeric(998, ":them to your DCCALLOW list until you leave IRC, type:");
+ user->WriteNumeric(998, ":/DCCALLOW +Brain 0");
+ user->WriteNumeric(998, ":To remove the user from your DCCALLOW list, type:");
+ user->WriteNumeric(998, ":/DCCALLOW -Brain");
+ user->WriteNumeric(998, ":To see the users in your DCCALLOW list, type:");
+ user->WriteNumeric(998, ":/DCCALLOW LIST");
+ user->WriteNumeric(998, ":NOTE: If the user leaves IRC or changes their nickname");
+ user->WriteNumeric(998, ": they will be removed from your DCCALLOW list.");
+ user->WriteNumeric(998, ": your DCCALLOW list will be deleted when you leave IRC.");
+ user->WriteNumeric(999, ":End of DCCALLOW HELP");
LocalUser* localuser = IS_LOCAL(user);
if (localuser)
void DisplayDCCAllowList(User* user)
{
// display current DCCALLOW list
- user->WriteNumeric(990, "%s :Users on your DCCALLOW list:", user->nick.c_str());
+ user->WriteNumeric(990, ":Users on your DCCALLOW list:");
- dl = ext->get(user);
+ dl = ext.get(user);
if (dl)
{
for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
{
- user->WriteNumeric(991, "%s %s :%s (%s)", user->nick.c_str(), user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
+ user->WriteNumeric(991, "%s :%s (%s)", user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
}
}
- user->WriteNumeric(992, "%s :End of DCCALLOW list", user->nick.c_str());
+ user->WriteNumeric(992, ":End of DCCALLOW list");
}
};
class ModuleDCCAllow : public Module
{
+ DCCAllowExt ext;
CommandDccallow cmd;
- public:
+ public:
ModuleDCCAllow()
- : cmd(this)
- {
- ext = NULL;
- }
-
- void init()
- {
- ext = new SimpleExtItem<dccallowlist>("dccallow", this);
- ServerInstance->Modules->AddService(*ext);
- ServerInstance->Modules->AddService(cmd);
- ReadFileConf();
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserQuit, I_OnUserPostNick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
+ : ext("dccallow", this)
+ , cmd(this, ext)
{
- ReadFileConf();
}
- virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
+ void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
{
- dccallowlist* udl = ext->get(user);
+ dccallowlist* udl = ext.get(user);
// remove their DCCALLOW list if they have one
if (udl)
RemoveNick(user);
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
RemoveNick(user);
}
- virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
- }
-
- virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
{
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl && dl->size())
{
for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
if ((!found) && (defaultaction == "allow"))
return MOD_RES_PASSTHRU;
- user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick.c_str(), u->nick.c_str(), filename.c_str());
- u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), filename.c_str());
- u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+ user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
+ u->WriteNotice(user->nick + " (" + user->ident + "@" + user->dhost + ") attempted to send you a file named " + filename + ", which was blocked.");
+ u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
return MOD_RES_DENY;
}
else if ((type == "CHAT") && (blockchat))
{
- user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick.c_str(), u->nick.c_str());
- u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str());
- u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
+ user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
+ u->WriteNotice(user->nick + " (" + user->ident + "@" + user->dhost + ") attempted to initiate a DCC CHAT session, which was blocked.");
+ u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
return MOD_RES_DENY;
}
}
for (userlist::iterator iter = ul.begin(); iter != ul.end();)
{
User* u = (User*)(*iter);
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl)
{
if (dl->size())
{
if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
{
- u->WriteNumeric(997, "%s %s :DCCALLOW entry for %s has expired", u->nick.c_str(), u->nick.c_str(), iter2->nickname.c_str());
+ u->WriteNumeric(997, "%s :DCCALLOW entry for %s has expired", u->nick.c_str(), iter2->nickname.c_str());
iter2 = dl->erase(iter2);
}
else
for (userlist::iterator iter = ul.begin(); iter != ul.end();)
{
User *u = (User*)(*iter);
- dl = ext->get(u);
+ dl = ext.get(u);
if (dl)
{
if (dl->size())
if (i->nickname == user->nick)
{
- u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
- u->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", u->nick.c_str(), u->nick.c_str(), i->nickname.c_str());
+ u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
+ u->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
dl->erase(i);
break;
}
}
}
- void ReadFileConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
bfl.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
}
}
- virtual ~ModuleDCCAllow()
- {
- delete ext;
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides usermode +d to block channel messages and channel notices */
-
/** User mode +d - filter out channel messages and channel notices
*/
class User_d : public ModeHandler
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
{
+ if (adding == dest->IsModeSet(this))
+ return MODEACTION_DENY;
+
if (adding)
- {
- if (!dest->IsModeSet('d'))
- {
- dest->WriteServ("NOTICE %s :*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode %s -d.", dest->nick.c_str(), dest->nick.c_str());
- dest->SetMode('d',true);
- return MODEACTION_ALLOW;
- }
- }
- else
- {
- if (dest->IsModeSet('d'))
- {
- dest->SetMode('d',false);
- return MODEACTION_ALLOW;
- }
- }
- return MODEACTION_DENY;
+ dest->WriteNotice("*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode " + dest->nick + " -d.");
+
+ dest->SetMode(this, adding);
+ return MODEACTION_ALLOW;
}
};
class ModuleDeaf : public Module
{
User_d m1;
-
std::string deaf_bypasschars;
std::string deaf_bypasschars_uline;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(m1);
-
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("deaf");
deaf_bypasschars = tag->getString("bypasschars");
deaf_bypasschars_uline = tag->getString("bypasscharsuline");
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- {
- Channel* chan = (Channel*)dest;
- if (chan)
- this->BuildDeafList(MSG_NOTICE, chan, user, status, text, exempt_list);
- }
-
- return MOD_RES_PASSTHRU;
- }
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
- if (target_type == TYPE_CHANNEL)
- {
- Channel* chan = (Channel*)dest;
- if (chan)
- this->BuildDeafList(MSG_PRIVMSG, chan, user, status, text, exempt_list);
- }
+ if (target_type != TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
- return MOD_RES_PASSTHRU;
- }
-
- virtual void BuildDeafList(MessageType message_type, Channel* chan, User* sender, char status, const std::string &text, CUList &exempt_list)
- {
- const UserMembList *ulist = chan->GetUsers();
- bool is_a_uline;
- bool is_bypasschar, is_bypasschar_avail;
- bool is_bypasschar_uline, is_bypasschar_uline_avail;
-
- is_bypasschar = is_bypasschar_avail = is_bypasschar_uline = is_bypasschar_uline_avail = 0;
- if (!deaf_bypasschars.empty())
- {
- is_bypasschar_avail = 1;
- if (deaf_bypasschars.find(text[0], 0) != std::string::npos)
- is_bypasschar = 1;
- }
- if (!deaf_bypasschars_uline.empty())
- {
- is_bypasschar_uline_avail = 1;
- if (deaf_bypasschars_uline.find(text[0], 0) != std::string::npos)
- is_bypasschar_uline = 1;
- }
+ Channel* chan = static_cast<Channel*>(dest);
+ bool is_bypasschar = (deaf_bypasschars.find(text[0]) != std::string::npos);
+ bool is_bypasschar_uline = (deaf_bypasschars_uline.find(text[0]) != std::string::npos);
/*
* If we have no bypasschars_uline in config, and this is a bypasschar (regular)
* Than it is obviously going to get through +d, no build required
*/
- if (!is_bypasschar_uline_avail && is_bypasschar)
- return;
+ if (!deaf_bypasschars_uline.empty() && is_bypasschar)
+ return MOD_RES_PASSTHRU;
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
/* not +d ? */
- if (!i->first->IsModeSet('d'))
+ if (!i->first->IsModeSet(m1))
continue; /* deliver message */
/* matched both U-line only and regular bypasses */
if (is_bypasschar && is_bypasschar_uline)
continue; /* deliver message */
- is_a_uline = ServerInstance->ULine(i->first->server);
+ bool is_a_uline = i->first->server->IsULine();
/* matched a U-line only bypass */
if (is_bypasschar_uline && is_a_uline)
continue; /* deliver message */
if (is_bypasschar && !is_a_uline)
continue; /* deliver message */
- if (status && !strchr(chan->GetAllPrefixChars(i->first), status))
+ if (status && !strchr(i->second->GetAllPrefixChars(), status))
continue;
/* don't deliver message! */
exempt_list.insert(i->first);
}
- }
- virtual ~ModuleDeaf()
- {
+ return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides usermode +d to block channel messages and channel notices", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleDeaf)
*/
-/* $ModDesc: Allows for delay-join channels (+D) where users don't appear to join until they speak */
-
#include "inspircd.h"
-#include <stdarg.h>
class DelayJoinMode : public ModeHandler
{
- private:
CUList empty;
public:
DelayJoinMode(Module* Parent) : ModeHandler(Parent, "delayjoin", 'D', PARAM_NONE, MODETYPE_CHANNEL)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(djm);
- ServerInstance->Modules->AddService(unjoined);
- Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart, I_OnUserKick, I_OnBuildNeighborList, I_OnNamesListItem, I_OnText, I_OnRawMode };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
- ~ModuleDelayJoin();
- Version GetVersion();
- void OnNamesListItem(User* issuer, Membership*, std::string &prefixes, std::string &nick);
- void OnUserJoin(Membership*, bool, bool, CUList&);
+ Version GetVersion() CXX11_OVERRIDE;
+ ModResult OnNamesListItem(User* issuer, Membership*, std::string& prefixes, std::string& nick) CXX11_OVERRIDE;
+ void OnUserJoin(Membership*, bool, bool, CUList&) CXX11_OVERRIDE;
void CleanUser(User* user);
- void OnUserPart(Membership*, std::string &partmessage, CUList&);
- void OnUserKick(User* source, Membership*, const std::string &reason, CUList&);
- void OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception);
- void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
- ModResult OnRawMode(User* user, Channel* channel, const char mode, const std::string ¶m, bool adding, int pcnt);
+ void OnUserPart(Membership*, std::string &partmessage, CUList&) CXX11_OVERRIDE;
+ void OnUserKick(User* source, Membership*, const std::string &reason, CUList&) CXX11_OVERRIDE;
+ void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE;
+ void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list) CXX11_OVERRIDE;
+ ModResult OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE;
};
ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
{
/* no change */
- if (channel->IsModeSet('D') == adding)
+ if (channel->IsModeSet(this) == adding)
return MODEACTION_DENY;
if (!adding)
* Make all users visible, as +D is being removed. If we don't do this,
* they remain permanently invisible on this channel!
*/
- const UserMembList* names = channel->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
+ const Channel::MemberMap& users = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
}
- channel->SetMode('D', adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
-ModuleDelayJoin::~ModuleDelayJoin()
-{
-}
-
Version ModuleDelayJoin::GetVersion()
{
return Version("Allows for delay-join channels (+D) where users don't appear to join until they speak", VF_VENDOR);
}
-void ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ModResult ModuleDelayJoin::OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick)
{
/* don't prevent the user from seeing themself */
if (issuer == memb->user)
- return;
+ return MOD_RES_PASSTHRU;
/* If the user is hidden by delayed join, hide them from the NAMES list */
if (unjoined.get(memb))
- nick.clear();
+ return MOD_RES_DENY;
+
+ return MOD_RES_PASSTHRU;
}
static void populate(CUList& except, Membership* memb)
{
- const UserMembList* users = memb->chan->GetUsers();
- for(UserMembCIter i = users->begin(); i != users->end(); i++)
+ const Channel::MemberMap& users = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
if (i->first == memb->user || !IS_LOCAL(i->first))
continue;
void ModuleDelayJoin::OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
{
- if (memb->chan->IsModeSet('D'))
+ if (memb->chan->IsModeSet(djm))
{
unjoined.set(memb, 1);
populate(except, memb);
populate(except, memb);
}
-void ModuleDelayJoin::OnBuildNeighborList(User* source, UserChanList &include, std::map<User*,bool> &exception)
+void ModuleDelayJoin::OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception)
{
- UCListIter i = include.begin();
- while (i != include.end())
+ for (IncludeChanList::iterator i = include.begin(); i != include.end(); )
{
- Channel* c = *i++;
- Membership* memb = c->GetUser(source);
- if (memb && unjoined.get(memb))
- include.erase(c);
+ Membership* memb = *i;
+ if (unjoined.get(memb))
+ i = include.erase(i);
+ else
+ ++i;
}
}
}
/* make the user visible if he receives any mode change */
-ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, const char mode, const std::string ¶m, bool adding, int pcnt)
+ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding)
{
if (!user || !channel || param.empty())
return MOD_RES_PASSTHRU;
- ModeHandler* mh = ServerInstance->Modes->FindMode(mode, MODETYPE_CHANNEL);
// If not a prefix mode then we got nothing to do here
- if (!mh || !mh->GetPrefixRank())
+ if (!mh->IsPrefixMode())
return MOD_RES_PASSTHRU;
User* dest;
#include "inspircd.h"
-/* $ModDesc: Provides channelmode +d <int>, to deny messages to a channel until <int> seconds. */
-
-class DelayMsgMode : public ModeHandler
+class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
{
public:
LocalIntExt jointime;
- DelayMsgMode(Module* Parent) : ModeHandler(Parent, "delaymsg", 'd', PARAM_SETONLY, MODETYPE_CHANNEL)
+ DelayMsgMode(Module* Parent)
+ : ParamMode<DelayMsgMode, LocalIntExt>(Parent, "delaymsg", 'd')
, jointime("delaymsg", Parent)
{
levelrequired = OP_VALUE;
return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
+ ModeAction OnSet(User* source, Channel* chan, std::string& parameter);
+ void OnUnset(User* source, Channel* chan);
+
+ void SerializeParam(Channel* chan, int n, std::string& out)
+ {
+ out += ConvToStr(n);
+ }
};
class ModuleDelayMsg : public Module
{
- private:
DelayMsgMode djm;
+ bool allownotice;
public:
ModuleDelayMsg() : djm(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(djm);
- ServerInstance->Modules->AddService(djm.jointime);
- Implementation eventlist[] = { I_OnUserJoin, I_OnUserPreMessage, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
- Version GetVersion();
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList&);
- ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list);
- ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list);
- void OnRehash(User* user);
+ Version GetVersion() CXX11_OVERRIDE;
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList&) CXX11_OVERRIDE;
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
};
-ModeAction DelayMsgMode::OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ModeAction DelayMsgMode::OnSet(User* source, Channel* chan, std::string& parameter)
{
- if (adding)
- {
- if ((channel->IsModeSet('d')) && (channel->GetModeParameter('d') == parameter))
- return MODEACTION_DENY;
-
- /* Setting a new limit, sanity check */
- long limit = atoi(parameter.c_str());
-
- /* Wrap low values at 32768 */
- if (limit < 0)
- limit = 0x7FFF;
+ // Setting a new limit, sanity check
+ unsigned int limit = ConvToInt(parameter);
+ if (limit == 0)
+ limit = 1;
- parameter = ConvToStr(limit);
- }
- else
- {
- if (!channel->IsModeSet('d'))
- return MODEACTION_DENY;
-
- /*
- * Clean up metadata
- */
- const UserMembList* names = channel->GetUsers();
- for (UserMembCIter n = names->begin(); n != names->end(); ++n)
- jointime.set(n->second, 0);
- }
- channel->SetModeParam('d', adding ? parameter : "");
+ ext.set(chan, limit);
return MODEACTION_ALLOW;
}
+void DelayMsgMode::OnUnset(User* source, Channel* chan)
+{
+ /*
+ * Clean up metadata
+ */
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
+ jointime.set(n->second, 0);
+}
+
Version ModuleDelayMsg::GetVersion()
{
return Version("Provides channelmode +d <int>, to deny messages to a channel until <int> seconds.", VF_VENDOR);
void ModuleDelayMsg::OnUserJoin(Membership* memb, bool sync, bool created, CUList&)
{
- if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet('d')))
+ if ((IS_LOCAL(memb->user)) && (memb->chan->IsModeSet(djm)))
{
djm.jointime.set(memb, ServerInstance->Time());
}
}
-ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
{
/* Server origin */
if ((!user) || (!IS_LOCAL(user)))
return MOD_RES_PASSTHRU;
- if (target_type != TYPE_CHANNEL)
+ if ((target_type != TYPE_CHANNEL) || ((!allownotice) && (msgtype == MSG_NOTICE)))
return MOD_RES_PASSTHRU;
Channel* channel = (Channel*) dest;
if (ts == 0)
return MOD_RES_PASSTHRU;
- std::string len = channel->GetModeParameter('d');
+ int len = djm.ext.get(channel);
- if (ts + atoi(len.c_str()) > ServerInstance->Time())
+ if ((ts + len) > ServerInstance->Time())
{
if (channel->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(404, "%s %s :You must wait %s seconds after joining to send to channel (+d)",
- user->nick.c_str(), channel->name.c_str(), len.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :You must wait %d seconds after joining to send to channel (+d)",
+ channel->name.c_str(), len);
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
-ModResult ModuleDelayMsg::OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list)
-{
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
-}
-
-void ModuleDelayMsg::OnRehash(User* user)
+void ModuleDelayMsg::ReadConfig(ConfigStatus& status)
{
ConfigTag* tag = ServerInstance->Config->ConfValue("delaymsg");
- if (tag->getBool("allownotice", true))
- ServerInstance->Modules->Detach(I_OnUserPreNotice, this);
- else
- ServerInstance->Modules->Attach(I_OnUserPreNotice, this);
+ allownotice = tag->getBool("allownotice", true);
}
MODULE_INIT(ModuleDelayMsg)
-
#include "inspircd.h"
-/* $ModDesc: Implements config tags which allow blocking of joins to channels */
-
class ModuleDenyChannels : public Module
{
+ ChanModeReference redirectmode;
+
public:
- void init()
+ ModuleDenyChannels()
+ : redirectmode(this, "redirect")
{
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
/* check for redirect validity and loops/chains */
ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
if (!redirect.empty())
{
- if (!ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(redirect))
{
- if (user)
- user->WriteServ("NOTICE %s :Invalid badchan redirect '%s'", user->nick.c_str(), redirect.c_str());
+ if (status.srcuser)
+ status.srcuser->WriteNotice("Invalid badchan redirect '" + redirect + "'");
throw ModuleException("Invalid badchan redirect, not a channel");
}
if (!goodchan)
{
/* <badchan:redirect> is a badchan */
- if (user)
- user->WriteServ("NOTICE %s :Badchan %s redirects to badchan %s", user->nick.c_str(), name.c_str(), redirect.c_str());
+ if (status.srcuser)
+ status.srcuser->WriteNotice("Badchan " + name + " redirects to badchan " + redirect);
throw ModuleException("Badchan redirect loop");
}
}
}
}
- virtual ~ModuleDenyChannels()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements config tags which allow blocking of joins to channels", VF_VENDOR);
}
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
ConfigTagList tags = ServerInstance->Config->ConfTags("badchan");
for (ConfigIter j = tags.first; j != tags.second; ++j)
{
if (InspIRCd::Match(cname, j->second->getString("name")))
{
- if (IS_OPER(user) && j->second->getBool("allowopers"))
+ if (user->IsOper() && j->second->getBool("allowopers"))
{
return MOD_RES_PASSTHRU;
}
}
}
- if (ServerInstance->IsChannel(redirect.c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (ServerInstance->IsChannel(redirect))
{
/* simple way to avoid potential loops: don't redirect to +L channels */
Channel *newchan = ServerInstance->FindChan(redirect);
- if ((!newchan) || (!(newchan->IsModeSet('L'))))
+ if ((!newchan) || (!newchan->IsModeSet(redirectmode)))
{
- user->WriteNumeric(926, "%s %s :Channel %s is forbidden, redirecting to %s: %s",user->nick.c_str(),cname,cname,redirect.c_str(), reason.c_str());
- Channel::JoinUser(user,redirect.c_str(),false,"",false,ServerInstance->Time());
+ user->WriteNumeric(926, "%s :Channel %s is forbidden, redirecting to %s: %s", cname.c_str(),cname.c_str(),redirect.c_str(), reason.c_str());
+ Channel::JoinUser(user, redirect);
return MOD_RES_DENY;
}
}
- user->WriteNumeric(926, "%s %s :Channel %s is forbidden: %s",user->nick.c_str(),cname,cname,reason.c_str());
+ user->WriteNumeric(926, "%s :Channel %s is forbidden: %s", cname.c_str(),cname.c_str(),reason.c_str());
return MOD_RES_DENY;
}
}
* Syntax: /DEVOICE <#chan>
*/
-/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
-
#include "inspircd.h"
/** Handle /DEVOICE
CommandDevoice(Module* Creator) : Command(Creator,"DEVOICE", 1)
{
syntax = "<channel>";
- TRANSLATE2(TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
- Channel* c = ServerInstance->FindChan(parameters[0]);
- if (c && c->HasUser(user))
- {
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back("-v");
- modes.push_back(user->nick);
-
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
- return CMD_SUCCESS;
- }
+ std::vector<std::string> modes;
+ modes.push_back(parameters[0]);
+ modes.push_back("-v");
+ modes.push_back(user->nick);
- return CMD_FAILURE;
+ ServerInstance->Parser.CallHandler("MODE", modes, ServerInstance->FakeClient);
+ return CMD_SUCCESS;
}
};
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleDeVoice()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides voiced users with the ability to devoice themselves.", VF_VENDOR);
}
#include "inspircd.h"
#include "xline.h"
-
-/* $ModDesc: Provides handling of DNS blacklists */
+#include "modules/dns.h"
/* Class holding data for a single entry */
class DNSBLConfEntry : public refcountbase
unsigned char records[256];
unsigned long stats_hits, stats_misses;
DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
- ~DNSBLConfEntry() { }
};
/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
*/
-class DNSBLResolver : public Resolver
+class DNSBLResolver : public DNS::Request
{
std::string theiruid;
LocalStringExt& nameExt;
public:
- DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf, bool &cached)
- : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
+ DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf)
+ : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
{
}
/* Note: This may be called multiple times for multiple A record results */
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
{
/* Check the user still exists */
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
+ if (!them)
+ return;
+
+ const DNS::ResourceRecord &ans_record = r->answers[0];
+
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
+
+ unsigned int bitmask = 0, record = 0;
+ bool match = false;
+ in_addr resultip;
+
+ inet_aton(ans_record.rdata.c_str(), &resultip);
+
+ switch (ConfEntry->type)
{
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
- if(result.length())
+ case DNSBLConfEntry::A_BITMASK:
+ bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
+ bitmask &= ConfEntry->bitmask;
+ match = (bitmask != 0);
+ break;
+ case DNSBLConfEntry::A_RECORD:
+ record = resultip.s_addr >> 24; /* Last octet */
+ match = (ConfEntry->records[record] == 1);
+ break;
+ }
+
+ if (match)
+ {
+ std::string reason = ConfEntry->reason;
+ std::string::size_type x = reason.find("%ip%");
+ while (x != std::string::npos)
{
- unsigned int bitmask = 0, record = 0;
- bool match = false;
- in_addr resultip;
+ reason.erase(x, 4);
+ reason.insert(x, them->GetIPString());
+ x = reason.find("%ip%");
+ }
- inet_aton(result.c_str(), &resultip);
+ ConfEntry->stats_hits++;
- switch (ConfEntry->type)
+ switch (ConfEntry->banaction)
+ {
+ case DNSBLConfEntry::I_KILL:
{
- case DNSBLConfEntry::A_BITMASK:
- bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
- bitmask &= ConfEntry->bitmask;
- match = (bitmask != 0);
- break;
- case DNSBLConfEntry::A_RECORD:
- record = resultip.s_addr >> 24; /* Last octet */
- match = (ConfEntry->records[record] == 1);
+ ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
break;
}
-
- if (match)
+ case DNSBLConfEntry::I_MARK:
{
- std::string reason = ConfEntry->reason;
- std::string::size_type x = reason.find("%ip%");
- while (x != std::string::npos)
+ if (!ConfEntry->ident.empty())
{
- reason.erase(x, 4);
- reason.insert(x, them->GetIPString());
- x = reason.find("%ip%");
+ them->WriteNumeric(304, ":Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
+ them->ChangeIdent(ConfEntry->ident);
}
- ConfEntry->stats_hits++;
-
- switch (ConfEntry->banaction)
+ if (!ConfEntry->host.empty())
{
- case DNSBLConfEntry::I_KILL:
- {
- ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
- break;
- }
- case DNSBLConfEntry::I_MARK:
- {
- if (!ConfEntry->ident.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
- them->ChangeIdent(ConfEntry->ident.c_str());
- }
-
- if (!ConfEntry->host.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
- them->ChangeDisplayedHost(ConfEntry->host.c_str());
- }
-
- nameExt.set(them, ConfEntry->name);
- break;
- }
- case DNSBLConfEntry::I_KLINE:
- {
- KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(kl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(kl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete kl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_GLINE:
- {
- GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(gl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(gl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete gl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_ZLINE:
- {
- ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- them->GetIPString());
- if (ServerInstance->XLines->AddLine(zl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(zl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- {
- delete zl;
- return;
- }
- break;
- }
- case DNSBLConfEntry::I_UNKNOWN:
- {
- break;
- }
- break;
+ them->WriteNumeric(304, ":Your host has been set to " + ConfEntry->host + " because you matched " + reason);
+ them->ChangeDisplayedHost(ConfEntry->host);
}
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
+ nameExt.set(them, ConfEntry->name);
+ break;
+ }
+ case DNSBLConfEntry::I_KLINE:
+ {
+ KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(kl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(kl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete kl;
+ return;
+ }
+ break;
+ }
+ case DNSBLConfEntry::I_GLINE:
+ {
+ GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(gl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete gl;
+ return;
+ }
+ break;
+ }
+ case DNSBLConfEntry::I_ZLINE:
+ {
+ ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ them->GetIPString());
+ if (ServerInstance->XLines->AddLine(zl,NULL))
+ {
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ {
+ delete zl;
+ return;
+ }
+ break;
}
- else
- ConfEntry->stats_misses++;
+ case DNSBLConfEntry::I_UNKNOWN:
+ default:
+ break;
}
- else
- ConfEntry->stats_misses++;
+
+ ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
}
+ else
+ ConfEntry->stats_misses++;
}
- virtual void OnError(ResolverError e, const std::string &errormessage)
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE
{
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
- {
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- }
- }
+ if (!them)
+ return;
- virtual ~DNSBLResolver()
- {
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND)
+ ConfEntry->stats_misses++;
}
};
class ModuleDNSBL : public Module
{
std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
+ dynamic_reference<DNS::Manager> DNS;
LocalStringExt nameExt;
LocalIntExt countExt;
return DNSBLConfEntry::I_UNKNOWN;
}
public:
- ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+ ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
- void init()
- {
- ReadConf();
- ServerInstance->Modules->AddService(nameExt);
- ServerInstance->Modules->AddService(countExt);
- Implementation eventlist[] = { I_OnRehash, I_OnSetUserIP, I_OnStats, I_OnSetConnectClass, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides handling of DNS blacklists", VF_VENDOR);
}
/** Fill our conf vector with data
*/
- void ReadConf()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
DNSBLConfEntries.clear();
}
e->banaction = str2banaction(tag->getString("action"));
- e->duration = ServerInstance->Duration(tag->getString("duration", "60"));
+ e->duration = tag->getDuration("duration", 60, 1);
/* Use portparser for record replies */
std::string location = tag->getTagLocation();
ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid banaction", location.c_str());
}
- else if (e->duration <= 0)
- {
- std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid duration", location.c_str());
- }
else
{
if (e->reason.empty())
}
}
- void OnRehash(User* user)
- {
- ReadConf();
- }
-
- void OnSetUserIP(LocalUser* user)
+ void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
{
- if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
+ if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET) || !DNS)
return;
if (user->MyClass)
return;
}
else
- ServerInstance->Logs->Log("m_dnsbl", DEBUG, "User has no connect class in OnSetUserIP");
-
- unsigned char a, b, c, d;
- char reversedipbuf[128];
- std::string reversedip;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
- d = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
- c = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
- b = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
- a = (unsigned char) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+ unsigned int a, b, c, d;
+ d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
+ c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
+ b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
+ a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
- snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
- reversedip = std::string(reversedipbuf);
+ const std::string reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
countExt.set(user, DNSBLConfEntries.size());
// For each DNSBL, we will run through this lookup
- unsigned int i = 0;
- while (i < DNSBLConfEntries.size())
+ for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i)
{
// Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain;
/* now we'd need to fire off lookups for `hostname'. */
- bool cached;
- DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached);
- ServerInstance->AddResolver(r, cached);
+ DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]);
+ try
+ {
+ this->DNS->Process(r);
+ }
+ catch (DNS::Exception &ex)
+ {
+ delete r;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
+ }
+
if (user->quitting)
break;
- i++;
}
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
std::string dnsbl;
if (!myclass->config->readString("dnsbl", dnsbl))
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
-
- ModResult OnCheckReady(LocalUser *user)
+
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
if (countExt.get(user))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
{
if (symbol != 'd')
return MOD_RES_PASSTHRU;
total_hits += (*i)->stats_hits;
total_misses += (*i)->stats_misses;
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
+ results.push_back("304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
}
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
- results.push_back(ServerInstance->Config->ServerName + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
+ results.push_back("304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
+ results.push_back("304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
return MOD_RES_PASSTHRU;
}
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides the ability to allow channel operators to be exempt from certain modes. */
+#include "listmode.h"
/** Handles channel mode +X
*/
bool ValidateParam(User* user, Channel* chan, std::string &word)
{
- // TODO actually make sure there's a prop for this
- if ((word.length() > 35) || (word.empty()))
+ std::string::size_type p = word.find(':');
+ if (p == std::string::npos)
+ {
+ user->WriteNumeric(955, "%s %s :Invalid exemptchanops entry, format is <restriction>:<prefix>", chan->name.c_str(), word.c_str());
+ return false;
+ }
+
+ std::string restriction = word.substr(0, p);
+ // If there is a '-' in the restriction string ignore it and everything after it
+ // to support "auditorium-vis" and "auditorium-see" in m_auditorium
+ p = restriction.find('-');
+ if (p != std::string::npos)
+ restriction.erase(p);
+
+ if (!ServerInstance->Modes->FindMode(restriction, MODETYPE_CHANNEL))
{
- user->WriteNumeric(955, "%s %s %s :word is too %s for exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str(), (word.empty() ? "short" : "long"));
+ user->WriteNumeric(955, "%s %s :Unknown restriction", chan->name.c_str(), restriction.c_str());
return false;
}
return true;
}
- bool TellListTooLong(User* user, Channel* chan, std::string &word)
+ void TellListTooLong(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(959, "%s %s %s :Channel exemptchanops list is full", user->nick.c_str(), chan->name.c_str(), word.c_str());
- return true;
+ user->WriteNumeric(959, "%s %s :Channel exemptchanops list is full", chan->name.c_str(), word.c_str());
}
void TellAlreadyOnList(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(957, "%s %s :The word %s is already on the exemptchanops list",user->nick.c_str(), chan->name.c_str(), word.c_str());
+ user->WriteNumeric(957, "%s :The word %s is already on the exemptchanops list", chan->name.c_str(), word.c_str());
}
void TellNotSet(User* user, Channel* chan, std::string &word)
{
- user->WriteNumeric(958, "%s %s :No such exemptchanops word is set",user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(958, "%s :No such exemptchanops word is set", chan->name.c_str());
}
};
public:
ExemptChanOps ec;
ExemptHandler(Module* me) : ec(me) {}
-
- ModeHandler* FindMode(const std::string& mid)
+
+ PrefixMode* FindMode(const std::string& mid)
{
if (mid.length() == 1)
- return ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL);
- for(char c='A'; c <= 'z'; c++)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
- if (mh && mh->name == mid)
- return mh;
- }
- return NULL;
+ return ServerInstance->Modes->FindPrefixMode(mid[0]);
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(mid, MODETYPE_CHANNEL);
+ return mh ? mh->IsPrefixMode() : NULL;
}
ModResult Call(User* user, Channel* chan, const std::string& restriction)
unsigned int mypfx = chan->GetPrefixValue(user);
std::string minmode;
- modelist* list = ec.extItem.get(chan);
+ ListModeBase::ModeList* list = ec.GetList(chan);
if (list)
{
- for (modelist::iterator i = list->begin(); i != list->end(); ++i)
+ for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); ++i)
{
std::string::size_type pos = (*i).mask.find(':');
if (pos == std::string::npos)
continue;
- if ((*i).mask.substr(0,pos) == restriction)
+ if (!i->mask.compare(0, pos, restriction))
minmode = (*i).mask.substr(pos + 1);
}
}
- ModeHandler* mh = FindMode(minmode);
+ PrefixMode* mh = FindMode(minmode);
if (mh && mypfx >= mh->GetPrefixRank())
return MOD_RES_ALLOW;
if (mh || minmode == "*")
class ModuleExemptChanOps : public Module
{
- std::string defaults;
ExemptHandler eh;
public:
-
ModuleExemptChanOps() : eh(this)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(eh.ec);
- Implementation eventlist[] = { I_OnRehash, I_OnSyncChannel };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
ServerInstance->OnCheckExemption = &eh;
-
- OnRehash(NULL);
}
~ModuleExemptChanOps()
ServerInstance->OnCheckExemption = &ServerInstance->HandleOnCheckExemption;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to allow channel operators to be exempt from certain modes.",VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
eh.ec.DoRehash();
}
-
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- eh.ec.DoSyncChannel(chan, proto, opaque);
- }
};
MODULE_INIT(ModuleExemptChanOps)
#include "inspircd.h"
#include "xline.h"
-#include "m_regex.h"
-
-/* $ModDesc: Text (spam) filtering */
-
-class ModuleFilter;
+#include "modules/regex.h"
enum FilterFlags
{
class FilterResult
{
public:
+ Regex* regex;
std::string freeform;
std::string reason;
FilterAction action;
bool flag_notice;
bool flag_strip_color;
- FilterResult(const std::string& free, const std::string& rea, FilterAction act, long gt, const std::string& fla) :
- freeform(free), reason(rea), action(act), gline_time(gt)
+ FilterResult(dynamic_reference<RegexFactory>& RegexEngine, const std::string& free, const std::string& rea, FilterAction act, long gt, const std::string& fla)
+ : freeform(free), reason(rea), action(act), gline_time(gt)
{
+ if (!RegexEngine)
+ throw ModuleException("Regex module implementing '"+RegexEngine.GetProvider()+"' is not loaded!");
+ regex = RegexEngine->Create(free);
this->FillFlags(fla);
}
}
};
-class ImplFilter : public FilterResult
-{
- public:
- Regex* regex;
-
- ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs);
-};
-
-
class ModuleFilter : public Module
{
+ typedef std::set<std::string, irc::insensitive_swo> ExemptTargetSet;
+
bool initing;
RegexFactory* factory;
void FreeFilters();
CommandFilter filtcommand;
dynamic_reference<RegexFactory> RegexEngine;
- std::vector<ImplFilter> filters;
+ std::vector<FilterResult> filters;
int flags;
- std::set<std::string> exemptfromfilter; // List of channel names excluded from filtering.
+ // List of channel names excluded from filtering.
+ ExemptTargetSet exemptedchans;
+
+ // List of target nicknames excluded from filtering.
+ ExemptTargetSet exemptednicks;
ModuleFilter();
- void init();
CullResult cull();
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
FilterResult* FilterMatch(User* user, const std::string &text, int flags);
bool DeleteFilter(const std::string &freeform);
std::pair<bool, std::string> AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flags);
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
- void OnRehash(User* user);
- Version GetVersion();
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ Version GetVersion() CXX11_OVERRIDE;
std::string EncodeFilter(FilterResult* filter);
FilterResult DecodeFilter(const std::string &data);
- void OnSyncNetwork(Module* proto, void* opaque);
- void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
- ModResult OnStats(char symbol, User* user, string_list &results);
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line);
- void OnUnloadModule(Module* mod);
+ void OnSyncNetwork(ProtocolInterface::Server& server) CXX11_OVERRIDE;
+ void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata) CXX11_OVERRIDE;
+ ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE;
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
bool AppliesToMe(User* user, FilterResult* filter, int flags);
void ReadFilters();
static bool StringToFilterAction(const std::string& str, FilterAction& fa);
Module *me = creator;
if (static_cast<ModuleFilter *>(me)->DeleteFilter(parameters[0]))
{
- user->WriteServ("NOTICE %s :*** Removed filter '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Removed filter '" + parameters[0] + "'");
ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" removed filter '"+parameters[0]+"'");
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Filter '%s' not found in list, try /stats s.", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Filter '" + parameters[0] + "' not found in list, try /stats s.");
return CMD_FAILURE;
}
}
if (!ModuleFilter::StringToFilterAction(parameters[1], type))
{
- user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.");
return CMD_FAILURE;
}
{
if (parameters.size() >= 5)
{
- duration = ServerInstance->Duration(parameters[3]);
+ duration = InspIRCd::Duration(parameters[3]);
reasonindex = 4;
}
else
{
- user->WriteServ("NOTICE %s :*** Not enough parameters: When setting a gline type filter, a gline duration must be specified as the third parameter.", user->nick.c_str());
+ user->WriteNotice("*** Not enough parameters: When setting a gline type filter, a gline duration must be specified as the third parameter.");
return CMD_FAILURE;
}
}
std::pair<bool, std::string> result = static_cast<ModuleFilter *>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
if (result.first)
{
- user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick.c_str(), freeform.c_str(),
- parameters[1].c_str(), (duration ? ", duration " : ""), (duration ? parameters[3].c_str() : ""),
- flags.c_str(), parameters[reasonindex].c_str());
+ user->WriteNotice("*** Added filter '" + freeform + "', type '" + parameters[1] + "'" +
+ (duration ? ", duration " + parameters[3] : "") + ", flags '" + flags + "', reason: '" +
+ parameters[reasonindex] + "'");
ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" added filter '"+freeform+"', type '"+parameters[1]+"', "+(duration ? "duration "+parameters[3]+", " : "")+"flags '"+flags+"', reason: "+parameters[reasonindex]);
}
else
{
- user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick.c_str(), freeform.c_str(), result.second.c_str());
+ user->WriteNotice("*** Filter '" + freeform + "' could not be added: " + result.second);
return CMD_FAILURE;
}
}
else
{
- user->WriteServ("NOTICE %s :*** Not enough parameters.", user->nick.c_str());
+ user->WriteNotice("*** Not enough parameters.");
return CMD_FAILURE;
}
bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
{
- if ((filter->flag_no_opers) && IS_OPER(user))
+ if ((filter->flag_no_opers) && user->IsOper())
return false;
if ((iflags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
return false;
{
}
-void ModuleFilter::init()
-{
- ServerInstance->Modules->AddService(filtcommand);
- Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
-}
-
CullResult ModuleFilter::cull()
{
FreeFilters();
void ModuleFilter::FreeFilters()
{
- for (std::vector<ImplFilter>::const_iterator i = filters.begin(); i != filters.end(); ++i)
+ for (std::vector<FilterResult>::const_iterator i = filters.begin(); i != filters.end(); ++i)
delete i->regex;
filters.clear();
}
-ModResult ModuleFilter::OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ModResult ModuleFilter::OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
{
+ // Leave remote users and servers alone
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- flags = FLAG_PRIVMSG;
- return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
-}
-
-ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
-{
- /* Leave ulines alone */
- if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
- return MOD_RES_PASSTHRU;
-
- if (!flags)
- flags = FLAG_NOTICE;
+ flags = (msgtype == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
FilterResult* f = this->FilterMatch(user, text, flags);
if (f)
if (target_type == TYPE_USER)
{
User* t = (User*)dest;
+ // Check if the target nick is exempted, if yes, ignore this message
+ if (exemptednicks.count(t->nick))
+ return MOD_RES_PASSTHRU;
+
target = t->nick;
}
else if (target_type == TYPE_CHANNEL)
{
Channel* t = (Channel*)dest;
- if (exemptfromfilter.find(t->name) != exemptfromfilter.end())
+ if (exemptedchans.count(t->name))
return MOD_RES_PASSTHRU;
target = t->name;
{
ServerInstance->SNO->WriteGlobalSno('a', "FILTER: "+user->nick+" had their message filtered, target was "+target+": "+f->reason);
if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(404, "%s %s :Message to channel blocked and opers notified (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Message to channel blocked and opers notified (%s)", target.c_str(), f->reason.c_str());
else
- user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked and opers notified: "+f->reason);
+ user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
}
else if (f->action == FA_SILENT)
{
if (target_type == TYPE_CHANNEL)
- user->WriteNumeric(404, "%s %s :Message to channel blocked (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Message to channel blocked (%s)", target.c_str(), f->reason.c_str());
else
- user->WriteServ("NOTICE "+user->nick+" :Your message to "+target+" was blocked: "+f->reason);
+ user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
}
else if (f->action == FA_KILL)
{
delete gl;
}
- ServerInstance->Logs->Log("FILTER",DEFAULT,"FILTER: "+ user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
{
- if (validated && IS_LOCAL(user))
+ if (validated)
{
flags = 0;
bool parting;
if (parameters.size() < 2)
return MOD_RES_PASSTHRU;
- if (exemptfromfilter.find(parameters[0]) != exemptfromfilter.end())
+ if (exemptedchans.count(parameters[0]))
return MOD_RES_PASSTHRU;
parting = true;
/* Are they parting, if so, kill is applicable */
if ((parting) && (f->action == FA_KILL))
{
- user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick.c_str(), f->reason.c_str());
+ user->WriteNotice("*** Your PART message was filtered: " + f->reason);
ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
}
if (f->action == FA_GLINE)
return MOD_RES_PASSTHRU;
}
-void ModuleFilter::OnRehash(User* user)
+void ModuleFilter::ReadConfig(ConfigStatus& status)
{
ConfigTagList tags = ServerInstance->Config->ConfTags("exemptfromfilter");
- exemptfromfilter.clear();
+ exemptedchans.clear();
+ exemptednicks.clear();
+
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
- std::string chan = i->second->getString("channel");
- if (!chan.empty())
- exemptfromfilter.insert(chan);
+ ConfigTag* tag = i->second;
+
+ // If "target" is not found, try the old "channel" key to keep compatibility with 2.0 configs
+ const std::string target = tag->getString("target", tag->getString("channel"));
+ if (!target.empty())
+ {
+ if (target[0] == '#')
+ exemptedchans.insert(target);
+ else
+ exemptednicks.insert(target);
+ }
}
std::string newrxengine = ServerInstance->Config->ConfValue("filteropts")->getString("engine");
return res;
}
-void ModuleFilter::OnSyncNetwork(Module* proto, void* opaque)
+void ModuleFilter::OnSyncNetwork(ProtocolInterface::Server& server)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); ++i)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
{
- proto->ProtoSendMetaData(opaque, NULL, "filter", EncodeFilter(&(*i)));
+ server.SendMetaData("filter", EncodeFilter(&(*i)));
}
}
}
catch (ModuleException& e)
{
- ServerInstance->Logs->Log("m_filter", DEBUG, "Error when unserializing filter: " + std::string(e.GetReason()));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error when unserializing filter: " + e.GetReason());
}
}
}
-ImplFilter::ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs)
- : FilterResult(pat, rea, act, glinetime, flgs)
-{
- if (!mymodule->RegexEngine)
- throw ModuleException("Regex module implementing '"+mymodule->RegexEngine.GetProvider()+"' is not loaded!");
- regex = mymodule->RegexEngine->Create(pat);
-}
-
FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int flgs)
{
static std::string stripped_text;
stripped_text.clear();
- for (std::vector<ImplFilter>::iterator index = filters.begin(); index != filters.end(); index++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
{
- FilterResult* filter = dynamic_cast<FilterResult*>(&(*index));
+ FilterResult* filter = &*i;
/* Skip ones that dont apply to us */
if (!AppliesToMe(user, filter, flgs))
InspIRCd::StripColor(stripped_text);
}
- //ServerInstance->Logs->Log("m_filter", DEBUG, "Match '%s' against '%s'", text.c_str(), index->freeform.c_str());
- if (index->regex->Matches(filter->flag_strip_color ? stripped_text : text))
- {
- //ServerInstance->Logs->Log("m_filter", DEBUG, "MATCH");
- return &*index;
- }
- //ServerInstance->Logs->Log("m_filter", DEBUG, "NO MATCH");
+ if (filter->regex->Matches(filter->flag_strip_color ? stripped_text : text))
+ return filter;
}
return NULL;
}
bool ModuleFilter::DeleteFilter(const std::string &freeform)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flgs)
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
try
{
- filters.push_back(ImplFilter(this, reason, type, duration, freeform, flgs));
+ filters.push_back(FilterResult(RegexEngine, freeform, reason, type, duration, flgs));
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason().c_str());
return std::make_pair(false, e.GetReason());
}
return std::make_pair(true, "");
std::string reason = i->second->getString("reason");
std::string action = i->second->getString("action");
std::string flgs = i->second->getString("flags");
- long gline_time = ServerInstance->Duration(i->second->getString("duration"));
+ unsigned long gline_time = i->second->getDuration("duration", 10*60, 1);
if (flgs.empty())
flgs = "*";
try
{
- filters.push_back(ImplFilter(this, reason, fa, gline_time, pattern, flgs));
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Regular expression %s loaded.", pattern.c_str());
+ filters.push_back(FilterResult(RegexEngine, pattern, reason, fa, gline_time, flgs));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Regular expression %s loaded.", pattern.c_str());
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason().c_str());
}
}
}
{
if (symbol == 's')
{
- for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ results.push_back("223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ }
+ for (ExemptTargetSet::const_iterator i = exemptedchans.begin(); i != exemptedchans.end(); ++i)
{
- results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ results.push_back("223 "+user->nick+" :EXEMPT "+(*i));
}
- for (std::set<std::string>::iterator i = exemptfromfilter.begin(); i != exemptfromfilter.end(); ++i)
+ for (ExemptTargetSet::const_iterator i = exemptednicks.begin(); i != exemptednicks.end(); ++i)
{
- results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :EXEMPT "+(*i));
+ results.push_back("223 "+user->nick+" :EXEMPT "+(*i));
}
}
return MOD_RES_PASSTHRU;
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class FlashPDSocket;
+
+namespace
+{
+ std::set<FlashPDSocket*> sockets;
+ std::string policy_reply;
+ const std::string expected_request("<policy-file-request/>\0", 23);
+}
+
+class FlashPDSocket : public BufferedSocket
+{
+ public:
+ time_t created;
+
+ FlashPDSocket(int newfd)
+ : BufferedSocket(newfd)
+ , created(ServerInstance->Time())
+ {
+ }
+
+ ~FlashPDSocket()
+ {
+ sockets.erase(this);
+ }
+
+ void OnError(BufferedSocketError) CXX11_OVERRIDE
+ {
+ AddToCull();
+ }
+
+ void OnDataReady() CXX11_OVERRIDE
+ {
+ if (recvq == expected_request)
+ WriteData(policy_reply);
+ AddToCull();
+ }
+
+ void AddToCull()
+ {
+ if (created == 0)
+ return;
+
+ created = 0;
+ Close();
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+};
+
+class ModuleFlashPD : public Module
+{
+ time_t timeout;
+
+ public:
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
+ {
+ for (std::set<FlashPDSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+ {
+ FlashPDSocket* sock = *i;
+ if ((sock->created + timeout <= curtime) && (sock->created != 0))
+ sock->AddToCull();
+ }
+ }
+
+ ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
+ {
+ if (from->bind_tag->getString("type") != "flashpolicyd")
+ return MOD_RES_PASSTHRU;
+
+ if (policy_reply.empty())
+ return MOD_RES_DENY;
+
+ sockets.insert(new FlashPDSocket(nfd));
+ return MOD_RES_ALLOW;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("flashpolicyd");
+ timeout = tag->getInt("timeout", 5, 1);
+ std::string file = tag->getString("file");
+
+ if (!file.empty())
+ {
+ try
+ {
+ FileReader reader(file);
+ policy_reply = reader.GetString();
+ }
+ catch (CoreException&)
+ {
+ const std::string error_message = "A file was specified for FlashPD, but it could not be loaded.";
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, error_message);
+ ServerInstance->SNO->WriteGlobalSno('a', error_message);
+ policy_reply.clear();
+ }
+ return;
+ }
+
+ // A file was not specified. Set the default setting.
+ // We allow access to all client ports by default
+ std::string to_ports;
+ for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
+ {
+ ListenSocket* ls = *i;
+ if (ls->bind_tag->getString("type", "clients") != "clients" || ls->bind_tag->getString("ssl", "plaintext") != "plaintext")
+ continue;
+
+ to_ports.append(ConvToStr(ls->bind_port)).push_back(',');
+ }
+
+ if (to_ports.empty())
+ {
+ policy_reply.clear();
+ return;
+ }
+
+ to_ports.erase(to_ports.size() - 1);
+
+ policy_reply =
+"<?xml version=\"1.0\"?>\
+<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\
+<cross-domain-policy>\
+<site-control permitted-cross-domain-policies=\"master-only\"/>\
+<allow-access-from domain=\"*\" to-ports=\"" + to_ports + "\" />\
+</cross-domain-policy>";
+ }
+
+ CullResult cull()
+ {
+ for (std::set<FlashPDSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+ {
+ FlashPDSocket* sock = *i;
+ sock->AddToCull();
+ }
+ return Module::cull();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Flash Policy Daemon. Allows Flash IRC clients to connect", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleFlashPD)
#include "inspircd.h"
-/* $ModDesc: Implements extban +b r: - realname (gecos) bans */
-
class ModuleGecosBan : public Module
{
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleGecosBan()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Extban 'r' - realname (gecos) ban", VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'r') && (mask[1] == ':'))
{
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('r');
+ tokens["EXTBAN"].push_back('r');
}
};
*/
-/* $ModDesc: Allows global loading of a module. */
-
#include "inspircd.h"
/** Handle /GLOADMODULE
{
flags_needed = 'o';
syntax = "<modulename> [servermask]";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
if (ServerInstance->Modules->Load(parameters[0].c_str()))
{
ServerInstance->SNO->WriteToSnoMask('a', "NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
- user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
}
else
{
- user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
}
}
else
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
+ if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+ InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
+ {
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
std::string servername = parameters.size() > 1 ? parameters[1] : "*";
if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
}
else
{
- user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
+ user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
}
}
else
- user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
+ user->SendText(":%s %03d %s %s :No such module", ServerInstance->Config->ServerName.c_str(), ERR_CANTUNLOADMODULE, user->nick.c_str(), parameters[0].c_str());
}
else
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
User* user = ServerInstance->FindNick(uid);
if (user)
- user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
- user->nick.c_str(), name.c_str(), result ? "" : "un");
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
+ name.c_str(), result ? "" : "un");
ServerInstance->GlobalCulls.AddItem(this);
}
};
}
else
{
- user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
return CMD_FAILURE;
}
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- ServerInstance->Modules->AddService(cmd3);
- }
-
- ~ModuleGlobalLoad()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleGlobalLoad)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for GLOBOPS and snomask +g */
-
/** Handle /GLOBOPS
*/
class CommandGlobops : public Command
CommandGlobops(Module* Creator) : Command(Creator,"GLOBOPS", 1,1)
{
flags_needed = 'o'; syntax = "<any-text>";
- TRANSLATE2(TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
public:
ModuleGlobops() : cmd(this) {}
- void init()
+ void init() CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(cmd);
ServerInstance->SNO->EnableSnomask('g',"GLOBOPS");
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for GLOBOPS and snomask +g", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleGlobops)
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $ModDesc: Channel half-operator mode provider */
-
-#include "inspircd.h"
-
-class ModeChannelHalfOp : public ModeHandler
-{
- public:
- ModeChannelHalfOp(Module* parent);
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding);
- unsigned int GetPrefixRank();
- void RemoveMode(Channel* channel, irc::modestacker* stack = NULL);
- void RemoveMode(User* user, irc::modestacker* stack = NULL);
-
- ModResult AccessCheck(User* src, Channel*, std::string& value, bool adding)
- {
- if (!adding && src->nick == value)
- return MOD_RES_ALLOW;
- return MOD_RES_PASSTHRU;
- }
-};
-
-ModeChannelHalfOp::ModeChannelHalfOp(Module* parent) : ModeHandler(parent, "halfop", 'h', PARAM_ALWAYS, MODETYPE_CHANNEL)
-{
- list = true;
- prefix = '%';
- levelrequired = OP_VALUE;
- m_paramtype = TR_NICK;
-}
-
-unsigned int ModeChannelHalfOp::GetPrefixRank()
-{
- return HALFOP_VALUE;
-}
-
-void ModeChannelHalfOp::RemoveMode(Channel* channel, irc::modestacker* stack)
-{
- const UserMembList* clist = channel->GetUsers();
-
- for (UserMembCIter i = clist->begin(); i != clist->end(); i++)
- {
- if (stack)
- {
- stack->Push(this->GetModeChar(), i->first->nick);
- }
- else
- {
- std::vector<std::string> parameters;
- parameters.push_back(channel->name);
- parameters.push_back("-h");
- parameters.push_back(i->first->nick);
- ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
- }
- }
-
-}
-
-void ModeChannelHalfOp::RemoveMode(User*, irc::modestacker* stack)
-{
-}
-
-ModeAction ModeChannelHalfOp::OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
-{
- return MODEACTION_ALLOW;
-}
-
-class ModuleHalfop : public Module
-{
- ModeChannelHalfOp mh;
- public:
- ModuleHalfop() : mh(this)
- {
- }
-
- void init()
- {
- ServerInstance->Modules->AddService(mh);
- }
-
- Version GetVersion()
- {
- return Version("Channel half-operator mode provider", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleHalfop)
*/
-/* $ModDesc: Provides the /HELPOP command for useful information */
-
#include "inspircd.h"
-static std::map<irc::string, std::string> helpop_map;
+typedef std::map<std::string, std::string, irc::insensitive_swo> HelpopMap;
+static HelpopMap helpop_map;
/** Handles user mode +h
*/
*/
class CommandHelpop : public Command
{
+ const std::string startkey;
public:
- CommandHelpop(Module* Creator) : Command(Creator, "HELPOP", 0)
+ CommandHelpop(Module* Creator)
+ : Command(Creator, "HELPOP", 0)
+ , startkey("start")
{
syntax = "<any-text>";
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
- irc::string parameter("start");
- if (parameters.size() > 0)
- parameter = parameters[0].c_str();
+ const std::string& parameter = (!parameters.empty() ? parameters[0] : startkey);
if (parameter == "index")
{
/* iterate over all helpop items */
- user->WriteServ("290 %s :HELPOP topic index", user->nick.c_str());
- for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
- {
- user->WriteServ("292 %s : %s", user->nick.c_str(), iter->first.c_str());
- }
- user->WriteServ("292 %s :*** End of HELPOP topic index", user->nick.c_str());
+ user->WriteNumeric(290, ":HELPOP topic index");
+ for (HelpopMap::const_iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
+ user->WriteNumeric(292, ": %s", iter->first.c_str());
+ user->WriteNumeric(292, ":*** End of HELPOP topic index");
}
else
{
- user->WriteServ("290 %s :*** HELPOP for %s", user->nick.c_str(), parameter.c_str());
- user->WriteServ("292 %s : -", user->nick.c_str());
+ user->WriteNumeric(290, ":*** HELPOP for %s", parameter.c_str());
+ user->WriteNumeric(292, ": -");
- std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter);
+ HelpopMap::const_iterator iter = helpop_map.find(parameter);
if (iter == helpop_map.end())
{
iter = helpop_map.find("nohelp");
}
- std::string value = iter->second;
+ const std::string& value = iter->second;
irc::sepstream stream(value, '\n');
std::string token = "*";
{
// Writing a blank line will not work with some clients
if (token.empty())
- user->WriteServ("292 %s : ", user->nick.c_str());
+ user->WriteNumeric(292, ": ");
else
- user->WriteServ("292 %s :%s", user->nick.c_str(), token.c_str());
+ user->WriteNumeric(292, ":%s", token.c_str());
}
- user->WriteServ("292 %s : -", user->nick.c_str());
- user->WriteServ("292 %s :*** End of HELPOP", user->nick.c_str());
+ user->WriteNumeric(292, ": -");
+ user->WriteNumeric(292, ":*** End of HELPOP");
}
return CMD_SUCCESS;
}
class ModuleHelpop : public Module
{
- private:
CommandHelpop cmd;
Helpop ho;
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- ReadConfig();
- ServerInstance->Modules->AddService(ho);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash, I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void ReadConfig()
- {
- std::map<irc::string, std::string> help;
+ HelpopMap help;
ConfigTagList tags = ServerInstance->Config->ConfTags("helpop");
for(ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- irc::string key = assign(tag->getString("key"));
+ std::string key = tag->getString("key");
std::string value;
tag->readString("value", value, true); /* Linefeeds allowed */
helpop_map.swap(help);
}
- void OnRehash(User* user)
- {
- ReadConfig();
- }
-
- void OnWhois(User* src, User* dst)
+ void OnWhois(User* src, User* dst) CXX11_OVERRIDE
{
- if (dst->IsModeSet('h'))
+ if (dst->IsModeSet(ho))
{
- ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is available for help.");
+ ServerInstance->SendWhoisLine(src, dst, 310, dst->nick+" :is available for help.");
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /HELPOP command for useful information", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides support for hiding channels with user mode +I */
-
/** Handles user mode +I
*/
class HideChans : public SimpleUserModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(hm);
- Implementation eventlist[] = { I_OnWhoisLine, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- virtual ~ModuleHideChans()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for hiding channels with user mode +I", VF_VENDOR);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
AffectsOpers = ServerInstance->Config->ConfValue("hidechans")->getBool("affectsopers");
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
{
/* always show to self */
if (user == dest)
return MOD_RES_PASSTHRU;
/* don't touch if -I */
- if (!dest->IsModeSet('I'))
+ if (!dest->IsModeSet(hm))
return MOD_RES_PASSTHRU;
/* if it affects opers, we don't care if they are opered */
}
};
-
MODULE_INIT(ModuleHideChans)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ListWatcher : public ModeWatcher
+{
+ // Minimum rank required to view the list
+ const unsigned int minrank;
+
+ public:
+ ListWatcher(Module* mod, const std::string& modename, unsigned int rank)
+ : ModeWatcher(mod, modename, MODETYPE_CHANNEL)
+ , minrank(rank)
+ {
+ }
+
+ bool BeforeMode(User* user, User* destuser, Channel* chan, std::string& param, bool adding)
+ {
+ // Only handle listmode list requests
+ if (!param.empty())
+ return true;
+
+ // If the user requesting the list is a member of the channel see if they have the
+ // rank required to view the list
+ Membership* memb = chan->GetUser(user);
+ if ((memb) && (memb->getRank() >= minrank))
+ return true;
+
+ if (user->HasPrivPermission("channels/auspex"))
+ return true;
+
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to view the %s list", chan->name.c_str(), GetModeName().c_str());
+ return false;
+ }
+};
+
+class ModuleHideList : public Module
+{
+ std::vector<ListWatcher*> watchers;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ stdalgo::delete_all(watchers);
+ watchers.clear();
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("hidelist");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string modename = tag->getString("mode");
+ // If rank is set to 0 everyone inside the channel can view the list,
+ // but non-members may not
+ unsigned int rank = tag->getInt("rank", HALFOP_VALUE, 0);
+ watchers.push_back(new ListWatcher(this, modename, rank));
+ }
+ }
+
+ ~ModuleHideList()
+ {
+ stdalgo::delete_all(watchers);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for hiding the list of listmodes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHideList)
#include "inspircd.h"
-/* $ModDesc: Provides support for hiding oper status with user mode +H */
-
/** Handles user mode +H
*/
class HideOper : public SimpleUserModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(hm);
- Implementation eventlist[] = { I_OnWhoisLine, I_OnSendWhoLine, I_OnStats };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual ~ModuleHideOper()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
}
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
{
/* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
* person doing the WHOIS is not an oper
if (numeric != 313)
return MOD_RES_PASSTHRU;
- if (!dest->IsModeSet('H'))
+ if (!dest->IsModeSet(hm))
return MOD_RES_PASSTHRU;
if (!user->HasPrivPermission("users/auspex"))
return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
{
- if (user->IsModeSet('H') && !source->HasPrivPermission("users/auspex"))
+ if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
{
// hide the "*" that marks the user as an oper from the /WHO line
std::string::size_type pos = line.find("*");
}
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(char symbol, User* user, string_list& results) CXX11_OVERRIDE
{
if (symbol != 'P')
return MOD_RES_PASSTHRU;
unsigned int count = 0;
- for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
{
User* oper = *i;
- if (!ServerInstance->ULine(oper->server) && (IS_OPER(user) || !oper->IsModeSet('H')))
+ if (!oper->server->IsULine() && (user->IsOper() || !oper->IsModeSet(hm)))
{
- results.push_back(ServerInstance->Config->ServerName+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
- (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
+ LocalUser* lu = IS_LOCAL(oper);
+ results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
count++;
}
}
- results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
+ results.push_back("249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
return MOD_RES_DENY;
}
};
-
MODULE_INIT(ModuleHideOper)
#include "inspircd.h"
-/* $ModDesc: Provides masking of user hostnames in a different way to m_cloaking */
-
/** Holds information on a host set by m_hostchange
*/
class Host
class ModuleHostChange : public Module
{
- private:
hostchanges_t hostchanges;
std::string MySuffix;
std::string MyPrefix;
std::string MySeparator;
public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* host = ServerInstance->Config->ConfValue("host");
MySuffix = host->getString("suffix");
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
// returns the version number of the module to be
// listed in /MODULES
return Version("Provides masking of user hostnames in a different way to m_cloaking", VF_VENDOR);
}
- virtual void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
{
}
if (!newhost.empty())
{
- user->WriteServ("NOTICE "+user->nick+" :Setting your virtual host: " + newhost);
- if (!user->ChangeDisplayedHost(newhost.c_str()))
- user->WriteServ("NOTICE "+user->nick+" :Could not set your virtual host: " + newhost);
+ user->WriteNotice("Setting your virtual host: " + newhost);
+ if (!user->ChangeDisplayedHost(newhost))
+ user->WriteNotice("Could not set your virtual host: " + newhost);
return;
}
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ModuleHostCycle : public Module
+{
+ /** Send fake quit/join/mode messages for host or ident cycle.
+ */
+ static void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const char* quitmsg)
+ {
+ // GetFullHost() returns the original data at the time this function is called
+ const std::string quitline = ":" + user->GetFullHost() + " QUIT :" + quitmsg;
+
+ already_sent_t silent_id = ++LocalUser::already_sent_id;
+ already_sent_t seen_id = ++LocalUser::already_sent_id;
+
+ IncludeChanList include_chans(user->chans.begin(), user->chans.end());
+ std::map<User*,bool> exceptions;
+
+ FOREACH_MOD(OnBuildNeighborList, (user, include_chans, exceptions));
+
+ for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ {
+ LocalUser* u = IS_LOCAL(i->first);
+ if (u && !u->quitting)
+ {
+ if (i->second)
+ {
+ u->already_sent = seen_id;
+ u->Write(quitline);
+ }
+ else
+ {
+ u->already_sent = silent_id;
+ }
+ }
+ }
+
+ std::string newfullhost = user->nick + "!" + newident + "@" + newhost;
+
+ for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
+ {
+ Membership* memb = *i;
+ Channel* c = memb->chan;
+ const std::string joinline = ":" + newfullhost + " JOIN " + c->name;
+ std::string modeline;
+
+ if (!memb->modes.empty())
+ {
+ modeline = ":" + (ServerInstance->Config->CycleHostsFromUser ? newfullhost : ServerInstance->Config->ServerName)
+ + " MODE " + c->name + " +" + memb->modes;
+
+ for (size_t j = 0; j < memb->modes.length(); j++)
+ modeline.append(" ").append(user->nick);
+ }
+
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator j = ulist.begin(); j != ulist.end(); ++j)
+ {
+ LocalUser* u = IS_LOCAL(j->first);
+ if (u == NULL || u == user)
+ continue;
+ if (u->already_sent == silent_id)
+ continue;
+
+ if (u->already_sent != seen_id)
+ {
+ u->Write(quitline);
+ u->already_sent = seen_id;
+ }
+
+ u->Write(joinline);
+ if (!memb->modes.empty())
+ u->Write(modeline);
+ }
+ }
+ }
+
+ public:
+ void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
+ {
+ DoHostCycle(user, newident, user->dhost, "Changing ident");
+ }
+
+ void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
+ {
+ DoHostCycle(user, user->ident, newhost, "Changing host");
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Cycles users in all their channels when their host or ident changes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHostCycle)
#include "inspircd.h"
-#include "httpd.h"
-
-/* $ModDesc: Provides HTTP serving facilities to modules */
-/* $ModDep: httpd.h */
+#include "iohook.h"
+#include "modules/httpd.h"
class ModuleHttpServer;
{
InternalState = HTTP_SERVE_WAIT_REQUEST;
- FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
- if (GetIOHook())
- GetIOHook()->OnStreamSocketAccept(this, client, server);
+ if (via->iohookprov)
+ via->iohookprov->OnAccept(this, client, server);
}
~HttpServerSocket()
sockets.erase(this);
}
- virtual void OnError(BufferedSocketError)
+ void OnError(BufferedSocketError) CXX11_OVERRIDE
{
ServerInstance->GlobalCulls.AddItem(this);
}
WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
- time_t local = ServerInstance->Time();
- struct tm *timeinfo = gmtime(&local);
- char *date = asctime(timeinfo);
- date[strlen(date) - 1] = '\0';
- rheaders.CreateHeader("Date", date);
-
- rheaders.CreateHeader("Server", BRANCH);
+ rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
+ rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
rheaders.SetHeader("Content-Length", ConvToStr(size));
if (size)
if (reqbuffer.length() >= 8192)
{
- ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
reqbuffer.clear();
SetError("Buffer");
}
}
};
+class HTTPdAPIImpl : public HTTPdAPIBase
+{
+ public:
+ HTTPdAPIImpl(Module* parent)
+ : HTTPdAPIBase(parent)
+ {
+ }
+
+ void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
+ {
+ claimed = true;
+ resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
+ }
+};
+
class ModuleHttpServer : public Module
{
+ std::vector<HttpServerSocket *> httpsocks;
+ HTTPdAPIImpl APIImpl;
unsigned int timeoutsec;
public:
+ ModuleHttpServer()
+ : APIImpl(this)
+ {
+ }
- void init()
+ void init() CXX11_OVERRIDE
{
HttpModule = this;
- Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
timeoutsec = tag->getInt("timeout");
}
- void OnRequest(Request& request)
- {
- if (strcmp(request.id, "HTTP-DOC") != 0)
- return;
- HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
- claimed = true;
- resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
- }
-
- ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
if (from->bind_tag->getString("type") != "httpd")
return MOD_RES_PASSTHRU;
return MOD_RES_ALLOW;
}
- void OnBackgroundTimer(time_t curtime)
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
if (!timeoutsec)
return;
}
}
- CullResult cull()
+ CullResult cull() CXX11_OVERRIDE
{
std::set<HttpServerSocket*> local;
local.swap(sockets);
return Module::cull();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
}
#include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
-
-/* $ModDesc: Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules */
+#include "modules/httpd.h"
class HTTPACL
{
const std::string &set_whitelist, const std::string &set_blacklist)
: path(set_path), username(set_username), password(set_password), whitelist(set_whitelist),
blacklist(set_blacklist) { }
-
- ~HTTPACL() { }
};
class ModuleHTTPAccessList : public Module
{
-
std::string stylesheet;
std::vector<HTTPACL> acl_list;
+ HTTPdAPI API;
public:
+ ModuleHTTPAccessList()
+ : API(this)
+ {
+ }
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
acl_list.clear();
ConfigTagList acls = ServerInstance->Config->ConfTags("httpdacl");
}
}
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
password.c_str(), whitelist.c_str(), blacklist.c_str());
acl_list.push_back(HTTPACL(path, username, password, whitelist, blacklist));
}
}
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnEvent, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
void BlockAccess(HTTPRequest* http, int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "BlockAccess (%d)", returnval);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BlockAccess (%d)", returnval);
std::stringstream data("Access to this resource is denied by an access control list. Please contact your IRC administrator.");
HTTPDocumentResponse response(this, *http, &data, returnval);
- response.headers.SetHeader("X-Powered-By", "m_httpd_acl.so");
+ response.headers.SetHeader("X-Powered-By", MODNAME);
if (!extraheaderkey.empty())
response.headers.SetHeader(extraheaderkey, extraheaderval);
- response.Send();
+ API->SendResponse(response);
}
- void OnEvent(Event& event)
+ void OnEvent(Event& event) CXX11_OVERRIDE
{
if (event.id == "httpd_acl")
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd acl event");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd acl event");
HTTPRequest* http = (HTTPRequest*)&event;
for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
{
if (InspIRCd::Match(http->GetIP(), entry, ascii_case_insensitive_map))
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
BlockAccess(http, 403);
return;
if (!allow_access)
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
BlockAccess(http, 403);
return;
if (!this_acl->password.empty() && !this_acl->username.empty())
{
/* Password auth, first look to see if we have a basic authentication header */
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
if (http->headers->IsSet("Authorization"))
sep.GetToken(base64);
std::string userpass = Base64ToBin(base64);
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
irc::sepstream userpasspair(userpass, ':');
if (userpasspair.GetToken(user))
/* Access granted if username and password are correct */
if (user == this_acl->username && pass == this_acl->password)
{
- ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: password and username match");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP authorization: password and username match");
return;
}
else
}
}
- virtual ~ModuleHTTPAccessList()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules", VF_VENDOR);
}
#include "inspircd.h"
-#include "httpd.h"
-#include "protocol.h"
-
-/* $ModDesc: Allows for the server configuration to be viewed over HTTP via m_httpd.so */
+#include "modules/httpd.h"
class ModuleHttpConfig : public Module
{
+ HTTPdAPI API;
+
public:
- void init()
+ ModuleHttpConfig()
+ : API(this)
{
- Implementation eventlist[] = { I_OnEvent };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
std::string Sanitize(const std::string &str)
return ret;
}
- void OnEvent(Event& event)
+ void OnEvent(Event& event) CXX11_OVERRIDE
{
std::stringstream data("");
if (event.id == "httpd_url")
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
HTTPRequest* http = (HTTPRequest*)&event;
if ((http->GetURI() == "/config") || (http->GetURI() == "/config/"))
data << "</body></html>";
/* Send the document back to m_httpd */
HTTPDocumentResponse response(this, *http, &data, 200);
- response.headers.SetHeader("X-Powered-By", "m_httpd_config.so");
+ response.headers.SetHeader("X-Powered-By", MODNAME);
response.headers.SetHeader("Content-Type", "text/html");
- response.Send();
+ API->SendResponse(response);
}
}
}
- virtual ~ModuleHttpConfig()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows for the server configuration to be viewed over HTTP via m_httpd.so", VF_VENDOR);
}
#include "inspircd.h"
-#include "httpd.h"
+#include "modules/httpd.h"
#include "xline.h"
-#include "protocol.h"
-
-/* $ModDesc: Provides statistics over HTTP via m_httpd.so */
class ModuleHttpStats : public Module
{
static std::map<char, char const*> const &entities;
+ HTTPdAPI API;
public:
-
- void init()
+ ModuleHttpStats()
+ : API(this)
{
- Implementation eventlist[] = { I_OnEvent };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
std::string Sanitize(const std::string &str)
data << "</metadata>";
}
- void OnEvent(Event& event)
+ void OnEvent(Event& event) CXX11_OVERRIDE
{
std::stringstream data("");
if (event.id == "httpd_url")
{
- ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
HTTPRequest* http = (HTTPRequest*)&event;
if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
<< Sanitize(ServerInstance->GetVersionString()) << "</version></server>";
data << "<general>";
- data << "<usercount>" << ServerInstance->Users->clientlist->size() << "</usercount>";
- data << "<channelcount>" << ServerInstance->chanlist->size() << "</channelcount>";
+ data << "<usercount>" << ServerInstance->Users->GetUsers().size() << "</usercount>";
+ data << "<channelcount>" << ServerInstance->GetChans().size() << "</channelcount>";
data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
- data << "<socketcount>" << (ServerInstance->SE->GetUsedFds()) << "</socketcount><socketmax>" << ServerInstance->SE->GetMaxFds() << "</socketmax><socketengine>" << ServerInstance->SE->GetName() << "</socketengine>";
+ data << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax><socketengine>" INSPIRCD_SOCKETENGINE_NAME "</socketengine>";
time_t current_time = 0;
current_time = ServerInstance->Time();
stime = gmtime(&server_uptime);
data << "<uptime><days>" << stime->tm_yday << "</days><hours>" << stime->tm_hour << "</hours><mins>" << stime->tm_min << "</mins><secs>" << stime->tm_sec << "</secs><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
- data << "<isupport>" << Sanitize(ServerInstance->Config->data005) << "</isupport></general><xlines>";
+ data << "<isupport>";
+ const std::vector<std::string>& isupport = ServerInstance->ISupport.GetLines();
+ for (std::vector<std::string>::const_iterator it = isupport.begin(); it != isupport.end(); it++)
+ {
+ data << Sanitize(*it) << std::endl;
+ }
+ data << "</isupport></general><xlines>";
std::vector<std::string> xltypes = ServerInstance->XLines->GetAllTypes();
for (std::vector<std::string>::iterator it = xltypes.begin(); it != xltypes.end(); ++it)
{
}
data << "</xlines><modulelist>";
- std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0);
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
- for (std::vector<std::string>::iterator i = module_names.begin(); i != module_names.end(); ++i)
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
{
- Module* m = ServerInstance->Modules->Find(i->c_str());
- Version v = m->GetVersion();
- data << "<module><name>" << *i << "</name><description>" << Sanitize(v.description) << "</description></module>";
+ Version v = i->second->GetVersion();
+ data << "<module><name>" << i->first << "</name><description>" << Sanitize(v.description) << "</description></module>";
}
data << "</modulelist><channellist>";
- for (chan_hash::const_iterator a = ServerInstance->chanlist->begin(); a != ServerInstance->chanlist->end(); ++a)
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
- Channel* c = a->second;
+ Channel* c = i->second;
data << "<channel>";
- data << "<usercount>" << c->GetUsers()->size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
+ data << "<usercount>" << c->GetUsers().size() << "</usercount><channelname>" << Sanitize(c->name) << "</channelname>";
data << "<channeltopic>";
data << "<topictext>" << Sanitize(c->topic) << "</topictext>";
data << "<setby>" << Sanitize(c->setby) << "</setby>";
data << "<settime>" << c->topicset << "</settime>";
data << "</channeltopic>";
data << "<channelmodes>" << Sanitize(c->ChanModes(true)) << "</channelmodes>";
- const UserMembList* ulist = c->GetUsers();
- for (UserMembCIter x = ulist->begin(); x != ulist->end(); ++x)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator x = ulist.begin(); x != ulist.end(); ++x)
{
Membership* memb = x->second;
data << "<channelmember><uid>" << memb->user->uuid << "</uid><privs>"
- << Sanitize(c->GetAllPrefixChars(x->first)) << "</privs><modes>"
+ << Sanitize(memb->GetAllPrefixChars()) << "</privs><modes>"
<< memb->modes << "</modes>";
DumpMeta(data, memb);
data << "</channelmember>";
data << "</channellist><userlist>";
- for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); ++a)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
{
- User* u = a->second;
+ User* u = i->second;
data << "<user>";
data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
<< u->host << "</realhost><displayhost>" << u->dhost << "</displayhost><gecos>"
<< Sanitize(u->fullname) << "</gecos><server>" << u->server << "</server>";
- if (IS_AWAY(u))
+ if (u->IsAway())
data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
- if (IS_OPER(u))
- data << "<opertype>" << Sanitize(u->oper->NameStr()) << "</opertype>";
+ if (u->IsOper())
+ data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
data << "<modes>" << u->FormatModes() << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
LocalUser* lu = IS_LOCAL(u);
if (lu)
data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
- << irc::sockets::satouser(lu->server_sa) << "</servaddr>";
+ << lu->server_sa.str() << "</servaddr>";
data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
DumpMeta(data, u);
data << "</userlist><serverlist>";
- ProtoServerList sl;
+ ProtocolInterface::ServerList sl;
ServerInstance->PI->GetServerList(sl);
- for (ProtoServerList::iterator b = sl.begin(); b != sl.end(); ++b)
+ for (ProtocolInterface::ServerList::const_iterator b = sl.begin(); b != sl.end(); ++b)
{
data << "<server>";
data << "<servername>" << b->servername << "</servername>";
/* Send the document back to m_httpd */
HTTPDocumentResponse response(this, *http, &data, 200);
- response.headers.SetHeader("X-Powered-By", "m_httpd_stats.so");
+ response.headers.SetHeader("X-Powered-By", MODNAME);
response.headers.SetHeader("Content-Type", "text/xml");
- response.Send();
+ API->SendResponse(response);
}
}
}
- virtual ~ModuleHttpStats()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides statistics over HTTP via m_httpd.so", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides support for RFC1413 ident lookups */
-
/* --------------------------------------------------------------
* Note that this is the third incarnation of m_ident. The first
* two attempts were pretty crashy, mainly due to the fact we tried
}
/* Attempt to bind (ident requests must come from the ip the query is referring to */
- if (ServerInstance->SE->Bind(GetFd(), bindaddr) < 0)
+ if (SocketEngine::Bind(GetFd(), bindaddr) < 0)
{
this->Close();
throw ModuleException("failed to bind()");
}
- ServerInstance->SE->NonBlocking(GetFd());
+ SocketEngine::NonBlocking(GetFd());
/* Attempt connection (nonblocking) */
- if (ServerInstance->SE->Connect(this, &connaddr.sa, connaddr.sa_size()) == -1 && errno != EINPROGRESS)
+ if (SocketEngine::Connect(this, &connaddr.sa, connaddr.sa_size()) == -1 && errno != EINPROGRESS)
{
this->Close();
throw ModuleException("connect() failed");
}
/* Add fd to socket engine */
- if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
+ if (!SocketEngine::AddFd(this, FD_WANT_NO_READ | FD_WANT_POLL_WRITE))
{
this->Close();
throw ModuleException("out of fds");
}
}
- virtual void OnConnected()
+ void OnConnected()
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"OnConnected()");
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnConnected()");
+ SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
char req[32];
/* Send failed if we didnt write the whole ident request --
* might as well give up if this happens!
*/
- if (ServerInstance->SE->Send(this, req, req_size, 0) < req_size)
+ if (SocketEngine::Send(this, req, req_size, 0) < req_size)
done = true;
}
- virtual void HandleEvent(EventType et, int errornum = 0)
+ void HandleEvent(EventType et, int errornum = 0)
{
switch (et)
{
break;
case EVENT_ERROR:
/* fd error event, ohshi- */
- ServerInstance->Logs->Log("m_ident",DEBUG,"EVENT_ERROR");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "EVENT_ERROR");
/* We *must* Close() here immediately or we get a
* huge storm of EVENT_ERROR events!
*/
*/
if (GetFd() > -1)
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"Close ident socket %d", GetFd());
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
- this->SetFd(-1);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Close ident socket %d", GetFd());
+ SocketEngine::Close(this);
}
}
/* We don't really need to buffer for incomplete replies here, since IDENT replies are
* extremely short - there is *no* sane reason it'd be in more than one packet
*/
- char ibuf[MAXBUF];
- int recvresult = ServerInstance->SE->Recv(this, ibuf, MAXBUF-1, 0);
+ char ibuf[256];
+ int recvresult = SocketEngine::Recv(this, ibuf, sizeof(ibuf)-1, 0);
/* Close (but don't delete from memory) our socket
* and flag as done since the ident lookup has finished
if (recvresult < 3)
return;
- ServerInstance->Logs->Log("m_ident",DEBUG,"ReadResponse()");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "ReadResponse()");
/* Truncate at the first null character, but first make sure
* there is at least one null char (at the end of the buffer).
* we're done.
*/
result += *i;
- if (!ServerInstance->IsIdent(result.c_str()))
+ if (!ServerInstance->IsIdent(result))
{
result.erase(result.end()-1);
break;
}
}
}
+
+ CullResult cull() CXX11_OVERRIDE
+ {
+ Close();
+ return EventHandler::cull();
+ }
};
class ModuleIdent : public Module
{
int RequestTimeout;
- SimpleExtItem<IdentRequestSocket> ext;
+ bool NoLookupPrefix;
+ SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> ext;
public:
ModuleIdent() : ext("ident_socket", this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(ext);
- OnRehash(NULL);
- Implementation eventlist[] = {
- I_OnRehash, I_OnUserInit, I_OnCheckReady,
- I_OnUserDisconnect, I_OnSetConnectClass
- };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleIdent()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for RFC1413 ident lookups", VF_VENDOR);
}
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- RequestTimeout = ServerInstance->Config->ConfValue("ident")->getInt("timeout", 5);
- if (!RequestTimeout)
- RequestTimeout = 5;
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ident");
+ RequestTimeout = tag->getInt("timeout", 5, 1);
+ NoLookupPrefix = tag->getBool("nolookupprefix", false);
}
- void OnUserInit(LocalUser *user)
+ void OnUserInit(LocalUser *user) CXX11_OVERRIDE
{
ConfigTag* tag = user->MyClass->config;
if (!tag->getBool("useident", true))
return;
- user->WriteServ("NOTICE Auth :*** Looking up your ident...");
+ user->WriteNotice("*** Looking up your ident...");
try
{
- IdentRequestSocket *isock = new IdentRequestSocket(IS_LOCAL(user));
+ IdentRequestSocket *isock = new IdentRequestSocket(user);
ext.set(user, isock);
}
catch (ModuleException &e)
{
- ServerInstance->Logs->Log("m_ident",DEBUG,"Ident exception: %s", e.GetReason());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Ident exception: " + e.GetReason());
}
}
* creating a Timer object and especially better than creating a
* Timer per ident lookup!
*/
- virtual ModResult OnCheckReady(LocalUser *user)
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
/* Does user have an ident socket attached at all? */
IdentRequestSocket *isock = ext.get(user);
if (!isock)
{
- ServerInstance->Logs->Log("m_ident",DEBUG, "No ident socket :(");
+ if ((NoLookupPrefix) && (user->ident[0] != '~'))
+ user->ident.insert(user->ident.begin(), 1, '~');
return MOD_RES_PASSTHRU;
}
- ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket");
-
time_t compare = isock->age;
compare += RequestTimeout;
if (ServerInstance->Time() >= compare)
{
/* Ident timeout */
- user->WriteServ("NOTICE Auth :*** Ident request timed out.");
- ServerInstance->Logs->Log("m_ident",DEBUG, "Timeout");
+ user->WriteNotice("*** Ident request timed out.");
}
else if (!isock->HasResult())
{
// time still good, no result yet... hold the registration
- ServerInstance->Logs->Log("m_ident",DEBUG, "No result yet");
return MOD_RES_DENY;
}
- ServerInstance->Logs->Log("m_ident",DEBUG, "Yay, result!");
-
/* wooo, got a result (it will be good, or bad) */
if (isock->result.empty())
{
user->ident.insert(user->ident.begin(), 1, '~');
- user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident.c_str());
+ user->WriteNotice("*** Could not find your ident, using " + user->ident + " instead.");
}
else
{
user->ident = isock->result;
- user->WriteServ("NOTICE Auth :*** Found your ident, '%s'", user->ident.c_str());
+ user->WriteNotice("*** Found your ident, '" + user->ident + "'");
}
user->InvalidateCache();
return MOD_RES_PASSTHRU;
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
if (myclass->config->getBool("requireident") && user->ident[0] == '~')
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
-
- virtual void OnCleanup(int target_type, void *item)
- {
- /* Module unloading, tidy up users */
- if (target_type == TYPE_USER)
- {
- LocalUser* user = IS_LOCAL((User*) item);
- if (user)
- OnUserDisconnect(user);
- }
- }
-
- virtual void OnUserDisconnect(LocalUser *user)
- {
- /* User disconnect (generic socket detatch event) */
- IdentRequestSocket *isock = ext.get(user);
- if (isock)
- {
- isock->Close();
- ext.unset(user);
- }
- }
};
MODULE_INIT(ModuleIdent)
-
#include "inspircd.h"
-#include "u_listmode.h"
-
-/* $ModDesc: Provides support for the +I channel mode */
-/* $ModDep: ../../include/u_listmode.h */
+#include "listmode.h"
/*
* Written by Om <om@inspircd.org>, April 2005.
{
}
- void init()
- {
- ServerInstance->Modules->AddService(ie);
-
- OnRehash(NULL);
- ie.DoImplements(this);
- Implementation eventlist[] = { I_On005Numeric, I_OnCheckInvite, I_OnCheckKey, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" INVEX=I");
+ tokens["INVEX"] = "I";
}
- ModResult OnCheckInvite(User* user, Channel* chan)
+ ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
{
- modelist* list = ie.extItem.get(chan);
+ ListModeBase::ModeList* list = ie.GetList(chan);
if (list)
{
- for (modelist::iterator it = list->begin(); it != list->end(); it++)
+ for (ListModeBase::ModeList::iterator it = list->begin(); it != list->end(); it++)
{
if (chan->CheckBan(user, it->mask))
{
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckKey(User* user, Channel* chan, const std::string& key)
+ ModResult OnCheckKey(User* user, Channel* chan, const std::string& key) CXX11_OVERRIDE
{
if (invite_bypass_key)
return OnCheckInvite(user, chan);
return MOD_RES_PASSTHRU;
}
- void OnSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- ie.DoSyncChannel(chan, proto, opaque);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
invite_bypass_key = ServerInstance->Config->ConfValue("inviteexception")->getBool("bypasskey", true);
ie.DoRehash();
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the +I channel mode", VF_VENDOR);
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */
-
#include "inspircd.h"
-#include "account.h"
-#include "m_cap.h"
+#include "modules/account.h"
+#include "modules/cap.h"
class ModuleIRCv3 : public Module
{
void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
{
- UserChanList chans(user->chans);
+ IncludeChanList chans(user->chans.begin(), user->chans.end());
std::map<User*, bool> exceptions;
- FOREACH_MOD(I_OnBuildNeighborList, OnBuildNeighborList(user, chans, exceptions));
+ FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
// Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
// Now consider sending it to all other users who has at least a common channel with the user
std::set<User*> already_sent;
- for (UCListIter i = chans.begin(); i != chans.end(); ++i)
+ for (IncludeChanList::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
- const UserMembList* userlist = (*i)->GetUsers();
- for (UserMembList::const_iterator m = userlist->begin(); m != userlist->end(); ++m)
+ const Channel::MemberMap& userlist = (*i)->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator m = userlist.begin(); m != userlist.end(); ++m)
{
/*
* Send the line if the channel member in question meets all of the following criteria:
{
}
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
- accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
+ accountnotify = conf->getBool("accountnotify", true);
awaynotify = conf->getBool("awaynotify", true);
extendedjoin = conf->getBool("extendedjoin", true);
}
- void OnEvent(Event& ev)
+ void OnEvent(Event& ev) CXX11_OVERRIDE
{
if (awaynotify)
cap_awaynotify.HandleEvent(ev);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
// Remember who is not going to see the JOIN because of other modules
- if ((awaynotify) && (IS_AWAY(memb->user)))
+ if ((awaynotify) && (memb->user->IsAway()))
last_excepts = excepts;
if (!extendedjoin)
std::string line;
std::string mode;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+ const Channel::MemberMap& userlist = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
{
// Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
User* member = IS_LOCAL(it->first);
}
}
- ModResult OnSetAway(User* user, const std::string &awaymsg)
+ ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
{
if (awaynotify)
{
return MOD_RES_PASSTHRU;
}
- void OnPostJoin(Membership *memb)
+ void OnPostJoin(Membership *memb) CXX11_OVERRIDE
{
- if ((!awaynotify) || (!IS_AWAY(memb->user)))
+ if ((!awaynotify) || (!memb->user->IsAway()))
return;
std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter it = userlist->begin(); it != userlist->end(); ++it)
+ const Channel::MemberMap& userlist = memb->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
{
// Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
User* member = IS_LOCAL(it->first);
ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +j (join flood protection) */
-
/** Holds settings and state associated with channel mode +j
*/
class joinfloodsettings
{
public:
- int secs;
- int joins;
+ unsigned int secs;
+ unsigned int joins;
time_t reset;
time_t unlocktime;
- int counter;
- bool locked;
+ unsigned int counter;
- joinfloodsettings(int b, int c) : secs(b), joins(c)
+ joinfloodsettings(unsigned int b, unsigned int c)
+ : secs(b), joins(c), unlocktime(0), counter(0)
{
reset = ServerInstance->Time() + secs;
- counter = 0;
- locked = false;
- };
+ }
void addjoin()
{
- counter++;
if (ServerInstance->Time() > reset)
{
- counter = 0;
+ counter = 1;
reset = ServerInstance->Time() + secs;
}
+ else
+ counter++;
}
bool shouldlock()
bool islocked()
{
- if (locked)
- {
- if (ServerInstance->Time() > unlocktime)
- {
- locked = false;
- return false;
- }
- else
- {
- return true;
- }
- }
- return false;
+ if (ServerInstance->Time() > unlocktime)
+ unlocktime = 0;
+
+ return (unlocktime != 0);
}
void lock()
{
- locked = true;
unlocktime = ServerInstance->Time() + 60;
}
+ bool operator==(const joinfloodsettings& other) const
+ {
+ return ((this->secs == other.secs) && (this->joins == other.joins));
+ }
};
/** Handles channel mode +j
*/
-class JoinFlood : public ModeHandler
+class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
{
public:
- SimpleExtItem<joinfloodsettings> ext;
- JoinFlood(Module* Creator) : ModeHandler(Creator, "joinflood", 'j', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("joinflood", Creator) { }
+ JoinFlood(Module* Creator)
+ : ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >(Creator, "joinflood", 'j')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- char ndata[MAXBUF];
- char* data = ndata;
- strlcpy(ndata,parameter.c_str(),MAXBUF);
- char* joins = data;
- char* secs = NULL;
- while (*data)
- {
- if (*data == ':')
- {
- *data = 0;
- data++;
- secs = data;
- break;
- }
- else data++;
- }
- if (secs)
-
- {
- /* Set up the flood parameters for this channel */
- int njoins = atoi(joins);
- int nsecs = atoi(secs);
- if ((njoins<1) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- joinfloodsettings* f = ext.get(channel);
- if (!f)
- {
- parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
- f = new joinfloodsettings(nsecs, njoins);
- ext.set(channel, f);
- channel->SetModeParam('j', parameter);
- return MODEACTION_ALLOW;
- }
- else
- {
- std::string cur_param = channel->GetModeParameter('j');
- parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
- if (cur_param == parameter)
- {
- // mode params match
- return MODEACTION_DENY;
- }
- else
- {
- // new mode param, replace old with new
- f = new joinfloodsettings(nsecs, njoins);
- ext.set(channel, f);
- channel->SetModeParam('j', parameter);
- return MODEACTION_ALLOW;
- }
- }
- }
- }
- else
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ return MODEACTION_DENY;
}
- else
+
+ /* Set up the flood parameters for this channel */
+ unsigned int njoins = ConvToInt(parameter.substr(0, colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
+ if ((njoins<1) || (nsecs<1))
{
- if (channel->IsModeSet('j'))
- {
- ext.unset(channel);
- channel->SetModeParam('j', "");
- return MODEACTION_ALLOW;
- }
+ source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ return MODEACTION_DENY;
}
- return MODEACTION_DENY;
+
+ ext.set(channel, new joinfloodsettings(nsecs, njoins));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const joinfloodsettings* jfs, std::string& out)
+ {
+ out.append(ConvToStr(jfs->joins)).push_back(':');
+ out.append(ConvToStr(jfs->secs));
}
};
class ModuleJoinFlood : public Module
{
-
JoinFlood jf;
public:
-
ModuleJoinFlood()
: jf(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(jf);
- ServerInstance->Modules->AddService(jf.ext);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
joinfloodsettings *f = jf.ext.get(chan);
if (f && f->islocked())
{
- user->WriteNumeric(609, "%s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick.c_str(),chan->name.c_str());
+ user->WriteNumeric(609, "%s :This channel is temporarily unavailable (+j). Please try again later.",chan->name.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
/* We arent interested in JOIN events caused by a network burst */
if (sync)
}
}
- ~ModuleJoinFlood()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
}
#include "inspircd.h"
-
-/* $ModDesc: Provides support for the RPL_REDIR numeric and the /JUMPSERVER command. */
+#include "modules/ssl.h"
/** Handle /JUMPSERVER
*/
std::string redirect_to;
std::string reason;
int port;
+ int sslport;
CommandJumpserver(Module* Creator) : Command(Creator, "JUMPSERVER", 0, 4)
{
- flags_needed = 'o'; syntax = "[<server> <port> <+/-an> <reason>]";
+ flags_needed = 'o';
+ syntax = "[<server> <port>[:<sslport>] <+/-an> <reason>]";
port = 0;
+ sslport = 0;
redirect_new_users = false;
}
if (!parameters.size())
{
if (port)
- user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick.c_str(), redirect_to.c_str(), port);
+ user->WriteNotice("*** Disabled jumpserver (previously set to '" + redirect_to + ":" + ConvToStr(port) + "')");
else
- user->WriteServ("NOTICE %s :*** Jumpserver was not enabled.", user->nick.c_str());
+ user->WriteNotice("*** Jumpserver was not enabled.");
port = 0;
+ sslport = 0;
redirect_to.clear();
return CMD_SUCCESS;
}
redirect_new_users = direction;
break;
default:
- user->WriteServ("NOTICE %s :*** Invalid JUMPSERVER flag: %c", user->nick.c_str(), *n);
+ user->WriteNotice("*** Invalid JUMPSERVER flag: " + ConvToStr(*n));
return CMD_FAILURE;
break;
}
}
- if (!atoi(parameters[1].c_str()))
+ size_t delimpos = parameters[1].find(':');
+ port = ConvToInt(parameters[1].substr(0, delimpos ? delimpos : std::string::npos));
+ sslport = (delimpos == std::string::npos ? 0 : ConvToInt(parameters[1].substr(delimpos + 1)));
+
+ if (parameters[1].find_first_not_of("0123456789:") != std::string::npos
+ || parameters[1].rfind(':') != delimpos
+ || port > 65535 || sslport > 65535)
{
- user->WriteServ("NOTICE %s :*** Invalid port number", user->nick.c_str());
+ user->WriteNotice("*** Invalid port number");
return CMD_FAILURE;
}
if (redirect_all_immediately)
{
/* Redirect everyone but the oper sending the command */
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- User* t = *i;
- if (!IS_OPER(t))
+ LocalUser* t = *i;
+ if (!t->IsOper())
{
- t->WriteNumeric(10, "%s %s %s :Please use this Server/Port instead", t->nick.c_str(), parameters[0].c_str(), parameters[1].c_str());
+ t->WriteNumeric(RPL_REDIR, "%s %d :Please use this Server/Port instead", parameters[0].c_str(), GetPort(t));
ServerInstance->Users->QuitUser(t, reason);
n_done++;
}
}
if (redirect_new_users)
- {
redirect_to = parameters[0];
- port = atoi(parameters[1].c_str());
- }
- user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick.c_str(), parameters[0].c_str(), parameters[1].c_str(),
- redirect_all_immediately ? "a" : "",
- redirect_new_users ? "n" : "",
- n_done ? " (" : "",
- n_done ? n_done_s.c_str() : "",
- n_done ? " user(s) redirected)" : "",
- reason.c_str());
+ user->WriteNotice("*** Set jumpserver to server '" + parameters[0] + "' port '" + (port ? ConvToStr(port) : "Auto") + ", SSL " + (sslport ? ConvToStr(sslport) : "Auto") + "', flags '+" +
+ (redirect_all_immediately ? "a" : "") + (redirect_new_users ? "n'" : "'") +
+ (n_done ? " (" + n_done_s + "user(s) redirected): " : ": ") + reason);
}
return CMD_SUCCESS;
}
-};
+ int GetPort(LocalUser* user)
+ {
+ int p = (SSLClientCert::GetCertificate(&user->eh) ? sslport : port);
+ if (p == 0)
+ p = user->GetServerPort();
+ return p;
+ }
+};
class ModuleJumpServer : public Module
{
{
}
- void init()
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(js);
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleJumpServer()
- {
- }
-
- virtual ModResult OnUserRegister(LocalUser* user)
- {
- if (js.port && js.redirect_new_users)
+ if (js.redirect_new_users)
{
- user->WriteNumeric(10, "%s %s %d :Please use this Server/Port instead",
- user->nick.c_str(), js.redirect_to.c_str(), js.port);
+ int port = js.GetPort(user);
+ user->WriteNumeric(RPL_REDIR, "%s %d :Please use this Server/Port instead",
+ js.redirect_to.c_str(), port);
ServerInstance->Users->QuitUser(user, js.reason);
return MOD_RES_PASSTHRU;
}
return MOD_RES_PASSTHRU;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
// Emergency way to unlock
- if (!user) js.redirect_new_users = false;
+ if (!status.srcuser)
+ js.redirect_new_users = false;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the RPL_REDIR numeric and the /JUMPSERVER command.", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleJumpServer)
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
-
typedef std::map<std::string, time_t> delaylist;
+struct KickRejoinData
+{
+ delaylist kicked;
+ unsigned int delay;
+
+ KickRejoinData(unsigned int Delay) : delay(Delay) { }
+};
+
/** Handles channel mode +J
*/
-class KickRejoin : public ModeHandler
+class KickRejoin : public ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >
{
+ static const unsigned int max = 60;
public:
- unsigned int max;
- SimpleExtItem<delaylist> ext;
KickRejoin(Module* Creator)
- : ModeHandler(Creator, "kicknorejoin", 'J', PARAM_SETONLY, MODETYPE_CHANNEL)
- , ext("norejoinusers", Creator)
+ : ParamMode<KickRejoin, SimpleExtItem<KickRejoinData> >(Creator, "kicknorejoin", 'J')
{
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
- {
- int v = ConvToInt(parameter);
- if (v <= 0)
- return MODEACTION_DENY;
- if (parameter == channel->GetModeParameter(this))
- return MODEACTION_DENY;
-
- if ((IS_LOCAL(source) && ((unsigned int)v > max)))
- v = max;
+ int v = ConvToInt(parameter);
+ if (v <= 0)
+ return MODEACTION_DENY;
- parameter = ConvToStr(v);
- channel->SetModeParam(this, parameter);
- }
- else
- {
- if (!channel->IsModeSet(this))
- return MODEACTION_DENY;
+ if ((IS_LOCAL(source) && ((unsigned int)v > max)))
+ v = max;
- ext.unset(channel);
- channel->SetModeParam(this, "");
- }
+ ext.set(channel, new KickRejoinData(v));
return MODEACTION_ALLOW;
}
+
+ void SerializeParam(Channel* chan, const KickRejoinData* krd, std::string& out)
+ {
+ out.append(ConvToStr(krd->delay));
+ }
};
class ModuleKickNoRejoin : public Module
KickRejoin kr;
public:
-
ModuleKickNoRejoin()
: kr(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(kr);
- ServerInstance->Modules->AddService(kr.ext);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserKick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- void OnRehash(User* user)
- {
- kr.max = ServerInstance->Duration(ServerInstance->Config->ConfValue("kicknorejoin")->getString("maxtime"));
- if (!kr.max)
- kr.max = 30*60;
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
- delaylist* dl = kr.ext.get(chan);
- if (dl)
+ KickRejoinData* data = kr.ext.get(chan);
+ if (data)
{
- for (delaylist::iterator iter = dl->begin(); iter != dl->end(); )
+ delaylist& kicked = data->kicked;
+ for (delaylist::iterator iter = kicked.begin(); iter != kicked.end(); )
{
if (iter->second > ServerInstance->Time())
{
if (iter->first == user->uuid)
{
- std::string modeparam = chan->GetModeParameter(&kr);
- user->WriteNumeric(ERR_DELAYREJOIN, "%s %s :You must wait %s seconds after being kicked to rejoin (+J)",
- user->nick.c_str(), chan->name.c_str(), modeparam.c_str());
+ user->WriteNumeric(ERR_DELAYREJOIN, "%s :You must wait %u seconds after being kicked to rejoin (+J)",
+ chan->name.c_str(), data->delay);
return MOD_RES_DENY;
}
++iter;
else
{
// Expired record, remove.
- dl->erase(iter++);
+ kicked.erase(iter++);
}
}
-
- if (dl->empty())
- kr.ext.unset(chan);
}
}
return MOD_RES_PASSTHRU;
}
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE
{
- if (memb->chan->IsModeSet(&kr) && (IS_LOCAL(memb->user)) && (source != memb->user))
+ if ((!IS_LOCAL(memb->user)) || (source == memb->user))
+ return;
+
+ KickRejoinData* data = kr.ext.get(memb->chan);
+ if (data)
{
- delaylist* dl = kr.ext.get(memb->chan);
- if (!dl)
- {
- dl = new delaylist;
- kr.ext.set(memb->chan, dl);
- }
- (*dl)[memb->user->uuid] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr));
+ data->kicked[memb->user->uuid] = ServerInstance->Time() + data->delay;
}
}
- ~ModuleKickNoRejoin()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Channel mode to delay rejoin after kick", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleKickNoRejoin)
#include "inspircd.h"
-/* $ModDesc: Provides support for /KNOCK and channel mode +K */
-
/** Handles the /KNOCK command
*/
class CommandKnock : public Command
{
+ SimpleChannelModeHandler& noknockmode;
+ ChanModeReference inviteonlymode;
+
public:
bool sendnotice;
bool sendnumeric;
- CommandKnock(Module* Creator) : Command(Creator,"KNOCK", 2, 2)
+ CommandKnock(Module* Creator, SimpleChannelModeHandler& Noknockmode)
+ : Command(Creator,"KNOCK", 2, 2)
+ , noknockmode(Noknockmode)
+ , inviteonlymode(Creator, "inviteonly")
{
syntax = "<channel> <reason>";
Penalty = 5;
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
- user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (c->HasUser(user))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, you are already on that channel.", user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_KNOCKONCHAN, "%s :Can't KNOCK on %s, you are already on that channel.", c->name.c_str(), c->name.c_str());
return CMD_FAILURE;
}
- if (c->IsModeSet('K'))
+ if (c->IsModeSet(noknockmode))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, +K is set.",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(480, ":Can't KNOCK on %s, +K is set.", c->name.c_str());
return CMD_FAILURE;
}
- if (!c->IsModeSet('i'))
+ if (!c->IsModeSet(inviteonlymode))
{
- user->WriteNumeric(480, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CHANOPEN, "%s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!", c->name.c_str(), c->name.c_str());
return CMD_FAILURE;
}
if (sendnumeric)
c->WriteChannelWithServ(ServerInstance->Config->ServerName, "710 %s %s %s :is KNOCKing: %s", c->name.c_str(), c->name.c_str(), user->GetFullHost().c_str(), parameters[1].c_str());
- user->WriteServ("NOTICE %s :KNOCKing on %s", user->nick.c_str(), c->name.c_str());
+ user->WriteNotice("KNOCKing on " + c->name);
return CMD_SUCCESS;
}
}
};
-/** Handles channel mode +K
- */
-class Knock : public SimpleChannelModeHandler
-{
- public:
- Knock(Module* Creator) : SimpleChannelModeHandler(Creator, "noknock", 'K') { }
-};
-
class ModuleKnock : public Module
{
+ SimpleChannelModeHandler kn;
CommandKnock cmd;
- Knock kn;
- public:
- ModuleKnock() : cmd(this), kn(this)
- {
- }
- void init()
+ public:
+ ModuleKnock()
+ : kn(this, "noknock", 'K')
+ , cmd(this, kn)
{
- ServerInstance->Modules->AddService(kn);
- ServerInstance->Modules->AddService(cmd);
-
- ServerInstance->Modules->Attach(I_OnRehash, this);
- OnRehash(NULL);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string knocknotify = ServerInstance->Config->ConfValue("knock")->getString("notify");
irc::string notify(knocknotify.c_str());
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for /KNOCK and channel mode +K", VF_OPTCOMMON | VF_VENDOR);
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
+ * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+namespace
+{
+ Module* me;
+ std::string killreason;
+ LocalIntExt* authed;
+ bool verbose;
+ std::string vhost;
+ LocalStringExt* vhosts;
+ std::vector<std::pair<std::string, std::string> > requiredattributes;
+}
+
+class BindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uid;
+ std::string DN;
+ bool checkingAttributes;
+ bool passed;
+ int attrCount;
+
+ static std::string SafeReplace(const std::string& text, std::map<std::string, std::string>& replacements)
+ {
+ std::string result;
+ result.reserve(text.length());
+
+ for (unsigned int i = 0; i < text.length(); ++i)
+ {
+ char c = text[i];
+ if (c == '$')
+ {
+ // find the first nonalpha
+ i++;
+ unsigned int start = i;
+
+ while (i < text.length() - 1 && isalpha(text[i + 1]))
+ ++i;
+
+ std::string key = text.substr(start, (i - start) + 1);
+ result.append(replacements[key]);
+ }
+ else
+ result.push_back(c);
+ }
+
+ return result;
+ }
+
+ static void SetVHost(User* user, const std::string& DN)
+ {
+ if (!vhost.empty())
+ {
+ irc::commasepstream stream(DN);
+
+ // mashed map of key:value parts of the DN
+ std::map<std::string, std::string> dnParts;
+
+ std::string dnPart;
+ while (stream.GetToken(dnPart))
+ {
+ std::string::size_type pos = dnPart.find('=');
+ if (pos == std::string::npos) // malformed
+ continue;
+
+ std::string key = dnPart.substr(0, pos);
+ std::string value = dnPart.substr(pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
+ dnParts[key] = value;
+ }
+
+ // change host according to config key
+ vhosts->set(user, SafeReplace(vhost, dnParts));
+ }
+ }
+
+ public:
+ BindInterface(Module* c, const std::string& p, const std::string& u, const std::string& dn)
+ : LDAPInterface(c)
+ , provider(p), uid(u), DN(dn), checkingAttributes(false), passed(false), attrCount(0)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+
+ if (!user || !LDAP)
+ {
+ if (!checkingAttributes || !--attrCount)
+ delete this;
+ return;
+ }
+
+ if (!checkingAttributes && requiredattributes.empty())
+ {
+ // We're done, there are no attributes to check
+ SetVHost(user, DN);
+ authed->set(user, 1);
+
+ delete this;
+ return;
+ }
+
+ // Already checked attributes?
+ if (checkingAttributes)
+ {
+ if (!passed)
+ {
+ // Only one has to pass
+ passed = true;
+
+ SetVHost(user, DN);
+ authed->set(user, 1);
+ }
+
+ // Delete this if this is the last ref
+ if (!--attrCount)
+ delete this;
+
+ return;
+ }
+
+ // check required attributes
+ checkingAttributes = true;
+
+ for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
+ {
+ // Note that only one of these has to match for it to be success
+ const std::string& attr = it->first;
+ const std::string& val = it->second;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
+ try
+ {
+ LDAP->Compare(this, DN, attr, val);
+ ++attrCount;
+ }
+ catch (LDAPException &ex)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Unable to compare attributes %s=%s: %s", attr.c_str(), val.c_str(), ex.GetReason().c_str());
+ }
+ }
+
+ // Nothing done
+ if (!attrCount)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (unable to validate attributes)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ if (checkingAttributes && --attrCount)
+ return;
+
+ if (passed)
+ {
+ delete this;
+ return;
+ }
+
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), err.getError().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ delete this;
+ }
+};
+
+class SearchInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uid;
+
+ public:
+ SearchInterface(Module* c, const std::string& p, const std::string& u)
+ : LDAPInterface(c), provider(p), uid(u)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (!LDAP || r.empty() || !user)
+ {
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ return;
+ }
+
+ try
+ {
+ const LDAPAttributes& a = r.get(0);
+ std::string bindDn = a.get("dn");
+ if (bindDn.empty())
+ {
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ return;
+ }
+
+ LDAP->Bind(new BindInterface(this->creator, provider, uid, bindDn), bindDn, user->password);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uuid;
+ const std::string base;
+ const std::string what;
+
+ public:
+ AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& b, const std::string& w)
+ : LDAPInterface(c), provider(p), uuid(u), base(b), what(w)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (LDAP)
+ {
+ try
+ {
+ LDAP->Search(new SearchInterface(this->creator, provider, uuid), base, what);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ LocalIntExt ldapAuthed;
+ LocalStringExt ldapVhost;
+ std::string base;
+ std::string attribute;
+ std::vector<std::string> allowpatterns;
+ std::vector<std::string> whitelistedcidrs;
+ bool useusername;
+
+public:
+ ModuleLDAPAuth()
+ : LDAP(this, "LDAP")
+ , ldapAuthed("ldapauth", this)
+ , ldapVhost("ldapauth_vhost", this)
+ {
+ me = this;
+ authed = &ldapAuthed;
+ vhosts = &ldapVhost;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
+ whitelistedcidrs.clear();
+ requiredattributes.clear();
+
+ base = tag->getString("baserdn");
+ attribute = tag->getString("attribute");
+ killreason = tag->getString("killreason");
+ vhost = tag->getString("host");
+ // Set to true if failed connects should be reported to operators
+ verbose = tag->getBool("verbose");
+ useusername = tag->getBool("userfield");
+
+ LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+
+ ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
+
+ for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
+ {
+ std::string cidr = i->second->getString("cidr");
+ if (!cidr.empty()) {
+ whitelistedcidrs.push_back(cidr);
+ }
+ }
+
+ ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
+
+ for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
+ {
+ const std::string attr = i->second->getString("attribute");
+ const std::string val = i->second->getString("value");
+
+ if (!attr.empty() && !val.empty())
+ requiredattributes.push_back(make_pair(attr, val));
+ }
+
+ std::string allowpattern = tag->getString("allowpattern");
+ irc::spacesepstream ss(allowpattern);
+ for (std::string more; ss.GetToken(more); )
+ {
+ allowpatterns.push_back(more);
+ }
+ }
+
+ void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
+ {
+ std::string* cc = ldapVhost.get(user);
+ if (cc)
+ {
+ user->ChangeDisplayedHost(*cc);
+ ldapVhost.unset(user);
+ }
+ }
+
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+ {
+ for (std::vector<std::string>::const_iterator i = allowpatterns.begin(); i != allowpatterns.end(); ++i)
+ {
+ if (InspIRCd::Match(user->nick, *i))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+ }
+
+ for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
+ {
+ if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+ }
+
+ if (user->password.empty())
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ if (!LDAP)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Unable to find LDAP provider)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ try
+ {
+ std::string what = attribute + "=" + (useusername ? user->ident : user->nick);
+ LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, base, what));
+ }
+ catch (LDAPException &ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ return MOD_RES_DENY;
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ldap.h"
+
+namespace
+{
+ Module* me;
+}
+
+class LDAPOperBase : public LDAPInterface
+{
+ protected:
+ const std::string uid;
+ const std::string opername;
+ const std::string password;
+
+ void Fallback(User* user)
+ {
+ if (!user)
+ return;
+
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
+ if (!oper_command)
+ return;
+
+ std::vector<std::string> params;
+ params.push_back(opername);
+ params.push_back(password);
+ oper_command->Handle(params, user);
+ }
+
+ void Fallback()
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ Fallback(user);
+ }
+
+ public:
+ LDAPOperBase(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+ : LDAPInterface(mod)
+ , uid(uuid), opername(oper), password(pass)
+ {
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str());
+ Fallback();
+ delete this;
+ }
+};
+
+class BindInterface : public LDAPOperBase
+{
+ public:
+ BindInterface(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass)
+ : LDAPOperBase(mod, uuid, oper, pass)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername);
+
+ if (!user || iter == ServerInstance->Config->oper_blocks.end())
+ {
+ Fallback();
+ delete this;
+ return;
+ }
+
+ OperInfo* ifo = iter->second;
+ user->Oper(ifo);
+ delete this;
+ }
+};
+
+class SearchInterface : public LDAPOperBase
+{
+ const std::string provider;
+
+ bool HandleResult(const LDAPResult& result)
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (!LDAP || result.empty())
+ return false;
+
+ try
+ {
+ const LDAPAttributes& attr = result.get(0);
+ std::string bindDn = attr.get("dn");
+ if (bindDn.empty())
+ return false;
+
+ LDAP->Bind(new BindInterface(this->creator, uid, opername, password), bindDn, password);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+
+ return true;
+ }
+
+ public:
+ SearchInterface(Module* mod, const std::string& prov, const std::string &uuid, const std::string& oper, const std::string& pass)
+ : LDAPOperBase(mod, uuid, oper, pass)
+ , provider(prov)
+ {
+ }
+
+ void OnResult(const LDAPResult& result) CXX11_OVERRIDE
+ {
+ if (!HandleResult(result))
+ Fallback();
+ delete this;
+ }
+};
+
+class AdminBindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string user;
+ const std::string opername;
+ const std::string password;
+ const std::string base;
+ const std::string what;
+
+ public:
+ AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& o, const std::string& pa, const std::string& b, const std::string& w)
+ : LDAPInterface(c), provider(p), user(u), opername(p), password(pa), base(b), what(w)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+ if (LDAP)
+ {
+ try
+ {
+ LDAP->Search(new SearchInterface(this->creator, provider, user, opername, password), base, what);
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason());
+ }
+ }
+ delete this;
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError());
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ std::string base;
+ std::string attribute;
+
+ public:
+ ModuleLDAPAuth()
+ : LDAP(this, "LDAP")
+ {
+ me = this;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
+
+ LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+ base = tag->getString("baserdn");
+ attribute = tag->getString("attribute");
+ }
+
+ ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line) CXX11_OVERRIDE
+ {
+ if (validated && command == "OPER" && parameters.size() >= 2)
+ {
+ const std::string& opername = parameters[0];
+ const std::string& password = parameters[1];
+
+ ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername);
+ if (it == ServerInstance->Config->oper_blocks.end())
+ return MOD_RES_PASSTHRU;
+
+ ConfigTag* tag = it->second->oper_block;
+ if (!tag)
+ return MOD_RES_PASSTHRU;
+
+ std::string acceptedhosts = tag->getString("host");
+ std::string hostname = user->ident + "@" + user->host;
+ if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
+ return MOD_RES_PASSTHRU;
+
+ if (!LDAP)
+ return MOD_RES_PASSTHRU;
+
+ try
+ {
+ std::string what = attribute + "=" + opername;
+ LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, opername, password, base, what));
+ return MOD_RES_DENY;
+ }
+ catch (LDAPException& ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+ }
+ }
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
#include "inspircd.h"
-/* $ModDesc: Allows locking of the server to stop all incoming connections till unlocked again */
-
/** Adds numerics
* 988 <nick> <servername> :Closed for new connections
* 989 <nick> <servername> :Open for new connections
-*/
-
+ */
class CommandLockserv : public Command
{
bool& locked;
-public:
+
+ public:
CommandLockserv(Module* Creator, bool& lock) : Command(Creator, "LOCKSERV", 0), locked(lock)
{
flags_needed = 'o';
{
if (locked)
{
- user->WriteServ("NOTICE %s :The server is already locked.", user->nick.c_str());
+ user->WriteNotice("The server is already locked.");
return CMD_FAILURE;
}
locked = true;
- user->WriteNumeric(988, "%s %s :Closed for new connections", user->nick.c_str(), user->server.c_str());
+ user->WriteNumeric(988, "%s :Closed for new connections", user->server->GetName().c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used LOCKSERV to temporarily disallow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
class CommandUnlockserv : public Command
{
-private:
bool& locked;
-public:
+ public:
CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock)
{
flags_needed = 'o';
{
if (!locked)
{
- user->WriteServ("NOTICE %s :The server isn't locked.", user->nick.c_str());
+ user->WriteNotice("The server isn't locked.");
return CMD_FAILURE;
}
locked = false;
- user->WriteNumeric(989, "%s %s :Open for new connections", user->nick.c_str(), user->server.c_str());
+ user->WriteNumeric(989, "%s :Open for new connections", user->server->GetName().c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Oper %s used UNLOCKSERV to allow new connections", user->nick.c_str());
return CMD_SUCCESS;
}
class ModuleLockserv : public Module
{
-private:
bool locked;
CommandLockserv lockcommand;
CommandUnlockserv unlockcommand;
-public:
+ public:
ModuleLockserv() : lockcommand(this, locked), unlockcommand(this, locked)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
locked = false;
- ServerInstance->Modules->AddService(lockcommand);
- ServerInstance->Modules->AddService(unlockcommand);
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleLockserv()
- {
- }
-
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
// Emergency way to unlock
- if (!user) locked = false;
+ if (!status.srcuser)
+ locked = false;
}
- virtual ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
if (locked)
{
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
return locked ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows locking of the server to stop all incoming connections until unlocked again", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Hide /MAP and /LINKS in the same form as ircu (mostly useless) */
-
class ModuleMapHide : public Module
{
std::string url;
public:
- void init()
- {
- Implementation eventlist[] = { I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
url = ServerInstance->Config->ConfValue("security")->getString("maphide");
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
- if (validated && !IS_OPER(user) && !url.empty() && (command == "MAP" || command == "LINKS"))
+ if (validated && !user->IsOper() && !url.empty() && (command == "MAP" || command == "LINKS"))
{
- user->WriteServ("NOTICE %s :/%s has been disabled; visit %s", user->nick.c_str(), command.c_str(), url.c_str());
+ user->WriteNotice("/" + command + " has been disabled; visit " + url);
return MOD_RES_DENY;
}
else
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleMapHide()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Hide /MAP and /LINKS in the same form as ircu (mostly useless)", VF_VENDOR);
}
};
MODULE_INIT(ModuleMapHide)
-
*/
-/* $ModDesc: Allows for MD5 encrypted oper passwords */
-
#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
+#include "modules/hash.h"
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define MD5STEP(f,w,x,y,z,in,s) \
(w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
-#endif
-
typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */
typedef unsigned char byte;
MD5Final((unsigned char*)dest, &context);
}
-
- void GenHash(const char* src, char* dest, const char* xtab, unsigned int* ikey, size_t srclen)
- {
- unsigned char bytes[16];
-
- MyMD5((char*)bytes, (void*)src, srclen, ikey);
-
- for (int i = 0; i < 16; i++)
- {
- *dest++ = xtab[bytes[i] / 16];
- *dest++ = xtab[bytes[i] % 16];
- }
- *dest++ = 0;
- }
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
char res[16];
MyMD5(res, (void*)data.data(), data.length(), NULL);
return std::string(res, 16);
}
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- char res[33];
- GenHash(sdata.data(), res, HexMap, IV, sdata.length());
- return res;
- }
-
- MD5Provider(Module* parent) : HashProvider(parent, "hash/md5", 16, 64) {}
+ MD5Provider(Module* parent) : HashProvider(parent, "md5", 16, 64) {}
};
class ModuleMD5 : public Module
public:
ModuleMD5() : md5(this)
{
- ServerInstance->Modules->AddService(md5);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements MD5 hashing",VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +f (message flood protection) */
-
/** Holds flood settings and state for mode +f
*/
class floodsettings
/** Handles channel mode +f
*/
-class MsgFlood : public ModeHandler
+class MsgFlood : public ParamMode<MsgFlood, SimpleExtItem<floodsettings> >
{
public:
- SimpleExtItem<floodsettings> ext;
- MsgFlood(Module* Creator) : ModeHandler(Creator, "flood", 'f', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("messageflood", Creator) { }
+ MsgFlood(Module* Creator)
+ : ParamMode<MsgFlood, SimpleExtItem<floodsettings> >(Creator, "flood", 'f')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- std::string::size_type colon = parameter.find(':');
- if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
-
- /* Set up the flood parameters for this channel */
- bool ban = (parameter[0] == '*');
- unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
- unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
- if ((nlines<2) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str());
+ return MODEACTION_DENY;
+ }
- floodsettings* f = ext.get(channel);
- if ((f) && (nlines == f->lines) && (nsecs == f->secs) && (ban == f->ban))
- // mode params match
- return MODEACTION_DENY;
+ /* Set up the flood parameters for this channel */
+ bool ban = (parameter[0] == '*');
+ unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
- ext.set(channel, new floodsettings(ban, nsecs, nlines));
- parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" + ConvToStr(nsecs);
- channel->SetModeParam('f', parameter);
- return MODEACTION_ALLOW;
- }
- else
+ if ((nlines<2) || (nsecs<1))
{
- if (!channel->IsModeSet('f'))
- return MODEACTION_DENY;
-
- ext.unset(channel);
- channel->SetModeParam('f', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str());
+ return MODEACTION_DENY;
}
+
+ ext.set(channel, new floodsettings(ban, nsecs, nlines));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const floodsettings* fs, std::string& out)
+ {
+ if (fs->ban)
+ out.push_back('*');
+ out.append(ConvToStr(fs->lines)).push_back(':');
+ out.append(ConvToStr(fs->secs));
}
};
{
}
- void init()
+ ModResult OnUserPreMessage(User* user, void* voiddest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(mf);
- ServerInstance->Modules->AddService(mf.ext);
- Implementation eventlist[] = { I_OnUserPreNotice, I_OnUserPreMessage };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ if (target_type != TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
- ModResult ProcessMessages(User* user,Channel* dest, const std::string &text)
- {
- if ((!IS_LOCAL(user)) || !dest->IsModeSet('f'))
+ Channel* dest = static_cast<Channel*>(voiddest);
+ if ((!IS_LOCAL(user)) || !dest->IsModeSet(mf))
return MOD_RES_PASSTHRU;
if (ServerInstance->OnCheckExemption(user,dest,"flood") == MOD_RES_ALLOW)
f->clear(user);
if (f->ban)
{
- std::vector<std::string> parameters;
- parameters.push_back(dest->name);
- parameters.push_back("+b");
- parameters.push_back(user->MakeWildHost());
- ServerInstance->SendGlobalMode(parameters, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, dest, NULL, changelist);
}
- char kickmessage[MAXBUF];
- snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %u lines in %u secs)", f->lines, f->secs);
+ const std::string kickMessage = "Channel flood triggered (limit is " + ConvToStr(f->lines) +
+ " in " + ConvToStr(f->secs) + " secs)";
- dest->KickUser(ServerInstance->FakeClient, user, kickmessage);
+ dest->KickUser(ServerInstance->FakeClient, user, kickMessage);
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- return ProcessMessages(user,(Channel*)dest,text);
-
- return MOD_RES_PASSTHRU;
- }
-
- ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- if (target_type == TYPE_CHANNEL)
- return ProcessMessages(user,(Channel*)dest,text);
-
- return MOD_RES_PASSTHRU;
- }
-
void Prioritize()
{
// we want to be after all modules that might deny the message (e.g. m_muteban, m_noctcp, m_blockcolor, etc.)
ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
- ServerInstance->Modules->SetPriority(this, I_OnUserPreNotice, PRIORITY_LAST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +f (message flood protection)", VF_VENDOR);
}
*/
-/* $ModDesc: Implements the ability to have server-side MLOCK enforcement. */
-
#include "inspircd.h"
class ModuleMLock : public Module
{
-private:
StringExtItem mlock;
-public:
- ModuleMLock() : mlock("mlock", this) {};
-
- void init()
+ public:
+ ModuleMLock()
+ : mlock("mlock", this)
{
- ServerInstance->Modules->Attach(I_OnRawMode, this);
- ServerInstance->Modules->AddService(this->mlock);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements the ability to have server-side MLOCK enforcement.", VF_VENDOR);
}
- ModResult OnRawMode(User* source, Channel* channel, const char mode, const std::string& parameter, bool adding, int pcnt)
+ ModResult OnRawMode(User* source, Channel* channel, ModeHandler* mh, const std::string& parameter, bool adding)
{
if (!channel)
return MOD_RES_PASSTHRU;
if (!mlock_str)
return MOD_RES_PASSTHRU;
+ const char mode = mh->GetModeChar();
std::string::size_type p = mlock_str->find(mode);
if (p != std::string::npos)
{
return MOD_RES_PASSTHRU;
}
-
};
MODULE_INIT(ModuleMLock)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class CommandModeNotice : public Command
+{
+ public:
+ CommandModeNotice(Module* parent) : Command(parent,"MODENOTICE",2,2)
+ {
+ syntax = "<modes> <message>";
+ flags_needed = 'o';
+ }
+
+ CmdResult Handle(const std::vector<std::string>& parameters, User *src)
+ {
+ std::string msg = "*** From " + src->nick + ": " + parameters[1];
+ int mlen = parameters[0].length();
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ User* user = *i;
+ for (int n = 0; n < mlen; n++)
+ {
+ if (!user->IsModeSet(parameters[0][n]))
+ goto next_user;
+ }
+ user->WriteNotice(msg);
+next_user: ;
+ }
+ return CMD_SUCCESS;
+ }
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return ROUTE_OPT_BCAST;
+ }
+};
+
+class ModuleModeNotice : public Module
+{
+ CommandModeNotice cmd;
+
+ public:
+ ModuleModeNotice()
+ : cmd(this)
+ {
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the /MODENOTICE command", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleModeNotice)
#include "inspircd.h"
-/* $ModDesc: Implements extban +b m: - mute bans */
-
class ModuleQuietBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleQuietBan()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements extban +b m: - mute bans",VF_OPTCOMMON|VF_VENDOR);
}
- virtual ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (!IS_LOCAL(user) || target_type != TYPE_CHANNEL)
return MOD_RES_PASSTHRU;
Channel* chan = static_cast<Channel*>(dest);
if (chan->GetExtBanStatus(user, 'm') == MOD_RES_DENY && chan->GetPrefixValue(user) < VOICE_VALUE)
{
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're muted)", user->nick.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're muted)", chan->name.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('m');
+ tokens["EXTBAN"].push_back('m');
}
};
-
MODULE_INIT(ModuleQuietBan)
-
*/
-/* $ModDesc: Provides the ability to manipulate modes via long names. */
-
#include "inspircd.h"
static void DisplayList(User* user, Channel* channel)
{
std::stringstream items;
- for(char letter = 'A'; letter <= 'z'; letter++)
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (!mh || mh->IsListMode())
- continue;
- if (!channel->IsModeSet(letter))
+ ModeHandler* mh = i->second;
+ if (!channel->IsModeSet(mh))
continue;
items << " +" << mh->name;
if (mh->GetNumParams(true))
- items << " " << channel->GetModeParameter(letter);
+ items << " " << channel->GetModeParameter(mh);
}
- char pfx[MAXBUF];
- snprintf(pfx, MAXBUF, ":%s 961 %s %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), channel->name.c_str());
- user->SendText(std::string(pfx), items);
- user->WriteNumeric(960, "%s %s :End of mode list", user->nick.c_str(), channel->name.c_str());
+ const std::string line = ":" + ServerInstance->Config->ServerName + " 961 " + user->nick + " " + channel->name;
+ user->SendText(line, items);
+ user->WriteNumeric(960, "%s :End of mode list", channel->name.c_str());
}
class CommandProp : public Command
CmdResult Handle(const std::vector<std::string> ¶meters, User *src)
{
+ Channel* const chan = ServerInstance->FindChan(parameters[0]);
+ if (!chan)
+ {
+ src->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
if (parameters.size() == 1)
{
- Channel* chan = ServerInstance->FindChan(parameters[0]);
- if (chan)
- DisplayList(src, chan);
+ DisplayList(src, chan);
return CMD_SUCCESS;
}
unsigned int i = 1;
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back("");
+ Modes::ChangeList modes;
while (i < parameters.size())
{
std::string prop = parameters[i++];
if (prop[0] == '+' || prop[0] == '-')
prop.erase(prop.begin());
- for(char letter = 'A'; letter <= 'z'; letter++)
+ ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
+ if (mh)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (mh && mh->name == prop)
+ if (mh->GetNumParams(plus))
{
- modes[1].append((plus ? "+" : "-") + std::string(1, letter));
- if (mh->GetNumParams(plus))
- {
- if (i != parameters.size())
- modes.push_back(parameters[i++]);
- }
+ if (i != parameters.size())
+ modes.push(mh, plus, parameters[i++]);
}
+ else
+ modes.push(mh, plus);
}
}
- ServerInstance->SendGlobalMode(modes, src);
+ ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
return CMD_SUCCESS;
}
};
{
list = true;
}
+
+ // Handle /MODE #chan Z
+ void DisplayList(User* user, Channel* chan)
+ {
+ ::DisplayList(user, chan);
+ }
};
class ModuleNamedModes : public Module
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(dummyZ);
-
- Implementation eventlist[] = { I_OnPreMode };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the ability to manipulate modes via long names.",VF_VENDOR);
}
ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
}
- ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (!channel)
return MOD_RES_PASSTHRU;
- if (parameters[1].find('Z') == std::string::npos)
- return MOD_RES_PASSTHRU;
- if (parameters.size() <= 2)
- {
- DisplayList(source, channel);
- return MOD_RES_DENY;
- }
- std::vector<std::string> newparms;
- newparms.push_back(parameters[0]);
- newparms.push_back(parameters[1]);
-
- std::string modelist = newparms[1];
- bool adding = true;
- unsigned int param_at = 2;
- for(unsigned int i = 0; i < modelist.length(); i++)
+ Modes::ChangeList::List& list = modes.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); )
{
- unsigned char modechar = modelist[i];
- if (modechar == '+' || modechar == '-')
- {
- adding = (modechar == '+');
- continue;
- }
- ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL);
- if (modechar == 'Z')
+ Modes::Change& curr = *i;
+ // Replace all namebase (dummyZ) modes being changed with the actual
+ // mode handler and parameter. The parameter format of the namebase mode is
+ // <modename>[=<parameter>].
+ if (curr.mh == &dummyZ)
{
- modechar = 0;
- std::string name, value;
- if (param_at < parameters.size())
- name = parameters[param_at++];
+ std::string name = curr.param;
+ std::string value;
std::string::size_type eq = name.find('=');
if (eq != std::string::npos)
{
value = name.substr(eq + 1);
name = name.substr(0, eq);
}
- for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++)
+
+ ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
+ if (!mh)
+ {
+ // Mode handler not found
+ i = list.erase(i);
+ continue;
+ }
+
+ curr.param.clear();
+ if (mh->GetNumParams(curr.adding))
{
- mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL);
- if (mh && mh->name == name)
+ if (value.empty())
{
- if (mh->GetNumParams(adding))
- {
- if (!value.empty())
- {
- newparms.push_back(value);
- modechar = letter;
- break;
- }
- }
- else
- {
- modechar = letter;
- break;
- }
+ // Mode needs a parameter but there wasn't one
+ i = list.erase(i);
+ continue;
}
+
+ // Change parameter to the text after the '='
+ curr.param = value;
}
- if (modechar)
- modelist[i] = modechar;
- else
- modelist.erase(i--, 1);
- }
- else if (mh && mh->GetNumParams(adding) && param_at < parameters.size())
- {
- newparms.push_back(parameters[param_at++]);
+
+ // Put the actual ModeHandler in place of the namebase handler
+ curr.mh = mh;
}
+
+ ++i;
}
- newparms[1] = modelist;
- ServerInstance->Modes->Process(newparms, source, false);
- return MOD_RES_DENY;
+
+ return MOD_RES_PASSTHRU;
}
};
#include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the NAMESX (CAP multi-prefix) capability. */
+#include "modules/cap.h"
class ModuleNamesX : public Module
{
- public:
GenericCap cap;
+ public:
ModuleNamesX() : cap(this, "multi-prefix")
{
}
- void init()
- {
- Implementation eventlist[] = { I_OnPreCommand, I_OnNamesListItem, I_On005Numeric, I_OnEvent, I_OnSendWhoLine };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- ~ModuleNamesX()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the NAMESX (CAP multi-prefix) capability.",VF_VENDOR);
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" NAMESX");
+ tokens["NAMESX"];
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
return MOD_RES_PASSTHRU;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (!cap.ext.get(issuer))
- return;
-
- /* Some module hid this from being displayed, dont bother */
- if (nick.empty())
- return;
+ if (cap.ext.get(issuer))
+ prefixes = memb->GetAllPrefixChars();
- prefixes = memb->chan->GetAllPrefixChars(memb->user);
+ return MOD_RES_PASSTHRU;
}
- void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
+ void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
{
- if (!cap.ext.get(source))
+ if ((!memb) || (!cap.ext.get(source)))
return;
// Channel names can contain ":", and ":" as a 'start-of-token' delimiter is
return;
// 352 21DAAAAAB #chan ident localhost insp21.test 21DAAAAAB H@ :0 a
- // a b pos
- std::string::size_type a = 4 + source->nick.length() + 1;
- std::string::size_type b = line.find(' ', a);
- if (b == std::string::npos)
- return;
-
- // Try to find this channel
- std::string channame = line.substr(a, b-a);
- Channel* chan = ServerInstance->FindChan(channame);
- if (!chan)
- return;
+ // pos
// Don't do anything if the user has only one prefix
- std::string prefixes = chan->GetAllPrefixChars(user);
+ std::string prefixes = memb->GetAllPrefixChars();
if (prefixes.length() <= 1)
return;
line.insert(pos, prefixes);
}
- void OnEvent(Event& ev)
+ void OnEvent(Event& ev) CXX11_OVERRIDE
{
cap.HandleEvent(ev);
}
by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru */
#include "inspircd.h"
-#include "caller.h"
#include <fstream>
-/* $ModDesc: Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING */
-
-class lwbNickHandler : public HandlerBase2<bool, const char*, size_t>
+class lwbNickHandler : public HandlerBase1<bool, const std::string&>
{
public:
- lwbNickHandler() { }
- virtual ~lwbNickHandler() { }
- virtual bool Call(const char*, size_t);
+ bool Call(const std::string&);
};
/*,m_reverse_additionalUp[256];*/
/* Conditions added */
-bool lwbNickHandler::Call(const char* n, size_t max)
+bool lwbNickHandler::Call(const std::string& nick)
{
- if (!n || !*n)
+ if (nick.empty())
return false;
+ const char* n = nick.c_str();
unsigned int p = 0;
for (const char* i = n; *i; i++, p++)
{
}
/* too long? or not -- pointer arithmetic rocks */
- return (p < max);
+ return (p < ServerInstance->Config->Limits.NickMax);
}
class ModuleNationalChars : public Module
{
- private:
lwbNickHandler myhandler;
std::string charset, casemapping;
unsigned char m_additional[256], m_additionalUp[256], m_lower[256], m_upper[256];
- caller2<bool, const char*, size_t> rememberer;
+ caller1<bool, const std::string&> rememberer;
bool forcequit;
const unsigned char * lowermap_rememberer;
unsigned char prev_map[256];
+ template <typename T>
+ void RehashHashmap(T& hashmap)
+ {
+ T newhash(hashmap.bucket_count());
+ for (typename T::const_iterator i = hashmap.begin(); i != hashmap.end(); ++i)
+ newhash.insert(std::make_pair(i->first, i->second));
+ hashmap.swap(newhash);
+ }
+
void CheckRehash()
{
// See if anything changed
memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
- ServerInstance->RehashUsersAndChans();
+ RehashHashmap(ServerInstance->Users.clientlist);
+ RehashHashmap(ServerInstance->Users.uuidlist);
+ RehashHashmap(ServerInstance->chanlist);
// The OnGarbageCollect() method in m_watch rebuilds the hashmap used by it
Module* mod = ServerInstance->Modules->Find("m_watch.so");
if (mod)
mod->OnGarbageCollect();
-
- // Send a Request to m_spanningtree asking it to rebuild its hashmaps
- mod = ServerInstance->Modules->Find("m_spanningtree.so");
- if (mod)
- {
- Request req(this, mod, "rehash");
- req.Send();
- }
}
public:
memcpy(prev_map, national_case_insensitive_map, sizeof(prev_map));
}
- void init()
+ void init() CXX11_OVERRIDE
{
memcpy(m_lower, rfc_case_insensitive_map, 256);
national_case_insensitive_map = m_lower;
ServerInstance->IsNick = &myhandler;
-
- Implementation eventlist[] = { I_OnRehash, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- std::string tmp(casemapping);
- tmp.insert(0, "CASEMAPPING=");
- SearchAndReplace(output, std::string("CASEMAPPING=rfc1459"), tmp);
+ tokens["CASEMAPPING"] = casemapping;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("nationalchars");
charset = tag->getString("file");
if (!forcequit)
return;
- for (LocalUserList::const_iterator iter = ServerInstance->Users->local_users.begin(); iter != ServerInstance->Users->local_users.end(); ++iter)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter)
{
/* Fix by Brain: Dont quit UID users */
User* n = *iter;
- if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick.c_str(), ServerInstance->Config->Limits.NickMax))
+ if (!isdigit(n->nick[0]) && !ServerInstance->IsNick(n->nick))
ServerInstance->Users->QuitUser(n, message);
}
}
- virtual ~ModuleNationalChars()
+ ~ModuleNationalChars()
{
ServerInstance->IsNick = rememberer;
national_case_insensitive_map = lowermap_rememberer;
CheckRehash();
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides an ability to have non-RFC1459 nicks & support for national CASEMAPPING", VF_VENDOR | VF_COMMON, charset);
}
/*so Bynets Unreal distribution stuff*/
void loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
{
- std::ifstream ifs(filename.c_str());
+ std::ifstream ifs(ServerInstance->Config->Paths.PrependConfig(filename).c_str());
if (ifs.fail())
{
- ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for missing file: %s", filename.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for missing file: %s", filename.c_str());
return;
}
{
if (loadtable(ifs, tables[n], 255) && (n < faillimit))
{
- ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
return;
}
}
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +F (nick flood protection) */
-
/** Holds settings and state associated with channel mode +F
*/
class nickfloodsettings
/** Handles channel mode +F
*/
-class NickFlood : public ModeHandler
+class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
{
public:
- SimpleExtItem<nickfloodsettings> ext;
- NickFlood(Module* Creator) : ModeHandler(Creator, "nickflood", 'F', PARAM_SETONLY, MODETYPE_CHANNEL),
- ext("nickflood", Creator) { }
+ NickFlood(Module* Creator)
+ : ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >(Creator, "nickflood", 'F')
+ {
+ }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
+ std::string::size_type colon = parameter.find(':');
+ if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
{
- std::string::size_type colon = parameter.find(':');
- if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
-
- /* Set up the flood parameters for this channel */
- unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
- unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
-
- if ((nnicks<1) || (nsecs<1))
- {
- source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ return MODEACTION_DENY;
+ }
- nickfloodsettings* f = ext.get(channel);
- if ((f) && (nnicks == f->nicks) && (nsecs == f->secs))
- // mode params match
- return MODEACTION_DENY;
+ /* Set up the flood parameters for this channel */
+ unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
+ unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
- ext.set(channel, new nickfloodsettings(nsecs, nnicks));
- parameter = ConvToStr(nnicks) + ":" + ConvToStr(nsecs);
- channel->SetModeParam('F', parameter);
- return MODEACTION_ALLOW;
- }
- else
+ if ((nnicks<1) || (nsecs<1))
{
- if (!channel->IsModeSet('F'))
- return MODEACTION_DENY;
-
- ext.unset(channel);
- channel->SetModeParam('F', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str());
+ return MODEACTION_DENY;
}
+
+ ext.set(channel, new nickfloodsettings(nsecs, nnicks));
+ return MODEACTION_ALLOW;
+ }
+
+ void SerializeParam(Channel* chan, const nickfloodsettings* nfs, std::string& out)
+ {
+ out.append(ConvToStr(nfs->nicks)).push_back(':');
+ out.append(ConvToStr(nfs->secs));
}
};
NickFlood nf;
public:
-
ModuleNickFlood()
: nf(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(nf);
- ServerInstance->Modules->AddService(nf.ext);
- Implementation eventlist[] = { I_OnUserPreNick, I_OnUserPostNick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
- return MOD_RES_PASSTHRU;
-
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- Channel *channel = *i;
+ Channel* channel = (*i)->chan;
ModResult res;
nickfloodsettings *f = nf.ext.get(channel);
if (f->islocked())
{
- user->WriteNumeric(447, "%s :%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", user->nick.c_str(), channel->name.c_str(), f->nicks, f->secs);
+ user->WriteNumeric(ERR_CANTCHANGENICK, ":%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), f->nicks, f->secs);
return MOD_RES_DENY;
}
/*
* XXX: HACK: We do the increment on the *POST* event here (instead of all together) because we have no way of knowing whether other modules would block a nickchange.
*/
- void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
if (isdigit(user->nick[0])) /* allow switches to UID */
return;
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); ++i)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
{
- Channel *channel = *i;
+ Channel* channel = (*i)->chan;
ModResult res;
nickfloodsettings *f = nf.ext.get(channel);
}
}
- ~ModuleNickFlood()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Channel mode F - nick flood protection", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit */
-
/** Handle /NICKLOCK
*/
class CommandNicklock : public Command
{
flags_needed = 'o';
syntax = "<oldnick> <newnick>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle(const std::vector<std::string>& parameters, User *user)
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
/* Do local sanity checks and bails */
if (IS_LOCAL(user))
{
- if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[1] + "'");
return CMD_FAILURE;
}
- user->WriteServ("947 %s %s :Nickname now locked.", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNumeric(947, "%s :Nickname now locked.", parameters[1].c_str());
}
/* If we made it this far, extend the user */
locked.set(target, 1);
std::string oldnick = target->nick;
- if (target->ForceNickChange(parameters[1].c_str()))
+ if (target->ChangeNick(parameters[1]))
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKLOCK to change and hold "+oldnick+" to "+parameters[1]);
else
{
{
flags_needed = 'o';
syntax = "<locked-nick>";
- TRANSLATE2(TR_NICK, TR_END);
+ TRANSLATE1(TR_NICK);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
if (!target)
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
}
};
-
class ModuleNickLock : public Module
{
LocalIntExt locked;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- ServerInstance->Modules->AddService(locked);
- ServerInstance->Modules->Attach(I_OnUserPreNick, this);
- }
-
- ~ModuleNickLock()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the NICKLOCK command, allows an oper to change a users nick and lock them to it until they quit", VF_OPTCOMMON | VF_VENDOR);
}
- ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
- if (ServerInstance->NICKForced.get(user)) /* Allow forced nick changes */
- return MOD_RES_PASSTHRU;
-
if (locked.get(user))
{
- user->WriteNumeric(447, "%s :You cannot change your nickname (your nick is locked)",user->nick.c_str());
+ user->WriteNumeric(ERR_CANTCHANGENICK, ":You cannot change your nickname (your nick is locked)");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +C to block CTCPs */
-
class NoCTCP : public SimpleChannelModeHandler
{
public:
class ModuleNoCTCP : public Module
{
-
NoCTCP nc;
public:
-
ModuleNoCTCP()
: nc(this)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(nc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleNoCTCP()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +C to block CTCPs", VF_VENDOR);
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet('C')))
+ if (!c->GetExtBanStatus(user, 'C').check(!c->IsModeSet(nc)))
{
- user->WriteNumeric(ERR_NOCTCPALLOWED, "%s %s :Can't send CTCP to channel (+C set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_NOCTCPALLOWED, "%s :Can't send CTCP to channel (+C set)", c->name.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('C');
+ tokens["EXTBAN"].push_back('C');
}
};
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +Q to prevent kicks on the channel. */
-
class NoKicks : public SimpleChannelModeHandler
{
public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(nk);
- Implementation eventlist[] = { I_OnUserPreKick, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('Q');
+ tokens["EXTBAN"].push_back('Q');
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet('Q')))
+ if (!memb->chan->GetExtBanStatus(source, 'Q').check(!memb->chan->IsModeSet(nk)))
{
// Can't kick with Q in place, not even opers with override, and founders
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Can't kick user %s from channel (+Q set)",source->nick.c_str(), memb->chan->name.c_str(), memb->user->nick.c_str());
+ source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :Can't kick user %s from channel (+Q set)", memb->chan->name.c_str(), memb->user->nick.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ~ModuleNoKicks()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +Q to prevent kicks on the channel.", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleNoKicks)
#include "inspircd.h"
-/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */
-
class NoNicks : public SimpleChannelModeHandler
{
public:
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(nn);
- Implementation eventlist[] = { I_OnUserPreNick, I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleNoNickChange()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +N & extban +b N: which prevents nick changes on channel", VF_VENDOR);
}
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('N');
+ tokens["EXTBAN"].push_back('N');
}
- virtual ModResult OnUserPreNick(User* user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
- // Allow forced nick changes.
- if (ServerInstance->NICKForced.get(user))
- return MOD_RES_PASSTHRU;
-
- for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
+ for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
{
- Channel* curr = *i;
+ Channel* curr = (*i)->chan;
ModResult res = ServerInstance->OnCheckExemption(user,curr,"nonick");
if (res == MOD_RES_ALLOW)
continue;
- if (override && IS_OPER(user))
+ if (override && user->IsOper())
continue;
- if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet('N')))
+ if (!curr->GetExtBanStatus(user, 'N').check(!curr->IsModeSet(nn)))
{
- user->WriteNumeric(ERR_CANTCHANGENICK, "%s :Can't change nickname while on %s (+N is set)",
- user->nick.c_str(), curr->name.c_str());
+ user->WriteNumeric(ERR_CANTCHANGENICK, ":Can't change nickname while on %s (+N is set)",
+ curr->name.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
override = ServerInstance->Config->ConfValue("nonicks")->getBool("operoverride", false);
}
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +T to block notices to the channel */
-
class NoNotice : public SimpleChannelModeHandler
{
public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(nt);
- Implementation eventlist[] = { I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('T');
+ tokens["EXTBAN"].push_back('T');
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
ModResult res;
- if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
+ if ((msgtype == MSG_NOTICE) && (target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
Channel* c = (Channel*)dest;
- if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet('T')))
+ if (!c->GetExtBanStatus(user, 'T').check(!c->IsModeSet(nt)))
{
res = ServerInstance->OnCheckExemption(user,c,"nonotice");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
else
{
- user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s %s :Can't send NOTICE to channel (+T set)",user->nick.c_str(), c->name.c_str());
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Can't send NOTICE to channel (+T set)", c->name.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleNoNotice()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +T to block notices to the channel", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Implements extban +b p: - part message bans */
-
class ModulePartMsgBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnUserPart, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModulePartMsgBan()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements extban +b p: - part message bans", VF_OPTCOMMON|VF_VENDOR);
}
-
- virtual void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
{
if (!IS_LOCAL(memb->user))
return;
if (memb->chan->GetExtBanStatus(memb->user, 'p') == MOD_RES_DENY)
partmessage.clear();
-
- return;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('p');
+ tokens["EXTBAN"].push_back('p');
}
};
-
MODULE_INIT(ModulePartMsgBan)
-
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2009 Taros <taros34@hotmail.com>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/*
- * Written for InspIRCd-1.2 by Taros on the Tel'Laerad M&D Team
- * <http://tellaerad.net>
- */
-
#include "inspircd.h"
-/* $ModConfig: <ojoin prefix="!" notice="yes" op="yes">
- * Specify the prefix that +Y will grant here, it should be unused.
- * Leave prefix empty if you do not wish +Y to grant a prefix
- * If notice is set to on, upon ojoin, the server will notice
- * the channel saying that the oper is joining on network business
- * If op is set to on, it will give them +o along with +Y */
-/* $ModDesc: Provides the /ojoin command, which joins a user to a channel on network business, and gives them +Y, which makes them immune to kick / deop and so on. */
-/* $ModAuthor: Taros */
-/* $ModAuthorMail: taros34@hotmail.com */
-
-/* A note: This will not protect against kicks from services,
- * ulines, or operoverride. */
-
#define NETWORK_VALUE 9000000
-char NPrefix;
-bool notice;
-bool op;
-
/** Handle /OJOIN
*/
-class CommandOjoin : public Command
+class CommandOjoin : public SplitCommand
{
public:
bool active;
- CommandOjoin(Module* parent) : Command(parent,"OJOIN", 1)
+ bool notice;
+ bool op;
+ ModeHandler* npmh;
+ CommandOjoin(Module* parent, ModeHandler& mode)
+ : SplitCommand(parent, "OJOIN", 1)
+ , npmh(&mode)
{
flags_needed = 'o'; Penalty = 0; syntax = "<channel>";
active = false;
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
// Make sure the channel name is allowable.
- if (!ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(parameters[0]))
{
- user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+ user->WriteNotice("*** Invalid characters in channel name or name too long");
return CMD_FAILURE;
}
active = true;
- Channel* channel = Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
+ // override is false because we want OnUserPreJoin to run
+ Channel* channel = Channel::JoinUser(user, parameters[0], false);
active = false;
if (channel)
}
else
{
+ channel = ServerInstance->FindChan(parameters[0]);
+ if (!channel)
+ return CMD_FAILURE;
+
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used OJOIN in "+parameters[0]);
// they're already in the channel
- std::vector<std::string> modes;
- modes.push_back(parameters[0]);
- modes.push_back(op ? "+Yo" : "+Y");
- modes.push_back(user->nick);
+ Modes::ChangeList changelist;
+ changelist.push_add(npmh, user->nick);
if (op)
- modes.push_back(user->nick);
- ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient);
+ changelist.push_add(ServerInstance->Modes->FindMode('o', MODETYPE_CHANNEL), user->nick);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
}
return CMD_SUCCESS;
}
/** channel mode +Y
*/
-class NetworkPrefix : public ModeHandler
+class NetworkPrefix : public PrefixMode
{
public:
- NetworkPrefix(Module* parent) : ModeHandler(parent, "official-join", 'Y', PARAM_ALWAYS, MODETYPE_CHANNEL)
+ NetworkPrefix(Module* parent, char NPrefix)
+ : PrefixMode(parent, "official-join", 'Y', NETWORK_VALUE, NPrefix)
{
- list = true;
- prefix = NPrefix;
levelrequired = INT_MAX;
- m_paramtype = TR_NICK;
- }
-
- void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- const UserMembList* cl = channel->GetUsers();
- std::vector<std::string> mode_junk;
- mode_junk.push_back(channel->name);
- irc::modestacker modestack(false);
- std::deque<std::string> stackresult;
-
- for (UserMembCIter i = cl->begin(); i != cl->end(); i++)
- {
- if (i->second->hasMode('Y'))
- {
- if (stack)
- stack->Push(this->GetModeChar(), i->first->nick);
- else
- modestack.Push(this->GetModeChar(), i->first->nick);
- }
- }
-
- if (stack)
- return;
-
- while (modestack.GetStackedLine(stackresult))
- {
- mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
- ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
- mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
- }
- }
-
- unsigned int GetPrefixRank()
- {
- return NETWORK_VALUE;
- }
-
- void RemoveMode(User* user, irc::modestacker* stack)
- {
}
ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding)
return MOD_RES_PASSTHRU;
}
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- return MODEACTION_ALLOW;
- }
-
};
class ModuleOjoin : public Module
{
- NetworkPrefix* np;
+ NetworkPrefix np;
CommandOjoin mycommand;
public:
ModuleOjoin()
- : np(NULL), mycommand(this)
+ : np(this, ServerInstance->Config->ConfValue("ojoin")->getString("prefix").c_str()[0])
+ , mycommand(this, np)
{
}
- void init()
- {
- /* Load config stuff */
- OnRehash(NULL);
-
- /* Initialise module variables */
- np = new NetworkPrefix(this);
-
- ServerInstance->Modules->AddService(*np);
- ServerInstance->Modules->AddService(mycommand);
-
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserPreKick, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (mycommand.active)
{
- privs += 'Y';
- if (op)
+ privs += np.GetModeChar();
+ if (mycommand.op)
privs += 'o';
return MOD_RES_ALLOW;
}
return MOD_RES_PASSTHRU;
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* Conf = ServerInstance->Config->ConfValue("ojoin");
-
- if (!np)
- {
- // This is done on module load only
- std::string npre = Conf->getString("prefix");
- NPrefix = npre.empty() ? 0 : npre[0];
-
- if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
- throw ModuleException("Looks like the +Y prefix you picked for m_ojoin is already in use. Pick another.");
- }
-
- notice = Conf->getBool("notice", true);
- op = Conf->getBool("op", true);
+ mycommand.notice = Conf->getBool("notice", true);
+ mycommand.op = Conf->getBool("op", true);
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
// Don't do anything if they're not +Y
- if (!memb->hasMode('Y'))
+ if (!memb->hasMode(np.GetModeChar()))
return MOD_RES_PASSTHRU;
// Let them do whatever they want to themselves.
if (source == memb->user)
return MOD_RES_PASSTHRU;
- source->WriteNumeric(484, source->nick+" "+memb->chan->name+" :Can't kick "+memb->user->nick+" as they're on official network business.");
+ source->WriteNumeric(ERR_RESTRICTED, memb->chan->name+" :Can't kick "+memb->user->nick+" as they're on official network business.");
return MOD_RES_DENY;
}
- ~ModuleOjoin()
- {
- delete np;
- }
-
void Prioritize()
{
ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Network Business Join", VF_VENDOR);
}
};
MODULE_INIT(ModuleOjoin)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
-
class OperChans : public SimpleChannelModeHandler
{
public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(oc);
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric, I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- if (chan && chan->IsModeSet('O') && !IS_OPER(user))
+ if (chan && chan->IsModeSet(oc) && !user->IsOper())
{
- user->WriteNumeric(ERR_CANTJOINOPERSONLY, "%s %s :Only IRC operators may join %s (+O is set)",
- user->nick.c_str(), chan->name.c_str(), chan->name.c_str());
+ user->WriteNumeric(ERR_CANTJOINOPERSONLY, "%s :Only IRC operators may join %s (+O is set)",
+ chan->name.c_str(), chan->name.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'O') && (mask[1] == ':'))
{
- if (IS_OPER(user) && InspIRCd::Match(user->oper->name, mask.substr(2)))
+ if (user->IsOper() && InspIRCd::Match(user->oper->name, mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
- {
- ServerInstance->AddExtBanChar('O');
- }
-
- ~ModuleOperChans()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
+ tokens["EXTBAN"].push_back('O');
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for oper-only chans via the +O channel mode and 'O' extban", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Forces opers to join the specified channel(s) on oper-up */
-
class ModuleOperjoin : public Module
{
- private:
- std::string operChan;
std::vector<std::string> operChans;
bool override;
- int tokenize(const std::string &str, std::vector<std::string> &tokens)
- {
- // skip delimiters at beginning.
- std::string::size_type lastPos = str.find_first_not_of(",", 0);
- // find first "non-delimiter".
- std::string::size_type pos = str.find_first_of(",", lastPos);
-
- while (std::string::npos != pos || std::string::npos != lastPos)
- {
- // found a token, add it to the vector.
- tokens.push_back(str.substr(lastPos, pos - lastPos));
- // skip delimiters. Note the "not_of"
- lastPos = str.find_first_not_of(",", pos);
- // find next "non-delimiter"
- pos = str.find_first_of(",", lastPos);
- }
- return tokens.size();
- }
-
public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnPostOper, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("operjoin");
- operChan = tag->getString("channel");
override = tag->getBool("override", false);
+ irc::commasepstream ss(tag->getString("channel"));
operChans.clear();
- if (!operChan.empty())
- tokenize(operChan,operChans);
- }
- virtual ~ModuleOperjoin()
- {
+ for (std::string channame; ss.GetToken(channame); )
+ operChans.push_back(channame);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Forces opers to join the specified channel(s) on oper-up", VF_VENDOR);
}
- virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
- if (!IS_LOCAL(user))
+ LocalUser* localuser = IS_LOCAL(user);
+ if (!localuser)
return;
- for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++)
- if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
- Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
+ for (std::vector<std::string>::const_iterator i = operChans.begin(); i != operChans.end(); ++i)
+ if (ServerInstance->IsChannel(*i))
+ Channel::JoinUser(localuser, *i, override);
- std::string chanList = IS_OPER(user)->getConfig("autojoin");
- if (!chanList.empty())
+ irc::commasepstream ss(localuser->oper->getConfig("autojoin"));
+ for (std::string channame; ss.GetToken(channame); )
{
- std::vector<std::string> typechans;
- tokenize(chanList, typechans);
- for (std::vector<std::string>::const_iterator it = typechans.begin(); it != typechans.end(); ++it)
- {
- if (ServerInstance->IsChannel(it->c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- Channel::JoinUser(user, it->c_str(), override, "", false, ServerInstance->Time());
- }
- }
+ if (ServerInstance->IsChannel(channame))
+ Channel::JoinUser(localuser, channame, override);
}
}
};
*/
-/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */
-
#include "inspircd.h"
class ModuleOperLevels : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnKill, this);
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives each oper type a 'level', cannot kill opers 'above' your level.", VF_VENDOR);
}
- virtual ModResult OnKill(User* source, User* dest, const std::string &reason)
+ ModResult OnKill(User* source, User* dest, const std::string &reason) CXX11_OVERRIDE
{
// oper killing an oper?
- if (IS_OPER(dest) && IS_OPER(source))
+ if (dest->IsOper() && source->IsOper())
{
std::string level = dest->oper->getConfig("level");
long dest_level = atol(level.c_str());
if (dest_level > source_level)
{
if (IS_LOCAL(source)) ServerInstance->SNO->WriteGlobalSno('a', "Oper %s (level %ld) attempted to /kill a higher oper: %s (level %ld): Reason: %s",source->nick.c_str(),source_level,dest->nick.c_str(),dest_level,reason.c_str());
- dest->WriteServ("NOTICE %s :*** Oper %s attempted to /kill you!",dest->nick.c_str(),source->nick.c_str());
- source->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper %s is a higher level than you",source->nick.c_str(),dest->nick.c_str());
+ dest->WriteNotice("*** Oper " + source->nick + " attempted to /kill you!");
+ source->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper %s is a higher level than you", dest->nick.c_str());
return MOD_RES_DENY;
}
}
};
MODULE_INIT(ModuleOperLevels)
-
#include "inspircd.h"
-/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */
-
class ModuleOperLog : public Module
{
bool tosnomask;
public:
- void init()
+ void init() CXX11_OVERRIDE
{
- Implementation eventlist[] = { I_OnPreCommand, I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
ServerInstance->SNO->EnableSnomask('r', "OPERLOG");
- OnRehash(NULL);
}
- virtual ~ModuleOperLog()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("A module which logs all oper commands to the ircd log at default loglevel.", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
tosnomask = ServerInstance->Config->ConfValue("operlog")->getBool("tosnomask", false);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return MOD_RES_PASSTHRU;
- if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command)))
+ if ((user->IsOper()) && (user->HasPermission(command)))
{
- Command* thiscommand = ServerInstance->Parser->GetHandler(command);
+ Command* thiscommand = ServerInstance->Parser.GetHandler(command);
if ((thiscommand) && (thiscommand->flags_needed == 'o'))
{
- std::string line;
- if (!parameters.empty())
- line = irc::stringjoiner(" ", parameters, 0, parameters.size() - 1).GetJoined();
- std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + line;
- ServerInstance->Logs->Log("m_operlog", DEFAULT, "OPERLOG: " + msg);
+ std::string msg = "[" + user->GetFullRealHost() + "] " + command + " " + irc::stringjoiner(parameters);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OPERLOG: " + msg);
if (tosnomask)
ServerInstance->SNO->WriteGlobalSno('r', msg);
}
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" OPERLOG");
+ tokens["OPERLOG"];
}
};
-
MODULE_INIT(ModuleOperLog)
#include "inspircd.h"
-/* $ModDesc: Sets (and unsets) modes on opers when they oper up */
-
class ModuleModesOnOper : public Module
{
public:
- void init()
- {
- ServerInstance->Modules->Attach(I_OnPostOper, this);
- }
-
- virtual ~ModuleModesOnOper()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sets (and unsets) modes on opers when they oper up", VF_VENDOR);
}
- virtual void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
while (ss >> buf)
modes.push_back(buf);
- ServerInstance->SendMode(modes, u);
+ ServerInstance->Parser.CallHandler("MODE", modes, u);
}
};
#include "inspircd.h"
-/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
-
/** Handle /OPERMOTD
*/
class CommandOpermotd : public Command
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnOper };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Shows a message to opers after oper-up, adds /opermotd", VF_VENDOR | VF_OPTCOMMON);
}
- virtual void OnOper(User* user, const std::string &opertype)
+ void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE
{
if (onoper && IS_LOCAL(user))
cmd.ShowOperMOTD(user);
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
cmd.opermotd.clear();
ConfigTag* conf = ServerInstance->Config->ConfValue("opermotd");
onoper = conf->getBool("onoper", true);
- FileReader f(conf->getString("file", "opermotd"));
- for (int i=0, filesize = f.FileSize(); i < filesize; i++)
- cmd.opermotd.push_back(f.GetLine(i));
+ try
+ {
+ FileReader reader(conf->getString("file", "opermotd"));
+ cmd.opermotd = reader.GetVector();
+ }
+ catch (CoreException&)
+ {
+ // Nothing happens here as we do the error handling in ShowOperMOTD.
+ }
if (conf->getBool("processcolors"))
InspIRCd::ProcessColors(cmd.opermotd);
* Originally by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru
*/
-/* $ModDesc: Gives opers cmode +y which provides a staff prefix. */
-
#include "inspircd.h"
#define OPERPREFIX_VALUE 1000000
-class OperPrefixMode : public ModeHandler
+class OperPrefixMode : public PrefixMode
{
public:
- OperPrefixMode(Module* Creator) : ModeHandler(Creator, "operprefix", 'y', PARAM_ALWAYS, MODETYPE_CHANNEL)
+ OperPrefixMode(Module* Creator)
+ : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
{
std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
- list = true;
prefix = pfx.empty() ? '!' : pfx[0];
- levelrequired = OPERPREFIX_VALUE;
- m_paramtype = TR_NICK;
- }
-
- unsigned int GetPrefixRank()
- {
- return OPERPREFIX_VALUE;
+ levelrequired = INT_MAX;
}
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- if (IS_SERVER(source) || ServerInstance->ULine(source->server))
- return MODEACTION_ALLOW;
- else
- {
- if (channel)
- source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only servers are permitted to change channel mode '%c'", source->nick.c_str(), channel->name.c_str(), 'y');
- return MODEACTION_DENY;
- }
- }
-
- bool NeedsOper() { return true; }
};
class ModuleOperPrefixMode;
class HideOperWatcher : public ModeWatcher
{
ModuleOperPrefixMode* parentmod;
+
public:
- HideOperWatcher(ModuleOperPrefixMode* parent) : ModeWatcher((Module*) parent, 'H', MODETYPE_USER), parentmod(parent) {}
- void AfterMode(User* source, User* dest, Channel* channel, const std::string ¶meter, bool adding, ModeType type);
+ HideOperWatcher(ModuleOperPrefixMode* parent);
+ void AfterMode(User* source, User* dest, Channel* channel, const std::string ¶meter, bool adding);
};
class ModuleOperPrefixMode : public Module
{
- private:
OperPrefixMode opm;
- bool mw_added;
HideOperWatcher hideoperwatcher;
+ UserModeReference hideopermode;
+
public:
ModuleOperPrefixMode()
- : opm(this), mw_added(false), hideoperwatcher(this)
- {
- }
-
- void init()
+ : opm(this), hideoperwatcher(this)
+ , hideopermode(this, "hideoper")
{
- ServerInstance->Modules->AddService(opm);
-
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnPostOper, I_OnLoadModule, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
/* To give clients a chance to learn about the new prefix we don't give +y to opers
* right now. That means if the module was loaded after opers have joined channels
* they need to rejoin them in order to get the oper prefix.
*/
-
- if (ServerInstance->Modules->Find("m_hideoper.so"))
- mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string& privs, const std::string& keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- /* The user may have the +H umode on himself, but +H does not necessarily correspond
- * to the +H of m_hideoper.
- * However we only add the modewatcher when m_hideoper is loaded, so these
- * conditions (mw_added and the user being +H) together mean the user is a hidden oper.
- */
-
- if (IS_OPER(user) && (!mw_added || !user->IsModeSet('H')))
+ if ((user->IsOper()) && (!user->IsModeSet(hideopermode)))
privs.push_back('y');
return MOD_RES_PASSTHRU;
}
void SetOperPrefix(User* user, bool add)
{
- std::vector<std::string> modechange;
- modechange.push_back("");
- modechange.push_back(add ? "+y" : "-y");
- modechange.push_back(user->nick);
- for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
- {
- modechange[0] = (*v)->name;
- ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
- }
+ Modes::ChangeList changelist;
+ changelist.push(&opm, add, user->nick);
+ for (User::ChanList::iterator v = user->chans.begin(); v != user->chans.end(); v++)
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, (*v)->chan, NULL, changelist);
}
- void OnPostOper(User* user, const std::string& opername, const std::string& opertype)
+ void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
{
- if (IS_LOCAL(user) && (!mw_added || !user->IsModeSet('H')))
+ if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
SetOperPrefix(user, true);
}
- void OnLoadModule(Module* mod)
- {
- if ((!mw_added) && (mod->ModuleSourceFile == "m_hideoper.so"))
- mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
- }
-
- void OnUnloadModule(Module* mod)
- {
- if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
- mw_added = false;
- }
-
- ~ModuleOperPrefixMode()
- {
- if (mw_added)
- ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Gives opers cmode +y which provides a staff prefix.", VF_VENDOR);
}
}
};
-void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
+HideOperWatcher::HideOperWatcher(ModuleOperPrefixMode* parent)
+ : ModeWatcher(parent, "hideoper", MODETYPE_USER)
+ , parentmod(parent)
+{
+}
+
+void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
{
// If hideoper is being unset because the user is deopering, don't set +y
- if (IS_LOCAL(dest) && IS_OPER(dest))
+ if (IS_LOCAL(dest) && dest->IsOper())
parentmod->SetOperPrefix(dest, !adding);
}
#include "inspircd.h"
-/* $ModDesc: Provides support for allowing opers to override certain things. */
-
class ModuleOverride : public Module
{
bool RequireKey;
bool NoisyOverride;
+ ChanModeReference topiclock;
+ ChanModeReference inviteonly;
+ ChanModeReference key;
+ ChanModeReference limit;
- static bool IsOverride(unsigned int userlevel, const std::string& modeline)
+ static bool IsOverride(unsigned int userlevel, const Modes::ChangeList::List& list)
{
- for (std::string::const_iterator i = modeline.begin(); i != modeline.end(); ++i)
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
-
+ ModeHandler* mh = i->mh;
if (mh->GetLevelRequired() > userlevel)
return true;
}
return false;
}
+ ModResult HandleJoinOverride(LocalUser* user, Channel* chan, const std::string& keygiven, const char* bypasswhat, const char* mode)
+ {
+ if (RequireKey && keygiven != "override")
+ {
+ // Can't join normally -- must use a special key to bypass restrictions
+ user->WriteNotice("*** You may not join normally. You must join with a key of 'override' to oper override.");
+ return MOD_RES_PASSTHRU;
+ }
+
+ if (NoisyOverride)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass %s", chan->name.c_str(), user->nick.c_str(), bypasswhat);
+ ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass " + mode + " on " + chan->name);
+ return MOD_RES_ALLOW;
+ }
+
public:
+ ModuleOverride()
+ : topiclock(this, "topiclock")
+ , inviteonly(this, "inviteonly")
+ , key(this, "key")
+ , limit(this, "limit")
+ {
+ }
- void init()
+ void init() CXX11_OVERRIDE
{
- // read our config options (main config file)
- OnRehash(NULL);
ServerInstance->SNO->EnableSnomask('v', "OVERRIDE");
- Implementation eventlist[] = { I_OnRehash, I_OnPreMode, I_On005Numeric, I_OnUserPreJoin, I_OnUserPreKick, I_OnPreTopicChange };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- // re-read our config options on a rehash
+ // re-read our config options
ConfigTag* tag = ServerInstance->Config->ConfValue("override");
NoisyOverride = tag->getBool("noisy");
RequireKey = tag->getBool("requirekey");
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" OVERRIDE");
+ tokens["OVERRIDE"];
}
bool CanOverride(User* source, const char* token)
}
- ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic)
+ ModResult OnPreTopicChange(User *source, Channel *channel, const std::string &topic) CXX11_OVERRIDE
{
- if (IS_LOCAL(source) && IS_OPER(source) && CanOverride(source, "TOPIC"))
+ if (IS_LOCAL(source) && source->IsOper() && CanOverride(source, "TOPIC"))
{
- if (!channel->HasUser(source) || (channel->IsModeSet('t') && channel->GetPrefixValue(source) < HALFOP_VALUE))
+ if (!channel->HasUser(source) || (channel->IsModeSet(topiclock) && channel->GetPrefixValue(source) < HALFOP_VALUE))
{
ServerInstance->SNO->WriteGlobalSno('v',source->nick+" used oper override to change a topic on "+channel->name);
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User* source, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (IS_OPER(source) && CanOverride(source,"KICK"))
+ if (source->IsOper() && CanOverride(source,"KICK"))
{
// If the kicker's status is less than the target's, or the kicker's status is less than or equal to voice
if ((memb->chan->GetPrefixValue(source) < memb->getRank()) || (memb->chan->GetPrefixValue(source) <= VOICE_VALUE))
return MOD_RES_PASSTHRU;
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
- if (!source || !channel)
+ if (!channel)
return MOD_RES_PASSTHRU;
- if (!IS_OPER(source) || !IS_LOCAL(source))
+ if (!source->IsOper() || !IS_LOCAL(source))
return MOD_RES_PASSTHRU;
+ const Modes::ChangeList::List& list = modes.getlist();
unsigned int mode = channel->GetPrefixValue(source);
- if (!IsOverride(mode, parameters[1]))
+ if (!IsOverride(mode, list))
return MOD_RES_PASSTHRU;
if (CanOverride(source, "MODE"))
{
- std::string msg = source->nick+" overriding modes:";
- for(unsigned int i=0; i < parameters.size(); i++)
- msg += " " + parameters[i];
+ std::string msg = source->nick + " overriding modes: ";
+
+ // Construct a MODE string in the old format for sending it as a snotice
+ std::string params;
+ char pm = 0;
+ for (Modes::ChangeList::List::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ if (!item.param.empty())
+ params.append(1, ' ').append(item.param);
+
+ char wanted_pm = (item.adding ? '+' : '-');
+ if (wanted_pm != pm)
+ {
+ pm = wanted_pm;
+ msg += pm;
+ }
+
+ msg += item.mh->GetModeChar();
+ }
+ msg += params;
ServerInstance->SNO->WriteGlobalSno('v',msg);
return MOD_RES_ALLOW;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- if (IS_LOCAL(user) && IS_OPER(user))
+ if (user->IsOper())
{
if (chan)
{
- if (chan->IsModeSet('i') && (CanOverride(user,"INVITE")))
+ if (chan->IsModeSet(inviteonly) && (CanOverride(user,"INVITE")))
{
- irc::string x(chan->name.c_str());
- if (!IS_LOCAL(user)->IsInvited(x))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass invite-only", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +i on "+std::string(cname));
- }
+ if (!user->IsInvited(chan))
+ return HandleJoinOverride(user, chan, keygiven, "invite-only", "+i");
return MOD_RES_ALLOW;
}
- if (chan->IsModeSet('k') && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter('k'))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel key", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +k on "+std::string(cname));
- return MOD_RES_ALLOW;
- }
+ if (chan->IsModeSet(key) && (CanOverride(user,"KEY")) && keygiven != chan->GetModeParameter(key))
+ return HandleJoinOverride(user, chan, keygiven, "the channel key", "+k");
- if (chan->IsModeSet('l') && (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l'))) && (CanOverride(user,"LIMIT")))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass the channel limit", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v', user->nick+" used oper override to bypass +l on "+std::string(cname));
- return MOD_RES_ALLOW;
- }
+ if (chan->IsModeSet(limit) && (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter(limit))) && (CanOverride(user,"LIMIT")))
+ return HandleJoinOverride(user, chan, keygiven, "the channel limit", "+l");
if (chan->IsBanned(user) && CanOverride(user,"BANWALK"))
- {
- if (RequireKey && keygiven != "override")
- {
- // Can't join normally -- must use a special key to bypass restrictions
- user->WriteServ("NOTICE %s :*** You may not join normally. You must join with a key of 'override' to oper override.", user->nick.c_str());
- return MOD_RES_PASSTHRU;
- }
-
- if (NoisyOverride)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper override to bypass channel ban", cname, user->nick.c_str());
- ServerInstance->SNO->WriteGlobalSno('v',"%s used oper override to bypass channel ban on %s", user->nick.c_str(), cname);
- return MOD_RES_ALLOW;
- }
+ return HandleJoinOverride(user, chan, keygiven, "channel ban", "channel ban");
}
}
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for allowing opers to override certain things",VF_VENDOR);
}
*/
-/* $ModDesc: Forwards a password users can send on connect (for example for NickServ identification). */
-
#include "inspircd.h"
class ModulePassForward : public Module
{
- private:
std::string nickrequired, forwardmsg, forwardcmd;
public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnPostConnect, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Sends server password to NickServ", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("passforward");
nickrequired = tag->getString("nick", "NickServ");
char c = format[i];
if (c == '$')
{
- if (format.substr(i, 13) == "$nickrequired")
+ if (!format.compare(i, 13, "$nickrequired", 13))
{
result.append(nickrequired);
i += 12;
}
- else if (format.substr(i, 5) == "$nick")
+ else if (!format.compare(i, 5, "$nick", 5))
{
result.append(user->nick);
i += 4;
}
- else if (format.substr(i, 5) == "$user")
+ else if (!format.compare(i, 5, "$user", 5))
{
result.append(user->ident);
i += 4;
}
- else if (format.substr(i,5) == "$pass")
+ else if (!format.compare(i, 5, "$pass", 5))
{
result.append(user->password);
i += 4;
}
}
- virtual void OnPostConnect(User* ruser)
+ void OnPostConnect(User* ruser) CXX11_OVERRIDE
{
LocalUser* user = IS_LOCAL(ruser);
if (!user || user->password.empty())
return;
+ // If the connect class requires a password, don't forward it
+ if (!user->MyClass->config->getString("password").empty())
+ return;
+
if (!nickrequired.empty())
{
/* Check if nick exists and its server is ulined */
User* u = ServerInstance->FindNick(nickrequired);
- if (!u || !ServerInstance->ULine(u->server))
+ if (!u || !u->server->IsULine())
return;
}
tmp.clear();
FormatStr(tmp,forwardcmd, user);
- ServerInstance->Parser->ProcessBuffer(tmp,user);
+ ServerInstance->Parser.ProcessBuffer(tmp,user);
}
};
*/
-/* $ModDesc: Allows for hashed oper passwords */
-
#include "inspircd.h"
-#include "hash.h"
+#include "modules/hash.h"
/* Handle /MKPASSWD
*/
void MakeHash(User* user, const std::string& algo, const std::string& stuff)
{
- if (algo.substr(0,5) == "hmac-")
+ if (!algo.compare(0, 5, "hmac-", 5))
{
std::string type = algo.substr(5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
{
- user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+ user->WriteNotice("Unknown hash type");
+ return;
+ }
+
+ if (hp->IsKDF())
+ {
+ user->WriteNotice(type + " does not support HMAC");
return;
}
- std::string salt = ServerInstance->GenRandomStr(6, false);
+
+ std::string salt = ServerInstance->GenRandomStr(hp->out_size, false);
std::string target = hp->hmac(salt, stuff);
std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0);
- user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
- user->nick.c_str(), algo.c_str(), stuff.c_str(), str.c_str());
+ user->WriteNotice(algo + " hashed password for " + stuff + " is " + str);
return;
}
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + algo);
if (hp)
{
/* Now attempt to generate a hash */
- std::string hexsum = hp->hexsum(stuff);
- user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
- user->nick.c_str(), algo.c_str(), stuff.c_str(), hexsum.c_str());
+ std::string hexsum = hp->Generate(stuff);
+ user->WriteNotice(algo + " hashed password for " + stuff + " is " + hexsum);
}
else
{
- user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str());
+ user->WriteNotice("Unknown hash type");
}
}
{
}
- void init()
+ ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) CXX11_OVERRIDE
{
- /* Read the config file first */
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnPassCompare };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
- {
- if (hashtype.substr(0,5) == "hmac-")
+ if (!hashtype.compare(0, 5, "hmac-", 5))
{
std::string type = hashtype.substr(5);
HashProvider* hp = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + type);
if (!hp)
return MOD_RES_PASSTHRU;
+
+ if (hp->IsKDF())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Tried to use HMAC with %s, which does not support HMAC", type.c_str());
+ return MOD_RES_DENY;
+ }
+
// this is a valid hash, from here on we either accept or deny
std::string::size_type sep = data.find('$');
if (sep == std::string::npos)
/* Is this a valid hash name? */
if (hp)
{
- /* Compare the hash in the config to the generated hash */
- if (data == hp->hexsum(input))
+ if (hp->Compare(input, data))
return MOD_RES_ALLOW;
else
/* No match, and must be hashed, forbid */
return MOD_RES_DENY;
}
- /* Not a hash, fall through to strcmp in core */
+ // We don't handle this type, let other mods or the core decide
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows for hashed oper passwords",VF_VENDOR);
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/hash.h"
+
+// Format:
+// Iterations:B64(Hash):B64(Salt)
+// E.g.
+// 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+class PBKDF2Hash
+{
+ public:
+ unsigned int iterations;
+ unsigned int length;
+ std::string salt;
+ std::string hash;
+
+ PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
+ : iterations(itr), length(dkl), salt(slt), hash(hsh)
+ {
+ }
+
+ PBKDF2Hash(const std::string& data)
+ {
+ irc::sepstream ss(data, ':');
+ std::string tok;
+
+ ss.GetToken(tok);
+ this->iterations = ConvToInt(tok);
+
+ ss.GetToken(tok);
+ this->hash = Base64ToBin(tok);
+
+ ss.GetToken(tok);
+ this->salt = Base64ToBin(tok);
+
+ this->length = this->hash.length();
+ }
+
+ std::string ToString()
+ {
+ if (!IsValid())
+ return "";
+ return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
+ }
+
+ bool IsValid()
+ {
+ if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
+ return false;
+ return true;
+ }
+};
+
+class PBKDF2Provider : public HashProvider
+{
+ public:
+ HashProvider* provider;
+ unsigned int iterations;
+ unsigned int dkey_length;
+
+ std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
+ {
+ size_t blocks = std::ceil((double)dkl / provider->out_size);
+
+ std::string output;
+ std::string tmphash;
+ std::string salt_block = salt;
+ for (size_t block = 1; block <= blocks; block++)
+ {
+ char salt_data[4];
+ for (size_t i = 0; i < sizeof(salt_data); i++)
+ salt_data[i] = block >> (24 - i * 8) & 0x0F;
+
+ salt_block.erase(salt.length());
+ salt_block.append(salt_data, sizeof(salt_data));
+
+ std::string blockdata = provider->hmac(pass, salt_block);
+ std::string lasthash = blockdata;
+ for (size_t iter = 1; iter < itr; iter++)
+ {
+ tmphash = provider->hmac(pass, lasthash);
+ for (size_t i = 0; i < provider->out_size; i++)
+ blockdata[i] ^= tmphash[i];
+
+ lasthash.swap(tmphash);
+ }
+ output += blockdata;
+ }
+
+ output.erase(dkl);
+ return output;
+ }
+
+ std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
+ hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
+ return hs.ToString();
+ }
+
+ bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
+ {
+ PBKDF2Hash hs(hash);
+ if (!hs.IsValid())
+ return false;
+
+ std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
+ return (cmp == hs.hash);
+ }
+
+ std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
+ {
+ return raw;
+ }
+
+ PBKDF2Provider(Module* mod, HashProvider* hp)
+ : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
+ , provider(hp)
+ {
+ DisableAutoRegister();
+ }
+};
+
+class ModulePBKDF2 : public Module
+{
+ std::vector<PBKDF2Provider*> providers;
+
+ void GetConfig()
+ {
+ // First set the common values
+ ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
+ unsigned int global_iterations = tag->getInt("iterations", 12288, 1);
+ unsigned int global_dkey_length = tag->getInt("length", 32, 1, 1024);
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
+ {
+ PBKDF2Provider* pi = *i;
+ pi->iterations = global_iterations;
+ pi->dkey_length = global_dkey_length;
+ }
+
+ // Then the specific values
+ ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ tag = i->second;
+ std::string hash_name = "hash/" + tag->getString("hash");
+ for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j)
+ {
+ PBKDF2Provider* pi = *j;
+ if (pi->provider->name != hash_name)
+ continue;
+
+ pi->iterations = tag->getInt("iterations", global_iterations, 1);
+ pi->dkey_length = tag->getInt("length", global_dkey_length, 1, 1024);
+ }
+ }
+ }
+
+ public:
+ ~ModulePBKDF2()
+ {
+ stdalgo::delete_all(providers);
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ OnLoadModule(NULL);
+ }
+
+ void OnLoadModule(Module* mod) CXX11_OVERRIDE
+ {
+ bool newProv = false;
+ // As the module doesn't tell us what ServiceProviders it has, let's iterate all (yay ...) the ServiceProviders
+ // Good thing people don't run loading and unloading those all the time
+ for (std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.begin(); i != ServerInstance->Modules->DataProviders.end(); ++i)
+ {
+ ServiceProvider* provider = i->second;
+
+ // Does the service belong to the new mod?
+ // In the case this is our first run (mod == NULL, continue anyway)
+ if (mod && provider->creator != mod)
+ continue;
+
+ // Check if it's a hash provider
+ if (provider->name.compare(0, 5, "hash/"))
+ continue;
+
+ HashProvider* hp = static_cast<HashProvider*>(provider);
+
+ if (hp->IsKDF())
+ continue;
+
+ bool has_prov = false;
+ for (std::vector<PBKDF2Provider*>::const_iterator j = providers.begin(); j != providers.end(); ++j)
+ {
+ if ((*j)->provider == hp)
+ {
+ has_prov = true;
+ break;
+ }
+ }
+ if (has_prov)
+ continue;
+
+ newProv = true;
+
+ PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
+ providers.push_back(prov);
+ ServerInstance->Modules->AddService(*prov);
+ }
+
+ if (newProv)
+ GetConfig();
+ }
+
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
+ {
+ for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); )
+ {
+ PBKDF2Provider* item = *i;
+ if (item->provider->creator != mod)
+ {
+ ++i;
+ continue;
+ }
+
+ ServerInstance->Modules->DelService(*item);
+ delete item;
+ i = providers.erase(i);
+ }
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ GetConfig();
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Implements PBKDF2 hashing", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModulePBKDF2)
#include "inspircd.h"
+#include "listmode.h"
+#include <fstream>
-/* $ModDesc: Provides support for channel mode +P to provide permanent channels */
-struct ListModeData
+/** Handles the +P channel mode
+ */
+class PermChannel : public ModeHandler
{
- std::string modes;
- std::string params;
+ public:
+ PermChannel(Module* Creator)
+ : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL)
+ {
+ oper = true;
+ }
+
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ {
+ if (adding == channel->IsModeSet(this))
+ return MODEACTION_DENY;
+
+ channel->SetMode(this, adding);
+ if (!adding)
+ channel->CheckDestroy();
+
+ return MODEACTION_ALLOW;
+ }
};
// Not in a class due to circular dependancy hell.
static std::string permchannelsconf;
-static bool WriteDatabase(Module* mod, bool save_listmodes)
+static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_listmodes)
{
- FILE *f;
+ ChanModeReference ban(mod, "ban");
+ /*
+ * We need to perform an atomic write so as not to fuck things up.
+ * So, let's write to a temporary file, flush it, then rename the file..
+ * -- w00t
+ */
+ // If the user has not specified a configuration file then we don't write one.
if (permchannelsconf.empty())
- {
- // Fake success.
return true;
- }
- std::string tempname = permchannelsconf + ".tmp";
-
- /*
- * We need to perform an atomic write so as not to fuck things up.
- * So, let's write to a temporary file, flush and sync the FD, then rename the file..
- * -- w00t
- */
- f = fopen(tempname.c_str(), "w");
- if (!f)
+ std::string permchannelsnewconf = permchannelsconf + ".tmp";
+ std::ofstream stream(permchannelsnewconf.c_str());
+ if (!stream.is_open())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
- fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
- // Now, let's write.
- std::string line;
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
+ << "<config format=\"xml\">" << std::endl;
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
{
Channel* chan = i->second;
- if (!chan->IsModeSet('P'))
+ if (!chan->IsModeSet(permchanmode))
continue;
std::string chanmodes = chan->ChanModes(true);
if (save_listmodes)
{
- ListModeData lm;
+ std::string modes;
+ std::string params;
- // Bans are managed by the core, so we have to process them separately
- lm.modes = std::string(chan->bans.size(), 'b');
- for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator j = listmodes.begin(); j != listmodes.end(); ++j)
{
- lm.params += j->data;
- lm.params += ' ';
- }
+ ListModeBase* lm = *j;
+ ListModeBase::ModeList* list = lm->GetList(chan);
+ if (!list || list->empty())
+ continue;
+
+ size_t n = 0;
+ // Append the parameters
+ for (ListModeBase::ModeList::const_iterator k = list->begin(); k != list->end(); ++k, n++)
+ {
+ params += k->mask;
+ params += ' ';
+ }
- // All other listmodes are managed by modules, so we need to ask them (call their
- // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
- // set on the channel. The ListModeData struct is passed as an opaque pointer
- // that will be passed back to us by the module handling the mode.
- FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
+ // Append the mode letters (for example "IIII", "gg")
+ modes.append(n, lm->GetModeChar());
+ }
- if (!lm.modes.empty())
+ if (!params.empty())
{
// Remove the last space
- lm.params.erase(lm.params.end()-1);
+ params.erase(params.end()-1);
// If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
// insert the listmode mode letters before the space. Otherwise just append them.
std::string::size_type p = chanmodes.find(' ');
if (p == std::string::npos)
- chanmodes += lm.modes;
+ chanmodes += modes;
else
- chanmodes.insert(p, lm.modes);
+ chanmodes.insert(p, modes);
// Append the listmode parameters (the masks themselves)
chanmodes += ' ';
- chanmodes += lm.params;
+ chanmodes += params;
}
}
- std::string chants = ConvToStr(chan->age);
- std::string topicts = ConvToStr(chan->topicset);
- const char* items[] =
- {
- "<permchannels channel=",
- chan->name.c_str(),
- " ts=",
- chants.c_str(),
- " topic=",
- chan->topic.c_str(),
- " topicts=",
- topicts.c_str(),
- " topicsetby=",
- chan->setby.c_str(),
- " modes=",
- chanmodes.c_str(),
- ">\n"
- };
-
- line.clear();
- int item = 0, ipos = 0;
- while (item < 13)
- {
- char c = items[item][ipos++];
- if (c == 0)
- {
- // end of this string; hop to next string, insert a quote
- item++;
- ipos = 0;
- c = '"';
- }
- else if (c == '\\' || c == '"')
- {
- line += '\\';
- }
- line += c;
- }
-
- // Erase last '"'
- line.erase(line.end()-1);
- fputs(line.c_str(), f);
+ stream << "<permchannels channel=\"" << ServerConfig::Escape(chan->name)
+ << "\" ts=\"" << chan->age
+ << "\" topic=\"" << ServerConfig::Escape(chan->topic)
+ << "\" topicts=\"" << chan->topicset
+ << "\" topicsetby=\"" << ServerConfig::Escape(chan->setby)
+ << "\" modes=\"" << ServerConfig::Escape(chanmodes)
+ << "\">" << std::endl;
}
- int write_error = 0;
- write_error = ferror(f);
- write_error |= fclose(f);
- if (write_error)
+ if (stream.fail())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database \"%s\"! %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new permchan db \"%s\": %s (%d)", permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
+ stream.close();
#ifdef _WIN32
remove(permchannelsconf.c_str());
#endif
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
- if (rename(tempname.c_str(), permchannelsconf.c_str()) < 0)
+ if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old permchan db \"%s\" with new db \"%s\": %s (%d)", permchannelsconf.c_str(), permchannelsnewconf.c_str(), strerror(errno), errno);
return false;
}
return true;
}
-
-
-/** Handles the +P channel mode
- */
-class PermChannel : public ModeHandler
-{
- public:
- PermChannel(Module* Creator) : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL) { oper = true; }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- if (adding)
- {
- if (!channel->IsModeSet('P'))
- {
- channel->SetMode('P',true);
- return MODEACTION_ALLOW;
- }
- }
- else
- {
- if (channel->IsModeSet('P'))
- {
- channel->SetMode(this,false);
- if (channel->GetUserCounter() == 0)
- {
- channel->DelUser(ServerInstance->FakeClient);
- }
- return MODEACTION_ALLOW;
- }
- }
-
- return MODEACTION_DENY;
- }
-};
-
class ModulePermanentChannels : public Module
{
PermChannel p;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(p);
- Implementation eventlist[] = { I_OnChannelPreDelete, I_OnPostTopicChange, I_OnRawMode, I_OnRehash, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- OnRehash(NULL);
- }
-
- CullResult cull()
- {
- /*
- * DelMode can't remove the +P mode on empty channels, or it will break
- * merging modes with remote servers. Remove the empty channels now as
- * we know this is not the case.
- */
- chan_hash::iterator iter = ServerInstance->chanlist->begin();
- while (iter != ServerInstance->chanlist->end())
- {
- Channel* c = iter->second;
- if (c->GetUserCounter() == 0)
- {
- chan_hash::iterator at = iter;
- iter++;
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(c));
- ServerInstance->chanlist->erase(at);
- ServerInstance->GlobalCulls.AddItem(c);
- }
- else
- iter++;
- }
- ServerInstance->Modes->DelMode(&p);
- return Module::cull();
- }
-
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("permchanneldb");
permchannelsconf = tag->getString("filename");
save_listmodes = tag->getBool("listmodes");
+
+ if (!permchannelsconf.empty())
+ permchannelsconf = ServerInstance->Config->Paths.PrependConfig(permchannelsconf);
}
void LoadDatabase()
{
ConfigTag* tag = i->second;
std::string channel = tag->getString("channel");
- std::string topic = tag->getString("topic");
std::string modes = tag->getString("modes");
if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax))
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
continue;
}
if (!c)
{
- time_t TS = tag->getInt("ts");
- c = new Channel(channel, ((TS > 0) ? TS : ServerInstance->Time()));
+ time_t TS = tag->getInt("ts", ServerInstance->Time(), 1);
+ c = new Channel(channel, TS);
- c->SetTopic(NULL, topic, true);
- c->setby = tag->getString("topicsetby");
- if (c->setby.empty())
- c->setby = ServerInstance->Config->ServerName;
unsigned int topicset = tag->getInt("topicts");
- // SetTopic() sets the topic TS to now, if there was no topicts saved then don't overwrite that with a 0
- if (topicset > 0)
+ c->topic = tag->getString("topic");
+
+ if ((topicset != 0) || (!c->topic.empty()))
+ {
+ if (topicset == 0)
+ topicset = ServerInstance->Time();
c->topicset = topicset;
+ c->setby = tag->getString("topicsetby");
+ if (c->setby.empty())
+ c->setby = ServerInstance->Config->ServerName;
+ }
- ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
if (modes.empty())
continue;
}
}
- virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt)
+ ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
{
- if (chan && (chan->IsModeSet('P') || mode == 'P'))
+ if (chan && (chan->IsModeSet(p) || mh == &p))
dirty = true;
return MOD_RES_PASSTHRU;
}
- virtual void OnPostTopicChange(User*, Channel *c, const std::string&)
+ void OnPostTopicChange(User*, Channel *c, const std::string&) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
dirty = true;
}
- void OnBackgroundTimer(time_t)
+ void OnBackgroundTimer(time_t) CXX11_OVERRIDE
{
if (dirty)
- WriteDatabase(this, save_listmodes);
+ WriteDatabase(p, this, save_listmodes);
dirty = false;
}
// Load only when there are no linked servers - we set the TS of the channels we
// create to the current time, this can lead to desync because spanningtree has
// no way of knowing what we do
- ProtoServerList serverlist;
+ ProtocolInterface::ServerList serverlist;
ServerInstance->PI->GetServerList(serverlist);
if (serverlist.size() < 2)
{
}
catch (CoreException& e)
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
}
}
}
- void ProtoSendMode(void* opaque, TargetTypeFlags type, void* target, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate)
- {
- // We never pass an empty modelist but better be sure
- if (modes.empty())
- return;
-
- ListModeData* lm = static_cast<ListModeData*>(opaque);
-
- // Append the mode letters without the trailing '+' (for example "IIII", "gg")
- lm->modes.append(modes[0].begin()+1, modes[0].end());
-
- // Append the parameters
- for (std::vector<std::string>::const_iterator i = modes.begin()+1; i != modes.end(); ++i)
- {
- lm->params += *i;
- lm->params += ' ';
- }
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR);
}
- virtual ModResult OnChannelPreDelete(Channel *c)
+ ModResult OnChannelPreDelete(Channel *c) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
*/
-/* $ModDesc: Provides random quotes on connect. */
-
#include "inspircd.h"
-static FileReader *quotes = NULL;
-
-std::string prefix;
-std::string suffix;
-
-/** Handle /RANDQUOTE
- */
-class CommandRandquote : public Command
-{
- public:
- CommandRandquote(Module* Creator) : Command(Creator,"RANDQUOTE", 0)
- {
- }
-
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
- {
- int fsize = quotes->FileSize();
- if (fsize)
- {
- std::string str = quotes->GetLine(ServerInstance->GenRandomInt(fsize));
- if (!str.empty())
- user->WriteServ("NOTICE %s :%s%s%s",user->nick.c_str(),prefix.c_str(),str.c_str(),suffix.c_str());
- }
-
- return CMD_SUCCESS;
- }
-};
-
class ModuleRandQuote : public Module
{
private:
- CommandRandquote cmd;
- public:
- ModuleRandQuote()
- : cmd(this)
- {
- }
+ std::string prefix;
+ std::string suffix;
+ std::vector<std::string> quotes;
- void init()
+ public:
+ void init() CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("randquote");
-
- std::string q_file = conf->getString("file","quotes");
prefix = conf->getString("prefix");
suffix = conf->getString("suffix");
-
- quotes = new FileReader(q_file);
- if (!quotes->Exists())
- {
- throw ModuleException("m_randquote: QuoteFile not Found!! Please check your config - module will not function.");
- }
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ FileReader reader(conf->getString("file", "quotes"));
+ quotes = reader.GetVector();
}
-
- virtual ~ModuleRandQuote()
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- delete quotes;
- }
-
- virtual Version GetVersion()
- {
- return Version("Provides random quotes on connect.",VF_VENDOR);
+ if (!quotes.empty())
+ {
+ unsigned long random = ServerInstance->GenRandomInt(quotes.size());
+ user->WriteNotice(prefix + quotes[random] + suffix);
+ }
}
- virtual void OnUserConnect(LocalUser* user)
+ Version GetVersion() CXX11_OVERRIDE
{
- cmd.Handle(std::vector<std::string>(), user);
+ return Version("Provides random quotes on connect.", VF_VENDOR);
}
};
#include "inspircd.h"
-/* $ModDesc: Provides channel mode +L (limit redirection) and usermode +L (no forced redirection) */
-
/** Handle channel mode +L
*/
-class Redirect : public ModeHandler
+class Redirect : public ParamMode<Redirect, LocalStringExt>
{
public:
- Redirect(Module* Creator) : ModeHandler(Creator, "redirect", 'L', PARAM_SETONLY, MODETYPE_CHANNEL) { }
+ Redirect(Module* Creator)
+ : ParamMode<Redirect, LocalStringExt>(Creator, "redirect", 'L') { }
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
{
- if (adding)
+ if (IS_LOCAL(source))
{
- if (IS_LOCAL(source))
+ if (!ServerInstance->IsChannel(parameter))
{
- if (!ServerInstance->IsChannel(parameter.c_str(), ServerInstance->Config->Limits.ChanMax))
- {
- source->WriteNumeric(403, "%s %s :Invalid channel name", source->nick.c_str(), parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
+ source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name", parameter.c_str());
+ return MODEACTION_DENY;
}
+ }
- if (IS_LOCAL(source) && !IS_OPER(source))
+ if (IS_LOCAL(source) && !source->IsOper())
+ {
+ Channel* c = ServerInstance->FindChan(parameter);
+ if (!c)
{
- Channel* c = ServerInstance->FindChan(parameter);
- if (!c)
- {
- source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- else if (c->GetPrefixValue(source) < OP_VALUE)
- {
- source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(),parameter.c_str());
- parameter.clear();
- return MODEACTION_DENY;
- }
- }
-
- if (channel->GetModeParameter('L') == parameter)
+ source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.",parameter.c_str());
return MODEACTION_DENY;
- /*
- * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
- * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
- */
- channel->SetModeParam('L', parameter);
- return MODEACTION_ALLOW;
- }
- else
- {
- if (channel->IsModeSet('L'))
+ }
+ else if (c->GetPrefixValue(source) < OP_VALUE)
{
- channel->SetModeParam('L', "");
- return MODEACTION_ALLOW;
+ source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.",parameter.c_str());
+ return MODEACTION_DENY;
}
}
- return MODEACTION_DENY;
+ /*
+ * We used to do some checking for circular +L here, but there is no real need for this any more especially as we
+ * now catch +L looping in PreJoin. Remove it, since O(n) logic makes me sad, and we catch it anyway. :) -- w00t
+ */
+ ext.set(channel, parameter);
+ return MODEACTION_ALLOW;
+ }
+ void SerializeParam(Channel* chan, const std::string* str, std::string& out)
+ {
+ out += *str;
}
};
class AntiRedirect : public SimpleUserModeHandler
{
public:
- AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L') {}
+ AntiRedirect(Module* Creator) : SimpleUserModeHandler(Creator, "antiredirect", 'L')
+ {
+ if (!ServerInstance->Config->ConfValue("redirect")->getBool("antiredirect"))
+ DisableAutoRegister();
+ }
};
class ModuleRedirect : public Module
{
-
Redirect re;
AntiRedirect re_u;
+ ChanModeReference limitmode;
bool UseUsermode;
public:
-
ModuleRedirect()
- : re(this), re_u(this)
+ : re(this)
+ , re_u(this)
+ , limitmode(this, "limit")
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
/* Setting this here so it isn't changable by rehasing the config later. */
UseUsermode = ServerInstance->Config->ConfValue("redirect")->getBool("antiredirect");
-
- /* Channel mode */
- ServerInstance->Modules->AddService(re);
-
- /* Check to see if the usermode is enabled in the config */
- if (UseUsermode)
- {
- /* Log noting that this breaks compatability. */
- ServerInstance->Logs->Log("m_redirect", DEFAULT, "REDIRECT: Enabled usermode +L. This breaks linking with servers that do not have this enabled. This is disabled by default in the 2.0 branch but will be enabled in the next version.");
-
- /* Try to add the usermode */
- ServerInstance->Modules->AddService(re_u);
- }
-
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
{
- if (chan->IsModeSet('L') && chan->IsModeSet('l'))
+ if (chan->IsModeSet(re) && chan->IsModeSet(limitmode))
{
- if (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter('l')))
+ if (chan->GetUserCounter() >= ConvToInt(chan->GetModeParameter(limitmode)))
{
- std::string channel = chan->GetModeParameter('L');
+ const std::string& channel = *re.ext.get(chan);
/* sometimes broken ulines can make circular or chained +L, avoid this */
- Channel* destchan = NULL;
- destchan = ServerInstance->FindChan(channel);
- if (destchan && destchan->IsModeSet('L'))
+ Channel* destchan = ServerInstance->FindChan(channel);
+ if (destchan && destchan->IsModeSet(re))
{
- user->WriteNumeric(470, "%s %s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", user->nick.c_str(), cname);
+ user->WriteNumeric(470, "%s * :You may not join this channel. A redirect is set, but you may not be redirected as it is a circular loop.", cname.c_str());
return MOD_RES_DENY;
}
/* We check the bool value here to make sure we have it enabled, if we don't then
usermode +L might be assigned to something else. */
- if (UseUsermode && user->IsModeSet('L'))
+ if (UseUsermode && user->IsModeSet(re_u))
{
- user->WriteNumeric(470, "%s %s %s :Force redirection stopped.",
- user->nick.c_str(), cname, channel.c_str());
+ user->WriteNumeric(470, "%s %s :Force redirection stopped.", cname.c_str(), channel.c_str());
return MOD_RES_DENY;
}
else
{
- user->WriteNumeric(470, "%s %s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", user->nick.c_str(), cname, channel.c_str());
- Channel::JoinUser(user, channel.c_str(), false, "", false, ServerInstance->Time());
+ user->WriteNumeric(470, "%s %s :You may not join this channel, so you are automatically being transferred to the redirect channel.", cname.c_str(), channel.c_str());
+ Channel::JoinUser(user, channel);
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleRedirect()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +L (limit redirection) and user mode +L (no forced redirection)", VF_VENDOR);
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef M_REGEX_H
-#define M_REGEX_H
-
-#include "inspircd.h"
-
-class Regex : public classbase
-{
-protected:
- std::string regex_string; // The raw uncompiled regex string.
-
- // Constructor may as well be protected, as this class is abstract.
- Regex(const std::string& rx) : regex_string(rx)
- {
- }
-
-public:
-
- virtual ~Regex()
- {
- }
-
- virtual bool Matches(const std::string& text) = 0;
-
- const std::string& GetRegexString() const
- {
- return regex_string;
- }
-};
-
-class RegexFactory : public DataProvider
-{
- public:
- RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
-
- virtual Regex* Create(const std::string& expr) = 0;
-};
-
-#endif
*/
-#include "m_regex.h"
+#include "modules/regex.h"
#include "inspircd.h"
-/* $ModDesc: Regex module using plain wildcard matching. */
-
class GlobRegex : public Regex
{
public:
{
}
- virtual ~GlobRegex()
- {
- }
-
- virtual bool Matches(const std::string& text)
+ bool Matches(const std::string& text) CXX11_OVERRIDE
{
return InspIRCd::Match(text, this->regex_string);
}
class GlobFactory : public RegexFactory
{
public:
- Regex* Create(const std::string& expr)
+ Regex* Create(const std::string& expr) CXX11_OVERRIDE
{
return new GlobRegex(expr);
}
{
GlobFactory gf;
public:
- ModuleRegexGlob() : gf(this) {
- ServerInstance->Modules->AddService(gf);
+ ModuleRegexGlob()
+ : gf(this)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Regex module using plain wildcard matching.", VF_VENDOR);
}
#include "inspircd.h"
-#include "account.h"
-
-/* $ModDesc: Prevents users whose nicks are not registered from creating new channels */
+#include "modules/account.h"
class ModuleRegOnlyCreate : public Module
{
+ UserModeReference regusermode;
+
public:
- void init()
+ ModuleRegOnlyCreate()
+ : regusermode(this, "u_registered")
{
- Implementation eventlist[] = { I_OnUserPreJoin };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
if (chan)
return MOD_RES_PASSTHRU;
- if (IS_OPER(user))
+ if (user->IsOper())
return MOD_RES_PASSTHRU;
- if (user->IsModeSet('r'))
+ if (user->IsModeSet(regusermode))
return MOD_RES_PASSTHRU;
const AccountExtItem* ext = GetAccountExtItem();
return MOD_RES_PASSTHRU;
// XXX. there may be a better numeric for this..
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have a registered nickname to create a new channel", user->nick.c_str(), cname);
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must have a registered nickname to create a new channel", cname.c_str());
return MOD_RES_DENY;
}
- ~ModuleRegOnlyCreate()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Prevents users whose nicks are not registered from creating new channels", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
-
/*
* This module supports the use of the +q and +a usermodes, but should work without them too.
* Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
*/
class RemoveBase : public Command
{
- private:
bool& supportnokicks;
+ ChanModeReference& nokicksmode;
public:
- RemoveBase(Module* Creator, bool& snk, const char* cmdn)
- : Command(Creator, cmdn, 2, 3), supportnokicks(snk)
+ unsigned int protectedrank;
+
+ RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn)
+ : Command(Creator, cmdn, 2, 3)
+ , supportnokicks(snk)
+ , nokicksmode(nkm)
{
}
- CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool neworder)
+ CmdResult HandleRMB(const std::vector<std::string>& parameters, User *user, bool fpart)
{
User* target;
Channel* channel;
std::string reason;
- std::string protectkey;
- std::string founderkey;
- bool hasnokicks;
+
+ // If the command is a /REMOVE then detect the parameter order
+ bool neworder = ((fpart) || (parameters[0][0] == '#'));
/* Set these to the parameters needed, the new version of this module switches it's parameters around
* supplying a new command with the new order while keeping the old /remove with the older order.
/* Fix by brain - someone needs to learn to validate their input! */
if ((!target) || (target->registered != REG_ALL) || (!channel))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), !channel ? channame.c_str() : username.c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", !channel ? channame.c_str() : username.c_str());
return CMD_FAILURE;
}
return CMD_FAILURE;
}
- int ulevel = channel->GetPrefixValue(user);
- int tlevel = channel->GetPrefixValue(target);
-
- hasnokicks = (ServerInstance->Modules->Find("m_nokicks.so") && channel->IsModeSet('Q'));
-
- if (ServerInstance->ULine(target->server))
+ if (target->server->IsULine())
{
- user->WriteNumeric(482, "%s %s :Only a u-line may remove a u-line from a channel.", user->nick.c_str(), channame.c_str());
+ user->WriteNumeric(482, "%s :Only a u-line may remove a u-line from a channel.", channame.c_str());
return CMD_FAILURE;
}
/* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */
- if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks))
+ if ((!IS_LOCAL(user)) || (!supportnokicks) || (!channel->IsModeSet(nokicksmode)))
{
/* We'll let everyone remove their level and below, eg:
* ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
* a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
- * Nobody may remove a founder.
+ * Nobody may remove people with >= protectedrank rank.
*/
- if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && (tlevel != 50000)))
+ unsigned int ulevel = channel->GetPrefixValue(user);
+ unsigned int tlevel = channel->GetPrefixValue(target);
+ if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && ((protectedrank == 0) || (tlevel < protectedrank))))
{
- // REMOVE/FPART will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
+ // REMOVE will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
if (!IS_LOCAL(target))
+ {
+ // Send an ENCAP REMOVE with parameters being in the old <user> <chan> order which is
+ // compatible with both 2.0 and 2.2. This also turns FPART into REMOVE.
+ std::vector<std::string> p;
+ p.push_back(target->uuid);
+ p.push_back(channel->name);
+ if (parameters.size() > 2)
+ p.push_back(":" + parameters[2]);
+ ServerInstance->PI->SendEncapsulatedData(target->server->GetName(), "REMOVE", p, user);
+
return CMD_SUCCESS;
+ }
std::string reasonparam;
reason = "Removed by " + user->nick + ": " + reasonparam;
channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name.c_str(), user->nick.c_str(), target->nick.c_str());
- target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick.c_str(), user->nick.c_str(), channel->name.c_str(), reasonparam.c_str());
+ target->WriteNotice("*** " + user->nick + " removed you from " + channel->name + " with the message: " + reasonparam);
channel->PartUser(target, reason);
}
else
{
/* m_nokicks.so was loaded and +Q was set, block! */
- user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick.c_str(), channel->name.c_str(), target->nick.c_str());
+ user->WriteNumeric(ERR_RESTRICTED, "%s :Can't remove user %s from channel (nokicks mode is set)", channel->name.c_str(), target->nick.c_str());
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
- virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) = 0;
};
/** Handle /REMOVE
class CommandRemove : public RemoveBase
{
public:
- CommandRemove(Module* Creator, bool& snk)
- : RemoveBase(Creator, snk, "REMOVE")
+ CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
+ : RemoveBase(Creator, snk, nkm, "REMOVE")
{
- syntax = "<nick> <channel> [<reason>]";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
+ syntax = "<channel> <nick> [<reason>]";
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
return HandleRMB(parameters, user, false);
}
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- User* dest = ServerInstance->FindNick(parameters[0]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
- }
};
/** Handle /FPART
class CommandFpart : public RemoveBase
{
public:
- CommandFpart(Module* Creator, bool& snk)
- : RemoveBase(Creator, snk, "FPART")
+ CommandFpart(Module* Creator, bool& snk, ChanModeReference& nkm)
+ : RemoveBase(Creator, snk, nkm, "FPART")
{
syntax = "<channel> <nick> [<reason>]";
- TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
return HandleRMB(parameters, user, true);
}
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- User* dest = ServerInstance->FindNick(parameters[1]);
- if (dest)
- return ROUTE_OPT_UCAST(dest->server);
- return ROUTE_LOCALONLY;
- }
};
class ModuleRemove : public Module
{
+ ChanModeReference nokicksmode;
CommandRemove cmd1;
CommandFpart cmd2;
bool supportnokicks;
-
public:
- ModuleRemove() : cmd1(this, supportnokicks), cmd2(this, supportnokicks)
- {
- }
-
- void init()
+ ModuleRemove()
+ : nokicksmode(this, "nokick")
+ , cmd1(this, supportnokicks, nokicksmode)
+ , cmd2(this, supportnokicks, nokicksmode)
{
- ServerInstance->Modules->AddService(cmd1);
- ServerInstance->Modules->AddService(cmd2);
- OnRehash(NULL);
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" REMOVE");
+ tokens["REMOVE"];
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- supportnokicks = ServerInstance->Config->ConfValue("remove")->getBool("supportnokicks");
+ ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+ supportnokicks = tag->getBool("supportnokicks");
+ cmd1.protectedrank = cmd2.protectedrank = tag->getInt("protectedrank", 50000);
}
- virtual ~ModuleRemove()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleRemove)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class ChannelSettings
+{
+ public:
+ enum RepeatAction
+ {
+ ACT_KICK,
+ ACT_BLOCK,
+ ACT_BAN
+ };
+
+ RepeatAction Action;
+ unsigned int Backlog;
+ unsigned int Lines;
+ unsigned int Diff;
+ unsigned int Seconds;
+
+ void serialize(std::string& out) const
+ {
+ if (Action == ACT_BAN)
+ out.push_back('*');
+ else if (Action == ACT_BLOCK)
+ out.push_back('~');
+
+ out.append(ConvToStr(Lines)).push_back(':');
+ out.append(ConvToStr(Seconds));
+ if (Diff)
+ {
+ out.push_back(':');
+ out.append(ConvToStr(Diff));
+ if (Backlog)
+ {
+ out.push_back(':');
+ out.append(ConvToStr(Backlog));
+ }
+ }
+ }
+};
+
+class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
+{
+ private:
+ struct RepeatItem
+ {
+ time_t ts;
+ std::string line;
+ RepeatItem(time_t TS, const std::string& Line) : ts(TS), line(Line) { }
+ };
+
+ typedef std::deque<RepeatItem> RepeatItemList;
+
+ struct MemberInfo
+ {
+ RepeatItemList ItemList;
+ unsigned int Counter;
+ MemberInfo() : Counter(0) {}
+ };
+
+ struct ModuleSettings
+ {
+ unsigned int MaxLines;
+ unsigned int MaxSecs;
+ unsigned int MaxBacklog;
+ unsigned int MaxDiff;
+ unsigned int MaxMessageSize;
+ ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
+ };
+
+ std::vector<unsigned int> mx[2];
+ ModuleSettings ms;
+
+ bool CompareLines(const std::string& message, const std::string& historyline, unsigned int trigger)
+ {
+ if (message == historyline)
+ return true;
+ else if (trigger)
+ return (Levenshtein(message, historyline) <= trigger);
+
+ return false;
+ }
+
+ unsigned int Levenshtein(const std::string& s1, const std::string& s2)
+ {
+ unsigned int l1 = s1.size();
+ unsigned int l2 = s2.size();
+
+ for (unsigned int i = 0; i < l2; i++)
+ mx[0][i] = i;
+ for (unsigned int i = 0; i < l1; i++)
+ {
+ mx[1][0] = i + 1;
+ for (unsigned int j = 0; j < l2; j++)
+ mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
+
+ mx[0].swap(mx[1]);
+ }
+ return mx[0][l2];
+ }
+
+ public:
+ SimpleExtItem<MemberInfo> MemberInfoExt;
+
+ RepeatMode(Module* Creator)
+ : ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
+ , MemberInfoExt("repeat_memb", Creator)
+ {
+ }
+
+ void OnUnset(User* source, Channel* chan)
+ {
+ // Unset the per-membership extension when the mode is removed
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+ MemberInfoExt.unset(i->second);
+ }
+
+ ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
+ {
+ ChannelSettings settings;
+ if (!ParseSettings(source, parameter, settings))
+ {
+ source->WriteNotice("*** Invalid syntax. Syntax is {[~*]}[lines]:[time]{:[difference]}{:[backlog]}");
+ return MODEACTION_DENY;
+ }
+
+ if ((settings.Backlog > 0) && (settings.Lines > settings.Backlog))
+ {
+ source->WriteNotice("*** You can't set needed lines higher than backlog");
+ return MODEACTION_DENY;
+ }
+
+ LocalUser* localsource = IS_LOCAL(source);
+ if ((localsource) && (!ValidateSettings(localsource, settings)))
+ return MODEACTION_DENY;
+
+ ext.set(channel, settings);
+
+ return MODEACTION_ALLOW;
+ }
+
+ bool MatchLine(Membership* memb, ChannelSettings* rs, std::string message)
+ {
+ // If the message is larger than whatever size it's set to,
+ // let's pretend it isn't. If the first 512 (def. setting) match, it's probably spam.
+ if (message.size() > ms.MaxMessageSize)
+ message.erase(ms.MaxMessageSize);
+
+ MemberInfo* rp = MemberInfoExt.get(memb);
+ if (!rp)
+ {
+ rp = new MemberInfo;
+ MemberInfoExt.set(memb, rp);
+ }
+
+ unsigned int matches = 0;
+ if (!rs->Backlog)
+ matches = rp->Counter;
+
+ RepeatItemList& items = rp->ItemList;
+ const unsigned int trigger = (message.size() * rs->Diff / 100);
+ const time_t now = ServerInstance->Time();
+
+ std::transform(message.begin(), message.end(), message.begin(), ::tolower);
+
+ for (std::deque<RepeatItem>::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ if (it->ts < now)
+ {
+ items.erase(it, items.end());
+ matches = 0;
+ break;
+ }
+
+ if (CompareLines(message, it->line, trigger))
+ {
+ if (++matches >= rs->Lines)
+ {
+ if (rs->Action != ChannelSettings::ACT_BLOCK)
+ rp->Counter = 0;
+ return true;
+ }
+ }
+ else if ((ms.MaxBacklog == 0) || (rs->Backlog == 0))
+ {
+ matches = 0;
+ items.clear();
+ break;
+ }
+ }
+
+ unsigned int max_items = (rs->Backlog ? rs->Backlog : 1);
+ if (items.size() >= max_items)
+ items.pop_back();
+
+ items.push_front(RepeatItem(now + rs->Seconds, message));
+ rp->Counter = matches;
+ return false;
+ }
+
+ void Resize(size_t size)
+ {
+ size_t newsize = size+1;
+ if (newsize <= mx[0].size())
+ return;
+ ms.MaxMessageSize = size;
+ mx[0].resize(newsize);
+ mx[1].resize(newsize);
+ }
+
+ void ReadConfig()
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("repeat");
+ ms.MaxLines = conf->getInt("maxlines", 20);
+ ms.MaxBacklog = conf->getInt("maxbacklog", 20);
+ ms.MaxSecs = conf->getInt("maxsecs", 0);
+
+ ms.MaxDiff = conf->getInt("maxdistance", 50);
+ if (ms.MaxDiff > 100)
+ ms.MaxDiff = 100;
+
+ unsigned int newsize = conf->getInt("size", 512);
+ if (newsize > ServerInstance->Config->Limits.MaxLine)
+ newsize = ServerInstance->Config->Limits.MaxLine;
+ Resize(newsize);
+ }
+
+ std::string GetModuleSettings() const
+ {
+ return ConvToStr(ms.MaxLines) + ":" + ConvToStr(ms.MaxSecs) + ":" + ConvToStr(ms.MaxDiff) + ":" + ConvToStr(ms.MaxBacklog);
+ }
+
+ void SerializeParam(Channel* chan, const ChannelSettings* chset, std::string& out)
+ {
+ chset->serialize(out);
+ }
+
+ private:
+ bool ParseSettings(User* source, std::string& parameter, ChannelSettings& settings)
+ {
+ irc::sepstream stream(parameter, ':');
+ std::string item;
+ if (!stream.GetToken(item))
+ // Required parameter missing
+ return false;
+
+ if ((item[0] == '*') || (item[0] == '~'))
+ {
+ settings.Action = ((item[0] == '*') ? ChannelSettings::ACT_BAN : ChannelSettings::ACT_BLOCK);
+ item.erase(item.begin());
+ }
+ else
+ settings.Action = ChannelSettings::ACT_KICK;
+
+ if ((settings.Lines = ConvToInt(item)) == 0)
+ return false;
+
+ if ((!stream.GetToken(item)) || ((settings.Seconds = InspIRCd::Duration(item)) == 0))
+ // Required parameter missing
+ return false;
+
+ // The diff and backlog parameters are optional
+ settings.Diff = settings.Backlog = 0;
+ if (stream.GetToken(item))
+ {
+ // There is a diff parameter, see if it's valid (> 0)
+ if ((settings.Diff = ConvToInt(item)) == 0)
+ return false;
+
+ if (stream.GetToken(item))
+ {
+ // There is a backlog parameter, see if it's valid
+ if ((settings.Backlog = ConvToInt(item)) == 0)
+ return false;
+
+ // If there are still tokens, then it's invalid because we allow only 4
+ if (stream.GetToken(item))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool ValidateSettings(LocalUser* source, const ChannelSettings& settings)
+ {
+ if (settings.Backlog && !ms.MaxBacklog)
+ {
+ source->WriteNotice("*** The server administrator has disabled backlog matching");
+ return false;
+ }
+
+ if (settings.Diff)
+ {
+ if (settings.Diff > ms.MaxDiff)
+ {
+ if (ms.MaxDiff == 0)
+ source->WriteNotice("*** The server administrator has disabled matching on edit distance");
+ else
+ source->WriteNotice("*** The distance you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxDiff));
+ return false;
+ }
+
+ if (ms.MaxLines && settings.Lines > ms.MaxLines)
+ {
+ source->WriteNotice("*** The line number you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxLines));
+ return false;
+ }
+
+ if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
+ {
+ source->WriteNotice("*** The seconds you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxSecs));
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class RepeatModule : public Module
+{
+ RepeatMode rm;
+
+ public:
+ RepeatModule() : rm(this) {}
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ rm.ReadConfig();
+ }
+
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
+ {
+ if (target_type != TYPE_CHANNEL || !IS_LOCAL(user))
+ return MOD_RES_PASSTHRU;
+
+ Channel* chan = reinterpret_cast<Channel*>(dest);
+ ChannelSettings* settings = rm.ext.get(chan);
+ if (!settings)
+ return MOD_RES_PASSTHRU;
+
+ Membership* memb = chan->GetUser(user);
+ if (!memb)
+ return MOD_RES_PASSTHRU;
+
+ if (ServerInstance->OnCheckExemption(user, chan, "repeat") == MOD_RES_ALLOW)
+ return MOD_RES_PASSTHRU;
+
+ if (rm.MatchLine(memb, settings, text))
+ {
+ if (settings->Action == ChannelSettings::ACT_BLOCK)
+ {
+ user->WriteNotice("*** This line is too similiar to one of your last lines.");
+ return MOD_RES_DENY;
+ }
+
+ if (settings->Action == ChannelSettings::ACT_BAN)
+ {
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
+ }
+
+ memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+ }
+
+ void Prioritize() CXX11_OVERRIDE
+ {
+ ServerInstance->Modules->SetPriority(this, I_OnUserPreMessage, PRIORITY_LAST);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides the +E channel mode - for blocking of similiar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+ }
+};
+
+MODULE_INIT(RepeatModule)
#include "inspircd.h"
-/* $ModDesc: Only opers may create new channels if this module is loaded */
-
class ModuleRestrictChans : public Module
{
- std::set<irc::string> allowchans;
+ std::set<std::string, irc::insensitive_swo> allowchans;
- void ReadConfig()
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
allowchans.clear();
ConfigTagList tags = ServerInstance->Config->ConfTags("allowchannel");
{
ConfigTag* tag = i->second;
std::string txt = tag->getString("name");
- allowchans.insert(txt.c_str());
+ allowchans.insert(txt);
}
}
- public:
- void init()
- {
- ReadConfig();
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
- {
- ReadConfig();
- }
-
-
- virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- irc::string x = cname;
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
// channel does not yet exist (record is null, about to be created IF we were to allow it)
if (!chan)
{
// user is not an oper and its not in the allow list
- if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
+ if ((!user->IsOper()) && (allowchans.find(cname) == allowchans.end()))
{
- user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Only IRC operators may create new channels",user->nick.c_str(),cname);
+ user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Only IRC operators may create new channels", cname.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleRestrictChans()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Only opers may create new channels if this module is loaded",VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
-
-
class ModuleRestrictMsg : public Module
{
-
public:
-
- void init()
- {
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
{
// (1) the sender is opered
// (2) the recipient is opered
// anything else, blocked.
- if (IS_OPER(u) || IS_OPER(user))
+ if (u->IsOper() || user->IsOper())
{
return MOD_RES_PASSTHRU;
}
- user->WriteNumeric(ERR_CANTSENDTOUSER, "%s %s :You are not permitted to send private messages to this user",user->nick.c_str(),u->nick.c_str());
+ user->WriteNumeric(ERR_CANTSENDTOUSER, "%s :You are not permitted to send private messages to this user", u->nick.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual ~ModuleRestrictMsg()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Forbids users from messaging each other. Users may still message opers and opers may message other opers.",VF_VENDOR);
}
*/
-/* $ModDesc: Allows for RIPEMD-160 encrypted oper passwords */
-
/* macro definitions */
#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
+#include "modules/hash.h"
#define RMDsize 160
-#ifndef HAS_STDINT
-typedef unsigned char byte;
-typedef unsigned int dword;
-#else
-typedef uint8_t byte;
-typedef uint32_t dword;
-#endif
+typedef uint8_t byte;
+typedef uint32_t dword;
/* collect four bytes into one word: */
#define BYTES_TO_DWORD(strptr) \
{
if (key)
{
- ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with custom mdbuf");
MDbuf[0] = key[0];
MDbuf[1] = key[1];
MDbuf[2] = key[2];
}
else
{
- ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with default mdbuf");
MDbuf[0] = 0x67452301UL;
MDbuf[1] = 0xefcdab89UL;
MDbuf[2] = 0x98badcfeUL;
byte *RMD(byte *message, dword length, unsigned int* key)
{
- ServerInstance->Logs->Log("m_ripemd160", DEBUG, "RMD: '%s' length=%u", (const char*)message, length);
dword MDbuf[RMDsize/32]; /* contains (A, B, C, D(E)) */
dword X[16]; /* current 16-word chunk */
unsigned int i; /* counter */
return (byte *)hashcode;
}
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
char* rv = (char*)RMD((byte*)data.data(), data.length(), NULL);
return std::string(rv, RMDsize / 8);
}
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- return "";
- }
-
- RIProv(Module* m) : HashProvider(m, "hash/ripemd160", 20, 64) {}
+ RIProv(Module* m) : HashProvider(m, "ripemd160", 20, 64) {}
};
class ModuleRIPEMD160 : public Module
RIProv mr;
ModuleRIPEMD160() : mr(this)
{
- ServerInstance->Modules->AddService(mr);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides RIPEMD-160 hashing", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleRIPEMD160)
-
*/
-/* $ModDesc: RLINE: Regexp user banning. */
-
#include "inspircd.h"
-#include "m_regex.h"
+#include "modules/regex.h"
#include "xline.h"
static bool ZlineOnMatch = false;
bool Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
std::string compare = u->nick + "!" + u->ident + "@" + u->host + " " + u->fullname;
ZLine* zl = new ZLine(ServerInstance->Time(), duration ? expiry - ServerInstance->Time() : 0, ServerInstance->Config->ServerName.c_str(), reason.c_str(), u->GetIPString());
if (ServerInstance->XLines->AddLine(zl, NULL))
{
- std::string timestr = ServerInstance->TimeString(zl->expiry);
+ std::string timestr = InspIRCd::TimeString(zl->expiry);
ServerInstance->SNO->WriteToSnoMask('x', "Z-line added due to R-line match on *@%s%s%s: %s",
zl->ipaddr.c_str(), zl->duration ? " to expire on " : "", zl->duration ? timestr.c_str() : "", zl->reason.c_str());
added_zline = true;
DefaultApply(u, "R", false);
}
- void DisplayExpiry()
+ const std::string& Displayable()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired R-line %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
- }
-
- const char* Displayable()
- {
- return matchtext.c_str();
+ return matchtext;
}
std::string matchtext;
RLineFactory(dynamic_reference<RegexFactory>& rx) : XLineFactory("R"), rxfactory(rx)
{
}
-
+
/** Generate a RLine
*/
XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory);
}
-
- ~RLineFactory()
- {
- }
};
/** Handle /RLINE
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
XLine *r = NULL;
try
}
catch (ModuleException &e)
{
- ServerInstance->SNO->WriteToSnoMask('a',"Could not add RLINE: %s", e.GetReason());
+ ServerInstance->SNO->WriteToSnoMask('a',"Could not add RLINE: " + e.GetReason());
}
if (r)
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x', "%s added timed R-line for %s to expire on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
}
else
{
delete r;
- user->WriteServ("NOTICE %s :*** R-Line for %s already exists", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** R-Line for " + parameters[0] + " already exists");
}
}
}
}
else
{
- user->WriteServ("NOTICE %s :*** R-Line %s not found in list, try /stats R.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** R-Line " + parameters[0] + " not found in list, try /stats R.");
}
}
class ModuleRLine : public Module
{
- private:
dynamic_reference<RegexFactory> rxfactory;
RLineFactory f;
CommandRLine r;
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
-
- ServerInstance->Modules->AddService(r);
ServerInstance->XLines->RegisterFactory(&f);
-
- Implementation eventlist[] = { I_OnUserRegister, I_OnRehash, I_OnUserPostNick, I_OnStats, I_OnBackgroundTimer, I_OnUnloadModule };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModuleRLine()
+ ~ModuleRLine()
{
ServerInstance->XLines->DelAll("R");
ServerInstance->XLines->UnregisterFactory(&f);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("RLINE: Regexp user banning.", VF_COMMON | VF_VENDOR, rxfactory ? rxfactory->name : "");
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
// Apply lines on user connect
XLine *rl = ServerInstance->XLines->MatchesLine("R", user);
return MOD_RES_PASSTHRU;
}
- virtual void OnRehash(User *user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("rline");
initing = false;
}
- virtual ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
{
if (symbol != 'R')
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
- virtual void OnUserPostNick(User *user, const std::string &oldnick)
+ void OnUserPostNick(User *user, const std::string &oldnick) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
}
}
- virtual void OnBackgroundTimer(time_t curtime)
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
if (added_zline)
{
}
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
// If the regex engine became unavailable or has changed, remove all rlines
if (!rxfactory)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "listmode.h"
+
+/** Handle /RMODE
+ */
+class CommandRMode : public Command
+{
+ public:
+ CommandRMode(Module* Creator) : Command(Creator,"RMODE", 2, 3)
+ {
+ allow_empty_last_param = false;
+ syntax = "<channel> <mode> [pattern]";
+ }
+
+ CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
+ {
+ ModeHandler* mh;
+ Channel* chan = ServerInstance->FindChan(parameters[0]);
+ char modeletter = parameters[1][0];
+
+ if (chan == NULL)
+ {
+ user->WriteNotice("The channel " + parameters[0] + " does not exist.");
+ return CMD_FAILURE;
+ }
+
+ mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+ if (mh == NULL || parameters[1].size() > 1)
+ {
+ user->WriteNotice(parameters[1] + " is not a valid channel mode.");
+ return CMD_FAILURE;
+ }
+
+ if (chan->GetPrefixValue(user) < mh->GetLevelRequired())
+ {
+ user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " + chan->name + ".");
+ return CMD_FAILURE;
+ }
+
+ std::string pattern = parameters.size() > 2 ? parameters[2] : "*";
+ PrefixMode* pm;
+ ListModeBase* lm;
+ ListModeBase::ModeList* ml;
+ Modes::ChangeList changelist;
+
+ if ((pm = mh->IsPrefixMode()))
+ {
+ // As user prefix modes don't have a GetList() method, let's iterate through the channel's users.
+ const Channel::MemberMap& users = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it)
+ {
+ if (!InspIRCd::Match(it->first->nick, pattern))
+ continue;
+ if (it->second->hasMode(modeletter) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
+ changelist.push_remove(mh, it->first->nick);
+ }
+ }
+ else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != NULL))
+ {
+ for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it)
+ {
+ if (!InspIRCd::Match(it->mask, pattern))
+ continue;
+ changelist.push_remove(mh, it->mask);
+ }
+ }
+ else
+ {
+ if (chan->IsModeSet(mh))
+ changelist.push_remove(mh);
+ }
+
+ ServerInstance->Modes->Process(user, chan, NULL, changelist);
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleRMode : public Module
+{
+ CommandRMode cmd;
+
+ public:
+ ModuleRMode() : cmd(this) { }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Allows glob-based removal of list modes", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleRMode)
#include "inspircd.h"
-/* $ModDesc: Provides command SAJOIN to allow opers to force-join users to channels */
-
/** Handle /SAJOIN
*/
class CommandSajoin : public Command
{
public:
- CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 2)
+ CommandSajoin(Module* Creator) : Command(Creator,"SAJOIN", 1)
{
allow_empty_last_param = false;
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ flags_needed = 'o'; Penalty = 0; syntax = "[<nick>] <channel>[,<channel>]";
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
- User* dest = ServerInstance->FindNick(parameters[0]);
+ const unsigned int channelindex = (parameters.size() > 1) ? 1 : 0;
+ if (CommandParser::LoopCall(user, this, parameters, channelindex))
+ return CMD_FAILURE;
+
+ const std::string& channel = parameters[channelindex];
+ const std::string& nickname = parameters.size() > 1 ? parameters[0] : user->nick;
+
+ User* dest = ServerInstance->FindNick(nickname);
if ((dest) && (dest->registered == REG_ALL))
{
- if (ServerInstance->ULine(dest->server))
+ if (user != dest && !user->HasPrivPermission("users/sajoin-others", false))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNotice("*** You are not allowed to /SAJOIN other users (the privilege users/sajoin-others is needed to /SAJOIN others).");
return CMD_FAILURE;
}
- if (IS_LOCAL(user) && !ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+
+ if (dest->server->IsULine())
+ {
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ return CMD_FAILURE;
+ }
+ if (IS_LOCAL(user) && !ServerInstance->IsChannel(channel))
{
/* we didn't need to check this for each character ;) */
- user->WriteServ("NOTICE "+user->nick+" :*** Invalid characters in channel name or name too long");
+ user->WriteNotice("*** Invalid characters in channel name or name too long");
+ return CMD_FAILURE;
+ }
+
+ Channel* chan = ServerInstance->FindChan(channel);
+ if ((chan) && (chan->HasUser(dest)))
+ {
+ user->SendText(":" + user->server->GetName() + " NOTICE " + user->nick + " :*** " + dest->nick + " is already on " + channel);
return CMD_FAILURE;
}
- /* For local users, we send the JoinUser which may create a channel and set its TS.
+ /* For local users, we call Channel::JoinUser which may create a channel and set its TS.
* For non-local users, we just return CMD_SUCCESS, knowing this will propagate it where it needs to be
- * and then that server will generate the users JOIN or FJOIN instead.
+ * and then that server will handle the command.
*/
- if (IS_LOCAL(dest))
+ LocalUser* localuser = IS_LOCAL(dest);
+ if (localuser)
{
- Channel::JoinUser(dest, parameters[1].c_str(), true, "", false, ServerInstance->Time());
- /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propagate */
- Channel* n = ServerInstance->FindChan(parameters[1]);
- if (n)
+ chan = Channel::JoinUser(localuser, channel, true);
+ if (chan)
{
- if (n->HasUser(dest))
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]+" (User is probably banned, or blocking modes)");
- return CMD_FAILURE;
- }
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAJOIN to make "+dest->nick+" join "+channel);
+ return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE "+user->nick+" :*** Could not join "+dest->nick+" to "+parameters[1]);
+ user->WriteNotice("*** Could not join "+dest->nick+" to "+channel);
return CMD_FAILURE;
}
}
}
else
{
- user->WriteServ("NOTICE "+user->nick+" :*** No such nickname "+parameters[0]);
+ user->WriteNotice("*** No such nickname "+nickname);
return CMD_FAILURE;
}
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSajoin()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAJOIN to allow opers to force-join users to channels", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSajoin)
#include "inspircd.h"
-/* $ModDesc: Provides a SAKICK command */
-
/** Handle /SAKICK
*/
class CommandSakick : public Command
CommandSakick(Module* Creator) : Command(Creator,"SAKICK", 2, 3)
{
flags_needed = 'o'; Penalty = 0; syntax = "<channel> <nick> [reason]";
- TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
User* dest = ServerInstance->FindNick(parameters[1]);
Channel* channel = ServerInstance->FindChan(parameters[0]);
- const char* reason = "";
if ((dest) && (dest->registered == REG_ALL) && (channel))
{
- if (parameters.size() > 2)
- {
- reason = parameters[2].c_str();
- }
- else
+ const std::string& reason = (parameters.size() > 2) ? parameters[2] : dest->nick;
+
+ if (dest->server->IsULine())
{
- reason = dest->nick.c_str();
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ return CMD_FAILURE;
}
- if (ServerInstance->ULine(dest->server))
+ if (!channel->HasUser(dest))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client", user->nick.c_str());
+ user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
return CMD_FAILURE;
}
*/
if (IS_LOCAL(dest))
{
+ // Target is on this server, kick them and send the snotice
channel->KickUser(ServerInstance->FakeClient, dest, reason);
-
- Channel *n = ServerInstance->FindChan(parameters[1]);
- if (n && n->HasUser(dest))
- {
- /* Sort-of-bug: If the command was issued remotely, this message won't be sent */
- user->WriteServ("NOTICE %s :*** Unable to kick %s from %s", user->nick.c_str(), dest->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
-
- if (IS_LOCAL(user))
- {
- /* Locally issued command; send the snomasks */
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + parameters[0]);
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " SAKICKed " + dest->nick + " on " + channel->name);
}
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+ user->WriteNotice("*** Invalid nickname or channel");
}
return CMD_FAILURE;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSakick()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a SAKICK command", VF_OPTCOMMON|VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSakick)
-
*/
-/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */
-
#include "inspircd.h"
/** Handle /SAMODE
user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
return CMD_FAILURE;
}
+
+ // Changing the modes of another user requires a special permission
+ if ((target != user) && (!user->HasPrivPermission("users/samode-usermodes", true)))
+ return CMD_FAILURE;
}
+ // XXX: Make ModeParser clear LastParse
+ Modes::ChangeList emptychangelist;
+ ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+
this->active = true;
- ServerInstance->Parser->CallHandler("MODE", parameters, user);
- if (ServerInstance->Modes->GetLastParse().length())
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
+ CmdResult result = ServerInstance->Parser.CallHandler("MODE", parameters, user);
this->active = false;
+
+ if (result == CMD_SUCCESS)
+ {
+ // If lastparse is empty and the MODE command handler returned CMD_SUCCESS then
+ // the client queried the list of a listmode (e.g. /SAMODE #chan b), which was
+ // handled internally by the MODE command handler.
+ //
+ // Viewing the modes of a user or a channel can also result in CMD_SUCCESS, but
+ // that is not possible with /SAMODE because we require at least 2 parameters.
+ const std::string& lastparse = ServerInstance->Modes.GetLastParse();
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + (lastparse.empty() ? irc::stringjoiner(parameters) : lastparse));
+ }
+
return CMD_SUCCESS;
}
};
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->Attach(I_OnPreMode, this);
- }
-
- ~ModuleSaMode()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
}
- ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
+ ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
{
if (cmd.active)
return MOD_RES_ALLOW;
#include "inspircd.h"
-/* $ModDesc: Provides support for SANICK command */
-
/** Handle /SANICK
*/
class CommandSanick : public Command
{
allow_empty_last_param = false;
flags_needed = 'o'; Penalty = 0; syntax = "<nick> <new-nick>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
/* Do local sanity checks and bails */
if (IS_LOCAL(user))
{
- if (target && ServerInstance->ULine(target->server))
+ if (target && target->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** No such nickname: '" + parameters[0] + "'");
return CMD_FAILURE;
}
- if (!ServerInstance->IsNick(parameters[1].c_str(), ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(parameters[1]))
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[1].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[1] + "'");
return CMD_FAILURE;
}
}
{
std::string oldnick = user->nick;
std::string newnick = target->nick;
- if (target->ChangeNick(parameters[1], true))
+ if (target->ChangeNick(parameters[1]))
{
ServerInstance->SNO->WriteGlobalSno('a', oldnick+" used SANICK to change "+newnick+" to "+parameters[1]);
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSanick()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for SANICK command", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSanick)
#include "inspircd.h"
-/* $ModDesc: Provides command SAPART to force-part users from a channel. */
-
/** Handle /SAPART
*/
class CommandSapart : public Command
public:
CommandSapart(Module* Creator) : Command(Creator,"SAPART", 2, 3)
{
- flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel> [reason]";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
+ flags_needed = 'o'; Penalty = 0; syntax = "<nick> <channel>[,<channel>] [reason]";
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
+ if (CommandParser::LoopCall(user, this, parameters, 1))
+ return CMD_FAILURE;
+
User* dest = ServerInstance->FindNick(parameters[0]);
Channel* channel = ServerInstance->FindChan(parameters[1]);
std::string reason;
if (parameters.size() > 2)
reason = parameters[2];
- if (ServerInstance->ULine(dest->server))
+ if (dest->server->IsULine())
+ {
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
+ return CMD_FAILURE;
+ }
+
+ if (!channel->HasUser(dest))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNotice("*** " + dest->nick + " is not on " + channel->name);
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
channel->PartUser(dest, reason);
-
- Channel* n = ServerInstance->FindChan(parameters[1]);
- if (!n)
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- if (!n->HasUser(dest))
- {
- ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+parameters[1]);
- return CMD_SUCCESS;
- }
- else
- {
- user->WriteServ("NOTICE %s :*** Unable to make %s part %s",user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
- return CMD_FAILURE;
- }
- }
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAPART to make "+dest->nick+" part "+channel->name);
}
return CMD_SUCCESS;
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick.c_str());
+ user->WriteNotice("*** Invalid nickname or channel");
}
return CMD_FAILURE;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSapart()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides command SAPART to force-part users from a channel.", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSapart)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
-
/** Handle /SAQUIT
*/
class CommandSaquit : public Command
CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
{
flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
User* dest = ServerInstance->FindNick(parameters[0]);
if ((dest) && (!IS_SERVER(dest)) && (dest->registered == REG_ALL))
{
- if (ServerInstance->ULine(dest->server))
+ if (dest->server->IsULine())
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
return CMD_FAILURE;
}
// Pass the command on, so the client's server can quit it properly.
if (!IS_LOCAL(dest))
return CMD_SUCCESS;
-
+
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAQUIT to make "+dest->nick+" quit with a reason of "+parameters[1]);
ServerInstance->Users->QuitUser(dest, parameters[1]);
}
else
{
- user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNotice("*** Invalid nickname '" + parameters[0] + "'");
return CMD_FAILURE;
}
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSaquit()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for an SAQUIT command, exits user with a reason", VF_OPTCOMMON | VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSaquit)
#include "inspircd.h"
-#include "m_cap.h"
-#include "account.h"
-#include "sasl.h"
-#include "ssl.h"
-
-/* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
+#include "modules/cap.h"
+#include "modules/account.h"
+#include "modules/sasl.h"
+#include "modules/ssl.h"
enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
static void SendSASL(const parameterlist& params)
{
- if (!ServerInstance->PI->SendEncapsulatedData(params))
+ if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
{
SASLFallback(NULL, params);
}
: user(user_), state(SASL_INIT), state_announced(false)
{
parameterlist params;
- params.push_back(sasl_target);
- params.push_back("SASL");
params.push_back(user->uuid);
params.push_back("*");
params.push_back("S");
params.push_back(method);
- if (method == "EXTERNAL" && IS_LOCAL(user_))
+ LocalUser* localuser = IS_LOCAL(user);
+ if (method == "EXTERNAL" && localuser)
{
- SocketCertificateRequest req(&((LocalUser*)user_)->eh, ServerInstance->Modules->Find("m_sasl.so"));
- std::string fp = req.GetFingerprint();
+ std::string fp = SSLClientCert::GetFingerprint(&localuser->eh);
if (fp.size())
params.push_back(fp);
else if (msg[2] == "M")
this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
else
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
break;
case SASL_DONE:
break;
default:
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
break;
}
return true;
parameterlist params;
- params.push_back(sasl_target);
- params.push_back("SASL");
params.push_back(this->user->uuid);
params.push_back(this->agent);
params.push_back("C");
switch (this->result)
{
case SASL_OK:
- this->user->WriteNumeric(903, "%s :SASL authentication successful", this->user->nick.c_str());
+ this->user->WriteNumeric(903, ":SASL authentication successful");
break;
case SASL_ABORT:
- this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
+ this->user->WriteNumeric(906, ":SASL authentication aborted");
break;
case SASL_FAIL:
- this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
+ this->user->WriteNumeric(904, ":SASL authentication failed");
break;
default:
break;
User* target = ServerInstance->FindNick(parameters[1]);
if ((!target) || (IS_SERVER(target)))
{
- ServerInstance->Logs->Log("m_sasl", DEBUG,"User not found in sasl ENCAP event: %s", parameters[1].c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
return CMD_FAILURE;
}
GenericCap cap;
CommandAuthenticate auth;
CommandSASL sasl;
+
public:
ModuleSASL()
: authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnEvent, I_OnUserRegister, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- ServiceProvider* providelist[] = { &auth, &sasl, &authExt };
- ServerInstance->Modules->AddServices(providelist, 3);
-
if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
- ServerInstance->Logs->Log("m_sasl", DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
}
- void OnRehash(User*)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
}
- ModResult OnUserRegister(LocalUser *user)
+ ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
{
SaslAuthenticator *sasl_ = authExt.get(user);
if (sasl_)
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE.",VF_VENDOR);
}
- void OnEvent(Event &ev)
+ void OnEvent(Event &ev) CXX11_OVERRIDE
{
cap.HandleEvent(ev);
}
*/
-/* $ModDesc: Provides a SATOPIC command */
-
#include "inspircd.h"
/** Handle /SATOPIC
if(target)
{
- std::string newTopic = parameters[1];
-
- // 3rd parameter overrides access checks
- target->SetTopic(user, newTopic, true);
+ const std::string& newTopic = parameters[1];
+ target->SetTopic(user, newTopic);
ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SATOPIC on " + target->name + ", new topic: " + newTopic);
return CMD_SUCCESS;
}
else
{
- user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
return CMD_FAILURE;
}
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSATopic()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a SATOPIC command", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Disallows /LIST for recently connected clients to hinder spam bots */
-
class ModuleSecureList : public Module
{
- private:
std::vector<std::string> allowlist;
time_t WaitTime;
- public:
- void init()
- {
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
- virtual ~ModuleSecureList()
- {
- }
-
- virtual Version GetVersion()
+ public:
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Disallows /LIST for recently connected clients to hinder spam bots", VF_VENDOR);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
allowlist.clear();
* OnPreCommand()
* Intercept the LIST command.
*/
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return MOD_RES_PASSTHRU;
- if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user)))
+ if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!user->IsOper()))
{
/* Normally wouldnt be allowed here, are they exempt? */
for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++)
return MOD_RES_PASSTHRU;
/* Not exempt, BOOK EM DANNO! */
- user->WriteServ("NOTICE %s :*** You cannot list within the first %lu seconds of connecting. Please try again later.",user->nick.c_str(), (unsigned long) WaitTime);
+ user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
/* Some crap clients (read: mIRC, various java chat applets) muck up if they don't
* receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
*/
- user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
- user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
+ user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name");
+ user->WriteNumeric(RPL_LISTEND, ":End of channel list.");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" SECURELIST");
+ tokens["SECURELIST"];
}
};
#include "inspircd.h"
-/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks 'n' and 'N'. */
-
class ModuleSeeNicks : public Module
{
public:
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->SNO->EnableSnomask('n',"NICK");
- Implementation eventlist[] = { I_OnUserPostNick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for seeing local and remote nickchanges via snomasks", VF_VENDOR);
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick.c_str());
}
#include "inspircd.h"
-/* $ModDesc: Implements extban +b s: - server name bans */
-
class ModuleServerBan : public Module
{
- private:
public:
- void init()
- {
- Implementation eventlist[] = { I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleServerBan()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Extban 's' - server ban",VF_OPTCOMMON|VF_VENDOR);
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 's') && (mask[1] == ':'))
{
- if (InspIRCd::Match(user->server, mask.substr(2)))
+ if (InspIRCd::Match(user->server->GetName(), mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('s');
+ tokens["EXTBAN"].push_back('s');
}
};
-
MODULE_INIT(ModuleServerBan)
-
*/
-/* $ModDesc: Provides support for ircu-style services accounts, including chmode +R, etc. */
-
#include "inspircd.h"
-#include "account.h"
+#include "modules/account.h"
/** Channel mode +r - mark a channel as identified
*/
if (!IS_LOCAL(source))
{
// Only change the mode if it's not redundant
- if ((adding != channel->IsModeSet('r')))
+ if ((adding != channel->IsModeSet(this)))
{
- channel->SetMode('r',adding);
+ channel->SetMode(this, adding);
return MODEACTION_ALLOW;
}
}
else
{
- source->WriteNumeric(500, "%s :Only a server may modify the +r channel mode", source->nick.c_str());
+ source->WriteNumeric(500, ":Only a server may modify the +r channel mode");
}
return MODEACTION_DENY;
}
{
if (!IS_LOCAL(source))
{
- if ((adding != dest->IsModeSet('r')))
+ if ((adding != dest->IsModeSet(this)))
{
- dest->SetMode('r',adding);
+ dest->SetMode(this, adding);
return MODEACTION_ALLOW;
}
}
else
{
- source->WriteNumeric(500, "%s :Only a server may modify the +r user mode", source->nick.c_str());
+ source->WriteNumeric(500, ":Only a server may modify the +r user mode");
}
return MODEACTION_DENY;
}
AChannel_M(Module* Creator) : SimpleChannelModeHandler(Creator, "regmoderated", 'M') { }
};
+class AccountExtItemImpl : public AccountExtItem
+{
+ public:
+ AccountExtItemImpl(Module* mod)
+ : AccountExtItem("accountname", mod)
+ {
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+ {
+ User* user = dynamic_cast<User*>(container);
+ if (!user)
+ return;
+
+ StringExtItem::unserialize(format, container, value);
+ if (!value.empty())
+ {
+ // Logged in
+ if (IS_LOCAL(user))
+ {
+ user->WriteNumeric(900, "%s %s :You are now logged in as %s",
+ user->GetFullHost().c_str(), value.c_str(), value.c_str());
+ }
+
+ AccountEvent(creator, user, value).Send();
+ }
+ else
+ {
+ // Logged out
+ AccountEvent(creator, user, "").Send();
+ }
+ }
+};
+
class ModuleServicesAccount : public Module
{
AChannel_R m1;
AUser_R m3;
Channel_r m4;
User_r m5;
- AccountExtItem accountname;
+ AccountExtItemImpl accountname;
bool checking_ban;
-
- static bool ReadCGIIRCExt(const char* extname, User* user, const std::string*& out)
- {
- ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname);
- if (!wiext)
- return false;
-
- if (wiext->creator->ModuleSourceFile != "m_cgiirc.so")
- return false;
-
- StringExtItem* stringext = static_cast<StringExtItem*>(wiext);
- std::string* addr = stringext->get(user);
- if (!addr)
- return false;
-
- out = addr;
- return true;
- }
-
public:
ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this),
- accountname("accountname", this), checking_ban(false)
+ accountname(this)
+ , checking_ban(false)
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServiceProvider* providerlist[] = { &m1, &m2, &m3, &m4, &m5, &accountname };
- ServerInstance->Modules->AddServices(providerlist, sizeof(providerlist)/sizeof(ServiceProvider*));
- Implementation eventlist[] = { I_OnWhois, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreJoin, I_OnCheckBan,
- I_OnDecodeMetaData, I_On005Numeric, I_OnUserPostNick, I_OnSetConnectClass };
-
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void On005Numeric(std::string &t)
- {
- ServerInstance->AddExtBanChar('R');
- ServerInstance->AddExtBanChar('U');
+ tokens["EXTBAN"].push_back('R');
+ tokens["EXTBAN"].push_back('U');
}
/* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
- void OnWhois(User* source, User* dest)
+ void OnWhois(User* source, User* dest) CXX11_OVERRIDE
{
std::string *account = accountname.get(dest);
if (account)
{
- ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick.c_str(), dest->nick.c_str(), account->c_str());
+ ServerInstance->SendWhoisLine(source, dest, 330, "%s %s :is logged in as", dest->nick.c_str(), account->c_str());
}
- if (dest->IsModeSet('r'))
+ if (dest->IsModeSet(m5))
{
/* user is registered */
- ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick.c_str(), dest->nick.c_str());
+ ServerInstance->SendWhoisLine(source, dest, 307, "%s :is a registered nick", dest->nick.c_str());
}
}
- void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
/* On nickchange, if they have +r, remove it */
- if (user->IsModeSet('r') && assign(user->nick) != oldnick)
- {
- std::vector<std::string> modechange;
- modechange.push_back(user->nick);
- modechange.push_back("-r");
- ServerInstance->SendMode(modechange, ServerInstance->FakeClient);
- }
+ if (user->IsModeSet(m5) && assign(user->nick) != oldnick)
+ m5.RemoveMode(user);
}
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
Channel* c = (Channel*)dest;
ModResult res = ServerInstance->OnCheckExemption(user,c,"regmoderated");
- if (c->IsModeSet('M') && !is_registered && res != MOD_RES_ALLOW)
+ if (c->IsModeSet(m2) && !is_registered && res != MOD_RES_ALLOW)
{
// user messaging a +M channel and is not registered
- user->WriteNumeric(477, user->nick+" "+c->name+" :You need to be identified to a registered account to message this channel");
+ user->WriteNumeric(477, c->name+" :You need to be identified to a registered account to message this channel");
return MOD_RES_DENY;
}
}
{
User* u = (User*)dest;
- if (u->IsModeSet('R') && !is_registered)
+ if (u->IsModeSet(m3) && !is_registered)
{
// user messaging a +R user and is not registered
- user->WriteNumeric(477, ""+ user->nick +" "+ u->nick +" :You need to be identified to a registered account to message this user");
+ user->WriteNumeric(477, u->nick +" :You need to be identified to a registered account to message this user");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
+ ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) CXX11_OVERRIDE
{
if (checking_ban)
return MOD_RES_PASSTHRU;
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
- {
- if (!IS_LOCAL(user))
- return MOD_RES_PASSTHRU;
-
std::string *account = accountname.get(user);
bool is_registered = account && !account->empty();
if (chan)
{
- if (chan->IsModeSet('R'))
+ if (chan->IsModeSet(m1))
{
if (!is_registered)
{
// joining a +R channel and not identified
- user->WriteNumeric(477, user->nick + " " + chan->name + " :You need to be identified to a registered account to join this channel");
+ user->WriteNumeric(477, chan->name + " :You need to be identified to a registered account to join this channel");
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- // Whenever the linking module receives metadata from another server and doesnt know what
- // to do with it (of course, hence the 'meta') it calls this method, and it is up to each
- // module in turn to figure out if this metadata key belongs to them, and what they want
- // to do with it.
- // In our case we're only sending a single string around, so we just construct a std::string.
- // Some modules will probably get much more complex and format more detailed structs and classes
- // in a textual way for sending over the link.
- void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata)
- {
- User* dest = dynamic_cast<User*>(target);
- // check if its our metadata key, and its associated with a user
- if (dest && (extname == "accountname"))
- {
- std::string *account = accountname.get(dest);
- if (account && !account->empty())
- {
- trim(*account);
-
- if (IS_LOCAL(dest))
- {
- const std::string* host = &dest->dhost;
- if (dest->registered != REG_ALL)
- {
- if (!ReadCGIIRCExt("cgiirc_webirc_hostname", dest, host))
- {
- ReadCGIIRCExt("cgiirc_webirc_ip", dest, host);
- }
- }
-
- dest->WriteNumeric(900, "%s %s!%s@%s %s :You are now logged in as %s",
- dest->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), host->c_str(), account->c_str(), account->c_str());
- }
-
- AccountEvent(this, dest, *account).Send();
- }
- else
- {
- AccountEvent(this, dest, "").Send();
- }
- }
- }
-
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
if (myclass->config->getBool("requireaccount") && !accountname.get(user))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for ircu-style services accounts, including chmode +R, etc.",VF_OPTCOMMON|VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides usermode +k to protect services from kicks, kills and mode changes. */
-
/** Handles user mode +k
*/
class ServProtectMode : public ModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(bm);
- Implementation eventlist[] = { I_OnWhois, I_OnKill, I_OnWhoisLine, I_OnRawMode, I_OnUserPreKick };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
-
- ~ModuleServProtectMode()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides usermode +k to protect services from kicks, kills, and mode changes.", VF_VENDOR);
}
- void OnWhois(User* src, User* dst)
+ void OnWhois(User* user, User* dest) CXX11_OVERRIDE
{
- if (dst->IsModeSet('k'))
+ if (dest->IsModeSet(bm))
{
- ServerInstance->SendWhoisLine(src, dst, 310, src->nick+" "+dst->nick+" :is an "+ServerInstance->Config->Network+" Service");
+ ServerInstance->SendWhoisLine(user, dest, 310, dest->nick+" :is a Network Service on "+ServerInstance->Config->Network);
}
}
- ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt)
+ ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
{
/* Check that the mode is not a server mode, it is being removed, the user making the change is local, there is a parameter,
* and the user making the change is not a uline
*/
- if (!adding && chan && IS_LOCAL(user) && !param.empty() && !ServerInstance->ULine(user->server))
+ if (!adding && chan && IS_LOCAL(user) && !param.empty())
{
/* Check if the parameter is a valid nick/uuid
*/
* This includes any prefix permission mode, even those registered in other modules, e.g. +qaohv. Using ::ModeString()
* here means that the number of modes is restricted to only modes the user has, limiting it to as short a loop as possible.
*/
- if (u->IsModeSet('k') && memb && memb->modes.find(mode) != std::string::npos)
+ if (u->IsModeSet(bm) && memb && memb->hasMode(mh->GetModeChar()))
{
/* BZZZT, Denied! */
- user->WriteNumeric(482, "%s %s :You are not permitted to remove privileges from %s services", user->nick.c_str(), chan->name.c_str(), ServerInstance->Config->Network.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You are not permitted to remove privileges from %s services", chan->name.c_str(), ServerInstance->Config->Network.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnKill(User* src, User* dst, const std::string &reason)
+ ModResult OnKill(User* src, User* dst, const std::string &reason) CXX11_OVERRIDE
{
if (src == NULL)
return MOD_RES_PASSTHRU;
- if (dst->IsModeSet('k'))
+ if (dst->IsModeSet(bm))
{
- src->WriteNumeric(485, "%s :You are not permitted to kill %s services!", src->nick.c_str(), ServerInstance->Config->Network.c_str());
+ src->WriteNumeric(485, ":You are not permitted to kill %s services!", ServerInstance->Config->Network.c_str());
ServerInstance->SNO->WriteGlobalSno('a', src->nick+" tried to kill service "+dst->nick+" ("+reason+")");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason)
+ ModResult OnUserPreKick(User *src, Membership* memb, const std::string &reason) CXX11_OVERRIDE
{
- if (memb->user->IsModeSet('k'))
+ if (memb->user->IsModeSet(bm))
{
- src->WriteNumeric(484, "%s %s :You are not permitted to kick services",
- src->nick.c_str(), memb->chan->name.c_str());
+ src->WriteNumeric(ERR_RESTRICTED, "%s :You are not permitted to kick services",
+ memb->chan->name.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ModResult OnWhoisLine(User* src, User* dst, int &numeric, std::string &text)
+ ModResult OnWhoisLine(User* src, User* dst, int &numeric, std::string &text) CXX11_OVERRIDE
{
- return ((src != dst) && (numeric == 319) && dst->IsModeSet('k')) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ return ((src != dst) && (numeric == 319) && dst->IsModeSet(bm)) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
}
};
-
MODULE_INIT(ModuleServProtectMode)
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETHOST command */
-
/** Handle /SETHOST
*/
class CommandSethost : public Command
{
- private:
char* hostmap;
+
public:
CommandSethost(Module* Creator, char* hmap) : Command(Creator,"SETHOST", 1), hostmap(hmap)
{
allow_empty_last_param = false;
flags_needed = 'o'; syntax = "<new-hostname>";
- TRANSLATE2(TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
if (!hostmap[(const unsigned char)*x])
{
- user->WriteServ("NOTICE "+user->nick+" :*** SETHOST: Invalid characters in hostname");
+ user->WriteNotice("*** SETHOST: Invalid characters in hostname");
return CMD_FAILURE;
}
}
- if (len > 64)
+ if (len > ServerInstance->Config->Limits.MaxHost)
{
- user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick.c_str());
+ user->WriteNotice("*** SETHOST: Host too long");
return CMD_FAILURE;
}
- if (user->ChangeDisplayedHost(parameters[0].c_str()))
+ if (user->ChangeDisplayedHost(parameters[0]))
{
ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SETHOST to change their displayed host to "+user->dhost);
return CMD_SUCCESS;
{
CommandSethost cmd;
char hostmap[256];
+
public:
ModuleSetHost()
: cmd(this, hostmap)
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
std::string hmap = ServerInstance->Config->ConfValue("hostname")->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789");
hostmap[(unsigned char)*n] = 1;
}
- virtual ~ModuleSetHost()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETHOST command", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSetHost)
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETIDENT command */
-
/** Handle /SETIDENT
*/
class CommandSetident : public Command
{
allow_empty_last_param = false;
flags_needed = 'o'; syntax = "<new-ident>";
- TRANSLATE2(TR_TEXT, TR_END);
}
CmdResult Handle(const std::vector<std::string>& parameters, User *user)
{
if (parameters[0].size() > ServerInstance->Config->Limits.IdentMax)
{
- user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick.c_str());
+ user->WriteNotice("*** SETIDENT: Ident is too long");
return CMD_FAILURE;
}
- if (!ServerInstance->IsIdent(parameters[0].c_str()))
+ if (!ServerInstance->IsIdent(parameters[0]))
{
- user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick.c_str());
+ user->WriteNotice("*** SETIDENT: Invalid characters in ident");
return CMD_FAILURE;
}
- user->ChangeIdent(parameters[0].c_str());
+ user->ChangeIdent(parameters[0]);
ServerInstance->SNO->WriteGlobalSno('a', "%s used SETIDENT to change their ident to '%s'", user->nick.c_str(), user->ident.c_str());
return CMD_SUCCESS;
}
};
-
class ModuleSetIdent : public Module
{
CommandSetident cmd;
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetIdent()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETIDENT command", VF_VENDOR);
}
-
};
-
MODULE_INIT(ModuleSetIdent)
#include "inspircd.h"
-/* $ModDesc: Allows opers to set their idle time */
-
/** Handle /SETIDLE
*/
-class CommandSetidle : public Command
+class CommandSetidle : public SplitCommand
{
public:
- CommandSetidle(Module* Creator) : Command(Creator,"SETIDLE", 1)
+ CommandSetidle(Module* Creator) : SplitCommand(Creator,"SETIDLE", 1)
{
flags_needed = 'o'; syntax = "<duration>";
- TRANSLATE2(TR_TEXT, TR_END);
}
- CmdResult Handle (const std::vector<std::string>& parameters, User *user)
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
- time_t idle = ServerInstance->Duration(parameters[0]);
+ int idle = InspIRCd::Duration(parameters[0]);
if (idle < 1)
{
- user->WriteNumeric(948, "%s :Invalid idle time.",user->nick.c_str());
+ user->WriteNumeric(948, ":Invalid idle time.");
return CMD_FAILURE;
}
user->idle_lastmsg = (ServerInstance->Time() - idle);
if (user->signon > user->idle_lastmsg)
user->signon = user->idle_lastmsg;
ServerInstance->SNO->WriteToSnoMask('a', user->nick+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds");
- user->WriteNumeric(944, "%s :Idle time set.",user->nick.c_str());
+ user->WriteNumeric(944, ":Idle time set.");
return CMD_SUCCESS;
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetIdle()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows opers to set their idle time", VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides support for the SETNAME command */
-
class CommandSetname : public Command
{
allow_empty_last_param = false;
syntax = "<new-gecos>";
- TRANSLATE2(TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
{
if (parameters[0].size() > ServerInstance->Config->Limits.MaxGecos)
{
- user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick.c_str());
+ user->WriteNotice("*** SETNAME: GECOS too long");
return CMD_FAILURE;
}
- if (user->ChangeName(parameters[0].c_str()))
+ if (user->ChangeName(parameters[0]))
{
ServerInstance->SNO->WriteGlobalSno('a', "%s used SETNAME to change their GECOS to '%s'", user->nick.c_str(), parameters[0].c_str());
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleSetName()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the SETNAME command", VF_VENDOR);
}
* SUCH DAMAGE.
*/
-/* $ModDesc: Allows for SHA-256 encrypted oper passwords */
-
#include "inspircd.h"
-#ifdef HAS_STDINT
-#include <stdint.h>
-#endif
-#include "hash.h"
-
-#ifndef HAS_STDINT
-typedef unsigned int uint32_t;
-#endif
+#include "modules/hash.h"
#define SHA256_DIGEST_SIZE (256 / 8)
#define SHA256_BLOCK_SIZE (512 / 8)
}
public:
- std::string sum(const std::string& data)
+ std::string GenerateRaw(const std::string& data)
{
unsigned char bytes[SHA256_DIGEST_SIZE];
SHA256(data.data(), bytes, data.length());
return std::string((char*)bytes, SHA256_DIGEST_SIZE);
}
- std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
- {
- return "";
- }
-
- HashSHA256(Module* parent) : HashProvider(parent, "hash/sha256", 32, 64) {}
+ HashSHA256(Module* parent) : HashProvider(parent, "sha256", 32, 64) {}
};
class ModuleSHA256 : public Module
public:
ModuleSHA256() : sha(this)
{
- ServerInstance->Modules->AddService(sha);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements SHA-256 hashing", VF_VENDOR);
}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+class CommandShowFile : public Command
+{
+ enum Method
+ {
+ SF_MSG,
+ SF_NOTICE,
+ SF_NUMERIC
+ };
+
+ std::string introtext;
+ std::string endtext;
+ unsigned int intronumeric;
+ unsigned int textnumeric;
+ unsigned int endnumeric;
+ file_cache contents;
+ Method method;
+
+ public:
+ CommandShowFile(Module* parent, const std::string& cmdname)
+ : Command(parent, cmdname)
+ {
+ }
+
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user) CXX11_OVERRIDE
+ {
+ const std::string& sn = ServerInstance->Config->ServerName;
+ if (method == SF_NUMERIC)
+ {
+ if (!introtext.empty())
+ user->SendText(":%s %03d %s :%s %s", sn.c_str(), intronumeric, user->nick.c_str(), sn.c_str(), introtext.c_str());
+
+ for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+ user->SendText(":%s %03d %s :- %s", sn.c_str(), textnumeric, user->nick.c_str(), i->c_str());
+
+ user->SendText(":%s %03d %s :%s", sn.c_str(), endnumeric, user->nick.c_str(), endtext.c_str());
+ }
+ else
+ {
+ const char* msgcmd = (method == SF_MSG ? "PRIVMSG" : "NOTICE");
+ std::string header = InspIRCd::Format(":%s %s %s :", sn.c_str(), msgcmd, user->nick.c_str());
+ for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
+ user->SendText(header + *i);
+ }
+ return CMD_SUCCESS;
+ }
+
+ void UpdateSettings(ConfigTag* tag, const std::vector<std::string>& filecontents)
+ {
+ introtext = tag->getString("introtext", "Showing " + name);
+ endtext = tag->getString("endtext", "End of " + name);
+ intronumeric = tag->getInt("intronumeric", RPL_RULESTART, 0, 999);
+ textnumeric = tag->getInt("numeric", RPL_RULES, 0, 999);
+ endnumeric = tag->getInt("endnumeric", RPL_RULESEND, 0, 999);
+ std::string smethod = tag->getString("method");
+
+ method = SF_NUMERIC;
+ if (smethod == "msg")
+ method = SF_MSG;
+ else if (smethod == "notice")
+ method = SF_NOTICE;
+
+ contents = filecontents;
+ if (tag->getBool("colors"))
+ InspIRCd::ProcessColors(contents);
+ }
+};
+
+class ModuleShowFile : public Module
+{
+ std::vector<CommandShowFile*> cmds;
+
+ void ReadTag(ConfigTag* tag, std::vector<CommandShowFile*>& newcmds)
+ {
+ std::string cmdname = tag->getString("name");
+ if (cmdname.empty())
+ throw ModuleException("Empty value for 'name'");
+
+ std::transform(cmdname.begin(), cmdname.end(), cmdname.begin(), ::toupper);
+
+ const std::string file = tag->getString("file", cmdname);
+ if (file.empty())
+ throw ModuleException("Empty value for 'file'");
+ FileReader reader(file);
+
+ CommandShowFile* sfcmd;
+ Command* handler = ServerInstance->Parser.GetHandler(cmdname);
+ if (handler)
+ {
+ // Command exists, check if it is ours
+ if (handler->creator != this)
+ throw ModuleException("Command " + cmdname + " already exists");
+
+ // This is our command, make sure we don't have the same entry twice
+ sfcmd = static_cast<CommandShowFile*>(handler);
+ if (std::find(newcmds.begin(), newcmds.end(), sfcmd) != newcmds.end())
+ throw ModuleException("Command " + cmdname + " is already used in a <showfile> tag");
+ }
+ else
+ {
+ // Command doesn't exist, create it
+ sfcmd = new CommandShowFile(this, cmdname);
+ ServerInstance->Modules->AddService(*sfcmd);
+ }
+
+ sfcmd->UpdateSettings(tag, reader.GetVector());
+ newcmds.push_back(sfcmd);
+ }
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ std::vector<CommandShowFile*> newcmds;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("showfile");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ try
+ {
+ ReadTag(tag, newcmds);
+ }
+ catch (CoreException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error: " + ex.GetReason() + " at " + tag->getTagLocation());
+ }
+ }
+
+ // Remove all commands that were removed from the config
+ std::vector<CommandShowFile*> removed(cmds.size());
+ std::sort(newcmds.begin(), newcmds.end());
+ std::set_difference(cmds.begin(), cmds.end(), newcmds.begin(), newcmds.end(), removed.begin());
+
+ stdalgo::delete_all(removed);
+ cmds.swap(newcmds);
+ }
+
+ ~ModuleShowFile()
+ {
+ stdalgo::delete_all(cmds);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for showing text files to users", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleShowFile)
#include "inspircd.h"
-/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
-
/** Handle user mode +W
*/
class SeeWhois : public SimpleUserModeHandler
{
public:
- SeeWhois(Module* Creator, bool IsOpersOnly) : SimpleUserModeHandler(Creator, "showwhois", 'W')
+ SeeWhois(Module* Creator)
+ : SimpleUserModeHandler(Creator, "showwhois", 'W')
{
- oper = IsOpersOnly;
+ }
+
+ void SetOperOnly(bool operonly)
+ {
+ oper = operonly;
}
};
void HandleFast(User* dest, User* src)
{
- dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you",
- dest->nick.c_str(), src->nick.c_str(), src->ident.c_str(),
- dest->HasPrivPermission("users/auspex") ? src->host.c_str() : src->dhost.c_str());
+ dest->WriteNotice("*** " + src->nick + " (" + src->ident + "@" +
+ (dest->HasPrivPermission("users/auspex") ? src->host : src->dhost) +
+ ") did a /whois on you");
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
class ModuleShowwhois : public Module
{
bool ShowWhoisFromOpers;
- SeeWhois* sw;
+ SeeWhois sw;
WhoisNoticeCmd cmd;
public:
ModuleShowwhois()
- : sw(NULL), cmd(this)
+ : sw(this), cmd(this)
{
}
- void init()
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("showwhois");
- bool OpersOnly = tag->getBool("opersonly", true);
+ sw.SetOperOnly(tag->getBool("opersonly", true));
ShowWhoisFromOpers = tag->getBool("showfromopers", true);
-
- sw = new SeeWhois(this, OpersOnly);
- ServerInstance->Modules->AddService(*sw);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnWhois };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ~ModuleShowwhois()
- {
- delete sw;
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows opers to set +W to see when a user uses WHOIS on them",VF_OPTCOMMON|VF_VENDOR);
}
- void OnWhois(User* source, User* dest)
+ void OnWhois(User* source, User* dest) CXX11_OVERRIDE
{
- if (!dest->IsModeSet('W') || source == dest)
+ if (!dest->IsModeSet(sw) || source == dest)
return;
- if (!ShowWhoisFromOpers && IS_OPER(source))
+ if (!ShowWhoisFromOpers && source->IsOper())
return;
if (IS_LOCAL(dest))
else
{
std::vector<std::string> params;
- params.push_back(dest->server);
- params.push_back("WHOISNOTICE");
params.push_back(dest->uuid);
params.push_back(source->uuid);
- ServerInstance->PI->SendEncapsulatedData(params);
+ ServerInstance->PI->SendEncapsulatedData(dest->server->GetName(), cmd.name, params);
}
}
-
};
MODULE_INIT(ModuleShowwhois)
#include "inspircd.h"
#include "xline.h"
-/* $ModDesc: Provides the /SHUN command, which stops a user from executing all except configured commands. */
-
class Shun : public XLine
{
public:
{
}
- ~Shun()
- {
- }
-
bool Matches(User *u)
{
// E: overrides shun
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext))
return false;
}
- void DisplayExpiry()
- {
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired shun %s (set by %s %ld seconds ago)",
- this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
- }
-
- const char* Displayable()
+ const std::string& Displayable()
{
- return matchtext.c_str();
+ return matchtext;
}
};
/* 'time' is a human-readable timestring, like 2d3h2s. */
std::string target = parameters[0];
-
+
User *find = ServerInstance->FindNick(target);
if ((find) && (find->registered == REG_ALL))
target = std::string("*!*@") + find->GetIPString();
}
else
{
- user->WriteServ("NOTICE %s :*** Shun %s not found in list, try /stats H.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** Shun " + target + " not found in list, try /stats H.");
return CMD_FAILURE;
}
}
else
{
// Adding - XXX todo make this respect <insane> tag perhaps..
- long duration;
+ unsigned long duration;
std::string expr;
if (parameters.size() > 2)
{
- duration = ServerInstance->Duration(parameters[1]);
+ duration = InspIRCd::Duration(parameters[1]);
expr = parameters[2];
}
else
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteToSnoMask('x', "%s added timed SHUN for %s to expire on %s: %s",
user->nick.c_str(), target.c_str(), timestr.c_str(), expr.c_str());
}
else
{
delete r;
- user->WriteServ("NOTICE %s :*** Shun for %s already exists", user->nick.c_str(), target.c_str());
+ user->WriteNotice("*** Shun for " + target + " already exists");
return CMD_FAILURE;
}
}
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&f);
- ServerInstance->Modules->AddService(cmd);
-
- Implementation eventlist[] = { I_OnStats, I_OnPreCommand, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- virtual ~ModuleShun()
+ ~ModuleShun()
{
ServerInstance->XLines->DelAll("SHUN");
ServerInstance->XLines->UnregisterFactory(&f);
ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, &alias);
}
- virtual ModResult OnStats(char symbol, User* user, string_list& out)
+ ModResult OnStats(char symbol, User* user, string_list& out) CXX11_OVERRIDE
{
if (symbol != 'H')
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("shun");
std::string cmds = tag->getString("enabledcommands");
affectopers = tag->getBool("affectopers", false);
}
- virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
if (validated)
return MOD_RES_PASSTHRU;
return MOD_RES_PASSTHRU;
}
- if (!affectopers && IS_OPER(user))
+ if (!affectopers && user->IsOper())
{
/* Don't do anything if the user is an operator and affectopers isn't set */
return MOD_RES_PASSTHRU;
if (i == ShunEnabledCommands.end())
{
if (NotifyOfShun)
- user->WriteServ("NOTICE %s :*** Command %s not processed, as you have been blocked from issuing commands (SHUN)", user->nick.c_str(), command.c_str());
+ user->WriteNotice("*** Command " + command + " not processed, as you have been blocked from issuing commands (SHUN)");
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the /SHUN command, which stops a user from executing all except configured commands.",VF_VENDOR|VF_COMMON);
}
};
MODULE_INIT(ModuleShun)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for the /SILENCE command */
-
/* Improved drop-in replacement for the /SILENCE command
* syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
*
CommandSVSSilence(Module* Creator) : Command(Creator,"SVSSILENCE", 2)
{
syntax = "<target> {[+|-]<mask> <p|c|i|n|t|a|x>}";
- TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
+ TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
* style command so services can modify lots of entries at once.
* leaving it backwards compatible for now as it's late. -- w
*/
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
return CMD_FAILURE;
User *u = ServerInstance->FindNick(parameters[0]);
if (IS_LOCAL(u))
{
- ServerInstance->Parser->CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
+ ServerInstance->Parser.CallHandler("SILENCE", std::vector<std::string>(parameters.begin() + 1, parameters.end()), u);
}
return CMD_SUCCESS;
{
allow_empty_last_param = false;
syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
- TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string>& parameters, User *user)
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
std::string decomppattern = DecompPattern(c->second);
- user->WriteNumeric(271, "%s %s %s %s",user->nick.c_str(), user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
+ user->WriteNumeric(271, "%s %s %s", user->nick.c_str(),c->first.c_str(), decomppattern.c_str());
}
}
- user->WriteNumeric(272, "%s :End of Silence List",user->nick.c_str());
+ user->WriteNumeric(272, ":End of Silence List");
return CMD_SUCCESS;
}
if (pattern == 0)
{
- user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str());
+ user->WriteNotice("Bad SILENCE pattern");
return CMD_INVALID;
}
if (listitem == mask && i->second == pattern)
{
sl->erase(i);
- user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(950, "%s :Removed %s %s from silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
if (!sl->size())
{
ext.unset(user);
}
}
}
- user->WriteNumeric(952, "%s %s :%s %s does not exist on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(952, "%s :%s %s does not exist on your silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
}
else if (action == '+')
{
}
if (sl->size() > maxsilence)
{
- user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick.c_str(), user->nick.c_str());
+ user->WriteNumeric(952, "%s :Your silence list is full",user->nick.c_str());
return CMD_FAILURE;
}
irc::string listitem = n->first.c_str();
if (listitem == mask && n->second == pattern)
{
- user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(952, "%s :%s %s is already on your silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
return CMD_FAILURE;
}
}
{
sl->push_back(silenceset(mask,pattern));
}
- user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str());
+ user->WriteNumeric(951, "%s :Added %s %s to silence list", user->nick.c_str(), mask.c_str(), decomppattern.c_str());
return CMD_SUCCESS;
}
}
{
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmdsilence);
- ServerInstance->Modules->AddService(cmdsvssilence);
- ServerInstance->Modules->AddService(cmdsilence.ext);
-
- Implementation eventlist[] = { I_OnRehash, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnUserPreInvite };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
maxsilence = ServerInstance->Config->ConfValue("silence")->getInt("maxentries", 32);
if (!maxsilence)
maxsilence = 32;
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- // we don't really have a limit...
- output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
+ tokens["ESILENCE"];
+ tokens["SILENCE"] = ConvToStr(maxsilence);
}
void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
{
int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
- const UserMembList *ulist = chan->GetUsers();
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
{
}
}
- ModResult PreText(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (target_type == TYPE_USER && IS_LOCAL(((User*)dest)))
{
- return MatchPattern((User*)dest, user, silence_type);
+ return MatchPattern((User*)dest, user, ((msgtype == MSG_PRIVMSG) ? SILENCE_PRIVATE : SILENCE_NOTICE));
}
else if (target_type == TYPE_CHANNEL)
{
Channel* chan = (Channel*)dest;
- if (chan)
- {
- this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list, "");
- }
+ this->OnBuildExemptList(msgtype, chan, user, status, exempt_list, "");
}
return MOD_RES_PASSTHRU;
}
- ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
- }
-
- ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
- }
-
- ModResult OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout)
+ ModResult OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout) CXX11_OVERRIDE
{
return MatchPattern(dest, source, SILENCE_INVITE);
}
ModResult MatchPattern(User* dest, User* source, int pattern)
{
- /* Server source */
- if (!source || !dest)
- return MOD_RES_ALLOW;
-
silencelist* sl = cmdsilence.ext.get(dest);
if (sl)
{
return MOD_RES_PASSTHRU;
}
- ~ModuleSilence()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the /SILENCE command", VF_OPTCOMMON | VF_VENDOR);
}
#include "inspircd.h"
#include "xline.h"
-#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
+#include "commands.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::AddLine(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandAddLine::Handle(User* usr, std::vector<std::string>& params)
{
- if (params.size() < 6)
- {
- std::string servername = MyRoot->GetName();
- ServerInstance->SNO->WriteToSnoMask('d', "%s sent me a malformed ADDLINE", servername.c_str());
- return true;
- }
-
XLineFactory* xlf = ServerInstance->XLines->GetFactory(params[0]);
-
- std::string setter = "<unknown>";
- User* usr = ServerInstance->FindNick(prefix);
- if (usr)
- setter = usr->nick;
- else
- {
- TreeServer* t = Utils->FindServer(prefix);
- if (t)
- setter = t->GetName();
- }
+ const std::string& setter = usr->nick;
if (!xlf)
{
ServerInstance->SNO->WriteToSnoMask('d',"%s sent me an unknown ADDLINE type (%s).",setter.c_str(),params[0].c_str());
- return true;
+ return CMD_FAILURE;
}
- long created = atol(params[3].c_str()), expires = atol(params[4].c_str());
- if (created < 0 || expires < 0)
- return true;
-
XLine* xl = NULL;
try
{
- xl = xlf->Generate(ServerInstance->Time(), expires, params[2], params[5], params[1]);
+ xl = xlf->Generate(ServerInstance->Time(), ConvToInt(params[4]), params[2], params[5], params[1]);
}
catch (ModuleException &e)
{
- ServerInstance->SNO->WriteToSnoMask('d',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason());
- return true;
+ ServerInstance->SNO->WriteToSnoMask('d',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason().c_str());
+ return CMD_FAILURE;
}
- xl->SetCreateTime(created);
+ xl->SetCreateTime(ConvToInt(params[3]));
if (ServerInstance->XLines->AddLine(xl, NULL))
{
if (xl->duration)
{
- std::string timestr = ServerInstance->TimeString(xl->expiry);
+ std::string timestr = InspIRCd::TimeString(xl->expiry);
ServerInstance->SNO->WriteToSnoMask('X',"%s added %s%s on %s to expire on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
params[1].c_str(), timestr.c_str(), params[5].c_str());
}
ServerInstance->SNO->WriteToSnoMask('X',"%s added permanent %s%s on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "",
params[1].c_str(),params[5].c_str());
}
- params[5] = ":" + params[5];
- User* u = ServerInstance->FindNick(prefix);
- Utils->DoOneToAllButSender(prefix, "ADDLINE", params, u ? u->server : prefix);
- TreeServer *remoteserver = Utils->FindServer(u ? u->server : prefix);
+ TreeServer* remoteserver = TreeServer::Get(usr);
- if (!remoteserver->bursting)
+ if (!remoteserver->IsBursting())
{
ServerInstance->XLines->ApplyLines();
}
+ return CMD_SUCCESS;
}
else
+ {
delete xl;
-
- return true;
+ return CMD_FAILURE;
+ }
}
+CommandAddLine::Builder::Builder(XLine* xline, User* user)
+ : CmdBuilder(user, "ADDLINE")
+{
+ push(xline->type);
+ push(xline->Displayable());
+ push(xline->source);
+ push_int(xline->set_time);
+ push_int(xline->duration);
+ push_last(xline->reason);
+}
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
-bool TreeSocket::Away(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandAway::HandleRemote(RemoteUser* u, std::vector<std::string>& params)
{
- User* u = ServerInstance->FindNick(prefix);
- if ((!u) || (IS_SERVER(u)))
- return true;
if (params.size())
{
- FOREACH_MOD(I_OnSetAway, OnSetAway(u, params[params.size() - 1]));
+ FOREACH_MOD(OnSetAway, (u, params.back()));
if (params.size() > 1)
- u->awaytime = atoi(params[0].c_str());
+ u->awaytime = ConvToInt(params[0]);
else
u->awaytime = ServerInstance->Time();
- u->awaymsg = params[params.size() - 1];
-
- params[params.size() - 1] = ":" + params[params.size() - 1];
+ u->awaymsg = params.back();
}
else
{
- FOREACH_MOD(I_OnSetAway, OnSetAway(u, ""));
+ FOREACH_MOD(OnSetAway, (u, ""));
u->awaymsg.clear();
}
- Utils->DoOneToAllButSender(prefix,"AWAY",params,u->server);
- return true;
+ return CMD_SUCCESS;
+}
+
+CommandAway::Builder::Builder(User* user)
+ : CmdBuilder(user, "AWAY")
+{
+ push_int(user->awaytime).push_last(user->awaymsg);
+}
+
+CommandAway::Builder::Builder(User* user, const std::string& msg)
+ : CmdBuilder(user, "AWAY")
+{
+ if (!msg.empty())
+ push_int(ServerInstance->Time()).push_last(msg);
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-
-#include "cachetimer.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/cachetimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
-
-CacheRefreshTimer::CacheRefreshTimer(SpanningTreeUtilities *Util) : Timer(3600, ServerInstance->Time(), true), Utils(Util)
-{
-}
-
-void CacheRefreshTimer::Tick(time_t TIME)
-{
- Utils->RefreshIPCache();
-}
-
*/
-#ifndef M_SPANNINGTREE_CACHETIMER_H
-#define M_SPANNINGTREE_CACHETIMER_H
-
-#include "timer.h"
-
-class ModuleSpanningTree;
-class SpanningTreeUtilities;
+#pragma once
/** Create a timer which recurs every second, we inherit from Timer.
* Timer is only one-shot however, so at the end of each Tick() we simply
*/
class CacheRefreshTimer : public Timer
{
- private:
- SpanningTreeUtilities *Utils;
public:
- CacheRefreshTimer(SpanningTreeUtilities* Util);
- virtual void Tick(time_t TIME);
+ CacheRefreshTimer();
+ bool Tick(time_t TIME);
};
-
-#endif
#include "inspircd.h"
-#include "xline.h"
-#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
#include "link.h"
std::string TreeSocket::MyModules(int filter)
{
- std::vector<std::string> modlist = ServerInstance->Modules->GetAllModuleNames(filter);
-
- if (filter == VF_COMMON && proto_version != ProtocolVersion)
- CompatAddModules(modlist);
+ const ModuleManager::ModuleMap& modlist = ServerInstance->Modules->GetModules();
std::string capabilities;
- sort(modlist.begin(),modlist.end());
- for (std::vector<std::string>::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
+ for (ModuleManager::ModuleMap::const_iterator i = modlist.begin(); i != modlist.end(); ++i)
{
+ Version v = i->second->GetVersion();
+ if (!(v.Flags & filter))
+ continue;
+
if (i != modlist.begin())
- capabilities.push_back(proto_version > 1201 ? ' ' : ',');
- capabilities.append(*i);
- Module* m = ServerInstance->Modules->Find(*i);
- if (m && proto_version > 1201)
+ capabilities.push_back(' ');
+ capabilities.append(i->first);
+ if (!v.link_data.empty())
{
- Version v = m->GetVersion();
- if (!v.link_data.empty())
- {
- capabilities.push_back('=');
- capabilities.append(v.link_data);
- }
+ capabilities.push_back('=');
+ capabilities.append(v.link_data);
}
}
return capabilities;
{
std::string mdesc = mh->name;
mdesc.push_back('=');
- if (mh->GetPrefix())
- mdesc.push_back(mh->GetPrefix());
- if (mh->GetModeChar())
- mdesc.push_back(mh->GetModeChar());
+ PrefixMode* pm = mh->IsPrefixMode();
+ if (pm)
+ {
+ if (pm->GetPrefix())
+ mdesc.push_back(pm->GetPrefix());
+ }
+ mdesc.push_back(mh->GetModeChar());
modes.push_back(mdesc);
}
}
- sort(modes.begin(), modes.end());
- irc::stringjoiner line(" ", modes, 0, modes.size() - 1);
- return line.GetJoined();
+ std::sort(modes.begin(), modes.end());
+ return irc::stringjoiner(modes);
}
void TreeSocket::SendCapabilities(int phase)
if (phase < 2)
return;
- char sep = proto_version > 1201 ? ' ' : ',';
+ const char sep = ' ';
irc::sepstream modulelist(MyModules(VF_COMMON), sep);
irc::sepstream optmodulelist(MyModules(VF_OPTCOMMON), sep);
/* Send module names, split at 509 length */
std::string extra;
/* Do we have sha256 available? If so, we send a challenge */
- if (Utils->ChallengeResponse && (ServerInstance->Modules->Find("m_sha256.so")))
+ if (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256"))
{
SetOurChallenge(ServerInstance->GenRandomStr(20));
extra = " CHALLENGE=" + this->GetOurChallenge();
}
- if (proto_version < 1202)
- extra += ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL) ? " HALFOP=1" : " HALFOP=0";
+
+ // 2.0 needs this key
+ if (proto_version == 1202)
+ extra.append(" PROTOCOL="+ConvToStr(ProtocolVersion));
this->WriteLine("CAPAB CAPABILITIES " /* Preprocessor does this one. */
":NICKMAX="+ConvToStr(ServerInstance->Config->Limits.NickMax)+
" MAXKICK="+ConvToStr(ServerInstance->Config->Limits.MaxKick)+
" MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxGecos)+
" MAXAWAY="+ConvToStr(ServerInstance->Config->Limits.MaxAway)+
- " IP6SUPPORT=1"+
- " PROTOCOL="+ConvToStr(ProtocolVersion)+extra+
+ " MAXHOST="+ConvToStr(ServerInstance->Config->Limits.MaxHost)+
+ extra+
" PREFIX="+ServerInstance->Modes->BuildPrefixes()+
- " CHANMODES="+ServerInstance->Modes->GiveModeList(MASK_CHANNEL)+
- " USERMODES="+ServerInstance->Modes->GiveModeList(MASK_USER)+
+ " CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL)+
+ " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER)+
// XXX: Advertise the presence or absence of m_globops in CAPAB CAPABILITIES.
// Services want to know about it, and since m_globops was not marked as VF_(OPT)COMMON
// in 2.0, we advertise it here to not break linking to previous versions.
// Protocol version 1201 (1.2) does not have this issue because we advertise m_globops
// to 1201 protocol servers irrespectively of its module flags.
- (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")+
- " SVSPART=1");
+ (ServerInstance->Modules->Find("m_globops.so") != NULL ? " GLOBOPS=1" : " GLOBOPS=0")
+ );
this->WriteLine("CAPAB END");
}
capab->OptModuleList.clear();
capab->CapKeys.clear();
if (params.size() > 1)
- proto_version = atoi(params[1].c_str());
+ proto_version = ConvToInt(params[1]);
+
+ if (proto_version < MinCompatProtocol)
+ {
+ SendError("CAPAB negotiation failed: Server is using protocol version " + (proto_version ? ConvToStr(proto_version) : "1201 or older")
+ + " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
+ + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")"));
+ return false;
+ }
+
+ // Special case, may be removed in the future
+ if (proto_version == 1203 || proto_version == 1204)
+ {
+ SendError("CAPAB negotiation failed: InspIRCd 2.1 beta is not supported");
+ return false;
+ }
+
SendCapabilities(2);
}
else if (params[0] == "END")
if ((this->capab->ModuleList != this->MyModules(VF_COMMON)) && (this->capab->ModuleList.length()))
{
std::string diffIneed, diffUneed;
- ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), proto_version > 1201 ? ' ' : ',', diffIneed, diffUneed);
+ ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), ' ', diffIneed, diffUneed);
if (diffIneed.length() || diffUneed.length())
{
reason = "Modules incorrectly matched on these servers.";
}
}
- if (this->capab->CapKeys.find("PROTOCOL") == this->capab->CapKeys.end())
- {
- reason = "Protocol version not specified";
- }
- else
- {
- proto_version = atoi(capab->CapKeys.find("PROTOCOL")->second.c_str());
- if (proto_version < MinCompatProtocol)
- {
- reason = "Server is using protocol version " + ConvToStr(proto_version) +
- " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
- + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")");
- }
- }
-
if(this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end() && this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes())
reason = "One or more of the prefixes on the remote server are invalid on this server.";
}
else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
{
- if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL))
+ if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL))
reason = "One or more of the channel modes on the remote server are invalid on this server.";
}
}
else if (this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end())
{
- if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MASK_USER))
+ if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MODETYPE_USER))
reason = "One or more of the user modes on the remote server are invalid on this server.";
}
/* Challenge response, store their challenge for our password */
std::map<std::string,std::string>::iterator n = this->capab->CapKeys.find("CHALLENGE");
- if (Utils->ChallengeResponse && (n != this->capab->CapKeys.end()) && (ServerInstance->Modules->Find("m_sha256.so")))
+ if ((n != this->capab->CapKeys.end()) && (ServerInstance->Modules->FindService(SERVICE_DATA, "hash/sha256")))
{
/* Challenge-response is on now */
this->SetTheirChallenge(n->second);
}
else
{
- /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
+ // They didn't specify a challenge or we don't have sha256, we use plaintext
if (this->LinkState == CONNECTING)
{
this->SendCapabilities(2);
}
else
{
- capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
+ capab->ModuleList.push_back(' ');
capab->ModuleList.append(params[1]);
}
}
}
return true;
}
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "utils.h"
+
+class TreeServer;
+
+class CmdBuilder
+{
+ protected:
+ std::string content;
+
+ public:
+ CmdBuilder(const char* cmd)
+ : content(1, ':')
+ {
+ content.append(ServerInstance->Config->GetSID());
+ push(cmd);
+ }
+
+ CmdBuilder(const std::string& src, const char* cmd)
+ : content(1, ':')
+ {
+ content.append(src);
+ push(cmd);
+ }
+
+ CmdBuilder(User* src, const char* cmd)
+ : content(1, ':')
+ {
+ content.append(src->uuid);
+ push(cmd);
+ }
+
+ CmdBuilder& push_raw(const std::string& s)
+ {
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push_raw(const char* s)
+ {
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push_raw(char c)
+ {
+ content.push_back(c);
+ return *this;
+ }
+
+ template <typename T>
+ CmdBuilder& push_raw_int(T i)
+ {
+ content.append(ConvToStr(i));
+ return *this;
+ }
+
+ CmdBuilder& push(const std::string& s)
+ {
+ content.push_back(' ');
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push(const char* s)
+ {
+ content.push_back(' ');
+ content.append(s);
+ return *this;
+ }
+
+ CmdBuilder& push(char c)
+ {
+ content.push_back(' ');
+ content.push_back(c);
+ return *this;
+ }
+
+ template <typename T>
+ CmdBuilder& push_int(T i)
+ {
+ content.push_back(' ');
+ content.append(ConvToStr(i));
+ return *this;
+ }
+
+ CmdBuilder& push_last(const std::string& s)
+ {
+ content.push_back(' ');
+ content.push_back(':');
+ content.append(s);
+ return *this;
+ }
+
+ template<typename T>
+ CmdBuilder& insert(const T& cont)
+ {
+ for (typename T::const_iterator i = cont.begin(); i != cont.end(); ++i)
+ push(*i);
+ return *this;
+ }
+
+ void push_back(const std::string& s) { push(s); }
+
+ const std::string& str() const { return content; }
+ operator const std::string&() const { return str(); }
+
+ void Broadcast() const
+ {
+ Utils->DoOneToMany(*this);
+ }
+
+ void Forward(TreeServer* omit) const
+ {
+ Utils->DoOneToAllButSender(*this, omit);
+ }
+
+ bool Unicast(const std::string& target) const
+ {
+ return Utils->DoOneToOne(*this, target);
+ }
+
+ void Unicast(User* target) const
+ {
+ Utils->DoOneToOne(*this, target->server);
+ }
+};
*/
-#ifndef M_SPANNINGTREE_COMMANDS_H
-#define M_SPANNINGTREE_COMMANDS_H
+#pragma once
-#include "main.h"
+#include "servercommand.h"
+#include "commandbuilder.h"
/** Handle /RCONNECT
*/
class CommandRConnect : public Command
{
- SpanningTreeUtilities* Utils; /* Utility class */
public:
- CommandRConnect (Module* Callback, SpanningTreeUtilities* Util);
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+ CommandRConnect(Module* Creator);
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
class CommandRSQuit : public Command
{
- SpanningTreeUtilities* Utils; /* Utility class */
public:
- CommandRSQuit(Module* Callback, SpanningTreeUtilities* Util);
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
- void NoticeUser(User* user, const std::string &msg);
+ CommandRSQuit(Module* Creator);
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
-class CommandSVSJoin : public Command
+class CommandMap : public Command
{
public:
- CommandSVSJoin(Module* Creator) : Command(Creator, "SVSJOIN", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
+ CommandMap(Module* Creator);
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
-class CommandSVSPart : public Command
+
+class CommandSVSJoin : public ServerCommand
{
public:
- CommandSVSPart(Module* Creator) : Command(Creator, "SVSPART", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
+ CommandSVSJoin(Module* Creator) : ServerCommand(Creator, "SVSJOIN", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
-class CommandSVSNick : public Command
+
+class CommandSVSPart : public ServerCommand
{
public:
- CommandSVSNick(Module* Creator) : Command(Creator, "SVSNICK", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
+ CommandSVSPart(Module* Creator) : ServerCommand(Creator, "SVSPART", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
-class CommandMetadata : public Command
+
+class CommandSVSNick : public ServerCommand
{
public:
- CommandMetadata(Module* Creator) : Command(Creator, "METADATA", 2) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandSVSNick(Module* Creator) : ServerCommand(Creator, "SVSNICK", 3) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
};
-class CommandUID : public Command
+
+class CommandMetadata : public ServerCommand
{
public:
- CommandUID(Module* Creator) : Command(Creator, "UID", 10) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandMetadata(Module* Creator) : ServerCommand(Creator, "METADATA", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user, const std::string& key, const std::string& val);
+ Builder(Channel* chan, const std::string& key, const std::string& val);
+ Builder(const std::string& key, const std::string& val);
+ };
};
-class CommandOpertype : public Command
+
+class CommandUID : public ServerOnlyServerCommand<CommandUID>
{
public:
- CommandOpertype(Module* Creator) : Command(Creator, "OPERTYPE", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandUID(Module* Creator) : ServerOnlyServerCommand<CommandUID>(Creator, "UID", 10) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ };
};
-class CommandFJoin : public Command
+
+class CommandOpertype : public UserOnlyServerCommand<CommandOpertype>
{
public:
- CommandFJoin(Module* Creator) : Command(Creator, "FJOIN", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandOpertype(Module* Creator) : UserOnlyServerCommand<CommandOpertype>(Creator, "OPERTYPE", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ };
+};
+
+class TreeSocket;
+class CommandFJoin : public ServerCommand
+{
/** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes.
* This does not update the timestamp of the target channel, this must be done seperately.
*/
- void RemoveStatus(User* source, parameterlist ¶ms);
+ static void RemoveStatus(Channel* c);
+
+ /**
+ * Lowers the TS on the given channel: removes all modes, unsets all extensions,
+ * clears the topic and removes all pending invites.
+ * @param chan The target channel whose TS to lower
+ * @param TS The new TS to set
+ * @param newname The new name of the channel; must be the same or a case change of the current name
+ */
+ static void LowerTS(Channel* chan, time_t TS, const std::string& newname);
+ void ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist);
+ public:
+ CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ /** Maximum possible Membership::Id length in decimal digits, used for determining whether a user will fit into
+ * a message or not
+ */
+ static const size_t membid_max_digits = 20;
+ static const size_t maxline = 480;
+ std::string::size_type pos;
+
+ public:
+ Builder(Channel* chan);
+ void add(Membership* memb);
+ bool has_room(Membership* memb) const;
+ void clear();
+ const std::string& finalize();
+ };
+};
+
+class CommandFMode : public ServerCommand
+{
+ public:
+ CommandFMode(Module* Creator) : ServerCommand(Creator, "FMODE", 3) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
+};
+
+class CommandFTopic : public ServerCommand
+{
+ public:
+ CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 4, 5) { }
+ CmdResult Handle(User* user, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(Channel* chan);
+ Builder(User* user, Channel* chan);
+ };
+};
+
+class CommandFHost : public UserOnlyServerCommand<CommandFHost>
+{
+ public:
+ CommandFHost(Module* Creator) : UserOnlyServerCommand<CommandFHost>(Creator, "FHOST", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+};
+
+class CommandFIdent : public UserOnlyServerCommand<CommandFIdent>
+{
+ public:
+ CommandFIdent(Module* Creator) : UserOnlyServerCommand<CommandFIdent>(Creator, "FIDENT", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
+};
+
+class CommandFName : public UserOnlyServerCommand<CommandFName>
+{
+ public:
+ CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
};
-class CommandFMode : public Command
+
+class CommandIJoin : public UserOnlyServerCommand<CommandIJoin>
{
public:
- CommandFMode(Module* Creator) : Command(Creator, "FMODE", 3) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 2) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params);
};
-class CommandFTopic : public Command
+
+class CommandResync : public ServerOnlyServerCommand<CommandResync>
{
public:
- CommandFTopic(Module* Creator) : Command(Creator, "FTOPIC", 4) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
};
-class CommandFHost : public Command
+
+class CommandAway : public UserOnlyServerCommand<CommandAway>
{
public:
- CommandFHost(Module* Creator) : Command(Creator, "FHOST", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandAway(Module* Creator) : UserOnlyServerCommand<CommandAway>(Creator, "AWAY", 0, 2) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(User* user);
+ Builder(User* user, const std::string& msg);
+ };
};
-class CommandFIdent : public Command
+
+class XLine;
+class CommandAddLine : public ServerCommand
{
public:
- CommandFIdent(Module* Creator) : Command(Creator, "FIDENT", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandAddLine(Module* Creator) : ServerCommand(Creator, "ADDLINE", 6, 6) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(XLine* xline, User* user = ServerInstance->FakeClient);
+ };
};
-class CommandFName : public Command
+
+class CommandDelLine : public ServerCommand
{
public:
- CommandFName(Module* Creator) : Command(Creator, "FNAME", 1) { flags_needed = FLAG_SERVERONLY; }
- CmdResult Handle (const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+ CommandDelLine(Module* Creator) : ServerCommand(Creator, "DELLINE", 2, 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class CommandEncap : public ServerCommand
+{
+ public:
+ CommandEncap(Module* Creator) : ServerCommand(Creator, "ENCAP", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+};
+
+class CommandIdle : public UserOnlyServerCommand<CommandIdle>
+{
+ public:
+ CommandIdle(Module* Creator) : UserOnlyServerCommand<CommandIdle>(Creator, "IDLE", 1) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandNick : public UserOnlyServerCommand<CommandNick>
+{
+ public:
+ CommandNick(Module* Creator) : UserOnlyServerCommand<CommandNick>(Creator, "NICK", 2) { }
+ CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& parameters);
+};
+
+class CommandPing : public ServerCommand
+{
+ public:
+ CommandPing(Module* Creator) : ServerCommand(Creator, "PING", 1) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandPong : public ServerOnlyServerCommand<CommandPong>
+{
+ public:
+ CommandPong(Module* Creator) : ServerOnlyServerCommand<CommandPong>(Creator, "PONG", 1) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandPush : public ServerCommand
+{
+ public:
+ CommandPush(Module* Creator) : ServerCommand(Creator, "PUSH", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_UNICAST(parameters[0]); }
+};
+
+class CommandSave : public ServerCommand
+{
+ public:
+ /** Timestamp of the uuid nick of all users who collided and got their nick changed to uuid
+ */
+ static const time_t SavedTimestamp = 100;
+
+ CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class CommandServer : public ServerOnlyServerCommand<CommandServer>
+{
+ static void HandleExtra(TreeServer* newserver, const std::vector<std::string>& params);
+
+ public:
+ CommandServer(Module* Creator) : ServerOnlyServerCommand<CommandServer>(Creator, "SERVER", 3) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ void push_property(const char* key, const std::string& val)
+ {
+ push(key).push_raw('=').push_raw(val);
+ }
+ public:
+ Builder(TreeServer* server);
+ };
+};
+
+class CommandSQuit : public ServerOnlyServerCommand<CommandSQuit>
+{
+ public:
+ CommandSQuit(Module* Creator) : ServerOnlyServerCommand<CommandSQuit>(Creator, "SQUIT", 2) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+};
+
+class CommandSNONotice : public ServerCommand
+{
+ public:
+ CommandSNONotice(Module* Creator) : ServerCommand(Creator, "SNONOTICE", 2) { }
+ CmdResult Handle(User* user, std::vector<std::string>& parameters);
+};
+
+class CommandEndBurst : public ServerOnlyServerCommand<CommandEndBurst>
+{
+ public:
+ CommandEndBurst(Module* Creator) : ServerOnlyServerCommand<CommandEndBurst>(Creator, "ENDBURST") { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+};
+
+class CommandSInfo : public ServerOnlyServerCommand<CommandSInfo>
+{
+ public:
+ CommandSInfo(Module* Creator) : ServerOnlyServerCommand<CommandSInfo>(Creator, "SINFO", 2) { }
+ CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(TreeServer* server, const char* type, const std::string& value);
+ };
};
class SpanningTreeCommands
{
public:
- CommandRConnect rconnect;
- CommandRSQuit rsquit;
CommandSVSJoin svsjoin;
CommandSVSPart svspart;
CommandSVSNick svsnick;
CommandUID uid;
CommandOpertype opertype;
CommandFJoin fjoin;
+ CommandIJoin ijoin;
+ CommandResync resync;
CommandFMode fmode;
CommandFTopic ftopic;
CommandFHost fhost;
CommandFIdent fident;
CommandFName fname;
+ CommandAway away;
+ CommandAddLine addline;
+ CommandDelLine delline;
+ CommandEncap encap;
+ CommandIdle idle;
+ CommandNick nick;
+ CommandPing ping;
+ CommandPong pong;
+ CommandPush push;
+ CommandSave save;
+ CommandServer server;
+ CommandSQuit squit;
+ CommandSNONotice snonotice;
+ CommandEndBurst endburst;
+ CommandSInfo sinfo;
SpanningTreeCommands(ModuleSpanningTree* module);
};
-
-#endif
#include "inspircd.h"
#include "main.h"
#include "treesocket.h"
+#include "treeserver.h"
-static const char* const forge_common_1201[] = {
- "m_allowinvite.so",
- "m_alltime.so",
- "m_auditorium.so",
- "m_banexception.so",
- "m_blockcaps.so",
- "m_blockcolor.so",
- "m_botmode.so",
- "m_censor.so",
- "m_chanfilter.so",
- "m_chanhistory.so",
- "m_channelban.so",
- "m_chanprotect.so",
- "m_chghost.so",
- "m_chgname.so",
- "m_commonchans.so",
- "m_customtitle.so",
- "m_deaf.so",
- "m_delayjoin.so",
- "m_delaymsg.so",
- "m_exemptchanops.so",
- "m_gecosban.so",
- "m_globops.so",
- "m_helpop.so",
- "m_hidechans.so",
- "m_hideoper.so",
- "m_invisible.so",
- "m_inviteexception.so",
- "m_joinflood.so",
- "m_kicknorejoin.so",
- "m_knock.so",
- "m_messageflood.so",
- "m_muteban.so",
- "m_nickflood.so",
- "m_nicklock.so",
- "m_noctcp.so",
- "m_nokicks.so",
- "m_nonicks.so",
- "m_nonotice.so",
- "m_nopartmsg.so",
- "m_ojoin.so",
- "m_operprefix.so",
- "m_permchannels.so",
- "m_redirect.so",
- "m_regex_glob.so",
- "m_regex_pcre.so",
- "m_regex_posix.so",
- "m_regex_tre.so",
- "m_remove.so",
- "m_sajoin.so",
- "m_sakick.so",
- "m_sanick.so",
- "m_sapart.so",
- "m_saquit.so",
- "m_serverban.so",
- "m_services_account.so",
- "m_servprotect.so",
- "m_setident.so",
- "m_showwhois.so",
- "m_silence.so",
- "m_sslmodes.so",
- "m_stripcolor.so",
- "m_swhois.so",
- "m_uninvite.so",
- "m_watch.so"
-};
-
-static std::string wide_newline("\r\n");
static std::string newline("\n");
-void TreeSocket::CompatAddModules(std::vector<std::string>& modlist)
+void TreeSocket::WriteLineNoCompat(const std::string& line)
{
- if (proto_version < 1202)
- {
- // you MUST have chgident loaded in order to be able to translate FIDENT
- modlist.push_back("m_chgident.so");
- for(int i=0; i * sizeof(char*) < sizeof(forge_common_1201); i++)
- {
- if (ServerInstance->Modules->Find(forge_common_1201[i]))
- modlist.push_back(forge_common_1201[i]);
- }
- // module was merged
- if (ServerInstance->Modules->Find("m_operchans.so"))
- {
- modlist.push_back("m_operchans.so");
- modlist.push_back("m_operinvex.so");
- }
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
+ this->WriteData(line);
+ this->WriteData(newline);
}
-void TreeSocket::WriteLine(std::string line)
+void TreeSocket::WriteLine(const std::string& original_line)
{
if (LinkState == CONNECTED)
{
- if (line[0] != ':')
+ if (original_line.c_str()[0] != ':')
{
- ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Sending line without server prefix!");
- line = ":" + ServerInstance->Config->GetSID() + " " + line;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sending line without server prefix!");
+ WriteLine(":" + ServerInstance->Config->GetSID() + " " + original_line);
+ return;
}
if (proto_version != ProtocolVersion)
{
+ std::string line = original_line;
std::string::size_type a = line.find(' ');
std::string::size_type b = line.find(' ', a + 1);
std::string command = line.substr(a + 1, b-a-1);
// now try to find a translation entry
// TODO a more efficient lookup method will be needed later
- if (proto_version < 1202 && command == "FIDENT")
- {
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting FIDENT for 1201-protocol server");
- line = ":" + ServerInstance->Config->GetSID() + " CHGIDENT " + line.substr(1,a-1) + line.substr(b);
- }
- else if (proto_version < 1202 && command == "SAVE")
+ if (proto_version < 1205)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting SAVE for 1201-protocol server");
- std::string::size_type c = line.find(' ', b + 1);
- std::string uid = line.substr(b, c - b);
- line = ":" + ServerInstance->Config->GetSID() + " SVSNICK" + uid + line.substr(b);
- }
- else if (proto_version < 1202 && command == "AWAY")
- {
- if (b != std::string::npos)
+ if (command == "IJOIN")
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Stripping AWAY timestamp for 1201-protocol server");
+ // Convert
+ // :<uid> IJOIN <chan> <membid> [<ts> [<flags>]]
+ // to
+ // :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
std::string::size_type c = line.find(' ', b + 1);
- line.erase(b,c-b);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ // Erase membership id first
+ line.erase(c, d-c);
+ if (d == std::string::npos)
+ {
+ // No TS or modes in the command
+ // :22DAAAAAB IJOIN #chan
+ const std::string channame = line.substr(b+1, c-b-1);
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (!chan)
+ return;
+
+ line.push_back(' ');
+ line.append(ConvToStr(chan->age));
+ line.append(" + ,");
+ }
+ else
+ {
+ d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ {
+ // TS present, no modes
+ // :22DAAAAAC IJOIN #chan 12345
+ line.append(" + ,");
+ }
+ else
+ {
+ // Both TS and modes are present
+ // :22DAAAAAC IJOIN #chan 12345 ov
+ std::string::size_type e = line.find(' ', d + 1);
+ if (e != std::string::npos)
+ line.erase(e);
+
+ line.insert(d, " +");
+ line.push_back(',');
+ }
+ }
+
+ // Move the uuid to the end and replace the I with an F
+ line.append(line.substr(1, 9));
+ line.erase(4, 6);
+ line[5] = 'F';
}
- }
- else if (proto_version < 1202 && command == "ENCAP")
- {
- // :src ENCAP target command [args...]
- // A B C D
- // Therefore B and C cannot be npos in a valid command
- if (b == std::string::npos)
+ else if (command == "RESYNC")
return;
- std::string::size_type c = line.find(' ', b + 1);
- if (c == std::string::npos)
- return;
- std::string::size_type d = line.find(' ', c + 1);
- std::string subcmd = line.substr(c + 1, d - c - 1);
+ else if (command == "METADATA")
+ {
+ // Drop TS for channel METADATA, translate METADATA operquit into an OPERQUIT command
+ // :sid METADATA #target TS extname ...
+ // A B C D
+ if (b == std::string::npos)
+ return;
- if (subcmd == "CHGIDENT" && d != std::string::npos)
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
+ if (line[b + 1] == '#')
+ {
+ // We're sending channel metadata
+ line.erase(c, d-c);
+ }
+ else if (!line.compare(c, d-c, " operquit", 9))
+ {
+ // ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message"
+ line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d);
+ }
+ }
+ else if (command == "FTOPIC")
{
+ // Drop channel TS for FTOPIC
+ // :sid FTOPIC #target TS TopicTS setter :newtopic
+ // A B C D E F
+ // :uid FTOPIC #target TS TopicTS :newtopic
+ // A B C D E
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
std::string::size_type e = line.find(' ', d + 1);
- if (e == std::string::npos)
- return; // not valid
- std::string target = line.substr(d + 1, e - d - 1);
+ if (line[e+1] == ':')
+ {
+ line.erase(c, e-c);
+ line.erase(a+1, 1);
+ }
+ else
+ line.erase(c, d-c);
+ }
+ else if ((command == "PING") || (command == "PONG"))
+ {
+ // :22D PING 20D
+ if (line.length() < 13)
+ return;
+
+ // Insert the source SID (and a space) between the command and the first parameter
+ line.insert(10, line.substr(1, 4));
+ }
+ else if (command == "OPERTYPE")
+ {
+ std::string::size_type colon = line.find(':', b);
+ if (colon != std::string::npos)
+ {
+ for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i)
+ {
+ if (*i == ' ')
+ *i = '_';
+ }
+ line.erase(colon, 1);
+ }
+ }
+ else if (command == "INVITE")
+ {
+ // :22D INVITE 22DAAAAAN #chan TS ExpirationTime
+ // A B C D E
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ return;
+
+ std::string::size_type e = line.find(' ', d + 1);
+ // If there is no expiration time then everything will be erased from 'd'
+ line.erase(d, e-d);
+ }
+ else if (command == "FJOIN")
+ {
+ // Strip membership ids
+ // :22D FJOIN #chan 1234 +f 4:3 :o,22DAAAAAB:15 o,22DAAAAAA:15
+ // :22D FJOIN #chan 1234 +f 4:3 o,22DAAAAAB:15
+ // :22D FJOIN #chan 1234 +Pf 4:3 :
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Forging acceptance of CHGIDENT from 1201-protocol server");
- recvq.insert(0, ":" + target + " FIDENT " + line.substr(e) + "\n");
+ // If the last parameter is prefixed by a colon then it's a userlist which may have 0 or more users;
+ // if it isn't, then it is a single member
+ std::string::size_type spcolon = line.find(" :");
+ if (spcolon != std::string::npos)
+ {
+ spcolon++;
+ // Loop while there is a ':' in the userlist, this is never true if the channel is empty
+ std::string::size_type pos = std::string::npos;
+ while ((pos = line.rfind(':', pos-1)) > spcolon)
+ {
+ // Find the next space after the ':'
+ std::string::size_type sp = line.find(' ', pos);
+ // Erase characters between the ':' and the next space after it, including the ':' but not the space;
+ // if there is no next space, everything will be erased between pos and the end of the line
+ line.erase(pos, sp-pos);
+ }
+ }
+ else
+ {
+ // Last parameter is a single member
+ std::string::size_type sp = line.rfind(' ');
+ std::string::size_type colon = line.find(':', sp);
+ line.erase(colon);
+ }
}
+ else if (command == "KICK")
+ {
+ // Strip membership id if the KICK has one
+ if (b == std::string::npos)
+ return;
+
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
- Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd);
- if (thiscmd && subcmd != "WHOISNOTICE")
+ std::string::size_type d = line.find(' ', c + 1);
+ if ((d < line.size()-1) && (original_line[d+1] != ':'))
+ {
+ // There is a third parameter which doesn't begin with a colon, erase it
+ std::string::size_type e = line.find(' ', d + 1);
+ line.erase(d, e-d);
+ }
+ }
+ else if (command == "SINFO")
+ {
+ // :22D SINFO version :InspIRCd-2.2
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ // Only translating SINFO version, discard everything else
+ if (line.compare(b, 9, " version ", 9))
+ return;
+
+ line = line.substr(0, 5) + "VERSION" + line.substr(c);
+ }
+ else if (command == "SERVER")
{
- Version ver = thiscmd->creator->GetVersion();
- if (ver.Flags & VF_OPTCOMMON)
+ // :001 SERVER inspircd.test 002 [<anything> ...] :gecos
+ // A B C
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ return;
+
+ std::string::size_type d = c + 4;
+ std::string::size_type spcolon = line.find(" :", d);
+ if (spcolon == std::string::npos)
+ return;
+
+ line.erase(d, spcolon-d);
+ line.insert(c, " * 0");
+
+ if (burstsent)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Removing ENCAP on '%s' for 1201-protocol server",
- subcmd.c_str());
- line.erase(a, c-a);
+ WriteLineNoCompat(line);
+
+ // Synthesize a :<newserver> BURST <time> message
+ spcolon = line.find(" :");
+ line = CmdBuilder(line.substr(spcolon-3, 3), "BURST").push_int(ServerInstance->Time()).str();
}
}
}
+ WriteLineNoCompat(line);
+ return;
}
}
- ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
- this->WriteData(line);
- if (proto_version < 1202)
- this->WriteData(wide_newline);
- else
- this->WriteData(newline);
+ WriteLineNoCompat(original_line);
+}
+
+namespace
+{
+ bool InsertCurrentChannelTS(std::vector<std::string>& params, unsigned int chanindex = 0, unsigned int pos = 1)
+ {
+ Channel* chan = ServerInstance->FindChan(params[chanindex]);
+ if (!chan)
+ return false;
+
+ // Insert the current TS of the channel after the pos-th parameter
+ params.insert(params.begin()+pos, ConvToStr(chan->age));
+ return true;
+ }
+}
+
+bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std::vector<std::string>& params)
+{
+ if ((cmd == "METADATA") && (params.size() >= 3) && (params[0][0] == '#'))
+ {
+ // :20D METADATA #channel extname :extdata
+ return InsertCurrentChannelTS(params);
+ }
+ else if ((cmd == "FTOPIC") && (params.size() >= 4))
+ {
+ // :20D FTOPIC #channel 100 Attila :topic text
+ return InsertCurrentChannelTS(params);
+ }
+ else if ((cmd == "PING") || (cmd == "PONG"))
+ {
+ if (params.size() == 1)
+ {
+ // If it's a PING with 1 parameter, reply with a PONG now, if it's a PONG with 1 parameter (weird), do nothing
+ if (cmd[1] == 'I')
+ this->WriteData(":" + ServerInstance->Config->GetSID() + " PONG " + params[0] + newline);
+
+ // Don't process this message further
+ return false;
+ }
+
+ // :20D PING 20D 22D
+ // :20D PONG 20D 22D
+ // Drop the first parameter
+ params.erase(params.begin());
+
+ // If the target is a server name, translate it to a SID
+ if (!InspIRCd::IsSID(params[0]))
+ {
+ TreeServer* server = Utils->FindServer(params[0]);
+ if (!server)
+ {
+ // We've no idea what this is, log and stop processing
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received a " + cmd + " with an unknown target: \"" + params[0] + "\", command dropped");
+ return false;
+ }
+
+ params[0] = server->GetID();
+ }
+ }
+ else if ((cmd == "GLINE") || (cmd == "KLINE") || (cmd == "ELINE") || (cmd == "ZLINE") || (cmd == "QLINE"))
+ {
+ // Fix undocumented protocol usage: translate GLINE, ZLINE, etc. into ADDLINE or DELLINE
+ if ((params.size() != 1) && (params.size() != 3))
+ return false;
+
+ parameterlist p;
+ p.push_back(cmd.substr(0, 1));
+ p.push_back(params[0]);
+
+ if (params.size() == 3)
+ {
+ cmd = "ADDLINE";
+ p.push_back(who->nick);
+ p.push_back(ConvToStr(ServerInstance->Time()));
+ p.push_back(ConvToStr(InspIRCd::Duration(params[1])));
+ p.push_back(params[2]);
+ }
+ else
+ cmd = "DELLINE";
+
+ params.swap(p);
+ }
+ else if (cmd == "SVSMODE")
+ {
+ cmd = "MODE";
+ }
+ else if (cmd == "OPERQUIT")
+ {
+ // Translate OPERQUIT into METADATA
+ if (params.empty())
+ return false;
+
+ cmd = "METADATA";
+ params.insert(params.begin(), who->uuid);
+ params.insert(params.begin()+1, "operquit");
+ who = MyRoot->ServerUser;
+ }
+ else if ((cmd == "TOPIC") && (params.size() >= 2))
+ {
+ // :20DAAAAAC TOPIC #chan :new topic
+ cmd = "FTOPIC";
+ if (!InsertCurrentChannelTS(params))
+ return false;
+
+ params.insert(params.begin()+2, ConvToStr(ServerInstance->Time()));
+ }
+ else if (cmd == "MODENOTICE")
+ {
+ // MODENOTICE is always supported by 2.0 but it's optional in 2.2.
+ params.insert(params.begin(), "*");
+ params.insert(params.begin()+1, cmd);
+ cmd = "ENCAP";
+ }
+ else if (cmd == "RULES")
+ {
+ return false;
+ }
+ else if (cmd == "INVITE")
+ {
+ // :20D INVITE 22DAAABBB #chan
+ // :20D INVITE 22DAAABBB #chan 123456789
+ // Insert channel timestamp after the channel name; the 3rd parameter, if there, is the invite expiration time
+ return InsertCurrentChannelTS(params, 1, 2);
+ }
+ else if (cmd == "VERSION")
+ {
+ // :20D VERSION :InspIRCd-2.0
+ // change to
+ // :20D SINFO version :InspIRCd-2.0
+ cmd = "SINFO";
+ params.insert(params.begin(), "version");
+ }
+ else if (cmd == "JOIN")
+ {
+ // 2.0 allows and forwards legacy JOINs but we don't, so translate them to FJOINs before processing
+ if ((params.size() != 1) || (IS_SERVER(who)))
+ return false; // Huh?
+
+ cmd = "FJOIN";
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ params.push_back(ConvToStr(chan ? chan->age : ServerInstance->Time()));
+ params.push_back("+");
+ params.push_back(",");
+ params.back().append(who->uuid);
+ who = TreeServer::Get(who)->ServerUser;
+ }
+ else if ((cmd == "FMODE") && (params.size() >= 2))
+ {
+ // Translate user mode changes with timestamp to MODE
+ if (params[0][0] != '#')
+ {
+ User* user = ServerInstance->FindUUID(params[0]);
+ if (!user)
+ return false;
+
+ // Emulate the old nonsensical behavior
+ if (user->age < ServerCommand::ExtractTS(params[1]))
+ return false;
+
+ cmd = "MODE";
+ params.erase(params.begin()+1);
+ }
+ }
+ else if ((cmd == "SERVER") && (params.size() > 4))
+ {
+ // This does not affect the initial SERVER line as it is sent before the link state is CONNECTED
+ // :20D SERVER <name> * 0 <sid> <desc>
+ // change to
+ // :20D SERVER <name> <sid> <desc>
+
+ params[1].swap(params[3]);
+ params.erase(params.begin()+2, params.begin()+4);
+
+ // If the source of this SERVER message is not bursting, then new servers it introduces are bursting
+ TreeServer* server = TreeServer::Get(who);
+ if (!server->IsBursting())
+ params.insert(params.begin()+2, "burst=" + ConvToStr(ServerInstance->Time()*1000));
+ }
+ else if (cmd == "BURST")
+ {
+ // A server is introducing another one, drop unnecessary BURST
+ return false;
+ }
+
+ return true; // Passthru
}
#include "inspircd.h"
#include "xline.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-
-bool TreeSocket::DelLine(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandDelLine::Handle(User* user, std::vector<std::string>& params)
{
- if (params.size() < 2)
- return true;
-
- std::string setter = "<unknown>";
-
- User* user = ServerInstance->FindNick(prefix);
- if (user)
- setter = user->nick;
- else
- {
- TreeServer* t = Utils->FindServer(prefix);
- if (t)
- setter = t->GetName();
- }
-
+ const std::string& setter = user->nick;
/* NOTE: No check needed on 'user', this function safely handles NULL */
if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user))
{
ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(),
params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str());
- Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix);
+ return CMD_SUCCESS;
}
- return true;
+ return CMD_FAILURE;
}
-
#include "inspircd.h"
-#include "xline.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
+#include "commands.h"
/** ENCAP */
-void TreeSocket::Encap(User* who, parameterlist ¶ms)
+CmdResult CommandEncap::Handle(User* user, std::vector<std::string>& params)
{
- if (params.size() > 1)
+ if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
{
- if (ServerInstance->Config->GetSID() == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0]))
- {
- parameterlist plist(params.begin() + 2, params.end());
- ServerInstance->Parser->CallHandler(params[1], plist, who);
- // discard return value, ENCAP shall succeed even if the command does not exist
- }
-
- params[params.size() - 1] = ":" + params[params.size() - 1];
+ parameterlist plist(params.begin() + 2, params.end());
+ Command* cmd = NULL;
+ ServerInstance->Parser.CallHandler(params[1], plist, user, &cmd);
+ // Discard return value, ENCAP shall succeed even if the command does not exist
- if (params[0].find_first_of("*?") != std::string::npos)
- {
- Utils->DoOneToAllButSender(who->uuid, "ENCAP", params, who->server);
- }
- else
- Utils->DoOneToOne(who->uuid, "ENCAP", params, params[0]);
+ if ((cmd) && (cmd->force_manual_route))
+ return CMD_FAILURE;
}
+ return CMD_SUCCESS;
}
+RouteDescriptor CommandEncap::GetRouting(User* user, const std::vector<std::string>& params)
+{
+ if (params[0].find_first_of("*?") != std::string::npos)
+ return ROUTE_BROADCAST;
+ return ROUTE_UNICAST(params[0]);
+}
#include "treesocket.h"
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
-CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- /* 1.1 FJOIN works as follows:
+ /* 1.1+ FJOIN works as follows:
*
* Each FJOIN is sent along with a timestamp, and the side with the lowest
* timestamp 'wins'. From this point on we will refer to this side as the
* The winning side on the other hand will ignore all user modes from the
* losing side, so only its own modes get applied. Life is simple for those
* who succeed at internets. :-)
+ *
+ * Syntax:
+ * :<sid> FJOIN <chan> <TS> <modes> :[<member> [<member> ...]]
+ * The last parameter is a list consisting of zero or more channel members
+ * (permanent channels may have zero users). Each entry on the list is in the
+ * following format:
+ * [[<modes>,]<uuid>[:<membid>]
+ * <modes> is a concatenation of the mode letters the user has on the channel
+ * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
+ * are not important but if a server ecounters an unknown mode letter, it will
+ * drop the link to avoid desync.
+ *
+ * InspIRCd 2.0 and older required a comma before the uuid even if the user
+ * had no prefix modes on the channel, InspIRCd 2.2 and later does not require
+ * a comma in this case anymore.
+ *
+ * <membid> is a positive integer representing the id of the membership.
+ * If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
+ *
*/
- irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
- User* who = NULL; /* User we are currently checking */
- std::string channel = params[0]; /* Channel name, as a string */
- time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
- irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : ""); /* users from the user list */
- bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
- Channel* chan = ServerInstance->FindChan(channel); /* The channel we're sending joins to */
- bool created = !chan; /* True if the channel doesnt exist here yet */
- std::string item; /* One item in the list of nicks */
+ time_t TS = ServerCommand::ExtractTS(params[1]);
- TreeServer* src_server = Utils->FindServer(srcuser->server);
- TreeSocket* src_socket = src_server->GetRoute()->GetSocket();
+ const std::string& channel = params[0];
+ Channel* chan = ServerInstance->FindChan(channel);
+ bool apply_other_sides_modes = true;
- if (!TS)
- {
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str());
- return CMD_INVALID;
- }
-
- if (created)
+ if (!chan)
{
chan = new Channel(channel, TS);
- ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN received for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
}
else
{
time_t ourTS = chan->age;
-
if (TS != ourTS)
+ {
ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu",
chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS));
- /* If our TS is less than theirs, we dont accept their modes */
- if (ourTS < TS)
- {
- ServerInstance->SNO->WriteToSnoMask('d', "NOT Applying modes from other side");
- apply_other_sides_modes = false;
- }
- else if (ourTS > TS)
- {
- /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
- ServerInstance->SNO->WriteToSnoMask('d', "Removing our modes, accepting remote");
- parameterlist param_list;
- if (Utils->AnnounceTSChange)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
- // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
- chan->name = channel;
- chan->age = TS;
- chan->ClearInvites();
- param_list.push_back(channel);
- this->RemoveStatus(ServerInstance->FakeClient, param_list);
-
- // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
- // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
- // deleted later) as soon as the permchan mode is removed from them.
- if (ServerInstance->FindChan(channel) == NULL)
+ /* If our TS is less than theirs, we dont accept their modes */
+ if (ourTS < TS)
+ {
+ apply_other_sides_modes = false;
+ }
+ else if (ourTS > TS)
{
- chan = new Channel(channel, TS);
+ // Our TS is greater than theirs, remove all modes, extensions, etc. from the channel
+ LowerTS(chan, TS, channel);
+
+ // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
+ // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
+ // deleted later) as soon as the permchan mode is removed from them.
+ if (ServerInstance->FindChan(channel) == NULL)
+ {
+ chan = new Channel(channel, TS);
+ }
}
}
- // The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
}
- /* First up, apply their modes if they won the TS war */
+ /* First up, apply their channel modes if they won the TS war */
+ Modes::ChangeList modechangelist;
if (apply_other_sides_modes)
{
- // Need to use a modestacker here due to maxmodes
- irc::modestacker stack(true);
- std::vector<std::string>::const_iterator paramit = params.begin() + 3;
- const std::vector<std::string>::const_iterator lastparamit = ((params.size() > 3) ? (params.end() - 1) : params.end());
- for (std::string::const_iterator i = params[2].begin(); i != params[2].end(); ++i)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
- if (!mh)
- continue;
+ ServerInstance->Modes.ModeParamsToChangeList(srcuser, MODETYPE_CHANNEL, params, modechangelist, 2, params.size() - 1);
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+ // Reuse for prefix modes
+ modechangelist.clear();
+ }
- std::string modeparam;
- if ((paramit != lastparamit) && (mh->GetNumParams(true)))
- {
- modeparam = *paramit;
- ++paramit;
- }
+ TreeServer* const sourceserver = TreeServer::Get(srcuser);
- stack.Push(*i, modeparam);
- }
+ /* Now, process every 'modes,uuid' pair */
+ irc::tokenstream users(params.back());
+ std::string item;
+ Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
+ while (users.GetToken(item))
+ {
+ ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr);
+ }
- std::vector<std::string> modelist;
+ // Set prefix modes on their users if we lost the FJOIN or had equal TS
+ if (apply_other_sides_modes)
+ ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
- // Mode parser needs to know what channel to act on.
- modelist.push_back(params[0]);
+ return CMD_SUCCESS;
+}
- while (stack.GetStackedLine(modelist))
- {
- ServerInstance->Modes->Process(modelist, srcuser, true);
- modelist.erase(modelist.begin() + 1, modelist.end());
- }
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist)
+{
+ std::string::size_type comma = item.find(',');
- ServerInstance->Modes->Process(modelist, srcuser, true);
+ // Comma not required anymore if the user has no modes
+ const std::string::size_type ubegin = (comma == std::string::npos ? 0 : comma+1);
+ std::string uuid(item, ubegin, UIDGenerator::UUID_LENGTH);
+ User* who = ServerInstance->FindUUID(uuid);
+ if (!who)
+ {
+ // Probably KILLed, ignore
+ return;
}
- /* Now, process every 'modes,nick' pair */
- while (users.GetToken(item))
+ TreeSocket* src_socket = sourceserver->GetSocket();
+ /* Check that the user's 'direction' is correct */
+ TreeServer* route_back_again = TreeServer::Get(who);
+ if (route_back_again->GetSocket() != src_socket)
{
- const char* usr = item.c_str();
- if (usr && *usr)
- {
- const char* unparsedmodes = usr;
- std::string modes;
-
-
- /* Iterate through all modes for this user and check they are valid. */
- while ((*unparsedmodes) && (*unparsedmodes != ','))
- {
- ModeHandler *mh = ServerInstance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL);
- if (!mh)
- {
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised mode %c, dropping link", *unparsedmodes);
- return CMD_INVALID;
- }
+ return;
+ }
- modes += *unparsedmodes;
- usr++;
- unparsedmodes++;
- }
+ /* Check if the user received at least one mode */
+ if ((modechangelist) && (comma > 0) && (comma != std::string::npos))
+ {
+ /* Iterate through the modes and see if they are valid here, if so, apply */
+ std::string::const_iterator commait = item.begin()+comma;
+ for (std::string::const_iterator i = item.begin(); i != commait; ++i)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
+ if (!mh)
+ throw ProtocolException("Unrecognised mode '" + std::string(1, *i) + "'");
- /* Advance past the comma, to the nick */
- usr++;
+ /* Add any modes this user had to the mode stack */
+ modechangelist->push_add(mh, who->nick);
+ }
+ }
- /* Check the user actually exists */
- who = ServerInstance->FindUUID(usr);
- if (who)
- {
- /* Check that the user's 'direction' is correct */
- TreeServer* route_back_again = Utils->BestRouteTo(who->server);
- if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
- continue;
+ Membership* memb = chan->ForceJoin(who, NULL, sourceserver->IsBursting());
+ if (!memb)
+ return;
- /* Add any modes this user had to the mode stack */
- for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
- modestack.Push(*x, who->nick);
+ // Assign the id to the new Membership
+ Membership::Id membid = 0;
+ const std::string::size_type colon = item.rfind(':');
+ if (colon != std::string::npos)
+ membid = Membership::IdFromString(item.substr(colon+1));
+ memb->id = membid;
+}
- Channel::JoinUser(who, channel.c_str(), true, "", src_server->bursting, TS);
- }
- else
- {
- ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistant user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
- continue;
- }
- }
- }
+void CommandFJoin::RemoveStatus(Channel* c)
+{
+ Modes::ChangeList changelist;
- /* Flush mode stacker if we lost the FJOIN or had equal TS */
- if (apply_other_sides_modes)
+ const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
+ for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
{
- parameterlist stackresult;
- stackresult.push_back(channel);
+ ModeHandler* mh = i->second;
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
+ * rather than applied immediately. Module unloads require this to be done immediately,
+ * for this function we require tidyness instead. Fixes bug #493
+ */
+ mh->RemoveMode(c, changelist);
}
- return CMD_SUCCESS;
+
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, changelist, ModeParser::MODE_LOCALONLY);
}
-void CommandFJoin::RemoveStatus(User* srcuser, parameterlist ¶ms)
+void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
{
- if (params.size() < 1)
- return;
+ if (Utils->AnnounceTSChange)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), newname.c_str(), (unsigned long) chan->age, (unsigned long) TS);
- Channel* c = ServerInstance->FindChan(params[0]);
+ // While the name is equal in case-insensitive compare, it might differ in case; use the remote version
+ chan->name = newname;
+ chan->age = TS;
- if (c)
- {
- irc::modestacker stack(false);
- parameterlist stackresult;
- stackresult.push_back(c->name);
+ // Remove all pending invites
+ chan->ClearInvites();
- for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
-
- /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
- * rather than applied immediately. Module unloads require this to be done immediately,
- * for this function we require tidyness instead. Fixes bug #493
- */
- if (mh)
- mh->RemoveMode(c, &stack);
- }
+ // Clear all modes
+ CommandFJoin::RemoveStatus(chan);
- while (stack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ // Unset all extensions
+ chan->FreeAllExtItems();
+
+ // Clear the topic, if it isn't empty then send a topic change message to local users
+ if (!chan->topic.empty())
+ {
+ chan->topic.clear();
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :", chan->name.c_str());
}
+ chan->setby.clear();
+ chan->topicset = 0;
}
+CommandFJoin::Builder::Builder(Channel* chan)
+ : CmdBuilder("FJOIN")
+{
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ pos = str().size();
+ push_raw(chan->ChanModes(true)).push_raw(" :");
+}
+
+void CommandFJoin::Builder::add(Membership* memb)
+{
+ push_raw(memb->modes).push_raw(',').push_raw(memb->user->uuid);
+ push_raw(':').push_raw_int(memb->id);
+ push_raw(' ');
+}
+
+bool CommandFJoin::Builder::has_room(Membership* memb) const
+{
+ return ((str().size() + memb->modes.size() + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
+}
+
+void CommandFJoin::Builder::clear()
+{
+ content.erase(pos);
+ push_raw(" :");
+}
+
+const std::string& CommandFJoin::Builder::finalize()
+{
+ if (*content.rbegin() == ' ')
+ content.erase(content.size()-1);
+ return str();
+}
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
/** FMODE command - server mode with timestamp checks */
-CmdResult CommandFMode::Handle(const std::vector<std::string>& params, User *who)
+CmdResult CommandFMode::Handle(User* who, std::vector<std::string>& params)
{
- std::string sourceserv = who->server;
-
- std::vector<std::string> modelist;
- time_t TS = 0;
- for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
- {
- if (q == 1)
- {
- /* The timestamp is in this position.
- * We don't want to pass that up to the
- * server->client protocol!
- */
- TS = atoi(params[q].c_str());
- }
- else
- {
- /* Everything else is fine to append to the modelist */
- modelist.push_back(params[q]);
- }
+ time_t TS = ServerCommand::ExtractTS(params[1]);
- }
- /* Extract the TS value of the object, either User or Channel */
- User* dst = ServerInstance->FindNick(params[0]);
- Channel* chan = NULL;
- time_t ourTS = 0;
+ Channel* const chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ // Channel doesn't exist
+ return CMD_FAILURE;
- if (dst)
- {
- ourTS = dst->age;
- }
- else
- {
- chan = ServerInstance->FindChan(params[0]);
- if (chan)
- {
- ourTS = chan->age;
- }
- else
- /* Oops, channel doesnt exist! */
- return CMD_FAILURE;
- }
+ // Extract the TS of the channel in question
+ time_t ourTS = chan->age;
- if (!TS)
- {
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
- return CMD_INVALID;
- }
+ /* If the TS is greater than ours, we drop the mode and don't pass it anywhere.
+ */
+ if (TS > ourTS)
+ return CMD_FAILURE;
/* TS is equal or less: Merge the mode changes into ours and pass on.
*/
- if (TS <= ourTS)
- {
- bool merge = (TS == ourTS) && IS_SERVER(who);
- ServerInstance->Modes->Process(modelist, who, merge);
- return CMD_SUCCESS;
- }
- /* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
- */
- return CMD_FAILURE;
-}
+ // Turn modes into a Modes::ChangeList; may have more elements than max modes
+ Modes::ChangeList changelist;
+ ServerInstance->Modes.ModeParamsToChangeList(who, MODETYPE_CHANNEL, params, changelist, 2);
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_LOCALONLY;
+ if ((TS == ourTS) && IS_SERVER(who))
+ flags |= ModeParser::MODE_MERGE;
+ ServerInstance->Modes->Process(who, chan, NULL, changelist, flags);
+ return CMD_SUCCESS;
+}
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
/** FTOPIC command */
-CmdResult CommandFTopic::Handle(const std::vector<std::string>& params, User *user)
+CmdResult CommandFTopic::Handle(User* user, std::vector<std::string>& params)
{
- time_t ts = atoi(params[1].c_str());
Channel* c = ServerInstance->FindChan(params[0]);
- if (c)
+ if (!c)
+ return CMD_FAILURE;
+
+ if (c->age < ServerCommand::ExtractTS(params[1]))
+ // Our channel TS is older, nothing to do
+ return CMD_FAILURE;
+
+ // Channel::topicset is initialized to 0 on channel creation, so their ts will always win if we never had a topic
+ time_t ts = ServerCommand::ExtractTS(params[2]);
+ if (ts < c->topicset)
+ return CMD_FAILURE;
+
+ // The topic text is always the last parameter
+ const std::string& newtopic = params.back();
+
+ // If there is a setter in the message use that, otherwise use the message source
+ const std::string& setter = ((params.size() > 4) ? params[3] : (ServerInstance->Config->FullHostInTopic ? user->GetFullHost() : user->nick));
+
+ /*
+ * If the topics were updated at the exact same second, accept
+ * the remote only when it's "bigger" than ours as defined by
+ * string comparision, so non-empty topics always overridde
+ * empty topics if their timestamps are equal
+ *
+ * Similarly, if the topic texts are equal too, keep one topic
+ * setter and discard the other
+ */
+ if (ts == c->topicset)
+ {
+ // Discard if their topic text is "smaller"
+ if (c->topic > newtopic)
+ return CMD_FAILURE;
+
+ // If the texts are equal in addition to the timestamps, decide which setter to keep
+ if ((c->topic == newtopic) && (c->setby >= setter))
+ return CMD_FAILURE;
+ }
+
+ if (c->topic != newtopic)
{
- if ((ts >= c->topicset) || (c->topic.empty()))
- {
- if (c->topic != params[3])
- {
- // Update topic only when it differs from current topic
- c->topic.assign(params[3], 0, ServerInstance->Config->Limits.MaxTopic);
- c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
- }
-
- // Always update setter and settime.
- c->setby.assign(params[2], 0, 127);
- c->topicset = ts;
- }
+ // Update topic only when it differs from current topic
+ c->topic.assign(newtopic, 0, ServerInstance->Config->Limits.MaxTopic);
+ c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
}
+
+ // Update setter and settime
+ c->setby.assign(setter, 0, 128);
+ c->topicset = ts;
+
+ FOREACH_MOD(OnPostTopicChange, (user, c, c->topic));
+
return CMD_SUCCESS;
}
+// Used when bursting and in reply to RESYNC, contains topic setter as the 4th parameter
+CommandFTopic::Builder::Builder(Channel* chan)
+ : CmdBuilder("FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push(chan->setby);
+ push_last(chan->topic);
+}
+
+// Used when changing the topic, the setter is the message source
+CommandFTopic::Builder::Builder(User* user, Channel* chan)
+ : CmdBuilder(user, "FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push_last(chan->topic);
+}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "../hash.h"
-#include "../ssl.h"
-#include "socketengine.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "link.h"
#include "treesocket.h"
-#include "resolvers.h"
const std::string& TreeSocket::GetOurChallenge()
{
/* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
* suggesting the use of HMAC to secure the password against various attacks.
*
- * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
+ * Note: If an sha256 provider is not available, we MUST fall back to plaintext with no
* HMAC challenge/response.
*/
HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
- if (Utils->ChallengeResponse && sha256 && !challenge.empty())
- {
- if (proto_version < 1202)
- {
- /* This is how HMAC is done in InspIRCd 1.2:
- *
- * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
- *
- * 5c and 36 were chosen as part of the HMAC standard, because they
- * flip the bits in a way likely to strengthen the function.
- */
- std::string hmac1, hmac2;
-
- for (size_t n = 0; n < password.length(); n++)
- {
- hmac1.push_back(static_cast<char>(password[n] ^ 0x5C));
- hmac2.push_back(static_cast<char>(password[n] ^ 0x36));
- }
-
- hmac2.append(challenge);
- hmac2 = sha256->hexsum(hmac2);
-
- std::string hmac = hmac1 + hmac2;
- hmac = sha256->hexsum(hmac);
-
- return "HMAC-SHA256:"+ hmac;
- }
- else
- {
- return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
- }
- }
- else if (!challenge.empty() && !sha256)
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
+ if (sha256 && !challenge.empty())
+ return "AUTH:" + BinToBase64(sha256->hmac(password, challenge));
+
+ if (!challenge.empty() && !sha256)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Not authenticating to server using SHA256/HMAC because we don't have an SHA256 provider (e.g. m_sha256) loaded!");
return password;
}
capab->auth_fingerprint = !link.Fingerprint.empty();
capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty();
- std::string fp;
- if (GetIOHook())
+ std::string fp = SSLClientCert::GetFingerprint(this);
+ if (capab->auth_fingerprint)
{
- SocketCertificateRequest req(this, Utils->Creator);
- if (req.cert)
+ /* Require fingerprint to exist and match */
+ if (link.Fingerprint != fp)
{
- fp = req.cert->GetFingerprint();
+ ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL certificate fingerprint on link %s: need \"%s\" got \"%s\"",
+ link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
+ SendError("Invalid SSL certificate fingerprint " + fp + " - expected " + link.Fingerprint);
+ return false;
}
}
{
std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge);
- /* Straight string compare of hashes */
- if (our_hmac != theirs)
+ // Use the timing-safe compare function to compare the hashes
+ if (!InspIRCd::TimingSafeCompare(our_hmac, theirs))
return false;
}
else
{
- /* Straight string compare of plaintext */
- if (link.RecvPass != theirs)
+ // Use the timing-safe compare function to compare the passwords
+ if (!InspIRCd::TimingSafeCompare(link.RecvPass, theirs))
return false;
}
- if (capab->auth_fingerprint)
+ // Tell opers to set up fingerprint verification if it's not already set up and the SSL mod gave us a fingerprint
+ // this time
+ if ((!capab->auth_fingerprint) && (!fp.empty()))
{
- /* Require fingerprint to exist and match */
- if (link.Fingerprint != fp)
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL fingerprint on link %s: need \"%s\" got \"%s\"",
- link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
- SendError("Provided invalid SSL fingerprint " + fp + " - expected " + link.Fingerprint);
- return false;
- }
- }
- else if (!fp.empty())
- {
- ServerInstance->SNO->WriteToSnoMask('l', "SSL fingerprint for link %s is \"%s\". "
+ ServerInstance->SNO->WriteToSnoMask('l', "SSL certificate fingerprint for link %s is \"%s\". "
"You can improve security by specifying this in <link:fingerprint>.", link.Name.c_str(), fp.c_str());
}
+
return true;
}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
-bool TreeSocket::Whois(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, std::vector<std::string>& params)
{
- if (params.size() < 1)
- return true;
- User* u = ServerInstance->FindNick(prefix);
- if (u)
+ /**
+ * There are two forms of IDLE: request and reply. Requests have one parameter,
+ * replies have more than one.
+ *
+ * If this is a request, 'issuer' did a /whois and its server wants to learn the
+ * idle time of the user in params[0].
+ *
+ * If this is a reply, params[0] is the user who did the whois and params.back() is
+ * the number of seconds 'issuer' has been idle.
+ */
+
+ User* target = ServerInstance->FindUUID(params[0]);
+ if ((!target) || (IS_SERVER(target) || (target->registered != REG_ALL)))
+ return CMD_FAILURE;
+
+ LocalUser* localtarget = IS_LOCAL(target);
+ if (!localtarget)
{
- // an incoming request
- if (params.size() == 1)
- {
- User* x = ServerInstance->FindNick(params[0]);
- if ((x) && (IS_LOCAL(x)))
- {
- long idle = labs((long)((x->idle_lastmsg) - ServerInstance->Time()));
- parameterlist par;
- par.push_back(prefix);
- par.push_back(ConvToStr(x->signon));
- par.push_back(ConvToStr(idle));
- // ours, we're done, pass it BACK
- Utils->DoOneToOne(params[0], "IDLE", par, u->server);
- }
- else
- {
- // not ours pass it on
- if (x)
- Utils->DoOneToOne(prefix, "IDLE", params, x->server);
- }
- }
- else if (params.size() == 3)
- {
- std::string who_did_the_whois = params[0];
- User* who_to_send_to = ServerInstance->FindNick(who_did_the_whois);
- if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)) && (who_to_send_to->registered == REG_ALL))
- {
- // an incoming reply to a whois we sent out
- std::string nick_whoised = prefix;
- unsigned long signon = atoi(params[1].c_str());
- unsigned long idle = atoi(params[2].c_str());
- if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
- {
- ServerInstance->DoWhois(who_to_send_to, u, signon, idle, nick_whoised.c_str());
- }
- }
- else
- {
- // not ours, pass it on
- if (who_to_send_to)
- Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
- }
- }
+ // Forward to target's server
+ return CMD_SUCCESS;
}
- return true;
-}
+ if (params.size() >= 2)
+ {
+ ServerInstance->Parser.CallHandler("WHOIS", params, issuer);
+ }
+ else
+ {
+ // A server is asking us the idle time of our user
+ unsigned int idle;
+ if (localtarget->idle_lastmsg >= ServerInstance->Time())
+ // Possible case when our clock ticked backwards
+ idle = 0;
+ else
+ idle = ((unsigned int) (ServerInstance->Time() - localtarget->idle_lastmsg));
+
+ CmdBuilder reply(params[0], "IDLE");
+ reply.push_back(issuer->uuid);
+ reply.push_back(ConvToStr(target->signon));
+ reply.push_back(ConvToStr(idle));
+ reply.Unicast(issuer);
+ }
+ return CMD_SUCCESS;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "commands.h"
+#include "utils.h"
+#include "treeserver.h"
+#include "treesocket.h"
+
+CmdResult CommandIJoin::HandleRemote(RemoteUser* user, std::vector<std::string>& params)
+{
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // Desync detected, recover
+ // Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received IJOIN for non-existant channel: " + params[0]);
+
+ CmdBuilder("RESYNC").push(params[0]).Unicast(user);
+
+ return CMD_FAILURE;
+ }
+
+ bool apply_modes;
+ if (params.size() > 2)
+ {
+ time_t RemoteTS = ServerCommand::ExtractTS(params[2]);
+ if (RemoteTS < chan->age)
+ throw ProtocolException("Attempted to lower TS via IJOIN. LocalTS=" + ConvToStr(chan->age));
+ apply_modes = ((params.size() > 3) && (RemoteTS == chan->age));
+ }
+ else
+ apply_modes = false;
+
+ // Join the user and set the membership id to what they sent
+ Membership* memb = chan->ForceJoin(user, apply_modes ? ¶ms[3] : NULL);
+ if (!memb)
+ return CMD_FAILURE;
+
+ memb->id = Membership::IdFromString(params[1]);
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandResync::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resyncing " + params[0]);
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // This can happen for a number of reasons, safe to ignore
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel does not exist");
+ return CMD_FAILURE;
+ }
+
+ if (!server->IsLocal())
+ throw ProtocolException("RESYNC from a server that is not directly connected");
+
+ // Send all known information about the channel
+ server->GetSocket()->SyncChannel(chan);
+ return CMD_SUCCESS;
+}
*/
-#ifndef M_SPANNINGTREE_LINK_H
-#define M_SPANNINGTREE_LINK_H
+#pragma once
class Link : public refcountbase
{
std::string SendPass;
std::string RecvPass;
std::string Fingerprint;
- std::string AllowMask;
+ std::vector<std::string> AllowMasks;
bool HiddenFromStats;
std::string Hook;
int Timeout;
int position;
Autoconnect(ConfigTag* Tag) : tag(Tag) {}
};
-
-#endif
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "socket.h"
#include "xline.h"
+#include "iohook.h"
-#include "cachetimer.h"
#include "resolvers.h"
#include "main.h"
#include "utils.h"
#include "link.h"
#include "treesocket.h"
#include "commands.h"
-#include "protocolinterface.h"
+#include "translate.h"
ModuleSpanningTree::ModuleSpanningTree()
- : KeepNickTS(false)
+ : rconnect(this), rsquit(this), map(this)
+ , commands(NULL)
+ , currmembid(0)
+ , DNS(this, "DNS")
+ , loopCall(false)
{
- Utils = new SpanningTreeUtilities(this);
- commands = new SpanningTreeCommands(this);
- RefreshTimer = NULL;
}
SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
- : rconnect(module, module->Utils), rsquit(module, module->Utils),
- svsjoin(module), svspart(module), svsnick(module), metadata(module),
- uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
- fhost(module), fident(module), fname(module)
+ : svsjoin(module), svspart(module), svsnick(module), metadata(module),
+ uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
+ fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
+ away(module), addline(module), delline(module), encap(module), idle(module),
+ nick(module), ping(module), pong(module), push(module), save(module),
+ server(module), squit(module), snonotice(module),
+ endburst(module), sinfo(module)
+{
+}
+
+namespace
{
+ void SetLocalUsersServer(Server* newserver)
+ {
+ // Does not change the server of quitting users because those are not in the list
+
+ ServerInstance->FakeClient->server = newserver;
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ (*i)->server = newserver;
+ }
+
+ void ResetMembershipIds()
+ {
+ // Set all membership ids to 0
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ for (User::ChanList::iterator j = user->chans.begin(); j != user->chans.end(); ++j)
+ (*j)->id = 0;
+ }
+ }
}
void ModuleSpanningTree::init()
{
- ServerInstance->Modules->AddService(commands->rconnect);
- ServerInstance->Modules->AddService(commands->rsquit);
- ServerInstance->Modules->AddService(commands->svsjoin);
- ServerInstance->Modules->AddService(commands->svspart);
- ServerInstance->Modules->AddService(commands->svsnick);
- ServerInstance->Modules->AddService(commands->metadata);
- ServerInstance->Modules->AddService(commands->uid);
- ServerInstance->Modules->AddService(commands->opertype);
- ServerInstance->Modules->AddService(commands->fjoin);
- ServerInstance->Modules->AddService(commands->fmode);
- ServerInstance->Modules->AddService(commands->ftopic);
- ServerInstance->Modules->AddService(commands->fhost);
- ServerInstance->Modules->AddService(commands->fident);
- ServerInstance->Modules->AddService(commands->fname);
- RefreshTimer = new CacheRefreshTimer(Utils);
- ServerInstance->Timers->AddTimer(RefreshTimer);
-
- Implementation eventlist[] =
- {
- I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
- I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
- I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
- I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
- I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
- I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
- };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
-
- delete ServerInstance->PI;
- ServerInstance->PI = new SpanningTreeProtocolInterface(Utils);
- loopCall = false;
-
- // update our local user count
- Utils->TreeRoot->SetUserCount(ServerInstance->Users->local_users.size());
+ ServerInstance->SNO->EnableSnomask('l', "LINK");
+
+ ResetMembershipIds();
+
+ Utils = new SpanningTreeUtilities(this);
+ Utils->TreeRoot = new TreeServer;
+ commands = new SpanningTreeCommands(this);
+
+ ServerInstance->PI = &protocolinterface;
+
+ delete ServerInstance->FakeClient->server;
+ SetLocalUsersServer(Utils->TreeRoot);
}
void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
{
Parent = Current->GetParent()->GetName();
}
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName()))))
+ TreeServer* server = *i;
+ if ((server->Hidden) || ((Utils->HideULines) && (server->IsULine())))
{
- if (IS_OPER(user))
+ if (user->IsOper())
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
else
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
/* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
- if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!IS_OPER(user)))
+ if ((Utils->HideULines) && (Current->IsULine()) && (!user->IsOper()))
return;
/* Or if the server is hidden and they're not an oper */
- else if ((Current->Hidden) && (!IS_OPER(user)))
+ else if ((Current->Hidden) && (!user->IsOper()))
return;
- std::string servername = Current->GetName();
- user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), servername.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
+ user->WriteNumeric(RPL_LINKS, "%s %s :%d %s", Current->GetName().c_str(),
+ (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
+ (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
Current->GetDesc().c_str());
}
-int ModuleSpanningTree::CountServs()
-{
- return Utils->serverlist.size();
-}
-
void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
{
ShowLinks(Utils->TreeRoot,user,0);
- user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
- return;
+ user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
}
std::string ModuleSpanningTree::TimeToStr(time_t secs)
{
TreeServer *s = i->second;
- if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING)
- {
- s->GetSocket()->Close();
- goto restart;
- }
+ // Skip myself
+ if (s->IsRoot())
+ continue;
- // Fix for bug #792, do not ping servers that are not connected yet!
- // Remote servers have Socket == NULL and local connected servers have
- // Socket->LinkState == CONNECTED
- if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
+ // Do not ping servers that are not fully connected yet!
+ // Servers which are connected to us have IsLocal() == true and if they're fully connected
+ // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
+ if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
continue;
// Now do PING checks on all servers
- TreeServer *mts = Utils->BestRouteTo(s->GetID());
-
- if (mts)
+ // Only ping if this server needs one
+ if (curtime >= s->NextPingTime())
{
- // Only ping if this server needs one
- if (curtime >= s->NextPingTime())
+ // And if they answered the last
+ if (s->AnsweredLastPing())
{
- // And if they answered the last
- if (s->AnsweredLastPing())
- {
- // They did, send a ping to them
- s->SetNextPingTime(curtime + Utils->PingFreq);
- TreeSocket *tsock = mts->GetSocket();
-
- // ... if we can find a proper route to them
- if (tsock)
- {
- tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " +
- ServerInstance->Config->GetSID() + " " + s->GetID());
- s->LastPingMsec = ts;
- }
- }
- else
+ // They did, send a ping to them
+ s->SetNextPingTime(curtime + Utils->PingFreq);
+ s->GetSocket()->WriteLine(CmdBuilder("PING").push(s->GetID()));
+ s->LastPingMsec = ts;
+ }
+ else
+ {
+ // They didn't answer the last ping, if they are locally connected, get rid of them.
+ if (s->IsLocal())
{
- // They didn't answer the last ping, if they are locally connected, get rid of them.
- TreeSocket *sock = s->GetSocket();
- if (sock)
- {
- sock->SendError("Ping timeout");
- sock->Close();
- goto restart;
- }
+ TreeSocket* sock = s->GetSocket();
+ sock->SendError("Ping timeout");
+ sock->Close();
+ goto restart;
}
}
+ }
- // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
- if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
- {
- /* The server hasnt responded, send a warning to opers */
- std::string servername = s->GetName();
- ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime);
- s->Warned = true;
- }
+ // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
+ if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
+ {
+ /* The server hasnt responded, send a warning to opers */
+ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
+ s->Warned = true;
}
}
}
return;
}
- QueryType start_type = DNS_QUERY_AAAA;
+ DNS::QueryType start_type = DNS::QUERY_AAAA;
if (strchr(x->IPAddr.c_str(),':'))
{
in6_addr n;
if (ipvalid)
{
/* Gave a hook, but it wasnt one we know */
- TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr);
+ TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
if (newsocket->GetFd() > -1)
{
/* Handled automatically on success */
ServerInstance->GlobalCulls.AddItem(newsocket);
}
}
+ else if (!DNS)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str());
+ }
else
{
+ ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
try
{
- bool cached = false;
- ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
- ServerInstance->AddResolver(snr, cached);
+ DNS->Process(snr);
}
- catch (ModuleException& e)
+ catch (DNS::Exception& e)
{
- ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
+ delete snr;
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason().c_str());
ConnectServer(y, false);
}
}
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
- std::string Version = found->GetVersion();
- user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
if (found == Utils->TreeRoot)
{
- ServerInstance->Config->Send005(user);
+ // Pass to default VERSION handler.
+ return MOD_RES_PASSTHRU;
}
+
+ // If an oper wants to see the version then show the full version string instead of the normal,
+ // but only if it is non-empty.
+ // If it's empty it might be that the server is still syncing (full version hasn't arrived yet)
+ // or the server is a 2.0 server and does not send a full version.
+ bool showfull = ((user->IsOper()) && (!found->GetFullVersion().empty()));
+ const std::string& Version = (showfull ? found->GetFullVersion() : found->GetVersion());
+ user->WriteNumeric(RPL_VERSION, ":%s", Version.c_str());
}
else
{
- user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
}
return MOD_RES_DENY;
}
*/
void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
{
- char text[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, format);
- vsnprintf(text, MAXBUF, format, argsPtr);
- va_end(argsPtr);
+ std::string text;
+ VAFORMAT(text, format, format);
if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
+ user->WriteNotice(text);
else
ServerInstance->PI->SendUserNotice(user, text);
}
}
else
{
- std::string servername = CheckDupe->GetParent()->GetName();
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.c_str());
+ RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_DENY;
}
-void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
-{
- TreeServer* s = Utils->FindServer(servername);
- if (s)
- {
- description = s->GetDesc();
- }
-}
-
void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
{
if (IS_LOCAL(source))
{
- parameterlist params;
+ CmdBuilder params(source, "INVITE");
params.push_back(dest->uuid);
params.push_back(channel->name);
+ params.push_int(channel->age);
params.push_back(ConvToStr(expiry));
- Utils->DoOneToMany(source->uuid,"INVITE",params);
+ params.Broadcast();
}
}
if (!IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(chan->name);
- params.push_back(":"+topic);
- Utils->DoOneToMany(user->uuid,"TOPIC",params);
+ CommandFTopic::Builder(user, chan).Broadcast();
}
-void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
+void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
{
- if (IS_LOCAL(user))
- {
- parameterlist params;
- params.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"WALLOPS",params);
- }
-}
-
-void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
-{
- /* Server origin */
- if (user == NULL)
- return;
-
- if (target_type == TYPE_USER)
- {
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && IS_LOCAL(user))
- {
- parameterlist params;
- params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
- }
- }
- else if (target_type == TYPE_CHANNEL)
- {
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
- }
- }
- }
- }
- else if (target_type == TYPE_SERVER)
- {
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"NOTICE",par);
- }
- }
-}
-
-void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
-{
- /* Server origin */
- if (user == NULL)
+ if (!IS_LOCAL(user))
return;
+ const char* message_type = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
if (target_type == TYPE_USER)
{
- // route private messages which are targetted at clients only to the server
- // which needs to receive them
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && (IS_LOCAL(user)))
+ User* d = (User*) dest;
+ if (!IS_LOCAL(d))
{
- parameterlist params;
+ CmdBuilder params(user, message_type);
params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+ params.push_last(text);
+ params.Unicast(d);
}
}
else if (target_type == TYPE_CHANNEL)
{
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
- }
- }
- }
+ Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type);
}
else if (target_type == TYPE_SERVER)
{
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
- }
+ char* target = (char*) dest;
+ CmdBuilder par(user, message_type);
+ par.push_back(target);
+ par.push_last(text);
+ par.Broadcast();
}
}
if (user->quitting)
return;
- parameterlist params;
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- params.push_back(user->nick);
- params.push_back(user->host);
- params.push_back(user->dhost);
- params.push_back(user->ident);
- params.push_back(user->GetIPString());
- params.push_back(ConvToStr(user->signon));
- params.push_back("+"+std::string(user->FormatModes(true)));
- params.push_back(":"+user->fullname);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
+ CommandUID::Builder(user).Broadcast();
- if (IS_OPER(user))
- {
- params.clear();
- params.push_back(user->oper->name);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
- }
+ if (user->IsOper())
+ CommandOpertype::Builder(user).Broadcast();
for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
{
ServerInstance->PI->SendMetaData(user, item->name, value);
}
- Utils->TreeRoot->SetUserCount(1); // increment by 1
+ Utils->TreeRoot->UserCount++;
}
-void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
{
// Only do this for local users
- if (IS_LOCAL(memb->user))
+ if (!IS_LOCAL(memb->user))
+ return;
+
+ // Assign the current membership id to the new Membership and increase it
+ memb->id = currmembid++;
+
+ if (created_by_local)
{
- parameterlist params;
- // set up their permissions and the channel TS with FJOIN.
- // All users are FJOINed now, because a module may specify
- // new joining permissions for the user.
+ CommandFJoin::Builder params(memb->chan);
+ params.add(memb);
+ params.finalize();
+ params.Broadcast();
+ }
+ else
+ {
+ CmdBuilder params(memb->user, "IJOIN");
params.push_back(memb->chan->name);
- params.push_back(ConvToStr(memb->chan->age));
- params.push_back(std::string("+") + memb->chan->ChanModes(true));
- params.push_back(memb->modes+","+memb->user->uuid);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ params.push_int(memb->id);
+ if (!memb->modes.empty())
+ {
+ params.push_back(ConvToStr(memb->chan->age));
+ params.push_back(memb->modes);
+ }
+ params.Broadcast();
}
}
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(newhost);
- Utils->DoOneToMany(user->uuid,"FHOST",params);
+ CmdBuilder(user, "FHOST").push(newhost).Broadcast();
}
void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(":" + gecos);
- Utils->DoOneToMany(user->uuid,"FNAME",params);
+ CmdBuilder(user, "FNAME").push_last(gecos).Broadcast();
}
void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(ident);
- Utils->DoOneToMany(user->uuid,"FIDENT",params);
+ CmdBuilder(user, "FIDENT").push(ident).Broadcast();
}
void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
{
if (IS_LOCAL(memb->user))
{
- parameterlist params;
+ CmdBuilder params(memb->user, "PART");
params.push_back(memb->chan->name);
if (!partmessage.empty())
- params.push_back(":"+partmessage);
- Utils->DoOneToMany(memb->user->uuid,"PART",params);
+ params.push_last(partmessage);
+ params.Broadcast();
}
}
void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
{
- if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ if (IS_LOCAL(user))
{
- parameterlist params;
-
if (oper_message != reason)
+ ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
+
+ CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
+ }
+ else
+ {
+ // Hide the message if one of the following is true:
+ // - User is being quit due to a netsplit and quietbursts is on
+ // - Server is a silent uline
+ TreeServer* server = TreeServer::Get(user);
+ bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine()));
+ if (!hide)
{
- params.push_back(":"+oper_message);
- Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
+ ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
+ user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str());
}
- params.clear();
- params.push_back(":"+reason);
- Utils->DoOneToMany(user->uuid,"QUIT",params);
}
// Regardless, We need to modify the user Counts..
- TreeServer* SourceServer = Utils->FindServer(user->server);
- if (SourceServer)
- {
- SourceServer->SetUserCount(-1); // decrement by 1
- }
+ TreeServer::Get(user)->UserCount--;
}
void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
{
if (IS_LOCAL(user))
{
- parameterlist params;
+ // The nick TS is updated by the core, we don't do it
+ CmdBuilder params(user, "NICK");
params.push_back(user->nick);
-
- /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
- */
- if ((irc::string(user->nick.c_str()) != assign(oldnick)) && (!this->KeepNickTS))
- user->age = ServerInstance->Time();
-
params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(user->uuid,"NICK",params);
- this->KeepNickTS = false;
+ params.Broadcast();
}
- else if (!loopCall && user->nick == user->uuid)
+ else if (!loopCall)
{
- parameterlist params;
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Changed nick of remote user %s from %s to %s TS %lu by ourselves!", user->uuid.c_str(), oldnick.c_str(), user->nick.c_str(), (unsigned long) user->age);
}
}
void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
{
- parameterlist params;
+ if ((!IS_LOCAL(source)) && (source != ServerInstance->FakeClient))
+ return;
+
+ CmdBuilder params(source, "KICK");
params.push_back(memb->chan->name);
params.push_back(memb->user->uuid);
- params.push_back(":"+reason);
- if (IS_LOCAL(source))
- {
- Utils->DoOneToMany(source->uuid,"KICK",params);
- }
- else if (source == ServerInstance->FakeClient)
- {
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
- }
-}
-
-void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
-{
- if (!IS_LOCAL(source))
- return; // Only start routing if we're origin.
-
- ServerInstance->OperQuit.set(dest, operreason);
- parameterlist params;
- params.push_back(":"+operreason);
- Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
- params.clear();
- params.push_back(dest->uuid);
- params.push_back(":"+reason);
- Utils->DoOneToMany(source->uuid,"KILL",params);
+ // If a remote user is being kicked by us then send the membership id in the kick too
+ if (!IS_LOCAL(memb->user))
+ params.push_int(memb->id);
+ params.push_last(reason);
+ params.Broadcast();
}
void ModuleSpanningTree::OnPreRehash(User* user, const std::string ¶meter)
if (loopCall)
return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
- ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
// Send out to other servers
if (!parameter.empty() && parameter[0] != '-')
{
- parameterlist params;
+ CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
params.push_back(parameter);
- Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
+ params.Forward(user ? TreeServer::Get(user)->GetRoute() : NULL);
}
}
-void ModuleSpanningTree::OnRehash(User* user)
+void ModuleSpanningTree::ReadConfig(ConfigStatus& status)
{
+ // Did this rehash change the description of this server?
+ const std::string& newdesc = ServerInstance->Config->ServerDesc;
+ if (newdesc != Utils->TreeRoot->GetDesc())
+ {
+ // Broadcast a SINFO desc message to let the network know about the new description. This is the description
+ // string that is sent in the SERVER message initially and shown for example in WHOIS.
+ // We don't need to update the field itself in the Server object - the core does that.
+ CommandSInfo::Builder(Utils->TreeRoot, "desc", newdesc).Broadcast();
+ }
+
// Re-read config stuff
try
{
std::string msg = "Error in configuration: ";
msg.append(e.GetReason());
ServerInstance->SNO->WriteToSnoMask('l', msg);
- if (user && !IS_LOCAL(user))
- ServerInstance->PI->SendSNONotice("L", msg);
+ if (status.srcuser && !IS_LOCAL(status.srcuser))
+ ServerInstance->PI->SendSNONotice('L', msg);
}
}
data.push_back('=');
data.append(v.link_data);
}
- ServerInstance->PI->SendMetaData(NULL, "modules", data);
+ ServerInstance->PI->SendMetaData("modules", data);
}
void ModuleSpanningTree::OnUnloadModule(Module* mod)
{
- ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
+ if (!Utils)
+ return;
+ ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile);
restart:
- unsigned int items = Utils->TreeRoot->ChildCount();
- for(unsigned int x = 0; x < items; x++)
+ // Close all connections which use an IO hook provided by this module
+ const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
{
- TreeServer* srv = Utils->TreeRoot->GetChild(x);
- TreeSocket* sock = srv->GetSocket();
- if (sock && sock->GetIOHook() == mod)
+ TreeSocket* sock = (*i)->GetSocket();
+ if (sock->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
{
sock->SendError("SSL module unloaded");
sock->Close();
- // XXX: The list we're iterating is modified by TreeSocket::Squit() which is called by Close()
+ // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close()
goto restart;
}
}
for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
{
TreeSocket* sock = i->first;
- if (sock->GetIOHook() == mod)
+ if (sock->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
sock->Close();
}
}
{
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(opertype);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
+ CommandOpertype::Builder(user).Broadcast();
}
void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
- params.push_back(ServerInstance->Config->ServerName);
- params.push_back(ConvToStr(x->set_time));
- params.push_back(ConvToStr(x->duration));
- params.push_back(":" + x->reason);
-
if (!user)
- {
- /* Server-set lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-set lines */
- Utils->DoOneToMany(user->uuid, "ADDLINE", params);
- }
+ user = ServerInstance->FakeClient;
+
+ CommandAddLine::Builder(x, user).Broadcast();
}
void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
-
if (!user)
- {
- /* Server-unset lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-unset lines */
- Utils->DoOneToMany(user->uuid, "DELLINE", params);
- }
-}
-
-void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
-{
- if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
- {
- parameterlist params;
- std::string output_text;
-
- ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
+ user = ServerInstance->FakeClient;
- if (target_type == TYPE_USER)
- {
- User* u = (User*)dest;
- params.push_back(u->uuid);
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "MODE", params);
- }
- else
- {
- Channel* c = (Channel*)dest;
- params.push_back(c->name);
- params.push_back(ConvToStr(c->age));
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "FMODE", params);
- }
- }
+ CmdBuilder params(user, "DELLINE");
+ params.push_back(x->type);
+ params.push_back(x->Displayable());
+ params.Broadcast();
}
ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
{
if (IS_LOCAL(user))
- {
- parameterlist params;
- if (!awaymsg.empty())
- {
- params.push_back(ConvToStr(ServerInstance->Time()));
- params.push_back(":" + awaymsg);
- }
- Utils->DoOneToMany(user->uuid, "AWAY", params);
- }
+ CommandAway::Builder(user, awaymsg).Broadcast();
return MOD_RES_PASSTHRU;
}
-void ModuleSpanningTree::OnRequest(Request& request)
-{
- if (!strcmp(request.id, "rehash"))
- Utils->Rehash();
-}
-
-void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
+void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode)
{
- TreeSocket* s = (TreeSocket*)opaque;
- std::string output_text;
+ if (processflags & ModeParser::MODE_LOCALONLY)
+ return;
- ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
+ if (u)
+ {
+ if (u->registered != REG_ALL)
+ return;
- if (target)
+ CmdBuilder params(source, "MODE");
+ params.push(u->uuid);
+ params.push(output_mode);
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
+ }
+ else
{
- if (target_type == TYPE_USER)
- {
- User* u = (User*)target;
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text);
- }
- else if (target_type == TYPE_CHANNEL)
- {
- Channel* c = (Channel*)target;
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
- }
+ CmdBuilder params(source, "FMODE");
+ params.push(c->name);
+ params.push_int(c->age);
+ params.push(output_mode);
+ params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
+ params.Broadcast();
}
}
-void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata)
-{
- TreeSocket* s = static_cast<TreeSocket*>(opaque);
- User* u = dynamic_cast<User*>(target);
- Channel* c = dynamic_cast<Channel*>(target);
- if (u)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
- else if (c)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
- else if (!target)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
-}
-
CullResult ModuleSpanningTree::cull()
{
- Utils->cull();
- ServerInstance->Timers->DelTimer(RefreshTimer);
+ if (Utils)
+ Utils->cull();
return this->Module::cull();
}
ModuleSpanningTree::~ModuleSpanningTree()
{
- delete ServerInstance->PI;
- ServerInstance->PI = new ProtocolInterface;
+ ServerInstance->PI = &ServerInstance->DefaultProtocolInterface;
+
+ Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
+ SetLocalUsersServer(newsrv);
/* This will also free the listeners */
delete Utils;
*/
-#ifndef M_SPANNINGTREE_MAIN_H
-#define M_SPANNINGTREE_MAIN_H
+#pragma once
#include "inspircd.h"
-#include <stdarg.h>
+#include "modules/dns.h"
+#include "servercommand.h"
+#include "commands.h"
+#include "protocolinterface.h"
/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
* Failure to document your protocol changes will result in a painfully
* painful death by pain. You have been warned.
*/
-const long ProtocolVersion = 1202;
-const long MinCompatProtocol = 1201;
+const long ProtocolVersion = 1205;
+const long MinCompatProtocol = 1202;
/** Forward declarations
*/
*/
class ModuleSpanningTree : public Module
{
+ /** Client to server commands, registered in the core
+ */
+ CommandRConnect rconnect;
+ CommandRSQuit rsquit;
+ CommandMap map;
+
+ /** Server to server only commands, not registered in the core
+ */
SpanningTreeCommands* commands;
+ /** Next membership id assigned when a local user joins a channel
+ */
+ Membership::Id currmembid;
+
+ /** The specialized ProtocolInterface that is assigned to ServerInstance->PI on load
+ */
+ SpanningTreeProtocolInterface protocolinterface;
+
public:
- SpanningTreeUtilities* Utils;
+ dynamic_reference<DNS::Manager> DNS;
+
+ ServerCommandManager CmdManager;
- CacheRefreshTimer *RefreshTimer;
/** Set to true if inside a spanningtree call, to prevent sending
* xlines and other things back to their source
*/
bool loopCall;
- /** If true OnUserPostNick() won't update the nick TS before sending the NICK,
- * used when handling SVSNICK.
- */
- bool KeepNickTS;
-
/** Constructor
*/
ModuleSpanningTree();
- void init();
+ void init() CXX11_OVERRIDE;
/** Shows /LINKS
*/
void ShowLinks(TreeServer* Current, User* user, int hops);
- /** Counts local and remote servers
- */
- int CountServs();
-
/** Handle LINKS command
*/
void HandleLinks(const std::vector<std::string>& parameters, User* user);
- /** Show MAP output to a user (recursive)
- */
- void ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats);
-
- /** Handle MAP command
- */
- bool HandleMap(const std::vector<std::string>& parameters, User* user);
-
/** Handle SQUIT
*/
ModResult HandleSquit(const std::vector<std::string>& parameters, User* user);
*/
void RemoteMessage(User* user, const char* format, ...) CUSTOM_PRINTF(3, 4);
- /** Returns oper-specific MAP information
- */
- const std::string MapOperInfo(TreeServer* Current);
-
/** Display a time as a human readable string
*/
- std::string TimeToStr(time_t secs);
+ static std::string TimeToStr(time_t secs);
/**
** *** MODULE EVENTS ***
**/
- ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
- void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
- void OnGetServerDescription(const std::string &servername,std::string &description);
- void OnUserConnect(LocalUser* source);
- void OnUserInvite(User* source,User* dest,Channel* channel, time_t);
- void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
- void OnWallops(User* user, const std::string &text);
- void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
- void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
- void OnBackgroundTimer(time_t curtime);
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts);
- void OnChangeHost(User* user, const std::string &newhost);
- void OnChangeName(User* user, const std::string &gecos);
- void OnChangeIdent(User* user, const std::string &ident);
- void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts);
- void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message);
- void OnUserPostNick(User* user, const std::string &oldnick);
- void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts);
- void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
- void OnPreRehash(User* user, const std::string ¶meter);
- void OnRehash(User* user);
- void OnOper(User* user, const std::string &opertype);
- void OnLine(User* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
- void OnAddLine(User *u, XLine *x);
- void OnDelLine(User *u, XLine *x);
- void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
- ModResult OnStats(char statschar, User* user, string_list &results);
- ModResult OnSetAway(User* user, const std::string &awaymsg);
- void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
- void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
- void OnLoadModule(Module* mod);
- void OnUnloadModule(Module* mod);
- ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
- void OnRequest(Request& request);
+ ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
+ void OnPostCommand(Command*, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line) CXX11_OVERRIDE;
+ void OnUserConnect(LocalUser* source) CXX11_OVERRIDE;
+ void OnUserInvite(User* source,User* dest,Channel* channel, time_t) CXX11_OVERRIDE;
+ void OnPostTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE;
+ void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE;
+ void OnChangeHost(User* user, const std::string &newhost) CXX11_OVERRIDE;
+ void OnChangeName(User* user, const std::string &gecos) CXX11_OVERRIDE;
+ void OnChangeIdent(User* user, const std::string &ident) CXX11_OVERRIDE;
+ void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE;
+ void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE;
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE;
+ void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE;
+ void OnPreRehash(User* user, const std::string ¶meter) CXX11_OVERRIDE;
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
+ void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE;
+ void OnAddLine(User *u, XLine *x) CXX11_OVERRIDE;
+ void OnDelLine(User *u, XLine *x) CXX11_OVERRIDE;
+ ModResult OnStats(char statschar, User* user, string_list &results) CXX11_OVERRIDE;
+ ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE;
+ void OnLoadModule(Module* mod) CXX11_OVERRIDE;
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
+ ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
+ void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode) CXX11_OVERRIDE;
CullResult cull();
~ModuleSpanningTree();
- Version GetVersion();
+ Version GetVersion() CXX11_OVERRIDE;
void Prioritize();
};
-
-#endif
#include "inspircd.h"
#include "commands.h"
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-CmdResult CommandMetadata::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandMetadata::Handle(User* srcuser, std::vector<std::string>& params)
{
- std::string value = params.size() < 3 ? "" : params[2];
- ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
if (params[0] == "*")
{
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(NULL,params[1],value));
+ std::string value = params.size() < 3 ? "" : params[2];
+ FOREACH_MOD(OnDecodeMetaData, (NULL,params[1],value));
+ return CMD_SUCCESS;
}
- else if (*(params[0].c_str()) == '#')
+
+ if (params[0][0] == '#')
{
+ // Channel METADATA has an additional parameter: the channel TS
+ // :22D METADATA #channel 12345 extname :extdata
+ if (params.size() < 3)
+ throw ProtocolException("Insufficient parameters for channel METADATA");
+
Channel* c = ServerInstance->FindChan(params[0]);
- if (c)
- {
- if (item)
- item->unserialize(FORMAT_NETWORK, c, value);
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[1],value));
- }
+ if (!c)
+ return CMD_FAILURE;
+
+ time_t ChanTS = ServerCommand::ExtractTS(params[1]);
+ if (c->age < ChanTS)
+ // Their TS is newer than ours, discard this command and do not propagate
+ return CMD_FAILURE;
+
+ std::string value = params.size() < 4 ? "" : params[3];
+
+ ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]);
+ if (item)
+ item->unserialize(FORMAT_NETWORK, c, value);
+ FOREACH_MOD(OnDecodeMetaData, (c,params[2],value));
}
- else if (*(params[0].c_str()) != '#')
+ else
{
User* u = ServerInstance->FindUUID(params[0]);
if ((u) && (!IS_SERVER(u)))
{
+ ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
+ std::string value = params.size() < 3 ? "" : params[2];
+
if (item)
item->unserialize(FORMAT_NETWORK, u, value);
- FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(u,params[1],value));
+ FOREACH_MOD(OnDecodeMetaData, (u,params[1],value));
}
}
return CMD_SUCCESS;
}
+CommandMetadata::Builder::Builder(User* user, const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push(user->uuid);
+ push(key);
+ push_last(val);
+}
+
+CommandMetadata::Builder::Builder(Channel* chan, const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push(key);
+ push_last(val);
+}
+
+CommandMetadata::Builder::Builder(const std::string& key, const std::string& val)
+ : CmdBuilder("METADATA")
+{
+ push("*");
+ push(key);
+ push_last(val);
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandSNONotice::Handle(User* user, std::vector<std::string>& params)
+{
+ ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + user->nick + ": " + params[1]);
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandEndBurst::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+ server->FinishBurst();
+ return CMD_SUCCESS;
+}
#include "inspircd.h"
#include "xline.h"
+#include "listmode.h"
#include "treesocket.h"
#include "treeserver.h"
-#include "utils.h"
#include "main.h"
+#include "commands.h"
+
+/**
+ * Creates FMODE messages, used only when syncing channels
+ */
+class FModeBuilder : public CmdBuilder
+{
+ static const size_t maxline = 480;
+ std::string params;
+ unsigned int modes;
+ std::string::size_type startpos;
+
+ public:
+ FModeBuilder(Channel* chan)
+ : CmdBuilder("FMODE"), modes(0)
+ {
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ startpos = str().size();
+ }
+
+ /** Add a mode to the message
+ */
+ void push_mode(const char modeletter, const std::string& mask)
+ {
+ push_raw(modeletter);
+ params.push_back(' ');
+ params.append(mask);
+ modes++;
+ }
+
+ /** Remove all modes from the message
+ */
+ void clear()
+ {
+ content.erase(startpos);
+ params.clear();
+ modes = 0;
+ }
+
+ /** Prepare the message for sending, next mode can only be added after clear()
+ */
+ const std::string& finalize()
+ {
+ return push_raw(params);
+ }
+
+ /** Returns true if the given mask can be added to the message, false if the message
+ * has no room for the mask
+ */
+ bool has_room(const std::string& mask) const
+ {
+ return ((str().size() + params.size() + mask.size() + 2 <= maxline) &&
+ (modes < ServerInstance->Config->Limits.MaxModes));
+ }
+
+ /** Returns true if this message is empty (has no modes)
+ */
+ bool empty() const
+ {
+ return (modes == 0);
+ }
+};
+
+struct TreeSocket::BurstState
+{
+ SpanningTreeProtocolInterface::Server server;
+ BurstState(TreeSocket* sock) : server(sock) { }
+};
/** This function is called when we want to send a netburst to a local
* server. There is a set order we must do this, because for example
*/
void TreeSocket::DoBurst(TreeServer* s)
{
- std::string servername = s->GetName();
ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s%s).",
- servername.c_str(),
- capab->auth_fingerprint ? "SSL Fingerprint and " : "",
+ s->GetName().c_str(),
+ capab->auth_fingerprint ? "SSL certificate fingerprint and " : "",
capab->auth_challenge ? "challenge-response" : "plaintext password");
this->CleanNegotiationInfo();
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " BURST " + ConvToStr(ServerInstance->Time()));
- /* send our version string */
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " VERSION :"+ServerInstance->GetVersionString());
+ this->WriteLine(CmdBuilder("BURST").push_int(ServerInstance->Time()));
/* Send server tree */
- this->SendServers(Utils->TreeRoot,s,1);
+ this->SendServers(Utils->TreeRoot, s);
+
+ BurstState bs(this);
/* Send users and their oper status */
- this->SendUsers();
- /* Send everything else (channel modes, xlines etc) */
- this->SendChannelModes();
+ this->SendUsers(bs);
+
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ SyncChannel(i->second, bs);
+
this->SendXLines();
- FOREACH_MOD(I_OnSyncNetwork,OnSyncNetwork(Utils->Creator,(void*)this));
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST");
+ FOREACH_MOD(OnSyncNetwork, (bs.server));
+ this->WriteLine(CmdBuilder("ENDBURST"));
ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+
+ this->burstsent = true;
}
-/** Recursively send the server tree with distances as hops.
+void TreeSocket::SendServerInfo(TreeServer* from)
+{
+ // Send public version string
+ this->WriteLine(CommandSInfo::Builder(from, "version", from->GetVersion()));
+
+ // Send full version string that contains more information and is shown to opers
+ this->WriteLine(CommandSInfo::Builder(from, "fullversion", from->GetFullVersion()));
+}
+
+/** Recursively send the server tree.
* This is used during network burst to inform the other server
* (and any of ITS servers too) of what servers we know about.
* If at any point any of these servers already exist on the other
- * end, our connection may be terminated. The hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
+ * end, our connection may be terminated.
*/
-void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
+void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
{
- char command[MAXBUF];
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ SendServerInfo(Current);
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- TreeServer* recursive_server = Current->GetChild(q);
+ TreeServer* recursive_server = *i;
if (recursive_server != s)
{
- std::string recursive_servername = recursive_server->GetName();
- snprintf(command, MAXBUF, ":%s SERVER %s * %d %s :%s", Current->GetID().c_str(), recursive_servername.c_str(), hops,
- recursive_server->GetID().c_str(),
- recursive_server->GetDesc().c_str());
- this->WriteLine(command);
- this->WriteLine(":"+recursive_server->GetID()+" VERSION :"+recursive_server->GetVersion());
+ this->WriteLine(CommandServer::Builder(recursive_server));
/* down to next level */
- this->SendServers(recursive_server, s, hops+1);
+ this->SendServers(recursive_server, s);
}
}
}
/** Send one or more FJOINs for a channel of users.
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
+ * Send one or more FMODEs for a channel with the
+ * channel bans, if there's any.
*/
void TreeSocket::SendFJoins(Channel* c)
{
- std::string buffer;
- char list[MAXBUF];
-
- size_t curlen, headlen;
- curlen = headlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu +%s :",
- ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->age, c->ChanModes(true));
- int numusers = 0;
- char* ptr = list + curlen;
- bool looped_once = false;
-
- const UserMembList *ulist = c->GetUsers();
- std::string modes;
- std::string params;
-
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
- {
- size_t ptrlen = 0;
- std::string modestr = i->second->modes;
-
- if ((curlen + modestr.length() + i->first->uuid.length() + 4) > 480)
- {
- // remove the final space
- if (ptr[-1] == ' ')
- ptr[-1] = '\0';
- buffer.append(list).append("\r\n");
- curlen = headlen;
- ptr = list + headlen;
- numusers = 0;
- }
-
- ptrlen = snprintf(ptr, MAXBUF-curlen, "%s,%s ", modestr.c_str(), i->first->uuid.c_str());
-
- looped_once = true;
-
- curlen += ptrlen;
- ptr += ptrlen;
-
- numusers++;
- }
-
- // Okay, permanent channels will (of course) need this \r\n anyway, numusers check is if there
- // actually were people in the channel (looped_once == true)
- if (!looped_once || numusers > 0)
- {
- // remove the final space
- if (ptr[-1] == ' ')
- ptr[-1] = '\0';
- buffer.append(list).append("\r\n");
- }
+ CommandFJoin::Builder fjoin(c);
- int linesize = 1;
- for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- int size = b->data.length() + 2;
- int currsize = linesize + size;
- if (currsize <= 350)
- {
- modes.append("b");
- params.append(" ").append(b->data);
- linesize += size;
- }
- if ((modes.length() >= ServerInstance->Config->Limits.MaxModes) || (currsize > 350))
+ Membership* memb = i->second;
+ if (!fjoin.has_room(memb))
{
- /* Wrap at MAXMODES */
- buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
- modes.clear();
- params.clear();
- linesize = 1;
+ // No room for this user, send the line and prepare a new one
+ this->WriteLine(fjoin.finalize());
+ fjoin.clear();
}
+ fjoin.add(memb);
}
-
- /* Only send these if there are any */
- if (!modes.empty())
- buffer.append(":").append(ServerInstance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
-
- this->WriteLine(buffer);
+ this->WriteLine(fjoin.finalize());
}
/** Send all XLines we know about */
void TreeSocket::SendXLines()
{
- char data[MAXBUF];
- std::string n = ServerInstance->Config->GetSID();
- const char* sn = n.c_str();
-
std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
- time_t current = ServerInstance->Time();
- for (std::vector<std::string>::iterator it = types.begin(); it != types.end(); ++it)
+ for (std::vector<std::string>::const_iterator it = types.begin(); it != types.end(); ++it)
{
+ /* Expired lines are removed in XLineManager::GetAll() */
XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
+ /* lookup cannot be NULL in this case but a check won't hurt */
if (lookup)
{
for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
if (!i->second->IsBurstable())
break;
- /* If it's expired, don't bother to burst it
- */
- if (i->second->duration && current > i->second->expiry)
- continue;
-
- snprintf(data,MAXBUF,":%s ADDLINE %s %s %s %lu %lu :%s",sn, it->c_str(), i->second->Displayable(),
- i->second->source.c_str(),
- (unsigned long)i->second->set_time,
- (unsigned long)i->second->duration,
- i->second->reason.c_str());
- this->WriteLine(data);
+ this->WriteLine(CommandAddLine::Builder(i->second));
}
}
}
}
-/** Send channel topic, modes and metadata */
-void TreeSocket::SendChannelModes()
+void TreeSocket::SendListModes(Channel* chan)
{
- char data[MAXBUF];
- std::string n = ServerInstance->Config->GetSID();
- const char* sn = n.c_str();
-
- for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
+ FModeBuilder fmode(chan);
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
{
- SendFJoins(c->second);
- if (!c->second->topic.empty())
+ ListModeBase* mh = *i;
+ ListModeBase::ModeList* list = mh->GetList(chan);
+ if (!list)
+ continue;
+
+ // Add all items on the list to the FMODE, send it whenever it becomes too long
+ const char modeletter = mh->GetModeChar();
+ for (ListModeBase::ModeList::const_iterator j = list->begin(); j != list->end(); ++j)
{
- snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s", sn, c->second->name.c_str(), (unsigned long)c->second->topicset, c->second->setby.c_str(), c->second->topic.c_str());
- this->WriteLine(data);
+ const std::string& mask = j->mask;
+ if (!fmode.has_room(mask))
+ {
+ // No room for this mask, send the current line as-is then add the mask to a
+ // new, empty FMODE message
+ this->WriteLine(fmode.finalize());
+ fmode.clear();
+ }
+ fmode.push_mode(modeletter, mask);
}
+ }
- for(Extensible::ExtensibleStore::const_iterator i = c->second->GetExtList().begin(); i != c->second->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_NETWORK, c->second, i->second);
- if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, c->second, item->name, value);
- }
+ if (!fmode.empty())
+ this->WriteLine(fmode.finalize());
+}
- FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this));
+/** Send channel topic, modes and metadata */
+void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
+{
+ SendFJoins(chan);
+
+ // If the topic was ever set, send it, even if it's empty now
+ // because a new empty topic should override an old non-empty topic
+ if (chan->topicset != 0)
+ this->WriteLine(CommandFTopic::Builder(chan));
+
+ SendListModes(chan);
+
+ for (Extensible::ExtensibleStore::const_iterator i = chan->GetExtList().begin(); i != chan->GetExtList().end(); i++)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_NETWORK, chan, i->second);
+ if (!value.empty())
+ this->WriteLine(CommandMetadata::Builder(chan, item->name, value));
}
+
+ FOREACH_MOD(OnSyncChannel, (chan, bs.server));
+}
+
+void TreeSocket::SyncChannel(Channel* chan)
+{
+ BurstState bs(this);
+ SyncChannel(chan, bs);
}
/** send all users and their oper state/modes */
-void TreeSocket::SendUsers()
+void TreeSocket::SendUsers(BurstState& bs)
{
- char data[MAXBUF];
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ ProtocolInterface::Server& piserver = bs.server;
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
{
- if (u->second->registered == REG_ALL)
- {
- TreeServer* theirserver = Utils->FindServer(u->second->server);
- if (theirserver)
- {
- snprintf(data,MAXBUF,":%s UID %s %lu %s %s %s %s %s %lu +%s :%s",
- theirserver->GetID().c_str(), /* Prefix: SID */
- u->second->uuid.c_str(), /* 0: UUID */
- (unsigned long)u->second->age, /* 1: TS */
- u->second->nick.c_str(), /* 2: Nick */
- u->second->host.c_str(), /* 3: Displayed Host */
- u->second->dhost.c_str(), /* 4: Real host */
- u->second->ident.c_str(), /* 5: Ident */
- u->second->GetIPString(), /* 6: IP string */
- (unsigned long)u->second->signon, /* 7: Signon time for WHOWAS */
- u->second->FormatModes(true), /* 8...n: Modes and params */
- u->second->fullname.c_str()); /* size-1: GECOS */
- this->WriteLine(data);
- if (IS_OPER(u->second))
- {
- snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->uuid.c_str(), u->second->oper->name.c_str());
- this->WriteLine(data);
- }
- if (IS_AWAY(u->second))
- {
- snprintf(data,MAXBUF,":%s AWAY %ld :%s", u->second->uuid.c_str(), (long)u->second->awaytime, u->second->awaymsg.c_str());
- this->WriteLine(data);
- }
- }
+ User* user = u->second;
+ if (user->registered != REG_ALL)
+ continue;
- for(Extensible::ExtensibleStore::const_iterator i = u->second->GetExtList().begin(); i != u->second->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
- if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, u->second, item->name, value);
- }
+ this->WriteLine(CommandUID::Builder(user));
+
+ if (user->IsOper())
+ this->WriteLine(CommandOpertype::Builder(user));
+
+ if (user->IsAway())
+ this->WriteLine(CommandAway::Builder(user));
- FOREACH_MOD(I_OnSyncUser,OnSyncUser(u->second,Utils->Creator,this));
+ const Extensible::ExtensibleStore& exts = user->GetExtList();
+ for (Extensible::ExtensibleStore::const_iterator i = exts.begin(); i != exts.end(); ++i)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
+ if (!value.empty())
+ this->WriteLine(CommandMetadata::Builder(user, item->name, value));
}
+
+ FOREACH_MOD(OnSyncUser, (user, piserver));
}
}
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2007-2008, 2012 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "main.h"
+#include "utils.h"
+#include "commands.h"
+#include "treeserver.h"
+
+CmdResult CommandNick::HandleRemote(RemoteUser* user, std::vector<std::string>& params)
+{
+ if ((isdigit(params[0][0])) && (params[0] != user->uuid))
+ throw ProtocolException("Attempted to change nick to an invalid or non-matching UUID");
+
+ // Timestamp of the new nick
+ time_t newts = ServerCommand::ExtractTS(params[1]);
+
+ /*
+ * On nick messages, check that the nick doesn't already exist here.
+ * If it does, perform collision logic.
+ */
+ User* x = ServerInstance->FindNickOnly(params[0]);
+ if ((x) && (x != user) && (x->registered == REG_ALL))
+ {
+ // 'x' is the already existing user using the same nick as params[0]
+ // 'user' is the user trying to change nick to the in use nick
+ bool they_change = Utils->DoCollision(x, TreeServer::Get(user), newts, user->ident, user->GetIPString(), user->uuid);
+ if (they_change)
+ {
+ // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
+ // calling ChangeNick() and forwarding the message
+ params[0] = user->uuid;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
+ newts = CommandSave::SavedTimestamp;
+ }
+ }
+
+ user->ChangeNick(params[0], newts);
+
+ return CMD_SUCCESS;
+}
#include "inspircd.h"
-#include "xline.h"
#include "treesocket.h"
#include "treeserver.h"
#include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
+#include "commandbuilder.h"
+#include "commands.h"
/*
* Yes, this function looks a little ugly.
* However, in some circumstances we may not have a User, so we need to do things this way.
- * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both.
- * Sends SAVEs as appropriate and forces nickchanges too.
+ * Returns true if remote or both lost, false otherwise.
+ * Sends SAVEs as appropriate and forces nick change of the user 'u' if our side loses or if both lose.
+ * Does not change the nick of the user that is trying to claim the nick of 'u', i.e. the "remote" user.
*/
-int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid)
+bool SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid)
{
+ // At this point we're sure that a collision happened, increment the counter regardless of who wins
+ ServerInstance->stats.Collisions++;
+
/*
* Under old protocol rules, we would have had to kill both clients.
* Really, this sucks.
bool bChangeLocal = true;
bool bChangeRemote = true;
- /* for brevity, don't use the User - use defines to avoid any copy */
- #define localts u->age
- #define localident u->ident
- #define localip u->GetIPString()
-
- /* mmk. let's do this again. */
- if (remotets == localts)
+ // If the timestamps are not equal only one of the users has to change nick,
+ // otherwise both have to change
+ const time_t localts = u->age;
+ if (remotets != localts)
{
- /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
- }
- else
- {
- /* fuck. now it gets complex. */
-
/* first, let's see if ident@host matches. */
+ const std::string& localident = u->ident;
+ const std::string& localip = u->GetIPString();
bool SamePerson = (localident == remoteident)
&& (localip == remoteip);
if((SamePerson && remotets < localts) ||
(!SamePerson && remotets > localts))
{
- /* remote needs to change */
+ // Only remote needs to change
bChangeLocal = false;
}
else
{
- /* ours needs to change */
+ // Only ours needs to change
bChangeRemote = false;
}
}
/*
- * Cheat a little here. Instead of a dedicated command to change UID,
- * use SAVE and accept the losing client with its UID (as we know the SAVE will
+ * Send SAVE and accept the losing client with its UID (as we know the SAVE will
* not fail under any circumstances -- UIDs are netwide exclusive).
*
* This means that each side of a collide will generate one extra NICK back to where
{
/*
* Local-side nick needs to change. Just in case we are hub, and
- * this "local" nick is actually behind us, send an SAVE out.
+ * this "local" nick is actually behind us, send a SAVE out.
*/
- parameterlist params;
+ CmdBuilder params("SAVE");
params.push_back(u->uuid);
params.push_back(ConvToStr(u->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
+ params.Broadcast();
- u->ForceNickChange(u->uuid.c_str());
-
- if (!bChangeRemote)
- return 1;
+ u->ChangeNick(u->uuid, CommandSave::SavedTimestamp);
}
if (bChangeRemote)
{
- User *remote = ServerInstance->FindUUID(remoteuid);
/*
- * remote side needs to change. If this happens, we will modify
- * the UID or halt the propagation of the nick change command,
- * so other servers don't need to see the SAVE
+ * Remote side needs to change. If this happens, we modify the UID or NICK and
+ * send back a SAVE to the source.
*/
- WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets));
-
- if (remote)
- {
- /* nick change collide. Force change their nick. */
- remote->ForceNickChange(remoteuid.c_str());
- }
-
- if (!bChangeLocal)
- return 2;
+ CmdBuilder("SAVE").push(remoteuid).push_int(remotets).Unicast(server->ServerUser);
}
- return 3;
+ return bChangeRemote;
}
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "xline.h"
-
-#include "treesocket.h"
-#include "treeserver.h"
-#include "utils.h"
-
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-
-bool TreeSocket::OperQuit(const std::string &prefix, parameterlist ¶ms)
-{
- if (params.size() < 1)
- return true;
-
- User* u = ServerInstance->FindUUID(prefix);
-
- if ((u) && (!IS_SERVER(u)))
- {
- ServerInstance->OperQuit.set(u, params[0]);
- params[0] = ":" + params[0];
- Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
- }
- return true;
-}
-
/** Because the core won't let users or even SERVERS set +o,
* we use the OPERTYPE command to do this.
*/
-CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User *u)
+CmdResult CommandOpertype::HandleRemote(RemoteUser* u, std::vector<std::string>& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- std::string opertype = params[0];
- if (!IS_OPER(u))
+ const std::string& opertype = params[0];
+ if (!u->IsOper())
ServerInstance->Users->all_opers.push_back(u);
- u->modes[UM_OPERATOR] = 1;
- OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + opertype);
- if (iter != ServerInstance->Config->oper_blocks.end())
+
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ u->SetMode(opermh, true);
+
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(opertype);
+ if (iter != ServerInstance->Config->OperTypes.end())
u->oper = iter->second;
else
{
* If quiet bursts are enabled, and server is bursting or silent uline (i.e. services),
* then do nothing. -- w00t
*/
- TreeServer* remoteserver = Utils->FindServer(u->server);
- if (remoteserver->bursting || ServerInstance->SilentULine(u->server))
+ TreeServer* remoteserver = TreeServer::Get(u);
+ if (remoteserver->IsBehindBursting() || remoteserver->IsSilentULine())
return CMD_SUCCESS;
}
- ServerInstance->SNO->WriteToSnoMask('O',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server.c_str(), u->nick.c_str(),u->ident.c_str(), u->host.c_str(), irc::Spacify(opertype.c_str()));
+ ServerInstance->SNO->WriteToSnoMask('O',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server->GetName().c_str(), u->nick.c_str(),u->ident.c_str(), u->host.c_str(), opertype.c_str());
return CMD_SUCCESS;
}
+CommandOpertype::Builder::Builder(User* user)
+ : CmdBuilder(user, "OPERTYPE")
+{
+ push_last(user->oper->name);
+}
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
* Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
-const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
+CommandMap::CommandMap(Module* Creator)
+ : Command(Creator, "MAP", 0, 1)
{
- time_t secs_up = ServerInstance->Time() - Current->age;
- return " [Up: " + TimeToStr(secs_up) + (Current->rtt == 0 ? "]" : " Lag: " + ConvToStr(Current->rtt) + "ms]");
+ Penalty = 2;
}
-void ModuleSpanningTree::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats)
+static inline bool IsHidden(User* user, TreeServer* server)
{
- ServerInstance->Logs->Log("map",DEBUG,"ShowMap depth %d on line %d", depth, line);
- float percent;
-
- if (ServerInstance->Users->clientlist->size() == 0)
+ if (!user->IsOper())
{
- // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
- percent = 0;
+ if (server->Hidden)
+ return true;
+ if (Utils->HideULines && server->IsULine())
+ return true;
}
- else
+
+ return false;
+}
+
+// Calculate the map depth the servers go, and the longest server name
+static void GetDepthAndLen(TreeServer* current, unsigned int depth, unsigned int& max_depth, unsigned int& max_len)
+{
+ if (depth > max_depth)
+ max_depth = depth;
+ if (current->GetName().length() > max_len)
+ max_len = current->GetName().length();
+
+ const TreeServer::ChildServers& servers = current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
{
- percent = Current->GetUserCount() * 100.0 / ServerInstance->Users->clientlist->size();
+ TreeServer* child = *i;
+ GetDepthAndLen(child, depth + 1, max_depth, max_len);
}
+}
- const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
-
- char* myname = names + 100 * line;
- char* mystat = stats + 50 * line;
- memset(myname, ' ', depth);
- int w = depth;
+static std::vector<std::string> GetMap(User* user, TreeServer* current, unsigned int max_len, unsigned int depth)
+{
+ float percent = 0;
- std::string servername = Current->GetName();
- if (IS_OPER(user))
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ if (!users.empty())
{
- w += snprintf(myname + depth, 99 - depth, "%s (%s)", servername.c_str(), Current->GetID().c_str());
+ // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
+ percent = current->UserCount * 100.0 / users.size();
}
- else
+
+ std::string buffer = current->GetName();
+ if (user->IsOper())
{
- w += snprintf(myname + depth, 99 - depth, "%s", servername.c_str());
+ buffer += " (" + current->GetID() + ")";
}
- memset(myname + w, ' ', 100 - w);
- if (w > maxnamew)
- maxnamew = w;
- snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->GetUserCount(), percent, operdata.c_str());
- line++;
+ // Pad with spaces until its at max len, max_len must always be >= my names length
+ buffer.append(max_len - current->GetName().length(), ' ');
- if (IS_OPER(user) || !Utils->FlatLinks)
- depth = depth + 2;
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%5d [%5.2f%%]", current->UserCount, percent);
+ buffer += buf;
+
+ if (user->IsOper())
{
- TreeServer* child = Current->GetChild(q);
- if (!IS_OPER(user)) {
- if (child->Hidden)
- continue;
- if ((Utils->HideULines) && (ServerInstance->ULine(child->GetName())))
- continue;
- }
- ShowMap(child, user, depth, line, names, maxnamew, stats);
+ time_t secs_up = ServerInstance->Time() - current->age;
+ buffer += " [Up: " + ModuleSpanningTree::TimeToStr(secs_up) + (current->rtt == 0 ? "]" : " Lag: " + ConvToStr(current->rtt) + "ms]");
}
-}
+ std::vector<std::string> map;
+ map.push_back(buffer);
-// Ok, prepare to be confused.
-// After much mulling over how to approach this, it struck me that
-// the 'usual' way of doing a /MAP isnt the best way. Instead of
-// keeping track of a ton of ascii characters, and line by line
-// under recursion working out where to place them using multiplications
-// and divisons, we instead render the map onto a backplane of characters
-// (a character matrix), then draw the branches as a series of "L" shapes
-// from the nodes. This is not only friendlier on CPU it uses less stack.
-bool ModuleSpanningTree::HandleMap(const std::vector<std::string>& parameters, User* user)
-{
- if (parameters.size() > 0)
+ const TreeServer::ChildServers& servers = current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i)
{
- /* Remote MAP, the server is within the 1st parameter */
- TreeServer* s = Utils->FindServerMask(parameters[0]);
- bool ret = false;
- if (!s)
+ TreeServer* child = *i;
+
+ if (IsHidden(user, child))
+ continue;
+
+ bool last = true;
+ for (TreeServer::ChildServers::const_iterator j = i + 1; last && j != servers.end(); ++j)
+ if (!IsHidden(user, *j))
+ last = false;
+
+ unsigned int next_len;
+
+ if (user->IsOper() || !Utils->FlatLinks)
{
- user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str());
- ret = true;
+ // This child is indented by us, so remove the depth from the max length to align the users properly
+ next_len = max_len - 2;
}
- else if (s && s != Utils->TreeRoot)
+ else
{
- parameterlist params;
- params.push_back(parameters[0]);
-
- params[0] = s->GetName();
- Utils->DoOneToOne(user->uuid, "MAP", params, s->GetName());
- ret = true;
+ // This user can not see depth, so max_len remains constant
+ next_len = max_len;
}
- // Don't return if s == Utils->TreeRoot (us)
- if (ret)
- return true;
- }
+ // Build the map for this child
+ std::vector<std::string> child_map = GetMap(user, child, next_len, depth + 1);
- // These arrays represent a virtual screen which we will
- // "scratch" draw to, as the console device of an irc
- // client does not provide for a proper terminal.
- int totusers = ServerInstance->Users->clientlist->size();
- int totservers = this->CountServs();
- int maxnamew = 0;
- int line = 0;
- char* names = new char[totservers * 100];
- char* stats = new char[totservers * 50];
-
- // The only recursive bit is called here.
- ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats);
-
- // Process each line one by one.
- for (int l = 1; l < line; l++)
- {
- char* myname = names + 100 * l;
- // scan across the line looking for the start of the
- // servername (the recursive part of the algorithm has placed
- // the servers at indented positions depending on what they
- // are related to)
- int first_nonspace = 0;
-
- while (myname[first_nonspace] == ' ')
+ for (std::vector<std::string>::const_iterator j = child_map.begin(); j != child_map.end(); ++j)
{
- first_nonspace++;
+ const char* prefix;
+
+ if (user->IsOper() || !Utils->FlatLinks)
+ {
+ // If this server is not the root child
+ if (j != child_map.begin())
+ {
+ // If this child is not my last child, then add |
+ // to be able to "link" the next server in my list to me, and to indent this childs servers
+ if (!last)
+ prefix = "| ";
+ // Otherwise this is my last child, so just use a space as theres nothing else linked to me below this
+ else
+ prefix = " ";
+ }
+ // If we get here, this server must be the root child
+ else
+ {
+ // If this is the last child, it gets a `-
+ if (last)
+ prefix = "`-";
+ // Otherwise this isn't the last child, so it gets |-
+ else
+ prefix = "|-";
+ }
+ }
+ else
+ // User can't see depth, so use no prefix
+ prefix = "";
+
+ // Add line to the map
+ map.push_back(prefix + *j);
}
+ }
- first_nonspace--;
-
- // Draw the `- (corner) section: this may be overwritten by
- // another L shape passing along the same vertical pane, becoming
- // a |- (branch) section instead.
-
- myname[first_nonspace] = '-';
- myname[first_nonspace-1] = '`';
- int l2 = l - 1;
+ return map;
+}
- // Draw upwards until we hit the parent server, causing possibly
- // other corners (`-) to become branches (|-)
- while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`'))
+CmdResult CommandMap::Handle(const std::vector<std::string>& parameters, User* user)
+{
+ if (parameters.size() > 0)
+ {
+ /* Remote MAP, the server is within the 1st parameter */
+ TreeServer* s = Utils->FindServerMask(parameters[0]);
+ if (!s)
{
- names[l2 * 100 + first_nonspace-1] = '|';
- l2--;
+ user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
+ return CMD_FAILURE;
}
+
+ if (!s->IsRoot())
+ return CMD_SUCCESS;
}
- float avg_users = totusers * 1.0 / line;
+ // Max depth and max server name length
+ unsigned int max_depth = 0;
+ unsigned int max_len = 0;
+ GetDepthAndLen(Utils->TreeRoot, 0, max_depth, max_len);
- ServerInstance->Logs->Log("map",DEBUG,"local");
- for (int t = 0; t < line; t++)
+ unsigned int max;
+ if (user->IsOper() || !Utils->FlatLinks)
+ {
+ // Each level of the map is indented by 2 characters, making the max possible line (max_depth * 2) + max_len
+ max = (max_depth * 2) + max_len;
+ }
+ else
{
- // terminate the string at maxnamew characters
- names[100 * t + maxnamew] = '\0';
- user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(),
- RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t);
+ // This user can't see any depth
+ max = max_len;
}
- user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server",
+
+ std::vector<std::string> map = GetMap(user, Utils->TreeRoot, max, 0);
+ for (std::vector<std::string>::const_iterator i = map.begin(); i != map.end(); ++i)
+ user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(),
+ RPL_MAP, user->nick.c_str(), i->c_str());
+
+ size_t totusers = ServerInstance->Users->GetUsers().size();
+ float avg_users = (float) totusers / Utils->serverlist.size();
+
+ user->SendText(":%s %03d %s :%u server%s and %u user%s, average %.2f users per server",
ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(),
- line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users);
+ (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users);
user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(),
RPL_ENDMAP, user->nick.c_str());
- delete[] names;
- delete[] stats;
-
- return true;
+ return CMD_SUCCESS;
}
+RouteDescriptor CommandMap::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ if (!parameters.empty())
+ return ROUTE_UNICAST(parameters[0]);
+ return ROUTE_LOCALONLY;
+}
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
#include "treesocket.h"
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
ModResult ModuleSpanningTree::HandleSquit(const std::vector<std::string>& parameters, User* user)
{
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
- if (s == Utils->TreeRoot)
+ if (s->IsRoot())
{
- user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (" + parameters[0] + " matches local server name)");
return MOD_RES_DENY;
}
- TreeSocket* sock = s->GetSocket();
-
- if (sock)
+ if (s->IsLocal())
{
ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0].c_str(),user->nick.c_str());
- sock->Squit(s,"Server quit by " + user->GetFullRealHost());
- ServerInstance->SE->DelFd(sock);
- sock->Close();
+ s->SQuit("Server quit by " + user->GetFullRealHost());
}
else
{
- user->WriteServ("NOTICE %s :*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.",user->nick.c_str());
+ user->WriteNotice("*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead.");
}
}
else
{
- user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SQUIT: The server \002" + parameters[0] + "\002 does not exist on the network.");
}
return MOD_RES_DENY;
}
-
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
#include "link.h"
-#include "treesocket.h"
ModResult ModuleSpanningTree::OnStats(char statschar, User* user, string_list &results)
{
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(L->HiddenFromStats ? "<hidden>" : L->IPAddr)+" * "+(*i)->Name.c_str()+" "+ConvToStr(L->Port)+" "+(L->Hook.empty() ? "plaintext" : L->Hook));
+ results.push_back("213 "+user->nick+" "+statschar+" *@"+(L->HiddenFromStats ? "<hidden>" : L->IPAddr)+" * "+(*i)->Name.c_str()+" "+ConvToStr(L->Port)+" "+(L->Hook.empty() ? "plaintext" : L->Hook));
if (statschar == 'c')
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+L->Name.c_str());
+ results.push_back("244 "+user->nick+" H * * "+L->Name.c_str());
+ }
+ return MOD_RES_DENY;
+ }
+ else if (statschar == 'U')
+ {
+ ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ std::string name = i->second->getString("server");
+ if (!name.empty())
+ results.push_back("248 "+user->nick+" U "+name);
}
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commandbuilder.h"
ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& parameters, User* user)
{
- if ((IS_LOCAL(user)) && (parameters.size() > 1))
+ User* remote = ServerInstance->FindNickOnly(parameters[1]);
+ if (remote && !IS_LOCAL(remote))
{
- User* remote = ServerInstance->FindNickOnly(parameters[1]);
- if (remote && !IS_LOCAL(remote))
- {
- parameterlist params;
- params.push_back(remote->uuid);
- Utils->DoOneToOne(user->uuid,"IDLE",params,remote->server);
- return MOD_RES_DENY;
- }
- else if (!remote)
- {
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
- user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), parameters[1].c_str());
- return MOD_RES_DENY;
- }
+ CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote);
+ return MOD_RES_DENY;
+ }
+ else if (!remote)
+ {
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[1].c_str());
+ user->WriteNumeric(RPL_ENDOFWHOIS, "%s :End of /WHOIS list.", parameters[1].c_str());
+ return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
-bool TreeSocket::LocalPing(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandPing::Handle(User* user, std::vector<std::string>& params)
{
- if (params.size() < 1)
- return true;
- if (params.size() == 1)
- {
- std::string stufftobounce = params[0];
- this->WriteLine(":"+ServerInstance->Config->GetSID()+" PONG "+stufftobounce);
- return true;
- }
- else
+ if (params[0] == ServerInstance->Config->GetSID())
{
- std::string forwardto = params[1];
- if (forwardto == ServerInstance->Config->ServerName || forwardto == ServerInstance->Config->GetSID())
- {
- // this is a ping for us, send back PONG to the requesting server
- params[1] = params[0];
- params[0] = forwardto;
- Utils->DoOneToOne(ServerInstance->Config->GetSID(),"PONG",params,params[1]);
- }
- else
- {
- // not for us, pass it on :)
- Utils->DoOneToOne(prefix,"PING",params,forwardto);
- }
- return true;
+ // PING for us, reply with a PONG
+ CmdBuilder reply("PONG");
+ reply.push_back(user->uuid);
+ if (params.size() >= 2)
+ // If there is a second parameter, append it
+ reply.push_back(params[1]);
+
+ reply.Unicast(user);
}
+ return CMD_SUCCESS;
}
-
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
+#include "utils.h"
-bool TreeSocket::LocalPong(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandPong::HandleServer(TreeServer* server, std::vector<std::string>& params)
{
- if (params.size() < 1)
- return true;
-
- if (params.size() == 1)
+ if (server->IsBursting())
{
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (ServerSource)
- {
- ServerSource->SetPingFlag();
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- ServerSource->rtt = ts - ServerSource->LastPingMsec;
- }
+ ServerInstance->SNO->WriteGlobalSno('l', "Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", server->GetName().c_str());
+ server->FinishBurst();
}
- else
- {
- std::string forwardto = params[1];
- if (forwardto == ServerInstance->Config->GetSID() || forwardto == ServerInstance->Config->ServerName)
- {
- /*
- * this is a PONG for us
- * if the prefix is a user, check theyre local, and if they are,
- * dump the PONG reply back to their fd. If its a server, do nowt.
- * Services might want to send these s->s, but we dont need to yet.
- */
- User* u = ServerInstance->FindNick(prefix);
- if (u)
- {
- u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
- }
- TreeServer *ServerSource = Utils->FindServer(params[0]);
-
- if (ServerSource)
- {
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- ServerSource->rtt = ts - ServerSource->LastPingMsec;
- ServerSource->SetPingFlag();
- }
- }
- else
- {
- // not for us, pass it on :)
- Utils->DoOneToOne(prefix,"PONG",params,forwardto);
- }
+ if (params[0] == ServerInstance->Config->GetSID())
+ {
+ // PONG for us
+ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ server->rtt = ts - server->LastPingMsec;
+ server->SetPingFlag();
}
-
- return true;
+ return CMD_SUCCESS;
}
-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
+#include "commandbuilder.h"
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-void ModuleSpanningTree::OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line)
+void ModuleSpanningTree::OnPostCommand(Command* command, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line)
{
if (result == CMD_SUCCESS)
Utils->RouteCommand(NULL, command, parameters, user);
}
-void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &command, const parameterlist& parameters, User *user)
+void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscmd, const parameterlist& parameters, User* user)
{
- if (!ServerInstance->Parser->IsValidCommand(command, parameters.size(), user))
- return;
-
- /* We know it's non-null because IsValidCommand returned true */
- Command* thiscmd = ServerInstance->Parser->GetHandler(command);
-
+ const std::string& command = thiscmd->name;
RouteDescriptor routing = thiscmd->GetRouting(user, parameters);
-
- std::string sent_cmd = command;
- parameterlist params;
-
if (routing.type == ROUTE_TYPE_LOCALONLY)
- {
- /* Broadcast when it's a core command with the default route descriptor and the source is a
- * remote user or a remote server
- */
+ return;
- Version ver = thiscmd->creator->GetVersion();
- if ((!(ver.Flags & VF_CORE)) || (IS_LOCAL(user)) || (IS_SERVER(user) == ServerInstance->FakeClient))
- return;
+ const bool encap = ((routing.type == ROUTE_TYPE_OPT_BCAST) || (routing.type == ROUTE_TYPE_OPT_UCAST));
+ CmdBuilder params(user, encap ? "ENCAP" : command.c_str());
+ TreeServer* sdest = NULL;
- routing = ROUTE_BROADCAST;
- }
- else if (routing.type == ROUTE_TYPE_OPT_BCAST)
+ if (routing.type == ROUTE_TYPE_OPT_BCAST)
{
- params.push_back("*");
+ params.push('*');
params.push_back(command);
- sent_cmd = "ENCAP";
}
- else if (routing.type == ROUTE_TYPE_OPT_UCAST)
+ else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
{
- TreeServer* sdest = FindServer(routing.serverdest);
+ sdest = static_cast<TreeServer*>(routing.server);
if (!sdest)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistant server %s",
- routing.serverdest.c_str());
- return;
+ sdest = FindServer(routing.serverdest);
+ if (!sdest)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistant server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
+ return;
+ }
+ }
+
+ if (encap)
+ {
+ params.push_back(sdest->GetID());
+ params.push_back(command);
}
- params.push_back(sdest->GetID());
- params.push_back(command);
- sent_cmd = "ENCAP";
}
else
{
if (!(ver.Flags & (VF_COMMON | VF_CORE)) && srcmodule != Creator)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Routed command %s from non-VF_COMMON module %s",
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Routed command %s from non-VF_COMMON module %s",
command.c_str(), srcmodule->ModuleSourceFile.c_str());
return;
}
}
- std::string output_text;
- ServerInstance->Parser->TranslateUIDs(thiscmd->translation, parameters, output_text, true, thiscmd);
+ std::string output_text = CommandParser::TranslateUIDs(thiscmd->translation, parameters, true, thiscmd);
params.push_back(output_text);
Channel* c = ServerInstance->FindChan(dest);
if (!c)
return;
- TreeServerList list;
// TODO OnBuildExemptList hook was here
- GetListOfServersForChannel(c,list,pfx, CUList());
- std::string data = ":" + user->uuid + " " + sent_cmd;
- for (unsigned int x = 0; x < params.size(); x++)
- data += " " + params[x];
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (origin && origin->GetSocket() == Sock)
- continue;
- if (Sock)
- Sock->WriteLine(data);
- }
+ CUList exempts;
+ SendChannelMessage(user->uuid, c, parameters[1], pfx, exempts, command.c_str(), origin ? origin->GetSocket() : NULL);
}
else if (dest[0] == '$')
{
- if (origin)
- DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
- else
- DoOneToMany(user->uuid, sent_cmd, params);
+ params.Forward(origin);
}
else
{
// user target?
User* d = ServerInstance->FindNick(dest);
- if (!d)
+ if (!d || IS_LOCAL(d))
return;
- TreeServer* tsd = BestRouteTo(d->server);
+ TreeServer* tsd = TreeServer::Get(d)->GetRoute();
if (tsd == origin)
// huh? no routing stuff around in a circle, please.
return;
- DoOneToOne(user->uuid, sent_cmd, params, d->server);
+ params.Unicast(d);
}
}
else if (routing.type == ROUTE_TYPE_BROADCAST || routing.type == ROUTE_TYPE_OPT_BCAST)
{
- if (origin)
- DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName());
- else
- DoOneToMany(user->uuid, sent_cmd, params);
+ params.Forward(origin);
}
else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
{
- if (origin && routing.serverdest == origin->GetName())
- return;
- DoOneToOne(user->uuid, sent_cmd, params, routing.serverdest);
+ params.Unicast(sdest->ServerUser);
}
}
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line)
{
{
return this->HandleSquit(parameters,user);
}
- else if (command == "MAP")
- {
- return this->HandleMap(parameters,user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
- }
else if (command == "LINKS")
{
this->HandleLinks(parameters,user);
}
else if ((command == "VERSION") && (parameters.size() > 0))
{
- this->HandleVersion(parameters,user);
- return MOD_RES_DENY;
+ return this->HandleVersion(parameters,user);
}
return MOD_RES_PASSTHRU;
}
-
#include "inspircd.h"
-#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
#include "protocolinterface.h"
+#include "commands.h"
/*
* For documentation on this class, see include/protocol.h.
*/
-void SpanningTreeProtocolInterface::GetServerList(ProtoServerList &sl)
+void SpanningTreeProtocolInterface::GetServerList(ServerList& sl)
{
- sl.clear();
for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
{
- ProtoServer ps;
+ ServerInfo ps;
ps.servername = i->second->GetName();
TreeServer* s = i->second->GetParent();
ps.parentname = s ? s->GetName() : "";
- ps.usercount = i->second->GetUserCount();
- ps.opercount = i->second->GetOperCount();
+ ps.usercount = i->second->UserCount;
+ ps.opercount = i->second->OperCount;
ps.gecos = i->second->GetDesc();
ps.latencyms = i->second->rtt;
sl.push_back(ps);
}
}
-bool SpanningTreeProtocolInterface::SendEncapsulatedData(const parameterlist &encap)
+bool SpanningTreeProtocolInterface::SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const parameterlist& params, User* source)
{
- if (encap[0].find_first_of("*?") != std::string::npos)
+ if (!source)
+ source = ServerInstance->FakeClient;
+
+ CmdBuilder encap(source, "ENCAP");
+
+ // Are there any wildcards in the target string?
+ if (targetmask.find_first_of("*?") != std::string::npos)
{
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ENCAP", encap);
- return true;
+ // Yes, send the target string as-is; servers will decide whether or not it matches them
+ encap.push(targetmask).push(cmd).insert(params).Broadcast();
}
- return Utils->DoOneToOne(ServerInstance->Config->GetSID(), "ENCAP", encap, encap[0]);
-}
-
-void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::string &key, const std::string &data)
-{
- parameterlist params;
-
- User* u = dynamic_cast<User*>(target);
- Channel* c = dynamic_cast<Channel*>(target);
- if (u)
- params.push_back(u->uuid);
- else if (c)
- params.push_back(c->name);
else
- params.push_back("*");
+ {
+ // No wildcards which means the target string has to be the name of a known server
+ TreeServer* server = Utils->FindServer(targetmask);
+ if (!server)
+ return false;
- params.push_back(key);
- params.push_back(":" + data);
+ // Use the SID of the target in the message instead of the server name
+ encap.push(server->GetID()).push(cmd).insert(params).Unicast(server->ServerUser);
+ }
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",params);
+ return true;
}
-void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
+void SpanningTreeProtocolInterface::BroadcastEncap(const std::string& cmd, const parameterlist& params, User* source, User* omit)
{
- parameterlist params;
+ if (!source)
+ source = ServerInstance->FakeClient;
- params.push_back(channel->name);
- params.push_back(ConvToStr(ServerInstance->Time()));
- params.push_back(ServerInstance->Config->ServerName);
- params.push_back(":" + topic);
-
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC", params);
+ // If omit is non-NULL we pass the route belonging to the user to Forward(),
+ // otherwise we pass NULL, which is equivalent to Broadcast()
+ TreeServer* server = (omit ? TreeServer::Get(omit)->GetRoute() : NULL);
+ CmdBuilder(source, "ENCAP * ").push_raw(cmd).insert(params).Forward(server);
}
-void SpanningTreeProtocolInterface::SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate)
+void SpanningTreeProtocolInterface::SendMetaData(User* u, const std::string& key, const std::string& data)
{
- if (modedata.empty())
- return;
-
- std::string outdata;
- ServerInstance->Parser->TranslateUIDs(translate, modedata, outdata);
-
- std::string uidtarget;
- ServerInstance->Parser->TranslateUIDs(TR_NICK, target, uidtarget);
-
- parameterlist outlist;
- outlist.push_back(uidtarget);
- outlist.push_back(outdata);
-
- User* a = ServerInstance->FindNick(uidtarget);
- if (a)
- {
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",outlist);
- return;
- }
- else
- {
- Channel* c = ServerInstance->FindChan(target);
- if (c)
- {
- outlist.insert(outlist.begin() + 1, ConvToStr(c->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",outlist);
- }
- }
+ CommandMetadata::Builder(u, key, data).Broadcast();
}
-void SpanningTreeProtocolInterface::SendSNONotice(const std::string &snomask, const std::string &text)
+void SpanningTreeProtocolInterface::SendMetaData(Channel* c, const std::string& key, const std::string& data)
{
- parameterlist p;
- p.push_back(snomask);
- p.push_back(":" + text);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "SNONOTICE", p);
+ CommandMetadata::Builder(c, key, data).Broadcast();
}
-void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
+void SpanningTreeProtocolInterface::SendMetaData(const std::string& key, const std::string& data)
{
- parameterlist p;
- p.push_back(target->uuid);
- p.push_back(":" + rawline);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", p, target->server);
+ CommandMetadata::Builder(key, data).Broadcast();
}
-void SpanningTreeProtocolInterface::SendChannel(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::Server::SendMetaData(const std::string& key, const std::string& data)
{
- std::string cname = target->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- CUList exempt_list;
- Utils->GetListOfServersForChannel(target,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(text);
- }
+ sock->WriteLine(CommandMetadata::Builder(key, data));
}
+void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
+{
+ CommandFTopic::Builder(ServerInstance->FakeClient, channel).Broadcast();
+}
-void SpanningTreeProtocolInterface::SendChannelPrivmsg(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
{
- SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" PRIVMSG "+target->name+" :"+text);
+ CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
}
-void SpanningTreeProtocolInterface::SendChannelNotice(Channel* target, char status, const std::string &text)
+void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
{
- SendChannel(target, status, ":" + ServerInstance->Config->GetSID()+" NOTICE "+target->name+" :"+text);
+ CmdBuilder("PUSH").push(target->uuid).push_last(rawline).Unicast(target);
}
-void SpanningTreeProtocolInterface::SendUserPrivmsg(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype)
{
- parameterlist p;
- p.push_back(target->uuid);
- p.push_back(":" + text);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PRIVMSG", p, target->server);
+ const char* cmd = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
+ CUList exempt_list;
+ Utils->SendChannelMessage(ServerInstance->Config->GetSID(), target, text, status, exempt_list, cmd);
}
-void SpanningTreeProtocolInterface::SendUserNotice(User* target, const std::string &text)
+void SpanningTreeProtocolInterface::SendMessage(User* target, const std::string& text, MessageType msgtype)
{
- parameterlist p;
+ CmdBuilder p(msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
p.push_back(target->uuid);
- p.push_back(":" + text);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "NOTICE", p, target->server);
+ p.push_last(text);
+ p.Unicast(target);
}
*/
-#ifndef M_SPANNINGTREE_PROTOCOLINTERFACE_H
-#define M_SPANNINGTREE_PROTOCOLINTERFACE_H
-
-class SpanningTreeUtilities;
-class ModuleSpanningTree;
+#pragma once
class SpanningTreeProtocolInterface : public ProtocolInterface
{
- SpanningTreeUtilities* Utils;
- void SendChannel(Channel* target, char status, const std::string &text);
public:
- SpanningTreeProtocolInterface(SpanningTreeUtilities* util) : Utils(util) { }
- virtual ~SpanningTreeProtocolInterface() { }
-
- virtual bool SendEncapsulatedData(const parameterlist &encap);
- virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data);
- virtual void SendTopic(Channel* channel, std::string &topic);
- virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &types);
- virtual void SendSNONotice(const std::string &snomask, const std::string &text);
- virtual void PushToClient(User* target, const std::string &rawline);
- virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text);
- virtual void SendChannelNotice(Channel* target, char status, const std::string &text);
- virtual void SendUserPrivmsg(User* target, const std::string &text);
- virtual void SendUserNotice(User* target, const std::string &text);
- virtual void GetServerList(ProtoServerList &sl);
-};
+ class Server : public ProtocolInterface::Server
+ {
+ TreeSocket* const sock;
-#endif
+ public:
+ Server(TreeSocket* s) : sock(s) { }
+ void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ };
+ bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const parameterlist& params, User* source) CXX11_OVERRIDE;
+ void BroadcastEncap(const std::string& cmd, const parameterlist& params, User* source, User* omit) CXX11_OVERRIDE;
+ void SendMetaData(User* user, const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendMetaData(Channel* chan, const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendMetaData(const std::string& key, const std::string& data) CXX11_OVERRIDE;
+ void SendTopic(Channel* channel, std::string &topic);
+ void SendSNONotice(char snomask, const std::string& text) CXX11_OVERRIDE;
+ void PushToClient(User* target, const std::string &rawline);
+ void SendMessage(Channel* target, char status, const std::string& text, MessageType msgtype);
+ void SendMessage(User* target, const std::string& text, MessageType msgtype);
+ void GetServerList(ServerList& sl);
+};
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
+#include "commands.h"
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::Push(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandPush::Handle(User* user, std::vector<std::string>& params)
{
- if (params.size() < 2)
- return true;
User* u = ServerInstance->FindNick(params[0]);
if (!u)
- return true;
+ return CMD_FAILURE;
if (IS_LOCAL(u))
{
u->Write(params[1]);
}
- else
- {
- // continue the raw onwards
- params[1] = ":" + params[1];
- Utils->DoOneToOne(prefix,"PUSH",params,u->server);
- }
- return true;
+ return CMD_SUCCESS;
}
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "resolvers.h"
#include "main.h"
#include "utils.h"
-#include "treeserver.h"
-#include "link.h"
-#include "treesocket.h"
#include "commands.h"
-CommandRConnect::CommandRConnect (Module* Creator, SpanningTreeUtilities* Util)
- : Command(Creator, "RCONNECT", 2), Utils(Util)
+CommandRConnect::CommandRConnect (Module* Creator)
+ : Command(Creator, "RCONNECT", 2)
{
flags_needed = 'o';
syntax = "<remote-server-mask> <target-server-mask>";
CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, User *user)
{
- if (IS_LOCAL(user))
+ /* First see if the server which is being asked to connect to another server in fact exists */
+ if (!Utils->FindServerMask(parameters[0]))
{
- if (!Utils->FindServerMask(parameters[0]))
- {
- user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick.c_str(),parameters[0].c_str(),parameters[1].c_str());
+ ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RCONNECT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str());
+ return CMD_FAILURE;
}
/* Is this aimed at our server? */
para.push_back(parameters[1]);
((ModuleSpanningTree*)(Module*)creator)->HandleConnect(para, user);
}
+ else
+ {
+ /* It's not aimed at our server, but if the request originates from our user
+ * acknowledge that we sent the request.
+ *
+ * It's possible that we're asking a server for something that makes no sense
+ * (e.g. connect to itself or to an already connected server), but we don't check
+ * for those conditions here, as ModuleSpanningTree::HandleConnect() (which will run
+ * on the target) does all the checking and error reporting.
+ */
+ if (IS_LOCAL(user))
+ {
+ user->WriteNotice("*** RCONNECT: Sending remote connect to \002 " + parameters[0] + "\002 to connect server \002" + parameters[1] + "\002.");
+ }
+ }
return CMD_SUCCESS;
}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
+#include "cachetimer.h"
#include "resolvers.h"
#include "main.h"
#include "utils.h"
#include "link.h"
#include "treesocket.h"
-/* $ModDep: m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
-
/** This class is used to resolve server hostnames during /connect and autoconnect.
* As of 1.1, the resolver system is seperated out from BufferedSocket, so we must do this
* resolver step first ourselves if we need it. This is totally nonblocking, and will
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
-ServernameResolver::ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac)
- : Resolver(hostname, qt, cached, Util->Creator), Utils(Util), query(qt), host(hostname), MyLink(x), myautoconnect(myac)
+ServernameResolver::ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac)
+ : DNS::Request(mgr, Utils->Creator, hostname, qt)
+ , query(qt), host(hostname), MyLink(x), myautoconnect(myac)
{
}
-void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void ServernameResolver::OnLookupComplete(const DNS::Query *r)
{
+ const DNS::ResourceRecord &ans_record = r->answers[0];
+
/* Initiate the connection, now that we have an IP to use.
* Passing a hostname directly to BufferedSocket causes it to
* just bail and set its FD to -1.
TreeServer* CheckDupe = Utils->FindServer(MyLink->Name.c_str());
if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
{
- TreeSocket* newsocket = new TreeSocket(Utils, MyLink, myautoconnect, result);
+ TreeSocket* newsocket = new TreeSocket(MyLink, myautoconnect, ans_record.rdata);
if (newsocket->GetFd() > -1)
{
/* We're all OK */
}
}
-void ServernameResolver::OnError(ResolverError e, const std::string &errormessage)
+void ServernameResolver::OnError(const DNS::Query *r)
{
/* Ooops! */
- if (query == DNS_QUERY_AAAA)
+ if (query == DNS::QUERY_AAAA)
{
- bool cached = false;
- ServernameResolver* snr = new ServernameResolver(Utils, host, MyLink, cached, DNS_QUERY_A, myautoconnect);
- ServerInstance->AddResolver(snr, cached);
- return;
+ ServernameResolver* snr = new ServernameResolver(this->manager, host, MyLink, DNS::QUERY_A, myautoconnect);
+ try
+ {
+ this->manager->Process(snr);
+ return;
+ }
+ catch (DNS::Exception &)
+ {
+ delete snr;
+ }
}
- ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), errormessage.c_str() );
+
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
Utils->Creator->ConnectServer(myautoconnect, false);
}
-SecurityIPResolver::SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt)
- : Resolver(hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt)
+SecurityIPResolver::SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt)
+ : DNS::Request(mgr, me, hostname, qt)
+ , MyLink(x), mine(me), host(hostname), query(qt)
{
}
-void SecurityIPResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void SecurityIPResolver::OnLookupComplete(const DNS::Query *r)
{
+ const DNS::ResourceRecord &ans_record = r->answers[0];
+
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i)
{
Link* L = *i;
if (L->IPAddr == host)
{
- Utils->ValidIPs.push_back(result);
+ Utils->ValidIPs.push_back(ans_record.rdata);
break;
}
}
}
-void SecurityIPResolver::OnError(ResolverError e, const std::string &errormessage)
+void SecurityIPResolver::OnError(const DNS::Query *r)
{
- if (query == DNS_QUERY_AAAA)
+ if (query == DNS::QUERY_AAAA)
{
- bool cached = false;
- SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, host, MyLink, cached, DNS_QUERY_A);
- ServerInstance->AddResolver(res, cached);
- return;
+ SecurityIPResolver* res = new SecurityIPResolver(mine, this->manager, host, MyLink, DNS::QUERY_A);
+ try
+ {
+ this->manager->Process(res);
+ return;
+ }
+ catch (DNS::Exception &)
+ {
+ delete res;
+ }
}
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Could not resolve IP associated with Link '%s': %s",
- MyLink->Name.c_str(),errormessage.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Could not resolve IP associated with Link '%s': %s",
+ MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str());
+}
+
+CacheRefreshTimer::CacheRefreshTimer()
+ : Timer(3600, true)
+{
+}
+
+bool CacheRefreshTimer::Tick(time_t TIME)
+{
+ Utils->RefreshIPCache();
+ return true;
}
*/
-#ifndef M_SPANNINGTREE_RESOLVERS_H
-#define M_SPANNINGTREE_RESOLVERS_H
+#pragma once
-#include "socket.h"
#include "inspircd.h"
-#include "xline.h"
+#include "modules/dns.h"
#include "utils.h"
#include "link.h"
/** Handle resolving of server IPs for the cache
*/
-class SecurityIPResolver : public Resolver
+class SecurityIPResolver : public DNS::Request
{
private:
reference<Link> MyLink;
- SpanningTreeUtilities* Utils;
Module* mine;
std::string host;
- QueryType query;
+ DNS::QueryType query;
public:
- SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt);
- void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
- void OnError(ResolverError e, const std::string &errormessage);
+ SecurityIPResolver(Module* me, DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt);
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE;
};
/** This class is used to resolve server hostnames during /connect and autoconnect.
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
-class ServernameResolver : public Resolver
+class ServernameResolver : public DNS::Request
{
private:
- SpanningTreeUtilities* Utils;
- QueryType query;
+ DNS::QueryType query;
std::string host;
reference<Link> MyLink;
reference<Autoconnect> myautoconnect;
public:
- ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac);
- void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
- void OnError(ResolverError e, const std::string &errormessage);
+ ServernameResolver(DNS::Manager* mgr, const std::string& hostname, Link* x, DNS::QueryType qt, Autoconnect* myac);
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE;
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE;
};
-
-#endif
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "treesocket.h"
#include "commands.h"
-CommandRSQuit::CommandRSQuit (Module* Creator, SpanningTreeUtilities* Util)
- : Command(Creator, "RSQUIT", 1), Utils(Util)
+CommandRSQuit::CommandRSQuit(Module* Creator)
+ : Command(Creator, "RSQUIT", 1)
{
flags_needed = 'o';
syntax = "<target-server-mask> [reason]";
CmdResult CommandRSQuit::Handle (const std::vector<std::string>& parameters, User *user)
{
TreeServer *server_target; // Server to squit
- TreeServer *server_linked; // Server target is linked to
server_target = Utils->FindServerMask(parameters[0]);
if (!server_target)
{
- user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick.c_str(), parameters[0].c_str());
+ ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RSQUIT: Server \002%s\002 isn't connected to the network!", parameters[0].c_str());
return CMD_FAILURE;
}
- if (server_target == Utils->TreeRoot)
+ if (server_target->IsRoot())
{
- NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+parameters[0]+" matches local server name)");
+ ((ModuleSpanningTree*)(Module*)creator)->RemoteMessage(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)", parameters[0].c_str());
return CMD_FAILURE;
}
- server_linked = server_target->GetParent();
-
- if (server_linked == Utils->TreeRoot)
+ if (server_target->IsLocal())
{
// We have been asked to remove server_target.
- TreeSocket* sock = server_target->GetSocket();
- if (sock)
- {
- const char *reason = parameters.size() == 2 ? parameters[1].c_str() : "No reason";
- ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s (%s)", parameters[0].c_str(), user->nick.c_str(), reason);
- sock->Squit(server_target, "Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
- sock->Close();
- }
+ const char* reason = parameters.size() == 2 ? parameters[1].c_str() : "No reason";
+ ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s (%s)", parameters[0].c_str(), user->nick.c_str(), reason);
+ server_target->SQuit("Server quit by " + user->GetFullRealHost() + " (" + reason + ")");
}
return CMD_SUCCESS;
{
return ROUTE_UNICAST(parameters[0]);
}
-
-// XXX use protocol interface instead of rolling our own :)
-void CommandRSQuit::NoticeUser(User* user, const std::string &msg)
-{
- if (IS_LOCAL(user))
- {
- user->WriteServ("NOTICE %s :%s",user->nick.c_str(),msg.c_str());
- }
- else
- {
- parameterlist params;
- params.push_back(user->nick);
- params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg);
- Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", params, user->server);
- }
-}
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
#include "utils.h"
-#include "treeserver.h"
#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
+#include "commands.h"
/**
* SAVE command - force nick change to UID on timestamp match
*/
-bool TreeSocket::ForceNick(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandSave::Handle(User* user, std::vector<std::string>& params)
{
- if (params.size() < 2)
- return true;
+ User* u = ServerInstance->FindUUID(params[0]);
+ if ((!u) || (IS_SERVER(u)))
+ return CMD_FAILURE;
- User* u = ServerInstance->FindNick(params[0]);
time_t ts = atol(params[1].c_str());
- if ((u) && (!IS_SERVER(u)) && (u->age == ts))
- {
- Utils->DoOneToAllButSender(prefix,"SAVE",params,prefix);
-
- if (!u->ForceNickChange(u->uuid.c_str()))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
- }
+ if (u->age == ts)
+ u->ChangeNick(u->uuid, SavedTimestamp);
- return true;
+ return CMD_SUCCESS;
}
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
#include "main.h"
#include "utils.h"
#include "link.h"
#include "treeserver.h"
#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h m_spanningtree/link.h */
+#include "commands.h"
/*
* Some server somewhere in the network introducing another server.
* -- w
*/
-bool TreeSocket::RemoteServer(const std::string &prefix, parameterlist ¶ms)
+CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, std::vector<std::string>& params)
{
- if (params.size() < 5)
- {
- SendError("Protocol error - Not enough parameters for SERVER command");
- return false;
- }
-
- std::string servername = params[0];
- // password is not used for a remote server
- // hopcount is not used (ever)
- std::string sid = params[3];
- std::string description = params[4];
- TreeServer* ParentOfThis = Utils->FindServer(prefix);
+ const std::string& servername = params[0];
+ const std::string& sid = params[1];
+ const std::string& description = params.back();
+ TreeSocket* socket = ParentOfThis->GetSocket();
- if (!ParentOfThis)
+ if (!InspIRCd::IsSID(sid))
{
- this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
- return false;
- }
- if (!ServerInstance->IsSID(sid))
- {
- this->SendError("Invalid format server ID: "+sid+"!");
- return false;
+ socket->SendError("Invalid format server ID: "+sid+"!");
+ return CMD_FAILURE;
}
TreeServer* CheckDupe = Utils->FindServer(servername);
if (CheckDupe)
{
- this->SendError("Server "+servername+" already exists!");
+ socket->SendError("Server "+servername+" already exists!");
ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+CheckDupe->GetName()+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, already exists. Closing link with " + ParentOfThis->GetName());
- return false;
+ return CMD_FAILURE;
}
CheckDupe = Utils->FindServer(sid);
if (CheckDupe)
{
- this->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
+ socket->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+servername+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName());
- return false;
+ return CMD_FAILURE;
}
Link* lnk = Utils->FindLink(servername);
- TreeServer *Node = new TreeServer(Utils, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false);
+ TreeServer* Node = new TreeServer(servername, description, sid, ParentOfThis, ParentOfThis->GetSocket(), lnk ? lnk->Hidden : false);
+
+ HandleExtra(Node, params);
- ParentOfThis->AddChild(Node);
- params[4] = ":" + params[4];
- Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")");
- return true;
+ return CMD_SUCCESS;
}
+void CommandServer::HandleExtra(TreeServer* newserver, const std::vector<std::string>& params)
+{
+ for (std::vector<std::string>::const_iterator i = params.begin() + 2; i != params.end() - 1; ++i)
+ {
+ const std::string& prop = *i;
+ std::string::size_type p = prop.find('=');
-/*
- * This is used after the other side of a connection has accepted our credentials.
- * They are then introducing themselves to us, BEFORE either of us burst. -- w
- */
-bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms)
+ std::string key = prop;
+ std::string val;
+ if (p != std::string::npos)
+ {
+ key.erase(p);
+ val = prop.substr(p+1);
+ }
+
+ if (key == "burst")
+ newserver->BeginBurst(ConvToInt(val));
+ }
+}
+
+Link* TreeSocket::AuthRemote(const parameterlist& params)
{
if (params.size() < 5)
{
SendError("Protocol error - Not enough parameters for SERVER command");
- return false;
+ return NULL;
}
irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
- int hops = atoi(params[2].c_str());
+ const std::string& sname = params[0];
+ const std::string& password = params[1];
+ const std::string& sid = params[3];
+ const std::string& description = params.back();
this->SendCapabilities(2);
- if (hops)
- {
- this->SendError("Server too far away for authentication");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
- return false;
- }
-
if (!ServerInstance->IsSID(sid))
{
this->SendError("Invalid format server ID: "+sid+"!");
- return false;
+ return NULL;
}
for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
continue;
}
- TreeServer* CheckDupe = Utils->FindServer(sname);
- if (CheckDupe)
- {
- std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>";
- SendError("Server "+sname+" already exists on server "+pname+"!");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname);
- return false;
- }
- CheckDupe = Utils->FindServer(sid);
- if (CheckDupe)
- {
- this->SendError("Server ID "+sid+" already exists on the network! You may want to specify the server ID for the server manually with <server:id> so they do not conflict.");
- ServerInstance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
- return false;
- }
+ if (!CheckDuplicate(sname, sid))
+ return NULL;
+
+ ServerInstance->SNO->WriteToSnoMask('l',"Verified server connection " + linkID + " ("+description+")");
+ return x;
+ }
+ this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
+ ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
+ return NULL;
+}
+
+/*
+ * This is used after the other side of a connection has accepted our credentials.
+ * They are then introducing themselves to us, BEFORE either of us burst. -- w
+ */
+bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms)
+{
+ const Link* x = AuthRemote(params);
+ if (x)
+ {
/*
* They're in WAIT_AUTH_2 (having accepted our credentials).
* Set our state to CONNECTED (since everything's peachy so far) and send our
* While we're at it, create a treeserver object so we know about them.
* -- w
*/
- this->LinkState = CONNECTED;
-
- Utils->timeoutlist.erase(this);
- linkID = sname;
-
- MyRoot = new TreeServer(Utils, sname, description, sid, Utils->TreeRoot, this, x->Hidden);
- Utils->TreeRoot->AddChild(MyRoot);
- this->DoBurst(MyRoot);
-
- params[4] = ":" + params[4];
-
- /* IMPORTANT: Take password/hmac hash OUT of here before we broadcast the introduction! */
- params[1] = "*";
- Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(),"SERVER",params,sname);
+ FinishAuth(params[0], params[3], params.back(), x->Hidden);
return true;
}
- this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
}
/* Check for fully initialized instances of the server by id */
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Looking for dupe SID %s", sid.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Looking for dupe SID %s", sid.c_str());
CheckDupe = Utils->FindServerID(sid);
if (CheckDupe)
*/
bool TreeSocket::Inbound_Server(parameterlist ¶ms)
{
- if (params.size() < 5)
- {
- SendError("Protocol error - Missing SID");
- return false;
- }
-
- irc::string servername = params[0].c_str();
- std::string sname = params[0];
- std::string password = params[1];
- std::string sid = params[3];
- std::string description = params[4];
- int hops = atoi(params[2].c_str());
-
- this->SendCapabilities(2);
-
- if (hops)
- {
- this->SendError("Server too far away for authentication");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
- return false;
- }
-
- if (!ServerInstance->IsSID(sid))
+ const Link* x = AuthRemote(params);
+ if (x)
{
- this->SendError("Invalid format server ID: "+sid+"!");
- return false;
- }
-
- for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
- {
- Link* x = *i;
- if (x->Name != servername && x->Name != "*") // open link allowance
- continue;
-
- if (!ComparePass(*x, password))
- {
- ServerInstance->SNO->WriteToSnoMask('l',"Invalid password on link: %s", x->Name.c_str());
- continue;
- }
-
- if (!CheckDuplicate(sname, sid))
- return false;
-
- ServerInstance->SNO->WriteToSnoMask('l',"Verified incoming server connection " + linkID + " ("+description+")");
-
- this->SendCapabilities(2);
-
// Save these for later, so when they accept our credentials (indicated by BURST) we remember them
this->capab->hidden = x->Hidden;
- this->capab->sid = sid;
- this->capab->description = description;
- this->capab->name = sname;
+ this->capab->sid = params[3];
+ this->capab->description = params.back();
+ this->capab->name = params[0];
// Send our details: Our server name and description and hopcount of 0,
// along with the sendpass from this block.
return true;
}
- this->SendError("Mismatched server name or password (check the other server's snomask output for details - e.g. umode +s +Ll)");
- ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
+CommandServer::Builder::Builder(TreeServer* server)
+ : CmdBuilder(server->GetParent()->GetID(), "SERVER")
+{
+ push(server->GetName());
+ push(server->GetID());
+ if (server->IsBursting())
+ push_property("burst", ConvToStr(server->StartBurst));
+ push_last(server->GetDesc());
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "main.h"
+#include "servercommand.h"
+
+ServerCommand::ServerCommand(Module* Creator, const std::string& Name, unsigned int MinParams, unsigned int MaxParams)
+ : CommandBase(Creator, Name, MinParams, MaxParams)
+{
+ this->ServiceProvider::DisableAutoRegister();
+ ModuleSpanningTree* st = static_cast<ModuleSpanningTree*>(Creator);
+ st->CmdManager.AddCommand(this);
+}
+
+RouteDescriptor ServerCommand::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ // Broadcast server-to-server commands unless overridden
+ return ROUTE_BROADCAST;
+}
+
+time_t ServerCommand::ExtractTS(const std::string& tsstr)
+{
+ time_t TS = ConvToInt(tsstr);
+ if (!TS)
+ throw ProtocolException("Invalid TS");
+ return TS;
+}
+
+ServerCommand* ServerCommandManager::GetHandler(const std::string& command) const
+{
+ ServerCommandMap::const_iterator it = commands.find(command);
+ if (it != commands.end())
+ return it->second;
+ return NULL;
+}
+
+bool ServerCommandManager::AddCommand(ServerCommand* cmd)
+{
+ return commands.insert(std::make_pair(cmd->name, cmd)).second;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "utils.h"
+#include "treeserver.h"
+
+class ProtocolException : public ModuleException
+{
+ public:
+ ProtocolException(const std::string& msg)
+ : ModuleException("Protocol violation: " + msg)
+ {
+ }
+};
+
+/** Base class for server-to-server commands that may have a (remote) user source or server source.
+ */
+class ServerCommand : public CommandBase
+{
+ public:
+ ServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0);
+
+ virtual CmdResult Handle(User* user, std::vector<std::string>& parameters) = 0;
+ virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters);
+
+ /**
+ * Extract the TS from a string.
+ * @param tsstr The string containing the TS.
+ * @return The raw timestamp value.
+ * This function throws a ProtocolException if it considers the TS invalid. Note that the detection of
+ * invalid timestamps is not designed to be bulletproof, only some cases - like "0" - trigger an exception.
+ */
+ static time_t ExtractTS(const std::string& tsstr);
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a user.
+ * When a server sends a command of this type and the source is a server (sid), the link is aborted.
+ */
+template <class T>
+class UserOnlyServerCommand : public ServerCommand
+{
+ public:
+ UserOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+ : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+ CmdResult Handle(User* user, std::vector<std::string>& parameters)
+ {
+ RemoteUser* remoteuser = IS_REMOTE(user);
+ if (!remoteuser)
+ throw ProtocolException("Invalid source");
+ return static_cast<T*>(this)->HandleRemote(remoteuser, parameters);
+ }
+};
+
+/** Base class for server-to-server command handlers which are only valid if their source is a server.
+ * When a server sends a command of this type and the source is a user (uuid), the link is aborted.
+ */
+template <class T>
+class ServerOnlyServerCommand : public ServerCommand
+{
+ public:
+ ServerOnlyServerCommand(Module* Creator, const std::string& Name, unsigned int MinPara = 0, unsigned int MaxPara = 0)
+ : ServerCommand(Creator, Name, MinPara, MaxPara) { }
+
+ CmdResult Handle(User* user, std::vector<std::string>& parameters)
+ {
+ if (!IS_SERVER(user))
+ throw ProtocolException("Invalid source");
+ TreeServer* server = TreeServer::Get(user);
+ return static_cast<T*>(this)->HandleServer(server, parameters);
+ }
+};
+
+class ServerCommandManager
+{
+ typedef TR1NS::unordered_map<std::string, ServerCommand*> ServerCommandMap;
+ ServerCommandMap commands;
+
+ public:
+ ServerCommand* GetHandler(const std::string& command) const;
+ bool AddCommand(ServerCommand* cmd);
+};
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+
+#include "treeserver.h"
+#include "commands.h"
+
+CmdResult CommandSInfo::HandleServer(TreeServer* server, std::vector<std::string>& params)
+{
+ const std::string& key = params.front();
+ const std::string& value = params.back();
+
+ if (key == "fullversion")
+ {
+ server->SetFullVersion(value);
+ }
+ else if (key == "version")
+ {
+ server->SetVersion(value);
+ }
+ else if (key == "desc")
+ {
+ // Only sent when the description of a server changes because of a rehash; not sent on burst
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Server description of " + server->GetName() + " changed: " + value);
+ server->SetDesc(value);
+ }
+
+ return CMD_SUCCESS;
+}
+
+CommandSInfo::Builder::Builder(TreeServer* server, const char* key, const std::string& val)
+ : CmdBuilder(server->GetID(), "SINFO")
+{
+ push(key).push_last(val);
+}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "commands.h"
-CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSJoin::Handle(User* user, std::vector<std::string>& parameters)
{
// Check for valid channel name
- if (!ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (!ServerInstance->IsChannel(parameters[1]))
return CMD_FAILURE;
// Check target exists
return CMD_FAILURE;
/* only join if it's local, otherwise just pass it on! */
- if (IS_LOCAL(u))
- Channel::JoinUser(u, parameters[1].c_str(), false, "", false, ServerInstance->Time());
+ LocalUser* localuser = IS_LOCAL(u);
+ if (localuser)
+ {
+ bool override = false;
+ std::string key;
+ if (parameters.size() >= 3)
+ {
+ key = parameters[2];
+ if (key.empty())
+ override = true;
+ }
+
+ Channel::JoinUser(localuser, parameters[1], override, key);
+ }
+
return CMD_SUCCESS;
}
#include "inspircd.h"
#include "main.h"
-#include "utils.h"
#include "commands.h"
-CmdResult CommandSVSNick::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSNick::Handle(User* user, std::vector<std::string>& parameters)
{
User* u = ServerInstance->FindNick(parameters[0]);
if (u && IS_LOCAL(u))
{
+ // The 4th parameter is optional and it is the expected nick TS of the target user. If this parameter is
+ // present and it doesn't match the user's nick TS, the SVSNICK is not acted upon.
+ // This makes it possible to detect the case when services wants to change the nick of a user, but the
+ // user changes their nick before the SVSNICK arrives, making the SVSNICK nick change (usually to a guest nick)
+ // unnecessary. Consider the following for example:
+ //
+ // 1. test changes nick to Attila which is protected by services
+ // 2. Services SVSNICKs the user to Guest12345
+ // 3. Attila changes nick to Attila_ which isn't protected by services
+ // 4. SVSNICK arrives
+ // 5. Attila_ gets his nick changed to Guest12345 unnecessarily
+ //
+ // In this case when the SVSNICK is processed the target has already changed his nick to something
+ // which isn't protected, so changing the nick again to a Guest nick is not desired.
+ // However, if the expected nick TS parameter is present in the SVSNICK then the nick change in step 5
+ // won't happen because the timestamps won't match.
+ if (parameters.size() > 3)
+ {
+ time_t ExpectedTS = ConvToInt(parameters[3]);
+ if (u->age != ExpectedTS)
+ return CMD_FAILURE; // Ignore SVSNICK
+ }
+
std::string nick = parameters[1];
if (isdigit(nick[0]))
nick = u->uuid;
- // Don't update the TS if the nick is exactly the same
- if (u->nick == nick)
- return CMD_FAILURE;
-
time_t NickTS = ConvToInt(parameters[2]);
if (NickTS <= 0)
return CMD_FAILURE;
- ModuleSpanningTree* st = (ModuleSpanningTree*)(Module*)creator;
- st->KeepNickTS = true;
- u->age = NickTS;
-
- if (!u->ForceNickChange(nick.c_str()))
+ if (!u->ChangeNick(nick, NickTS))
{
/* buh. UID them */
- if (!u->ForceNickChange(u->uuid.c_str()))
- {
- ServerInstance->Users->QuitUser(u, "Nickname collision");
- }
+ u->ChangeNick(u->uuid);
}
-
- st->KeepNickTS = false;
}
return CMD_SUCCESS;
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
#include "commands.h"
-CmdResult CommandSVSPart::Handle(const std::vector<std::string>& parameters, User *user)
+CmdResult CommandSVSPart::Handle(User* user, std::vector<std::string>& parameters)
{
User* u = ServerInstance->FindUUID(parameters[0]);
if (!u)
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "translate.h"
+
+std::string Translate::ModeChangeListToParams(const Modes::ChangeList::List& modes)
+{
+ std::string ret;
+ for (Modes::ChangeList::List::const_iterator i = modes.begin(); i != modes.end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+ if (!mh->GetNumParams(item.adding))
+ continue;
+
+ ret.push_back(' ');
+
+ if (mh->IsPrefixMode())
+ {
+ User* target = ServerInstance->FindNick(item.param);
+ if (target)
+ {
+ ret.append(target->uuid);
+ continue;
+ }
+ }
+
+ ret.append(item.param);
+ }
+ return ret;
+}
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+namespace Translate
+{
+ /** Generate a list of mode parameters suitable for FMODE/MODE from a Modes::ChangeList::List
+ * @param modes List of mode changes
+ * @return List of mode parameters built from the input. Does not include the modes themselves,
+ * only the parameters.
+ */
+ std::string ModeChangeListToParams(const Modes::ChangeList::List& modes);
+}
#include "inspircd.h"
-#include "socket.h"
#include "xline.h"
#include "main.h"
-#include "../spanningtree.h"
+#include "modules/spanningtree.h"
#include "utils.h"
#include "treeserver.h"
-/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
-
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
-TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id)
- : ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util), ServerUser(ServerInstance->FakeClient)
+TreeServer::TreeServer()
+ : Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc)
+ , Parent(NULL), Route(NULL)
+ , VersionString(ServerInstance->GetVersionString())
+ , fullversion(ServerInstance->GetVersionString(true))
+ , Socket(NULL), sid(ServerInstance->Config->GetSID()), behind_bursting(0), isdead(false)
+ , ServerUser(ServerInstance->FakeClient)
+ , age(ServerInstance->Time()), Warned(false), UserCount(ServerInstance->Users.GetLocalUsers().size())
+ , OperCount(0), rtt(0), StartBurst(0), Hidden(false)
{
- age = ServerInstance->Time();
- bursting = false;
- Parent = NULL;
- VersionString.clear();
- ServerUserCount = ServerOperCount = 0;
- VersionString = ServerInstance->GetVersionString();
- Route = NULL;
- Socket = NULL; /* Fix by brain */
- StartBurst = rtt = 0;
- Warned = Hidden = false;
AddHashEntry();
- SetID(id);
}
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
* its ping counters so that it will be pinged one minute from now.
*/
-TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide)
- : Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), ServerUser(new FakeUser(id, Name)), Hidden(Hide)
+TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide)
+ : Server(Name, Desc)
+ , Parent(Above), Socket(Sock), sid(id), behind_bursting(Parent->behind_bursting), isdead(false)
+ , ServerUser(new FakeUser(id, this))
+ , age(ServerInstance->Time()), Warned(false), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
{
- age = ServerInstance->Time();
- bursting = true;
- VersionString.clear();
- ServerUserCount = ServerOperCount = 0;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
+ CheckULine();
SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
SetPingFlag();
- Warned = false;
- rtt = 0;
-
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- this->StartBurst = ts;
- ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Started bursting at time %lu", ts);
/* find the 'route' for this server (e.g. the one directly connected
* to the local server, which we can use to reach it)
*/
this->AddHashEntry();
+ Parent->AddChild(this);
+}
- SetID(id);
+void TreeServer::BeginBurst(unsigned long startms)
+{
+ behind_bursting++;
+
+ unsigned long now = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // If the start time is in the future (clocks are not synced) then use current time
+ if ((!startms) || (startms > now))
+ startms = now;
+ this->StartBurst = startms;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %lu behind_bursting %u", sid.c_str(), startms, behind_bursting);
}
const std::string& TreeServer::GetID()
void TreeServer::FinishBurstInternal()
{
- this->bursting = false;
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
- for(unsigned int q=0; q < ChildCount(); q++)
+ // Check is needed because 1202 protocol servers don't send the bursting state of a server, so servers
+ // introduced during a netburst may later send ENDBURST which would normally decrease this counter
+ if (behind_bursting > 0)
+ behind_bursting--;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "FinishBurstInternal() %s behind_bursting %u", GetName().c_str(), behind_bursting);
+
+ if (!IsBehindBursting())
{
- TreeServer* child = GetChild(q);
+ SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
+ SetPingFlag();
+ }
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ {
+ TreeServer* child = *i;
child->FinishBurstInternal();
}
}
void TreeServer::FinishBurst()
{
- FinishBurstInternal();
ServerInstance->XLines->ApplyLines();
long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
unsigned long bursttime = ts - this->StartBurst;
ServerInstance->SNO->WriteToSnoMask(Parent == Utils->TreeRoot ? 'l' : 'L', "Received end of netburst from \2%s\2 (burst time: %lu %s)",
- ServerName.c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
- AddServerEvent(Utils->Creator, ServerName.c_str());
+ GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
+ AddServerEvent(Utils->Creator, GetName());
+
+ StartBurst = 0;
+ FinishBurstInternal();
}
-void TreeServer::SetID(const std::string &id)
+void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Setting SID to " + id);
- sid = id;
- Utils->sidlist[sid] = this;
+ DelServerEvent(Utils->Creator, server->GetName());
+ DelChild(server);
+
+ if (IsRoot())
+ {
+ // Server split from us, generate a SQUIT message and broadcast it
+ ServerInstance->SNO->WriteGlobalSno('l', "Server \002" + server->GetName() + "\002 split: " + reason);
+ CmdBuilder("SQUIT").push(server->GetID()).push_last(reason).Broadcast();
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
+ }
+
+ unsigned int num_lost_servers = 0;
+ server->SQuitInternal(num_lost_servers);
+
+ const std::string quitreason = GetName() + " " + server->GetName();
+ unsigned int num_lost_users = QuitUsers(quitreason);
+
+ ServerInstance->SNO->WriteToSnoMask(IsRoot() ? 'l' : 'L', "Netsplit complete, lost \002%u\002 user%s on \002%u\002 server%s.",
+ num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
+
+ // No-op if the socket is already closed (i.e. it called us)
+ if (server->IsLocal())
+ server->GetSocket()->Close();
+
+ // Add the server to the cull list, the servers behind it are handled by cull() and the destructor
+ ServerInstance->GlobalCulls.AddItem(server);
}
-int TreeServer::QuitUsers(const std::string &reason)
+void TreeServer::SQuitInternal(unsigned int& num_lost_servers)
{
- const char* reason_s = reason.c_str();
- std::vector<User*> time_to_die;
- for (user_hash::iterator n = ServerInstance->Users->clientlist->begin(); n != ServerInstance->Users->clientlist->end(); n++)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s lost in split", GetName().c_str());
+
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
- if (n->second->server == ServerName)
- {
- time_to_die.push_back(n->second);
- }
+ TreeServer* server = *i;
+ server->SQuitInternal(num_lost_servers);
}
- for (std::vector<User*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
+
+ // Mark server as dead
+ isdead = true;
+ num_lost_servers++;
+ RemoveHash();
+}
+
+unsigned int TreeServer::QuitUsers(const std::string& reason)
+{
+ std::string publicreason = ServerInstance->Config->HideSplits ? "*.net *.split" : reason;
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ unsigned int original_size = users.size();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); )
{
- User* a = (User*)*n;
- if (!IS_LOCAL(a))
- {
- if (this->Utils->quiet_bursts)
- a->quietquit = true;
+ User* user = i->second;
+ // Increment the iterator now because QuitUser() removes the user from the container
+ ++i;
+ TreeServer* server = TreeServer::Get(user);
+ if (server->IsDead())
+ ServerInstance->Users->QuitUser(user, publicreason, &reason);
+ }
+ return original_size - users.size();
+}
+
+void TreeServer::CheckULine()
+{
+ uline = silentuline = false;
- if (ServerInstance->Config->HideSplits)
- ServerInstance->Users->QuitUser(a, "*.net *.split", reason_s);
- else
- ServerInstance->Users->QuitUser(a, reason_s);
+ ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+ std::string server = tag->getString("server");
+ if (!strcasecmp(server.c_str(), GetName().c_str()))
+ {
+ if (this->IsRoot())
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Servers should not uline themselves (at " + tag->getTagLocation() + ")");
+ return;
+ }
+
+ uline = true;
+ silentuline = tag->getBool("silent");
+ break;
}
}
- return time_to_die.size();
}
/** This method is used to add the structure to the
*/
void TreeServer::AddHashEntry()
{
- server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
- if (iter == Utils->serverlist.end())
- Utils->serverlist[this->ServerName.c_str()] = this;
-}
-
-/** This method removes the reference to this object
- * from the hash_map which is used for linear searches.
- * It is only called by the default destructor.
- */
-void TreeServer::DelHashEntry()
-{
- server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
- if (iter != Utils->serverlist.end())
- Utils->serverlist.erase(iter);
+ Utils->serverlist[GetName()] = this;
+ Utils->sidlist[sid] = this;
}
/** These accessors etc should be pretty self-
return Route;
}
-std::string TreeServer::GetName()
-{
- return ServerName.c_str();
-}
-
-const std::string& TreeServer::GetDesc()
-{
- return ServerDesc;
-}
-
const std::string& TreeServer::GetVersion()
{
return VersionString;
LastPingWasGood = true;
}
-unsigned int TreeServer::GetUserCount()
-{
- return ServerUserCount;
-}
-
-void TreeServer::SetUserCount(int diff)
-{
- ServerUserCount += diff;
-}
-
-void TreeServer::SetOperCount(int diff)
-{
- ServerOperCount += diff;
-}
-
-unsigned int TreeServer::GetOperCount()
-{
- return ServerOperCount;
-}
-
TreeSocket* TreeServer::GetSocket()
{
return Socket;
VersionString = Version;
}
-unsigned int TreeServer::ChildCount()
-{
- return Children.size();
-}
-
-TreeServer* TreeServer::GetChild(unsigned int n)
-{
- if (n < Children.size())
- {
- /* Make sure they cant request
- * an out-of-range object. After
- * all we know what these programmer
- * types are like *grin*.
- */
- return Children[n];
- }
- else
- {
- return NULL;
- }
-}
-
void TreeServer::AddChild(TreeServer* Child)
{
Children.push_back(Child);
return false;
}
-/** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
- */
-bool TreeServer::Tidy()
+CullResult TreeServer::cull()
{
- while (1)
+ // Recursively cull all servers that are under us in the tree
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
- std::vector<TreeServer*>::iterator a = Children.begin();
- if (a == Children.end())
- return true;
- TreeServer* s = *a;
- s->Tidy();
- s->cull();
- Children.erase(a);
- delete s;
+ TreeServer* server = *i;
+ server->cull();
}
-}
-CullResult TreeServer::cull()
-{
- if (ServerUser != ServerInstance->FakeClient)
+ if (!IsRoot())
ServerUser->cull();
return classbase::cull();
}
TreeServer::~TreeServer()
{
- /* We'd better tidy up after ourselves, eh? */
- this->DelHashEntry();
- if (ServerUser != ServerInstance->FakeClient)
+ // Recursively delete all servers that are under us in the tree first
+ for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
+ delete *i;
+
+ // Delete server user unless it's us
+ if (!IsRoot())
delete ServerUser;
+}
+
+void TreeServer::RemoveHash()
+{
+ // XXX: Erase server from UserManager::uuidlist now, to allow sid reuse in the current main loop
+ // iteration, before the cull list is applied
+ ServerInstance->Users->uuidlist.erase(sid);
- server_hash::iterator iter = Utils->sidlist.find(GetID());
- if (iter != Utils->sidlist.end())
- Utils->sidlist.erase(iter);
+ Utils->sidlist.erase(sid);
+ Utils->serverlist.erase(GetName());
}
*/
-#ifndef M_SPANNINGTREE_TREESERVER_H
-#define M_SPANNINGTREE_TREESERVER_H
+#pragma once
#include "treesocket.h"
* TreeServer items, deleting and inserting them as they
* are created and destroyed.
*/
-class TreeServer : public classbase
+class TreeServer : public Server
{
TreeServer* Parent; /* Parent entry */
TreeServer* Route; /* Route entry */
std::vector<TreeServer*> Children; /* List of child objects */
- irc::string ServerName; /* Server's name */
- std::string ServerDesc; /* Server's description */
std::string VersionString; /* Version string or empty string */
- unsigned int ServerUserCount; /* How many users are on this server? [note: doesn't care about +i] */
- unsigned int ServerOperCount; /* How many opers are on this server? */
- TreeSocket* Socket; /* For directly connected servers this points at the socket object */
+
+ /** Full version string including patch version and other info
+ */
+ std::string fullversion;
+
+ TreeSocket* Socket; /* Socket used to communicate with this server */
time_t NextPing; /* After this time, the server should be PINGed*/
bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */
- SpanningTreeUtilities* Utils; /* Utility class */
std::string sid; /* Server ID */
- /** Set server ID
- * @param id Server ID
- * @throws CoreException on duplicate ID
+ /** Counter counting how many servers are bursting in front of this server, including
+ * this server. Set to parents' value on construction then it is increased if the
+ * server itself starts bursting. Decreased when a server on the path to this server
+ * finishes burst.
+ */
+ unsigned int behind_bursting;
+
+ /** True if this server has been lost in a split and is awaiting destruction
+ */
+ bool isdead;
+
+ /** This method is used to add this TreeServer to the
+ * hash maps. It is only called by the constructors.
+ */
+ void AddHashEntry();
+
+ /** Used by SQuit logic to recursively remove servers
+ */
+ void SQuitInternal(unsigned int& num_lost_servers);
+
+ /** Remove the reference to this server from the hash maps
*/
- void SetID(const std::string &id);
+ void RemoveHash();
public:
+ typedef std::vector<TreeServer*> ChildServers;
FakeUser* const ServerUser; /* User representing this server */
- time_t age;
+ const time_t age;
bool Warned; /* True if we've warned opers about high latency on this server */
- bool bursting; /* whether or not this server is bursting */
+
+ unsigned int UserCount; /* How many users are on this server? [note: doesn't care about +i] */
+ unsigned int OperCount; /* How many opers are on this server? */
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
- TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id);
+ TreeServer();
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
* its ping counters so that it will be pinged one minute from now.
*/
- TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide);
-
- int QuitUsers(const std::string &reason);
+ TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide);
- /** This method is used to add the structure to the
- * hash_map for linear searches. It is only called
- * by the constructors.
+ /** SQuit a server connected to this server, removing the given server and all servers behind it
+ * @param server Server to squit, must be directly below this server
+ * @param reason Reason for quitting the server, sent to opers and other servers
*/
- void AddHashEntry();
+ void SQuitChild(TreeServer* server, const std::string& reason);
- /** This method removes the reference to this object
- * from the hash_map which is used for linear searches.
- * It is only called by the default destructor.
+ /** SQuit this server, removing this server and all servers behind it
+ * @param reason Reason for quitting the server, sent to opers and other servers
*/
- void DelHashEntry();
+ void SQuit(const std::string& reason)
+ {
+ GetParent()->SQuitChild(this, reason);
+ }
+
+ static unsigned int QuitUsers(const std::string& reason);
/** Get route.
* The 'route' is defined as the locally-
*/
TreeServer* GetRoute();
- /** Get server name
+ /** Returns true if this server is the tree root (i.e.: us)
*/
- std::string GetName();
+ bool IsRoot() const { return (this->Parent == NULL); }
- /** Get server description (GECOS)
+ /** Returns true if this server is locally connected
*/
- const std::string& GetDesc();
+ bool IsLocal() const { return (this->Route == this); }
+
+ /** Returns true if the server is awaiting destruction
+ * @return True if the server is waiting to be culled and deleted, false otherwise
+ */
+ bool IsDead() const { return isdead; }
/** Get server version string
*/
const std::string& GetVersion();
+ /** Get the full version string of this server
+ * @return The full version string of this server, including patch version and other info
+ */
+ const std::string& GetFullVersion() const { return fullversion; }
+
/** Set time we are next due to ping this server
*/
void SetNextPingTime(time_t t);
*/
void SetPingFlag();
- /** Get the number of users on this server.
- */
- unsigned int GetUserCount();
-
- /** Increment or decrement the user count by diff.
- */
- void SetUserCount(int diff);
-
- /** Gets the numbers of opers on this server.
- */
- unsigned int GetOperCount();
-
- /** Increment or decrement the oper count by diff.
- */
- void SetOperCount(int diff);
-
/** Get the TreeSocket pointer for local servers.
* For remote servers, this returns NULL.
*/
*/
void SetVersion(const std::string &Version);
- /** Return number of child servers
+ /** Set the full version string
+ * @param verstr The version string to set
*/
- unsigned int ChildCount();
+ void SetFullVersion(const std::string& verstr) { fullversion = verstr; }
- /** Return a child server indexed 0..n
+ /** Sets the description of this server. Called when the description of a remote server changes
+ * and we are notified about it.
+ * @param descstr The description to set
*/
- TreeServer* GetChild(unsigned int n);
+ void SetDesc(const std::string& descstr) { description = descstr; }
+
+ /** Return all child servers
+ */
+ const ChildServers& GetChildren() const { return Children; }
/** Add a child server
*/
*/
bool DelChild(TreeServer* Child);
- /** Removes child nodes of this node, and of that node, etc etc.
- * This is used during netsplits to automatically tidy up the
- * server tree. It is slow, we don't use it for much else.
- */
- bool Tidy();
-
/** Get server ID
*/
const std::string& GetID();
/** Recursive call for child servers */
void FinishBurstInternal();
+ /** (Re)check the uline state of this server
+ */
+ void CheckULine();
+
+ /** Get the bursting state of this server
+ * @return True if this server is bursting, false if it isn't
+ */
+ bool IsBursting() const { return (StartBurst != 0); }
+
+ /** Check whether this server is behind a bursting server or is itself bursting.
+ * This can tell whether a user is on a part of the network that is still bursting.
+ * @return True if this server is bursting or is behind a server that is bursting, false if it isn't
+ */
+ bool IsBehindBursting() const { return (behind_bursting != 0); }
+
+ /** Set the bursting state of the server
+ * @param startms Time the server started bursting, if 0 or omitted, use current time
+ */
+ void BeginBurst(unsigned long startms = 0);
+
CullResult cull();
- /** Destructor
+
+ /** Destructor, deletes ServerUser unless IsRoot()
*/
~TreeServer();
-};
-#endif
+ /** Returns the TreeServer the given user is connected to
+ * @param user The user whose server to return
+ * @return The TreeServer this user is connected to.
+ */
+ static TreeServer* Get(User* user)
+ {
+ return static_cast<TreeServer*>(user->server);
+ }
+};
*/
-#ifndef M_SPANNINGTREE_TREESOCKET_H
-#define M_SPANNINGTREE_TREESOCKET_H
+#pragma once
-#include "socket.h"
#include "inspircd.h"
-#include "xline.h"
#include "utils.h"
std::string ourchallenge; /* Challenge sent for challenge/response */
std::string theirchallenge; /* Challenge recv for challenge/response */
int capab_phase; /* Have sent CAPAB already */
- bool auth_fingerprint; /* Did we auth using SSL fingerprint */
+ bool auth_fingerprint; /* Did we auth using SSL certificate fingerprint */
bool auth_challenge; /* Did we auth using challenge/response */
// Data saved from incoming SERVER command, for later use when our credentials have been accepted by the other party
*/
class TreeSocket : public BufferedSocket
{
- SpanningTreeUtilities* Utils; /* Utility class */
+ struct BurstState;
+
std::string linkID; /* Description for this link */
ServerState LinkState; /* Link state */
CapabData* capab; /* Link setup data (held until burst is sent) */
TreeServer* MyRoot; /* The server we are talking to */
int proto_version; /* Remote protocol version */
- bool ConnectionFailureShown; /* Set to true if a connection failure message was shown */
+
+ /** True if we've sent our burst.
+ * This only changes the behavior of message translation for 1202 protocol servers and it can be
+ * removed once 1202 support is dropped.
+ */
+ bool burstsent;
/** Checks if the given servername and sid are both free
*/
bool CheckDuplicate(const std::string& servername, const std::string& sid);
+ /** Send all ListModeBase modes set on the channel
+ */
+ void SendListModes(Channel* chan);
+
+ /** Send all known information about a channel */
+ void SyncChannel(Channel* chan, BurstState& bs);
+
+ /** Send all users and their oper state, away state and metadata */
+ void SendUsers(BurstState& bs);
+
+ /** Send all additional info about the given server to this server */
+ void SendServerInfo(TreeServer* from);
+
+ /** Find the User source of a command given a prefix and a command string.
+ * This connection must be fully up when calling this function.
+ * @param prefix Prefix string to find the source User object for. Can be a sid, a uuid or a server name.
+ * @param command The command whose source to find. This is required because certain commands (like mode
+ * changes and kills) must be processed even if their claimed source doesn't exist. If the given command is
+ * such a command and the source does not exist, the function returns a valid FakeUser that can be used to
+ * to process the command with.
+ * @return The command source to use when processing the command or NULL if the source wasn't found.
+ * Note that the direction of the returned source is not verified.
+ */
+ User* FindSource(const std::string& prefix, const std::string& command);
+
+ /** Finish the authentication phase of this connection.
+ * Change the state of the connection to CONNECTED, create a TreeServer object for the server on the
+ * other end of the connection using the details provided in the parameters, and finally send a burst.
+ * @param remotename Name of the remote server
+ * @param remotesid SID of the remote server
+ * @param remotedesc Description of the remote server
+ * @param hidden True if the remote server is hidden according to the configuration
+ */
+ void FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden);
+
+ /** Authenticate the remote server.
+ * Validate the parameters and find the link block that matches the remote server. In case of an error,
+ * an appropriate snotice is generated, an ERROR message is sent and the connection is closed.
+ * Failing to find a matching link block counts as an error.
+ * @param params Parameters they sent in the SERVER command
+ * @return Link block for the remote server, or NULL if an error occurred
+ */
+ Link* AuthRemote(const parameterlist& params);
+
+ /** Write a line on this socket with a new line character appended, skipping all translation for old protocols
+ * @param line Line to write without a new line character at the end
+ */
+ void WriteLineNoCompat(const std::string& line);
+
public:
- time_t age;
+ const time_t age;
/** Because most of the I/O gubbins are encapsulated within
* BufferedSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
- TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr);
+ TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr);
/** When a listening socket gives us a new file descriptor,
* we must associate it with a socket without creating a new
* connection. This constructor is used for this purpose.
*/
- TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
+ TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
/** Get link state
*/
* to server docs on the inspircd.org site, the other side
* will then send back its own server string.
*/
- virtual void OnConnected();
+ void OnConnected();
/** Handle socket error event
*/
- virtual void OnError(BufferedSocketError e);
+ void OnError(BufferedSocketError e) CXX11_OVERRIDE;
/** Sends an error to the remote server, and displays it locally to show
* that it was sent.
/** Recursively send the server tree with distances as hops.
* This is used during network burst to inform the other server
* (and any of ITS servers too) of what servers we know about.
- * If at any point any of these servers already exist on the other
- * end, our connection may be terminated. The hopcounts given
- * by this function are relative, this doesn't matter so long as
- * they are all >1, as all the remote servers re-calculate them
- * to be relative too, with themselves as hop 0.
*/
- void SendServers(TreeServer* Current, TreeServer* s, int hops);
+ void SendServers(TreeServer* Current, TreeServer* s);
/** Returns module list as a string, filtered by filter
* @param filter a module version bitmask, such as VF_COMMON or VF_OPTCOMMON
*/
void SendCapabilities(int phase);
- /** Add modules to VF_COMMON list for backwards compatability */
- void CompatAddModules(std::vector<std::string>& modlist);
-
/* Isolate and return the elements that are different between two lists */
void ListDifference(const std::string &one, const std::string &two, char sep,
std::string& mleft, std::string& mright);
bool Capab(const parameterlist ¶ms);
- /** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
- void SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users);
-
- /** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
- void Squit(TreeServer* Current, const std::string &reason);
-
- /* Used on nick collision ... XXX ugly function HACK */
- int DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid);
-
/** Send one or more FJOINs for a channel of users.
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
/** Send G, Q, Z and E lines */
void SendXLines();
- /** Send channel modes and topics */
- void SendChannelModes();
-
- /** send all users and their oper state/modes */
- void SendUsers();
+ /** Send all known information about a channel */
+ void SyncChannel(Channel* chan);
/** This function is called when we want to send a netburst to a local
* server. There is a set order we must do this, because for example
/** Send one or more complete lines down the socket
*/
- void WriteLine(std::string line);
+ void WriteLine(const std::string& line);
/** Handle ERROR command */
void Error(parameterlist ¶ms);
- /** Remote AWAY */
- bool Away(const std::string &prefix, parameterlist ¶ms);
-
- /** SAVE to resolve nick collisions without killing */
- bool ForceNick(const std::string &prefix, parameterlist ¶ms);
-
- /** ENCAP command
- */
- void Encap(User* who, parameterlist ¶ms);
-
- /** OPERQUIT command
- */
- bool OperQuit(const std::string &prefix, parameterlist ¶ms);
-
- /** PONG
- */
- bool LocalPong(const std::string &prefix, parameterlist ¶ms);
-
- /** VERSION
- */
- bool ServerVersion(const std::string &prefix, parameterlist ¶ms);
-
- /** ADDLINE
- */
- bool AddLine(const std::string &prefix, parameterlist ¶ms);
-
- /** DELLINE
- */
- bool DelLine(const std::string &prefix, parameterlist ¶ms);
-
- /** WHOIS
- */
- bool Whois(const std::string &prefix, parameterlist ¶ms);
-
- /** PUSH
- */
- bool Push(const std::string &prefix, parameterlist ¶ms);
-
- /** PING
- */
- bool LocalPing(const std::string &prefix, parameterlist ¶ms);
-
- /** <- (remote) <- SERVER
- */
- bool RemoteServer(const std::string &prefix, parameterlist ¶ms);
-
/** (local) -> SERVER
*/
bool Outbound_Reply_Server(parameterlist ¶ms);
/** Handle socket timeout from connect()
*/
- virtual void OnTimeout();
+ void OnTimeout();
/** Handle server quit on close
*/
- virtual void Close();
+ void Close();
- /** Returns true if this server was introduced to the rest of the network
+ /** Fixes messages coming from old servers so the new command handlers understand them
*/
- bool Introduced();
+ bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, std::vector<std::string>& params);
};
-
-#endif
-
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
+#include "iohook.h"
#include "main.h"
-#include "../spanningtree.h"
+#include "modules/spanningtree.h"
#include "utils.h"
#include "treeserver.h"
#include "link.h"
#include "treesocket.h"
-#include "resolvers.h"
+#include "commands.h"
/** Because most of the I/O gubbins are encapsulated within
* BufferedSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
-TreeSocket::TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr)
- : Utils(Util)
+TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr)
+ : linkID(assign(link->Name)), LinkState(CONNECTING), MyRoot(NULL), proto_version(0)
+ , burstsent(false), age(ServerInstance->Time())
{
- age = ServerInstance->Time();
- linkID = assign(link->Name);
capab = new CapabData;
capab->link = link;
capab->ac = myac;
capab->capab_phase = 0;
- MyRoot = NULL;
- proto_version = 0;
- ConnectionFailureShown = false;
- LinkState = CONNECTING;
- if (!link->Hook.empty())
- {
- ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook);
- if (!prov)
- {
- SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID);
- return;
- }
- AddIOHook(prov->creator);
- }
+
DoConnect(ipaddr, link->Port, link->Timeout, link->Bind);
Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout);
SendCapabilities(1);
* we must associate it with a socket without creating a new
* connection. This constructor is used for this purpose.
*/
-TreeSocket::TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- : BufferedSocket(newfd), Utils(Util)
+TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ : BufferedSocket(newfd)
+ , linkID("inbound from " + client->addr()), LinkState(WAIT_AUTH_1), MyRoot(NULL), proto_version(0)
+ , burstsent(false), age(ServerInstance->Time())
{
capab = new CapabData;
capab->capab_phase = 0;
- MyRoot = NULL;
- age = ServerInstance->Time();
- LinkState = WAIT_AUTH_1;
- proto_version = 0;
- ConnectionFailureShown = false;
- linkID = "inbound from " + client->addr();
- FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
- if (GetIOHook())
- GetIOHook()->OnStreamSocketAccept(this, client, server);
+ if (via->iohookprov)
+ via->iohookprov->OnAccept(this, client, server);
SendCapabilities(1);
Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
TreeSocket::~TreeSocket()
{
- if (capab)
- delete capab;
+ delete capab;
}
/** When an outbound connection finishes connecting, we receive
{
if (this->LinkState == CONNECTING)
{
+ if (!capab->link->Hook.empty())
+ {
+ ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, capab->link->Hook);
+ if (!prov)
+ {
+ SetError("Could not find hook '" + capab->link->Hook + "' for connection to " + linkID);
+ return;
+ }
+ static_cast<IOHookProvider*>(prov)->OnConnect(this);
+ }
+
ServerInstance->SNO->WriteGlobalSno('l', "Connection to \2%s\2[%s] started.", linkID.c_str(),
(capab->link->HiddenFromStats ? "<hidden>" : capab->link->IPAddr.c_str()));
this->SendCapabilities(1);
ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\002%s\002' failed with error: %s",
linkID.c_str(), getError().c_str());
LinkState = DYING;
+ Close();
}
void TreeSocket::SendError(const std::string &errormessage)
SetError(errormessage);
}
-/** This function forces this server to quit, removing this server
- * and any users on it (and servers and users below that, etc etc).
- * It's very slow and pretty clunky, but luckily unless your network
- * is having a REAL bad hair day, this function shouldnt be called
- * too many times a month ;-)
- */
-void TreeSocket::SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users)
+CmdResult CommandSQuit::HandleServer(TreeServer* server, std::vector<std::string>& params)
{
- std::string servername = Current->GetName();
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"SquitServer for %s from %s",
- servername.c_str(), from.c_str());
- /* recursively squit the servers attached to 'Current'.
- * We're going backwards so we don't remove users
- * while we still need them ;)
- */
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ TreeServer* quitting = Utils->FindServer(params[0]);
+ if (!quitting)
{
- TreeServer* recursive_server = Current->GetChild(q);
- this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Squit from unknown server");
+ return CMD_FAILURE;
}
- /* Now we've whacked the kids, whack self */
- num_lost_servers++;
- num_lost_users += Current->QuitUsers(from);
-}
-
-/** This is a wrapper function for SquitServer above, which
- * does some validation first and passes on the SQUIT to all
- * other remaining servers.
- */
-void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
-{
- bool LocalSquit = false;
- if ((Current) && (Current != Utils->TreeRoot))
+ CmdResult ret = CMD_SUCCESS;
+ if (quitting == server)
{
- DelServerEvent(Utils->Creator, Current->GetName());
+ ret = CMD_FAILURE;
+ server = server->GetParent();
+ }
+ else if (quitting->GetParent() != server)
+ throw ProtocolException("Attempted to SQUIT a non-directly connected server or the parent");
- if (!Current->GetSocket() || Current->GetSocket()->Introduced())
- {
- parameterlist params;
- params.push_back(Current->GetID());
- params.push_back(":"+reason);
- Utils->DoOneToAllButSender(Current->GetParent()->GetID(),"SQUIT",params,Current->GetID());
- }
+ server->SQuitChild(quitting, params[1]);
- if (Current->GetParent() == Utils->TreeRoot)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason);
- LocalSquit = true;
- }
- else
- {
- ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
- }
- int num_lost_servers = 0;
- int num_lost_users = 0;
- std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
- SquitServer(from, Current, num_lost_servers, num_lost_users);
- ServerInstance->SNO->WriteToSnoMask(LocalSquit ? 'l' : 'L', "Netsplit complete, lost \002%d\002 user%s on \002%d\002 server%s.",
- num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
- Current->Tidy();
- Current->GetParent()->DelChild(Current);
- Current->cull();
- const bool ismyroot = (Current == MyRoot);
- delete Current;
- if (ismyroot)
- {
- MyRoot = NULL;
- Close();
- }
- }
- else
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Squit from unknown server");
+ // XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down)
+ // to stop this message from being forwarded.
+ // The squit logic generates a SQUIT message with our sid as the source and sends it to the
+ // remaining servers.
+ return ret;
}
/** This function is called when we receive data from a remote
SendError("Read null character from socket");
break;
}
- ProcessLine(line);
+
+ try
+ {
+ ProcessLine(line);
+ }
+ catch (CoreException& ex)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error while processing: " + line);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason());
+ SendError(ex.GetReason() + " - check the log file for details");
+ }
+
if (!getError().empty())
break;
}
SendError("RecvQ overrun (line too long)");
Utils->Creator->loopCall = false;
}
-
-bool TreeSocket::Introduced()
-{
- return (capab == NULL);
-}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "link.h"
#include "treesocket.h"
#include "resolvers.h"
+#include "commands.h"
/* Handle ERROR command */
void TreeSocket::Error(parameterlist ¶ms)
if (!tokens.GetToken(prefix))
return;
-
+
if (prefix[0] == ':')
{
prefix = prefix.substr(1);
std::string command;
parameterlist params;
- ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());
Split(line, prefix, command, params);
{
if (params.size())
{
- time_t them = atoi(params[0].c_str());
+ time_t them = ConvToInt(params[0]);
time_t delta = them - ServerInstance->Time();
if ((delta < -600) || (delta > 600))
{
if (!CheckDuplicate(capab->name, capab->sid))
return;
- this->LinkState = CONNECTED;
- Utils->timeoutlist.erase(this);
-
- linkID = capab->name;
-
- MyRoot = new TreeServer(Utils, capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden);
- Utils->TreeRoot->AddChild(MyRoot);
-
- MyRoot->bursting = true;
- this->DoBurst(MyRoot);
-
- parameterlist sparams;
- sparams.push_back(MyRoot->GetName());
- sparams.push_back("*");
- sparams.push_back("0");
- sparams.push_back(MyRoot->GetID());
- sparams.push_back(":" + MyRoot->GetDesc());
- Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(), "SERVER", sparams, MyRoot->GetName());
- Utils->DoOneToAllButSender(MyRoot->GetID(), "BURST", params, MyRoot->GetName());
+ FinishAuth(capab->name, capab->sid, capab->description, capab->hidden);
}
else if (command == "ERROR")
{
}
}
-void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+User* TreeSocket::FindSource(const std::string& prefix, const std::string& command)
{
+ // Empty prefix means the source is the directly connected server that sent this command
+ if (prefix.empty())
+ return MyRoot->ServerUser;
+
+ // If the prefix string is a uuid or a sid FindUUID() returns the appropriate User object
User* who = ServerInstance->FindUUID(prefix);
- std::string direction;
+ if (who)
+ return who;
- if (!who)
- {
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (prefix.empty())
- ServerSource = MyRoot;
+ // Some implementations wrongly send a server name as prefix occasionally, handle that too for now
+ TreeServer* const server = Utils->FindServer(prefix);
+ if (server)
+ return server->ServerUser;
- if (ServerSource)
- {
- who = ServerSource->ServerUser;
- }
- else
- {
- /* It is important that we don't close the link here, unknown prefix can occur
- * due to various race conditions such as the KILL message for a user somehow
- * crossing the users QUIT further upstream from the server. Thanks jilles!
- */
+ /* It is important that we don't close the link here, unknown prefix can occur
+ * due to various race conditions such as the KILL message for a user somehow
+ * crossing the users QUIT further upstream from the server. Thanks jilles!
+ */
- if ((prefix.length() == UUID_LENGTH-1) && (isdigit(prefix[0])) &&
- ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
- {
- /* Special case, we cannot drop these commands as they've been committed already on a
- * part of the network by the time we receive them, so in this scenario pretend the
- * command came from a server to avoid desync.
- */
+ if ((prefix.length() == UIDGenerator::UUID_LENGTH) && (isdigit(prefix[0])) &&
+ ((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
+ {
+ /* Special case, we cannot drop these commands as they've been committed already on a
+ * part of the network by the time we receive them, so in this scenario pretend the
+ * command came from a server to avoid desync.
+ */
- who = ServerInstance->FindUUID(prefix.substr(0, 3));
- if (!who)
- who = this->MyRoot->ServerUser;
- }
- else
- {
- ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
- command.c_str(), prefix.c_str());
- return;
- }
- }
+ who = ServerInstance->FindUUID(prefix.substr(0, 3));
+ if (who)
+ return who;
+ return this->MyRoot->ServerUser;
}
- // Make sure prefix is still good
- direction = who->server;
- prefix = who->uuid;
+ // Unknown prefix
+ return NULL;
+}
+
+void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
+{
+ User* who = FindSource(prefix, command);
+ if (!who)
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", command.c_str(), prefix.c_str());
+ return;
+ }
/*
* Check for fake direction here, and drop any instances that are found.
* a valid SID or a valid UUID, so that invalid UUID or SID never makes it
* to the higher level functions. -- B
*/
- TreeServer* route_back_again = Utils->BestRouteTo(direction);
- if ((!route_back_again) || (route_back_again->GetSocket() != this))
+ TreeServer* const server = TreeServer::Get(who);
+ if (server->GetSocket() != this)
{
- if (route_back_again)
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
- prefix.c_str(),linkID.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Protocol violation: Fake direction '%s' from connection '%s'", prefix.c_str(), linkID.c_str());
return;
}
- /*
- * First up, check for any malformed commands (e.g. MODE without a timestamp)
- * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
- */
- if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
- command = "MODE";
-
- // TODO move all this into Commands
- if (command == "MAP")
- {
- Utils->Creator->HandleMap(params, who);
- }
- else if (command == "SERVER")
- {
- this->RemoteServer(prefix,params);
- }
- else if (command == "ERROR")
- {
- this->Error(params);
- }
- else if (command == "AWAY")
- {
- this->Away(prefix,params);
- }
- else if (command == "PING")
- {
- this->LocalPing(prefix,params);
- }
- else if (command == "PONG")
- {
- TreeServer *s = Utils->FindServer(prefix);
- if (s && s->bursting)
- {
- ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
- s->FinishBurst();
- }
- this->LocalPong(prefix,params);
- }
- else if (command == "VERSION")
- {
- this->ServerVersion(prefix,params);
- }
- else if (command == "ADDLINE")
- {
- this->AddLine(prefix,params);
- }
- else if (command == "DELLINE")
- {
- this->DelLine(prefix,params);
- }
- else if (command == "SAVE")
- {
- this->ForceNick(prefix,params);
- }
- else if (command == "OPERQUIT")
- {
- this->OperQuit(prefix,params);
- }
- else if (command == "IDLE")
- {
- this->Whois(prefix,params);
- }
- else if (command == "PUSH")
- {
- this->Push(prefix,params);
- }
- else if (command == "SQUIT")
- {
- if (params.size() == 2)
- {
- this->Squit(Utils->FindServer(params[0]),params[1]);
- }
- }
- else if (command == "SNONOTICE")
- {
- if (params.size() >= 2)
- {
- ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + who->nick + ": "+ params[1]);
- params[1] = ":" + params[1];
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
- }
- }
- else if (command == "BURST")
+ // Translate commands coming from servers using an older protocol
+ if (proto_version < ProtocolVersion)
{
- // Set prefix server as bursting
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (!ServerSource)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
+ if (!PreProcessOldProtocolMessage(who, command, params))
return;
- }
-
- ServerSource->bursting = true;
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
}
- else if (command == "ENDBURST")
- {
- TreeServer* ServerSource = Utils->FindServer(prefix);
- if (!ServerSource)
- {
- ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
- return;
- }
- ServerSource->FinishBurst();
- Utils->DoOneToAllButSender(prefix, command, params, prefix);
- }
- else if (command == "ENCAP")
+ ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command);
+ CommandBase* cmdbase = scmd;
+ Command* cmd = NULL;
+ if (!scmd)
{
- this->Encap(who, params);
- }
- else if (command == "NICK")
- {
- if (params.size() != 2)
- {
- SendError("Protocol violation: Wrong number of parameters for NICK message");
- return;
- }
-
- if (IS_SERVER(who))
- {
- SendError("Protocol violation: Server changing nick");
- return;
- }
-
- if ((isdigit(params[0][0])) && (params[0] != who->uuid))
- {
- SendError("Protocol violation: User changing nick to an invalid UID - " + params[0]);
- return;
- }
-
- /* Update timestamp on user when they change nicks */
- who->age = atoi(params[1].c_str());
-
- /*
- * On nick messages, check that the nick doesnt already exist here.
- * If it does, perform collision logic.
- */
- bool callfnc = true;
- User* x = ServerInstance->FindNickOnly(params[0]);
- if ((x) && (x != who) && (x->registered == REG_ALL))
+ // Not a special server-to-server command
+ cmd = ServerInstance->Parser.GetHandler(command);
+ if (!cmd)
{
- int collideret = 0;
- /* x is local, who is remote */
- collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
- if (collideret != 1)
+ if (command == "ERROR")
{
- // Remote client lost, or both lost, rewrite this nick change as a change to uuid before
- // forwarding and don't call ForceNickChange() because DoCollision() has done it already
- params[0] = who->uuid;
- callfnc = false;
+ this->Error(params);
+ return;
}
- }
- if (callfnc)
- who->ForceNickChange(params[0].c_str());
- Utils->RouteCommand(route_back_again, command, params, who);
- }
- else
- {
- Command* cmd = ServerInstance->Parser->GetHandler(command);
-
- if (!cmd)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Unrecognised command '" + command + "' -- possibly loaded mismatched modules");
- return;
- }
- if (params.size() < cmd->min_params)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Insufficient parameters for S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Insufficient parameters for command '" + command + "'");
- return;
+ throw ProtocolException("Unknown command");
}
+ cmdbase = cmd;
+ }
- if ((!params.empty()) && (params.back().empty()) && (!cmd->allow_empty_last_param))
- {
- // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
- if (params.size()-1 < cmd->min_params)
- return;
- params.pop_back();
- }
+ if (params.size() < cmdbase->min_params)
+ throw ProtocolException("Insufficient parameters");
- CmdResult res = cmd->Handle(params, who);
+ if ((!params.empty()) && (params.back().empty()) && (!cmdbase->allow_empty_last_param))
+ {
+ // the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
+ if (params.size()-1 < cmdbase->min_params)
+ return;
+ params.pop_back();
+ }
+ CmdResult res;
+ if (scmd)
+ res = scmd->Handle(who, params);
+ else
+ {
+ res = cmd->Handle(params, who);
if (res == CMD_INVALID)
- {
- irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Error handling S2S command :%s %s %s",
- who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
- SendError("Error handling '" + command + "' -- possibly loaded mismatched modules");
- }
- else if (res == CMD_SUCCESS)
- Utils->RouteCommand(route_back_again, command, params, who);
+ throw ProtocolException("Error in command handler");
}
+
+ if (res == CMD_SUCCESS)
+ Utils->RouteCommand(server->GetRoute(), cmdbase, params, who);
}
void TreeSocket::OnTimeout()
void TreeSocket::Close()
{
- if (fd != -1)
- ServerInstance->GlobalCulls.AddItem(this);
+ if (fd < 0)
+ return;
+
+ ServerInstance->GlobalCulls.AddItem(this);
this->BufferedSocket::Close();
SetError("Remote host closed connection");
// If the connection is fully up (state CONNECTED)
// then propogate a netsplit to all peers.
if (MyRoot)
- Squit(MyRoot,getError());
+ MyRoot->SQuit(getError());
- if (!ConnectionFailureShown)
- {
- ConnectionFailureShown = true;
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",linkID.c_str());
- time_t server_uptime = ServerInstance->Time() - this->age;
- if (server_uptime)
- {
- std::string timestr = Utils->Creator->TimeToStr(server_uptime);
- ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
- }
+ time_t server_uptime = ServerInstance->Time() - this->age;
+ if (server_uptime)
+ {
+ std::string timestr = ModuleSpanningTree::TimeToStr(server_uptime);
+ ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", linkID.c_str(), timestr.c_str());
}
}
+
+void TreeSocket::FinishAuth(const std::string& remotename, const std::string& remotesid, const std::string& remotedesc, bool hidden)
+{
+ this->LinkState = CONNECTED;
+ Utils->timeoutlist.erase(this);
+
+ linkID = remotename;
+
+ MyRoot = new TreeServer(remotename, remotedesc, remotesid, Utils->TreeRoot, this, hidden);
+
+ // Mark the server as bursting
+ MyRoot->BeginBurst();
+ this->DoBurst(MyRoot);
+
+ CommandServer::Builder(MyRoot).Forward(MyRoot);
+}
#include "commands.h"
#include "utils.h"
-#include "link.h"
-#include "treesocket.h"
#include "treeserver.h"
-#include "resolvers.h"
-CmdResult CommandUID::Handle(const parameterlist ¶ms, User* serversrc)
+CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::string>& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
/** Do we have enough parameters:
* 0 1 2 3 4 5 6 7 8 9 (n-1)
* UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos
*/
- time_t age_t = ConvToInt(params[1]);
- time_t signon = ConvToInt(params[7]);
+ time_t age_t = ServerCommand::ExtractTS(params[1]);
+ time_t signon = ServerCommand::ExtractTS(params[7]);
std::string empty;
- std::string modestr(params[8]);
+ const std::string& modestr = params[8];
- TreeServer* remoteserver = Utils->FindServer(serversrc->server);
-
- if (!remoteserver)
- return CMD_INVALID;
/* Is this a valid UID, and not misrouted? */
- if (params[0].length() != 9 || params[0].substr(0,3) != serversrc->uuid)
- return CMD_INVALID;
+ if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].compare(0, 3, remoteserver->GetID()))
+ throw ProtocolException("Bogus UUID");
/* Check parameters for validity before introducing the client, discovered by dmb */
- if (!age_t)
- return CMD_INVALID;
- if (!signon)
- return CMD_INVALID;
if (modestr[0] != '+')
- return CMD_INVALID;
- TreeSocket* sock = remoteserver->GetRoute()->GetSocket();
-
- /* check for collision */
- User* const collideswith = ServerInstance->FindNickOnly(params[2]);
+ throw ProtocolException("Invalid mode string");
+ // See if there is a nick collision
+ User* collideswith = ServerInstance->FindNickOnly(params[2]);
if ((collideswith) && (collideswith->registered != REG_ALL))
{
// User that the incoming user is colliding with is not fully registered, we force nick change the
// unregistered user to their uuid and tell them what happened
collideswith->WriteFrom(collideswith, "NICK %s", collideswith->uuid.c_str());
- collideswith->WriteNumeric(433, "%s %s :Nickname overruled.", collideswith->nick.c_str(), collideswith->nick.c_str());
+ collideswith->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", collideswith->nick.c_str());
// Clear the bit before calling User::ChangeNick() to make it NOT run the OnUserPostNick() hook
collideswith->registered &= ~REG_NICK;
- collideswith->ChangeNick(collideswith->uuid, true);
+ collideswith->ChangeNick(collideswith->uuid);
}
else if (collideswith)
{
- /*
- * Nick collision.
- */
- int collide = sock->DoCollision(collideswith, age_t, params[5], params[6], params[0]);
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"*** Collision on %s, collide=%d", params[2].c_str(), collide);
+ // The user on this side is registered, handle the collision
+ bool they_change = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0]);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Collision on %s %d", params[2].c_str(), they_change);
- if (collide != 1)
+ if (they_change)
{
- /* remote client lost, make sure we change their nick for the hash too
- *
- * This alters the line that will be sent to other servers, which
- * commands normally shouldn't do; hence the required const_cast.
- */
- const_cast<parameterlist&>(params)[2] = params[0];
+ // The client being introduced needs to change nick to uuid, change the nick in the message before
+ // processing/forwarding it. Also change the nick TS to CommandSave::SavedTimestamp.
+ age_t = CommandSave::SavedTimestamp;
+ params[1] = ConvToStr(CommandSave::SavedTimestamp);
+ params[2] = params[0];
}
}
/* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically
* sets it up in the UUID hash for us.
+ *
+ * If the UUID already exists User::User() throws an exception which causes this connection to be closed.
*/
- User* _new = NULL;
- try
- {
- _new = new RemoteUser(params[0], remoteserver->GetName());
- }
- catch (...)
- {
- ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str());
- return CMD_INVALID;
- }
- (*(ServerInstance->Users->clientlist))[params[2]] = _new;
+ RemoteUser* _new = new RemoteUser(params[0], remoteserver);
+ ServerInstance->Users->clientlist[params[2]] = _new;
_new->nick = params[2];
_new->host = params[3];
_new->dhost = params[4];
_new->ident = params[5];
- _new->fullname = params[params.size() - 1];
+ _new->fullname = params.back();
_new->registered = REG_ALL;
_new->signon = signon;
_new->age = age_t;
- /* we need to remove the + from the modestring, so we can do our stuff */
- std::string::size_type pos_after_plus = modestr.find_first_not_of('+');
- if (pos_after_plus != std::string::npos)
- modestr = modestr.substr(pos_after_plus);
-
unsigned int paramptr = 9;
- for (std::string::iterator v = modestr.begin(); v != modestr.end(); v++)
+
+ for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v)
{
- /* For each mode thats set, increase counter */
+ // Accept more '+' chars, for now
+ if (*v == '+')
+ continue;
+
+ /* For each mode thats set, find the mode handler and set it on the new user */
ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER);
+ if (!mh)
+ throw ProtocolException("Unrecognised mode '" + std::string(1, *v) + "'");
- if (mh)
+ if (mh->GetNumParams(true))
{
- if (mh->GetNumParams(true))
- {
- if (paramptr >= params.size() - 1)
- return CMD_INVALID;
- std::string mp = params[paramptr++];
- /* IMPORTANT NOTE:
- * All modes are assumed to succeed here as they are being set by a remote server.
- * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
- * to note as all but one modules currently cannot ever fail in this situation, except for
- * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
- * but here, at client introduction. You may safely assume this behaviour is standard and
- * will not change in future versions if you want to make use of this protective behaviour
- * yourself.
- */
- mh->OnModeChange(_new, _new, NULL, mp, true);
- }
- else
- mh->OnModeChange(_new, _new, NULL, empty, true);
- _new->SetMode(*v, true);
+ if (paramptr >= params.size() - 1)
+ throw ProtocolException("Out of parameters while processing modes");
+ std::string mp = params[paramptr++];
+ /* IMPORTANT NOTE:
+ * All modes are assumed to succeed here as they are being set by a remote server.
+ * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important
+ * to note as all but one modules currently cannot ever fail in this situation, except for
+ * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE
+ * but here, at client introduction. You may safely assume this behaviour is standard and
+ * will not change in future versions if you want to make use of this protective behaviour
+ * yourself.
+ */
+ mh->OnModeChange(_new, _new, NULL, mp, true);
}
+ else
+ mh->OnModeChange(_new, _new, NULL, empty, true);
+ _new->SetMode(mh, true);
}
- /* now we've done with modes processing, put the + back for remote servers */
- if (modestr[0] != '+')
- modestr = "+" + modestr;
-
_new->SetClientIP(params[6].c_str());
- ServerInstance->Users->AddGlobalClone(_new);
- remoteserver->SetUserCount(1); // increment by 1
+ ServerInstance->Users->AddClone(_new);
+ remoteserver->UserCount++;
bool dosend = true;
- if ((Utils->quiet_bursts && remoteserver->bursting) || ServerInstance->SilentULine(_new->server))
+ if ((Utils->quiet_bursts && remoteserver->IsBehindBursting()) || _new->server->IsSilentULine())
dosend = false;
if (dosend)
- ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", _new->server.c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString(), _new->fullname.c_str());
+ ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->fullname.c_str());
- FOREACH_MOD(I_OnPostConnect,OnPostConnect(_new));
+ FOREACH_MOD(OnPostConnect, (_new));
return CMD_SUCCESS;
}
-CmdResult CommandFHost::Handle(const parameterlist ¶ms, User* src)
+CmdResult CommandFHost::HandleRemote(RemoteUser* src, std::vector<std::string>& params)
{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeDisplayedHost(params[0].c_str());
+ src->ChangeDisplayedHost(params[0]);
return CMD_SUCCESS;
}
-CmdResult CommandFIdent::Handle(const parameterlist ¶ms, User* src)
+CmdResult CommandFIdent::HandleRemote(RemoteUser* src, std::vector<std::string>& params)
{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeIdent(params[0].c_str());
+ src->ChangeIdent(params[0]);
return CMD_SUCCESS;
}
-CmdResult CommandFName::Handle(const parameterlist ¶ms, User* src)
+CmdResult CommandFName::HandleRemote(RemoteUser* src, std::vector<std::string>& params)
{
- if (IS_SERVER(src))
- return CMD_FAILURE;
- src->ChangeName(params[0].c_str());
+ src->ChangeName(params[0]);
return CMD_SUCCESS;
}
+CommandUID::Builder::Builder(User* user)
+ : CmdBuilder(TreeServer::Get(user)->GetID(), "UID")
+{
+ push(user->uuid);
+ push_int(user->age);
+ push(user->nick);
+ push(user->host);
+ push(user->dhost);
+ push(user->ident);
+ push(user->GetIPString());
+ push_int(user->signon);
+ push('+').push_raw(user->FormatModes(true));
+ push_last(user->fullname);
+}
#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
#include "main.h"
#include "utils.h"
#include "treeserver.h"
-#include "link.h"
#include "treesocket.h"
#include "resolvers.h"
+#include "commandbuilder.h"
+
+SpanningTreeUtilities* Utils = NULL;
/* Create server sockets off a listener. */
ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
if (*i == "*" || *i == incomingip || irc::sockets::cidr_mask(*i).match(*client))
{
/* we don't need to do anything with the pointer, creating it stores it in the necessary places */
- new TreeSocket(Utils, newsock, from, client, server);
+ new TreeSocket(newsock, from, client, server);
return MOD_RES_ALLOW;
}
}
*/
TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
{
- if (ServerInstance->IsSID(ServerName))
+ if (InspIRCd::IsSID(ServerName))
return this->FindServerID(ServerName);
- server_hash::iterator iter = serverlist.find(ServerName.c_str());
+ server_hash::iterator iter = serverlist.find(ServerName);
if (iter != serverlist.end())
{
return iter->second;
*/
TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
{
- if (ServerName.c_str() == TreeRoot->GetName() || ServerName == ServerInstance->Config->GetSID())
- return NULL;
TreeServer* Found = FindServer(ServerName);
if (Found)
{
User *u = ServerInstance->FindNick(ServerName);
if (u)
{
- Found = FindServer(u->server);
- if (Found)
- return Found->GetRoute();
+ return TreeServer::Get(u)->GetRoute();
}
return NULL;
return NULL;
}
-SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
+SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C)
+ : Creator(C), TreeRoot(NULL)
{
- ServerInstance->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
-
- this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
- this->ReadConfiguration();
+ ServerInstance->Timers.AddTimer(&RefreshTimer);
}
CullResult SpanningTreeUtilities::cull()
{
- while (TreeRoot->ChildCount())
+ const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+ while (!children.empty())
{
- TreeServer* child_server = TreeRoot->GetChild(0);
- if (child_server)
- {
- TreeSocket* sock = child_server->GetSocket();
- sock->Close();
- }
+ TreeSocket* sock = children.front()->GetSocket();
+ sock->Close();
}
for(std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = timeoutlist.begin(); i != timeoutlist.end(); ++i)
delete TreeRoot;
}
-void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
-{
- if (list.find(server) == list.end())
- list[server] = server;
-}
-
/* returns a list of DIRECT servernames for a specific channel */
-void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
+void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list)
{
unsigned int minrank = 0;
if (status)
{
- ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
+ PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
if (mh)
minrank = mh->GetPrefixRank();
}
- const UserMembList *ulist = c->GetUsers();
-
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
if (IS_LOCAL(i->first))
continue;
if (exempt_list.find(i->first) == exempt_list.end())
{
- TreeServer* best = this->BestRouteTo(i->first->server);
- if (best)
- AddThisServer(best,list);
+ TreeServer* best = TreeServer::Get(i->first);
+ list.insert(best->GetSocket());
}
}
return;
}
-bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& omit)
+void SpanningTreeUtilities::DoOneToAllButSender(const CmdBuilder& params, TreeServer* omitroute)
{
- TreeServer* omitroute = this->BestRouteTo(omit);
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- unsigned int items = this->TreeRoot->ChildCount();
- for (unsigned int x = 0; x < items; x++)
+ const std::string& FullLine = params.str();
+
+ const TreeServer::ChildServers& children = TreeRoot->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- TreeServer* Route = this->TreeRoot->GetChild(x);
- // Send the line IF:
- // The route has a socket (its a direct connection)
- // The route isnt the one to be omitted
- // The route isnt the path to the one to be omitted
- if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
+ TreeServer* Route = *i;
+ // Send the line if the route isn't the path to the one to be omitted
+ if (Route != omitroute)
{
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
+ Route->GetSocket()->WriteLine(FullLine);
}
}
- return true;
}
-bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist ¶ms)
+bool SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, const std::string& target)
{
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- unsigned int items = this->TreeRoot->ChildCount();
- for (unsigned int x = 0; x < items; x++)
- {
- TreeServer* Route = this->TreeRoot->GetChild(x);
- if (Route && Route->GetSocket())
- {
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
- }
- }
+ TreeServer* Route = this->BestRouteTo(target);
+ if (!Route)
+ return false;
+
+ DoOneToOne(params, Route);
return true;
}
-bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& target)
+void SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, Server* server)
{
- TreeServer* Route = this->BestRouteTo(target);
- if (Route)
- {
- std::string FullLine = ":" + prefix + " " + command;
- unsigned int words = params.size();
- for (unsigned int x = 0; x < words; x++)
- {
- FullLine = FullLine + " " + params[x];
- }
- if (Route && Route->GetSocket())
- {
- TreeSocket* Sock = Route->GetSocket();
- if (Sock)
- Sock->WriteLine(FullLine);
- }
- return true;
- }
- else
- {
- return false;
- }
+ TreeServer* ts = static_cast<TreeServer*>(server);
+ TreeSocket* sock = ts->GetSocket();
+ if (sock)
+ sock->WriteLine(params);
}
void SpanningTreeUtilities::RefreshIPCache()
Link* L = *i;
if (!L->Port)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block without a port.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring a link block without a port.");
/* Invalid link block */
continue;
}
- if (L->AllowMask.length())
- ValidIPs.push_back(L->AllowMask);
+ ValidIPs.insert(ValidIPs.end(), L->AllowMasks.begin(), L->AllowMasks.end());
irc::sockets::sockaddrs dummy;
bool ipvalid = irc::sockets::aptosa(L->IPAddr, L->Port, dummy);
if ((L->IPAddr == "*") || (ipvalid))
ValidIPs.push_back(L->IPAddr);
- else
+ else if (this->Creator->DNS)
{
+ SecurityIPResolver* sr = new SecurityIPResolver(Creator, *this->Creator->DNS, L->IPAddr, L, DNS::QUERY_AAAA);
try
{
- bool cached = false;
- SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, DNS_QUERY_AAAA);
- ServerInstance->AddResolver(sr, cached);
+ this->Creator->DNS->Process(sr);
}
- catch (...)
+ catch (DNS::Exception &)
{
+ delete sr;
}
}
}
HideULines = security->getBool("hideulines");
AnnounceTSChange = options->getBool("announcets");
AllowOptCommon = options->getBool("allowmismatch");
- ChallengeResponse = !security->getBool("disablehmac");
quiet_bursts = ServerInstance->Config->ConfValue("performance")->getBool("quietbursts");
PingWarnTime = options->getInt("pingwarning");
PingFreq = options->getInt("serverpingfreq");
reference<Link> L = new Link(tag);
std::string linkname = tag->getString("name");
L->Name = linkname.c_str();
- L->AllowMask = tag->getString("allowmask");
+
+ irc::spacesepstream sep = tag->getString("allowmask");
+ for (std::string s; sep.GetToken(s);)
+ L->AllowMasks.push_back(s);
+
L->IPAddr = tag->getString("ipaddr");
L->Port = tag->getInt("port");
L->SendPass = tag->getString("sendpass", tag->getString("password"));
L->RecvPass = tag->getString("recvpass", tag->getString("password"));
L->Fingerprint = tag->getString("fingerprint");
L->HiddenFromStats = tag->getBool("statshidden");
- L->Timeout = tag->getInt("timeout", 30);
+ L->Timeout = tag->getDuration("timeout", 30);
L->Hook = tag->getString("ssl");
L->Bind = tag->getString("bind");
L->Hidden = tag->getBool("hidden");
if (L->Name.find('.') == std::string::npos)
throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it must contain at least one '.' character");
- if (L->Name.length() > 64)
- throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it is longer than 64 characters");
+ if (L->Name.length() > ServerInstance->Config->Limits.MaxHost)
+ throw ModuleException("The link name '"+assign(L->Name)+"' is invalid as it is longer than " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters");
if (L->RecvPass.empty())
throw ModuleException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined");
if (L->IPAddr.empty())
{
L->IPAddr = "*";
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block '" + assign(L->Name) + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + assign(L->Name) + "' has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
}
if (!L->Port)
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block '" + assign(L->Name) + "' has no port defined, you will not be able to /connect it.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Configuration warning: Link block '" + assign(L->Name) + "' has no port defined, you will not be able to /connect it.");
L->Fingerprint.erase(std::remove(L->Fingerprint.begin(), L->Fingerprint.end(), ':'), L->Fingerprint.end());
LinkBlocks.push_back(L);
{
ConfigTag* tag = i->second;
reference<Autoconnect> A = new Autoconnect(tag);
- A->Period = tag->getInt("period");
+ A->Period = tag->getDuration("period", 60, 1);
A->NextConnectTime = ServerInstance->Time() + A->Period;
A->position = -1;
irc::spacesepstream ss(tag->getString("server"));
A->servers.push_back(server);
}
- if (A->Period <= 0)
- {
- throw ModuleException("Invalid configuration for autoconnect, period not a positive integer!");
- }
-
if (A->servers.empty())
{
throw ModuleException("Invalid configuration for autoconnect, server cannot be empty!");
AutoconnectBlocks.push_back(A);
}
+ for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+ i->second->CheckULine();
+
RefreshIPCache();
}
return NULL;
}
-void SpanningTreeUtilities::Rehash()
+void SpanningTreeUtilities::SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit)
{
- server_hash temp;
- for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
- temp.insert(std::make_pair(i->first, i->second));
- serverlist.swap(temp);
- temp.clear();
-
- for (server_hash::const_iterator i = sidlist.begin(); i != sidlist.end(); ++i)
- temp.insert(std::make_pair(i->first, i->second));
- sidlist.swap(temp);
+ CmdBuilder msg(prefix, message_type);
+ msg.push_raw(' ');
+ if (status != 0)
+ msg.push_raw(status);
+ msg.push_raw(target->name).push_last(text);
+
+ TreeSocketSet list;
+ this->GetListOfServersForChannel(target, list, status, exempt_list);
+ for (TreeSocketSet::iterator i = list.begin(); i != list.end(); ++i)
+ {
+ TreeSocket* Sock = *i;
+ if (Sock != omit)
+ Sock->WriteLine(msg);
+ }
}
*/
-#ifndef M_SPANNINGTREE_UTILS_H
-#define M_SPANNINGTREE_UTILS_H
+#pragma once
#include "inspircd.h"
+#include "cachetimer.h"
/* Foward declarations */
class TreeServer;
class Autoconnect;
class ModuleSpanningTree;
class SpanningTreeUtilities;
+class CmdBuilder;
+
+extern SpanningTreeUtilities* Utils;
/* This hash_map holds the hash equivalent of the server
* tree, used for rapid linear lookups.
*/
-#ifdef HASHMAP_DEPRECATED
- typedef nspace::hash_map<std::string, TreeServer*, nspace::insensitive, irc::StrHashComp> server_hash;
-#else
- typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<std::string>, irc::StrHashComp> server_hash;
-#endif
-
-typedef std::map<TreeServer*,TreeServer*> TreeServerList;
+typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
/** Contains helper functions and variables for this module,
* and keeps them out of the global namespace
*/
class SpanningTreeUtilities : public classbase
{
+ CacheRefreshTimer RefreshTimer;
+
public:
+ typedef std::set<TreeSocket*> TreeSocketSet;
typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
/** Creator module
*/
std::vector<reference<Autoconnect> > AutoconnectBlocks;
- /** True (default) if we are to use challenge-response HMAC
- * to authenticate passwords.
- *
- * NOTE: This defaults to on, but should be turned off if
- * you are linking to an older version of inspircd.
- */
- bool ChallengeResponse;
-
/** Ping frequency of server to server links
*/
int PingFreq;
*/
~SpanningTreeUtilities();
- void RouteCommand(TreeServer*, const std::string&, const parameterlist&, User*);
+ void RouteCommand(TreeServer* origin, CommandBase* cmd, const parameterlist& parameters, User* user);
/** Send a message from this server to one other local or remote
*/
- bool DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& target);
+ bool DoOneToOne(const CmdBuilder& params, const std::string& target);
+ void DoOneToOne(const CmdBuilder& params, Server* target);
/** Send a message from this server to all but one other, local or remote
*/
- bool DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& omit);
+ void DoOneToAllButSender(const CmdBuilder& params, TreeServer* omit);
/** Send a message from this server to all others
*/
- bool DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist ¶ms);
+ void DoOneToMany(const CmdBuilder& params);
/** Read the spanningtree module's tags from the config file
*/
void ReadConfiguration();
- /** Add a server to the server list for GetListOfServersForChannel
+ /** Handle nick collision
*/
- void AddThisServer(TreeServer* server, TreeServerList &list);
+ bool DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid);
/** Compile a list of servers which contain members of channel c
*/
- void GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list);
+ void GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list);
/** Find a server by name
*/
*/
void RefreshIPCache();
- /** Recreate serverlist and sidlist, this is needed because of m_nationalchars changing
- * national_case_insensitive_map which is used by the hash function
+ /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix
*/
- void Rehash();
+ void SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit = NULL);
};
-#endif
+inline void SpanningTreeUtilities::DoOneToMany(const CmdBuilder& params)
+{
+ DoOneToAllButSender(params, NULL);
+}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "socket.h"
-#include "xline.h"
-#include "socketengine.h"
-
-#include "main.h"
-#include "utils.h"
-#include "treeserver.h"
-#include "treesocket.h"
-
-/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */
-
-bool TreeSocket::ServerVersion(const std::string &prefix, parameterlist ¶ms)
-{
- if (params.size() < 1)
- return true;
-
- TreeServer* ServerSource = Utils->FindServer(prefix);
-
- if (ServerSource)
- {
- ServerSource->SetVersion(params[0]);
- }
- params[0] = ":" + params[0];
- Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
- return true;
-}
-
#include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
-
-/* $ModDesc: Allow/Deny connections based upon an arbitrary SQL table */
+#include "modules/sql.h"
+#include "modules/hash.h"
+#include "modules/ssl.h"
enum AuthState {
AUTH_STATE_NONE = 0,
: SQLQuery(me), uid(u), pendingExt(e), verbose(v)
{
}
-
- void OnResult(SQLResult& res)
+
+ void OnResult(SQLResult& res) CXX11_OVERRIDE
{
User* user = ServerInstance->FindNick(uid);
if (!user)
}
}
- void OnError(SQLerror& error)
+ void OnError(SQLerror& error) CXX11_OVERRIDE
{
User* user = ServerInstance->FindNick(uid);
if (!user)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(pendingExt);
- OnRehash(NULL);
- Implementation eventlist[] = { I_OnCheckReady, I_OnRehash, I_OnUserRegister };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("sqlauth");
std::string dbid = conf->getString("dbid");
verbose = conf->getBool("verbose");
}
- ModResult OnUserRegister(LocalUser* user)
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
// Note this is their initial (unresolved) connect block
ConfigTag* tag = user->MyClass->config;
HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
if (md5)
- userinfo["md5pass"] = md5->hexsum(user->password);
+ userinfo["md5pass"] = md5->Generate(user->password);
HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
if (sha256)
- userinfo["sha256pass"] = sha256->hexsum(user->password);
+ userinfo["sha256pass"] = sha256->Generate(user->password);
+
+ const std::string certfp = SSLClientCert::GetFingerprint(&user->eh);
+ userinfo["certfp"] = certfp;
SQL->submit(new AuthQuery(this, user->uuid, pendingExt, verbose), freeformquery, userinfo);
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckReady(LocalUser* user)
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
{
switch (pendingExt.get(user))
{
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allow/Deny connections based upon an arbitrary SQL table", VF_VENDOR);
}
#include "inspircd.h"
-#include "sql.h"
-#include "hash.h"
-
-/* $ModDesc: Allows storage of oper credentials in an SQL table */
-
-static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
+#include "modules/sql.h"
+#include "modules/hash.h"
class OpMeQuery : public SQLQuery
{
{
}
- void OnResult(SQLResult& res)
+ void OnResult(SQLResult& res) CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: result for %s", uid.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "result for %s", uid.c_str());
User* user = ServerInstance->FindNick(uid);
if (!user)
return;
SQLEntries row;
while (res.GetRow(row))
{
-#if 0
- parameterlist cols;
- res.GetCols(cols);
-
- std::vector<KeyVal>* items;
- reference<ConfigTag> tag = ConfigTag::create("oper", "<m_sqloper>", 0, items);
- for(unsigned int i=0; i < cols.size(); i++)
- {
- if (!row[i].nul)
- items->insert(std::make_pair(cols[i], row[i]));
- }
-#else
if (OperUser(user, row[0], row[1]))
return;
-#endif
}
- ServerInstance->Logs->Log("m_sqloper",DEBUG, "SQLOPER: no matches for %s (checked %d rows)", uid.c_str(), res.Rows());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "no matches for %s (checked %d rows)", uid.c_str(), res.Rows());
// nobody succeeded... fall back to OPER
fallback();
}
- void OnError(SQLerror& error)
+ void OnError(SQLerror& error) CXX11_OVERRIDE
{
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: query failed (%s)", error.Str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "query failed (%s)", error.Str());
fallback();
}
if (!user)
return;
- Command* oper_command = ServerInstance->Parser->GetHandler("OPER");
+ Command* oper_command = ServerInstance->Parser.GetHandler("OPER");
if (oper_command)
{
}
else
{
- ServerInstance->Logs->Log("m_sqloper",SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
+ ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "BUG: WHAT?! Why do we have no OPER command?!");
}
}
bool OperUser(User* user, const std::string &pattern, const std::string &type)
{
- OperIndex::iterator iter = ServerInstance->Config->oper_blocks.find(" " + type);
- if (iter == ServerInstance->Config->oper_blocks.end())
+ ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(type);
+ if (iter == ServerInstance->Config->OperTypes.end())
{
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "bad type '%s' in returned row for oper %s", type.c_str(), username.c_str());
return false;
}
OperInfo* ifo = iter->second;
hostname.append("@").append(user->host);
- if (OneOfMatches(hostname.c_str(), user->GetIPString(), pattern))
+ if (InspIRCd::MatchMask(pattern, hostname, user->GetIPString()))
{
/* Opertype and host match, looks like this is it. */
public:
ModuleSQLOper() : SQL(this, "SQL") {}
- void init()
- {
- OnRehash(NULL);
-
- Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("sqloper");
query = tag->getString("query", "SELECT hostname as host, type FROM ircd_opers WHERE username='$username' AND password='$password'");
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
if (validated && command == "OPER" && parameters.size() >= 2)
{
/* Query is in progress, it will re-invoke OPER if needed */
return MOD_RES_DENY;
}
- ServerInstance->Logs->Log("m_sqloper",DEFAULT, "SQLOPER: database not present");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "database not present");
}
return MOD_RES_PASSTHRU;
}
ParamM userinfo;
SQL->PopulateUserInfo(user, userinfo);
userinfo["username"] = username;
- userinfo["password"] = hash ? hash->hexsum(password) : password;
+ userinfo["password"] = hash ? hash->Generate(password) : password;
SQL->submit(new OpMeQuery(this, user->uuid, username, password), query, userinfo);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Allows storage of oper credentials in an SQL table", VF_VENDOR);
}
-
};
MODULE_INIT(ModuleSQLOper)
#include "inspircd.h"
-#include "ssl.h"
-
-/* $ModDesc: Provides SSL metadata, including /WHOIS information and /SSLINFO command */
+#include "modules/ssl.h"
class SSLCertExt : public ExtensionItem {
public:
if ((!target) || (target->registered != REG_ALL))
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nickname", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nickname", parameters[0].c_str());
return CMD_FAILURE;
}
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
- if (operonlyfp && !IS_OPER(user) && target != user)
+ if (operonlyfp && !user->IsOper() && target != user)
{
- user->WriteServ("NOTICE %s :*** You cannot view SSL certificate information for other users", user->nick.c_str());
+ user->WriteNotice("*** You cannot view SSL certificate information for other users");
return CMD_FAILURE;
}
ssl_cert* cert = CertExt.get(target);
if (!cert)
{
- user->WriteServ("NOTICE %s :*** No SSL certificate for this user", user->nick.c_str());
+ user->WriteNotice("*** No SSL certificate for this user");
}
else if (cert->GetError().length())
{
- user->WriteServ("NOTICE %s :*** No SSL certificate information for this user (%s).", user->nick.c_str(), cert->GetError().c_str());
+ user->WriteNotice("*** No SSL certificate information for this user (" + cert->GetError() + ").");
}
else
{
- user->WriteServ("NOTICE %s :*** Distinguished Name: %s", user->nick.c_str(), cert->GetDN().c_str());
- user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick.c_str(), cert->GetIssuer().c_str());
- user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick.c_str(), cert->GetFingerprint().c_str());
+ user->WriteNotice("*** Distinguished Name: " + cert->GetDN());
+ user->WriteNotice("*** Issuer: " + cert->GetIssuer());
+ user->WriteNotice("*** Key Fingerprint: " + cert->GetFingerprint());
}
return CMD_SUCCESS;
}
};
-class ModuleSSLInfo : public Module
+class UserCertificateAPIImpl : public UserCertificateAPIBase
{
- CommandSSLInfo cmd;
+ SSLCertExt& ext;
public:
- ModuleSSLInfo() : cmd(this)
+ UserCertificateAPIImpl(Module* mod, SSLCertExt& certext)
+ : UserCertificateAPIBase(mod), ext(certext)
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
+ ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
+ {
+ return ext.get(user);
+ }
+};
- ServerInstance->Modules->AddService(cmd.CertExt);
+class ModuleSSLInfo : public Module
+{
+ CommandSSLInfo cmd;
+ UserCertificateAPIImpl APIImpl;
- Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ public:
+ ModuleSSLInfo()
+ : cmd(this), APIImpl(this, cmd.CertExt)
+ {
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("SSL Certificate Utilities", VF_VENDOR);
}
- void OnWhois(User* source, User* dest)
+ void OnWhois(User* source, User* dest) CXX11_OVERRIDE
{
ssl_cert* cert = cmd.CertExt.get(dest);
if (cert)
{
- ServerInstance->SendWhoisLine(source, dest, 671, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
+ ServerInstance->SendWhoisLine(source, dest, 671, "%s :is using a secure connection", dest->nick.c_str());
bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
- if ((!operonlyfp || source == dest || IS_OPER(source)) && !cert->fingerprint.empty())
- ServerInstance->SendWhoisLine(source, dest, 276, "%s %s :has client certificate fingerprint %s",
- source->nick.c_str(), dest->nick.c_str(), cert->fingerprint.c_str());
+ if ((!operonlyfp || source == dest || source->IsOper()) && !cert->fingerprint.empty())
+ ServerInstance->SendWhoisLine(source, dest, 276, "%s :has client certificate fingerprint %s",
+ dest->nick.c_str(), cert->fingerprint.c_str());
}
}
- bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
- {
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
- }
-
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
if ((command == "OPER") && (validated))
{
- OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
+ ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
if (i != ServerInstance->Config->oper_blocks.end())
{
OperInfo* ifo = i->second;
if (ifo->oper_block->getBool("sslonly") && !cert)
{
- user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
+ user->WriteNumeric(491, ":This oper login requires an SSL connection.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
std::string fingerprint;
if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
{
- user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
+ user->WriteNumeric(491, ":This oper login requires a matching SSL certificate fingerprint.");
user->CommandFloodPenalty += 10000;
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- void OnUserConnect(LocalUser* user)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- SocketCertificateRequest req(&user->eh, this);
- if (!req.cert)
- return;
- cmd.CertExt.set(user, req.cert);
+ ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
+ if (cert)
+ cmd.CertExt.set(user, cert);
}
- void OnPostConnect(User* user)
+ void OnPostConnect(User* user) CXX11_OVERRIDE
{
ssl_cert *cert = cmd.CertExt.get(user);
if (!cert || cert->fingerprint.empty())
return;
// find an auto-oper block for this user
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
+ for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
{
OperInfo* ifo = i->second;
std::string fp = ifo->oper_block->getString("fingerprint");
}
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
- SocketCertificateRequest req(&user->eh, this);
+ ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
bool ok = true;
if (myclass->config->getString("requiressl") == "trusted")
{
- ok = (req.cert && req.cert->IsCAVerified());
+ ok = (cert && cert->IsCAVerified());
}
else if (myclass->config->getBool("requiressl"))
{
- ok = (req.cert != NULL);
+ ok = (cert != NULL);
}
if (!ok)
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
-
- void OnRequest(Request& request)
- {
- if (strcmp("GET_USER_CERT", request.id) == 0)
- {
- UserCertificateRequest& req = static_cast<UserCertificateRequest&>(request);
- req.cert = cmd.CertExt.get(req.user);
- }
- }
};
MODULE_INIT(ModuleSSLInfo)
-
#include "inspircd.h"
-#include "ssl.h"
-
-/* $ModDesc: Provides channel mode +z to allow for Secure/SSL only channels */
+#include "modules/ssl.h"
/** Handle channel mode +z
*/
class SSLMode : public ModeHandler
{
public:
- SSLMode(Module* Creator) : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL) { }
+ UserCertificateAPI API;
+
+ SSLMode(Module* Creator)
+ : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL)
+ , API(Creator)
+ {
+ }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
{
if (adding)
{
- if (!channel->IsModeSet('z'))
+ if (!channel->IsModeSet(this))
{
if (IS_LOCAL(source))
{
- const UserMembList* userlist = channel->GetUsers();
- for(UserMembCIter i = userlist->begin(); i != userlist->end(); i++)
+ if (!API)
+ return MODEACTION_DENY;
+
+ const Channel::MemberMap& userlist = channel->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
{
- UserCertificateRequest req(i->first, creator);
- req.Send();
- if(!req.cert && !ServerInstance->ULine(i->first->server))
+ ssl_cert* cert = API->GetCertificate(i->first);
+ if (!cert && !i->first->server->IsULine())
{
- source->WriteNumeric(ERR_ALLMUSTSSL, "%s %s :all members of the channel must be connected via SSL", source->nick.c_str(), channel->name.c_str());
+ source->WriteNumeric(ERR_ALLMUSTSSL, "%s :all members of the channel must be connected via SSL", channel->name.c_str());
return MODEACTION_DENY;
}
}
}
- channel->SetMode('z',true);
+ channel->SetMode(this, true);
return MODEACTION_ALLOW;
}
else
}
else
{
- if (channel->IsModeSet('z'))
+ if (channel->IsModeSet(this))
{
- channel->SetMode('z',false);
+ channel->SetMode(this, false);
return MODEACTION_ALLOW;
}
{
}
- void init()
+ ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(sslm);
- Implementation eventlist[] = { I_OnUserPreJoin, I_OnCheckBan, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
- {
- if(chan && chan->IsModeSet('z'))
+ if(chan && chan->IsModeSet(sslm))
{
- UserCertificateRequest req(user, this);
- req.Send();
- if (req.cert)
+ if (!sslm.API)
+ return MOD_RES_DENY;
+
+ ssl_cert* cert = sslm.API->GetCertificate(user);
+ if (cert)
{
// Let them in
return MOD_RES_PASSTHRU;
else
{
// Deny
- user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick.c_str(), cname);
+ user->WriteNumeric(489, "%s :Cannot join channel; SSL users only (+z)", cname.c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
}
- ModResult OnCheckBan(User *user, Channel *c, const std::string& mask)
+ ModResult OnCheckBan(User *user, Channel *c, const std::string& mask) CXX11_OVERRIDE
{
if ((mask.length() > 2) && (mask[0] == 'z') && (mask[1] == ':'))
{
- UserCertificateRequest req(user, this);
- req.Send();
- if (req.cert && InspIRCd::Match(req.cert->GetFingerprint(), mask.substr(2)))
+ if (!sslm.API)
+ return MOD_RES_DENY;
+
+ ssl_cert* cert = sslm.API->GetCertificate(user);
+ if (cert && InspIRCd::Match(cert->GetFingerprint(), mask.substr(2)))
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- ~ModuleSSLModes()
- {
- }
-
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('z');
+ tokens["EXTBAN"].push_back('z');
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel mode +z to allow for Secure/SSL only channels", VF_VENDOR);
}
};
-
MODULE_INIT(ModuleSSLModes)
-
--- /dev/null
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2014 Adam <Adam@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd 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, version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/ssl.h"
+#include "modules/cap.h"
+
+// From IRCv3 tls-3.1
+enum
+{
+ RPL_STARTTLS = 670,
+ ERR_STARTTLS = 691
+};
+
+class CommandStartTLS : public SplitCommand
+{
+ dynamic_reference_nocheck<IOHookProvider>& ssl;
+
+ public:
+ CommandStartTLS(Module* mod, dynamic_reference_nocheck<IOHookProvider>& s)
+ : SplitCommand(mod, "STARTTLS")
+ , ssl(s)
+ {
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
+ {
+ if (!ssl)
+ {
+ user->WriteNumeric(ERR_STARTTLS, ":STARTTLS is not enabled");
+ return CMD_FAILURE;
+ }
+
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(ERR_STARTTLS, ":STARTTLS is not permitted after client registration is complete");
+ return CMD_FAILURE;
+ }
+
+ if (user->eh.GetIOHook())
+ {
+ user->WriteNumeric(ERR_STARTTLS, ":STARTTLS failure");
+ return CMD_FAILURE;
+ }
+
+ user->WriteNumeric(RPL_STARTTLS, ":STARTTLS successful, go ahead with TLS handshake");
+ /* We need to flush the write buffer prior to adding the IOHook,
+ * otherwise we'll be sending this line inside the SSL session - which
+ * won't start its handshake until the client gets this line. Currently,
+ * we assume the write will not block here; this is usually safe, as
+ * STARTTLS is sent very early on in the registration phase, where the
+ * user hasn't built up much sendq. Handling a blocked write here would
+ * be very annoying.
+ */
+ user->eh.DoWrite();
+
+ ssl->OnAccept(&user->eh, NULL, NULL);
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleStartTLS : public Module
+{
+ CommandStartTLS starttls;
+ GenericCap tls;
+ dynamic_reference_nocheck<IOHookProvider> ssl;
+
+ public:
+ ModuleStartTLS()
+ : starttls(this, ssl)
+ , tls(this, "tls")
+ , ssl(this, "ssl")
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("starttls");
+
+ std::string newprovider = conf->getString("provider");
+ if (newprovider.empty())
+ ssl.SetProvider("ssl");
+ else
+ ssl.SetProvider("ssl/" + newprovider);
+ }
+
+ void OnEvent(Event& ev) CXX11_OVERRIDE
+ {
+ tls.HandleEvent(ev);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ tokens["STARTTLS"];
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for the STARTTLS command", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleStartTLS)
#include "inspircd.h"
-/* $ModDesc: Provides channel +S mode (strip ansi color) */
-
/** Handles channel mode +S
*/
class ChannelStripColor : public SimpleChannelModeHandler
{
}
- void init()
- {
- ServerInstance->Modules->AddService(usc);
- ServerInstance->Modules->AddService(csc);
- Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual ~ModuleStripColor()
- {
- }
-
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->AddExtBanChar('S');
+ tokens["EXTBAN"].push_back('S');
}
- virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
+ ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return MOD_RES_PASSTHRU;
if (target_type == TYPE_USER)
{
User* t = (User*)dest;
- active = t->IsModeSet('S');
+ active = t->IsModeSet(usc);
}
else if (target_type == TYPE_CHANNEL)
{
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
- active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet('S'));
+ active = !t->GetExtBanStatus(user, 'S').check(!t->IsModeSet(csc));
}
if (active)
return MOD_RES_PASSTHRU;
}
- virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
- {
- return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides channel +S mode (strip ansi color)", VF_VENDOR);
}
#include "inspircd.h"
#include "xline.h"
-/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
-
namespace
{
bool silent;
class SVSHold : public XLine
{
public:
- irc::string nickname;
+ std::string nickname;
SVSHold(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& nick)
: XLine(s_time, d, src, re, "SVSHOLD")
{
- this->nickname = nick.c_str();
- }
-
- ~SVSHold()
- {
+ this->nickname = nick;
}
bool Matches(User *u)
bool Matches(const std::string &s)
{
- if (nickname == s)
- return true;
- return false;
+ return InspIRCd::Match(s, nickname);
}
void DisplayExpiry()
{
if (!silent)
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired SVSHOLD %s (set by %s %ld seconds ago)",
- this->nickname.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
+ ServerInstance->SNO->WriteToSnoMask('x', "Removing expired SVSHOLD %s (set by %s %ld seconds ago)",
+ nickname.c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
}
}
- const char* Displayable()
+ const std::string& Displayable()
{
- return nickname.c_str();
+ return nickname;
}
};
CommandSvshold(Module* Creator) : Command(Creator, "SVSHOLD", 1)
{
flags_needed = 'o'; this->syntax = "<nickname> [<duration> :<reason>]";
- TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
/* syntax: svshold nickname time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
{
/* don't allow SVSHOLD from non-ulined clients */
return CMD_FAILURE;
}
else
{
- user->WriteServ("NOTICE %s :*** SVSHOLD %s not found in list, try /stats S.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** SVSHOLD " + parameters[0] + " not found in list, try /stats S.");
}
}
else
if (parameters.size() < 3)
return CMD_FAILURE;
- // Adding - XXX todo make this respect <insane> tag perhaps..
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
SVSHold* r = new SVSHold(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
if (ServerInstance->XLines->AddLine(r, user))
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
- std::string timestr = ServerInstance->TimeString(c_requires_crap);
+ std::string timestr = InspIRCd::TimeString(c_requires_crap);
ServerInstance->SNO->WriteGlobalSno('x', "%s added timed SVSHOLD for %s, expires on %s: %s", user->nick.c_str(), parameters[0].c_str(), timestr.c_str(), parameters[2].c_str());
}
}
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->XLines->RegisterFactory(&s);
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnUserPreNick, I_OnStats, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- OnRehash(NULL);
}
- void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
ConfigTag* tag = ServerInstance->Config->ConfValue("svshold");
- silent = tag->getBool("silent");
+ silent = tag->getBool("silent", true);
}
- virtual ModResult OnStats(char symbol, User* user, string_list &out)
+ ModResult OnStats(char symbol, User* user, string_list &out) CXX11_OVERRIDE
{
if(symbol != 'S')
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
- virtual ModResult OnUserPreNick(User *user, const std::string &newnick)
+ ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
{
XLine *rl = ServerInstance->XLines->MatchesLine("SVSHOLD", newnick);
if (rl)
{
- user->WriteServ( "432 %s %s :Services reserved nickname: %s", user->nick.c_str(), newnick.c_str(), rl->reason.c_str());
+ user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Services reserved nickname: %s", newnick.c_str(), rl->reason.c_str());
return MOD_RES_DENY;
}
return MOD_RES_PASSTHRU;
}
- virtual ~ModuleSVSHold()
+ ~ModuleSVSHold()
{
ServerInstance->XLines->DelAll("SVSHOLD");
ServerInstance->XLines->UnregisterFactory(&s);
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services.", VF_COMMON | VF_VENDOR);
}
#include "inspircd.h"
-/* $ModDesc: Provides the SWHOIS command which allows setting of arbitrary WHOIS lines */
-
/** Handle /SWHOIS
*/
class CommandSwhois : public Command
CommandSwhois(Module* Creator) : Command(Creator,"SWHOIS", 2,2), swhois("swhois", Creator)
{
flags_needed = 'o'; syntax = "<nick> :<swhois>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User* user)
if ((!dest) || (IS_SERVER(dest))) // allow setting swhois using SWHOIS before reg
{
- user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
return CMD_FAILURE;
}
if (text)
{
// We already had it set...
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
// Ulines set SWHOISes silently
ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick.c_str(), dest->nick.c_str(), text->c_str(), parameters[1].c_str());
}
- else if (!ServerInstance->ULine(user->server))
+ else if (!user->server->IsULine())
{
// Ulines set SWHOISes silently
ServerInstance->SNO->WriteGlobalSno('a', "%s used SWHOIS to set %s's extra whois to '%s'", user->nick.c_str(), dest->nick.c_str(), parameters[1].c_str());
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(cmd.swhois);
- Implementation eventlist[] = { I_OnWhoisLine, I_OnPostOper };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
- ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
+ ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
{
/* We use this and not OnWhois because this triggers for remote, too */
if (numeric == 312)
std::string* swhois = cmd.swhois.get(dest);
if (swhois)
{
- ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), swhois->c_str());
+ ServerInstance->SendWhoisLine(user, dest, 320, "%s :%s", dest->nick.c_str(), swhois->c_str());
}
}
return MOD_RES_PASSTHRU;
}
- void OnPostOper(User* user, const std::string &opertype, const std::string &opername)
+ void OnPostOper(User* user, const std::string &opertype, const std::string &opername) CXX11_OVERRIDE
{
if (!IS_LOCAL(user))
return;
ServerInstance->PI->SendMetaData(user, "swhois", swhois);
}
- ~ModuleSWhois()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the SWHOIS command which allows setting of arbitrary WHOIS lines", VF_OPTCOMMON | VF_VENDOR);
}
*/
-/* $ModDesc: Provides a module for testing the server while linked in a network */
-
#include "inspircd.h"
-struct vtbase
-{
- virtual void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods) = 0;
- virtual ~vtbase() {}
-};
-
-template<typename T> struct vtable : public vtbase
-{
- union u {
- T function;
- struct v {
- size_t delta;
- size_t vtoff;
- } v;
- } u;
- vtable(T t) {
- u.function = t;
- }
- /** member function pointer dereference from vtable; depends on the GCC 4.4 ABI (x86_64) */
- template<typename E> void* read(E* obj)
- {
- if (u.v.delta & 1)
- {
- uint8_t* optr = reinterpret_cast<uint8_t*>(obj);
- optr += u.v.vtoff;
- uint8_t* vptr = *reinterpret_cast<uint8_t**>(optr);
- vptr += u.v.delta - 1;
- return *reinterpret_cast<void**>(vptr);
- }
- else
- return reinterpret_cast<void*>(u.v.delta);
- }
- void isok(const char* name, int impl, Module* basemod, std::vector<std::string>& allmods)
- {
- void* base = read(basemod);
- for(unsigned int i=0; i < allmods.size(); ++i)
- {
- Module* mod = ServerInstance->Modules->Find(allmods[i]);
- void* fptr = read(mod);
- for(EventHandlerIter j = ServerInstance->Modules->EventHandlers[impl].begin();
- j != ServerInstance->Modules->EventHandlers[impl].end(); j++)
- {
- if (mod == *j)
- {
- if (fptr == base)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "Module %s implements %s but uses default function",
- mod->ModuleSourceFile.c_str(), name);
- }
- goto done;
- }
- }
- if (fptr != base)
- {
- ServerInstance->SNO->WriteToSnoMask('a', "Module %s does not implement %s but overrides function",
- mod->ModuleSourceFile.c_str(), name);
- }
- done:;
- }
- }
-};
-
-template<typename T> vtbase* vtinit(T t)
-{
- return new vtable<T>(t);
-}
-
-static void checkall(Module* noimpl)
-{
- std::vector<std::string> allmods = ServerInstance->Modules->GetAllModuleNames(0);
-#define CHK(name) do { \
- vtbase* vt = vtinit(&Module::name); \
- vt->isok(#name, I_ ## name, noimpl, allmods); \
- delete vt; \
-} while (0)
- CHK(OnUserConnect);
- CHK(OnUserQuit);
- CHK(OnUserDisconnect);
- CHK(OnUserJoin);
- CHK(OnUserPart);
- CHK(OnRehash);
- CHK(OnSendSnotice);
- CHK(OnUserPreJoin);
- CHK(OnUserPreKick);
- CHK(OnUserKick);
- CHK(OnOper);
- CHK(OnInfo);
- CHK(OnWhois);
- CHK(OnUserPreInvite);
- CHK(OnUserInvite);
- CHK(OnUserPreMessage);
- CHK(OnUserPreNotice);
- CHK(OnUserPreNick);
- CHK(OnUserMessage);
- CHK(OnUserNotice);
- CHK(OnMode);
- CHK(OnGetServerDescription);
- CHK(OnSyncUser);
- CHK(OnSyncChannel);
- CHK(OnDecodeMetaData);
- CHK(OnWallops);
- CHK(OnAcceptConnection);
- CHK(OnChangeHost);
- CHK(OnChangeName);
- CHK(OnAddLine);
- CHK(OnDelLine);
- CHK(OnExpireLine);
- CHK(OnUserPostNick);
- CHK(OnPreMode);
- CHK(On005Numeric);
- CHK(OnKill);
- CHK(OnRemoteKill);
- CHK(OnLoadModule);
- CHK(OnUnloadModule);
- CHK(OnBackgroundTimer);
- CHK(OnPreCommand);
- CHK(OnCheckReady);
- CHK(OnCheckInvite);
- CHK(OnRawMode);
- CHK(OnCheckKey);
- CHK(OnCheckLimit);
- CHK(OnCheckBan);
- CHK(OnCheckChannelBan);
- CHK(OnExtBanCheck);
- CHK(OnStats);
- CHK(OnChangeLocalUserHost);
- CHK(OnPreTopicChange);
- CHK(OnPostTopicChange);
- CHK(OnEvent);
- CHK(OnGlobalOper);
- CHK(OnPostConnect);
- CHK(OnAddBan);
- CHK(OnDelBan);
- CHK(OnChangeLocalUserGECOS);
- CHK(OnUserRegister);
- CHK(OnChannelPreDelete);
- CHK(OnChannelDelete);
- CHK(OnPostOper);
- CHK(OnSyncNetwork);
- CHK(OnSetAway);
- CHK(OnPostCommand);
- CHK(OnPostJoin);
- CHK(OnWhoisLine);
- CHK(OnBuildNeighborList);
- CHK(OnGarbageCollect);
- CHK(OnText);
- CHK(OnPassCompare);
- CHK(OnRunTestSuite);
- CHK(OnNamesListItem);
- CHK(OnNumeric);
- CHK(OnHookIO);
- CHK(OnPreRehash);
- CHK(OnModuleRehash);
- CHK(OnSendWhoLine);
- CHK(OnChangeIdent);
-}
-
class CommandTest : public Command
{
public:
{
IS_LOCAL(user)->CommandFloodPenalty += atoi(parameters[1].c_str());
}
- else if (parameters[0] == "check")
- {
- checkall(creator);
- ServerInstance->SNO->WriteToSnoMask('a', "Module check complete");
- }
return CMD_SUCCESS;
}
};
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
if (!strstr(ServerInstance->Config->ServerName.c_str(), ".test"))
throw ModuleException("Don't load modules without reading their descriptions!");
- ServerInstance->Modules->AddService(cmd);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides a module for testing the server while linked in a network", VF_VENDOR|VF_OPTCOMMON);
}
};
MODULE_INIT(ModuleTest)
-
*/
-/* $ModDesc: Adds timed bans */
-
#include "inspircd.h"
/** Holds a timed ban
CommandTban(Module* Creator) : Command(Creator,"TBAN", 3)
{
syntax = "<channel> <duration> <banmask>";
- TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
Channel* channel = ServerInstance->FindChan(parameters[0]);
if (!channel)
{
- user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
return CMD_FAILURE;
}
int cm = channel->GetPrefixValue(user);
if (cm < HALFOP_VALUE)
{
- user->WriteNumeric(482, "%s %s :You do not have permission to set bans on this channel",
- user->nick.c_str(), channel->name.c_str());
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have permission to set bans on this channel",
+ channel->name.c_str());
return CMD_FAILURE;
- }
+ }
TimedBan T;
std::string channelname = parameters[0];
- long duration = ServerInstance->Duration(parameters[1]);
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
unsigned long expire = duration + ServerInstance->Time();
if (duration < 1)
{
- user->WriteServ("NOTICE "+user->nick+" :Invalid ban time");
+ user->WriteNotice("Invalid ban time");
return CMD_FAILURE;
}
std::string mask = parameters[2];
- std::vector<std::string> setban;
- setban.push_back(parameters[0]);
- setban.push_back("+b");
bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
- if (!isextban && !ServerInstance->IsValidMask(mask))
+ if (!isextban && !InspIRCd::IsValidMask(mask))
mask.append("!*@*");
- if ((mask.length() > 250) || (!ServerInstance->IsValidMask(mask) && !isextban))
+
+ Modes::ChangeList setban;
+ setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ // Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
+ // make it so that the user sets the mode themselves
+ ServerInstance->Modes->Process(user, channel, NULL, setban);
+ if (ServerInstance->Modes->GetLastParse().empty())
{
- user->WriteServ("NOTICE "+user->nick+" :Invalid ban mask");
+ user->WriteNotice("Invalid ban mask");
return CMD_FAILURE;
}
- setban.push_back(mask);
- // use CallHandler to make it so that the user sets the mode
- // themselves
- ServerInstance->Parser->CallHandler("MODE",setban,user);
- for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
- if (!strcasecmp(i->data.c_str(), mask.c_str()))
- goto found;
- return CMD_FAILURE;
-found:
+
CUList tmp;
T.channel = channelname;
T.mask = mask;
}
};
-class ModuleTimedBans : public Module
+class BanWatcher : public ModeWatcher
{
- CommandTban cmd;
public:
- ModuleTimedBans()
- : cmd(this)
+ BanWatcher(Module* parent)
+ : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
{
}
- void init()
+ void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding)
{
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_OnDelBan, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
+ if (adding)
+ return;
- virtual ModResult OnDelBan(User* source, Channel* chan, const std::string &banmask)
- {
irc::string listitem = banmask.c_str();
irc::string thischan = chan->name.c_str();
- for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); i++)
+ for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
{
irc::string target = i->mask.c_str();
irc::string tchan = i->channel.c_str();
break;
}
}
- return MOD_RES_PASSTHRU;
}
+};
- virtual void OnBackgroundTimer(time_t curtime)
+class ModuleTimedBans : public Module
+{
+ CommandTban cmd;
+ BanWatcher banwatcher;
+
+ public:
+ ModuleTimedBans()
+ : cmd(this)
+ , banwatcher(this)
+ {
+ }
+
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
timedbans expired;
for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
Channel* cr = ServerInstance->FindChan(chan);
if (cr)
{
- std::vector<std::string> setban;
- setban.push_back(chan);
- setban.push_back("-b");
- setban.push_back(mask);
-
CUList empty;
std::string expiry = "*** Timed ban on " + chan + " expired.";
cr->WriteAllExcept(ServerInstance->FakeClient, true, '@', empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
ServerInstance->PI->SendChannelNotice(cr, '@', expiry);
- ServerInstance->SendGlobalMode(setban, ServerInstance->FakeClient);
+ Modes::ChangeList setban;
+ setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
}
}
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleTimedBans)
-
#include "inspircd.h"
-/* $ModDesc: Provides /tline command used to test who a mask matches */
-
/** Handle /TLINE
*/
class CommandTline : public Command
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
- float n_counted = 0;
- float n_matched = 0;
- float n_match_host = 0;
- float n_match_ip = 0;
+ unsigned int n_matched = 0;
+ unsigned int n_match_host = 0;
+ unsigned int n_match_ip = 0;
- for (user_hash::const_iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
{
- n_counted++;
if (InspIRCd::Match(u->second->GetFullRealHost(),parameters[0]))
{
n_matched++;
}
}
}
+
+ unsigned long n_counted = users.size();
if (n_matched)
- user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against %0.0f user(s) (%0.2f%% of the userbase). %0.0f by hostname and %0.0f by IP address.",user->nick.c_str(), n_counted, parameters[0].c_str(), n_matched, (n_matched/n_counted)*100, n_match_host, n_match_ip);
+ {
+ float p = (n_matched / (float)n_counted) * 100;
+ user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against %u user(s) (%0.2f%% of the userbase). %u by hostname and %u by IP address.", n_counted, parameters[0].c_str(), n_matched, p, n_match_host, n_match_ip));
+ }
else
- user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against no user(s).", user->nick.c_str(), n_counted, parameters[0].c_str());
+ user->WriteNotice(InspIRCd::Format("*** TLINE: Counted %lu user(s). Matched '%s' against no user(s).", n_counted, parameters[0].c_str()));
return CMD_SUCCESS;
}
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleTLine()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides /tline command used to test who a mask matches", VF_VENDOR);
}
};
MODULE_INIT(ModuleTLine)
-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* $ModDesc: Implements server-side topic locks and the server-to-server command SVSTOPIC */
-
#include "inspircd.h"
class CommandSVSTOPIC : public Command
CmdResult Handle(const std::vector<std::string> ¶meters, User *user)
{
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
{
// Ulines only
return CMD_FAILURE;
time_t topicts = ConvToInt(parameters[1]);
if (!topicts)
{
- ServerInstance->Logs->Log("m_topiclock", DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped.");
return CMD_INVALID;
}
{
}
- virtual ~FlagExtItem()
- {
- }
-
bool get(const Extensible* container) const
{
return (get_raw(container) != NULL);
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- ServerInstance->Modules->AddService(topiclock);
- ServerInstance->Modules->Attach(I_OnPreTopicChange, this);
- }
-
- ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic)
+ ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE
{
// Only fired for local users currently, but added a check anyway
if ((IS_LOCAL(user)) && (topiclock.get(chan)))
return MOD_RES_PASSTHRU;
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Implements server-side topic locks and the server-to-server command SVSTOPIC", VF_COMMON | VF_VENDOR);
}
#include "inspircd.h"
-#include "m_cap.h"
-
-/* $ModDesc: Provides the UHNAMES facility. */
+#include "modules/cap.h"
class ModuleUHNames : public Module
{
{
}
- void init()
- {
- Implementation eventlist[] = { I_OnEvent, I_OnPreCommand, I_OnNamesListItem, I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- ~ModuleUHNames()
- {
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the UHNAMES facility.",VF_VENDOR);
}
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- output.append(" UHNAMES");
+ tokens["UHNAMES"];
}
- ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+ ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
{
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
return MOD_RES_PASSTHRU;
}
- void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick)
+ ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (!cap.ext.get(issuer))
- return;
+ if (cap.ext.get(issuer))
+ nick = memb->user->GetFullHost();
- if (nick.empty())
- return;
-
- nick = memb->user->GetFullHost();
+ return MOD_RES_PASSTHRU;
}
- void OnEvent(Event& ev)
+ void OnEvent(Event& ev) CXX11_OVERRIDE
{
cap.HandleEvent(ev);
}
*/
-/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
-
#include "inspircd.h"
/** Handle /UNINVITE
CommandUninvite(Module* Creator) : Command(Creator,"UNINVITE", 2)
{
syntax = "<nick> <channel>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_NICK, TR_TEXT);
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
if (!c)
{
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
+ user->WriteNumeric(401, "%s :No such nick/channel", parameters[1].c_str());
}
else
{
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
+ user->WriteNumeric(401, "%s :No such nick/channel", parameters[0].c_str());
}
return CMD_FAILURE;
{
if (c->GetPrefixValue(user) < HALFOP_VALUE)
{
- user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator", user->nick.c_str(), c->name.c_str(), c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-");
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator", c->name.c_str(), c->GetPrefixValue(u) == HALFOP_VALUE ? "" : "half-");
return CMD_FAILURE;
}
}
LocalUser* lu = IS_LOCAL(u);
if (lu)
{
- irc::string xname(c->name.c_str());
- if (!lu->IsInvited(xname))
+ if (!lu->RemoveInvite(c))
{
- user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server.c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
+ user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server->GetName().c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
return CMD_FAILURE;
}
- user->SendText(":%s 494 %s %s %s :Uninvited", user->server.c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
- lu->RemoveInvite(xname);
- lu->WriteNumeric(493, "%s :You were uninvited from %s by %s", u->nick.c_str(), c->name.c_str(), user->nick.c_str());
+ user->SendText(":%s 494 %s %s %s :Uninvited", user->server->GetName().c_str(), user->nick.c_str(), c->name.c_str(), u->nick.c_str());
+ lu->WriteNumeric(493, ":You were uninvited from %s by %s", c->name.c_str(), user->nick.c_str());
std::string msg = "*** " + user->nick + " uninvited " + u->nick + ".";
c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE " + c->name + " :" + msg);
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleUninvite()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides the UNINVITE command which lets users un-invite other users from channels", VF_VENDOR | VF_OPTCOMMON);
}
};
MODULE_INIT(ModuleUninvite)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for USERIP command */
-
/** Handle /USERIP
*/
class CommandUserip : public Command
checked_privs = true;
has_privs = user->HasPrivPermission("users/auspex");
if (!has_privs)
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
}
if (!has_privs)
continue;
}
- retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=";
- if (IS_AWAY(u))
+ retbuf = retbuf + u->nick + (u->IsOper() ? "*" : "") + "=";
+ if (u->IsAway())
retbuf += "-";
else
retbuf += "+";
{
}
- void init()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ tokens["USERIP"];
}
- virtual void On005Numeric(std::string &output)
- {
- output = output + " USERIP";
- }
-
- virtual ~ModuleUserIP()
- {
- }
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for USERIP command",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleUserIP)
-
#include "inspircd.h"
-/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
-
/** Handle /VHOST
*/
class CommandVhost : public Command
std::string pass = tag->getString("pass");
std::string hash = tag->getString("hash");
- if (parameters[0] == username && !ServerInstance->PassCompare(user, pass, parameters[1], hash))
+ if (parameters[0] == username && ServerInstance->PassCompare(user, pass, parameters[1], hash))
{
if (!mask.empty())
{
- user->WriteServ("NOTICE "+user->nick+" :Setting your VHost: " + mask);
- user->ChangeDisplayedHost(mask.c_str());
+ user->WriteNotice("Setting your VHost: " + mask);
+ user->ChangeDisplayedHost(mask);
return CMD_SUCCESS;
}
}
}
- user->WriteServ("NOTICE "+user->nick+" :Invalid username or password.");
+ user->WriteNotice("Invalid username or password.");
return CMD_FAILURE;
}
};
class ModuleVHost : public Module
{
- private:
CommandVhost cmd;
public:
{
}
- void init()
- {
- ServerInstance->Modules->AddService(cmd);
- }
-
- virtual ~ModuleVHost()
- {
- }
-
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides masking of user hostnames via traditional /VHOST command",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleVHost)
-
#include "inspircd.h"
-/* $ModDesc: Provides support for the /WATCH command */
-
/*
* Okay, it's nice that this was documented and all, but I at least understood very little
* of users using WATCH.
*/
-/*
- * Before you start screaming, this definition is only used here, so moving it to a header is pointless.
- * Yes, it's horrid. Blame cl for being different. -- w00t
- */
-
-typedef nspace::hash_map<irc::string, std::deque<User*>, irc::hash> watchentries;
+typedef TR1NS::unordered_map<irc::string, std::deque<User*>, irc::hash> watchentries;
typedef std::map<irc::string, std::string> watchlist;
/* Who's watching each nickname.
CommandSVSWatch(Module* Creator) : Command(Creator,"SVSWATCH", 2)
{
syntax = "<target> [C|L|S]|[+|-<nick>]";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
+ TRANSLATE2(TR_NICK, TR_TEXT); /* we watch for a nick. not a UID. */
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
{
- if (!ServerInstance->ULine(user->server))
+ if (!user->server->IsULine())
return CMD_FAILURE;
User *u = ServerInstance->FindNick(parameters[0]);
if (IS_LOCAL(u))
{
- ServerInstance->Parser->CallHandler("WATCH", parameters, u);
+ ServerInstance->Parser.CallHandler("WATCH", parameters, u);
}
return CMD_SUCCESS;
CmdResult remove_watch(User* user, const char* nick)
{
// removing an item from the list
- if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(nick))
{
- user->WriteNumeric(942, "%s %s :Invalid nickname", user->nick.c_str(), nick);
+ user->WriteNumeric(942, "%s :Invalid nickname", nick);
return CMD_FAILURE;
}
if (n != wl->end())
{
if (!n->second.empty())
- user->WriteNumeric(602, "%s %s %s :stopped watching", user->nick.c_str(), n->first.c_str(), n->second.c_str());
+ user->WriteNumeric(602, "%s %s :stopped watching", n->first.c_str(), n->second.c_str());
else
- user->WriteNumeric(602, "%s %s * * 0 :stopped watching", user->nick.c_str(), nick);
+ user->WriteNumeric(602, "%s * * 0 :stopped watching", nick);
wl->erase(n);
}
CmdResult add_watch(User* user, const char* nick)
{
- if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
+ if (!ServerInstance->IsNick(nick))
{
- user->WriteNumeric(942, "%s %s :Invalid nickname",user->nick.c_str(),nick);
+ user->WriteNumeric(942, "%s :Invalid nickname", nick);
return CMD_FAILURE;
}
if (wl->size() >= MAX_WATCH)
{
- user->WriteNumeric(512, "%s %s :Too many WATCH entries", user->nick.c_str(), nick);
+ user->WriteNumeric(512, "%s :Too many WATCH entries", nick);
return CMD_FAILURE;
}
if ((target) && (target->registered == REG_ALL))
{
(*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
- user->WriteNumeric(604, "%s %s %s :is online",user->nick.c_str(), nick, (*wl)[nick].c_str());
- if (IS_AWAY(target))
+ user->WriteNumeric(604, "%s %s :is online", nick, (*wl)[nick].c_str());
+ if (target->IsAway())
{
- user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
+ user->WriteNumeric(609, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
}
}
else
{
(*wl)[nick].clear();
- user->WriteNumeric(605, "%s %s * * 0 :is offline",user->nick.c_str(), nick);
+ user->WriteNumeric(605, "%s * * 0 :is offline", nick);
}
}
CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", parent)
{
syntax = "[C|L|S]|[+|-<nick>]";
- TRANSLATE2(TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
}
CmdResult Handle (const std::vector<std::string> ¶meters, User *user)
for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
{
if (!q->second.empty())
- user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
+ user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
}
}
- user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
+ user->WriteNumeric(607, ":End of WATCH list");
}
else if (parameters.size() > 0)
{
User* targ = ServerInstance->FindNick(q->first.c_str());
if (targ && !q->second.empty())
{
- user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
- if (IS_AWAY(targ))
+ user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
+ if (targ->IsAway())
{
- user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
+ user->WriteNumeric(609, "%s %s %s %lu :is away", targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
}
}
else
- user->WriteNumeric(605, "%s %s * * 0 :is offline", user->nick.c_str(), q->first.c_str());
+ user->WriteNumeric(605, "%s * * 0 :is offline", q->first.c_str());
}
}
- user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
+ user->WriteNumeric(607, ":End of WATCH list");
}
else if (!strcasecmp(nick,"S"))
{
if (i2 != whos_watching_me->end())
youre_on = i2->second.size();
- user->WriteNumeric(603, "%s :You have %d and are on %d WATCH entries", user->nick.c_str(), you_have, youre_on);
- user->WriteNumeric(606, "%s :%s",user->nick.c_str(), list.c_str());
- user->WriteNumeric(607, "%s :End of WATCH S",user->nick.c_str());
+ user->WriteNumeric(603, ":You have %d and are on %d WATCH entries", you_have, youre_on);
+ user->WriteNumeric(606, ":%s", list.c_str());
+ user->WriteNumeric(607, ":End of WATCH S");
}
else if (nick[0] == '-')
{
whos_watching_me = new watchentries();
}
- void init()
- {
- OnRehash(NULL);
- ServerInstance->Modules->AddService(cmdw);
- ServerInstance->Modules->AddService(sw);
- ServerInstance->Modules->AddService(cmdw.ext);
- Implementation eventlist[] = { I_OnRehash, I_OnGarbageCollect, I_OnUserQuit, I_OnPostConnect, I_OnUserPostNick, I_On005Numeric, I_OnSetAway };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void OnRehash(User* user)
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
maxwatch = ServerInstance->Config->ConfValue("watch")->getInt("maxentries", 32);
if (!maxwatch)
maxwatch = 32;
}
- virtual ModResult OnSetAway(User *user, const std::string &awaymsg)
+ ModResult OnSetAway(User *user, const std::string &awaymsg) CXX11_OVERRIDE
{
std::string numeric;
int inum;
{
for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
{
- (*n)->WriteNumeric(inum, (*n)->nick + " " + numeric);
+ (*n)->WriteNumeric(inum, numeric);
}
}
return MOD_RES_PASSTHRU;
}
- virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
+ void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
{
watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
if (x != whos_watching_me->end())
{
for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
{
- (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str() ,user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
+ (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
watchlist* wl = cmdw.ext.get(*n);
if (wl)
}
}
- virtual void OnGarbageCollect()
+ void OnGarbageCollect()
{
watchentries* old_watch = whos_watching_me;
whos_watching_me = new watchentries();
delete old_watch;
}
- virtual void OnPostConnect(User* user)
+ void OnPostConnect(User* user) CXX11_OVERRIDE
{
watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
if (x != whos_watching_me->end())
{
for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
{
- (*n)->WriteNumeric(600, "%s %s %s %s %lu :arrived online", (*n)->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
+ (*n)->WriteNumeric(600, "%s %s %s %lu :arrived online", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
watchlist* wl = cmdw.ext.get(*n);
if (wl)
}
}
- virtual void OnUserPostNick(User* user, const std::string &oldnick)
+ void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
{
watchentries::iterator new_offline = whos_watching_me->find(oldnick.c_str());
watchentries::iterator new_online = whos_watching_me->find(user->nick.c_str());
watchlist* wl = cmdw.ext.get(*n);
if (wl)
{
- (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str(), oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
+ (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
(*wl)[oldnick.c_str()].clear();
}
}
if (wl)
{
(*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
- (*n)->WriteNumeric(600, "%s %s %s :arrived online", (*n)->nick.c_str(), user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
+ (*n)->WriteNumeric(600, "%s %s :arrived online", user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
}
}
}
}
- virtual void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
- // we don't really have a limit...
- output = output + " WATCH=" + ConvToStr(maxwatch);
+ tokens["WATCH"] = ConvToStr(maxwatch);
}
- virtual ~Modulewatch()
+ ~Modulewatch()
{
delete whos_watching_me;
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for the /WATCH command", VF_OPTCOMMON | VF_VENDOR);
}
};
MODULE_INIT(Modulewatch)
-
#include "inspircd.h"
#include "xline.h"
-
-/* $ModConfig: <xlinedb filename="data/xline.db">
- * Specify the filename for the xline database here*/
-/* $ModDesc: Keeps a dynamic log of all XLines created, and stores them in a seperate conf file (xline.db). */
+#include <fstream>
class ModuleXLineDB : public Module
{
bool dirty;
std::string xlinedbpath;
public:
- void init()
+ void init() CXX11_OVERRIDE
{
/* Load the configuration
* Note:
- * this is on purpose not in the OnRehash() method. It would be non-trivial to change the database on-the-fly.
+ * This is on purpose not changed on a rehash. It would be non-trivial to change the database on-the-fly.
* Imagine a scenario where the new file already exists. Merging the current XLines with the existing database is likely a bad idea
* ...and so is discarding all current in-memory XLines for the ones in the database.
*/
ConfigTag* Conf = ServerInstance->Config->ConfValue("xlinedb");
- xlinedbpath = Conf->getString("filename", DATA_PATH "/xline.db");
+ xlinedbpath = ServerInstance->Config->Paths.PrependData(Conf->getString("filename", "xline.db"));
// Read xlines before attaching to events
ReadDatabase();
- Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
dirty = false;
}
- virtual ~ModuleXLineDB()
- {
- }
-
/** Called whenever an xline is added by a local user.
* This method is triggered after the line is added.
* @param source The sender of the line or NULL for local server
* @param line The xline being added
*/
- void OnAddLine(User* source, XLine* line)
+ void OnAddLine(User* source, XLine* line) CXX11_OVERRIDE
{
dirty = true;
}
* @param source The user removing the line or NULL for local server
* @param line the line being deleted
*/
- void OnDelLine(User* source, XLine* line)
+ void OnDelLine(User* source, XLine* line) CXX11_OVERRIDE
{
dirty = true;
}
- void OnExpireLine(XLine *line)
+ void OnExpireLine(XLine *line) CXX11_OVERRIDE
{
dirty = true;
}
- void OnBackgroundTimer(time_t now)
+ void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
{
if (dirty)
{
bool WriteDatabase()
{
- FILE *f;
-
/*
* We need to perform an atomic write so as not to fuck things up.
- * So, let's write to a temporary file, flush and sync the FD, then rename the file..
+ * So, let's write to a temporary file, flush it, then rename the file..
* Technically, that means that this can block, but I have *never* seen that.
- * -- w00t
+ * -- w00t
*/
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opening temporary database");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opening temporary database");
std::string xlinenewdbpath = xlinedbpath + ".new";
- f = fopen(xlinenewdbpath.c_str(), "w");
- if (!f)
+ std::ofstream stream(xlinenewdbpath.c_str());
+ if (!stream.is_open())
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot create database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot create database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opened. Writing..");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Opened. Writing..");
/*
* Now, much as I hate writing semi-unportable formats, additional
* semblance of backwards compatibility for reading on startup..
* -- w00t
*/
- fprintf(f, "VERSION 1\n");
+ stream << "VERSION 1" << std::endl;
// Now, let's write.
std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
{
XLine* line = i->second;
- fprintf(f, "LINE %s %s %s %lu %lu :%s\n", line->type.c_str(), line->Displayable(),
- ServerInstance->Config->ServerName.c_str(), (unsigned long)line->set_time, (unsigned long)line->duration, line->reason.c_str());
+ stream << "LINE " << line->type << " " << line->Displayable() << " "
+ << ServerInstance->Config->ServerName << " " << line->set_time << " "
+ << line->duration << " " << line->reason << std::endl;
}
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Finished writing XLines. Checking for error..");
- int write_error = 0;
- write_error = ferror(f);
- write_error |= fclose(f);
- if (write_error)
+ if (stream.fail())
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot write to new database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot write to new database \"%s\"! %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new xline db \"%s\": %s (%d)", xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
+ stream.close();
#ifdef _WIN32
remove(xlinedbpath.c_str());
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
if (rename(xlinenewdbpath.c_str(), xlinedbpath.c_str()) < 0)
{
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot move new to old database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot replace old database \"%s\" with new database \"%s\"! %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old xline db \"%s\" with new db \"%s\": %s (%d)", xlinedbpath.c_str(), xlinenewdbpath.c_str(), strerror(errno), errno);
return false;
}
bool ReadDatabase()
{
- FILE *f;
- char linebuf[MAXBUF];
+ // If the xline database doesn't exist then we don't need to load it.
+ if (!FileSystem::FileExists(xlinedbpath))
+ return true;
- f = fopen(xlinedbpath.c_str(), "r");
- if (!f)
+ std::ifstream stream(xlinedbpath.c_str());
+ if (!stream.is_open())
{
- if (errno == ENOENT)
- {
- /* xline.db doesn't exist, fake good return value (we don't care about this) */
- return true;
- }
- else
- {
- /* this might be slightly more problematic. */
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
- ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
- return false;
- }
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot read database \"%s\"! %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read xline db \"%s\": %s (%d)", xlinedbpath.c_str(), strerror(errno), errno);
+ return false;
}
- while (fgets(linebuf, MAXBUF, f))
+ std::string line;
+ while (std::getline(stream, line))
{
- char *c = linebuf;
-
- while (c && *c)
- {
- if (*c == '\n')
- {
- *c = '\0';
- }
-
- c++;
- }
-
// Inspired by the command parser. :)
- irc::tokenstream tokens(linebuf);
+ irc::tokenstream tokens(line);
int items = 0;
std::string command_p[7];
std::string tmp;
items++;
}
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Processing %s", linebuf);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing %s", line.c_str());
if (command_p[0] == "VERSION")
{
- if (command_p[1] == "1")
- {
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
- }
- else
+ if (command_p[1] != "1")
{
- fclose(f);
- ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
+ stream.close();
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "I got database version %s - I don't understand it", command_p[1].c_str());
ServerInstance->SNO->WriteToSnoMask('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
return false;
}
delete xl;
}
}
-
- fclose(f);
+ stream.close();
return true;
}
-
-
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Keeps a dynamic log of all XLines created, and stores them in a separate conf file (xline.db).", VF_VENDOR);
}
};
MODULE_INIT(ModuleXLineDB)
-
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef SASL_H
-#define SASL_H
-
-class SASLFallback : public Event
-{
- public:
- const parameterlist& params;
- SASLFallback(Module* me, const parameterlist& p)
- : Event(me, "sasl_fallback"), params(p)
- {
- Send();
- }
-};
-
-#endif
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef SPANNINGTREE_H
-#define SPANNINGTREE_H
-
-struct AddServerEvent : public Event
-{
- const std::string servername;
- AddServerEvent(Module* me, const std::string& name)
- : Event(me, "new_server"), servername(name)
- {
- Send();
- }
-};
-
-struct DelServerEvent : public Event
-{
- const std::string servername;
- DelServerEvent(Module* me, const std::string& name)
- : Event(me, "lost_server"), servername(name)
- {
- Send();
- }
-};
-
-#endif
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef INSPIRCD_SQLAPI_3
-#define INSPIRCD_SQLAPI_3
-
-/** Defines the error types which SQLerror may be set to
- */
-enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
-
-/** A list of format parameters for an SQLquery object.
- */
-typedef std::vector<std::string> ParamL;
-
-typedef std::map<std::string, std::string> ParamM;
-
-class SQLEntry
-{
- public:
- std::string value;
- bool nul;
- SQLEntry() : nul(true) {}
- SQLEntry(const std::string& v) : value(v), nul(false) {}
- inline operator std::string&() { return value; }
-};
-
-typedef std::vector<SQLEntry> SQLEntries;
-
-/**
- * Result of an SQL query. Only valid inside OnResult
- */
-class SQLResult : public classbase
-{
- public:
- /**
- * Return the number of rows in the result.
- *
- * Note that if you have perfomed an INSERT or UPDATE query or other
- * query which will not return rows, this will return the number of
- * affected rows. In this case you SHOULD NEVER access any of the result
- * set rows, as there aren't any!
- * @returns Number of rows in the result set.
- */
- virtual int Rows() = 0;
-
- /**
- * Return a single row (result of the query). The internal row counter
- * is incremented by one.
- *
- * @param result Storage for the result data.
- * @returns true if there was a row, false if no row exists (end of
- * iteration)
- */
- virtual bool GetRow(SQLEntries& result) = 0;
-
- /** Returns column names for the items in this row
- */
- virtual void GetCols(std::vector<std::string>& result) = 0;
-};
-
-/** SQLerror holds the error state of a request.
- * The error string varies from database software to database software
- * and should be used to display informational error messages to users.
- */
-class SQLerror
-{
- public:
- /** The error id
- */
- SQLerrorNum id;
-
- /** The error string
- */
- std::string str;
-
- /** Initialize an SQLerror
- * @param i The error ID to set
- * @param s The (optional) error string to set
- */
- SQLerror(SQLerrorNum i, const std::string &s = "")
- : id(i), str(s)
- {
- }
-
- /** Return the error string for an error
- */
- const char* Str()
- {
- if(str.length())
- return str.c_str();
-
- switch(id)
- {
- case SQL_BAD_DBID:
- return "Invalid database ID";
- case SQL_BAD_CONN:
- return "Invalid connection";
- case SQL_QSEND_FAIL:
- return "Sending query failed";
- case SQL_QREPLY_FAIL:
- return "Getting query result failed";
- default:
- return "Unknown error";
- }
- }
-};
-
-/**
- * Object representing an SQL query. This should be allocated on the heap and
- * passed to an SQLProvider, which will free it when the query is complete or
- * when the querying module is unloaded.
- *
- * You should store whatever information is needed to have the callbacks work in
- * this object (UID of user, channel name, etc).
- */
-class SQLQuery : public classbase
-{
- public:
- ModuleRef creator;
-
- SQLQuery(Module* Creator) : creator(Creator) {}
- virtual ~SQLQuery() {}
-
- virtual void OnResult(SQLResult& result) = 0;
- /**
- * Called when the query fails
- */
- virtual void OnError(SQLerror& error) { }
-};
-
-/**
- * Provider object for SQL servers
- */
-class SQLProvider : public DataProvider
-{
- public:
- SQLProvider(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {}
- /** Submit an asynchronous SQL request
- * @param callback The result reporting point
- * @param query The hardcoded query string. If you have parameters to substitute, see below.
- */
- virtual void submit(SQLQuery* callback, const std::string& query) = 0;
-
- /** Submit an asynchronous SQL request
- * @param callback The result reporting point
- * @param format The simple parameterized query string ('?' parameters)
- * @param p Parameters to fill in for the '?' entries
- */
- virtual void submit(SQLQuery* callback, const std::string& format, const ParamL& p) = 0;
-
- /** Submit an asynchronous SQL request.
- * @param callback The result reporting point
- * @param format The parameterized query string ('$name' parameters)
- * @param p Parameters to fill in for the '$name' entries
- */
- virtual void submit(SQLQuery* callback, const std::string& format, const ParamM& p) = 0;
-
- /** Convenience function to prepare a map from a User* */
- void PopulateUserInfo(User* user, ParamM& userinfo)
- {
- userinfo["nick"] = user->nick;
- userinfo["host"] = user->host;
- userinfo["ip"] = user->GetIPString();
- userinfo["gecos"] = user->fullname;
- userinfo["ident"] = user->ident;
- userinfo["server"] = user->server;
- userinfo["uuid"] = user->uuid;
- }
-};
-
-#endif
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef SSL_H
-#define SSL_H
-
-#include <map>
-#include <string>
-
-/** ssl_cert is a class which abstracts SSL certificate
- * and key information.
- *
- * Because gnutls and openssl represent key information in
- * wildly different ways, this class allows it to be accessed
- * in a unified manner. These classes are attached to ssl-
- * connected local users using SSLCertExt
- */
-class ssl_cert : public refcountbase
-{
- public:
- std::string dn;
- std::string issuer;
- std::string error;
- std::string fingerprint;
- bool trusted, invalid, unknownsigner, revoked;
-
- ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
-
- /** Get certificate distinguished name
- * @return Certificate DN
- */
- const std::string& GetDN()
- {
- return dn;
- }
-
- /** Get Certificate issuer
- * @return Certificate issuer
- */
- const std::string& GetIssuer()
- {
- return issuer;
- }
-
- /** Get error string if an error has occured
- * @return The error associated with this users certificate,
- * or an empty string if there is no error.
- */
- const std::string& GetError()
- {
- return error;
- }
-
- /** Get key fingerprint.
- * @return The key fingerprint as a hex string.
- */
- const std::string& GetFingerprint()
- {
- return fingerprint;
- }
-
- /** Get trust status
- * @return True if this is a trusted certificate
- * (the certificate chain validates)
- */
- bool IsTrusted()
- {
- return trusted;
- }
-
- /** Get validity status
- * @return True if the certificate itself is
- * correctly formed.
- */
- bool IsInvalid()
- {
- return invalid;
- }
-
- /** Get signer status
- * @return True if the certificate appears to be
- * self-signed.
- */
- bool IsUnknownSigner()
- {
- return unknownsigner;
- }
-
- /** Get revokation status.
- * @return True if the certificate is revoked.
- * Note that this only works properly for GnuTLS
- * right now.
- */
- bool IsRevoked()
- {
- return revoked;
- }
-
- bool IsCAVerified()
- {
- return trusted && !invalid && !revoked && !unknownsigner && error.empty();
- }
-
- std::string GetMetaLine()
- {
- std::stringstream value;
- bool hasError = !error.empty();
- value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
- << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
- if (hasError)
- value << GetError();
- else
- value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
- return value.str();
- }
-};
-
-/** Get certificate from a socket (only useful with an SSL module) */
-struct SocketCertificateRequest : public Request
-{
- StreamSocket* const sock;
- ssl_cert* cert;
-
- SocketCertificateRequest(StreamSocket* ss, Module* Me)
- : Request(Me, ss->GetIOHook(), "GET_SSL_CERT"), sock(ss), cert(NULL)
- {
- Send();
- }
-
- std::string GetFingerprint()
- {
- if (cert)
- return cert->GetFingerprint();
- return "";
- }
-};
-
-/** Get certificate from a user (requires m_sslinfo) */
-struct UserCertificateRequest : public Request
-{
- User* const user;
- ssl_cert* cert;
-
- UserCertificateRequest(User* u, Module* Me, Module* info = ServerInstance->Modules->Find("m_sslinfo.so"))
- : Request(Me, info, "GET_USER_CERT"), user(u), cert(NULL)
- {
- Send();
- }
-
- std::string GetFingerprint()
- {
- if (cert)
- return cert->GetFingerprint();
- return "";
- }
-};
-
-class SSLRawSessionRequest : public Request
-{
- public:
- const int fd;
- void* data;
-
- SSLRawSessionRequest(int FD, Module* srcmod, Module* destmod)
- : Request(srcmod, destmod, "GET_RAW_SSL_SESSION")
- , fd(FD)
- , data(NULL)
- {
- Send();
- }
-};
-
-#endif
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef INSPIRCD_LISTMODE_PROVIDER
-#define INSPIRCD_LISTMODE_PROVIDER
-
-/** Get the time as a string
- */
-inline std::string stringtime()
-{
- std::ostringstream TIME;
- TIME << ServerInstance->Time();
- return TIME.str();
-}
-
-/** An item in a listmode's list
- */
-class ListItem
-{
-public:
- std::string nick;
- std::string mask;
- std::string time;
-};
-
-/** The number of items a listmode's list may contain
- */
-class ListLimit
-{
-public:
- std::string mask;
- unsigned int limit;
-};
-
-/** Items stored in the channel's list
- */
-typedef std::list<ListItem> modelist;
-/** Max items per channel by name
- */
-typedef std::list<ListLimit> limitlist;
-
-/** The base class for list modes, should be inherited.
- */
-class ListModeBase : public ModeHandler
-{
- protected:
- /** Numeric to use when outputting the list
- */
- unsigned int listnumeric;
- /** Numeric to indicate end of list
- */
- unsigned int endoflistnumeric;
- /** String to send for end of list
- */
- std::string endofliststring;
- /** Automatically tidy up entries
- */
- bool tidy;
- /** Config tag to check for max items per channel
- */
- std::string configtag;
- /** Limits on a per-channel basis read from the tag
- * specified in ListModeBase::configtag
- */
- limitlist chanlimits;
-
- public:
- /** Storage key
- */
- SimpleExtItem<modelist> extItem;
-
- /** Constructor.
- * @param Instance The creator of this class
- * @param modechar Mode character
- * @param eolstr End of list string
- * @pram lnum List numeric
- * @param eolnum End of list numeric
- * @param autotidy Automatically tidy list entries on add
- * @param ctag Configuration tag to get limits from
- */
- ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist")
- : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL),
- listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
- configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
- {
- list = true;
- }
-
- /** See mode.h
- */
- std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string ¶meter)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if(parameter == it->mask)
- {
- return std::make_pair(true, parameter);
- }
- }
- }
- return std::make_pair(false, parameter);
- }
-
- /** Display the list for this mode
- * @param user The user to send the list to
- * @param channel The channel the user is requesting the list for
- */
- virtual void DisplayList(User* user, Channel* channel)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
- {
- user->WriteNumeric(listnumeric, "%s %s %s %s %s", user->nick.c_str(), channel->name.c_str(), it->mask.c_str(), (it->nick.length() ? it->nick.c_str() : ServerInstance->Config->ServerName.c_str()), it->time.c_str());
- }
- }
- user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
- }
-
- virtual void DisplayEmptyList(User* user, Channel* channel)
- {
- user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
- }
-
- /** Remove all instances of the mode from a channel.
- * See mode.h
- * @param channel The channel to remove all instances of the mode from
- */
- virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
- {
- modelist* el = extItem.get(channel);
- if (el)
- {
- irc::modestacker modestack(false);
-
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (stack)
- stack->Push(this->GetModeChar(), it->mask);
- else
- modestack.Push(this->GetModeChar(), it->mask);
- }
-
- if (stack)
- return;
-
- std::vector<std::string> stackresult;
- stackresult.push_back(channel->name);
- while (modestack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
- stackresult.clear();
- stackresult.push_back(channel->name);
- }
- }
- }
-
- /** See mode.h
- */
- virtual void RemoveMode(User*, irc::modestacker* stack)
- {
- /* Listmodes dont get set on users */
- }
-
- /** Perform a rehash of this mode's configuration data
- */
- virtual void DoRehash()
- {
- ConfigTagList tags = ServerInstance->Config->ConfTags(configtag);
-
- chanlimits.clear();
-
- for (ConfigIter i = tags.first; i != tags.second; i++)
- {
- // For each <banlist> tag
- ConfigTag* c = i->second;
- ListLimit limit;
- limit.mask = c->getString("chan");
- limit.limit = c->getInt("limit");
-
- if (limit.mask.size() && limit.limit > 0)
- chanlimits.push_back(limit);
- }
-
- // Add the default entry. This is inserted last so if the user specifies a
- // wildcard record in the config it will take precedence over this entry.
- ListLimit limit;
- limit.mask = "*";
- limit.limit = 64;
- chanlimits.push_back(limit);
- }
-
- /** Populate the Implements list with the correct events for a List Mode
- */
- virtual void DoImplements(Module* m)
- {
- ServerInstance->Modules->AddService(extItem);
- this->DoRehash();
- Implementation eventlist[] = { I_OnSyncChannel, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, m, sizeof(eventlist)/sizeof(Implementation));
- }
-
- /** Handle the list mode.
- * See mode.h
- */
- virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding)
- {
- // Try and grab the list
- modelist* el = extItem.get(channel);
-
- if (adding)
- {
- if (tidy)
- ModeParser::CleanMask(parameter);
-
- if (parameter.length() > 250)
- return MODEACTION_DENY;
-
- // If there was no list
- if (!el)
- {
- // Make one
- el = new modelist;
- extItem.set(channel, el);
- }
-
- // Check if the item already exists in the list
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (parameter == it->mask)
- {
- /* Give a subclass a chance to error about this */
- TellAlreadyOnList(source, channel, parameter);
-
- // it does, deny the change
- return MODEACTION_DENY;
- }
- }
-
- unsigned int maxsize = 0;
-
- for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
- {
- if (InspIRCd::Match(channel->name, it->mask))
- {
- // We have a pattern matching the channel...
- maxsize = el->size();
- if (!IS_LOCAL(source) || (maxsize < it->limit))
- {
- /* Ok, it *could* be allowed, now give someone subclassing us
- * a chance to validate the parameter.
- * The param is passed by reference, so they can both modify it
- * and tell us if we allow it or not.
- *
- * eg, the subclass could:
- * 1) allow
- * 2) 'fix' parameter and then allow
- * 3) deny
- */
- if (ValidateParam(source, channel, parameter))
- {
- // And now add the mask onto the list...
- ListItem e;
- e.mask = parameter;
- e.nick = source->nick;
- e.time = stringtime();
-
- el->push_back(e);
- return MODEACTION_ALLOW;
- }
- else
- {
- /* If they deny it they have the job of giving an error message */
- return MODEACTION_DENY;
- }
- }
- }
- }
-
- /* List is full, give subclass a chance to send a custom message */
- if (!TellListTooLong(source, channel, parameter))
- {
- source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
- }
-
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- // We're taking the mode off
- if (el)
- {
- for (modelist::iterator it = el->begin(); it != el->end(); it++)
- {
- if (parameter == it->mask)
- {
- el->erase(it);
- if (el->empty())
- {
- extItem.unset(channel);
- }
- return MODEACTION_ALLOW;
- }
- }
- /* Tried to remove something that wasn't set */
- TellNotSet(source, channel, parameter);
- parameter.clear();
- return MODEACTION_DENY;
- }
- else
- {
- /* Hmm, taking an exception off a non-existant list, DIE */
- TellNotSet(source, channel, parameter);
- parameter.clear();
- return MODEACTION_DENY;
- }
- }
- return MODEACTION_DENY;
- }
-
- /** Syncronize channel item list with another server.
- * See modules.h
- * @param chan Channel to syncronize
- * @param proto Protocol module pointer
- * @param opaque Opaque connection handle
- */
- virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
- {
- modelist* mlist = extItem.get(chan);
- irc::modestacker modestack(true);
- std::vector<std::string> stackresult;
- std::vector<TranslateType> types;
- types.push_back(TR_TEXT);
- if (mlist)
- {
- for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
- {
- modestack.Push(std::string(1, mode)[0], it->mask);
- }
- }
- while (modestack.GetStackedLine(stackresult))
- {
- types.assign(stackresult.size(), this->GetTranslateType());
- proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
- stackresult.clear();
- }
- }
-
- /** Clean up module on unload
- * @param target_type Type of target to clean
- * @param item Item to clean
- */
- virtual void DoCleanup(int, void*)
- {
- }
-
- /** Validate parameters.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- * @return true if the parameter is valid
- */
- virtual bool ValidateParam(User*, Channel*, std::string&)
- {
- return true;
- }
-
- /** Tell the user the list is too long.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- * @return Ignored
- */
- virtual bool TellListTooLong(User*, Channel*, std::string&)
- {
- return false;
- }
-
- /** Tell the user an item is already on the list.
- * Overridden by implementing module.
- * @param source Source user adding the parameter
- * @param channel Channel the parameter is being added to
- * @param parameter The actual parameter being added
- */
- virtual void TellAlreadyOnList(User*, Channel*, std::string&)
- {
- }
-
- /** Tell the user that the parameter is not in the list.
- * Overridden by implementing module.
- * @param source Source user removing the parameter
- * @param channel Channel the parameter is being removed from
- * @param parameter The actual parameter being removed
- */
- virtual void TellNotSet(User*, Channel*, std::string&)
- {
- }
-};
-
-#endif
#include <signal.h>
#include "exitcodes.h"
#include "inspircd.h"
-#include "inspircd_version.h"
void InspIRCd::SignalHandler(int signal)
{
#else
if (signal == SIGHUP)
{
- Rehash("Caught SIGHUP");
+ ServerInstance->SNO->WriteGlobalSno('a', "Rehashing due to SIGHUP");
+ Rehash();
}
else if (signal == SIGTERM)
#endif
{
- Exit(signal);
+ Exit(EXIT_STATUS_SIGTERM);
}
}
#endif
this->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
this->Cleanup();
- delete this;
ServerInstance = NULL;
+ delete this;
exit (status);
}
-void RehashHandler::Call(const std::string &reason)
+void InspIRCd::Rehash(const std::string& uuid)
{
- ServerInstance->SNO->WriteToSnoMask('a', "Rehashing config file %s %s",ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()), reason.c_str());
- ServerInstance->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
if (!ServerInstance->ConfigThread)
{
- ServerInstance->ConfigThread = new ConfigReaderThread("");
- ServerInstance->Threads->Start(ServerInstance->ConfigThread);
+ ServerInstance->ConfigThread = new ConfigReaderThread(uuid);
+ ServerInstance->Threads.Start(ServerInstance->ConfigThread);
}
}
-std::string InspIRCd::GetVersionString(bool operstring)
+std::string InspIRCd::GetVersionString(bool getFullVersion)
{
- char versiondata[MAXBUF];
- if (operstring)
- {
- std::string sename = SE->GetName();
- snprintf(versiondata,MAXBUF,"%s %s :%s [%s,%s,%s]",VERSION, Config->ServerName.c_str(), SYSTEM,REVISION, sename.c_str(), Config->sid.c_str());
- }
- else
- snprintf(versiondata,MAXBUF,"%s %s :%s",BRANCH,Config->ServerName.c_str(),Config->CustomVersion.c_str());
- return versiondata;
+ if (getFullVersion)
+ return INSPIRCD_VERSION " " + Config->ServerName + " :" INSPIRCD_SYSTEM " [" INSPIRCD_REVISION "," INSPIRCD_SOCKETENGINE_NAME "," + Config->sid + "]";
+ return INSPIRCD_BRANCH " " + Config->ServerName + " :" + Config->CustomVersion;
}
-const char InspIRCd::LogHeader[] =
- "Log started for " VERSION " (" REVISION ", " MODULE_INIT_STR ")"
- " - compiled on " SYSTEM;
-
-void InspIRCd::BuildISupport()
+std::string UIDGenerator::GenerateSID(const std::string& servername, const std::string& serverdesc)
{
- // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
- std::stringstream v;
- v << "WALLCHOPS WALLVOICES MODES=" << Config->Limits.MaxModes << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << Config->Limits.NickMax;
- v << " CASEMAPPING=rfc1459 STATUSMSG=" << Modes->BuildPrefixes(false) << " CHARSET=ascii TOPICLEN=" << Config->Limits.MaxTopic << " KICKLEN=" << Config->Limits.MaxKick << " MAXTARGETS=" << Config->MaxTargets;
- v << " AWAYLEN=" << Config->Limits.MaxAway << " CHANMODES=" << this->Modes->GiveModeList(MASK_CHANNEL) << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU" << " CHANNELLEN=" << Config->Limits.ChanMax;
- Config->data005 = v.str();
- FOREACH_MOD(I_On005Numeric,On005Numeric(Config->data005));
- Config->Update005();
+ unsigned int sid = 0;
+
+ for (std::string::const_iterator i = servername.begin(); i != servername.end(); ++i)
+ sid = 5 * sid + *i;
+ for (std::string::const_iterator i = serverdesc.begin(); i != serverdesc.end(); ++i)
+ sid = 5 * sid + *i;
+
+ std::string sidstr = ConvToStr(sid % 1000);
+ sidstr.insert(0, 3 - sidstr.length(), '0');
+ return sidstr;
}
-void InspIRCd::IncrementUID(int pos)
+void UIDGenerator::IncrementUID(unsigned int pos)
{
/*
* Okay. The rules for generating a UID go like this...
* A again, in an iterative fashion.. so..
* AAA9 -> AABA, and so on. -- w00t
*/
- if ((pos == 3) && (current_uid[3] == '9'))
+
+ // If we hit Z, wrap around to 0.
+ if (current_uid[pos] == 'Z')
{
- // At pos 3, if we hit '9', we've run out of available UIDs, and need to reset to AAA..AAA.
- for (int i = 3; i < UUID_LENGTH-1; i++)
+ current_uid[pos] = '0';
+ }
+ else if (current_uid[pos] == '9')
+ {
+ /*
+ * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
+ * e.g. A9 -> BA -> BB ..
+ */
+ current_uid[pos] = 'A';
+ if (pos == 3)
{
- current_uid[i] = 'A';
+ // At pos 3, if we hit '9', we've run out of available UIDs, and reset to AAA..AAA.
+ return;
}
+ this->IncrementUID(pos - 1);
}
else
{
- // If we hit Z, wrap around to 0.
- if (current_uid[pos] == 'Z')
- {
- current_uid[pos] = '0';
- }
- else if (current_uid[pos] == '9')
- {
- /*
- * Or, if we hit 9, wrap around to pos = 'A' and (pos - 1)++,
- * e.g. A9 -> BA -> BB ..
- */
- current_uid[pos] = 'A';
- this->IncrementUID(pos - 1);
- }
- else
- {
- // Anything else, nobody gives a shit. Just increment.
- current_uid[pos]++;
- }
+ // Anything else, nobody gives a shit. Just increment.
+ current_uid[pos]++;
}
}
-/*
- * Retrieve the next valid UUID that is free for this server.
- */
-std::string InspIRCd::GetUID()
+void UIDGenerator::init(const std::string& sid)
{
- static bool inited = false;
-
/*
- * If we're setting up, copy SID into the first three digits, 9's to the rest, null term at the end
+ * Copy SID into the first three digits, 9's to the rest, null term at the end
* Why 9? Well, we increment before we find, otherwise we have an unnecessary copy, and I want UID to start at AAA..AA
* and not AA..AB. So by initialising to 99999, we force it to rollover to AAAAA on the first IncrementUID call.
* Kind of silly, but I like how it looks.
* -- w
*/
- if (!inited)
- {
- inited = true;
- current_uid[0] = Config->sid[0];
- current_uid[1] = Config->sid[1];
- current_uid[2] = Config->sid[2];
-
- for (int i = 3; i < (UUID_LENGTH - 1); i++)
- current_uid[i] = '9';
- // Null terminator. Important.
- current_uid[UUID_LENGTH - 1] = '\0';
- }
+ current_uid.resize(UUID_LENGTH, '9');
+ current_uid[0] = sid[0];
+ current_uid[1] = sid[1];
+ current_uid[2] = sid[2];
+}
+/*
+ * Retrieve the next valid UUID that is free for this server.
+ */
+std::string UIDGenerator::GetUID()
+{
while (1)
{
// Add one to the last UID
- this->IncrementUID(UUID_LENGTH - 2);
+ this->IncrementUID(UUID_LENGTH - 1);
- if (this->FindUUID(current_uid))
- {
- /*
- * It's in use. We need to try the loop again.
- */
- continue;
- }
+ if (!ServerInstance->FindUUID(current_uid))
+ break;
- return current_uid;
+ /*
+ * It's in use. We need to try the loop again.
+ */
}
- /* not reached. */
- return "";
+ return current_uid;
}
+void ISupportManager::Build()
+{
+ /**
+ * This is currently the neatest way we can build the initial ISUPPORT map. In
+ * the future we can use an initializer list here.
+ */
+ std::map<std::string, std::string> tokens;
+
+ tokens["AWAYLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxAway);
+ tokens["CASEMAPPING"] = "rfc1459";
+ tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL);
+ tokens["CHANNELLEN"] = ConvToStr(ServerInstance->Config->Limits.ChanMax);
+ tokens["CHANTYPES"] = "#";
+ tokens["ELIST"] = "MU";
+ tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick);
+ tokens["MAXBANS"] = "64"; // TODO: make this a config setting.
+ tokens["MAXCHANNELS"] = ConvToStr(ServerInstance->Config->MaxChans);
+ tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets);
+ tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes);
+ tokens["NETWORK"] = ServerInstance->Config->Network;
+ tokens["NICKLEN"] = ConvToStr(ServerInstance->Config->Limits.NickMax);
+ tokens["PREFIX"] = ServerInstance->Modes->BuildPrefixes();
+ tokens["STATUSMSG"] = ServerInstance->Modes->BuildPrefixes(false);
+ tokens["TOPICLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxTopic);
+
+ tokens["FNC"] = tokens["VBANLIST"];
+
+ // Modules can add new tokens and also edit or remove existing tokens
+ FOREACH_MOD(On005Numeric, (tokens));
+
+ // EXTBAN is a special case as we need to sort it and prepend a comma.
+ std::map<std::string, std::string>::iterator extban = tokens.find("EXTBAN");
+ if (extban != tokens.end())
+ {
+ std::sort(extban->second.begin(), extban->second.end());
+ extban->second.insert(0, ",");
+ }
+ // Transform the map into a list of lines, ready to be sent to clients
+ std::string line;
+ unsigned int token_count = 0;
+ cachedlines.clear();
+ for (std::map<std::string, std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
+ {
+ line.append(it->first);
+
+ // If this token has a value then append a '=' char after the name and then the value itself
+ if (!it->second.empty())
+ line.append(1, '=').append(it->second);
+
+ // Always append a space, even if it's the last token because all lines will be suffixed
+ line.push_back(' ');
+ token_count++;
+
+ if (token_count % 13 == 12 || it == --tokens.end())
+ {
+ // Reached maximum number of tokens for this line or the current token
+ // is the last one; finalize the line and store it for later use
+ line.append(":are supported by this server");
+ cachedlines.push_back(line);
+ line.clear();
+ }
+ }
+}
+
+void ISupportManager::SendTo(LocalUser* user)
+{
+ for (std::vector<std::string>::const_iterator i = cachedlines.begin(); i != cachedlines.end(); ++i)
+ user->WriteNumeric(RPL_ISUPPORT, *i);
+}
#include "inspircd.h"
-#include <stdarg.h>
void SnomaskManager::FlushSnotices()
{
{
WriteToSnoMask(letter, text);
letter = toupper(letter);
- ServerInstance->PI->SendSNONotice(std::string(1, letter), text);
+ ServerInstance->PI->SendSNONotice(letter, text);
}
void SnomaskManager::WriteToSnoMask(char letter, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteToSnoMask(letter, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteToSnoMask(letter, textbuffer);
}
void SnomaskManager::WriteGlobalSno(char letter, const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteGlobalSno(letter, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteGlobalSno(letter, textbuffer);
}
SnomaskManager::SnomaskManager()
EnableSnomask('c',"CONNECT"); /* Local connect notices */
EnableSnomask('q',"QUIT"); /* Local quit notices */
EnableSnomask('k',"KILL"); /* Kill notices */
- EnableSnomask('l',"LINK"); /* Linking notices */
EnableSnomask('o',"OPER"); /* Oper up/down notices */
EnableSnomask('a',"ANNOUNCEMENT"); /* formerly WriteOpers() - generic notices to all opers */
EnableSnomask('d',"DEBUG"); /* Debug notices */
EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */
EnableSnomask('t',"STATS"); /* Local or remote stats request */
- EnableSnomask('f',"FLOOD"); /* Flooding notices */
}
-/*************************************************************************************/
+bool SnomaskManager::IsSnomaskUsable(char ch) const
+{
+ return ((isalpha(ch)) && (!masks[tolower(ch) - 'a'].Description.empty()));
+}
-void Snomask::SendMessage(const std::string &message, char mysnomask)
+Snomask::Snomask()
+ : Count(0)
{
- if (ServerInstance->Config->NoSnoticeStack || message != LastMessage || mysnomask != LastLetter)
+}
+
+void Snomask::SendMessage(const std::string& message, char letter)
+{
+ if ((!ServerInstance->Config->NoSnoticeStack) && (message == LastMessage) && (letter == LastLetter))
{
- this->Flush();
- LastMessage = message;
- LastLetter = mysnomask;
-
- std::string desc = Description;
- if (desc.empty())
- desc = std::string("SNO-") + (char)tolower(mysnomask);
- if (isupper(mysnomask))
- desc = "REMOTE" + desc;
- ModResult MOD_RESULT;
- ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), message.c_str());
-
- FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (mysnomask, desc, message));
-
- LastBlocked = (MOD_RESULT == MOD_RES_DENY);
-
- if (!LastBlocked)
- {
- /* Only opers can receive snotices, so we iterate the oper list */
- std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
- while (i != ServerInstance->Users->all_opers.end())
- {
- User* a = *i;
- if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(mysnomask) && !a->quitting)
- {
- a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), message.c_str());
- }
-
- i++;
- }
- }
+ Count++;
+ return;
}
+
+ this->Flush();
+
+ std::string desc = GetDescription(letter);
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnSendSnotice, MOD_RESULT, (letter, desc, message));
+ if (MOD_RESULT == MOD_RES_DENY)
+ return;
+
+ Snomask::Send(letter, desc, message);
+ LastMessage = message;
+ LastLetter = letter;
Count++;
}
{
if (Count > 1)
{
- std::string desc = Description;
- if (desc.empty())
- desc = std::string("SNO-") + (char)tolower(LastLetter);
- if (isupper(LastLetter))
- desc = "REMOTE" + desc;
- std::string mesg = "(last message repeated "+ConvToStr(Count)+" times)";
-
- ServerInstance->Logs->Log("snomask", DEFAULT, "%s: %s", desc.c_str(), mesg.c_str());
-
- FOREACH_MOD(I_OnSendSnotice, OnSendSnotice(LastLetter, desc, mesg));
-
- if (!LastBlocked)
- {
- /* Only opers can receive snotices, so we iterate the oper list */
- std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin();
-
- while (i != ServerInstance->Users->all_opers.end())
- {
- User* a = *i;
- if (IS_LOCAL(a) && a->IsModeSet('s') && a->IsNoticeMaskSet(LastLetter) && !a->quitting)
- {
- a->WriteServ("NOTICE %s :*** %s: %s", a->nick.c_str(), desc.c_str(), mesg.c_str());
- }
-
- i++;
- }
- }
+ std::string desc = GetDescription(LastLetter);
+ std::string msg = "(last message repeated " + ConvToStr(Count) + " times)";
+ FOREACH_MOD(OnSendSnotice, (LastLetter, desc, msg));
+ Snomask::Send(LastLetter, desc, msg);
}
+
LastMessage.clear();
- LastBlocked = false;
Count = 0;
}
+
+void Snomask::Send(char letter, const std::string& desc, const std::string& msg)
+{
+ std::string log = desc;
+ log.append(": ").append(msg);
+ ServerInstance->Logs->Log("snomask", LOG_DEFAULT, log);
+
+ std::string finalmsg = "*** ";
+ finalmsg.append(log);
+ /* Only opers can receive snotices, so we iterate the oper list */
+ const UserManager::OperList& opers = ServerInstance->Users->all_opers;
+ for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
+ {
+ User* user = *i;
+ // IsNoticeMaskSet() returns false for opers who aren't +s, no need to check for it seperately
+ if (IS_LOCAL(user) && user->IsNoticeMaskSet(letter))
+ user->WriteNotice(finalmsg);
+ }
+}
+
+std::string Snomask::GetDescription(char letter) const
+{
+ std::string ret;
+ if (isupper(letter))
+ ret = "REMOTE";
+ if (!Description.empty())
+ ret += Description;
+ else
+ ret += std::string("SNO-") + (char)tolower(letter);
+ return ret;
+}
#include "inspircd.h"
-#include "socket.h"
-#include "socketengine.h"
-using irc::sockets::sockaddrs;
-
-/** This will bind a socket to a port. It works for UDP/TCP.
- * It can only bind to IP addresses, if you wish to bind to hostnames
- * you should first resolve them using class 'Resolver'.
- */
-bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten)
-{
- sockaddrs servaddr;
- int ret;
-
- if ((*addr == '*' || *addr == '\0') && port == -1)
- {
- /* Port -1: Means UDP IPV4 port binding - Special case
- * used by DNS engine.
- */
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.in4.sin_family = AF_INET;
- }
- else if (!irc::sockets::aptosa(addr, port, servaddr))
- return false;
-
- ret = SE->Bind(sockfd, servaddr);
-
- if (ret < 0)
- {
- return false;
- }
- else
- {
- if (dolisten)
- {
- if (SE->Listen(sockfd, Config->MaxConn) == -1)
- {
- this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
- return false;
- }
- else
- {
- this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
- SE->NonBlocking(sockfd);
- return true;
- }
- }
- else
- {
- this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
- return true;
- }
- }
-}
int InspIRCd::BindPorts(FailedPortList &failed_ports)
{
std::string Addr = tag->getString("address");
if (strncasecmp(Addr.c_str(), "::ffff:", 7) == 0)
- this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
irc::portparser portrange(porttag, false);
int portno = -1;
if ((**n).bind_desc == bind_readable)
{
(*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
+ (*n)->ResetIOHookProvider();
+
skip = true;
old_ports.erase(n);
break;
n++;
if (n == ports.end())
{
- this->Logs->Log("SOCKET",DEFAULT,"Port bindings slipped out of vector, aborting close!");
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Port bindings slipped out of vector, aborting close!");
break;
}
- this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
+ this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
(**n).bind_desc.c_str());
delete *n;
std::string irc::sockets::sockaddrs::str() const
{
- char buffer[MAXBUF];
if (sa.sa_family == AF_INET)
{
- const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
- sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
+ char ipaddr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &in4.sin_addr, ipaddr, sizeof(ipaddr));
+ return InspIRCd::Format("%s:%u", ipaddr, ntohs(in4.sin_port));
}
- else if (sa.sa_family == AF_INET6)
+
+ if (sa.sa_family == AF_INET6)
{
- buffer[0] = '[';
- if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
- return "<unknown>"; // should never happen, buffer is large enough
- int len = strlen(buffer);
- // no need for snprintf, buffer has at least 9 chars left, max short len = 5
- sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
+ char ipaddr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &in6.sin6_addr, ipaddr, sizeof(ipaddr));
+ return InspIRCd::Format("[%s]:%u", ipaddr, ntohs(in6.sin6_port));
}
- else
- return "<unknown>";
- return std::string(buffer);
+
+ // This should never happen.
+ return "<unknown>";
}
int irc::sockets::sockaddrs::sa_size() const
irc::sockets::cidr_mask tmp(addr, length);
return tmp == *this;
}
-
#include "inspircd.h"
+
+/** Reference table, contains all current handlers
+ **/
+std::vector<EventHandler*> SocketEngine::ref;
+
+/** Current number of descriptors in the engine
+ */
+size_t SocketEngine::CurrentSetSize = 0;
+
+/** List of handlers that want a trial read/write
+ */
+std::set<int> SocketEngine::trials;
+
+int SocketEngine::MAX_DESCRIPTORS;
+
+/** Socket engine statistics: count of various events, bandwidth usage
+ */
+SocketEngine::Statistics SocketEngine::stats;
+
EventHandler::EventHandler()
{
fd = -1;
this->fd = FD;
}
-SocketEngine::SocketEngine()
-{
- TotalEvents = WriteEvents = ReadEvents = ErrorEvents = 0;
- lastempty = ServerInstance->Time();
- indata = outdata = 0;
-}
-
-SocketEngine::~SocketEngine()
-{
-}
-
-void SocketEngine::SetEventMask(EventHandler* eh, int mask)
-{
- eh->event_mask = mask;
-}
-
void SocketEngine::ChangeEventMask(EventHandler* eh, int change)
{
int old_m = eh->event_mask;
new_m &= ~FD_WANT_READ_MASK;
if (change & FD_WANT_WRITE_MASK)
new_m &= ~FD_WANT_WRITE_MASK;
-
+
// if adding a trial read/write, insert it into the set
if (change & FD_TRIAL_NOTE_MASK && !(old_m & FD_TRIAL_NOTE_MASK))
trials.insert(eh->GetFd());
}
}
-bool SocketEngine::HasFd(int fd)
+bool SocketEngine::AddFdRef(EventHandler* eh)
{
- if ((fd < 0) || (fd > GetMaxFds()))
+ int fd = eh->GetFd();
+ if (HasFd(fd))
return false;
- return (ref[fd] != NULL);
+
+ while (static_cast<unsigned int>(fd) >= ref.size())
+ ref.resize(ref.empty() ? 1 : (ref.size() * 2));
+ ref[fd] = eh;
+ CurrentSetSize++;
+ return true;
+}
+
+void SocketEngine::DelFdRef(EventHandler *eh)
+{
+ int fd = eh->GetFd();
+ if (GetRef(fd) == eh)
+ {
+ ref[fd] = NULL;
+ CurrentSetSize--;
+ }
+}
+
+bool SocketEngine::HasFd(int fd)
+{
+ return GetRef(fd) != NULL;
}
EventHandler* SocketEngine::GetRef(int fd)
{
- if ((fd < 0) || (fd > GetMaxFds()))
- return 0;
+ if (fd < 0 || static_cast<unsigned int>(fd) >= ref.size())
+ return NULL;
return ref[fd];
}
{
if (!eh)
return false;
- if ((eh->GetFd() < 0) || (eh->GetFd() > GetMaxFds()))
+ if (eh->GetFd() < 0)
return false;
return true;
}
return accept(fd->GetFd(), addr, addrlen);
}
-int SocketEngine::Close(EventHandler* fd)
+int SocketEngine::Close(EventHandler* eh)
{
-#ifdef _WIN32
- return closesocket(fd->GetFd());
-#else
- return close(fd->GetFd());
-#endif
+ DelFd(eh);
+ int ret = Close(eh->GetFd());
+ eh->SetFd(-1);
+ return ret;
}
int SocketEngine::Close(int fd)
{
int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen);
if (nbRecvd > 0)
- this->UpdateStats(nbRecvd, 0);
+ stats.Update(nbRecvd, 0);
return nbRecvd;
}
{
int nbSent = send(fd->GetFd(), (const char*)buf, len, flags);
if (nbSent > 0)
- this->UpdateStats(0, nbSent);
+ stats.Update(0, nbSent);
return nbSent;
}
{
int nbRecvd = recv(fd->GetFd(), (char*)buf, len, flags);
if (nbRecvd > 0)
- this->UpdateStats(nbRecvd, 0);
+ stats.Update(nbRecvd, 0);
return nbRecvd;
}
{
int nbSent = sendto(fd->GetFd(), (const char*)buf, len, flags, to, tolen);
if (nbSent > 0)
- this->UpdateStats(0, nbSent);
+ stats.Update(0, nbSent);
return nbSent;
}
return shutdown(fd, how);
}
-void SocketEngine::RecoverFromFork()
+void SocketEngine::Statistics::Update(size_t len_in, size_t len_out)
{
+ CheckFlush();
+ indata += len_in;
+ outdata += len_out;
}
-void SocketEngine::UpdateStats(size_t len_in, size_t len_out)
+void SocketEngine::Statistics::CheckFlush() const
{
- if (lastempty != ServerInstance->Time())
+ // Reset the in/out byte counters if it has been more than a second
+ time_t now = ServerInstance->Time();
+ if (lastempty != now)
{
- lastempty = ServerInstance->Time();
+ lastempty = now;
indata = outdata = 0;
}
- indata += len_in;
- outdata += len_out;
}
-void SocketEngine::GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total)
+void SocketEngine::Statistics::GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const
{
- UpdateStats(0, 0); /* Forces emptying of the values if its been more than a second */
+ CheckFlush();
float in_kbit = indata * 8;
float out_kbit = outdata * 8;
kbitpersec_total = ((in_kbit + out_kbit) / 1024);
*/
-#include <vector>
-#include <string>
-#include <map>
#include "inspircd.h"
#include "exitcodes.h"
-#include "socketengine.h"
+
#include <sys/epoll.h>
#include <ulimit.h>
#include <iostream>
/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
*/
-class EPollEngine : public SocketEngine
+namespace
{
-private:
- /** These are used by epoll() to hold socket events
- */
- struct epoll_event* events;
int EngineHandle;
-public:
- /** Create a new EPollEngine
- */
- EPollEngine();
- /** Delete an EPollEngine
+
+ /** These are used by epoll() to hold socket events
*/
- virtual ~EPollEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
+ std::vector<struct epoll_event> events(1);
+}
-EPollEngine::EPollEngine()
+void SocketEngine::Init()
{
- CurrentSetSize = 0;
- int max = ulimit(4, 0);
- if (max > 0)
- {
- MAX_DESCRIPTORS = max;
- }
- else
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
+ // MAX_DESCRIPTORS is mainly used for display purposes, no problem if ulimit() fails and returns a negative number
+ MAX_DESCRIPTORS = ulimit(4, 0);
- // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
- EngineHandle = epoll_create(GetMaxFds() / 4);
+ // 128 is not a maximum, just a hint at the eventual number of sockets that may be polled,
+ // and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero.
+ EngineHandle = epoll_create(128);
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
+}
- ref = new EventHandler* [GetMaxFds()];
- events = new struct epoll_event[GetMaxFds()];
-
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::RecoverFromFork()
+{
}
-EPollEngine::~EPollEngine()
+void SocketEngine::Deinit()
{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] events;
+ Close(EngineHandle);
}
static unsigned mask_to_epoll(int event_mask)
return rv;
}
-bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
return false;
}
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
+ memset(&ev, 0, sizeof(ev));
ev.events = mask_to_epoll(event_mask);
- ev.data.fd = fd;
+ ev.data.ptr = static_cast<void*>(eh);
int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
return false;
}
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
+
+ eh->SetEventMask(event_mask);
+ ResizeDouble(events);
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
- CurrentSetSize++;
return true;
}
-void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
unsigned old_events = mask_to_epoll(old_mask);
unsigned new_events = mask_to_epoll(new_mask);
{
// ok, we actually have something to tell the kernel about
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
+ memset(&ev, 0, sizeof(ev));
ev.events = new_events;
- ev.data.fd = eh->GetFd();
+ ev.data.ptr = static_cast<void*>(eh);
epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
}
}
-void EPollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
return;
}
+ // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
+ // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
+ // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
struct epoll_event ev;
- memset(&ev,0,sizeof(ev));
- ev.data.fd = fd;
int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
}
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
- CurrentSetSize--;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-int EPollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
- socklen_t codesize = sizeof(int);
- int errcode;
- int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
+ int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
ServerInstance->UpdateTime();
- TotalEvents += i;
+ stats.TotalEvents += i;
for (int j = 0; j < i; j++)
{
- EventHandler* eh = ref[events[j].data.fd];
- if (!eh)
- {
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
- epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
+ // Copy these in case the vector gets resized and ev invalidated
+ const epoll_event ev = events[j];
+
+ EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr);
+ const int fd = eh->GetFd();
+ if (fd < 0)
continue;
- }
- if (events[j].events & EPOLLHUP)
+
+ if (ev.events & EPOLLHUP)
{
- ErrorEvents++;
+ stats.ErrorEvents++;
eh->HandleEvent(EVENT_ERROR, 0);
continue;
}
- if (events[j].events & EPOLLERR)
+
+ if (ev.events & EPOLLERR)
{
- ErrorEvents++;
+ stats.ErrorEvents++;
/* Get error number */
- if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ socklen_t codesize = sizeof(int);
+ int errcode;
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
eh->HandleEvent(EVENT_ERROR, errcode);
continue;
}
+
int mask = eh->GetEventMask();
- if (events[j].events & EPOLLIN)
+ if (ev.events & EPOLLIN)
mask &= ~FD_READ_WILL_BLOCK;
- if (events[j].events & EPOLLOUT)
+ if (ev.events & EPOLLOUT)
{
mask &= ~FD_WRITE_WILL_BLOCK;
if (mask & FD_WANT_SINGLE_WRITE)
mask = nm;
}
}
- SetEventMask(eh, mask);
- if (events[j].events & EPOLLIN)
+ eh->SetEventMask(mask);
+ if (ev.events & EPOLLIN)
{
- ReadEvents++;
+ stats.ReadEvents++;
eh->HandleEvent(EVENT_READ);
- if (eh != ref[events[j].data.fd])
+ if (eh != GetRef(fd))
// whoa! we got deleted, better not give out the write event
continue;
}
- if (events[j].events & EPOLLOUT)
+ if (ev.events & EPOLLOUT)
{
- WriteEvents++;
+ stats.WriteEvents++;
eh->HandleEvent(EVENT_WRITE);
}
}
return i;
}
-
-std::string EPollEngine::GetName()
-{
- return "epoll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new EPollEngine;
-}
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
-#include "socketengine.h"
#include <iostream>
+#include <sys/sysctl.h>
/** A specialisation of the SocketEngine class, designed to use BSD kqueue().
*/
-class KQueueEngine : public SocketEngine
+namespace
{
-private:
int EngineHandle;
+ unsigned int ChangePos = 0;
/** These are used by kqueue() to hold socket events
*/
- struct kevent* ke_list;
- /** This is a specialised time value used by kqueue()
- */
- struct timespec ts;
-public:
- /** Create a new KQueueEngine
- */
- KQueueEngine();
- /** Delete a KQueueEngine
- */
- virtual ~KQueueEngine();
- bool AddFd(EventHandler* eh, int event_mask);
- void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
- virtual void RecoverFromFork();
-};
+ std::vector<struct kevent> ke_list(16);
-#include <sys/sysctl.h>
+ /** Pending changes
+ */
+ std::vector<struct kevent> changelist(8);
+}
-KQueueEngine::KQueueEngine()
+/** Initialize the kqueue engine
+ */
+void SocketEngine::Init()
{
MAX_DESCRIPTORS = 0;
int mib[2];
mib[1] = KERN_MAXFILES;
#endif
len = sizeof(MAX_DESCRIPTORS);
+ // MAX_DESCRIPTORS is mainly used for display purposes, no problem if the sysctl() below fails
sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
- if (MAX_DESCRIPTORS <= 0)
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
- this->RecoverFromFork();
- ke_list = new struct kevent[GetMaxFds()];
- ref = new EventHandler* [GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+ RecoverFromFork();
}
-void KQueueEngine::RecoverFromFork()
+void SocketEngine::RecoverFromFork()
{
/*
* The only bad thing about kqueue is that its fd cant survive a fork and is not inherited.
EngineHandle = kqueue();
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: this is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
- CurrentSetSize = 0;
}
-KQueueEngine::~KQueueEngine()
+/** Shutdown the kqueue engine
+ */
+void SocketEngine::Deinit()
{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] ke_list;
+ Close(EngineHandle);
}
-bool KQueueEngine::AddFd(EventHandler* eh, int event_mask)
+static struct kevent* GetChangeKE()
+{
+ if (ChangePos >= changelist.size())
+ changelist.resize(changelist.size() * 2);
+ return &changelist[ChangePos++];
+}
+
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
return false;
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
return false;
// We always want to read from the socket...
- struct kevent ke;
- EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<void*>(eh));
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i == -1)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s",
- fd, strerror(errno));
- return false;
- }
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
+ eh->SetEventMask(event_mask);
OnSetEvent(eh, 0, event_mask);
- CurrentSetSize++;
+ ResizeDouble(ke_list);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
return true;
}
-void KQueueEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
return;
}
- struct kevent ke;
-
// First remove the write filter ignoring errors, since we can't be
// sure if there are actually any write filters registered.
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
- kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
// Then remove the read filter.
- EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
- int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
+ ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
- if (j < 0)
- {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
- fd, strerror(errno));
- }
+ SocketEngine::DelFdRef(eh);
- CurrentSetSize--;
- ref[fd] = NULL;
-
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE))
{
// new poll-style write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, static_cast<void*>(eh));
}
else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE))
{
// removing poll-style write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
}
if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)))
{
- // new one-shot write
- struct kevent ke;
- EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
- int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
- if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
- eh->GetFd(), strerror(errno));
- }
+ struct kevent* ke = GetChangeKE();
+ EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast<void*>(eh));
}
}
-int KQueueEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
+ struct timespec ts;
ts.tv_nsec = 0;
ts.tv_sec = 1;
- int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts);
+ int i = kevent(EngineHandle, &changelist.front(), ChangePos, &ke_list.front(), ke_list.size(), &ts);
+ ChangePos = 0;
ServerInstance->UpdateTime();
- TotalEvents += i;
+ if (i < 0)
+ return i;
+
+ stats.TotalEvents += i;
for (int j = 0; j < i; j++)
{
- EventHandler* eh = ref[ke_list[j].ident];
+ struct kevent& kev = ke_list[j];
+ EventHandler* eh = static_cast<EventHandler*>(kev.udata);
if (!eh)
continue;
- if (ke_list[j].flags & EV_EOF)
+
+ // Copy these in case the vector gets resized and kev invalidated
+ const int fd = eh->GetFd();
+ const short filter = kev.filter;
+ if (fd < 0)
+ continue;
+
+ if (kev.flags & EV_EOF)
{
- ErrorEvents++;
- eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
+ stats.ErrorEvents++;
+ eh->HandleEvent(EVENT_ERROR, kev.fflags);
continue;
}
- if (ke_list[j].filter == EVFILT_WRITE)
+ if (filter == EVFILT_WRITE)
{
- WriteEvents++;
+ stats.WriteEvents++;
/* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE,
* we set a one-shot write, so we need to clear that bit
* to detect when it set again.
*/
const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
- SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr);
+ eh->SetEventMask(eh->GetEventMask() & ~bits_to_clr);
eh->HandleEvent(EVENT_WRITE);
-
- if (eh != ref[ke_list[j].ident])
- // whoops, deleted out from under us
- continue;
}
- if (ke_list[j].filter == EVFILT_READ)
+ else if (filter == EVFILT_READ)
{
- ReadEvents++;
- SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ stats.ReadEvents++;
+ eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
eh->HandleEvent(EVENT_READ);
}
}
return i;
}
-
-std::string KQueueEngine::GetName()
-{
- return "kqueue";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new KQueueEngine;
-}
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
* Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
*/
-#include "inspircd.h"
#include "exitcodes.h"
-
-#ifndef SOCKETENGINE_POLL
-#define SOCKETENGINE_POLL
-
-#include <iostream>
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
#include "inspircd.h"
-#include "socketengine.h"
-
-#ifndef _WIN32
-# ifndef __USE_XOPEN
-# define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
-# endif
-# include <poll.h>
-# include <sys/poll.h>
-# include <sys/resource.h>
-#else
-# define struct pollfd WSAPOLLFD
-# define poll WSAPoll
-#endif
-
-class InspIRCd;
+
+#include <sys/poll.h>
+#include <sys/resource.h>
/** A specialisation of the SocketEngine class, designed to use poll().
*/
-class PollEngine : public SocketEngine
+namespace
{
-private:
/** These are used by poll() to hold socket events
*/
- struct pollfd *events;
- /** This map maps fds to an index in the events array.
- */
- std::map<int, unsigned int> fd_mappings;
-public:
- /** Create a new PollEngine
+ std::vector<struct pollfd> events(16);
+ /** This vector maps fds to an index in the events array.
*/
- PollEngine();
- /** Delete a PollEngine
- */
- virtual ~PollEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual EventHandler* GetRef(int fd);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-#endif
-
-PollEngine::PollEngine()
+ std::vector<int> fd_mappings(16);
+}
+
+void SocketEngine::Init()
{
- CurrentSetSize = 0;
struct rlimit limits;
if (!getrlimit(RLIMIT_NOFILE, &limits))
{
}
else
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
- std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
+ // MAX_DESCRIPTORS is mainly used for display purposes, it's not a problem that getrlimit() failed
+ MAX_DESCRIPTORS = -1;
}
+}
- ref = new EventHandler* [GetMaxFds()];
- events = new struct pollfd[GetMaxFds()];
-
- memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Deinit()
+{
}
-PollEngine::~PollEngine()
+void SocketEngine::RecoverFromFork()
{
- // No destruction required, either.
- delete[] ref;
- delete[] events;
}
static int mask_to_poll(int event_mask)
return rv;
}
-bool PollEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
return false;
}
- if (fd_mappings.find(fd) != fd_mappings.end())
+ if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
unsigned int index = CurrentSetSize;
+ if (!SocketEngine::AddFdRef(eh))
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
+ return false;
+ }
+
+ while (static_cast<unsigned int>(fd) >= fd_mappings.size())
+ fd_mappings.resize(fd_mappings.size() * 2, -1);
fd_mappings[fd] = index;
- ref[index] = eh;
+
+ ResizeDouble(events);
events[index].fd = fd;
events[index].events = mask_to_poll(event_mask);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
- SocketEngine::SetEventMask(eh, event_mask);
- CurrentSetSize++;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
+ eh->SetEventMask(event_mask);
return true;
}
-EventHandler* PollEngine::GetRef(int fd)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
- std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
- if (it == fd_mappings.end())
- return NULL;
- return ref[it->second];
-}
-
-void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
-{
- std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
- if (it == fd_mappings.end())
+ int fd = eh->GetFd();
+ if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
return;
}
- events[it->second].events = mask_to_poll(new_mask);
+ events[fd_mappings[fd]].events = mask_to_poll(new_mask);
}
-void PollEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
return;
}
- std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
- if (it == fd_mappings.end())
+ if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
return;
}
- unsigned int index = it->second;
+ unsigned int index = fd_mappings[fd];
unsigned int last_index = CurrentSetSize - 1;
int last_fd = events[last_index].fd;
// move last_fd from last_index into index
events[index].fd = last_fd;
events[index].events = events[last_index].events;
-
- ref[index] = ref[last_index];
}
// Now remove all data for the last fd we got into out list.
// Above code made sure this always is right
- fd_mappings.erase(it);
+ fd_mappings[fd] = -1;
events[last_index].fd = 0;
events[last_index].events = 0;
- ref[last_index] = NULL;
- CurrentSetSize--;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
"(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
}
-int PollEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
- int i = poll(events, CurrentSetSize, 1000);
- int index;
- socklen_t codesize = sizeof(int);
- int errcode;
+ int i = poll(&events[0], CurrentSetSize, 1000);
int processed = 0;
ServerInstance->UpdateTime();
- if (i > 0)
+ for (int index = 0; index < CurrentSetSize && processed < i; index++)
{
- for (index = 0; index < CurrentSetSize && processed != i; index++)
+ struct pollfd& pfd = events[index];
+
+ // Copy these in case the vector gets resized and pfd invalidated
+ const int fd = pfd.fd;
+ const short revents = pfd.revents;
+
+ if (revents)
+ processed++;
+
+ EventHandler* eh = GetRef(fd);
+ if (!eh)
+ continue;
+
+ if (revents & POLLHUP)
{
- if (events[index].revents)
- processed++;
- EventHandler* eh = ref[index];
- if (!eh)
- continue;
+ eh->HandleEvent(EVENT_ERROR, 0);
+ continue;
+ }
+
+ if (revents & POLLERR)
+ {
+ // Get error number
+ socklen_t codesize = sizeof(int);
+ int errcode;
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
+ errcode = errno;
+ eh->HandleEvent(EVENT_ERROR, errcode);
+ continue;
+ }
- if (events[index].revents & POLLHUP)
- {
- eh->HandleEvent(EVENT_ERROR, 0);
+ if (revents & POLLIN)
+ {
+ eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ eh->HandleEvent(EVENT_READ);
+ if (eh != GetRef(fd))
+ // whoops, deleted out from under us
continue;
- }
+ }
- if (events[index].revents & POLLERR)
- {
- // Get fd
- int fd = events[index].fd;
+ if (revents & POLLOUT)
+ {
+ int mask = eh->GetEventMask();
+ mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
+ eh->SetEventMask(mask);
- // Get error number
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
- errcode = errno;
- eh->HandleEvent(EVENT_ERROR, errcode);
- continue;
- }
-
- if (events[index].revents & POLLIN)
- {
- SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
- eh->HandleEvent(EVENT_READ);
- if (eh != ref[index])
- // whoops, deleted out from under us
- continue;
- }
-
- if (events[index].revents & POLLOUT)
- {
- int mask = eh->GetEventMask();
- mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
- SetEventMask(eh, mask);
- events[index].events = mask_to_poll(mask);
- eh->HandleEvent(EVENT_WRITE);
- }
+ // The vector could've been resized, reference can be invalid by now; don't use it
+ events[index].events = mask_to_poll(mask);
+ eh->HandleEvent(EVENT_WRITE);
}
}
return i;
}
-
-std::string PollEngine::GetName()
-{
- return "poll";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new PollEngine;
-}
#include "inspircd.h"
#include "exitcodes.h"
-#include <port.h>
-
-#ifndef SOCKETENGINE_PORTS
-#define SOCKETENGINE_PORTS
#ifndef __sun
# error You need Solaris 10 or later to make use of this code.
#endif
-#include <vector>
-#include <string>
-#include <map>
-#include "inspircd_config.h"
#include "inspircd.h"
-#include "socketengine.h"
#include <port.h>
#include <iostream>
+#include <ulimit.h>
/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
*/
-class PortsEngine : public SocketEngine
+namespace
{
-private:
- /** These are used by epoll() to hold socket events
+ /** These are used by ports to hold socket events
*/
- port_event_t* events;
+ std::vector<port_event_t> events(16);
int EngineHandle;
-public:
- /** Create a new PortsEngine
- */
- PortsEngine();
- /** Delete a PortsEngine
- */
- virtual ~PortsEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
- virtual void DelFd(EventHandler* eh);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-#endif
-
-
-#include <ulimit.h>
+}
-PortsEngine::PortsEngine()
+/** Initialize ports engine
+ */
+void SocketEngine::Init()
{
- int max = ulimit(4, 0);
- if (max > 0)
- {
- MAX_DESCRIPTORS = max;
- }
- else
- {
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
- }
+ // MAX_DESCRIPTORS is mainly used for display purposes, no problem if ulimit() fails and returns a negative number
+ MAX_DESCRIPTORS = ulimit(4, 0);
+
EngineHandle = port_create();
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: This is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
- CurrentSetSize = 0;
+}
- ref = new EventHandler* [GetMaxFds()];
- events = new port_event_t[GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+/** Shutdown the ports engine
+ */
+void SocketEngine::Deinit()
+{
+ SocketEngine::Close(EngineHandle);
}
-PortsEngine::~PortsEngine()
+void SocketEngine::RecoverFromFork()
{
- this->Close(EngineHandle);
- delete[] ref;
- delete[] events;
}
static int mask_to_events(int event_mask)
return rv;
}
-bool PortsEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
return false;
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
return false;
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
+ eh->SetEventMask(event_mask);
port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
- CurrentSetSize++;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
+ ResizeDouble(events);
+
return true;
}
-void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
if (mask_to_events(new_mask) != mask_to_events(old_mask))
port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), mask_to_events(new_mask), eh);
}
-void PortsEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > GetMaxFds() - 1))
+ if (fd < 0)
return;
port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
- CurrentSetSize--;
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-int PortsEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
struct timespec poll_time;
poll_time.tv_nsec = 0;
unsigned int nget = 1; // used to denote a retrieve request.
- int ret = port_getn(EngineHandle, this->events, GetMaxFds() - 1, &nget, &poll_time);
+ int ret = port_getn(EngineHandle, &events[0], events.size(), &nget, &poll_time);
ServerInstance->UpdateTime();
// first handle an error condition
if (ret == -1)
return -1;
- TotalEvents += nget;
+ stats.TotalEvents += nget;
unsigned int i;
for (i = 0; i < nget; i++)
{
- switch (this->events[i].portev_source)
+ port_event_t& ev = events[i];
+
+ if (ev.portev_source != PORT_SOURCE_FD)
+ continue;
+
+ // Copy these in case the vector gets resized and ev invalidated
+ const int fd = ev.portev_object;
+ const int portev_events = ev.portev_events;
+ EventHandler* eh = static_cast<EventHandler*>(ev.portev_user);
+ if (eh->GetFd() < 0)
+ continue;
+
+ int mask = eh->GetEventMask();
+ if (portev_events & POLLWRNORM)
+ mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
+ if (portev_events & POLLRDNORM)
+ mask &= ~FD_READ_WILL_BLOCK;
+ // reinsert port for next time around, pretending to be one-shot for writes
+ eh->SetEventMask(mask);
+ port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
+ if (portev_events & POLLRDNORM)
{
- case PORT_SOURCE_FD:
- {
- int fd = this->events[i].portev_object;
- EventHandler* eh = ref[fd];
- if (eh)
- {
- int mask = eh->GetEventMask();
- if (events[i].portev_events & POLLWRNORM)
- mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
- if (events[i].portev_events & POLLRDNORM)
- mask &= ~FD_READ_WILL_BLOCK;
- // reinsert port for next time around, pretending to be one-shot for writes
- SetEventMask(eh, mask);
- port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
- if (events[i].portev_events & POLLRDNORM)
- {
- ReadEvents++;
- eh->HandleEvent(EVENT_READ);
- if (eh != ref[fd])
- continue;
- }
- if (events[i].portev_events & POLLWRNORM)
- {
- WriteEvents++;
- eh->HandleEvent(EVENT_WRITE);
- }
- }
- }
- default:
- break;
+ stats.ReadEvents++;
+ eh->HandleEvent(EVENT_READ);
+ if (eh != GetRef(fd))
+ continue;
+ }
+ if (portev_events & POLLWRNORM)
+ {
+ stats.WriteEvents++;
+ eh->HandleEvent(EVENT_WRITE);
}
}
return (int)i;
}
-
-std::string PortsEngine::GetName()
-{
- return "ports";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new PortsEngine;
-}
/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2014 Adam <Adam@anope.org>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
*
*/
-#include "inspircd_config.h"
-
#include "inspircd.h"
-#include "socketengine.h"
#ifndef _WIN32
#include <sys/select.h>
/** A specialisation of the SocketEngine class, designed to use traditional select().
*/
-class SelectEngine : public SocketEngine
+namespace
{
fd_set ReadSet, WriteSet, ErrSet;
- int MaxFD;
-
-public:
- /** Create a new SelectEngine
- */
- SelectEngine();
- /** Delete a SelectEngine
- */
- virtual ~SelectEngine();
- virtual bool AddFd(EventHandler* eh, int event_mask);
- virtual void DelFd(EventHandler* eh);
- void OnSetEvent(EventHandler* eh, int, int);
- virtual int DispatchEvents();
- virtual std::string GetName();
-};
-
-SelectEngine::SelectEngine()
+ int MaxFD = 0;
+}
+
+void SocketEngine::Init()
{
MAX_DESCRIPTORS = FD_SETSIZE;
- CurrentSetSize = 0;
-
- ref = new EventHandler* [GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
FD_ZERO(&ReadSet);
FD_ZERO(&WriteSet);
FD_ZERO(&ErrSet);
- MaxFD = 0;
}
-SelectEngine::~SelectEngine()
+void SocketEngine::Deinit()
+{
+}
+
+void SocketEngine::RecoverFromFork()
{
- delete[] ref;
}
-bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
+bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
return false;
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
return false;
- ref[fd] = eh;
-
- SocketEngine::SetEventMask(eh, event_mask);
+ eh->SetEventMask(event_mask);
OnSetEvent(eh, 0, event_mask);
FD_SET(fd, &ErrSet);
if (fd > MaxFD)
MaxFD = fd;
- CurrentSetSize++;
-
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
return true;
}
-void SelectEngine::DelFd(EventHandler* eh)
+void SocketEngine::DelFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
return;
- CurrentSetSize--;
- ref[fd] = NULL;
+ SocketEngine::DelFdRef(eh);
FD_CLR(fd, &ReadSet);
FD_CLR(fd, &WriteSet);
if (fd == MaxFD)
--MaxFD;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
int fd = eh->GetFd();
int diff = old_mask ^ new_mask;
}
}
-int SelectEngine::DispatchEvents()
+int SocketEngine::DispatchEvents()
{
timeval tval;
tval.tv_sec = 1;
int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
ServerInstance->UpdateTime();
- /* Nothing to process this time around */
- if (sresult < 1)
- return 0;
-
for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
{
int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
- if (has_read || has_write || has_error)
- {
- --j;
+ if (!(has_read || has_write || has_error))
+ continue;
- EventHandler* ev = ref[i];
- if (!ev)
- continue;
+ --j;
+
+ EventHandler* ev = GetRef(i);
+ if (!ev)
+ continue;
- if (has_error)
- {
- ErrorEvents++;
+ if (has_error)
+ {
+ stats.ErrorEvents++;
+
+ socklen_t codesize = sizeof(int);
+ int errcode = 0;
+ if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
+ errcode = errno;
- socklen_t codesize = sizeof(int);
- int errcode = 0;
- if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
- errcode = errno;
+ ev->HandleEvent(EVENT_ERROR, errcode);
+ continue;
+ }
- ev->HandleEvent(EVENT_ERROR, errcode);
+ if (has_read)
+ {
+ stats.ReadEvents++;
+ ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ ev->HandleEvent(EVENT_READ);
+ if (ev != GetRef(i))
continue;
- }
-
- if (has_read)
- {
- ReadEvents++;
- SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
- ev->HandleEvent(EVENT_READ);
- if (ev != ref[i])
- continue;
- }
- if (has_write)
- {
- WriteEvents++;
- int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
- this->OnSetEvent(ev, ev->GetEventMask(), newmask);
- SetEventMask(ev, newmask);
- ev->HandleEvent(EVENT_WRITE);
- }
+ }
+
+ if (has_write)
+ {
+ stats.WriteEvents++;
+ int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+ SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
+ ev->SetEventMask(newmask);
+ ev->HandleEvent(EVENT_WRITE);
}
}
return sresult;
}
-
-std::string SelectEngine::GetName()
-{
- return "select";
-}
-
-SocketEngine* CreateSocketEngine()
-{
- return new SelectEngine;
-}
*/
-/* $Core */
+#ifdef INSPIRCD_ENABLE_TESTSUITE
#include "inspircd.h"
#include "testsuite.h"
-#include "threadengine.h"
#include <iostream>
class TestSuiteThread : public Thread
switch (choice)
{
case '1':
- FOREACH_MOD(I_OnRunTestSuite, OnRunTestSuite());
+ {
+ const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
+ for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
+ i->second->OnRunTestSuite();
break;
+ }
case '2':
std::cout << "Enter module filename to load: ";
std::cin >> modname;
bool TestSuite::DoGenerateUIDTests()
{
- bool success = RealGenerateUIDTests();
+ const unsigned int UUID_LENGTH = UIDGenerator::UUID_LENGTH;
+ UIDGenerator uidgen;
+ uidgen.init(ServerInstance->Config->GetSID());
+ std::string first_uid = uidgen.GetUID();
- // Reset the UID generation state so running the tests multiple times won't mess things up
- for (unsigned int i = 0; i < 3; i++)
- ServerInstance->current_uid[i] = ServerInstance->Config->sid[i];
- for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
-
- ServerInstance->current_uid[UUID_LENGTH-1] = '\0';
-
- return success;
-}
-
-bool TestSuite::RealGenerateUIDTests()
-{
- std::string first_uid = ServerInstance->GetUID();
- if (first_uid.length() != UUID_LENGTH-1)
+ if (first_uid.length() != UUID_LENGTH)
{
std::cout << "GENERATEUID: Generated UID is " << first_uid.length() << " characters long instead of " << UUID_LENGTH-1 << std::endl;
return false;
}
- if (ServerInstance->current_uid[UUID_LENGTH-1] != '\0')
+ if (uidgen.current_uid.c_str()[UUID_LENGTH] != '\0')
{
std::cout << "GENERATEUID: The null terminator is missing from the end of current_uid" << std::endl;
return false;
}
// The correct UID when generating one for the first time is ...AAAAAA
- std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 4, 'A');
+ std::string correct_uid = ServerInstance->Config->sid + std::string(UUID_LENGTH - 3, 'A');
if (first_uid != correct_uid)
{
std::cout << "GENERATEUID: Generated an invalid first UID: " << first_uid << " instead of " << correct_uid << std::endl;
}
// Set current_uid to be ...Z99999
- ServerInstance->current_uid[3] = 'Z';
- for (unsigned int i = 4; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
+ uidgen.current_uid[3] = 'Z';
+ for (unsigned int i = 4; i < UUID_LENGTH; i++)
+ uidgen.current_uid[i] = '9';
// Store the UID we'll be incrementing so we can display what's wrong later if necessary
- std::string before_increment(ServerInstance->current_uid);
- std::string generated_uid = ServerInstance->GetUID();
+ std::string before_increment(uidgen.current_uid);
+ std::string generated_uid = uidgen.GetUID();
// Correct UID after incrementing ...Z99999 is ...0AAAAA
- correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 5, 'A');
+ correct_uid = ServerInstance->Config->sid + "0" + std::string(UUID_LENGTH - 4, 'A');
if (generated_uid != correct_uid)
{
}
// Set current_uid to be ...999999 to see if it rolls over correctly
- for (unsigned int i = 3; i < UUID_LENGTH-1; i++)
- ServerInstance->current_uid[i] = '9';
+ for (unsigned int i = 3; i < UUID_LENGTH; i++)
+ uidgen.current_uid[i] = '9';
- before_increment.assign(ServerInstance->current_uid);
- generated_uid = ServerInstance->GetUID();
+ before_increment.assign(uidgen.current_uid);
+ generated_uid = uidgen.GetUID();
// Correct UID after rolling over is the first UID we've generated (...AAAAAA)
if (generated_uid != first_uid)
std::cout << "\n\n*** END OF TEST SUITE ***\n";
}
+#endif
*/
-/* $Core */
-
-/********* DEFAULTS **********/
-/* $ExtraSources: threadengines/threadengine_pthread.cpp */
-/* $ExtraObjects: threadengine_pthread.o */
-
#include "inspircd.h"
-#include "threadengine.h"
void Thread::SetExitFlag()
{
void Thread::join()
{
- state->FreeThread(this);
- delete state;
- state = 0;
-}
-
-/** If this thread has a Creator set, call it to
- * free the thread
- */
-Thread::~Thread()
-{
+ ServerInstance->Threads.Stop(this);
}
#include "inspircd.h"
#include "threadengines/threadengine_pthread.h"
#include <pthread.h>
-#include <signal.h>
#include <fcntl.h>
-ThreadEngine::ThreadEngine()
-{
-}
-
static void* entry_point(void* parameter)
{
/* Recommended by nenolod, signal safety on a per-thread basis */
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- if (pthread_create(&data->pthread_id, NULL, entry_point, thread) != 0)
- {
- thread->state = NULL;
- delete data;
+ if (pthread_create(&thread->state.pthread_id, NULL, entry_point, thread) != 0)
throw CoreException("Unable to create new thread: " + std::string(strerror(errno)));
- }
-}
-
-ThreadEngine::~ThreadEngine()
-{
}
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
- pthread_join(pthread_id, NULL);
+ pthread_join(thread->state.pthread_id, NULL);
}
#ifdef HAS_EVENTFD
ThreadSignalSocket(SocketThread* p, int newfd) : parent(p)
{
SetFd(newfd);
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
}
~ThreadSignalSocket()
{
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
+ SocketEngine::Close(this);
}
void Notify()
parent(p), send_fd(sendfd)
{
SetFd(recvfd);
- ServerInstance->SE->NonBlocking(fd);
- ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
+ SocketEngine::NonBlocking(fd);
+ SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
}
~ThreadSignalSocket()
{
close(send_fd);
- ServerInstance->SE->DelFd(this);
- ServerInstance->SE->Close(GetFd());
+ SocketEngine::Close(this);
}
void Notify()
#include "inspircd.h"
#include "threadengines/threadengine_win32.h"
-ThreadEngine::ThreadEngine()
-{
-}
-
void ThreadEngine::Start(Thread* thread)
{
- ThreadData* data = new ThreadData;
- thread->state = data;
-
- DWORD ThreadId = 0;
- data->handle = CreateThread(NULL,0,ThreadEngine::Entry,thread,0,&ThreadId);
+ thread->state.handle = CreateThread(NULL, 0, ThreadEngine::Entry, thread, 0, NULL);
- if (data->handle == NULL)
+ if (thread->state.handle == NULL)
{
DWORD lasterr = GetLastError();
- thread->state = NULL;
- delete data;
std::string err = "Unable to create new thread: " + ConvToStr(lasterr);
SetLastError(ERROR_SUCCESS);
throw CoreException(err);
}
}
-ThreadEngine::~ThreadEngine()
-{
-}
-
DWORD WINAPI ThreadEngine::Entry(void* parameter)
{
Thread* pt = static_cast<Thread*>(parameter);
return 0;
}
-void ThreadData::FreeThread(Thread* thread)
+void ThreadEngine::Stop(Thread* thread)
{
thread->SetExitFlag();
+ HANDLE handle = thread->state.handle;
WaitForSingleObject(handle,INFINITE);
CloseHandle(handle);
}
}
};
+static bool BindAndListen(int sockfd, int port, const char* addr)
+{
+ irc::sockets::sockaddrs servaddr;
+ if (!irc::sockets::aptosa(addr, port, servaddr))
+ return false;
+
+ if (SocketEngine::Bind(sockfd, servaddr) != 0)
+ return false;
+
+ if (SocketEngine::Listen(sockfd, ServerInstance->Config->MaxConn) != 0)
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR in listen(): %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
SocketThread::SocketThread()
{
int listenFD = socket(AF_INET, SOCK_STREAM, 0);
if (connFD == -1)
throw CoreException("Could not create ITC pipe");
- if (!ServerInstance->BindSocket(listenFD, 0, "127.0.0.1", true))
+ if (!BindAndListen(listenFD, 0, "127.0.0.1"))
throw CoreException("Could not create ITC pipe");
- ServerInstance->SE->NonBlocking(connFD);
+ SocketEngine::NonBlocking(connFD);
struct sockaddr_in addr;
socklen_t sz = sizeof(addr);
getsockname(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
connect(connFD, reinterpret_cast<struct sockaddr*>(&addr), sz);
- ServerInstance->SE->Blocking(listenFD);
+ SocketEngine::Blocking(listenFD);
int nfd = accept(listenFD, reinterpret_cast<struct sockaddr*>(&addr), &sz);
if (nfd < 0)
throw CoreException("Could not create ITC pipe");
new ThreadSignalSocket(this, nfd);
closesocket(listenFD);
- ServerInstance->SE->Blocking(connFD);
+ SocketEngine::Blocking(connFD);
this->signal.connFD = connFD;
}
*/
-/* $Core */
-
#include "inspircd.h"
-#include "timer.h"
-TimerManager::TimerManager()
+void Timer::SetInterval(time_t newinterval)
{
+ ServerInstance->Timers.DelTimer(this);
+ secs = newinterval;
+ SetTrigger(ServerInstance->Time() + newinterval);
+ ServerInstance->Timers.AddTimer(this);
}
-TimerManager::~TimerManager()
+Timer::Timer(unsigned int secs_from_now, bool repeating)
+ : trigger(ServerInstance->Time() + secs_from_now)
+ , secs(secs_from_now)
+ , repeat(repeating)
{
- for(std::vector<Timer *>::iterator i = Timers.begin(); i != Timers.end(); i++)
- delete *i;
+}
+
+Timer::~Timer()
+{
+ ServerInstance->Timers.DelTimer(this);
}
void TimerManager::TickTimers(time_t TIME)
{
- while ((Timers.size()) && (TIME > (*Timers.begin())->GetTimer()))
+ for (TimerMap::iterator i = Timers.begin(); i != Timers.end(); )
{
- std::vector<Timer *>::iterator i = Timers.begin();
- Timer *t = (*i);
+ Timer* t = i->second;
+ if (t->GetTrigger() > TIME)
+ break;
+
+ Timers.erase(i++);
- // Probable fix: move vector manipulation to *before* we modify the vector.
- Timers.erase(i);
+ if (!t->Tick(TIME))
+ continue;
- t->Tick(TIME);
if (t->GetRepeat())
{
- t->SetTimer(TIME + t->GetSecs());
+ t->SetTrigger(TIME + t->GetInterval());
AddTimer(t);
}
- else
- delete t;
}
}
-void TimerManager::DelTimer(Timer* T)
+void TimerManager::DelTimer(Timer* t)
{
- std::vector<Timer *>::iterator i = std::find(Timers.begin(), Timers.end(), T);
+ std::pair<TimerMap::iterator, TimerMap::iterator> itpair = Timers.equal_range(t->GetTrigger());
- if (i != Timers.end())
+ for (TimerMap::iterator i = itpair.first; i != itpair.second; ++i)
{
- delete (*i);
- Timers.erase(i);
+ if (i->second == t)
+ {
+ Timers.erase(i);
+ break;
+ }
}
}
-void TimerManager::AddTimer(Timer* T)
-{
- Timers.push_back(T);
- std::sort(Timers.begin(), Timers.end(), TimerManager::TimerComparison);
-}
-
-bool TimerManager::TimerComparison( Timer *one, Timer *two)
+void TimerManager::AddTimer(Timer* t)
{
- return (one->GetTimer()) < (two->GetTimer());
+ Timers.insert(std::make_pair(t->GetTrigger(), t));
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-UserResolver::UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache) :
- Resolver(to_resolve, qt, cache, NULL), uuid(user->uuid)
-{
- this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
-}
-
-void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
-{
- UserResolver *res_forward; // for forward-resolution
- LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
- if (!bound_user)
- {
- ServerInstance->Logs->Log("RESOLVER", DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
- return;
- }
-
- ServerInstance->Logs->Log("RESOLVER", DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), input.c_str(), result.c_str());
-
- if (!fwd)
- {
- // first half of resolution is done. We now need to verify that the host matches.
- bound_user->stored_host = result;
- try
- {
- /* Check we didnt time out */
- if (bound_user->registered != REG_ALL)
- {
- bool lcached = false;
- if (bound_user->client_sa.sa.sa_family == AF_INET6)
- {
- /* IPV6 forward lookup */
- res_forward = new UserResolver(bound_user, result, DNS_QUERY_AAAA, lcached);
- }
- else
- {
- /* IPV4 lookup */
- res_forward = new UserResolver(bound_user, result, DNS_QUERY_A, lcached);
- }
- ServerInstance->AddResolver(res_forward, lcached);
- }
- }
- catch (CoreException& e)
- {
- ServerInstance->Logs->Log("RESOLVER", DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
- else
- {
- /* Both lookups completed */
-
- irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
- bool rev_match = false;
- if (user_ip->sa.sa_family == AF_INET6)
- {
- struct in6_addr res_bin;
- if (inet_pton(AF_INET6, result.c_str(), &res_bin))
- {
- rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
- }
- }
- else
- {
- struct in_addr res_bin;
- if (inet_pton(AF_INET, result.c_str(), &res_bin))
- {
- rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
- }
- }
-
- if (rev_match)
- {
- std::string hostname = bound_user->stored_host;
- if (hostname.length() < 65)
- {
- /* Check we didnt time out */
- if ((bound_user->registered != REG_ALL) && (!bound_user->dns_done))
- {
- /* Hostnames starting with : are not a good thing (tm) */
- if (hostname[0] == ':')
- hostname.insert(0, "0");
-
- bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
- bound_user->dns_done = true;
- bound_user->dhost.assign(hostname, 0, 64);
- bound_user->host.assign(hostname, 0, 64);
- /* Invalidate cache */
- bound_user->InvalidateCache();
- }
- }
- else
- {
- if (!bound_user->dns_done)
- {
- bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", bound_user->GetIPString());
- bound_user->dns_done = true;
- }
- }
- }
- else
- {
- if (!bound_user->dns_done)
- {
- bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", bound_user->GetIPString());
- bound_user->dns_done = true;
- }
- }
-
- // Save some memory by freeing this up; it's never used again in the user's lifetime.
- bound_user->stored_host.resize(0);
- }
-}
-
-void UserResolver::OnError(ResolverError e, const std::string &errormessage)
-{
- LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
- if (bound_user)
- {
- bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), bound_user->GetIPString());
- bound_user->dns_done = true;
- bound_user->stored_host.resize(0);
- ServerInstance->stats->statsDnsBad++;
- }
-}
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
+#include "iohook.h"
UserManager::UserManager()
- : unregistered_count(0), local_count(0)
+ : unregistered_count(0)
{
}
+UserManager::~UserManager()
+{
+ for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
+ {
+ delete i->second;
+ }
+}
+
/* add a client connection to the sockets list */
void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
{
}
catch (...)
{
- ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
return;
}
UserIOHandler* eh = &New->eh;
- /* Give each of the modules an attempt to hook the user for I/O */
- FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
+ // If this listener has an IO hook provider set then tell it about the connection
+ if (via->iohookprov)
+ via->iohookprov->OnAccept(eh, client, server);
- if (eh->GetIOHook())
- {
- try
- {
- eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
- }
- catch (CoreException& modexcept)
- {
- ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
- }
- }
-
- ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
this->unregistered_count++;
/* The users default nick is their UUID */
New->nick = New->uuid;
- (*(this->clientlist))[New->nick] = New;
+ this->clientlist[New->nick] = New;
New->registered = REG_NONE;
New->signon = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
New->lastping = 1;
- ServerInstance->Users->AddLocalClone(New);
- ServerInstance->Users->AddGlobalClone(New);
+ this->AddClone(New);
- New->localuseriter = this->local_users.insert(local_users.end(), New);
- local_count++;
+ this->local_users.push_front(New);
- if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
+ if (this->local_users.size() > ServerInstance->Config->SoftLimit)
{
ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
this->QuitUser(New,"No more connections allowed");
* Check connect class settings and initialise settings into User.
* This will be done again after DNS resolution. -- w00t
*/
- New->CheckClass();
+ New->CheckClass(ServerInstance->Config->CCOnConnect);
if (New->quitting)
return;
*/
New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
- if (BanCacheHit *b = ServerInstance->BanCache->GetHit(New->GetIPString()))
+ BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
+ if (b)
{
if (!b->Type.empty() && !New->exempt)
{
/* user banned */
- ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Positive hit for ") + New->GetIPString());
- if (!ServerInstance->Config->MoronBanner.empty())
- New->WriteServ("NOTICE %s :*** %s", New->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
+ if (!ServerInstance->Config->XLineMessage.empty())
+ New->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
this->QuitUser(New, b->Reason);
return;
}
else
{
- ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Negative hit for ") + New->GetIPString());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
}
}
else
}
}
- if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
+ if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
{
- ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
this->QuitUser(New, "Internal error handling connection");
}
- /* NOTE: even if dns lookups are *off*, we still need to display this.
- * BOPM and other stuff requires it.
- */
- New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
if (ServerInstance->Config->RawLog)
- New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+ New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
- FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
+ FOREACH_MOD(OnSetUserIP, (New));
if (New->quitting)
return;
- FOREACH_MOD(I_OnUserInit,OnUserInit(New));
-
- if (ServerInstance->Config->NoUserDns)
- {
- New->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", New->nick.c_str());
- New->dns_done = true;
- }
- else
- {
- New->StartDNSLookup();
- }
+ FOREACH_MOD(OnUserInit, (New));
}
-void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason)
+void UserManager::QuitUser(User* user, const std::string& quitreason, const std::string* operreason)
{
if (user->quitting)
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
return;
}
if (IS_SERVER(user))
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
return;
}
user->quitting = true;
- ServerInstance->Logs->Log("USERS", DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
- user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), *operreason ? operreason : quitreason.c_str());
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
+ user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), operreason ? operreason->c_str() : quitreason.c_str());
std::string reason;
- std::string oper_reason;
reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
- if (operreason && *operreason)
- oper_reason.assign(operreason, 0, ServerInstance->Config->Limits.MaxQuit);
- else
- oper_reason = quitreason;
+ if (!operreason)
+ operreason = &reason;
ServerInstance->GlobalCulls.AddItem(user);
if (user->registered == REG_ALL)
{
- FOREACH_MOD(I_OnUserQuit,OnUserQuit(user, reason, oper_reason));
- user->WriteCommonQuit(reason, oper_reason);
+ FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
+ user->WriteCommonQuit(reason, *operreason);
}
-
- if (user->registered != REG_ALL)
- if (ServerInstance->Users->unregistered_count)
- ServerInstance->Users->unregistered_count--;
+ else
+ unregistered_count--;
if (IS_LOCAL(user))
{
LocalUser* lu = IS_LOCAL(user);
- FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(lu));
+ FOREACH_MOD(OnUserDisconnect, (lu));
lu->eh.Close();
- }
- /*
- * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
- * if they were an oper with +s +qQ.
- */
- if (user->registered == REG_ALL)
- {
- if (IS_LOCAL(user))
- {
- if (!user->quietquit)
- {
- ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]",
- user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
- }
- }
- else
- {
- if ((!ServerInstance->SilentULine(user->server)) && (!user->quietquit))
- {
- ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s (%s) [%s]",
- user->server.c_str(), user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
- }
- }
- user->AddToWhoWas();
+ if (lu->registered == REG_ALL)
+ ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operreason->c_str());
+ local_users.erase(lu);
}
- user_hash::iterator iter = this->clientlist->find(user->nick);
+ if (!clientlist.erase(user->nick))
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
- if (iter != this->clientlist->end())
- this->clientlist->erase(iter);
- else
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
-
- ServerInstance->Users->uuidlist->erase(user->uuid);
-}
-
-
-void UserManager::AddLocalClone(User *user)
-{
- local_clones[user->GetCIDRMask()]++;
+ uuidlist.erase(user->uuid);
+ user->PurgeEmptyChannels();
}
-void UserManager::AddGlobalClone(User *user)
+void UserManager::AddClone(User* user)
{
- global_clones[user->GetCIDRMask()]++;
+ CloneCounts& counts = clonemap[user->GetCIDRMask()];
+ counts.global++;
+ if (IS_LOCAL(user))
+ counts.local++;
}
void UserManager::RemoveCloneCounts(User *user)
{
- if (IS_LOCAL(user))
+ CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
+ if (it != clonemap.end())
{
- clonemap::iterator x = local_clones.find(user->GetCIDRMask());
- if (x != local_clones.end())
+ CloneCounts& counts = it->second;
+ counts.global--;
+ if (counts.global == 0)
{
- x->second--;
- if (!x->second)
- {
- local_clones.erase(x);
- }
+ // No more users from this IP, remove entry from the map
+ clonemap.erase(it);
+ return;
}
- }
- clonemap::iterator y = global_clones.find(user->GetCIDRMask());
- if (y != global_clones.end())
- {
- y->second--;
- if (!y->second)
- {
- global_clones.erase(y);
- }
+ if (IS_LOCAL(user))
+ counts.local--;
}
}
-unsigned long UserManager::GlobalCloneCount(User *user)
-{
- clonemap::iterator x = global_clones.find(user->GetCIDRMask());
- if (x != global_clones.end())
- return x->second;
- else
- return 0;
-}
-
-unsigned long UserManager::LocalCloneCount(User *user)
+const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
{
- clonemap::iterator x = local_clones.find(user->GetCIDRMask());
- if (x != local_clones.end())
- return x->second;
+ CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
+ if (it != clonemap.end())
+ return it->second;
else
- return 0;
-}
-
-/* this function counts all users connected, wether they are registered or NOT. */
-unsigned int UserManager::UserCount()
-{
- /*
- * XXX: Todo:
- * As part of this restructuring, move clientlist/etc fields into usermanager.
- * -- w00t
- */
- return this->clientlist->size();
-}
-
-/* this counts only registered users, so that the percentages in /MAP don't mess up */
-unsigned int UserManager::RegisteredUserCount()
-{
- return this->clientlist->size() - this->UnregisteredUserCount();
-}
-
-/* return how many users are opered */
-unsigned int UserManager::OperCount()
-{
- return this->all_opers.size();
-}
-
-/* return how many users are unregistered */
-unsigned int UserManager::UnregisteredUserCount()
-{
- return this->unregistered_count;
-}
-
-/* return how many local registered users there are */
-unsigned int UserManager::LocalUserCount()
-{
- /* Doesnt count unregistered clients */
- return (this->local_count - this->UnregisteredUserCount());
+ return zeroclonecounts;
}
void UserManager::ServerNoticeAll(const char* text, ...)
{
- if (!text)
- return;
-
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
+ std::string message;
+ VAFORMAT(message, text, text);
+ message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
- snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
-
- for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
{
User* t = *i;
- t->WriteServ(std::string(formatbuffer));
+ t->WriteServ(message);
}
}
-void UserManager::ServerPrivmsgAll(const char* text, ...)
+void UserManager::GarbageCollect()
{
- if (!text)
- return;
-
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
-
- for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
+ // Reset the already_sent IDs so we don't wrap it around and drop a message
+ LocalUser::already_sent_id = 0;
+ for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
{
- User* t = *i;
- t->WriteServ(std::string(formatbuffer));
+ (**i).already_sent = 0;
+ (**i).RemoveExpiredInvites();
}
}
+/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
+ * (until this returns true, a user will block in the waiting state, waiting to connect up to the
+ * registration timeout maximum seconds)
+ */
+bool UserManager::AllModulesReportReady(LocalUser* user)
+{
+ ModResult res;
+ FIRST_MOD_RESULT(OnCheckReady, res, (user));
+ return (res == MOD_RES_PASSTHRU);
+}
-/* return how many users have a given mode e.g. 'a' */
-int UserManager::ModeCount(const char mode)
+/**
+ * This function is called once a second from the mainloop.
+ * It is intended to do background checking on all the user structs, e.g.
+ * stuff like ping checks, registration timeouts, etc.
+ */
+void UserManager::DoBackgroundUserStuff()
{
- int c = 0;
- for(user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
+ /*
+ * loop over all local users..
+ */
+ for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
{
- User* u = i->second;
- if (u->modes[mode-65])
- c++;
+ LocalUser* curr = *i;
+
+ if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
+ {
+ unsigned int rate = curr->MyClass->GetCommandRate();
+ if (curr->CommandFloodPenalty > rate)
+ curr->CommandFloodPenalty -= rate;
+ else
+ curr->CommandFloodPenalty = 0;
+ curr->eh.OnDataReady();
+ }
+
+ switch (curr->registered)
+ {
+ case REG_ALL:
+ if (ServerInstance->Time() >= curr->nping)
+ {
+ // This user didn't answer the last ping, remove them
+ if (!curr->lastping)
+ {
+ time_t time = ServerInstance->Time() - (curr->nping - curr->MyClass->GetPingTime());
+ const std::string message = "Ping timeout: " + ConvToStr(time) + (time != 1 ? " seconds" : " second");
+ this->QuitUser(curr, message);
+ continue;
+ }
+
+ curr->Write("PING :" + ServerInstance->Config->ServerName);
+ curr->lastping = 0;
+ curr->nping = ServerInstance->Time() + curr->MyClass->GetPingTime();
+ }
+ break;
+ case REG_NICKUSER:
+ if (AllModulesReportReady(curr))
+ {
+ /* User has sent NICK/USER, modules are okay, DNS finished. */
+ curr->FullConnect();
+ continue;
+ }
+ break;
+ }
+
+ if (curr->registered != REG_ALL && (ServerInstance->Time() > (curr->age + curr->MyClass->GetRegTimeout())))
+ {
+ /*
+ * registration timeout -- didnt send USER/NICK/HOST
+ * in the time specified in their connection class.
+ */
+ this->QuitUser(curr, "Registration timeout");
+ continue;
+ }
}
- return c;
}
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006 Craig McLure <craig@chatspike.net>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-/* $Core */
-
-#include "inspircd.h"
-#include "xline.h"
-#include "socketengine.h"
-#include "command_parse.h"
-
-void FloodQuitUserHandler::Call(User* current)
-{
- ServerInstance->Logs->Log("USERS",DEFAULT,"Excess flood from: %s@%s", current->ident.c_str(), current->host.c_str());
- ServerInstance->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s",
- current->registered == REG_ALL ? current->nick.c_str() : "",
- current->registered == REG_ALL ? "!" : "", current->ident.c_str(), current->host.c_str());
- ServerInstance->Users->QuitUser(current, "Excess flood");
-
- if (current->registered != REG_ALL)
- {
- ZLine* zl = new ZLine(ServerInstance->Time(), 0, ServerInstance->Config->ServerName, "Flood from unregistered connection", current->GetIPString());
- if (ServerInstance->XLines->AddLine(zl,NULL))
- ServerInstance->XLines->ApplyLines();
- else
- delete zl;
- }
-}
-
-/**
- * This function is called once a second from the mainloop.
- * It is intended to do background checking on all the user structs, e.g.
- * stuff like ping checks, registration timeouts, etc.
- */
-void InspIRCd::DoBackgroundUserStuff()
-{
- /*
- * loop over all local users..
- */
- LocalUserList::reverse_iterator count2 = this->Users->local_users.rbegin();
- while (count2 != this->Users->local_users.rend())
- {
- LocalUser *curr = *count2;
- count2++;
-
- if (curr->quitting)
- continue;
-
- if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
- {
- unsigned int rate = curr->MyClass->GetCommandRate();
- if (curr->CommandFloodPenalty > rate)
- curr->CommandFloodPenalty -= rate;
- else
- curr->CommandFloodPenalty = 0;
- curr->eh.OnDataReady();
- }
-
- switch (curr->registered)
- {
- case REG_ALL:
- if (Time() > curr->nping)
- {
- // This user didn't answer the last ping, remove them
- if (!curr->lastping)
- {
- time_t time = this->Time() - (curr->nping - curr->MyClass->GetPingTime());
- char message[MAXBUF];
- snprintf(message, MAXBUF, "Ping timeout: %ld second%s", (long)time, time > 1 ? "s" : "");
- curr->lastping = 1;
- curr->nping = Time() + curr->MyClass->GetPingTime();
- this->Users->QuitUser(curr, message);
- continue;
- }
-
- curr->Write("PING :%s",this->Config->ServerName.c_str());
- curr->lastping = 0;
- curr->nping = Time() +curr->MyClass->GetPingTime();
- }
- break;
- case REG_NICKUSER:
- if (AllModulesReportReady(curr) && curr->dns_done)
- {
- /* User has sent NICK/USER, modules are okay, DNS finished. */
- curr->FullConnect();
- continue;
- }
- break;
- }
-
- if (curr->registered != REG_ALL && (Time() > (curr->age + curr->MyClass->GetRegTimeout())))
- {
- /*
- * registration timeout -- didnt send USER/NICK/HOST
- * in the time specified in their connection class.
- */
- this->Users->QuitUser(curr, "Registration timeout");
- continue;
- }
- }
-}
-
#include "inspircd.h"
-#include <stdarg.h>
-#include "socketengine.h"
#include "xline.h"
-#include "bancache.h"
-#include "commands/cmd_whowas.h"
already_sent_t LocalUser::already_sent_id = 0;
-std::string User::ProcessNoticeMasks(const char *sm)
-{
- bool adding = true, oldadding = false;
- const char *c = sm;
- std::string output;
-
- while (c && *c)
- {
- switch (*c)
- {
- case '+':
- adding = true;
- break;
- case '-':
- adding = false;
- break;
- case '*':
- for (unsigned char d = 'a'; d <= 'z'; d++)
- {
- if (!ServerInstance->SNO->masks[d - 'a'].Description.empty())
- {
- if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(d, adding);
-
- output += d;
- }
- oldadding = adding;
- char u = toupper(d);
- if ((!IsNoticeMaskSet(u) && adding) || (IsNoticeMaskSet(u) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(u, adding);
-
- output += u;
- }
- oldadding = adding;
- }
- }
- break;
- default:
- if (isalpha(*c))
- {
- if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
- {
- if ((oldadding != adding) || (!output.length()))
- output += (adding ? '+' : '-');
-
- this->SetNoticeMask(*c, adding);
-
- output += *c;
- oldadding = adding;
- }
- }
- else
- this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c);
-
- break;
- }
-
- c++;
- }
-
- std::string s = this->FormatNoticeMasks();
- if (s.length() == 0)
- {
- this->modes[UM_SNOMASK] = false;
- }
-
- return output;
-}
-
-void LocalUser::StartDNSLookup()
-{
- try
- {
- bool cached = false;
- const char* sip = this->GetIPString();
- UserResolver *res_reverse;
-
- QueryType resolvtype = this->client_sa.sa.sa_family == AF_INET6 ? DNS_QUERY_PTR6 : DNS_QUERY_PTR4;
- res_reverse = new UserResolver(this, sip, resolvtype, cached);
-
- ServerInstance->AddResolver(res_reverse, cached);
- }
- catch (CoreException& e)
- {
- ServerInstance->Logs->Log("USERS", DEBUG,"Error in resolver: %s",e.GetReason());
- dns_done = true;
- ServerInstance->stats->statsDnsBad++;
- }
-}
-
bool User::IsNoticeMaskSet(unsigned char sm)
{
if (!isalpha(sm))
return (snomasks[sm-65]);
}
-void User::SetNoticeMask(unsigned char sm, bool value)
-{
- if (!isalpha(sm))
- return;
- snomasks[sm-65] = value;
-}
-
-const char* User::FormatNoticeMasks()
-{
- static char data[MAXBUF];
- int offset = 0;
-
- for (int n = 0; n < 64; n++)
- {
- if (snomasks[n])
- data[offset++] = n+65;
- }
-
- data[offset] = 0;
- return data;
-}
-
bool User::IsModeSet(unsigned char m)
{
- if (!isalpha(m))
- return false;
- return (modes[m-65]);
-}
-
-void User::SetMode(unsigned char m, bool value)
-{
- if (!isalpha(m))
- return;
- modes[m-65] = value;
+ ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
+ return (mh && modes[mh->GetId()]);
}
const char* User::FormatModes(bool showparameters)
{
- static char data[MAXBUF];
+ static std::string data;
std::string params;
- int offset = 0;
+ data.clear();
for (unsigned char n = 0; n < 64; n++)
{
- if (modes[n])
+ ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
+ if (mh && IsModeSet(mh))
{
- data[offset++] = n + 65;
- ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
- if (showparameters && mh && mh->GetNumParams(true))
+ data.push_back(n + 65);
+ if (showparameters && mh->GetNumParams(true))
{
std::string p = mh->GetUserParameter(this);
if (p.length())
}
}
}
- data[offset] = 0;
- strlcat(data, params.c_str(), MAXBUF);
- return data;
+ data += params;
+ return data.c_str();
}
-User::User(const std::string &uid, const std::string& sid, int type)
- : uuid(uid), server(sid), usertype(type)
+User::User(const std::string& uid, Server* srv, int type)
+ : uuid(uid), server(srv), usertype(type)
{
age = ServerInstance->Time();
- signon = idle_lastmsg = 0;
+ signon = 0;
registered = 0;
- quietquit = quitting = exempt = dns_done = false;
- quitting_sendq = false;
+ quitting = false;
client_sa.sa.sa_family = AF_UNSPEC;
- ServerInstance->Logs->Log("USERS", DEBUG, "New UUID for user: %s", uuid.c_str());
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
- user_hash::iterator finduuid = ServerInstance->Users->uuidlist->find(uuid);
- if (finduuid == ServerInstance->Users->uuidlist->end())
- (*ServerInstance->Users->uuidlist)[uuid] = this;
- else
+ if (!ServerInstance->Users->uuidlist.insert(std::make_pair(uuid, this)).second)
throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
}
LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
- : User(ServerInstance->GetUID(), ServerInstance->Config->ServerName, USERTYPE_LOCAL), eh(this),
- localuseriter(ServerInstance->Users->local_users.end()),
+ : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL), eh(this),
bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
already_sent(0)
{
+ exempt = quitting_sendq = false;
+ idle_lastmsg = 0;
ident = "unknown";
lastping = 0;
eh.SetFd(myfd);
User::~User()
{
- if (ServerInstance->Users->uuidlist->find(uuid) != ServerInstance->Users->uuidlist->end())
- ServerInstance->Logs->Log("USERS", DEFAULT, "User destructor for %s called without cull", uuid.c_str());
}
const std::string& User::MakeHost()
if (!this->cached_makehost.empty())
return this->cached_makehost;
- char nhost[MAXBUF];
- /* This is much faster than snprintf */
- char* t = nhost;
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = host.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_makehost.assign(nhost);
-
+ // XXX: Is there really a need to cache this?
+ this->cached_makehost = ident + "@" + host;
return this->cached_makehost;
}
if (!this->cached_hostip.empty())
return this->cached_hostip;
- char ihost[MAXBUF];
- /* This is much faster than snprintf */
- char* t = ihost;
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = this->GetIPString(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_hostip = ihost;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_hostip = ident + "@" + this->GetIPString();
return this->cached_hostip;
}
if (!this->cached_fullhost.empty())
return this->cached_fullhost;
- char result[MAXBUF];
- char* t = result;
- for(const char* n = nick.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '!';
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = dhost.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_fullhost = result;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_fullhost = nick + "!" + ident + "@" + dhost;
return this->cached_fullhost;
}
-char* User::MakeWildHost()
-{
- static char nresult[MAXBUF];
- char* t = nresult;
- *t++ = '*'; *t++ = '!';
- *t++ = '*'; *t++ = '@';
- for(const char* n = dhost.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
- return nresult;
-}
-
const std::string& User::GetFullRealHost()
{
if (!this->cached_fullrealhost.empty())
return this->cached_fullrealhost;
- char fresult[MAXBUF];
- char* t = fresult;
- for(const char* n = nick.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '!';
- for(const char* n = ident.c_str(); *n; n++)
- *t++ = *n;
- *t++ = '@';
- for(const char* n = host.c_str(); *n; n++)
- *t++ = *n;
- *t = 0;
-
- this->cached_fullrealhost = fresult;
-
+ // XXX: Is there really a need to cache this?
+ this->cached_fullrealhost = nick + "!" + ident + "@" + host;
return this->cached_fullrealhost;
}
-bool LocalUser::IsInvited(const irc::string &channel)
-{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (!chan)
- return false;
-
- return (Invitation::Find(chan, this) != NULL);
-}
-
InviteList& LocalUser::GetInviteList()
{
RemoveExpiredInvites();
return invites;
}
-void LocalUser::InviteTo(const irc::string &channel, time_t invtimeout)
-{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (chan)
- Invitation::Create(chan, this, invtimeout);
-}
-
-void LocalUser::RemoveInvite(const irc::string &channel)
+bool LocalUser::RemoveInvite(Channel* chan)
{
- Channel* chan = ServerInstance->FindChan(channel.c_str());
- if (chan)
+ Invitation* inv = Invitation::Find(chan, this);
+ if (inv)
{
- Invitation* inv = Invitation::Find(chan, this);
- if (inv)
- {
- inv->cull();
- delete inv;
- }
+ delete inv;
+ return true;
}
+ return false;
}
void LocalUser::RemoveExpiredInvites()
bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
return false;
if (mode < 'A' || mode > ('A' + 64)) return false;
bool LocalUser::HasPermission(const std::string &command)
{
// are they even an oper at all?
- if (!IS_OPER(this))
+ if (!this->IsOper())
{
return false;
}
bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
{
if (noisy)
- this->WriteServ("NOTICE %s :You are not an oper", this->nick.c_str());
+ this->WriteNotice("You are not an oper");
return false;
}
}
if (noisy)
- this->WriteServ("NOTICE %s :Oper type %s does not have access to priv %s", this->nick.c_str(), oper->NameStr(), privstr.c_str());
+ this->WriteNotice("Oper type " + oper->name + " does not have access to priv " + privstr);
+
return false;
}
while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
{
std::string line;
- line.reserve(MAXBUF);
+ line.reserve(ServerInstance->Config->Limits.MaxLine);
std::string::size_type qpos = 0;
while (qpos < recvq.length())
{
case '\n':
goto eol_found;
}
- if (line.length() < MAXBUF - 2)
+ if (line.length() < ServerInstance->Config->Limits.MaxLine - 2)
line.push_back(c);
}
// if we got here, the recvq ran out before we found a newline
recvq = recvq.substr(qpos);
// TODO should this be moved to when it was inserted in recvq?
- ServerInstance->stats->statsRecv += qpos;
+ ServerInstance->stats.Recv += qpos;
user->bytes_in += qpos;
user->cmds_in++;
- ServerInstance->Parser->ProcessBuffer(line, user);
+ ServerInstance->Parser.ProcessBuffer(line, user);
if (user->quitting)
return;
}
{
if (!quitting)
ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
- PurgeEmptyChannels();
if (client_sa.sa.sa_family != AF_UNSPEC)
ServerInstance->Users->RemoveCloneCounts(this);
CullResult LocalUser::cull()
{
- // The iterator is initialized to local_users.end() in the constructor. It is
- // overwritten in UserManager::AddUser() with the real iterator so this check
- // is only a precaution currently.
- if (localuseriter != ServerInstance->Users->local_users.end())
- {
- ServerInstance->Users->local_count--;
- ServerInstance->Users->local_users.erase(localuseriter);
- }
- else
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick);
-
ClearInvites();
eh.cull();
return User::cull();
{
// Fake users don't quit, they just get culled.
quitting = true;
- ServerInstance->Users->clientlist->erase(nick);
- ServerInstance->Users->uuidlist->erase(uuid);
+ // Fake users are not inserted into UserManager::clientlist, they're only in the uuidlist
+ // and they are removed from there by the linking mod when the server splits
return User::cull();
}
void User::Oper(OperInfo* info)
{
- if (this->IsModeSet('o'))
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ if (this->IsModeSet(opermh))
this->UnOper();
- this->modes[UM_OPERATOR] = 1;
+ this->SetMode(opermh, true);
this->oper = info;
- this->WriteServ("MODE %s :+o", this->nick.c_str());
- FOREACH_MOD(I_OnOper, OnOper(this, info->name));
+ this->WriteCommand("MODE", "+o");
+ FOREACH_MOD(OnOper, (this, info->name));
std::string opername;
if (info->oper_block)
}
ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
- nick.c_str(), ident.c_str(), host.c_str(), oper->NameStr(), opername.c_str());
- this->WriteNumeric(381, "%s :You are now %s %s", nick.c_str(), strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->NameStr());
+ nick.c_str(), ident.c_str(), host.c_str(), oper->name.c_str(), opername.c_str());
+ this->WriteNumeric(RPL_YOUAREOPER, ":You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str());
- ServerInstance->Logs->Log("OPER", DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->NameStr());
+ ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
ServerInstance->Users->all_opers.push_back(this);
// Expand permissions from config for faster lookup
if (IS_LOCAL(this))
oper->init();
- FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
+ FOREACH_MOD(OnPostOper, (this, oper->name, opername));
}
void OperInfo::init()
void User::UnOper()
{
- if (!IS_OPER(this))
+ if (!this->IsOper())
return;
/*
/* Remove all oper only modes from the user when the deoper - Bug #466*/
- std::string moderemove("-");
-
- for (unsigned char letter = 'A'; letter <= 'z'; letter++)
+ Modes::ChangeList changelist;
+ const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
+ for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
{
- ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER);
- if (mh && mh->NeedsOper())
- moderemove += letter;
+ ModeHandler* mh = i->second;
+ if (mh->NeedsOper())
+ changelist.push_remove(mh);
}
+ ServerInstance->Modes->Process(this, NULL, this, changelist);
- std::vector<std::string> parameters;
- parameters.push_back(this->nick);
- parameters.push_back(moderemove);
-
- ServerInstance->Parser->CallHandler("MODE", parameters, this);
-
- /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */
- ServerInstance->Users->all_opers.remove(this);
+ // Remove the user from the oper list
+ stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
- this->modes[UM_OPERATOR] = 0;
-}
-
-/* adds or updates an entry in the whowas list */
-void User::AddToWhoWas()
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_ADD);
- req.user = this;
- req.Send();
- }
+ ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
+ this->SetMode(opermh, false);
}
/*
* Check class restrictions
*/
-void LocalUser::CheckClass()
+void LocalUser::CheckClass(bool clone_count)
{
ConnectClass* a = this->MyClass;
ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
return;
}
- else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
+ else if (clone_count)
{
- ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
- if (a->maxconnwarn)
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
- return;
- }
- else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
- {
- ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
- if (a->maxconnwarn)
- ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
- return;
+ const UserManager::CloneCounts& clonecounts = ServerInstance->Users->GetCloneCounts(this);
+ if ((a->GetMaxLocal()) && (clonecounts.local > a->GetMaxLocal()))
+ {
+ ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
+ if (a->maxconnwarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
+ return;
+ }
+ else if ((a->GetMaxGlobal()) && (clonecounts.global > a->GetMaxGlobal()))
+ {
+ ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
+ if (a->maxconnwarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
+ return;
+ }
}
this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
}
-bool User::CheckLines(bool doZline)
+bool LocalUser::CheckLines(bool doZline)
{
const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
void LocalUser::FullConnect()
{
- ServerInstance->stats->statsConnects++;
+ ServerInstance->stats.Connects++;
this->idle_lastmsg = ServerInstance->Time();
/*
if (quitting)
return;
- if (ServerInstance->Config->WelcomeNotice)
- this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network.c_str());
- this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s",this->nick.c_str(), ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
- this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version %s",this->nick.c_str(),ServerInstance->Config->ServerName.c_str(),BRANCH);
- this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__);
+ this->WriteNumeric(RPL_WELCOME, ":Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
+ this->WriteNumeric(RPL_YOURHOSTIS, ":Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH);
+ this->WriteNumeric(RPL_SERVERCREATED, ":This server was created %s %s", __TIME__, __DATE__);
- std::string umlist = ServerInstance->Modes->UserModeList();
- std::string cmlist = ServerInstance->Modes->ChannelModeList();
- std::string pmlist = ServerInstance->Modes->ParaModeList();
- this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s %s %s %s", this->nick.c_str(), ServerInstance->Config->ServerName.c_str(), BRANCH, umlist.c_str(), cmlist.c_str(), pmlist.c_str());
+ const std::string& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
+ this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH, modelist.c_str());
- ServerInstance->Config->Send005(this);
- this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
+ ServerInstance->ISupport.SendTo(this);
/* Now registered */
if (ServerInstance->Users->unregistered_count)
/* Trigger MOTD and LUSERS output, give modules a chance too */
ModResult MOD_RESULT;
- std::string command("MOTD");
+ std::string command("LUSERS");
std::vector<std::string> parameters;
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
+ ServerInstance->Parser.CallHandler(command, parameters, this);
MOD_RESULT = MOD_RES_PASSTHRU;
- command = "LUSERS";
+ command = "MOTD";
FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
if (!MOD_RESULT)
- ServerInstance->Parser->CallHandler(command, parameters, this);
+ ServerInstance->Parser.CallHandler(command, parameters, this);
if (ServerInstance->Config->RawLog)
WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
* We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
* for a user that doesn't exist yet.
*/
- FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
+ FOREACH_MOD(OnUserConnect, (this));
this->registered = REG_ALL;
- FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
+ FOREACH_MOD(OnPostConnect, (this));
ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
- this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString(), this->fullname.c_str());
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString());
- ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
+ this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
+ ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
// reset the flood penalty (which could have been raised due to things like auto +x)
CommandFloodPenalty = 0;
}
cached_fullrealhost.clear();
}
-bool User::ChangeNick(const std::string& newnick, bool force)
+bool User::ChangeNick(const std::string& newnick, time_t newts)
{
if (quitting)
{
- ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
- return false;
- }
-
- ModResult MOD_RESULT;
-
- if (force)
- ServerInstance->NICKForced.set(this, 1);
- FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
- ServerInstance->NICKForced.set(this, 0);
-
- if (MOD_RESULT == MOD_RES_DENY)
- {
- ServerInstance->stats->statsCollisions++;
+ ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
return false;
}
- if (assign(newnick) == assign(nick))
+ User* const InUse = ServerInstance->FindNickOnly(newnick);
+ if (InUse == this)
{
- // case change, don't need to check Q:lines and such
+ // case change, don't need to check campers
// and, if it's identical including case, we can leave right now
+ // We also don't update the nick TS if it's a case change, either
if (newnick == nick)
return true;
}
else
{
- /*
- * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
- * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
- * Thanks Kein for finding this. -- w00t
- *
- * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
- * -- w00t
- */
- if (IS_LOCAL(this) && !force)
- {
- XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
- if (mq)
- {
- if (this->registered == REG_ALL)
- {
- ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
- newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
- }
- this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str());
- return false;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
- {
- Channel *chan = *i;
- if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
- {
- this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str());
- return false;
- }
- }
- }
- }
-
/*
* Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
* then we have a potential collide. Check whether someone else is camping on the nick
* If the guy using the nick is already using it, tell the incoming nick change to gtfo,
* because the nick is already (rightfully) in use. -- w00t
*/
- User* InUse = ServerInstance->FindNickOnly(newnick);
- if (InUse && (InUse != this))
+ if (InUse)
{
if (InUse->registered != REG_ALL)
{
/* force the camper to their UUID, and ask them to re-send a NICK. */
- InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str());
- InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str());
-
- ServerInstance->Users->clientlist->erase(InUse->nick);
- (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse;
+ InUse->WriteFrom(InUse, "NICK %s", InUse->uuid.c_str());
+ InUse->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", InUse->nick.c_str());
- InUse->nick = InUse->uuid;
- InUse->InvalidateCache();
InUse->registered &= ~REG_NICK;
+ InUse->ChangeNick(InUse->uuid);
}
else
{
/* No camping, tell the incoming user to stop trying to change nick ;p */
- this->WriteNumeric(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str());
+ this->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname is already in use.", newnick.c_str());
return false;
}
}
+
+ age = newts ? newts : ServerInstance->Time();
}
if (this->registered == REG_ALL)
nick = newnick;
InvalidateCache();
- ServerInstance->Users->clientlist->erase(oldnick);
- (*(ServerInstance->Users->clientlist))[newnick] = this;
+ ServerInstance->Users->clientlist.erase(oldnick);
+ ServerInstance->Users->clientlist[newnick] = this;
if (registered == REG_ALL)
- FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick));
+ FOREACH_MOD(OnUserPostNick, (this,oldnick));
return true;
}
return 0;
}
-const char* User::GetIPString()
+const std::string& User::GetIPString()
{
int port;
if (cachedip.empty())
{
irc::sockets::satoap(client_sa, cachedip, port);
/* IP addresses starting with a : on irc are a Bad Thing (tm) */
- if (cachedip.c_str()[0] == ':')
+ if (cachedip[0] == ':')
cachedip.insert(cachedip.begin(),1,'0');
}
- return cachedip.c_str();
+ return cachedip;
}
irc::sockets::cidr_mask User::GetCIDRMask()
if (recheck_eline)
this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
- FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
+ FOREACH_MOD(OnSetUserIP, (this));
}
}
void LocalUser::Write(const std::string& text)
{
- if (!ServerInstance->SE->BoundsCheckFd(&eh))
+ if (!SocketEngine::BoundsCheckFd(&eh))
return;
- if (text.length() > MAXBUF - 2)
+ if (text.length() > ServerInstance->Config->Limits.MaxLine - 2)
{
// this should happen rarely or never. Crop the string at 512 and try again.
- std::string try_again = text.substr(0, MAXBUF - 2);
+ std::string try_again = text.substr(0, ServerInstance->Config->Limits.MaxLine - 2);
Write(try_again);
return;
}
- ServerInstance->Logs->Log("USEROUTPUT", RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+ ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
eh.AddWriteBuf(text);
eh.AddWriteBuf(wide_newline);
- ServerInstance->stats->statsSent += text.length() + 2;
+ ServerInstance->stats.Sent += text.length() + 2;
this->bytes_out += text.length() + 2;
this->cmds_out++;
}
*/
void LocalUser::Write(const char *text, ...)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->Write(std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->Write(textbuffer);
}
void User::WriteServ(const std::string& text)
*/
void User::WriteServ(const char* text, ...)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteServ(std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteServ(textbuffer);
}
+void User::WriteCommand(const char* command, const std::string& text)
+{
+ this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
+}
void User::WriteNumeric(unsigned int numeric, const char* text, ...)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteNumeric(numeric, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteNumeric(numeric, textbuffer);
}
void User::WriteNumeric(unsigned int numeric, const std::string &text)
{
- char textbuffer[MAXBUF];
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
if (MOD_RESULT == MOD_RES_DENY)
return;
- snprintf(textbuffer,MAXBUF,":%s %03u %s",ServerInstance->Config->ServerName.c_str(), numeric, text.c_str());
- this->Write(std::string(textbuffer));
+ const std::string message = InspIRCd::Format(":%s %03u %s %s", ServerInstance->Config->ServerName.c_str(),
+ numeric, this->registered & REG_NICK ? this->nick.c_str() : "*", text.c_str());
+ this->Write(message);
}
void User::WriteFrom(User *user, const std::string &text)
{
- char tb[MAXBUF];
-
- snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str());
-
- this->Write(std::string(tb));
+ const std::string message = ":" + user->GetFullHost() + " " + text;
+ this->Write(message);
}
void User::WriteFrom(User *user, const char* text, ...)
{
- va_list argsPtr;
- char textbuffer[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteFrom(user, std::string(textbuffer));
-}
-
-
-/* write text to an destination user from a source user (e.g. user privmsg) */
-
-void User::WriteTo(User *dest, const char *data, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, data);
- vsnprintf(textbuffer, MAXBUF, data, argsPtr);
- va_end(argsPtr);
-
- this->WriteTo(dest, std::string(textbuffer));
-}
-
-void User::WriteTo(User *dest, const std::string &data)
-{
- dest->WriteFrom(this, data);
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ this->WriteFrom(user, textbuffer);
}
void User::WriteCommon(const char* text, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
- if (this->registered != REG_ALL || quitting)
- return;
-
- int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteCommonRaw(std::string(textbuffer), true);
-}
-
-void User::WriteCommonExcept(const char* text, ...)
-{
- char textbuffer[MAXBUF];
- va_list argsPtr;
-
if (this->registered != REG_ALL || quitting)
return;
- int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
- va_end(argsPtr);
-
- this->WriteCommonRaw(std::string(textbuffer), false);
+ std::string textbuffer;
+ VAFORMAT(textbuffer, text, text);
+ textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
+ this->WriteCommonRaw(textbuffer, true);
}
void User::WriteCommonRaw(const std::string &line, bool include_self)
LocalUser::already_sent_id++;
- UserChanList include_c(chans);
+ IncludeChanList include_c(chans.begin(), chans.end());
std::map<User*,bool> exceptions;
exceptions[this] = include_self;
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
+ FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
u->Write(line);
}
}
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
+ for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
{
- Channel* c = *v;
- const UserMembList* ulist = c->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
+ Channel* c = (*v)->chan;
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting && u->already_sent != LocalUser::already_sent_id)
+ if (u && u->already_sent != LocalUser::already_sent_id)
{
u->already_sent = LocalUser::already_sent_id;
u->Write(line);
void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
{
- char tb1[MAXBUF];
- char tb2[MAXBUF];
-
if (this->registered != REG_ALL)
return;
already_sent_t uniq_id = ++LocalUser::already_sent_id;
- snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str());
- snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str());
- std::string out1 = tb1;
- std::string out2 = tb2;
+ const std::string normalMessage = ":" + this->GetFullHost() + " QUIT :" + normal_text;
+ const std::string operMessage = ":" + this->GetFullHost() + " QUIT :" + oper_text;
- UserChanList include_c(chans);
+ IncludeChanList include_c(chans.begin(), chans.end());
std::map<User*,bool> exceptions;
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
+ FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
{
u->already_sent = uniq_id;
if (i->second)
- u->Write(IS_OPER(u) ? out2 : out1);
+ u->Write(u->IsOper() ? operMessage : normalMessage);
}
}
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
+ for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
{
- const UserMembList* ulist = (*v)->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
+ const Channel::MemberMap& ulist = (*v)->chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); i++)
{
LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting && (u->already_sent != uniq_id))
+ if (u && (u->already_sent != uniq_id))
{
u->already_sent = uniq_id;
- u->Write(IS_OPER(u) ? out2 : out1);
+ u->Write(u->IsOper() ? operMessage : normalMessage);
}
}
}
void User::SendText(const char *text, ...)
{
- va_list argsPtr;
- char line[MAXBUF];
-
- va_start(argsPtr, text);
- vsnprintf(line, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- SendText(std::string(line));
+ std::string line;
+ VAFORMAT(line, text, text);
+ SendText(line);
}
-void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream)
+void User::SendText(const std::string& linePrefix, std::stringstream& textStream)
{
std::string line;
- std::string Word;
- while (TextStream >> Word)
+ std::string word;
+ while (textStream >> word)
{
- size_t lineLength = LinePrefix.length() + line.length() + Word.length() + 13;
- if (lineLength > MAXBUF)
+ size_t lineLength = linePrefix.length() + line.length() + word.length() + 3; // "\s\n\r"
+ if (lineLength > ServerInstance->Config->Limits.MaxLine)
{
- SendText(LinePrefix + line);
+ SendText(linePrefix + line);
line.clear();
}
- line += " " + Word;
+ line += " " + word;
}
- SendText(LinePrefix + line);
+ SendText(linePrefix + line);
}
/* return 0 or 1 depending if users u and u2 share one or more common channels
*/
bool User::SharesChannelWith(User *other)
{
- if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
- return false;
-
/* Outer loop */
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); ++i)
{
/* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
* by replacing it with a map::find which *should* be more efficient
*/
- if ((*i)->HasUser(other))
+ if ((*i)->chan->HasUser(other))
return true;
}
return false;
}
-bool User::ChangeName(const char* gecos)
+bool User::ChangeName(const std::string& gecos)
{
if (!this->fullname.compare(gecos))
return true;
FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
if (MOD_RESULT == MOD_RES_DENY)
return false;
- FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+ FOREACH_MOD(OnChangeName, (this,gecos));
}
this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
return true;
}
-void User::DoHostCycle(const std::string &quitline)
-{
- char buffer[MAXBUF];
-
- if (!ServerInstance->Config->CycleHosts)
- return;
-
- already_sent_t silent_id = ++LocalUser::already_sent_id;
- already_sent_t seen_id = ++LocalUser::already_sent_id;
-
- UserChanList include_c(chans);
- std::map<User*,bool> exceptions;
-
- FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
-
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
- {
- if (i->second)
- {
- u->already_sent = seen_id;
- u->Write(quitline);
- }
- else
- {
- u->already_sent = silent_id;
- }
- }
- }
- for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
- {
- Channel* c = *v;
- snprintf(buffer, MAXBUF, ":%s JOIN %s", GetFullHost().c_str(), c->name.c_str());
- std::string joinline(buffer);
- Membership* memb = c->GetUser(this);
- std::string modeline = memb->modes;
- if (modeline.length() > 0)
- {
- for(unsigned int i=0; i < memb->modes.length(); i++)
- modeline.append(" ").append(nick);
- snprintf(buffer, MAXBUF, ":%s MODE %s +%s",
- ServerInstance->Config->CycleHostsFromUser ? GetFullHost().c_str() : ServerInstance->Config->ServerName.c_str(),
- c->name.c_str(), modeline.c_str());
- modeline = buffer;
- }
-
- const UserMembList *ulist = c->GetUsers();
- for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u == NULL || u == this)
- continue;
- if (u->already_sent == silent_id)
- continue;
-
- if (u->already_sent != seen_id)
- {
- u->Write(quitline);
- u->already_sent = seen_id;
- }
- u->Write(joinline);
- if (modeline.length() > 0)
- u->Write(modeline);
- }
- }
-}
-
-bool User::ChangeDisplayedHost(const char* shost)
+bool User::ChangeDisplayedHost(const std::string& shost)
{
if (dhost == shost)
return true;
return false;
}
- FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost));
-
- std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host";
-
- /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
- this->dhost.assign(shost, 0, 64);
+ FOREACH_MOD(OnChangeHost, (this,shost));
+ this->dhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost);
this->InvalidateCache();
- this->DoHostCycle(quitstr);
-
if (IS_LOCAL(this))
- this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str());
+ this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s :is now your displayed host", this->dhost.c_str());
return true;
}
-bool User::ChangeIdent(const char* newident)
+bool User::ChangeIdent(const std::string& newident)
{
if (this->ident == newident)
return true;
- FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
-
- std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
+ FOREACH_MOD(OnChangeIdent, (this,newident));
this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
-
this->InvalidateCache();
- this->DoHostCycle(quitstr);
-
return true;
}
-void User::SendAll(const char* command, const char* text, ...)
-{
- char textbuffer[MAXBUF];
- char formatbuffer[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, text);
- vsnprintf(textbuffer, MAXBUF, text, argsPtr);
- va_end(argsPtr);
-
- snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost().c_str(), command, textbuffer);
- std::string fmt = formatbuffer;
-
- for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
- {
- if ((*i)->registered == REG_ALL)
- (*i)->Write(fmt);
- }
-}
-
-
-std::string User::ChannelList(User* source, bool spy)
-{
- std::string list;
-
- for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
- {
- Channel* c = *i;
- /* If the target is the sender, neither +p nor +s is set, or
- * the channel contains the user, it is not a spy channel
- */
- if (spy != (source == this || !(c->IsModeSet('p') || c->IsModeSet('s')) || c->HasUser(source)))
- list.append(c->GetPrefixChar(this)).append(c->name).append(" ");
- }
-
- return list;
-}
-
-void User::SplitChanList(User* dest, const std::string &cl)
-{
- std::string line;
- std::ostringstream prefix;
- std::string::size_type start, pos;
-
- prefix << this->nick << " " << dest->nick << " :";
- line = prefix.str();
- int namelen = ServerInstance->Config->ServerName.length() + 6;
-
- for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
- {
- if (line.length() + namelen + pos - start > 510)
- {
- ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
- line = prefix.str();
- }
-
- line.append(cl.substr(start, pos - start + 1));
- }
-
- if (line.length() != prefix.str().length())
- {
- ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
- }
-}
-
/*
* Sets a user's connection class.
* If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
{
ConnectClass *found = NULL;
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
if (!explicit_name.empty())
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
if (explicit_name == c->name)
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Explicitly set to %s", explicit_name.c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
found = c;
}
}
}
else
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
{
ConnectClass* c = *i;
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Checking %s", c->GetName().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
continue;
if (MOD_RESULT == MOD_RES_ALLOW)
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class forced by module to %s", c->GetName().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
found = c;
break;
}
if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
!InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "No host match (for %s)", c->GetHost().c_str());
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
continue;
}
*/
if (c->limit && (c->GetReferenceCount() >= c->limit))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
continue;
}
int port = c->config->getInt("port");
if (port)
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", port);
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires port (%d)", port);
/* and our port doesn't match, fail. */
if (this->GetServerPort() != port)
if (regdone && !c->config->getString("password").empty())
{
- if (ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
+ if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
{
- ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Bad password, skipping");
+ ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
continue;
}
}
}
}
-/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
- * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
- * then their ip will be taken as 'priority' anyway, so for example,
- * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
- */
-ConnectClass* LocalUser::GetClass()
-{
- return MyClass;
-}
-
-ConnectClass* User::GetClass()
-{
- return NULL;
-}
-
void User::PurgeEmptyChannels()
{
// firstly decrement the count on each channel
- for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
+ for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
{
- Channel* c = *f;
+ Channel* c = (*i)->chan;
+ ++i;
c->DelUser(this);
}
{
if (!ServerInstance->Config->HideWhoisServer.empty())
return ServerInstance->Config->HideWhoisServer;
- return server;
+ return server->GetName();
}
const std::string& FakeUser::GetFullRealHost()
{
if (!ServerInstance->Config->HideWhoisServer.empty())
return ServerInstance->Config->HideWhoisServer;
- return server;
+ return server->GetName();
}
ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
: config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
- penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(0), limit(0)
+ penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(ServerInstance->Config->MaxChans),
+ limit(0), resolvehostnames(true)
{
}
softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
- limit(parent.limit)
+ limit(parent.limit), resolvehostnames(parent.resolvehostnames)
{
}
maxconnwarn = src->maxconnwarn;
maxchans = src->maxchans;
limit = src->limit;
+ resolvehostnames = src->resolvehostnames;
}
#!/bin/sh
-echo "InspIRCd-2.0.18"
+echo "InspIRCd-2.2.0+pre"
+++ /dev/null
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *
- * This file is part of InspIRCd. InspIRCd 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, version 2.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-void InspIRCd::DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick)
-{
- this->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
- if (user == dest || user->HasPrivPermission("users/auspex"))
- {
- this->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString());
- }
-
- std::string cl = dest->ChannelList(user, false);
- const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
-
- if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
- cl.append(dest->ChannelList(user, true));
-
- user->SplitChanList(dest,cl);
-
- if (state == ServerConfig::SPYWHOIS_SPLITMSG)
- {
- std::string scl = dest->ChannelList(user, true);
- if (scl.length())
- {
- SendWhoisLine(user, dest, 336, "%s %s :is on private/secret channels:",user->nick.c_str(), dest->nick.c_str());
- user->SplitChanList(dest,scl);
- }
- }
- if (user != dest && !this->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- {
- this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), this->Config->HideWhoisServer.c_str(), this->Config->Network.c_str());
- }
- else
- {
- std::string serverdesc = GetServerDescription(dest->server);
- this->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->server.c_str(), serverdesc.c_str());
- }
-
- if (IS_AWAY(dest))
- {
- this->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
- }
-
- if (IS_OPER(dest))
- {
- if (this->Config->GenericOper)
- this->SendWhoisLine(user, dest, 313, "%s %s :is an IRC operator",user->nick.c_str(), dest->nick.c_str());
- else
- this->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick.c_str(), dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->NameStr(), this->Config->Network.c_str());
- }
-
- if (user == dest || user->HasPrivPermission("users/auspex"))
- {
- if (dest->IsModeSet('s') != 0)
- {
- this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes(), dest->FormatNoticeMasks());
- }
- else
- {
- this->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes());
- }
- }
-
- FOREACH_MOD(I_OnWhois,OnWhois(user,dest));
-
- /*
- * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
- * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
- */
- if ((idle) || (signon))
- {
- this->SendWhoisLine(user, dest, 317, "%s %s %lu %lu :seconds idle, signon time",user->nick.c_str(), dest->nick.c_str(), idle, signon);
- }
-
- this->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick.c_str(), dest->nick.c_str());
-}
-
-
-
*/
-/* $Core */
-
#include "inspircd.h"
-#include "hashcomp.h"
-#include "inspstring.h"
-static bool match_internal(const unsigned char *str, const unsigned char *mask, unsigned const char *map)
+static bool MatchInternal(const unsigned char* str, const unsigned char* mask, unsigned const char* map)
{
- unsigned char *cp = NULL, *mp = NULL;
+ unsigned char* cp = NULL;
+ unsigned char* mp = NULL;
unsigned char* string = (unsigned char*)str;
unsigned char* wild = (unsigned char*)mask;
return !*wild;
}
-/********************************************************************
- * Below here is all wrappers around match_internal
- ********************************************************************/
+// Below here is all wrappers around MatchInternal
-CoreExport bool InspIRCd::Match(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::Match(const std::string& str, const std::string& mask, unsigned const char* map)
{
if (!map)
map = national_case_insensitive_map;
- return match_internal((const unsigned char *)str.c_str(), (const unsigned char *)mask.c_str(), map);
+ return MatchInternal((const unsigned char*)str.c_str(), (const unsigned char*)mask.c_str(), map);
}
-CoreExport bool InspIRCd::Match(const char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::Match(const char* str, const char* mask, unsigned const char* map)
{
if (!map)
map = national_case_insensitive_map;
- return match_internal((const unsigned char *)str, (const unsigned char *)mask, map);
+
+ return MatchInternal((const unsigned char*)str, (const unsigned char*)mask, map);
}
-CoreExport bool InspIRCd::MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map)
{
if (irc::sockets::MatchCIDR(str, mask, true))
return true;
- if (!map)
- map = national_case_insensitive_map;
-
// Fall back to regular match
return InspIRCd::Match(str, mask, map);
}
-CoreExport bool InspIRCd::MatchCIDR(const char *str, const char *mask, unsigned const char *map)
+bool InspIRCd::MatchCIDR(const char* str, const char* mask, unsigned const char* map)
{
if (irc::sockets::MatchCIDR(str, mask, true))
return true;
- if (!map)
- map = national_case_insensitive_map;
-
// Fall back to regular match
return InspIRCd::Match(str, mask, map);
}
+bool InspIRCd::MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr)
+{
+ std::stringstream masklist(masks);
+ std::string mask;
+ while (masklist >> mask)
+ {
+ if (InspIRCd::Match(hostname, mask, ascii_case_insensitive_map) ||
+ InspIRCd::MatchCIDR(ipaddr, mask, ascii_case_insensitive_map))
+ {
+ return true;
+ }
+ }
+ return false;
+}
#include "inspircd.h"
#include "xline.h"
-#include "bancache.h"
/** An XLineFactory specialized to generate GLine* pointers
*/
if (ELines.empty())
return;
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
- User* u = (User*)(*u2);
+ LocalUser* u = *u2;
/* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
LookupIter safei;
ContainerIter x = lookup_lines.find(line->type);
if (x != lookup_lines.end())
{
- LookupIter i = x->second.find(line->Displayable());
+ LookupIter i = x->second.find(line->Displayable().c_str());
if (i != x->second.end())
{
// XLine propagation bug was here, if the line to be added already exists and
if (!xlf)
return false;
- ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
+ ServerInstance->BanCache.RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
if (xlf->AutoApplyToUserList(line))
pending_lines.push_back(line);
- lookup_lines[line->type][line->Displayable()] = line;
+ lookup_lines[line->type][line->Displayable().c_str()] = line;
line->OnAdd();
- FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
+ FOREACH_MOD(OnAddLine, (user, line));
return true;
}
if (simulate)
return true;
- ServerInstance->BanCache->RemoveEntries(y->second->type, true);
+ ServerInstance->BanCache.RemoveEntries(y->second->type, true);
- FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
+ FOREACH_MOD(OnDelLine, (user, y->second));
y->second->Unset();
void ELine::Unset()
{
/* remove exempt from everyone and force recheck after deleting eline */
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
- User* u = (User*)(*u2);
+ LocalUser* u = *u2;
u->exempt = false;
}
// removes lines that have expired
void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
{
- FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
+ FOREACH_MOD(OnExpireLine, (item->second));
item->second->DisplayExpiry();
item->second->Unset();
// applies lines, removing clients and changing nicks etc as applicable
void XLineManager::ApplyLines()
{
- LocalUserList::reverse_iterator u2 = ServerInstance->Users->local_users.rbegin();
- while (u2 != ServerInstance->Users->local_users.rend())
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
{
- User* u = *u2++;
+ LocalUser* u = *j;
// Don't ban people who are exempt.
if (u->exempt)
ExpireLine(n, i);
}
else
- results.push_back(ServerInstance->Config->ServerName+" "+ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
+ results.push_back(ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+i->second->source+" :"+i->second->reason);
i = safei;
}
void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
{
- char sreason[MAXBUF];
- snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason.c_str());
- if (!ServerInstance->Config->MoronBanner.empty())
- u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
+ const std::string banReason = line + "-Lined: " + reason;
+
+ if (!ServerInstance->Config->XLineMessage.empty())
+ u->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
if (ServerInstance->Config->HideBans)
- ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
+ ServerInstance->Users->QuitUser(u, line + "-Lined", &banReason);
else
- ServerInstance->Users->QuitUser(u, sreason);
+ ServerInstance->Users->QuitUser(u, banReason);
if (bancache)
{
- ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
- if (this->duration > 0)
- ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, this->duration);
- else
- ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
+ ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding positive hit (" + line + ") for " + u->GetIPString());
+ ServerInstance->BanCache.AddHit(u->GetIPString(), this->type, banReason, this->duration);
}
}
bool KLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
bool GLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
bool ELine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
bool ZLine::Matches(User *u)
{
- if (u->exempt)
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu && lu->exempt)
return false;
if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
void QLine::Apply(User* u)
{
/* Force to uuid on apply of qline, no need to disconnect any more :) */
- u->ForceNickChange(u->uuid.c_str());
+ u->ChangeNick(u->uuid);
}
void ELine::OnAdd()
{
/* When adding one eline, only check the one eline */
- for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator u2 = list.begin(); u2 != list.end(); u2++)
{
- User* u = (User*)(*u2);
+ LocalUser* u = *u2;
if (this->Matches(u))
u->exempt = true;
}
}
-void ELine::DisplayExpiry()
-{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired E-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
-}
-
-void QLine::DisplayExpiry()
-{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Q-Line %s (set by %s %ld seconds ago)",
- nick.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
-}
-
-void ZLine::DisplayExpiry()
-{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Z-Line %s (set by %s %ld seconds ago)",
- ipaddr.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
-}
-
-void KLine::DisplayExpiry()
-{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired K-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
-}
-
-void GLine::DisplayExpiry()
+void XLine::DisplayExpiry()
{
- ServerInstance->SNO->WriteToSnoMask('x',"Removing expired G-Line %s@%s (set by %s %ld seconds ago)",
- identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
+ bool onechar = (type.length() == 1);
+ ServerInstance->SNO->WriteToSnoMask('x', "Removing expired %s%s %s (set by %s %ld seconds ago)",
+ type.c_str(), (onechar ? "-Line" : ""), Displayable().c_str(), source.c_str(), (long)(ServerInstance->Time() - set_time));
}
-const char* ELine::Displayable()
+const std::string& ELine::Displayable()
{
- return matchtext.c_str();
+ return matchtext;
}
-const char* KLine::Displayable()
+const std::string& KLine::Displayable()
{
- return matchtext.c_str();
+ return matchtext;
}
-const char* GLine::Displayable()
+const std::string& GLine::Displayable()
{
- return matchtext.c_str();
+ return matchtext;
}
-const char* ZLine::Displayable()
+const std::string& ZLine::Displayable()
{
- return ipaddr.c_str();
+ return ipaddr;
}
-const char* QLine::Displayable()
+const std::string& QLine::Displayable()
{
- return nick.c_str();
+ return nick;
}
bool KLine::IsBurstable()
+++ /dev/null
-#!/usr/bin/perl
-
-#
-# InspIRCd -- Internet Relay Chat Daemon
-#
-# Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
-#
-# This file is part of InspIRCd. InspIRCd 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, version 2.
-#
-# 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, see <http://www.gnu.org/licenses/>.
-#
-
-
-use strict;
-use warnings;
-
-my $maxparams = shift;
-
-die "You must supply a number of parameters to generate headers allowing for!" unless(defined $maxparams);
-die "You must request a non-negative parameter limit!" unless($maxparams >= 0);
-
-print STDERR "Generating headerfile for a maximium of $maxparams parameters\n";
-
-# First generate the HanderBase family
-
-my @templatetypes = ('ReturnType');
-for(my $i = 0; $i <= $maxparams; $i++)
-{
- push @templatetypes, "Param" . $i if($i > 0);
- print "template <typename " . join(', typename ', @templatetypes) . "> class CoreExport HandlerBase" . $i . "\n";
- print "{\n";
- print " public:\n";
- print " virtual ReturnType Call(" . join(', ', @templatetypes[1..$#templatetypes]) . ") = 0;\n";
- print " virtual ~HandlerBase" . $i . "() { }\n";
- print "};\n\n";
-}
-
-# And now the caller family
-
-print "template <typename HandlerType> class caller\n";
-print "{\n";
-print " public:\n";
-print " HandlerType* target;\n\n";
-print " caller(HandlerType* initial)\n";
-print " : target(initial)\n";
-print " { }\n\n";
-print " virtual ~caller() { }\n\n";
-print " caller& operator=(HandlerType* newtarget)\n";
-print " {\n";
-print " target = newtarget;\n";
-print " return *this;\n";
-print " }\n";
-print "};\n\n";
-
-
-
-
-@templatetypes = ('ReturnType');
-for(my $i = 0; $i <= $maxparams; $i++)
-{
- push @templatetypes, "Param" . $i if($i > 0);
-
- my $handlertype = "HandlerBase" . $i . "<" . join(', ', @templatetypes) . ">";
- my @templatetypepairs = map { $_ . " " . lc($_) } @templatetypes;
- my @lctemplatetypes = map(lc, @templatetypes);
-
- print "template <typename " . join(', typename ', @templatetypes) . "> class caller" . $i . " : public caller< " . $handlertype . " >\n";
- print "{\n";
- print " public:\n";
- print " caller" . $i . "(" . $handlertype . "* initial)\n";
- print " : caller< " . $handlertype. " >::caller(initial)\n";
- print " { }\n\n";
- print " ReturnType operator() (" . join(', ', @templatetypepairs[1..$#templatetypepairs]) . ")\n";
- print " {\n";
- print " return this->target->Call(" . join(', ', @lctemplatetypes[1..$#lctemplatetypes]) . ");\n";
- print " }\n";
- print "};\n\n";
-}
-
+++ /dev/null
-handle SIGPIPE pass nostop noprint
-handle SIGHUP pass nostop noprint
-run
--- /dev/null
+#!/usr/bin/env perl
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+# Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc>
+# Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd. InspIRCd 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, version 2.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+
+BEGIN {
+ require 5.8.0;
+}
+
+use strict;
+use warnings FATAL => qw(all);
+
+use File::Temp();
+
+# IMPORTANT: This script has to be able to run by itself so that it can be used
+# by binary distributions where the make/utilities.pm module will not
+# be available!
+
+sub prompt($$) {
+ my ($question, $default) = @_;
+ print "$question\n";
+ print "[$default] => ";
+ chomp(my $answer = <STDIN>);
+ print "\n";
+ return $answer ? $answer : $default;
+}
+
+if ($#ARGV != 0 || $ARGV[0] !~ /^(?:auto|gnutls|openssl)$/i) {
+ print "Syntax: genssl <auto|gnutls|openssl>\n";
+ exit 1;
+}
+
+# On OS X the GnuTLS certtool is prefixed to avoid collision with the system certtool.
+my $certtool = $^O eq 'darwin' ? 'gnutls-certtool' : 'certtool';
+
+# Check whether the user has the required tools installed.
+my $has_gnutls = `$certtool --version v 2>/dev/null`;
+my $has_openssl = !system 'openssl version >/dev/null 2>&1';
+
+# The framework the user has specified.
+my $tool = lc $ARGV[0];
+
+# If the user has not explicitly specified a framework then detect one.
+if ($tool eq 'auto') {
+ if ($has_gnutls) {
+ $tool = 'gnutls';
+ } elsif ($has_openssl) {
+ $tool = 'openssl';
+ } else {
+ print STDERR "SSL generation failed: could not find $certtool or openssl in the PATH!\n";
+ exit 1;
+ }
+} elsif ($tool eq 'gnutls' && !$has_gnutls) {
+ print STDERR "SSL generation failed: could not find '$certtool' in the PATH!\n";
+ exit 1;
+} elsif ($tool eq 'openssl' && !$has_openssl) {
+ print STDERR "SSL generation failed: could not find 'openssl' in the PATH!\n";
+ exit 1;
+}
+
+# Harvest information needed to generate the certificate.
+my $common_name = prompt('What is the hostname of your server?', 'irc.example.com');
+my $email = prompt('What email address can you be contacted at?', 'example@example.com');
+my $unit = prompt('What is the name of your unit?', 'Server Admins');
+my $organization = prompt('What is the name of your organization?', 'Example IRC Network');
+my $city = prompt('What city are you located in?', 'Example City');
+my $state = prompt('What state are you located in?', 'Example State');
+my $country = prompt('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
+my $days = prompt('How many days do you want your certificate to be valid for?', '365');
+
+# Contains the SSL certificate in DER form.
+my $dercert;
+
+# Contains the exit code of openssl/gnutls-certtool.
+my $status = 0;
+
+if ($tool eq 'gnutls') {
+ $has_gnutls =~ /certtool.+?(\d+\.\d+)/;
+ my $sec_param = $1 lt '2.10' ? '--bits 2048' : '--sec-param normal';
+ my $tmp = new File::Temp();
+ print $tmp <<__GNUTLS_END__;
+cn = "$common_name"
+email = "$email"
+unit = "$unit"
+organization = "$organization"
+locality = "$city"
+state = "$state"
+country = "$country"
+expiration_days = $days
+tls_www_client
+tls_www_server
+signing_key
+encryption_key
+cert_signing_key
+crl_signing_key
+code_signing_key
+ocsp_signing_key
+time_stamping_key
+__GNUTLS_END__
+ close($tmp);
+ $status ||= system "$certtool --generate-privkey $sec_param --outfile key.pem";
+ $status ||= system "$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template $tmp";
+ $status ||= system "$certtool --generate-dh-params $sec_param --outfile dhparams.pem";
+ $dercert = `$certtool --certificate-info --infile cert.pem --outder` unless $status;
+} elsif ($tool eq 'openssl') {
+ my $tmp = new File::Temp();
+ print $tmp <<__OPENSSL_END__;
+$country
+$state
+$city
+$organization
+$unit
+$common_name
+$email
+__OPENSSL_END__
+ close($tmp);
+ $status ||= system "cat $tmp | openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days $days 2>/dev/null";
+ $status ||= system 'openssl dhparam -out dhparams.pem 2048';
+ $dercert = `openssl x509 -in cert.pem -outform DER` unless $status;
+}
+
+if ($status) {
+ print STDERR "SSL generation failed: $tool exited with a non-zero status!\n";
+ exit 1;
+}
+
+if (defined $dercert && eval 'use Digest::SHA; 1') {
+ my $hash = Digest::SHA->new(256);
+ $hash->add($dercert);
+ print "\nAdd this TLSA record to your domain for DANE support:\n";
+ print "_6697._tcp." . $common_name . " TLSA 3 0 1 " . $hash->hexdigest . "\n";
+}
--- /dev/null
+#!/usr/bin/env perl
+#
+# InspIRCd -- Internet Relay Chat Daemon
+#
+# Copyright (C) 2013 Peter Powell <petpow@saberuk.com>
+#
+# This file is part of InspIRCd. InspIRCd 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, version 2.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+
+BEGIN {
+ require 5.8.0;
+ unless (-f 'configure') {
+ print "Error: test-build must be run from the main source directory!\n";
+ exit 1;
+ }
+}
+
+use strict;
+use warnings FATAL => qw(all);
+
+use make::configure;
+use make::utilities;
+
+$ENV{D} = $ENV{V} = 1;
+
+system 'git', 'clean', '-dfx';
+
+foreach my $compiler ('g++', 'clang++', 'icpc') {
+ next if system "$compiler -v > /dev/null 2>&1";
+ $ENV{CXX} = $compiler;
+ my @socketengines = ( 'select' );
+ push @socketengines, 'epoll' if test_header $compiler, 'sys/epoll.h';
+ push @socketengines, 'kqueue' if test_file $compiler, 'kqueue.cpp';
+ push @socketengines, 'poll' if test_header $compiler, 'poll.h';
+ push @socketengines, 'ports' if test_header $compiler, 'ports.h';
+ foreach my $socketengine (@socketengines) {
+ print "Attempting to build using the $compiler compiler and the $socketengine socket engine...\n";
+ if (system './configure', '--disable-interactive', "--socketengine=$socketengine") {
+ print "Failed to configure using the $compiler compiler and the $socketengine socket engine!\n";
+ exit 1;
+ }
+ $ENV{PURE_STATIC} = 1;
+ if (system 'make', '-j'.get_cpu_count, 'install') {
+ print "Failed to compile with static modules using the $compiler compiler and the $socketengine socket engine!\n";
+ exit 1;
+ }
+ delete $ENV{PURE_STATIC};
+ if (system 'make', '-j'.get_cpu_count, 'install') {
+ print "Failed to compile with dynamic modules using the $compiler compiler and the $socketengine socket engine!\n";
+ exit 1;
+ }
+ print "Building using the $compiler compiler and the $socketengine socket engine succeeded!\n";
+ }
+
+ system 'git', 'clean', '-dfx';
+}
-inspircd_version.h\r
-inspircd_config.h\r
+config.h\r
inspircd.rc\r
project(InspIRCd CXX)
-set(CONF_PATH "conf" CACHE PATH "Configuration file path")
-set(MODULE_PATH "modules" CACHE PATH "Module path")
-set(DATA_PATH "data" CACHE PATH "Data path")
-set(LOG_PATH "logs" CACHE PATH "Log file path")
+set(CONFIG_DIR "conf" CACHE PATH "Configuration file path")
+set(MODULE_DIR "modules" CACHE PATH "Module path")
+set(DATA_DIR "data" CACHE PATH "Data path")
+set(LOG_DIR "logs" CACHE PATH "Log file path")
set(EXTRA_INCLUDES "" CACHE PATH "Extra include paths")
set(EXTRA_LIBS "" CACHE PATH "Extra library paths")
set(INSPIRCD_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../")
+set(SYSTEM_NAME_VERSION ${CMAKE_SYSTEM})
+set(SOCKETENGINE "select")
+
+# Build with multiple processes
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+
# Use our own NSIS template
set(CMAKE_MODULE_PATH "${INSPIRCD_BASE}/win")
# Grab version info from version.sh
file(STRINGS "${INSPIRCD_BASE}/src/version.sh" VERSIONSH)
-string(REGEX REPLACE ".*InspIRCd-([0-9]*).*" "\\1" MAJOR_VERSION "${VERSIONSH}")
-string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.([0-9]*).*" "\\1" MINOR_VERSION "${VERSIONSH}")
-string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.[0-9]*\\.([0-9]*).*" "\\1" PATCH_VERSION "${VERSIONSH}")
-set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
+string(REGEX REPLACE ".*InspIRCd-([0-9]*).*" "\\1" VERSION_MAJOR "${VERSIONSH}")
+string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.([0-9]*).*" "\\1" VERSION_MINOR "${VERSIONSH}")
+string(REGEX REPLACE ".*InspIRCd-[0-9]*\\.[0-9]*\\.([0-9]*).*" "\\1" VERSION_PATCH "${VERSIONSH}")
if(MSVC)
# Without /SAFESEH:NO old libraries compiled with VS 2010 or older won't link correctly to VS2012 (eg, extra module libs)
endif(MSVC)
configure_file("${INSPIRCD_BASE}/win/inspircd.rc.cmake" "${INSPIRCD_BASE}/win/inspircd.rc")
-configure_file("${INSPIRCD_BASE}/win/inspircd_version.h.cmake" "${INSPIRCD_BASE}/win/inspircd_version.h")
-configure_file("${INSPIRCD_BASE}/win/inspircd_config.h.cmake" "${INSPIRCD_BASE}/win/inspircd_config.h")
+configure_file("${INSPIRCD_BASE}/make/template/config.h" "${INSPIRCD_BASE}/include/config.h")
add_executable(inspircd ${INSPIRCD_SOURCES} "${INSPIRCD_BASE}/win/inspircd.rc")
target_link_libraries(inspircd win32_memory)
install(FILES ${EXAMPLE_CONFIGS} DESTINATION conf)
# Create an empty data and logs directory and install them
-file(MAKE_DIRECTORY ${DATA_PATH})
-install(DIRECTORY ${DATA_PATH} DESTINATION .)
-file(MAKE_DIRECTORY ${LOG_PATH})
-install(DIRECTORY ${LOG_PATH} DESTINATION .)
+file(MAKE_DIRECTORY ${DATA_DIR})
+install(DIRECTORY ${DATA_DIR} DESTINATION .)
+file(MAKE_DIRECTORY ${LOG_DIR})
+install(DIRECTORY ${LOG_DIR} DESTINATION .)
if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "InspIRCd IRC Daemon")
set(CPACK_PACKAGE_VENDOR "InspIRCd Development Team")
- set(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
- set(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
- set(CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
- set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${FULL_VERSION}")
+ set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
+ set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
+ set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
+ set(CPACK_PACKAGE_FILE_NAME "InspIRCd-${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../docs/COPYING")
set(CPACK_GENERATOR "NSIS")
101 ICON "inspircd.ico"\r
\r
1 VERSIONINFO\r
- FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
- PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
+ FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
+ PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
BEGIN\r
BLOCK "040904b0"\r
BEGIN\r
- VALUE "Comments", "InspIRCd @MAJOR_VERSION@.@MINOR_VERSION@ IRC Daemon"\r
+ VALUE "Comments", "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@ IRC Daemon"\r
VALUE "CompanyName", "InspIRCd Development Team"\r
VALUE "FileDescription", "InspIRCd"\r
VALUE "FileVersion", "@FULL_VERSION@"\r
+++ /dev/null
-#ifndef INSPIRCD_CONFIG_H\r
-#define INSPIRCD_CONFIG_H\r
-\r
-#define CONFIG_PATH "@CONF_PATH@"\r
-#define MOD_PATH "@MODULE_PATH@"\r
-#define DATA_PATH "@DATA_PATH@"\r
-#define LOG_PATH "@LOG_PATH@"\r
-#define MAXBUF 514\r
-\r
-#include "inspircd_win32wrapper.h"\r
-#include "threadengines/threadengine_win32.h"\r
-\r
-#endif
\ No newline at end of file
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include <windows.h>
#include <exception>
#include <new>
+++ /dev/null
-#define BRANCH "@MAJOR_VERSION@.@MINOR_VERSION@"\r
-#define VERSION "@FULL_VERSION@"\r
-#define REVISION "0"\r
-#define SYSTEM "@CMAKE_SYSTEM@"
\ No newline at end of file
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include "inspircd_win32wrapper.h"
#include "inspircd.h"
#include "configreader.h"
}
return 1;
}
-
+
return 0;
}
// optind++; // Trash this next argument, we won't be needing it.
par = ___argv[optind-1];
}
- }
+ }
// increment the argument for next time
// optind++;
{
if (__longopts[i].val == -1 || par == 0)
return 1;
-
+
return __longopts[i].val;
- }
+ }
break;
}
}
*/
+#pragma once
+
/* Windows Port
Wrapper Functions/Definitions
By Burlex */
-
-#ifndef INSPIRCD_WIN32WRAPPER_H
-#define INSPIRCD_WIN32WRAPPER_H
-
/*
* Starting with PSAPI version 2 for Windows 7 and Windows Server 2008 R2, this function is defined as K32GetProcessMemoryInfo in Psapi.h and exported
* in Kernel32.lib and Kernel32.dll. However, you should always call this function as GetProcessMemoryInfo. To ensure correct resolution of symbols
* for programs that will run on earlier versions of Windows, add Psapi.lib to the TARGETLIBS macro and compile the program with PSAPI_VERSION=1.
- *
+ *
* We do this before anything to make sure it's done.
*/
#define PSAPI_VERSION 1
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
-/* They just have to be *different*, don't they. */
-#define PATH_MAX MAX_PATH
-
/* Macros for exporting symbols - dependant on what is being compiled */
#ifdef DLL_BUILD
#include <sys/stat.h>
#include <direct.h>
#include <process.h>
+#include <io.h>
+
+#define F_OK 0 /* test for existence of file */
+#define X_OK (1<<0) /* test for execute or search permission */
+#define W_OK (1<<1) /* test for write permission */
+#define R_OK (1<<2) /* test for read permission */
+
+// Windows defines these already.
+#undef ERROR
+#undef min
+#undef max
/* strcasecmp is not defined on windows by default */
#define strcasecmp _stricmp
#define snprintf _snprintf
#define vsnprintf _vsnprintf
+#ifndef va_copy
+#define va_copy(dest, src) (dest = src)
+#endif
+
/* Unix-style sleep (argument is in seconds) */
__inline void sleep(int seconds) { Sleep(seconds * 1000); }
#define popen _popen
#define pclose _pclose
+/* _access */
+#define access _access
+
/* IPV4 only convert string to address struct */
__inline int inet_aton(const char *cp, struct in_addr *addr)
-{
+{
addr->s_addr = inet_addr(cp);
return (addr->s_addr == INADDR_NONE) ? 0 : 1;
};
DWORD dwErrorCode;
};
-#endif
-
+// Same value as EXIT_STATUS_FORK (EXIT_STATUS_FORK is unused on Windows)
+#define EXIT_STATUS_SERVICE 4
# so copy the file out of extra/\r
file(COPY "${INSPIRCD_BASE}/src/modules/extra/m_regex_stdlib.cpp" DESTINATION "${INSPIRCD_BASE}/src/modules/")\r
\r
-file(GLOB INSPIRCD_MODULES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/commands/*.cpp" "${INSPIRCD_BASE}/src/modules/*.cpp")\r
+file(GLOB INSPIRCD_MODULES "${INSPIRCD_BASE}/src/coremods/core_*" "${INSPIRCD_BASE}/src/modules/m_*")\r
list(SORT INSPIRCD_MODULES)\r
\r
add_definitions("-DDLL_BUILD")\r
\r
foreach(MODULE_NAME ${INSPIRCD_MODULES})\r
- string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1.so" SO_NAME ${MODULE_NAME})\r
- add_library(${SO_NAME} MODULE ${MODULE_NAME})\r
- set_target_properties(${SO_NAME} PROPERTIES PREFIX "" SUFFIX "")\r
+ if(IS_DIRECTORY "${MODULE_NAME}")\r
+ string(REGEX REPLACE "^.*[/\\](.*)$" "\\1" BASE_NAME ${MODULE_NAME})\r
+ else(IS_DIRECTORY "${MODULE_NAME}")\r
+ string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1" BASE_NAME ${MODULE_NAME})\r
+ endif(IS_DIRECTORY "${MODULE_NAME}")\r
+ set(SO_NAME "${BASE_NAME}.so")\r
+\r
+ if(IS_DIRECTORY "${MODULE_NAME}")\r
+ file(GLOB MODULES_SUBDIR_SRCS "${MODULE_NAME}/*.cpp")\r
+ list(SORT MODULES_SUBDIR_SRCS)\r
+ add_library(${SO_NAME} MODULE ${MODULES_SUBDIR_SRCS})\r
+ else(IS_DIRECTORY "${MODULE_NAME}")\r
+ add_library(${SO_NAME} MODULE ${MODULE_NAME})\r
+ endif(IS_DIRECTORY "${MODULE_NAME}")\r
+\r
+ # Generate the module and set its linker flags, also set it to depend on the main executable to be built beforehand\r
target_link_libraries(${SO_NAME} inspircd)\r
add_dependencies(${SO_NAME} inspircd)\r
if(MSVC)\r
target_link_libraries(${SO_NAME} win32_memory)\r
add_dependencies(${SO_NAME} win32_memory)\r
endif(MSVC)\r
- install(TARGETS ${SO_NAME} DESTINATION modules)\r
-endforeach(MODULE_NAME ${INSPIRCD_MODULES})\r
\r
-file(GLOB INSPIRCD_MODULES_SPANNINGTREE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/modules/m_spanningtree/*.cpp")\r
-list(SORT INSPIRCD_MODULES_SPANNINGTREE)\r
+ set_target_properties(${SO_NAME} PROPERTIES\r
+ PREFIX ""\r
+ SUFFIX ""\r
+ COMPILE_DEFINITIONS "MODNAME=\"${BASE_NAME}\""\r
+ )\r
\r
-add_library(m_spanningtree.so MODULE ${INSPIRCD_MODULES_SPANNINGTREE})\r
-set_target_properties(m_spanningtree.so PROPERTIES PREFIX "" SUFFIX "")\r
-target_link_libraries(m_spanningtree.so inspircd)\r
-add_dependencies(m_spanningtree.so inspircd)\r
-if(MSVC)\r
- target_link_libraries(m_spanningtree.so win32_memory)\r
- add_dependencies(m_spanningtree.so win32_memory)\r
-endif(MSVC)\r
-install(TARGETS m_spanningtree.so DESTINATION modules)
\ No newline at end of file
+ # Set the module to be installed to the module directory\r
+ install(TARGETS ${SO_NAME} DESTINATION ${MODULE_DIR})\r
+endforeach(MODULE_NAME ${INSPIRCD_MODULES})\r
*/
-#include "inspircd_config.h"
+#include "config.h"
#include "inspircd.h"
#include "exitcodes.h"
#include <windows.h>
}
else
{
- return EXIT_STATUS_INTERNAL;
+ return EXIT_STATUS_SERVICE;
}
}
return 0;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#pragma once
+
#ifdef _WIN32
/* Hook for win32service.cpp to exit properly with the service specific error code */